Skip to content

Commit 974dcb5

Browse files
florent-leborgnecursoragentkibanamachine
authored andcommitted
[OAS] Extend required-field fix script to detect defaultValue-based optional fields (#267449)
## Summary Extends `oas_docs/scripts/generate_required_field_fixes.js` to detect a second category of wrongly-required fields in the OAS bundles. **Root cause (new):** `schema.object/array/etc.` with a `defaultValue` option in `@kbn/config-schema` emits `default:` on the resolved OAS schema. This makes the field optional at runtime (callers may omit it and the server applies the default), but the OAS generator still lists it in the parent schema's `required` array. **Previously** the script only caught fields marked `x-oas-optional: true` (emitted by `schema.maybe()`). **Now** it also catches fields whose resolved schema has a `default:` value. Newly fixed fields include: - **Dashboard API:** `options` on `kbn-dashboard-data` (and the `PUT /api/dashboards/{id}` request body) - **Visualization API:** `orientation` on `gaugeShapeBullet`, `metricBarBackgroundChart`, `metricComplementaryBar` ## Test plan - [ ] Run `make api-docs-fix-required` and verify `required_field_fixes.overlays.yaml` no longer lists the above fields as required - [ ] Run `make api-docs` and verify the final `output/kibana.yaml` does not mark `options` as required in the dashboard schema Made with [Cursor](https://cursor.com) --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
1 parent 6075072 commit 974dcb5

2 files changed

Lines changed: 71 additions & 35 deletions

File tree

oas_docs/overlays/dashboards.overlays.yaml

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1946,13 +1946,3 @@ actions:
19461946
label: Delete a dashboard - Console
19471947
source: |
19481948
DELETE kbn:/api/dashboards/3c4b8e10-d57a-11ef-9a52-4f3c2a8d0e1b
1949-
1950-
# ──────────────────────────────────────────────
1951-
# Fix required fields incorrectly promoted by schema.maybe()
1952-
# ──────────────────────────────────────────────
1953-
- target: "$.components.schemas['Kibana_HTTP_APIs_kbn-dashboard-data']"
1954-
description: 'Remove x-oas-optional fields from required: query, refresh_interval, time_range'
1955-
update:
1956-
required:
1957-
- options
1958-
- title

oas_docs/scripts/generate_required_field_fixes.js

Lines changed: 71 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,17 @@
1313
* Auto-generates overlays/required_field_fixes.overlays.yaml from the merged
1414
* OAS bundles (output/kibana.yaml and output/kibana.serverless.yaml).
1515
*
16-
* Root cause: schema.maybe() in @kbn/config-schema emits x-oas-optional: true
17-
* on the resolved type schema, but the OAS generator still includes the field
18-
* in the parent schema's required array. This script detects every such
19-
* occurrence and emits a remove + update overlay action pair to fix it.
16+
* Detects two categories of wrongly-required fields and emits a remove + update
17+
* overlay action pair for each affected schema:
18+
*
19+
* 1. x-oas-optional fields — schema.maybe() in @kbn/config-schema emits
20+
* x-oas-optional: true on the resolved type schema, but the OAS generator
21+
* still includes the field in the parent schema's required array.
22+
*
23+
* 2. default-value fields — schema.object/array/etc. with a defaultValue option
24+
* emits a `default:` on the resolved schema, making the field optional at
25+
* runtime (callers may omit it and the server applies the default), but the
26+
* OAS generator still lists it in required.
2027
*
2128
* The generated file is committed and applied early in the api-docs-overlay
2229
* pipeline, before any hand-authored overlays run.
@@ -38,47 +45,80 @@ const OUTPUT_FILE = path.join(OAS_DOCS_DIR, 'overlays', 'required_field_fixes.ov
3845

3946
// ─── helpers ──────────────────────────────────────────────────────────────────
4047

48+
/**
49+
* Load an OAS spec from disk. Supports both YAML (.yaml/.yml) and JSON (.json).
50+
* Returns null if the file does not exist.
51+
*/
4152
function loadSpec(filePath) {
4253
if (!fs.existsSync(filePath)) return null;
43-
return yaml.load(fs.readFileSync(filePath, 'utf8'));
54+
const raw = fs.readFileSync(filePath, 'utf8');
55+
if (filePath.endsWith('.json')) return JSON.parse(raw);
56+
return yaml.load(raw);
4457
}
4558

4659
/**
47-
* Returns the schema name if a $ref points to an x-oas-optional component schema.
60+
* Returns true if a property schema is optional due to x-oas-optional: true
61+
* (emitted by schema.maybe()) on its resolved type.
4862
*
49-
* Limitation: this only detects optional fields that are expressed as a $ref to a
50-
* named component schema that carries x-oas-optional: true. It does NOT detect
51-
* x-oas-optional: true set inline on a property schema (i.e. no $ref). As of writing,
52-
* ~30 inline occurrences exist in the bundles (e.g. fleet package policy inputs around
53-
* line 29633 of kibana.yaml), but none sit inside a parent required array, so there is
54-
* no active bug to fix. If that changes, extend this function to also check
63+
* Note: only detects the $ref case. Inline x-oas-optional (no $ref) does exist
64+
* (~30 occurrences in the bundles) but none currently sit inside a parent required
65+
* array, so there is no active bug. If that changes, also check
5566
* propSchema['x-oas-optional'] directly.
5667
*/
57-
function optionalRefName(propSchema, optionalSchemas) {
58-
if (!propSchema?.$ref) return null;
68+
function isXOasOptional(propSchema, optionalSchemas) {
69+
if (!propSchema?.$ref) return false;
5970
const m = propSchema.$ref.match(/^#\/components\/schemas\/(.+)$/);
60-
return m && optionalSchemas.has(m[1]) ? m[1] : null;
71+
return m ? optionalSchemas.has(m[1]) : false;
72+
}
73+
74+
/**
75+
* Returns true if a property schema is optional because its resolved type
76+
* declares a default value (emitted by the defaultValue option in
77+
* @kbn/config-schema). Such fields can be omitted by callers; the server
78+
* applies the default. Checks both inline schemas and $ref targets.
79+
*/
80+
function hasDefaultValue(propSchema, defaultSchemas) {
81+
if (!propSchema) return false;
82+
if (propSchema.default !== undefined) return true;
83+
if (propSchema.$ref) {
84+
const m = propSchema.$ref.match(/^#\/components\/schemas\/(.+)$/);
85+
return m ? defaultSchemas.has(m[1]) : false;
86+
}
87+
return false;
6188
}
6289

6390
/** Walk a spec object recursively, collecting required-field bugs. */
6491
function collectBugs(spec) {
92+
const componentSchemas = spec.components?.schemas || {};
93+
94+
// Category 1: schemas marked x-oas-optional (from schema.maybe())
6595
const optionalSchemas = new Set();
66-
for (const [name, schema] of Object.entries(spec.components?.schemas || {})) {
96+
for (const [name, schema] of Object.entries(componentSchemas)) {
6797
if (schema['x-oas-optional'] === true) optionalSchemas.add(name);
6898
}
6999

100+
// Category 2: schemas that declare a default value (from defaultValue option)
101+
const defaultSchemas = new Set();
102+
for (const [name, schema] of Object.entries(componentSchemas)) {
103+
if (schema.default !== undefined) defaultSchemas.add(name);
104+
}
105+
70106
const bugs = []; // { jsonpath, buggyFields, correctRequired }
71107

72108
function walk(node, jsonpath) {
73109
if (!node || typeof node !== 'object' || Array.isArray(node)) return;
74110

75111
if (node.properties && Array.isArray(node.required) && node.required.length > 0) {
76-
const buggy = node.required.filter((f) =>
77-
optionalRefName(node.properties[f], optionalSchemas)
112+
const buggy = node.required.filter(
113+
(f) =>
114+
isXOasOptional(node.properties[f], optionalSchemas) ||
115+
hasDefaultValue(node.properties[f], defaultSchemas)
78116
);
79117
if (buggy.length > 0) {
80118
const correct = node.required.filter(
81-
(f) => !optionalRefName(node.properties[f], optionalSchemas)
119+
(f) =>
120+
!isXOasOptional(node.properties[f], optionalSchemas) &&
121+
!hasDefaultValue(node.properties[f], defaultSchemas)
82122
);
83123
bugs.push({ jsonpath, buggyFields: buggy, correctRequired: correct });
84124
}
@@ -117,11 +157,13 @@ function generateActions(bugs) {
117157
const target = toTarget(jsonpath);
118158
lines.push(` - target: "${target}.required"`);
119159
lines.push(
120-
` description: "Remove x-oas-optional fields from required: ${buggyFields.join(', ')}"`
160+
` description: "Remove wrongly-required fields (x-oas-optional or has default): ${buggyFields.join(
161+
', '
162+
)}"`
121163
);
122164
lines.push(' remove: true');
123165
lines.push(` - target: "${target}"`);
124-
lines.push(' description: "Restore required array without x-oas-optional fields"');
166+
lines.push(' description: "Restore required array without optional fields"');
125167
if (correctRequired.length === 0) {
126168
lines.push(' update:');
127169
lines.push(' required: []');
@@ -169,10 +211,14 @@ function buildOverlayFile(statefulBugs, serverlessBugs) {
169211
'# THIS FILE IS AUTO-GENERATED — DO NOT EDIT BY HAND',
170212
`# Generated by: node oas_docs/scripts/generate_required_field_fixes.js`,
171213
'#',
172-
'# Root cause: schema.maybe() in @kbn/config-schema emits x-oas-optional: true',
173-
'# on the resolved type schema, but the OAS generator still includes the field',
174-
'# in the parent required array. Each fix below uses remove + update because',
175-
"# the Bump overlay engine appends arrays on 'update' alone rather than replacing them.",
214+
'# Fixes two categories of wrongly-required fields in the OAS bundles:',
215+
'# 1. x-oas-optional — schema.maybe() emits x-oas-optional: true on the resolved',
216+
'# type schema, but the OAS generator still lists the field in required.',
217+
'# 2. default-value — schema.object/etc. with defaultValue emits default: on the',
218+
'# resolved schema (the field is optional at runtime), but the OAS generator',
219+
'# still lists it in required.',
220+
'# Each fix uses remove + update because the Bump overlay engine appends arrays',
221+
"# on 'update' alone rather than replacing them.",
176222
'#',
177223
`# ${allBugs.length} schema(s) affected.`,
178224
'',

0 commit comments

Comments
 (0)