Skip to content

ApiExplorer: fail the build on invalid supplemental files - #3936

Merged
reakaleek merged 16 commits into
mainfrom
cursor/8325fadd
Aug 27, 2026
Merged

ApiExplorer: fail the build on invalid supplemental files#3936
reakaleek merged 16 commits into
mainfrom
cursor/8325fadd

Conversation

@reakaleek

Copy link
Copy Markdown
Member

Why

  • An op-*.md or tag-*.md file can name a missing operation, tag, parameter, or request-body field, and the build still succeeds.
  • The OpenAPI content enrichment RFC requires those cases to fail at generate time. See docs-eng-team#729.

What

  • The generate step now fails unmatched op-*.md and tag-*.md files against the latest spec.
  • Unknown parameter and request-body keys in matched files are build errors.
  • Version-suffixed files are checked against that major. A base file for an operation that exists only in latest does not fail older-version generates.

Notes

  • Version-suffix merge remains #730.
  • Closest-match suggestions are not included.

Made with Cursor

Unmatched op/tag files and unknown parameter or request-body keys
must error at generate time so authors catch typos before publish.

Co-authored-by: Cursor <cursoragent@cursor.com>
@reakaleek
reakaleek marked this pull request as ready for review August 26, 2026 12:20
@reakaleek
reakaleek requested a review from a team as a code owner August 26, 2026 12:20
@reakaleek
reakaleek requested a review from Mpdreamz August 26, 2026 12:20

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Requesting changes: base supplemental operation overrides are not validated for non-main renders, so invalid keys can pass for versioned builds.


What is this? | From workflow: PR Review

Give us feedback! React with 🚀 if perfect, 👍 if helpful, 👎 if not.

Comment thread src/Elastic.ApiExplorer/Supplemental/ApiSupplementalValidator.cs Outdated
…y @github-actions)

Co-authored-by: Cursor <cursoragent@cursor.com>

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Requesting changes: unmatched base supplemental files are not enforced when no main moniker is rendered, so invalid op-*.md/tag-*.md files can still pass generation in that configuration.


What is this? | From workflow: PR Review

Give us feedback! React with 🚀 if perfect, 👍 if helpful, 👎 if not.

Comment thread src/Elastic.ApiExplorer/Supplemental/ApiSupplementalValidator.cs Outdated
…thub-actions)

Numeric-only products never hit the main unmatched check, so invalid op-/tag- files could slip through.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@reakaleek
reakaleek enabled auto-merge (squash) August 26, 2026 12:52

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Requesting changes: unmatched base supplemental validation can still be skipped when main is absent and the first rendered moniker is non-numeric.


What is this? | From workflow: PR Review

Give us feedback! React with 🚀 if perfect, 👍 if helpful, 👎 if not.

Comment thread src/Elastic.ApiExplorer/OpenApiGenerator.cs Outdated
…eview by @github-actions)

The unmatched flag was ignored when the first rendered version was not main or numeric.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Requesting changes: matched base supplemental override-key validation is skipped for non-main, non-numeric monikers, so invalid keys can still pass in that render path.


What is this? | From workflow: PR Review

Give us feedback! React with 🚀 if perfect, 👍 if helpful, 👎 if not.

Comment thread src/Elastic.ApiExplorer/Supplemental/ApiSupplementalValidator.cs Outdated
reakaleek and others added 2 commits August 26, 2026 15:34
Remove the moniker early return so unmatched and override checks are independent. Version-suffixed files stay numeric-only.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds build-time validation for API Explorer supplemental Markdown files against versioned OpenAPI specifications.

Changes:

  • Reports unmatched operation/tag supplemental files.
  • Validates parameter and request-body override keys.
  • Adds version-aware validation tests.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

File Description
ApiSupplementalValidationTests.cs Covers supplemental validation scenarios.
ApiSupplementalValidator.cs Implements supplemental-file validation.
ApiSupplementalDiscovery.cs Exposes operations for validation.
OpenApiGenerator.cs Runs validation during generation.
Suppressed comments (2)

src/Elastic.ApiExplorer/Supplemental/ApiSupplementalValidator.cs:56

  • This helper has six parameters, exceeding the project's four-parameter method limit and making the version-validation state difficult to pass consistently. Bundle the collected spec entities and validation services into a context/options record rather than extending this parameter list.
	private static void ValidateVersionSuffixed(
		IReadOnlyList<ApiSupplementalVersionedFile> versionSuffixed,
		int major,
		IReadOnlyDictionary<string, OpenApiOperation> operationsById,
		IReadOnlySet<string> tagSlugs,

src/Elastic.ApiExplorer/Supplemental/ApiSupplementalValidator.cs:117

  • This helper introduces five parameters, over the project's four-parameter limit. Passing a shared validation context containing the analyzer and collector alongside the file and operation would keep this validation path easier to extend.
	private static void ValidateOverrideKeys(
		IFileInfo file,
		OpenApiOperation operation,
		SchemaAnalyzer analyzer,
		IDiagnosticsCollector collector,

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +156 to +158
var schema = operation.RequestBody?.Content?.FirstOrDefault().Value?.Schema;
var properties = analyzer.GetSchemaProperties(schema);
return new HashSet<string>(properties?.Keys ?? [], StringComparer.OrdinalIgnoreCase);

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in 772ca70 — request-body override keys now collect nested, array-item, additionalProperties, and union field names, matching the renderer leaf-name lookup. Traversal stays in the validator; sharing the property-tree builder would be a larger change than this PR needs.

Comment on lines +15 to +19
public static void Validate(
ApiSupplementalDiscoveryResult discovery,
OpenApiDocument document,
IDiagnosticsCollector collector,
string moniker,

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in 4125d46 — Validate now takes discovery plus an ApiSupplementalValidationRequest. The private helpers at the other lines already exceeded four parameters before this PR and are left as-is.

Comment on lines 234 to 238
@@ -227,9 +236,13 @@ private async Task GenerateApiProduct(
OpenApiDocument openApiDocument,
ResolvedApiConfiguration? apiConfig,
IReadOnlyList<ApiVersionSwitcherItem> versionSwitcherItems,

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in 6000881 — GenerateApiProduct now takes an ApiProductGeneration record plus Cancel. Suffix, document, switcher items, moniker, and unmatched-file policy live on that record.

Comment on lines +202 to +206
private static CapturingDiagnosticsCollector Validate(
IDirectoryInfo folder,
OpenApiDocument document,
string moniker,
bool? emitUnmatchedBaseFiles = null)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in 99eadcb — default-latest helper is three parameters; the explicit unmatched-file flag is a four-parameter overload.

reakaleek and others added 4 commits August 26, 2026 15:51
…-pull-request-reviewer)

The renderer matches overrides by leaf name at any depth, so top-level-only validation was a false error.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…ew by @copilot-pull-request-reviewer)

Validate was over the four-parameter limit. Private helpers that already exceeded it are unchanged.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…ull-request-reviewer)

Keep the default-latest path at three parameters and the explicit unmatched-file flag at four.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…pull-request-reviewer)

GenerateApiProduct was over the four-parameter limit after this PR added moniker and unmatched-file policy.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

Suppressed comments (2)

Previously missed (1) — in code that hasn't changed since the last review.

src/Elastic.ApiExplorer/Supplemental/ApiSupplementalValidator.cs:131

  • Base supplemental files are validated against every generated major, but this error does not identify which version rejected the parameter. For a key present on main but absent from v8, the diagnostic only names op-*.md and the operation, leaving the failing spec ambiguous. Include the moniker/spec label in the message.

This issue also appears on line 141 of the same file.

					collector.EmitError(file, $"API supplemental: Parameter '{key}' not found in operation '{operationId}'");

src/Elastic.ApiExplorer/Supplemental/ApiSupplementalValidator.cs:141

  • This diagnostic also omits the version being validated. Since a base request-body override is checked against multiple specs, a field can be valid on latest and fail only on an older major without the message saying which one. Include the moniker/spec label in the error.
					collector.EmitError(file, $"API supplemental: Request body field '{key}' not found in operation '{operationId}'");

Comment on lines +125 to +126
EmitUnmatchedBaseFiles: versioned.Version.Moniker == "main"
|| (!hasMain && versioned.Version.Moniker == monikers[0])),

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in 1f41a6f — unmatched base files now run only on the declared latest version, and only if that document actually resolved. A failed main fetch no longer treats an older spec as latest.

…review by @copilot-pull-request-reviewer)

If main fails to fetch, do not treat an older resolved version as latest. That would fail base files for operations that exist only on main.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated no new comments.

Suppressed comments (3)

Previously missed (2) — in code that hasn't changed since the last review.

src/Elastic.ApiExplorer/Supplemental/ApiSupplementalValidator.cs:31

  • Version-suffixed tag validation rebuilds this set from every tag name, so it re-admits slugs that discovery deliberately classified as collisions. For tags such as foo bar and foo-bar, tag-foo-bar.v8.md passes validation even though it has no unique target; the equivalent base file is unmatched by ApiSupplementalDiscovery.IndexTags (lines 144–168). Exclude collision slugs before validating versioned files.
			var tagSlugs = new HashSet<string>(tagNames.Select(ApiUrlBuilder.TagSlug), StringComparer.Ordinal);

src/Elastic.ApiExplorer/Supplemental/ApiSupplementalValidator.cs:131

  • This error can be emitted while validating any generated major, but it does not identify that version. A base file may be valid for main and fail only for version 8, leaving identical file/operation diagnostics with no indication of which spec rejected the key. Include the current moniker/spec label in the message so the author can resolve version-specific failures.

This issue also appears on line 141 of the same file.

					collector.EmitError(file, $"API supplemental: Parameter '{key}' not found in operation '{operationId}'");

src/Elastic.ApiExplorer/Supplemental/ApiSupplementalValidator.cs:141

  • Like the parameter diagnostic, this request-body error omits the spec version being validated. Since a base supplemental file is checked against every matched major, the same field can be valid on main but fail on an older version; include the current moniker/spec label so the error identifies the incompatible spec.
					collector.EmitError(file, $"API supplemental: Request body field '{key}' not found in operation '{operationId}'");

reakaleek and others added 2 commits August 26, 2026 16:36
…opilot-pull-request-reviewer)

Version-suffixed tag checks now use the same unique-slug index as discovery, so tag-foo-bar.v8.md cannot match both foo bar and foo-bar.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…ilot-pull-request-reviewer)

A base file can be valid on main and fail on an older major. The diagnostic now says which spec rejected the key.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@reakaleek

Copy link
Copy Markdown
Member Author

Addressed the suppressed items from #3936 (review):

  • Versioned tag files now use discovery's unique-slug index, so a colliding slug such as tag-foo-bar.v8.md is an error: 33a5bb1
  • Parameter and request-body override errors now name the spec (the latest spec or version 8): f8d03e1

@reakaleek
reakaleek disabled auto-merge August 27, 2026 07:23
@reakaleek
reakaleek dismissed github-actions[bot]’s stale review August 27, 2026 07:24

review quota exceeded

@reakaleek
reakaleek merged commit 5ad02f3 into main Aug 27, 2026
31 of 32 checks passed
@reakaleek
reakaleek deleted the cursor/8325fadd branch August 27, 2026 07:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants