Skip to content

feat(validate): extract spec validation into a public 'validate' package#944

Merged
reuvenharrison merged 2 commits into
mainfrom
feat/extract-validate-to-public-package
May 26, 2026
Merged

feat(validate): extract spec validation into a public 'validate' package#944
reuvenharrison merged 2 commits into
mainfrom
feat/extract-validate-to-public-package

Conversation

@reuvenharrison

Copy link
Copy Markdown
Collaborator

What

Extracts the kin-openapi error → formatters.Finding mapping out of internal/validate.go into a new public validate package with one entry point:

package validate

// Validate validates the spec against the OpenAPI and JSON Schema rules
// and returns a flat list of findings.
func Validate(spec *openapi3.T, source string) formatters.Findings

This mirrors how diff.Get and checker.CheckBackwardCompatibility are publicly callable from any consumer; the validation logic now follows the same convention.

Why

oasdiff validate shipped in v1.16.0 (#894) but its kin-error → Finding mapping (rule IDs, severities, source locations, dedup-preferring-components, fingerprints) lives entirely in internal/validate.go. That locks ~800 lines of validation logic inside the CLI binary; anything else that wants the same structured output (oasdiff-service, third-party Go tools) has to either duplicate it or shell out to the CLI.

The immediate driver is oasdiff-service#200: the calculator at oasdiff.com/diff needs a /public/validate endpoint, and the cleanest way to wire that up is to call validate.Validate(spec, source) the same way the diff endpoint calls diff.Get(config, s1, s2). The longer-term driver is that any future Go consumer (a linter wrapper, an IDE extension, an enterprise pipeline tool) should be able to import oasdiff and call validate.Validate(...) without copying internals.

What moves vs stays

Moves to validate/validate.go (public):

  • mapKinErrors, flattenKinErrors, dedupePreferringComponents
  • severityForKinError, sectionForKinError, sectionFromField
  • pathOperationForKinError, unwrapContext
  • argsForKinError, lineForKinError, columnForKinError
  • locationForKinError, fieldLoc
  • ruleIDForKinError, joinFieldsForRuleID, ruleIDFromField
  • unknownValidationID const

All helpers stay unexported in the new package; only Validate is the public surface.

Stays in internal/validate.go (CLI plumbing):

  • getValidateCmd (cobra command + flag wiring)
  • runValidate (load spec via flags, call validate.Validate, route through formatter)
  • outputFindings (formatter lookup + render to stdout)

Behaviour is unchanged

All 471 lines of internal/validate_test.go exercise the full CLI command, not the internal helpers directly. They all pass against the lifted code without modification. No fixture changes, no output changes.

Full Go suite green:

ok  	github.com/oasdiff/oasdiff/checker
ok  	github.com/oasdiff/oasdiff/diff
ok  	github.com/oasdiff/oasdiff/internal
?   	github.com/oasdiff/oasdiff/validate   [no test files]
... (all others ok)

The new package has no tests of its own in this PR — the existing CLI tests cover every mapping path. Direct unit tests for validate.Validate(...) would be a useful follow-up but are not required for behaviour preservation.

Small incidental rename

internal/viper.go had a local helper named validate(IViper) that validated viper-loaded config values. The new package import would collide with that name (Go does not allow a package and a top-level identifier to share a name in the same file). Renamed it to validateViperConfig — descriptive of what it actually validates, no external API impact (it was a package-internal function).

What's next

  • oasdiff-service#200: add /public/validate endpoint that calls validate.Validate(...) and returns findings as JSON. Will pin to a main pseudoversion of this PR.
  • oasdiff/w3#227: add the calculator's fourth analysis mode (Validate). Depends on the service endpoint.

File count / line count

 3 files changed, 852 insertions(+), 815 deletions(-)
 create mode 100644 validate/validate.go

The +37 net is the public package's doc comment + one nil-spec guard in Validate (returns nil rather than panicking on a nil spec, since the public surface should be defensive).

@codecov-commenter

codecov-commenter commented May 26, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0.70093% with 425 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.56%. Comparing base (21e8eb4) to head (e20a786).

Files with missing lines Patch % Lines
validate/validate.go 0.00% 425 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #944      +/-   ##
==========================================
- Coverage   90.53%   88.56%   -1.98%     
==========================================
  Files         271      272       +1     
  Lines       16231    16235       +4     
==========================================
- Hits        14695    14378     -317     
- Misses        963     1321     +358     
+ Partials      573      536      -37     
Flag Coverage Δ
unittests 88.56% <0.70%> (-1.98%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

reuvenharrison and others added 2 commits May 26, 2026 20:32
The kin-openapi error mapping logic (rule IDs, severities, source
locations, fingerprints) has lived in internal/validate.go since
PR #894 introduced 'oasdiff validate'. That keeps it locked inside
the CLI; any other consumer (oasdiff-service, third-party tools) has
no way to call it.

Move the pure validation+mapping logic into a new 'validate' package
with one public entry point:

  func Validate(spec *openapi3.T, source string) formatters.Findings

This mirrors how diff.Get and checker.CheckBackwardCompatibility are
callable from any consumer rather than being CLI-internal.

What moves:
- mapKinErrors, flattenKinErrors, dedupePreferringComponents
- severityForKinError, sectionForKinError, sectionFromField
- pathOperationForKinError, unwrapContext
- argsForKinError, lineForKinError, columnForKinError
- locationForKinError, fieldLoc
- ruleIDForKinError, joinFieldsForRuleID, ruleIDFromField
- unknownValidationID const

What stays in internal/validate.go (CLI-only):
- getValidateCmd (cobra command + flags)
- runValidate (load spec, call validate.Validate, format output)
- outputFindings (formatter lookup + render)

Behaviour unchanged: the CLI command now calls validate.Validate(spec,
source) and threads the result through the same formatter logic. All
existing CLI validate tests (internal/validate_test.go, 471 lines)
pass without modification.

While here: renamed the local validate() helper in internal/viper.go
to validateViperConfig() since 'validate' was a generic name that
collided with the new package import. One call site updated.

Blocks: oasdiff-service#200, which will call validate.Validate from
the new /public/validate endpoint.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The 'mirrors how diff.Get and checker.CheckBackwardCompatibility are
callable...' paragraph was internal-project rationale, not useful to
a library user reading the package doc. Keep the first paragraph
(what the package does); drop the second.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@reuvenharrison reuvenharrison force-pushed the feat/extract-validate-to-public-package branch from 99605b8 to e20a786 Compare May 26, 2026 17:32
@reuvenharrison reuvenharrison merged commit 8a1984a into main May 26, 2026
14 checks passed
@reuvenharrison reuvenharrison deleted the feat/extract-validate-to-public-package branch May 26, 2026 17:36
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