Skip to content

Support type: "null" for OpenAPI 3.1 schemas (runtime + TypeNullIn30 rule) - #197

Open
takayamaki wants to merge 4 commits into
ota42y:masterfrom
takayamaki:pr8-type-null-in-31
Open

Support type: "null" for OpenAPI 3.1 schemas (runtime + TypeNullIn30 rule)#197
takayamaki wants to merge 4 commits into
ota42y:masterfrom
takayamaki:pr8-type-null-in-31

Conversation

@takayamaki

Copy link
Copy Markdown
Contributor

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

OpenAPI 3.1 (via JSON Schema) adds "null" as a primitive type name.
In 3.0, nullability was expressed via the nullable keyword; type: "null" had no meaning.

This PR adds both runtime validation and version-mismatch detection for type: "null".

Runtime validation

# 3.1 document
schema:
  type: "null"
# nil → valid, "hello" → ValidateError

The accept side and the reject side live in two different validators,
because of how SchemaValidator#validator dispatches:

def validator(value, schema)
  # ...
  return nil_validator if value.nil?   # <- every nil value goes here,
                                       #    before the type dispatch below
  case schema.type
  # ...
  when 'null'
    null_type_validator                # <- only non-nil values can reach this
  # ...
end

Accepting nil

happens in the existing NilValidator, which handles all nil values regardless of schema type. It now treats schema.type == "null" as valid, exactly like the existing nullable: true path.

Rejecting everything else

happens in the new NullTypeValidator. Since nil values are routed to NilValidator before the type dispatch, any value reaching the when 'null' branch is by construction non-nil.
So NullTypeValidator unconditionally returns a validation error.
Without the new branch, type: "null" would fall through to UnspecifiedTypeValidator and accept any value.

SpecValidator rule

TypeNullIn30 walks all schemas and reports a violation when a 3.0 document uses type: "null".
Array-form types like ["string", "null"] are covered by a separate rule in a later PR.

OpenAPIParser.load(
  'spec.yaml',
  strict_specification_version: :warn,
)
# [TypeNullIn30] #/…/schema — `type: "null"` is a 3.1 addition; 3.0 documents have no such primitive

- 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.
@takayamaki
takayamaki marked this pull request as ready for review July 4, 2026 02:45
@geemus

geemus commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator

@takayamaki broadly I think this looks good. I guess my question would be around things like ["string", "null"]. Will this change their behavior, or would they be fine until the subsequent PR? Maybe we should at least add some basic tests here if we are deferring to capture the current state and avoid any potential regressions in the interim. What do you think?

@takayamaki

Copy link
Copy Markdown
Contributor Author

@geemus Thank you for review!

This PR does not change behavior for array-form types like type: ["string", "null"], only type: "null".
Arrays still fall through to unspecified_type_validator as before.

Actually, I have the array-type support ready and opened it as a stacked PR:
#198

Let me know if you'd still prefer the snapshot-test approach in this PR instead.

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.

2 participants