Skip to content

Support array-form type for OpenAPI 3.1 schemas (runtime + TypeArrayIn30 rule) - #198

Draft
takayamaki wants to merge 8 commits into
ota42y:masterfrom
takayamaki:pr9-type-array-in-31-stacked
Draft

Support array-form type for OpenAPI 3.1 schemas (runtime + TypeArrayIn30 rule)#198
takayamaki wants to merge 8 commits into
ota42y:masterfrom
takayamaki:pr9-type-array-in-31-stacked

Conversation

@takayamaki

@takayamaki takayamaki commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Continuing the OpenAPI 3.1 work from #197 / #152.

Note: this PR is stacked on #197 and includes its commits.

The diff will shrink to the array-form changes once #197 is merged and this branch is rebased.
takayamaki/openapi_parser@pr8-type-null-in-31...pr9-type-array-in-31-stacked
Review #197 first.

OpenAPI 3.1 (via JSON Schema) allows type to be an array of primitive type names, e.g. type: ["string", "null"].
In 3.0, type was always a single string.

This PR adds both runtime validation and version-mismatch detection for the array form.

Runtime validation

# 3.1 document
schema:
  type: ["string", "null"]
# "hello" → valid, nil → valid, 42 → ValidateError

Dispatch works in two steps inside SchemaValidator#validator:

Accepting nil

As with type: "null" (#197), nil values are routed to NilValidator before the type dispatch.
It now also accepts nil when the array form includes "null".

Dispatching non-nil values

pick_array_type matches the value's Ruby class against each declared primitive
(String"string", Integer"integer", Numeric"number", …)
and dispatches to the corresponding single-type validator, so all existing per-type
validation (format, enum, coercion, …) applies unchanged.

If the value matches none of the declared types, the schema-level type mismatch is
reported via the validator introduced in #197 for non-nil values against type: "null".
Since it now serves both dispatch paths as an unconditional type-mismatch reporter,
this PR renames it from NullTypeValidator to TypeMismatchValidator.
Without this, array-form types would fall through to UnspecifiedTypeValidator and accept any value.

SpecValidator rule

TypeArrayIn30 walks all schemas and reports a violation when a 3.0 document uses the array form.

OpenAPIParser.load(
  'spec.yaml',
  strict_specification_version: :warn,
)
# [TypeArrayIn30] #/…/schema — `type` as an Array of primitive names is a 3.1 addition; 3.0 expects a single type String

- nil_validator now passes when schema declares `type: \"null\"` even
  without `nullable: true`
- NullTypeValidator handles non-nil values against `type: \"null\"`
  with a ValidateError so 3.1 enforces the singleton-null semantic
- Rules::TypeNullIn30 flags `type: \"null\"` on 3.0 documents
type: "null" on a 3.0 document warns and raises (3.0 has no such
primitive); the same type on a 3.1 document stays clean.
- Rules::TypeArrayIn30 flags array-form `type` on 3.0 documents
- nil_validator also passes when `type` is an Array containing "null"
- schema_validator picks the primitive in the Array that matches the
  value's class and dispatches to the existing per-primitive validator;
  values that match no primitive fall to NullTypeValidator which is now
  general-purpose for "no applicable type"
An Array-valued type on a 3.0 document warns and raises (3.0 expects a
single type String); the same form on a 3.1 document stays clean.
The validator unconditionally reports a type mismatch and now serves
two dispatch paths: non-nil values against `type: "null"`, and values
matching none of an array-form type's primitives. Name it for what it
does rather than its original null-only use case.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant