openapi_parser 2.3.1 → 2.4.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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yaml +1 -1
  3. data/.ruby-version +1 -1
  4. data/CHANGELOG.md +31 -0
  5. data/lib/openapi_parser/config.rb +12 -0
  6. data/lib/openapi_parser/schema_validator/array_validator.rb +7 -3
  7. data/lib/openapi_parser/schema_validator/minimum_maximum.rb +17 -4
  8. data/lib/openapi_parser/schema_validator/nil_validator.rb +4 -0
  9. data/lib/openapi_parser/schema_validator/type_mismatch_validator.rb +11 -0
  10. data/lib/openapi_parser/schema_validator.rb +82 -1
  11. data/lib/openapi_parser/schemas/components.rb +4 -0
  12. data/lib/openapi_parser/schemas/openapi.rb +20 -0
  13. data/lib/openapi_parser/schemas/schema.rb +12 -1
  14. data/lib/openapi_parser/spec_validator/rule.rb +68 -0
  15. data/lib/openapi_parser/spec_validator/rules/const_in_30.rb +25 -0
  16. data/lib/openapi_parser/spec_validator/rules/content_encoding_in_30.rb +24 -0
  17. data/lib/openapi_parser/spec_validator/rules/content_media_type_in_30.rb +25 -0
  18. data/lib/openapi_parser/spec_validator/rules/content_schema_in_30.rb +25 -0
  19. data/lib/openapi_parser/spec_validator/rules/dynamic_anchor_in_30.rb +25 -0
  20. data/lib/openapi_parser/spec_validator/rules/dynamic_ref_in_30.rb +25 -0
  21. data/lib/openapi_parser/spec_validator/rules/example_singular_deprecation.rb +25 -0
  22. data/lib/openapi_parser/spec_validator/rules/exclusive_maximum.rb +32 -0
  23. data/lib/openapi_parser/spec_validator/rules/exclusive_minimum.rb +32 -0
  24. data/lib/openapi_parser/spec_validator/rules/json_schema_dialect_in_30.rb +19 -0
  25. data/lib/openapi_parser/spec_validator/rules/nullable_deprecation.rb +25 -0
  26. data/lib/openapi_parser/spec_validator/rules/path_items_in_30.rb +25 -0
  27. data/lib/openapi_parser/spec_validator/rules/prefix_items_in_30.rb +25 -0
  28. data/lib/openapi_parser/spec_validator/rules/type_array_in_30.rb +25 -0
  29. data/lib/openapi_parser/spec_validator/rules/type_null_in_30.rb +26 -0
  30. data/lib/openapi_parser/spec_validator/rules/webhooks_in_30.rb +18 -0
  31. data/lib/openapi_parser/spec_validator/spec_violation.rb +11 -0
  32. data/lib/openapi_parser/spec_validator.rb +85 -0
  33. data/lib/openapi_parser/version.rb +1 -1
  34. data/lib/openapi_parser.rb +3 -0
  35. data/sig/openapi_parser/config.rbs +2 -0
  36. data/sig/openapi_parser/schema_validator.rbs +3 -0
  37. data/sig/openapi_parser/spec_validator.rbs +109 -0
  38. data/sig/openapi_parser.rbs +2 -0
  39. metadata +23 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b3129b448382c0b36273808c25a197c9264ec23d4729c93f8c5594375cf16cc5
4
- data.tar.gz: 57aa683478c58dd684995e326687ef8e187a08fe4818e707812b1484a3e6e13b
3
+ metadata.gz: d03c17295ee85e0cf17e06e83f6e28c1bed8028e7de1ae68574e2d69ad6160b2
4
+ data.tar.gz: 6cae65df5bab290cff5d7c014d181f5eb631e153863f5eac82c7ce460d224dd9
5
5
  SHA512:
6
- metadata.gz: 1e99aa6ea5ae7234c886c17c6d48bdeb75480db7698057aa77825301c0151caa986b59f6cc0b5a5a421c135fb47fb513e6440b6a3a18db4dae2f25620001f05a
7
- data.tar.gz: b5886770b412d8d73fe30a07039fcf76773880874e4762bd704e6ab3c4b545ade6ed722ebd010717af42c9e09e4e3ccd4fc0f53954c9d98bd24e64583ed99e48
6
+ metadata.gz: 3074ba856acef7b064c0b1d2cf7b79b274e00766d90797ddfe95626f19ea09571fe72e2346e397bc7cac18f986959c659b62de2933b42d0a403074d5e41a73a9
7
+ data.tar.gz: 7c373bb6e33c6bf221b7d3dbc41f89951109b8adc7bc9aef362eb5c820733e5c1203279c272e92e96b1cd4a6278b09f70aab5ffee809cf635e9253d6e616bb06
@@ -20,7 +20,7 @@ jobs:
20
20
  - ruby-head
21
21
  runs-on: ${{ matrix.os }}
22
22
  steps:
23
- - uses: actions/checkout@v5
23
+ - uses: actions/checkout@v7
24
24
  - uses: ruby/setup-ruby@v1
25
25
  with:
26
26
  ruby-version: ${{ matrix.ruby }}
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 3.1.0
1
+ 3.3.12
data/CHANGELOG.md CHANGED
@@ -1,5 +1,36 @@
1
1
  ## Unreleased
2
2
 
3
+ ## 2.4.0 (2026-10-14)
4
+ * support `components.pathItems` so `$ref`s into it resolve, unblocking OpenAPI 3.1 documents that use reusable path items
5
+ * support array-form `type` (3.1) in value validation
6
+ * support root-level `jsonSchemaDialect` (OpenAPI 3.1) in the parse layer
7
+ * support `prefixItems` (OpenAPI 3.1) with positional tuple validation
8
+ * support `contentMediaType` (OpenAPI 3.1) in the parse layer
9
+ * support `contentEncoding` (OpenAPI 3.1) in the parse layer
10
+ * add `SpecValidator` with `strict_specification_version` config (`:silent` / `:warn` / `:raise`) to detect version mismatches between declared OpenAPI version and actual field usage
11
+ * `ContentEncodingIn30`: detect `contentEncoding` usage in 3.0 documents (3.1 addition)
12
+ * `ContentMediaTypeIn30`: detect `contentMediaType` usage in 3.0 documents (3.1 addition)
13
+ * `PrefixItemsIn30`: detect `prefixItems` usage in 3.0 documents (3.1 addition)
14
+ * `JsonSchemaDialectIn30`: detect root-level `jsonSchemaDialect` usage in 3.0 documents (3.1 addition)
15
+ * `TypeArrayIn30`: detect array-form `type` usage in 3.0 documents (3.1 form)
16
+ * `NullableDeprecation`: detect `nullable` usage in 3.1 documents (removed in 3.1)
17
+ * `ExampleSingularDeprecation`: detect singular `example` on schemas in 3.1 documents (deprecated in 3.1)
18
+ * `PathItemsIn30`: detect `components.pathItems` usage in 3.0 documents (3.1 addition)
19
+ * `ExclusiveMinimum` / `ExclusiveMaximum`: detect 3.0 Boolean vs 3.1 numeric form mismatch
20
+ * `TypeNullIn30`: detect `type: "null"` usage in 3.0 documents (3.1 primitive)
21
+ * `WebhooksIn30`: detect root-level `webhooks` usage in 3.0 documents (3.1 addition)
22
+ * `ConstIn30`: detect `const` usage in 3.0 documents (3.1 addition)
23
+ * `DynamicRefIn30`: detect `$dynamicRef` usage in 3.0 documents (3.1 addition)
24
+ * `DynamicAnchorIn30`: detect `$dynamicAnchor` usage in 3.0 documents (3.1 addition)
25
+ * `ContentSchemaIn30`: detect `contentSchema` usage in 3.0 documents (3.1 addition)
26
+ * expose the declared version as `OpenAPI#openapi_version` (a `Gem::Version`, or nil when the field is missing or malformed) so `SpecValidator` rules compare version ranges; a 3.2 document is checked by the 3.1-or-later rules
27
+ * support 3.1-style numeric `exclusiveMinimum` / `exclusiveMaximum` in value validation (standalone bound, not a Boolean modifier on `minimum` / `maximum`)
28
+ * support `type: "null"` (3.1 primitive) in value validation
29
+ * support root-level `webhooks` (OpenAPI 3.1) in the parse layer
30
+ * support `const` (OpenAPI 3.1) with exact-equality value validation
31
+ * support `contentSchema` (OpenAPI 3.1) in the parse layer
32
+ * coerce String values into array-form `type` (3.1) when `coerce_value` is on, so params like `type: [integer, "null"]` accept `"5"`
33
+
3
34
  ## 2.3.1 (2025-11-14)
4
35
  * add optional date coercion with behavior matching existing datetime coercion
5
36
 
@@ -41,6 +41,18 @@ class OpenAPIParser::Config
41
41
  @config.fetch(:strict_reference_validation, false)
42
42
  end
43
43
 
44
+ KNOWN_STRICT_SPECIFICATION_VERSION_VALUES = %i[silent warn raise].freeze
45
+
46
+ # @return [Symbol] :silent / :warn / :raise
47
+ def strict_specification_version
48
+ value = @config.fetch(:strict_specification_version, :silent)
49
+ unless KNOWN_STRICT_SPECIFICATION_VERSION_VALUES.include?(value)
50
+ warn("[OpenAPIParser] unknown strict_specification_version: #{value.inspect}, falling back to :silent")
51
+ return :silent
52
+ end
53
+ value
54
+ end
55
+
44
56
  def validate_header
45
57
  @config.fetch(:validate_header, true)
46
58
  end
@@ -11,10 +11,14 @@ class OpenAPIParser::SchemaValidator
11
11
  value, err = validate_unique_items(value, schema)
12
12
  return [nil, err] if err
13
13
 
14
- # array type have an schema in items property
14
+ # 3.1: prefixItems positionally validates the leading N elements.
15
+ # Elements past the prefix fall back to schema.items, matching how
16
+ # JSON Schema 2020-12 layers the two keywords.
17
+ prefix_schemas = schema.prefix_items || []
15
18
  items_schema = schema.items
16
- coerced_values = value.map do |v|
17
- coerced, err = validatable.validate_schema(v, items_schema)
19
+ coerced_values = value.each_with_index.map do |v, idx|
20
+ sub_schema = prefix_schemas[idx] || items_schema
21
+ coerced, err = validatable.validate_schema(v, sub_schema)
18
22
  return [nil, err] if err
19
23
 
20
24
  coerced
@@ -4,8 +4,10 @@ class OpenAPIParser::SchemaValidator
4
4
  # @param [Object] value
5
5
  # @param [OpenAPIParser::Schemas::Schema] schema
6
6
  def check_minimum_maximum(value, schema)
7
- include_min_max = schema.minimum || schema.maximum
8
- return [value, nil] unless include_min_max
7
+ has_min_or_max = schema.minimum || schema.maximum
8
+ has_numeric_exclusive_min = schema.exclusiveMinimum.is_a?(Numeric)
9
+ has_numeric_exclusive_max = schema.exclusiveMaximum.is_a?(Numeric)
10
+ return [value, nil] unless has_min_or_max || has_numeric_exclusive_min || has_numeric_exclusive_max
9
11
 
10
12
  validate(value, schema)
11
13
  [value, nil]
@@ -18,16 +20,27 @@ class OpenAPIParser::SchemaValidator
18
20
  def validate(value, schema)
19
21
  reference = schema.object_reference
20
22
 
23
+ # 3.1: exclusiveMinimum is a standalone numeric bound.
24
+ if schema.exclusiveMinimum.is_a?(Numeric) && value <= schema.exclusiveMinimum
25
+ raise OpenAPIParser::LessThanExclusiveMinimum.new(value, reference)
26
+ end
27
+
21
28
  if schema.minimum
22
- if schema.exclusiveMinimum && value <= schema.minimum
29
+ # 3.0: exclusiveMinimum is a Boolean modifier on `minimum`.
30
+ if schema.exclusiveMinimum == true && value <= schema.minimum
23
31
  raise OpenAPIParser::LessThanExclusiveMinimum.new(value, reference)
24
32
  elsif value < schema.minimum
25
33
  raise OpenAPIParser::LessThanMinimum.new(value, reference)
26
34
  end
27
35
  end
28
36
 
37
+ # 3.1: standalone numeric upper bound, mirror of exclusiveMinimum.
38
+ if schema.exclusiveMaximum.is_a?(Numeric) && value >= schema.exclusiveMaximum
39
+ raise OpenAPIParser::MoreThanExclusiveMaximum.new(value, reference)
40
+ end
41
+
29
42
  if schema.maximum
30
- if schema.exclusiveMaximum && value >= schema.maximum
43
+ if schema.exclusiveMaximum == true && value >= schema.maximum
31
44
  raise OpenAPIParser::MoreThanExclusiveMaximum.new(value, reference)
32
45
  elsif value > schema.maximum
33
46
  raise OpenAPIParser::MoreThanMaximum.new(value, reference)
@@ -4,6 +4,10 @@ class OpenAPIParser::SchemaValidator
4
4
  # @param [OpenAPIParser::Schemas::Schema] schema
5
5
  def coerce_and_validate(value, schema, **_keyword_args)
6
6
  return [value, nil] if schema.nullable
7
+ # 3.1: `type: "null"` makes nil the only valid value for the schema.
8
+ return [value, nil] if schema.type == 'null'
9
+ # 3.1: `type: [..., "null"]` lets nil coexist with another primitive.
10
+ return [value, nil] if schema.type.is_a?(Array) && schema.type.include?('null')
7
11
 
8
12
  [nil, OpenAPIParser::NotNullError.new(schema.object_reference)]
9
13
  end
@@ -0,0 +1,11 @@
1
+ class OpenAPIParser::SchemaValidator
2
+ # Emits a ValidateError unconditionally. Used when the dispatcher has
3
+ # already decided that no primitive applies to the value, e.g.:
4
+ # - schema declares `type: "null"` and the value is non-nil (3.1)
5
+ # - schema declares `type: [t1, t2, ...]` and the value matches none
6
+ class TypeMismatchValidator < Base
7
+ def coerce_and_validate(value, schema, **_keyword_args)
8
+ OpenAPIParser::ValidateError.build_error_result(value, schema)
9
+ end
10
+ end
11
+ end
@@ -13,6 +13,7 @@ require_relative 'schema_validator/any_of_validator'
13
13
  require_relative 'schema_validator/all_of_validator'
14
14
  require_relative 'schema_validator/one_of_validator'
15
15
  require_relative 'schema_validator/nil_validator'
16
+ require_relative 'schema_validator/type_mismatch_validator'
16
17
  require_relative 'schema_validator/unspecified_type_validator'
17
18
 
18
19
  class OpenAPIParser::SchemaValidator
@@ -74,6 +75,15 @@ class OpenAPIParser::SchemaValidator
74
75
  def validate_schema(value, schema, **keyword_args)
75
76
  return [value, nil] unless schema
76
77
 
78
+ # 3.1: `const` pins the value to exactly that constant. Checked before
79
+ # type dispatch so it applies uniformly across primitives. Detection
80
+ # uses raw_schema so an intentional `const: null` is honored.
81
+ if schema.respond_to?(:raw_schema) && schema.raw_schema.is_a?(Hash) && schema.raw_schema.key?('const')
82
+ return [nil, OpenAPIParser::ValidateError.new(value, "const #{schema.const.inspect}", schema.object_reference)] if value != schema.const
83
+ end
84
+
85
+ return coerce_into_type_array(value, schema, **keyword_args) if coerce_into_type_array?(value, schema)
86
+
77
87
  if (v = validator(value, schema))
78
88
  if keyword_args.empty?
79
89
  return v.coerce_and_validate(value, schema)
@@ -103,7 +113,25 @@ class OpenAPIParser::SchemaValidator
103
113
  return one_of_validator if schema.one_of
104
114
  return nil_validator if value.nil?
105
115
 
106
- case schema.type
116
+ # 3.1: pick the type from an Array whose primitive class matches the
117
+ # value, then dispatch as if it were a single-type schema. If nothing
118
+ # matches we still pick a candidate so the relevant validator emits
119
+ # a sensible error rather than silently accepting via the unspecified
120
+ # fallback.
121
+ effective_type = schema.type
122
+ if effective_type.is_a?(Array)
123
+ matched = pick_array_type(value, effective_type)
124
+ return type_mismatch_validator if matched.nil?
125
+
126
+ effective_type = matched
127
+ end
128
+
129
+ validator_for_type(effective_type)
130
+ end
131
+
132
+ # @return [OpenAPIParser::SchemaValidator::Base]
133
+ def validator_for_type(type)
134
+ case type
107
135
  when 'string'
108
136
  string_validator
109
137
  when 'integer'
@@ -116,11 +144,64 @@ class OpenAPIParser::SchemaValidator
116
144
  object_validator
117
145
  when 'array'
118
146
  array_validator
147
+ when 'null'
148
+ # 3.1: only nil values are valid here. nil is handled earlier in
149
+ # this method, so a non-nil value reaching this branch is a type
150
+ # mismatch that should fail validation.
151
+ type_mismatch_validator
119
152
  else
120
153
  unspecified_type_validator
121
154
  end
122
155
  end
123
156
 
157
+ # 3.1: request params arrive as Strings, so `type: [integer, "null"]`
158
+ # never matches by class. When coercing, try each listed type instead.
159
+ # @return [Boolean]
160
+ def coerce_into_type_array?(value, schema)
161
+ return false unless @coerce_value && value.is_a?(String)
162
+ return false if schema.any_of || schema.all_of || schema.one_of
163
+
164
+ schema.type.is_a?(Array) && pick_array_type(value, schema.type).nil?
165
+ end
166
+
167
+ # 3.1: returns the first listed type that coerces, or the first type's error.
168
+ # @return [Array] coerced value and error
169
+ def coerce_into_type_array(value, schema, **keyword_args)
170
+ first_error = nil
171
+ (schema.type - ['null']).each do |type|
172
+ v = validator_for_type(type)
173
+ coerced, err = if keyword_args.empty?
174
+ v.coerce_and_validate(value, schema)
175
+ else
176
+ v.coerce_and_validate(value, schema, **keyword_args)
177
+ end
178
+ return [coerced, nil] if err.nil?
179
+
180
+ first_error ||= err
181
+ end
182
+ return [nil, first_error] if first_error
183
+
184
+ type_mismatch_validator.coerce_and_validate(value, schema)
185
+ end
186
+
187
+ def type_mismatch_validator
188
+ @type_mismatch_validator ||= OpenAPIParser::SchemaValidator::TypeMismatchValidator.new(self, @coerce_value)
189
+ end
190
+
191
+ def pick_array_type(value, types)
192
+ types.find do |t|
193
+ case t
194
+ when 'string' then value.is_a?(String)
195
+ when 'integer' then value.is_a?(Integer)
196
+ when 'number' then value.is_a?(Numeric)
197
+ when 'boolean' then value == true || value == false
198
+ when 'array' then value.is_a?(Array)
199
+ when 'object' then value.is_a?(Hash)
200
+ when 'null' then value.nil?
201
+ end
202
+ end
203
+ end
204
+
124
205
  def string_validator
125
206
  @string_validator ||= OpenAPIParser::SchemaValidator::StringValidator.new(self, @allow_empty_date_and_datetime, @coerce_value, @datetime_coerce_class, @date_coerce_class)
126
207
  end
@@ -24,5 +24,9 @@ module OpenAPIParser::Schemas
24
24
  # @!attribute [r] headers
25
25
  # @return [Hash{String => Header}, nil] header objects
26
26
  openapi_attr_hash_object :headers, Header, reference: true
27
+
28
+ # @!attribute [r] path_items
29
+ # @return [Hash{String => PathItem}, nil] path item objects (OpenAPI 3.1+)
30
+ openapi_attr_hash_object :path_items, PathItem, reference: true, schema_key: :pathItems
27
31
  end
28
32
  end
@@ -21,6 +21,18 @@ module OpenAPIParser::Schemas
21
21
  # @return [String, nil]
22
22
  openapi_attr_values :openapi
23
23
 
24
+ # Declared OpenAPI version as a comparable value.
25
+ # A prerelease tag is dropped ("3.1.0-rc1" => 3.1.0) because it does not
26
+ # change which version's rules the document is written against.
27
+ # @return [Gem::Version, nil] nil when the field is missing or not a
28
+ # major.minor[.patch] version string
29
+ def openapi_version
30
+ return nil unless openapi.is_a?(String)
31
+ return nil unless openapi.match?(/\A\d+\.\d+/) && Gem::Version.correct?(openapi)
32
+
33
+ Gem::Version.new(openapi).release
34
+ end
35
+
24
36
  # @!attribute [r] paths
25
37
  # @return [Paths, nil]
26
38
  openapi_attr_object :paths, Paths, reference: false
@@ -33,6 +45,14 @@ module OpenAPIParser::Schemas
33
45
  # @return [Info, nil]
34
46
  openapi_attr_object :info, Info, reference: false
35
47
 
48
+ # @!attribute [r] webhooks
49
+ # @return [Hash{String => PathItem}, nil] webhook path items (OpenAPI 3.1+)
50
+ openapi_attr_hash_object :webhooks, PathItem, reference: true
51
+
52
+ # @!attribute [r] json_schema_dialect
53
+ # @return [String, nil] dialect URI for embedded JSON Schemas (OpenAPI 3.1+)
54
+ openapi_attr_value :json_schema_dialect, schema_key: :jsonSchemaDialect
55
+
36
56
  # @return [OpenAPIParser::RequestOperation, nil]
37
57
  def request_operation(http_method, request_path)
38
58
  OpenAPIParser::RequestOperation.create(http_method, request_path, @path_item_finder, @config)
@@ -71,7 +71,10 @@ module OpenAPIParser::Schemas
71
71
  :type,
72
72
  :nullable,
73
73
  :example,
74
- :deprecated
74
+ :deprecated,
75
+ :const,
76
+ :contentMediaType,
77
+ :contentEncoding
75
78
 
76
79
  # @!attribute [r] read_only
77
80
  # @return [Boolean, nil]
@@ -97,6 +100,14 @@ module OpenAPIParser::Schemas
97
100
  # @return [Schema, nil]
98
101
  openapi_attr_object :items, Schema, reference: true
99
102
 
103
+ # @!attribute [r] prefix_items
104
+ # @return [Array<Schema>, nil] tuple-style positional schemas (OpenAPI 3.1+)
105
+ openapi_attr_list_object :prefix_items, Schema, reference: true, schema_key: :prefixItems
106
+
107
+ # @!attribute [r] content_schema
108
+ # @return [Schema, nil] schema describing the content payload (OpenAPI 3.1+)
109
+ openapi_attr_object :content_schema, Schema, reference: true, schema_key: :contentSchema
110
+
100
111
  # @!attribute [r] properties
101
112
  # @return [Hash{String => Schema}, nil]
102
113
  openapi_attr_hash_object :properties, Schema, reference: true
@@ -0,0 +1,68 @@
1
+ module OpenAPIParser
2
+ class SpecValidator
3
+ class Rule
4
+ def self.rule_name
5
+ name
6
+ .split('::')
7
+ .last
8
+ .gsub(/([A-Z])/) { "_#{Regexp.last_match(1).downcase}" }
9
+ .sub(/^_/, '')
10
+ .to_sym
11
+ end
12
+
13
+ def initialize(version)
14
+ @version = version
15
+ end
16
+
17
+ # @return [Gem::Version, nil] declared OpenAPI version, nil when unknown
18
+ attr_reader :version
19
+
20
+ def check(_root)
21
+ raise NotImplementedError
22
+ end
23
+
24
+ # @param [String] boundary like '3.1'
25
+ # @return [Boolean] false when the version is unknown
26
+ def version_before?(boundary)
27
+ !version.nil? && version < Gem::Version.new(boundary)
28
+ end
29
+
30
+ # @param [String] boundary like '3.1'
31
+ # @return [Boolean] false when the version is unknown
32
+ def version_at_least?(boundary)
33
+ !version.nil? && version >= Gem::Version.new(boundary)
34
+ end
35
+
36
+ private
37
+
38
+ def violation(path:, message:)
39
+ OpenAPIParser::SpecViolation.new(
40
+ message: message,
41
+ path: path,
42
+ rule_name: self.class.rule_name,
43
+ )
44
+ end
45
+
46
+ def each_schema(root, &block)
47
+ return enum_for(:each_schema, root) unless block
48
+
49
+ visited = {}
50
+ walk(root, visited) do |node|
51
+ yield node if node.is_a?(OpenAPIParser::Schemas::Schema)
52
+ end
53
+ end
54
+
55
+ def walk(node, visited, &block)
56
+ return unless node.respond_to?(:_openapi_all_child_objects)
57
+ return if visited[node.object_id]
58
+
59
+ visited[node.object_id] = true
60
+ block.call(node)
61
+
62
+ node._openapi_all_child_objects.each_value do |child|
63
+ walk(child, visited, &block)
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,25 @@
1
+ module OpenAPIParser
2
+ class SpecValidator
3
+ module Rules
4
+ # `const` is a JSON Schema 2020-12 keyword adopted by OpenAPI 3.1.
5
+ # 3.0 does not recognize it. Detection inspects raw_schema so a
6
+ # literal `const: null` (deliberate) still flags.
7
+ class ConstIn30 < 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?('const')
14
+
15
+ violations << violation(
16
+ path: schema.object_reference,
17
+ message: '`const` is a 3.1 addition (from JSON Schema 2020-12); 3.0 has no equivalent',
18
+ )
19
+ end
20
+ violations
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,24 @@
1
+ module OpenAPIParser
2
+ class SpecValidator
3
+ module Rules
4
+ # `contentEncoding` is a JSON Schema 2020-12 annotation adopted by
5
+ # 3.1. Metadata only, no runtime side-effects.
6
+ class ContentEncodingIn30 < Rule
7
+ def check(root)
8
+ return [] unless version_before?('3.1')
9
+
10
+ violations = []
11
+ each_schema(root) do |schema|
12
+ next unless schema.raw_schema.is_a?(Hash) && schema.raw_schema.key?('contentEncoding')
13
+
14
+ violations << violation(
15
+ path: schema.object_reference,
16
+ message: '`contentEncoding` is a 3.1 addition (from JSON Schema 2020-12); 3.0 has no equivalent',
17
+ )
18
+ end
19
+ violations
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,25 @@
1
+ module OpenAPIParser
2
+ class SpecValidator
3
+ module Rules
4
+ # `contentMediaType` is a JSON Schema 2020-12 annotation adopted by
5
+ # 3.1. It is metadata, so it has no runtime side-effects; we only
6
+ # report version mismatches.
7
+ class ContentMediaTypeIn30 < 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?('contentMediaType')
14
+
15
+ violations << violation(
16
+ path: schema.object_reference,
17
+ message: '`contentMediaType` is a 3.1 addition (from JSON Schema 2020-12); 3.0 has no equivalent',
18
+ )
19
+ end
20
+ violations
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ module OpenAPIParser
2
+ class SpecValidator
3
+ module Rules
4
+ # `contentSchema` is a JSON Schema 2020-12 annotation adopted by 3.1.
5
+ # The parse layer accepts it permissively; this rule reports the
6
+ # version mismatch on 3.0 documents.
7
+ class ContentSchemaIn30 < 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?('contentSchema')
14
+
15
+ violations << violation(
16
+ path: schema.object_reference,
17
+ message: '`contentSchema` is a 3.1 addition (from JSON Schema 2020-12); 3.0 has no equivalent',
18
+ )
19
+ end
20
+ violations
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ module OpenAPIParser
2
+ class SpecValidator
3
+ module Rules
4
+ # `$dynamicAnchor` is the companion of `$dynamicRef` from JSON Schema
5
+ # 2020-12, declaring the dynamic resolution point. 3.0 has nothing
6
+ # equivalent; the rule flags it on 3.0 documents.
7
+ class DynamicAnchorIn30 < 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?('$dynamicAnchor')
14
+
15
+ violations << violation(
16
+ path: schema.object_reference,
17
+ message: '`$dynamicAnchor` is a 3.1 addition (from JSON Schema 2020-12); 3.0 has no equivalent',
18
+ )
19
+ end
20
+ violations
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ module OpenAPIParser
2
+ class SpecValidator
3
+ module Rules
4
+ # `$dynamicRef` is a JSON Schema 2020-12 referencing mechanism that
5
+ # 3.1 adopted. 3.0 does not recognize it. The parse layer keeps the
6
+ # value as raw schema data; this rule reports the version mismatch.
7
+ class DynamicRefIn30 < 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?('$dynamicRef')
14
+
15
+ violations << violation(
16
+ path: schema.object_reference,
17
+ message: '`$dynamicRef` is a 3.1 addition (from JSON Schema 2020-12); 3.0 only knows `$ref`',
18
+ )
19
+ end
20
+ violations
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ module OpenAPIParser
2
+ class SpecValidator
3
+ module Rules
4
+ # In 3.1 the singular `example` keyword on a Schema is deprecated in
5
+ # favor of the JSON Schema `examples` array. The field is still
6
+ # allowed but discouraged, so we report it as a violation.
7
+ class ExampleSingularDeprecation < Rule
8
+ def check(root)
9
+ return [] unless version_at_least?('3.1')
10
+
11
+ violations = []
12
+ each_schema(root) do |schema|
13
+ next unless schema.raw_schema.is_a?(Hash) && schema.raw_schema.key?('example')
14
+
15
+ violations << violation(
16
+ path: schema.object_reference,
17
+ message: 'singular `example` on a Schema is deprecated in 3.1; use the `examples` array',
18
+ )
19
+ end
20
+ violations
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,32 @@
1
+ module OpenAPIParser
2
+ class SpecValidator
3
+ module Rules
4
+ class ExclusiveMaximum < Rule
5
+ def check(root)
6
+ return [] if version.nil?
7
+
8
+ violations = []
9
+ each_schema(root) do |schema|
10
+ value = schema.exclusiveMaximum
11
+ next if value.nil?
12
+
13
+ if version_before?('3.1')
14
+ if value.is_a?(Numeric)
15
+ violations << violation(
16
+ path: schema.object_reference,
17
+ message: 'numeric exclusiveMaximum is a 3.1-only form; in 3.0 use a Boolean modifier paired with `maximum`',
18
+ )
19
+ end
20
+ elsif value == true || value == false
21
+ violations << violation(
22
+ path: schema.object_reference,
23
+ message: 'Boolean exclusiveMaximum is a 3.0-only form; in 3.1 use a standalone numeric bound',
24
+ )
25
+ end
26
+ end
27
+ violations
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,32 @@
1
+ module OpenAPIParser
2
+ class SpecValidator
3
+ module Rules
4
+ class ExclusiveMinimum < Rule
5
+ def check(root)
6
+ return [] if version.nil?
7
+
8
+ violations = []
9
+ each_schema(root) do |schema|
10
+ value = schema.exclusiveMinimum
11
+ next if value.nil?
12
+
13
+ if version_before?('3.1')
14
+ if value.is_a?(Numeric)
15
+ violations << violation(
16
+ path: schema.object_reference,
17
+ message: 'numeric exclusiveMinimum is a 3.1-only form; in 3.0 use a Boolean modifier paired with `minimum`',
18
+ )
19
+ end
20
+ elsif value == true || value == false
21
+ violations << violation(
22
+ path: schema.object_reference,
23
+ message: 'Boolean exclusiveMinimum is a 3.0-only form; in 3.1 use a standalone numeric bound',
24
+ )
25
+ end
26
+ end
27
+ violations
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,19 @@
1
+ module OpenAPIParser
2
+ class SpecValidator
3
+ module Rules
4
+ # `jsonSchemaDialect` is a 3.1 root-level addition; 3.0 has no
5
+ # equivalent.
6
+ class JsonSchemaDialectIn30 < Rule
7
+ def check(root)
8
+ return [] unless version_before?('3.1')
9
+ return [] unless root.raw_schema.is_a?(Hash) && root.raw_schema.key?('jsonSchemaDialect')
10
+
11
+ [violation(
12
+ path: '#/jsonSchemaDialect',
13
+ message: '`jsonSchemaDialect` is a 3.1 root-level addition; 3.0 documents have no such field',
14
+ )]
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,25 @@
1
+ module OpenAPIParser
2
+ class SpecValidator
3
+ module Rules
4
+ # `nullable` is a 3.0 keyword that 3.1 removed in favor of
5
+ # `type: [..., "null"]`. The field's mere presence on a 3.1 document
6
+ # is a spec violation, regardless of true/false.
7
+ class NullableDeprecation < Rule
8
+ def check(root)
9
+ return [] unless version_at_least?('3.1')
10
+
11
+ violations = []
12
+ each_schema(root) do |schema|
13
+ next unless schema.raw_schema.is_a?(Hash) && schema.raw_schema.key?('nullable')
14
+
15
+ violations << violation(
16
+ path: schema.object_reference,
17
+ message: '`nullable` was removed in 3.1; use `type: [..., "null"]` instead',
18
+ )
19
+ end
20
+ violations
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ module OpenAPIParser
2
+ class SpecValidator
3
+ module Rules
4
+ # `components.pathItems` is a 3.1 addition. The parse layer accepts it
5
+ # in 3.0 documents permissively; this rule reports the version
6
+ # mismatch.
7
+ class PathItemsIn30 < Rule
8
+ def check(root)
9
+ return [] unless version_before?('3.1')
10
+
11
+ components = root.components
12
+ return [] unless components
13
+
14
+ raw = components.raw_schema
15
+ return [] unless raw.is_a?(Hash) && raw.key?('pathItems')
16
+
17
+ [violation(
18
+ path: "#{components.object_reference}/pathItems",
19
+ message: '`components.pathItems` is a 3.1 addition; 3.0 documents should not declare it',
20
+ )]
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ module OpenAPIParser
2
+ class SpecValidator
3
+ module Rules
4
+ # `prefixItems` is JSON Schema 2020-12's positional tuple keyword.
5
+ # 3.1 adopts it; 3.0 has no equivalent and parsing it is a spec
6
+ # mismatch the validator should report.
7
+ class PrefixItemsIn30 < 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?('prefixItems')
14
+
15
+ violations << violation(
16
+ path: schema.object_reference,
17
+ message: '`prefixItems` is a 3.1 addition (from JSON Schema 2020-12); 3.0 has no equivalent',
18
+ )
19
+ end
20
+ violations
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ module OpenAPIParser
2
+ class SpecValidator
3
+ module Rules
4
+ # 3.1 lets `type` be an Array of primitive type names (e.g.
5
+ # `["string", "null"]`). 3.0 only allowed a single String, so the
6
+ # array form on a 3.0 document is a spec violation.
7
+ class TypeArrayIn30 < 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.type.is_a?(Array)
14
+
15
+ violations << violation(
16
+ path: schema.object_reference,
17
+ message: '`type` as an Array of primitive names is a 3.1 addition; 3.0 expects a single type String',
18
+ )
19
+ end
20
+ violations
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,26 @@
1
+ module OpenAPIParser
2
+ class SpecValidator
3
+ module Rules
4
+ # `type: "null"` was added by 3.1; 3.0 only allowed the original six
5
+ # primitive type names. The parse layer accepts the literal anyway,
6
+ # so we report the mismatch here. Array-form types like
7
+ # `["string", "null"]` are covered by a separate rule.
8
+ class TypeNullIn30 < Rule
9
+ def check(root)
10
+ return [] unless version_before?('3.1')
11
+
12
+ violations = []
13
+ each_schema(root) do |schema|
14
+ next unless schema.type == 'null'
15
+
16
+ violations << violation(
17
+ path: schema.object_reference,
18
+ message: '`type: "null"` is a 3.1 addition; 3.0 documents have no such primitive',
19
+ )
20
+ end
21
+ violations
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,18 @@
1
+ module OpenAPIParser
2
+ class SpecValidator
3
+ module Rules
4
+ # `webhooks` is a 3.1 root-level addition; 3.0 has no equivalent.
5
+ class WebhooksIn30 < Rule
6
+ def check(root)
7
+ return [] unless version_before?('3.1')
8
+ return [] unless root.raw_schema.is_a?(Hash) && root.raw_schema.key?('webhooks')
9
+
10
+ [violation(
11
+ path: '#/webhooks',
12
+ message: '`webhooks` is a 3.1 root-level addition; 3.0 documents have no such field',
13
+ )]
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,11 @@
1
+ module OpenAPIParser
2
+ class SpecValidator
3
+ SpecViolation = Struct.new(:message, :path, :rule_name, keyword_init: true) do
4
+ def to_s
5
+ "[#{rule_name}] #{path}: #{message}"
6
+ end
7
+ end
8
+ end
9
+
10
+ SpecViolation = SpecValidator::SpecViolation
11
+ end
@@ -0,0 +1,85 @@
1
+ require_relative 'spec_validator/spec_violation'
2
+ require_relative 'spec_validator/rule'
3
+ require_relative 'spec_validator/rules/exclusive_minimum'
4
+ require_relative 'spec_validator/rules/exclusive_maximum'
5
+ require_relative 'spec_validator/rules/content_encoding_in_30'
6
+ require_relative 'spec_validator/rules/content_media_type_in_30'
7
+ require_relative 'spec_validator/rules/prefix_items_in_30'
8
+ require_relative 'spec_validator/rules/json_schema_dialect_in_30'
9
+ require_relative 'spec_validator/rules/type_array_in_30'
10
+ require_relative 'spec_validator/rules/path_items_in_30'
11
+ require_relative 'spec_validator/rules/nullable_deprecation'
12
+ require_relative 'spec_validator/rules/example_singular_deprecation'
13
+ require_relative 'spec_validator/rules/type_null_in_30'
14
+ require_relative 'spec_validator/rules/webhooks_in_30'
15
+ require_relative 'spec_validator/rules/const_in_30'
16
+ require_relative 'spec_validator/rules/dynamic_ref_in_30'
17
+ require_relative 'spec_validator/rules/dynamic_anchor_in_30'
18
+ require_relative 'spec_validator/rules/content_schema_in_30'
19
+
20
+ module OpenAPIParser
21
+ class SpecViolationError < OpenAPIError
22
+ attr_reader :violations
23
+
24
+ def initialize(violations)
25
+ @violations = violations
26
+ super(nil)
27
+ end
28
+
29
+ def message
30
+ @violations.map(&:to_s).join("\n")
31
+ end
32
+ end
33
+
34
+ class SpecValidator
35
+ def self.run(root)
36
+ new(root).run
37
+ end
38
+
39
+ def self.run!(root, policy:)
40
+ return if policy == :silent
41
+
42
+ violations = run(root)
43
+ return if violations.empty?
44
+
45
+ case policy
46
+ when :warn
47
+ violations.each { |v| warn(v.to_s) }
48
+ when :raise
49
+ raise OpenAPIParser::SpecViolationError.new(violations)
50
+ end
51
+ end
52
+
53
+ def initialize(root)
54
+ @root = root
55
+ @version = root.openapi_version
56
+ end
57
+
58
+ def run
59
+ rules.flat_map { |klass| klass.new(@version).check(@root) }
60
+ end
61
+
62
+ private
63
+
64
+ def rules
65
+ [
66
+ Rules::ExclusiveMinimum,
67
+ Rules::ExclusiveMaximum,
68
+ Rules::ContentEncodingIn30,
69
+ Rules::ContentMediaTypeIn30,
70
+ Rules::PrefixItemsIn30,
71
+ Rules::JsonSchemaDialectIn30,
72
+ Rules::TypeArrayIn30,
73
+ Rules::PathItemsIn30,
74
+ Rules::NullableDeprecation,
75
+ Rules::ExampleSingularDeprecation,
76
+ Rules::TypeNullIn30,
77
+ Rules::WebhooksIn30,
78
+ Rules::ConstIn30,
79
+ Rules::DynamicRefIn30,
80
+ Rules::DynamicAnchorIn30,
81
+ Rules::ContentSchemaIn30,
82
+ ]
83
+ end
84
+ end
85
+ end
@@ -1,3 +1,3 @@
1
1
  module OpenAPIParser
2
- VERSION = '2.3.1'.freeze
2
+ VERSION = '2.4.0'.freeze
3
3
  end
@@ -15,6 +15,7 @@ require 'openapi_parser/request_operation'
15
15
  require 'openapi_parser/schema_validator'
16
16
  require 'openapi_parser/parameter_validator'
17
17
  require 'openapi_parser/reference_expander'
18
+ require 'openapi_parser/spec_validator'
18
19
 
19
20
  module OpenAPIParser
20
21
  class << self
@@ -99,6 +100,8 @@ module OpenAPIParser
99
100
  path_item.set_path_item_to_operation
100
101
  end
101
102
 
103
+ OpenAPIParser::SpecValidator.run!(root, policy: config.strict_specification_version)
104
+
102
105
  root
103
106
  end
104
107
  end
@@ -15,6 +15,8 @@ module OpenAPIParser
15
15
  def expand_reference: -> bool
16
16
  def strict_response_validation: -> bool
17
17
  def strict_reference_validation: -> bool
18
+ KNOWN_STRICT_SPECIFICATION_VERSION_VALUES: Array[Symbol]
19
+ def strict_specification_version: -> (:silent | :warn | :raise)
18
20
  def validate_header: -> bool
19
21
  def request_validator_options: -> OpenAPIParser::SchemaValidator::Options
20
22
  def response_validate_options: -> OpenAPIParser::SchemaValidator::ResponseValidateOptions
@@ -27,6 +27,9 @@ module OpenAPIParser
27
27
 
28
28
  private
29
29
  def validator: (Object value, OpenAPIParser::Schemas::Schema schema) -> [OpenAPIParser::SchemaValidator::Base, OpenAPIParser::validate_error]
30
+ def validator_for_type: (String? type) -> OpenAPIParser::SchemaValidator::Base
31
+ def coerce_into_type_array?: (Object value, OpenAPIParser::Schemas::Schema schema) -> bool
32
+ def coerce_into_type_array: (Object value, OpenAPIParser::Schemas::Schema schema, **untyped) -> [Object, OpenAPIParser::validate_error]
30
33
  def string_validator: -> OpenAPIParser::SchemaValidator::StringValidator
31
34
  def integer_validator: -> OpenAPIParser::SchemaValidator::IntegerValidator
32
35
  def float_validator: -> OpenAPIParser::SchemaValidator::FloatValidator
@@ -0,0 +1,109 @@
1
+ module OpenAPIParser
2
+ class SpecViolationError < OpenAPIError
3
+ @violations: Array[OpenAPIParser::SpecValidator::SpecViolation]
4
+ attr_reader violations: Array[OpenAPIParser::SpecValidator::SpecViolation]
5
+
6
+ def initialize: (Array[OpenAPIParser::SpecValidator::SpecViolation] violations) -> void
7
+ def message: () -> String
8
+ end
9
+
10
+ class SpecValidator
11
+ @root: OpenAPIParser::Schemas::OpenAPI
12
+ @version: Gem::Version?
13
+
14
+ def self.run: (OpenAPIParser::Schemas::OpenAPI root) -> Array[SpecViolation]
15
+ def self.run!: (OpenAPIParser::Schemas::OpenAPI root, policy: (:silent | :warn | :raise)) -> void
16
+ def initialize: (OpenAPIParser::Schemas::OpenAPI root) -> void
17
+ def run: () -> Array[SpecViolation]
18
+ private def rules: () -> Array[singleton(Rule)]
19
+
20
+ class SpecViolation < ::Struct[untyped]
21
+ attr_accessor message: String
22
+ attr_accessor path: String
23
+ attr_accessor rule_name: Symbol
24
+ def to_s: () -> String
25
+ end
26
+
27
+ class Rule
28
+ @version: Gem::Version?
29
+ attr_reader version: Gem::Version?
30
+
31
+ def self.rule_name: () -> Symbol
32
+ def initialize: (Gem::Version? version) -> void
33
+ def check: (OpenAPIParser::Schemas::OpenAPI root) -> Array[SpecViolation]
34
+ def version_before?: (String boundary) -> bool
35
+ def version_at_least?: (String boundary) -> bool
36
+ private def violation: (path: String, message: String) -> SpecViolation
37
+ private def each_schema: (untyped root) ?{ (untyped) -> void } -> untyped
38
+ private def walk: (untyped node, Hash[Integer, bool] visited) { (untyped) -> void } -> void
39
+ end
40
+
41
+ module Rules
42
+ class ExclusiveMinimum < Rule
43
+ def check: (OpenAPIParser::Schemas::OpenAPI root) -> Array[SpecValidator::SpecViolation]
44
+ end
45
+
46
+ class ExclusiveMaximum < Rule
47
+ def check: (OpenAPIParser::Schemas::OpenAPI root) -> Array[SpecValidator::SpecViolation]
48
+ end
49
+
50
+ class ContentEncodingIn30 < Rule
51
+ def check: (OpenAPIParser::Schemas::OpenAPI root) -> Array[SpecValidator::SpecViolation]
52
+ end
53
+
54
+ class ContentMediaTypeIn30 < Rule
55
+ def check: (OpenAPIParser::Schemas::OpenAPI root) -> Array[SpecValidator::SpecViolation]
56
+ end
57
+
58
+ class PrefixItemsIn30 < Rule
59
+ def check: (OpenAPIParser::Schemas::OpenAPI root) -> Array[SpecValidator::SpecViolation]
60
+ end
61
+
62
+ class JsonSchemaDialectIn30 < Rule
63
+ def check: (OpenAPIParser::Schemas::OpenAPI root) -> Array[SpecValidator::SpecViolation]
64
+ end
65
+
66
+ class TypeArrayIn30 < Rule
67
+ def check: (OpenAPIParser::Schemas::OpenAPI root) -> Array[SpecValidator::SpecViolation]
68
+ end
69
+
70
+ class PathItemsIn30 < Rule
71
+ def check: (OpenAPIParser::Schemas::OpenAPI root) -> Array[SpecValidator::SpecViolation]
72
+ end
73
+
74
+ class NullableDeprecation < Rule
75
+ def check: (OpenAPIParser::Schemas::OpenAPI root) -> Array[SpecValidator::SpecViolation]
76
+ end
77
+
78
+ class ExampleSingularDeprecation < Rule
79
+ def check: (OpenAPIParser::Schemas::OpenAPI root) -> Array[SpecValidator::SpecViolation]
80
+ end
81
+
82
+ class TypeNullIn30 < Rule
83
+ def check: (OpenAPIParser::Schemas::OpenAPI root) -> Array[SpecValidator::SpecViolation]
84
+ end
85
+
86
+ class WebhooksIn30 < Rule
87
+ def check: (OpenAPIParser::Schemas::OpenAPI root) -> Array[SpecValidator::SpecViolation]
88
+ end
89
+
90
+ class ConstIn30 < Rule
91
+ def check: (OpenAPIParser::Schemas::OpenAPI root) -> Array[SpecValidator::SpecViolation]
92
+ end
93
+
94
+ class DynamicRefIn30 < Rule
95
+ def check: (OpenAPIParser::Schemas::OpenAPI root) -> Array[SpecValidator::SpecViolation]
96
+ end
97
+
98
+ class DynamicAnchorIn30 < Rule
99
+ def check: (OpenAPIParser::Schemas::OpenAPI root) -> Array[SpecValidator::SpecViolation]
100
+ end
101
+
102
+ class ContentSchemaIn30 < Rule
103
+ def check: (OpenAPIParser::Schemas::OpenAPI root) -> Array[SpecValidator::SpecViolation]
104
+ end
105
+ end
106
+ end
107
+
108
+ SpecViolation: singleton(SpecValidator::SpecViolation)
109
+ end
@@ -14,6 +14,8 @@ module OpenAPIParser
14
14
  module Schemas
15
15
  class OpenAPI
16
16
  def initialize: (Hash[bot, bot] hash, untyped config, uri: OpenAPIParser::readable_uri?, schema_registry: Hash[bot, bot]) -> OpenAPIParser::Schemas::OpenAPI
17
+ def openapi: () -> String?
18
+ def openapi_version: () -> Gem::Version?
17
19
  end
18
20
  end
19
21
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: openapi_parser
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.1
4
+ version: 2.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - ota42y
@@ -188,6 +188,7 @@ files:
188
188
  - lib/openapi_parser/schema_validator/options.rb
189
189
  - lib/openapi_parser/schema_validator/properties_number.rb
190
190
  - lib/openapi_parser/schema_validator/string_validator.rb
191
+ - lib/openapi_parser/schema_validator/type_mismatch_validator.rb
191
192
  - lib/openapi_parser/schema_validator/unspecified_type_validator.rb
192
193
  - lib/openapi_parser/schemas.rb
193
194
  - lib/openapi_parser/schemas/base.rb
@@ -207,6 +208,25 @@ files:
207
208
  - lib/openapi_parser/schemas/response.rb
208
209
  - lib/openapi_parser/schemas/responses.rb
209
210
  - lib/openapi_parser/schemas/schema.rb
211
+ - lib/openapi_parser/spec_validator.rb
212
+ - lib/openapi_parser/spec_validator/rule.rb
213
+ - lib/openapi_parser/spec_validator/rules/const_in_30.rb
214
+ - lib/openapi_parser/spec_validator/rules/content_encoding_in_30.rb
215
+ - lib/openapi_parser/spec_validator/rules/content_media_type_in_30.rb
216
+ - lib/openapi_parser/spec_validator/rules/content_schema_in_30.rb
217
+ - lib/openapi_parser/spec_validator/rules/dynamic_anchor_in_30.rb
218
+ - lib/openapi_parser/spec_validator/rules/dynamic_ref_in_30.rb
219
+ - lib/openapi_parser/spec_validator/rules/example_singular_deprecation.rb
220
+ - lib/openapi_parser/spec_validator/rules/exclusive_maximum.rb
221
+ - lib/openapi_parser/spec_validator/rules/exclusive_minimum.rb
222
+ - lib/openapi_parser/spec_validator/rules/json_schema_dialect_in_30.rb
223
+ - lib/openapi_parser/spec_validator/rules/nullable_deprecation.rb
224
+ - lib/openapi_parser/spec_validator/rules/path_items_in_30.rb
225
+ - lib/openapi_parser/spec_validator/rules/prefix_items_in_30.rb
226
+ - lib/openapi_parser/spec_validator/rules/type_array_in_30.rb
227
+ - lib/openapi_parser/spec_validator/rules/type_null_in_30.rb
228
+ - lib/openapi_parser/spec_validator/rules/webhooks_in_30.rb
229
+ - lib/openapi_parser/spec_validator/spec_violation.rb
210
230
  - lib/openapi_parser/version.rb
211
231
  - openapi_parser.gemspec
212
232
  - sig/openapi_parser.rbs
@@ -217,6 +237,7 @@ files:
217
237
  - sig/openapi_parser/schema_validators/base.rbs
218
238
  - sig/openapi_parser/schema_validators/options.rbs
219
239
  - sig/openapi_parser/schemas/base.rbs
240
+ - sig/openapi_parser/spec_validator.rbs
220
241
  - sig/openapi_parser/version.rbs
221
242
  - sig/types.rbs
222
243
  - sig/wip_types.rbs
@@ -238,7 +259,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
238
259
  - !ruby/object:Gem::Version
239
260
  version: '0'
240
261
  requirements: []
241
- rubygems_version: 3.6.9
262
+ rubygems_version: 4.0.3
242
263
  specification_version: 4
243
264
  summary: OpenAPI3 parser
244
265
  test_files: []