Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* `ServerNameBefore32`: detect Server Object `name` usage in pre-3.2 documents (3.2 addition)
* `ExampleValueFieldsBefore32`: detect Example Object `dataValue` / `serializedValue` usage in pre-3.2 documents (3.2 additions)
* validate a value against a schema without `type`: keywords such as `properties`, `required`, `items`, `maxLength`, `enum` and `minimum` now apply when the value has the type the keyword is for, as in JSON Schema. They were all skipped before, so a value that breaks them may now fail validation
* accept a file object for array-form `type` with `format: binary` (3.1) when `coerce_value` is off, the same way as a single `type: string`

## 2.5.0 (2026-10-21)
* add OpenAPI 3.2 `SpecValidator` rules
Expand Down
13 changes: 13 additions & 0 deletions lib/openapi_parser/schema_validator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,11 @@ def validator(value, schema)
return one_of_validator if schema.one_of
return nil_validator if value.nil?

# 3.1: `format: binary` with `string` in an array-form `type` accepts a
# non-String object such as an uploaded file, the same way as a single
# `type: string`. The string validator skips the check for `binary`.
return string_validator if binary_string_in_type_array?(value, schema)

# 3.1: pick the type from an Array whose primitive class matches the
# value, then dispatch as if it were a single-type schema. If nothing
# matches we still pick a candidate so the relevant validator emits
Expand Down Expand Up @@ -169,6 +174,14 @@ def validator_for_type(type)
end
end

# 3.1: a non-String value for an array-form `type` that lists `string`
# with `format: binary`, such as an uploaded file object.
def binary_string_in_type_array?(value, schema)
return false if value.is_a?(String)

schema.format == 'binary' && schema.type.is_a?(Array) && schema.type.include?('string')
end

# 3.1: when coercing, a value whose class matches no listed type is tried
# against each listed type, the same way a single `type` coerces it.
# Request params arrive as Strings ("5" for `type: [integer, "null"]`),
Expand Down
52 changes: 52 additions & 0 deletions spec/openapi_parser/schema_validator_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1194,4 +1194,56 @@ class ValidatableTest
end
end
end

describe 'array-form type with format: binary (3.1)' do
let(:options) { ::OpenAPIParser::SchemaValidator::Options.new }
let(:target) { { 'type' => ['string', 'null'], 'format' => 'binary' } }
let(:schema) do
raw = {
'openapi' => '3.1.0',
'info' => { 'title' => 'test', 'version' => '1.0' },
'paths' => {},
'components' => { 'schemas' => { 'Target' => target } },
}
OpenAPIParser.parse(raw, strict_reference_validation: false).components.schemas['Target']
end

context 'when an arbitrary object except String is given' do
it 'passes validation the same way as a single string type' do
value = ['b', 'i', 'n', 'a', 'r', 'y']
expect(OpenAPIParser::SchemaValidator.validate(value, schema, options)).to eq value
end
end

context 'when a String is given' do
it 'passes validation' do
expect(OpenAPIParser::SchemaValidator.validate('binary', schema, options)).to eq 'binary'
end
end

context 'when nil is given' do
it 'passes validation' do
expect(OpenAPIParser::SchemaValidator.validate(nil, schema, options)).to eq nil
end
end

context 'when string is not listed' do
let(:target) { { 'type' => ['integer', 'null'], 'format' => 'binary' } }

it 'raises a type-mismatch error' do
expect do
OpenAPIParser::SchemaValidator.validate(['b', 'i', 'n', 'a', 'r', 'y'], schema, options)
end.to raise_error(OpenAPIParser::ValidateError, /expected \["integer", "null"\]/)
end
end

context 'when coerce_value is on' do
let(:options) { ::OpenAPIParser::SchemaValidator::Options.new(coerce_value: true) }

it 'passes validation' do
value = ['b', 'i', 'n', 'a', 'r', 'y']
expect(OpenAPIParser::SchemaValidator.validate(value, schema, options)).to eq value
end
end
end
end
Loading