Skip to content

Feat/openapi 3.1 support#3

Open
AlinsRan wants to merge 34 commits intomasterfrom
feat/openapi-3.1-support
Open

Feat/openapi 3.1 support#3
AlinsRan wants to merge 34 commits intomasterfrom
feat/openapi-3.1-support

Conversation

@AlinsRan
Copy link
Copy Markdown
Owner

@AlinsRan AlinsRan commented Mar 26, 2026

Summary

Comprehensive OpenAPI 3.1 / JSON Schema 2020-12 support, building on the base implementation in getkin/kin-openapi#1125.

This PR adds the remaining implementation on top of that base: built-in validator enhancements, components/pathItems support, and anchor resolution.


What this PR does

1. components/pathItems — full implementation (OAS 3.1)

OAS 3.1 added components/pathItems for reusable path items referenced via $ref from paths.

  • Added PathItems map[string]*PathItem type with JSONLookup for JSON Pointer support
  • Added Components.PathItems PathItems field with full marshal/unmarshal/validate
  • UnmarshalJSON deletes "pathItems" from Extensions (no longer treated as unknown extension)
  • Components.Validate() iterates and validates each pathItem entry
  • Loader.ResolveRefsIn() now iterates components.PathItems and resolves $ref within each PathItem
  • MarshalYAML emits pathItems when non-empty
  • Docs regenerated

Tests added:

  • openapi3/loader_test.go: TestLoadComponentsPathItems — verifies loader resolves $ref in components/pathItems
  • routers/gorillamux/router_test.go: TestRouterComponentsPathItems — verifies router finds routes through components/pathItems $ref
  • openapi3filter/validate_request_test.go: TestValidateRequestComponentsPathItems — verifies request validation through components/pathItems $ref
  • openapi3filter/components_path_items_test.go: TestComponentsPathItemsDocValidate, TestComponentsPathItemsRefResolved, TestComponentsPathItemsRouterAndValidation
  • openapi3filter/components_path_items_maingo_test.go: TestComponentsPathItemsValidateRequestSimulateMainGo

2. Built-in validator enhancements (OAS 3.1 / JSON Schema 2020-12)

Features implemented directly in the built-in validator (schema.go) without requiring EnableJSONSchema2020():

prefixItems — positional tuple validation

  • visitJSONArray validates each array element against its corresponding prefixItems schema by position
  • items schema now applies only to elements beyond the prefixItems tuple length (correct 3.1 semantics; in 3.0, items applied to all elements)

patternProperties — pattern-based property validation

  • visitJSONObject compiles each pattern, matches against property names, and validates matched values
  • Tracks matchedPattern set for correct additionalProperties interaction

const: null — null value constraint (ConstIsSet flag)

  • Added ConstIsSet bool field (not serialized) that is set by UnmarshalJSON when "const" key is present
  • visitConstOperation skips only when !ConstIsSet && Const == nil, allowing const: null to correctly reject non-null values
  • Fixes ambiguity between "no const constraint" (Const == nil, ConstIsSet == false) and "must be null" (Const == nil, ConstIsSet == true)

Silent fallback fix

  • visitJSONWithJSONSchema previously silently passed validation if JSON Schema compilation failed
  • Now falls back to the built-in validator on compilation failure, ensuring validation is always applied

3. Loader enhancements

$anchor fragment resolution

  • Loader builds an anchorIndex map[string]*Schema during resolveSchemaRef
  • resolveComponent resolves bare fragment $refs (e.g., "#AnchorName") by looking up the anchor index
  • Enables schemas with $anchor to be referenced directly by anchor name

$dynamicAnchor indexing

  • Loader builds dynamicAnchorIndex map[string]*Schema alongside anchorIndex
  • Groundwork for full $dynamicRef resolution (not yet dereferenced)

Sibling $ref merge (3.1 only)

  • In OAS 3.1, keywords alongside $ref (e.g., description, title, default, nullable, example) are valid and must be applied
  • Added mergeSiblingFields() in loader; applied when doc.IsOpenAPI3_1() and sibling fields are present
  • Supported siblings: description, title, default, readOnly, writeOnly, deprecated, nullable, example, externalDocs

4. Format as annotation-only (3.1)

  • Per JSON Schema 2020-12, format is an annotation (not a validator)
  • visitJSONNumber and visitJSONString now skip format enforcement when useJSONSchema2020 is active
  • Prevents spurious validation errors for formats in 3.1 documents

OAS 3.1 Validation Feature Coverage

Feature Built-in validator JSON Schema 2020-12 Tests
prefixItems (tuple) ✅ implemented ✅ via santhosh-tekuri
patternProperties ✅ implemented ✅ via santhosh-tekuri partial (doc-level only)
const: null ✅ fixed (ConstIsSet) ✅ via santhosh-tekuri partial
$anchor resolution ✅ loader ✅ via santhosh-tekuri no dedicated test
$dynamicAnchor indexing ✅ indexed ✅ via santhosh-tekuri no dedicated test
Sibling $ref merge ✅ loader (3.1 only) n/a no dedicated test
Format annotation-only ✅ skipped in 3.1 ✅ via santhosh-tekuri no dedicated test
type arrays with null ✅ (base PR) ✅ via santhosh-tekuri
exclusiveMin/Max numeric ✅ (base PR) ✅ via santhosh-tekuri
if/then/else ❌ silently ignored ✅ via santhosh-tekuri ✅ (2020-12 path)
dependentRequired ❌ silently ignored ✅ via santhosh-tekuri ✅ (2020-12 path)
contains/minContains/maxContains ❌ silently ignored ✅ via santhosh-tekuri partial
unevaluatedItems ❌ silently ignored ✅ via santhosh-tekuri partial
unevaluatedProperties ❌ silently ignored ✅ via santhosh-tekuri partial
dependentSchemas ❌ silently ignored ✅ via santhosh-tekuri partial
propertyNames ❌ silently ignored ✅ via santhosh-tekuri partial
components/pathItems ✅ full n/a
$dynamicRef ❌ not dereferenced ✅ via santhosh-tekuri no test
Webhooks ✅ (base PR) n/a

Note on built-in vs JSON Schema 2020-12 validator: Features marked "silently ignored" in the built-in validator are fully supported when EnableJSONSchema2020() (per-schema) or EnableJSONSchema2020Validation() (document-level) is used. For OAS 3.1 documents, doc.Validate() auto-enables JSON Schema 2020-12 mode. The built-in validator handles the most common keywords (prefixItems, patternProperties, const: null) directly for use without the external dependency.


Known limitations (follow-up work)

  1. $dynamicRef not dereferenced$dynamicAnchor is indexed but $dynamicRef is not resolved during loading. Schemas using $dynamicRef for recursive polymorphism will not work correctly.
  2. Built-in validator does not evaluate if/then/else, contains, unevaluated*, dependentSchemas, propertyNames — these require EnableJSONSchema2020(). By design for now.
  3. patternProperties + additionalProperties: false edge caseadditionalProperties: false may incorrectly reject properties that match a pattern property when the built-in validator is used.
  4. contentMediaType/contentEncoding not validated — annotation-only per JSON Schema 2020-12 spec; no runtime validation.

Backward compatibility

  • 100% backward compatible with OpenAPI 3.0
  • ConstIsSet is a non-serialized field; existing schemas unaffected
  • PathItems type is additive; existing code using Components continues to work

reuvenharrison and others added 30 commits March 26, 2026 08:48
Add comprehensive OpenAPI 3.1 / JSON Schema 2020-12 support:

- Type arrays with null support (e.g., ["string", "null"])
- JSON Schema 2020-12 keywords: const, examples, prefixItems, contains,
  minContains, maxContains, patternProperties, dependentSchemas,
  propertyNames, unevaluatedItems, unevaluatedProperties
- Conditional keywords: if/then/else, dependentRequired
- ExclusiveBound union type for 3.0 (boolean) and 3.1 (numeric) exclusive bounds
- Webhooks support with $ref resolution
- Version detection helpers: IsOpenAPI3_0(), IsOpenAPI3_1()
- Info.Summary and License.Identifier fields
- JSON Schema 2020-12 validator via EnableJSONSchema2020()
- Document-level validation option: EnableJSONSchema2020Validation()
- Allow $ref alongside other keywords in 3.1 schemas
- Handle "null" type in schema validation
- Auto-detect 3.1 in cmd/validate

Co-Authored-By: Chance Kirsch <>
Co-Authored-By: RobbertDM <>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…, etc.)

The loader's resolveSchemaRef only resolved $ref in pre-3.1 fields (items,
properties, additionalProperties, not, allOf, anyOf, oneOf). References
inside the new OpenAPI 3.1 / JSON Schema 2020-12 fields were silently
left unresolved, causing nil Value pointers.

This adds ref resolution for: prefixItems, contains, patternProperties,
dependentSchemas, propertyNames, unevaluatedItems, unevaluatedProperties.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The OpenAPI-to-JSON-Schema transformation only recursed into pre-3.1
fields (properties, additionalProperties, items, not, oneOf, anyOf,
allOf). Nested schemas inside 3.1 fields with OpenAPI 3.0-isms like
nullable:true were not converted, causing incorrect validation.

This adds recursion into: prefixItems, contains, patternProperties,
dependentSchemas, propertyNames, unevaluatedItems,
unevaluatedProperties.

Also consolidates the properties/patternProperties/dependentSchemas
map iteration into a single loop.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Schema.Validate() (used by doc.Validate()) recursively validates
sub-schemas in Items, Properties, AdditionalProperties, etc. but did
not recurse into the new OpenAPI 3.1 / JSON Schema 2020-12 fields.
Invalid sub-schemas nested inside these fields went undetected.

This adds validation for: prefixItems, contains, patternProperties,
dependentSchemas, propertyNames, unevaluatedItems,
unevaluatedProperties.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Make paths optional in 3.1 (required only in 3.0)
- Add mutualTLS security scheme type validation
- Validate license url/identifier mutual exclusivity
- Enable JSON Schema 2020-12 validation in openapi3filter for 3.1 docs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add $id, $anchor, $dynamicRef, $dynamicAnchor identity keywords
- Add contentMediaType, contentEncoding, contentSchema vocabulary
- Add discriminator support for anyOf (was only oneOf)
- Validate jsonSchemaDialect as valid URI

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- IsEmpty(): add missing checks for PrefixItems, Contains, MinContains,
  MaxContains, PatternProperties, DependentSchemas, PropertyNames,
  UnevaluatedItems, UnevaluatedProperties, Examples
- JSONLookup(): add all 23 missing JSON Schema 2020-12 field cases
- validate(): relax items requirement for arrays when in 3.1 mode or
  when prefixItems is present
- transformOpenAPIToJSONSchema: clean up exclusiveMinimum/Maximum false,
  handle nullable:true without type field
- MarshalYAML: only emit paths when non-nil (valid in 3.1)
- visitConstOperation: use reflect.DeepEqual for json.Number comparison
- Webhooks validation: use componentNames() for deterministic ordering

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Rephrase text to not contain literal json struct tag syntax that
triggers the json/yaml tag consistency check.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Open issues are tracked in the PR getkin#1125 description instead.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add $comment keyword to Schema struct (MarshalYAML, UnmarshalJSON,
  IsEmpty, JSONLookup)
- Fix PrefixItems type from []*SchemaRef to SchemaRefs for consistency
  with OneOf/AnyOf/AllOf and JSON Pointer support
- Fix exclusiveBoundToBool data loss: preserve numeric bound value
  when converting OAS 3.1 exclusive bounds to OAS 2.0
- Auto-enable JSON Schema 2020-12 validation for OpenAPI 3.1 documents
  in doc.Validate() so library users don't need explicit opt-in
- Add ref resolution tests for if/then/else and contentSchema
- Add transform test for contentSchema with nullable nested schema
- Add validate test for contentSchema with invalid sub-schema
- Document breaking API changes in README (ExclusiveBound, PrefixItems)
- Regenerate docs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add $schema keyword to Schema struct for per-schema dialect declaration
- Add $defs keyword (Schemas map) for local reusable schema definitions,
  with full support: struct, marshal, unmarshal, IsEmpty, JSONLookup,
  validate (recurse), loader (resolve refs), transform (recurse)
- Fix jsonSchemaDialect URI validation to require a scheme
- Refactor discriminator resolution into shared helper to eliminate
  code duplication between oneOf and anyOf paths
- Regenerate docs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
OpenAPI 3.1 adds Const (any) and Examples ([]any) fields to Schema.
Like Enum/Default/Example, these can contain arbitrary JSON/YAML values
that pick up __origin__ metadata from the YAML loader. Strip it on
unmarshal to prevent false diffs and unexpected metadata in parsed values.

Adds TestOrigin_ConstAndExamplesStripped regression test.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The origin-tracking YAML loader injects __origin__ as a key inside
any map-valued field to record source location. However, typed Go maps
(map[string]*Encoding, map[string]*ServerVariable, map[string]*PathItem)
treat __origin__ as a real entry, causing false positive diffs when the
same spec is loaded from two different file paths.

Fix by deleting originKey from these three maps after JSON unmarshaling,
mirroring the existing pattern used for Extensions and the unmarshalStringMapP
helper already used by Content, Schemas, Headers, etc.

Affected:
- MediaType.Encoding (map[string]*Encoding)
- Server.Variables (map[string]*ServerVariable)
- T.Webhooks (map[string]*PathItem)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implement scoped OpenAPI 3.1 features for CGo validation library:

- P0-1: Fall back to built-in validator when JSON Schema 2020-12
  compilation fails (e.g. unresolved cross-schema $ref)
- P0-2: Merge $ref sibling keywords (description, title, default,
  example, nullable, etc.) for OpenAPI 3.1 documents only
- P1-3: Add patternProperties validation in visitJSONObject with
  proper regex matching and error handling
- P1-4: Add prefixItems positional validation in visitJSONArray
- P1-5: Fix const:null ambiguity via ConstIsSet field detection
  in UnmarshalJSON, supporting both JSON-parsed and programmatic schemas
- P1-6: Treat format as annotation-only in OpenAPI 3.1 mode
- P2-7: Add $anchor fragment resolution in loader
- P2-8: Add $dynamicAnchor indexing in loader

Also fixes:
- nullable transform uses []any instead of []string for jsonschema
  compiler compatibility
- Remove unsafe pointer-based validator cache that caused stale
  validator reuse across GC cycles

Ref: #2
- loader: reset anchorIndex/dynamicAnchorIndex in resetVisitedPathItemRefs to prevent anchor leaks on Loader reuse
- loader: set refPath on SchemaRef when resolving anchors, consistent with other resolution paths
- schema: fix ExclusiveBound.MarshalJSON to return []byte("null") instead of nil,nil
- schema: fix nullable handling in JSON Schema validator; only add null to type when type is explicitly set
- test: replace empty if-blocks with require.False/True assertions in issue230_test.go
- test: fix misplaced test function comments in origin_test.go
- test: canonicalize HTTP header keys in components_path_items_maingo_test.go
- docs: update doc.go, schema.go, schema_validation_settings.go comments; regenerate openapi3.txt
- schema: fix const:null handling — IsEmpty() and MarshalJSON() now use
  ConstIsSet flag so that an explicit 'const: null' is preserved and not
  treated as an absent keyword
- schema: return foundUnresolvedRef for unresolved prefixItems entries
  instead of silently continuing, matching the behavior of items/properties
- schema: patternProperties now uses settings.regexCompiler when set and
  returns foundUnresolvedRef for unresolved schema refs, matching the
  validator's standard behavior
Add an internal isOpenAPI31 flag to ValidationOptions, injected
automatically by T.Validate() for 3.1 documents. SecurityScheme.Validate()
now returns an error when mutualTLS is used in a non-3.1 context.

Addresses CodeRabbit comment #2991964095.
- CI: use require.ErrorContains instead of require.Contains(err.Error())
  in TestMutualTLSVersionGating
- openapi3/openapi3.go: enforce OAS 3.1 requires at least one of paths,
  webhooks, or components (spec §4.7.1)
- openapi2conv: fix effectiveMin/Max and exclusiveMinToBool/exclusiveMaxToBool
  so that {minimum:5, exclusiveMinimum:3} converts correctly (>= 5, not > 5)
- openapi3/schema_const_test.go: fix 'null const' test to use ConstIsSet:true
  and type:null so the const check is actually exercised
- openapi3/schema_jsonschema_validator_test.go: rename fallback test to
  CompilationFallback, document why fallback exists; fix
  TestBuiltInValidatorStillWorks first sub-test to omit EnableJSONSchema2020
- openapi3/schema_jsonschema_validator.go: add comment explaining why the
  fallback from newJSONSchemaValidator to built-in is intentional (internal
  document $refs cannot be resolved in standalone compilation)
- openapi3/schema.go: cache patternProperties regex via compiledPatterns
  sync.Map, mirroring the existing pattern field caching
…m JSON Schema validator

When formatValidationError creates SchemaError instances, it now attaches
the original value, schema, and customizeMessageError function from settings.
This ensures SchemaError.Error() prints correct schema/value details instead
of null, and that user-supplied SetSchemaErrorMessageCustomizer callbacks
are invoked for errors produced by the JSON Schema 2020-12 validator path.
Foo Bar added 4 commits April 1, 2026 10:54
Only index schema anchors when visiting canonical inline definitions
(component.Ref == ). This prevents sibling-merged  copies from
overwriting anchorIndex entries with per-reference overrides, and pre-indexes
component schema anchors before schema ref traversal to reduce forward-reference
order dependence within components.
When ConstIsSet is true and Const is nil, the legacy visitJSONNull path
now permits null values, matching visitConstOperation behavior and JSON
Schema 2020-12 validation. Added regression tests for const:null without
type in the built-in validator path.
…hemas

Only permit nil via the visitJSONNull const:null shortcut when schema does
not explicitly constrain nullability/type (Type == nil and Nullable == false).
Add regression test that type:string + const:null still rejects nil.
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