Skip to content

Flag nullable fields and drop options rows that repeat the type - #4381

Merged
akira28 merged 2 commits into
mainfrom
api-explorer/nullable-and-options-row
Oct 9, 2026
Merged

akira28 merged 2 commits into
mainfrom
api-explorer/nullable-and-options-row

Conversation

@akira28

@akira28 akira28 commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

API Explorer rows now show a muted "nullable" badge when a value can be null, and a union whose type already names its options no longer repeats them in a "One of:" row. On the Elasticsearch and Kibana pages, 3,894 rows gain the badge and 198 repeated options rows go, across 294 pages.

Affects: API reference

Prompt summary: The author noticed that max_score reads "union number | string" and then "One of: number or string", and asked whether that was a bug. Looking at it showed that the spec also allows null there, which the explorer drops. The author asked to fix the repetition and to show nullability, in one PR.

Why

A field such as max_score read "union number | string" and then "One of: number or string" below it; 193 rows on 64 pages repeated their type this way. The rule that hides such a row only covered X | X[] unions. Separately, the explorer drops null from types, so nothing told the reader that a value can be null: 107 schemas in the Elasticsearch spec and 1,053 in the Kibana spec allow it. That matters most in requests, where a required field may still accept null, for example to clear a value.

What

No options row that repeats the type

A union whose type already names exactly its options, in any order, has no "One of:" row. A row keeps it when it has more to say: a discriminator, a list of variants, or a type that names the union instead of its options, such as union NodeIds.

A nullable badge

A row whose value can be null shows "nullable" after "required" on the name line, in muted grey with a tooltip. A value is nullable when its type lists null, which also covers OpenAPI 3.0's nullable: true, or when any oneOf/anyOf branch allows it, nested unions included, as Elasticsearch's NullValue does. A oneOf with several branches that allow null still counts as nullable, since generated specs write nullable fields that way. A { "type": "null" } union member is no longer offered as an option. The Markdown export marks such fields "— nullable".

Where to look

Example Link
max_score: union number | string nullable, no options row open
A required field that is also nullable: error open

Verify

dotnet run --project tests/Elastic.ApiExplorer.Tests
# NullableTests
# BuildPropertyList_InlineUnionWithAlias_NamesTheAliasInItsType

Out of scope: query and path parameters do not get the badge; this PR covers body and response fields.

🤖 Generated with Claude Code

A field such as `max_score` read "union number | string" and then
"One of: number or string" below it, on 193 rows across 64 pages. A
union whose type already names exactly its options, with no
discriminator and no variants to list, no longer repeats them.

The explorer dropped `null` from types, so nothing said that a value
can be null, on 1,160 schemas in the Elasticsearch and Kibana specs. A
muted "nullable" badge now follows "required" on such rows, and the
Markdown export marks them too. A schema is nullable when its type
lists `null`, which also covers OpenAPI 3.0's `nullable: true`, or when
a union member allows it. A `{ "type": "null" }` union member is no
longer offered as an option.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@akira28
akira28 requested a review from a team as a code owner October 9, 2026 13:36
@akira28
akira28 requested a review from Mpdreamz October 9, 2026 13:36
Comment on lines +324 to 325

/// <summary>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Severity: P3 (Low)

Nested union branches that allow null are not recognized, so the explorer can omit the nullable badge for valid schemas.

Generated by Libra. React with 👍 or 👎 to give feedback on this comment.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in a4cef8e. Nullability now follows union branches all the way down, with a guard for unions that refer back to themselves. A null inside an inner oneOf/anyOf counts. On the Elasticsearch pages this adds the badge to predicted_value on the trained model inference response. BuildPropertyList_AnyUnionBranchAllowingNull_MakesTheFieldNullable covers the nested case.

Comment on lines +324 to 325

/// <summary>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Severity: P3 (Low)

A oneOf whose multiple branches accept null is incorrectly marked nullable even though oneOf rejects null when more than one branch matches.

Generated by Libra. React with 👍 or 👎 to give feedback on this comment.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True by the letter of JSON Schema, but I kept a oneOf nullable when any branch allows null, and documented why in AllowsNull. Applying the strict rule removed the badge from 56 rows in the Kibana timeline APIs. For example, dateRange.end is oneOf: [{type: [string, null]}, {type: [number, null]}]. That shape comes from a generator that writes oneOf for what is really an anyOf, and the API accepts null there. Readers take a missing badge to mean "never null", so the strict reading would mislead them on exactly those fields. The test asserts this case explicitly.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the clarification. I agree that this is not a strict JSON Schema interpretation of oneOf: null matching multiple branches would fail the exclusivity constraint. The implementation explicitly chooses the API-facing interpretation for the generated-spec shape you describe, and documents the trade-off in AllowsNull; the test also asserts that the two-nullable-branches case should display as nullable. Given that evidence, I consider this finding addressed by the documented behavior rather than a remaining defect. Implementation and rationale · Regression test

Generated by Libra. React with 👍 or 👎 to give feedback on this comment.

@github-actions

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

Docs preview (local build)

Handbook preview: https://docs-v3-preview.elastic.dev/elastic/docs-builder/pull/4381/

A union member that is itself a union with a `null` branch did not count,
so `predicted_value` on the trained model inference response lost its
nullable badge. Nullability now follows union branches all the way
down, with a guard for unions that refer back to themselves.

A `oneOf` counts as nullable when any branch allows `null`, as an
`anyOf` does. Read strictly, `null` matching two branches fails a
`oneOf`, but generated specs write `oneOf: [string | null, number |
null]` for fields the API accepts `null` for, such as the Kibana
timeline `dateRange.end`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@akira28
akira28 merged commit 7bb1d9a into main Oct 9, 2026
32 checks passed
@akira28
akira28 deleted the api-explorer/nullable-and-options-row branch October 9, 2026 14:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant