feat: OpenAPI 3.1 support with scoped validation enhancements#1
feat: OpenAPI 3.1 support with scoped validation enhancements#1AlinsRan merged 34 commits intoapi7:masterfrom
Conversation
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
|
Foo Bar seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account. You have signed the CLA already but the status is still pending? Let us recheck it. |
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdded OpenAPI 3.1 / JSON Schema 2020-12 support across the library: new ExclusiveBound type, many Schema fields and helpers, a JSON‑Schema 2020-12 validator path, enhanced loader/ref resolution and anchor indexing, Components.PathItems and webhooks, version detection helpers, and conditional runtime enabling of 2020-12 validation. Changes
Sequence Diagram(s)sequenceDiagram
participant Client as Client
participant Doc as openapi3.T
participant Schema as openapi3.Schema
participant JS as jsonschema.Compiler
Client->>Doc: doc.Validate(ctx, opts...)
Doc->>Doc: IsOpenAPI3_1()
alt OpenAPI 3.1 detected
Doc->>Doc: append EnableJSONSchema2020Validation()
end
Client->>Schema: Schema.VisitJSON(value, opts...)
Schema->>Schema: check useJSONSchema2020 option
alt JSON Schema 2020-12 enabled
Schema->>JS: newJSONSchemaValidator(transform(schema))
JS->>JS: compile schema (2020-12)
JS->>JS: validate(value)
JS-->>Schema: errors or nil
else built-in validator
Schema->>Schema: run built-in VisitJSON checks
Schema-->>Client: errors or nil
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 10
🧹 Nitpick comments (13)
openapi3/openapi3_version_test.go (2)
11-11: Package-levelctxvariable shadows local test contexts.Declaring
ctxat package level can cause confusion and potential issues if tests are run in parallel or if individual tests need different context configurations. Consider declaringctxlocally within each test function that needs it.♻️ Proposed refactor
-var ctx = context.Background() - func TestDocumentVersionDetection(t *testing.T) {Then add local
ctx := context.Background()in functions that need it, such as:func TestWebhooksField(t *testing.T) { // ... t.Run("validate webhooks", func(t *testing.T) { ctx := context.Background() // ... err := doc.Validate(ctx)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@openapi3/openapi3_version_test.go` at line 11, Remove the package-level ctx variable and instead declare a local context inside each test that needs it (e.g., add ctx := context.Background() at the top of TestWebhooksField and inside subtests that call doc.Validate(ctx)); update any references to the package-level ctx to use the local ctx so tests do not share or shadow a global context.
193-217: Test webhook operation is missingResponsesfield.The webhook
Postoperation at line 203-206 doesn't have aResponsesfield. While this works for theJSONLookuptest, it would fail validation ifdoc.Validate(ctx)were called on this document. This inconsistency could mask issues if the test is later extended.🧪 Proposed fix for consistency
doc := &T{ OpenAPI: "3.1.0", Info: &Info{ Title: "Test API", Version: "1.0.0", }, Paths: NewPaths(), Webhooks: map[string]*PathItem{ "test": { Post: &Operation{ Summary: "Test webhook", + Responses: NewResponses( + WithStatus(200, &ResponseRef{ + Value: &Response{ + Description: Ptr("OK"), + }, + }), + ), }, }, }, }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@openapi3/openapi3_version_test.go` around lines 193 - 217, The Post operation in TestJSONLookupWithWebhooks is missing a Responses value which will cause validation failures; update the Webhooks entry inside TestJSONLookupWithWebhooks so the Post Operation includes a minimal Responses (e.g., set Operation.Responses to a non-empty Responses instance such as NewResponses() or an explicit &Responses{...} with at least one response entry) so the constructed doc would also pass doc.Validate(ctx).openapi3/schema_const_test.go (2)
47-56: The "null const" test doesn't actually testconst: nullbehavior.The test sets
Const: nilwhich means "const not set" (per the comment), but the test name suggests it's testingconst: null. To properly test the JSON Schemaconst: nullcase where a value must equalnull, you would need to setConstIsSet: truealong withConst: nil.🧪 Proposed fix to properly test const:null
t.Run("null const", func(t *testing.T) { schema := &Schema{ - Type: &Types{"null"}, - Const: nil, + Type: &Types{"null"}, + Const: nil, + ConstIsSet: true, } - // nil const means "not set", so this should pass as empty schema + // const: null - value must be null err := schema.VisitJSON(nil) require.NoError(t, err) + + // Non-null value should fail + err = schema.VisitJSON("not null") + require.Error(t, err) + require.ErrorContains(t, err, "const") })🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@openapi3/schema_const_test.go` around lines 47 - 56, The test "null const" incorrectly sets Const: nil (which currently means "not set") instead of representing an explicit const:null; update the test to set ConstIsSet: true alongside Const: nil on the Schema instance (i.e., in the test that constructs &Schema{ Type: &Types{"null"}, Const: nil, ... } add ConstIsSet: true) so that VisitJSON(nil) exercises the code path for an explicit const:null constraint and the test name matches the behavior being validated.
58-68: Object const comparison may fail with JSON-decoded values.The
visitConstOperationimplementation usesreflect.DeepEqualwhich is type-sensitive. This test passes because bothConstand the validated value are in-memory Go map literals with identical types. However, if the input were decoded from actual JSON (where all objects becomemap[string]any), type mismatches in nested values could cause unexpected failures. Consider adding a test case that validates JSON-decoded input against the const.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@openapi3/schema_const_test.go` around lines 58 - 68, Add a test that exercises const comparison with JSON-decoded input: create the schema.Const by unmarshaling a JSON byte blob (e.g. []byte(`{"key":"value"}`) into a map[string]any) and then call Schema.VisitJSON with a value decoded from JSON (not a Go literal) to ensure visitConstOperation and Schema.VisitJSON correctly compare JSON-decoded maps; reference the Schema type, its VisitJSON method, and the visitConstOperation behavior when adding this new subtest (e.g., t.Run("object const from json", ...)) so the test fails if reflect.DeepEqual type-sensitivity causes issues.openapi3/schema_validate_31_test.go (1)
10-96: Consider adding test cases forIf/Then/Elsesub-schema validation.The test comprehensively covers most OpenAPI 3.1 sub-schema locations but omits
If,Then, andElsewhich are also validated recursively (peropenapi3/schema.go:1491-1520). For completeness, consider adding:🧪 Proposed addition for conditional sub-schemas
t.Run("if with invalid sub-schema", func(t *testing.T) { schema := &Schema{ If: &SchemaRef{Value: invalidSchema}, } err := schema.Validate(ctx) require.Error(t, err, "should detect invalid sub-schema in if") }) t.Run("then with invalid sub-schema", func(t *testing.T) { schema := &Schema{ Then: &SchemaRef{Value: invalidSchema}, } err := schema.Validate(ctx) require.Error(t, err, "should detect invalid sub-schema in then") }) t.Run("else with invalid sub-schema", func(t *testing.T) { schema := &Schema{ Else: &SchemaRef{Value: invalidSchema}, } err := schema.Validate(ctx) require.Error(t, err, "should detect invalid sub-schema in else") })🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@openapi3/schema_validate_31_test.go` around lines 10 - 96, Add three sub-tests to TestSchemaValidate31SubSchemas that exercise conditional subschemas: create schemas using If, Then, and Else set to &SchemaRef{Value: invalidSchema} and call schema.Validate(ctx) expecting an error; specifically add t.Run cases named "if with invalid sub-schema", "then with invalid sub-schema", and "else with invalid sub-schema" which each build a Schema with the respective field (If, Then, Else) pointing at invalidSchema and assert require.Error(t, err). This ensures the Schema.Validate method's recursive validation over If/Then/Else (see Schema.Validate) is covered.openapi3/schema_jsonschema_validator.go (3)
193-201: Silent fallback on compilation errors may hide issues.When
newJSONSchemaValidatorfails, the code silently falls back to the built-in validator. While this ensures validation continues, it could mask schema issues (like unresolved$refs mentioned in the PR objectives). Consider logging the compilation error at debug level or returning it wrapped in the fallback case.📝 Consider logging fallback reason
The fallback is intentional per the PR objectives, but adding observability would help users understand when the external validator couldn't be used:
func (schema *Schema) visitJSONWithJSONSchema(settings *schemaValidationSettings, value any) error { validator, err := newJSONSchemaValidator(schema) if err != nil { + // Log at debug level: compilation failed, falling back to built-in validator return schema.visitJSON(settings, value) } return validator.validate(value) }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@openapi3/schema_jsonschema_validator.go` around lines 193 - 201, The fallback in visitJSONWithJSONSchema silently ignores errors from newJSONSchemaValidator; update visitJSONWithJSONSchema to capture the error returned by newJSONSchemaValidator and either log the compilation error at debug/trace level or return a wrapped error when falling back to schema.visitJSON, so callers have observability into why the JSON Schema validator wasn't used (referencing newJSONSchemaValidator, visitJSONWithJSONSchema, schema.visitJSON and validator.validate to locate the logic).
17-47: Schema compilation creates new validator per call - consider caching.Each call to
visitJSONWithJSONSchemacreates a newjsonSchemaValidatorby marshaling the schema to JSON, transforming it, and compiling it. For schemas validated repeatedly, this could impact performance. The PR mentions removing an unsafesync.Mapcache; consider a safer caching strategy if performance becomes an issue.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@openapi3/schema_jsonschema_validator.go` around lines 17 - 47, The newJSONSchemaValidator function currently marshals, transforms and compiles a schema on every call (used by visitJSONWithJSONSchema), causing repeated expensive work; introduce a thread-safe cache keyed by a stable schema identifier (e.g., the marshaled schema bytes or a computed hash) to store and reuse *jsonSchemaValidator instances instead of recompiling each time; implement the cache as a package-level map protected by sync.RWMutex (or a concurrent-safe structure), ensure newJSONSchemaValidator checks the cache first, returns a cached validator when present, and only performs marshal/transform/compile and stores the result under the lock when missing, and keep the existing function names (newJSONSchemaValidator and visitJSONWithJSONSchema) as points to integrate the cache.
156-191: Error path construction may produce malformed paths.In
formatValidationError, the path construction logic at lines 158-163 has edge cases:
- If
parentPathis non-empty andpathis "/", the result is justparentPath, which is correct- If
parentPathis empty andpathis "/", the result is empty string, losing the root indicatorThis could make error messages less clear for root-level validation failures.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@openapi3/schema_jsonschema_validator.go` around lines 156 - 191, The path construction in formatValidationError builds "path" from verr.InstanceLocation and then combines it with parentPath but loses the root indicator when parentPath is empty and path == "/", producing an empty path; fix by normalizing and joining paths: compute path = "/" + strings.Join(verr.InstanceLocation, "/"), then if parentPath == "" keep path (and if path == "/" keep it as "/"), otherwise join parentPath and the child path without producing double slashes (e.g., strip trailing '/' from parentPath then append child path unless child path == "/"), so that root-level errors still show "/" and composed paths are well-formed; update any uses of path (error message and recursive formatValidationError calls) accordingly so SchemaError.Reason contains the corrected path.openapi3/schema.go (1)
2832-2867: PatternProperties regex compilation on every validation call.Each validation call that checks
patternPropertiescompiles regexes at runtime (line 2837). Unlike thepatternfield which usescompiledPatternssync.Map cache,patternPropertiespatterns are compiled fresh each time. For schemas validated frequently, this could impact performance.⚡ Consider caching compiled patternProperties regexes
You could use the same
compiledPatternssync.Map pattern used for thepatternfield:for pattern, ppRef := range schema.PatternProperties { if ppRef == nil || ppRef.Value == nil { continue } - re, err := regexp.Compile(intoGoRegexp(pattern)) + goPattern := intoGoRegexp(pattern) + cpiface, _ := compiledPatterns.Load(goPattern) + re, _ := cpiface.(*regexp.Regexp) + if re == nil { + var err error + re, err = regexp.Compile(goPattern) + if err != nil { + // handle error... + } + compiledPatterns.Store(goPattern, re) + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@openapi3/schema.go` around lines 2832 - 2867, The patternProperties loop currently compiles regexes on every validation pass (regexp.Compile(intoGoRegexp(pattern))); change it to reuse the existing compiledPatterns sync.Map cache used for the pattern field: attempt a Load for a key derived from the pattern (e.g., "patternProperties:"+pattern) and, if missing, compile via regexp.Compile(intoGoRegexp(pattern)) and store the compiled *regexp.Regexp in compiledPatterns; keep the current behavior when settings.patternValidationDisabled is true, and ensure the rest of the logic around re.MatchString(k) and subsequent visitJSON calls (ppRef.Value.visitJSON) and SchemaError creation remains unchanged.openapi3/schema_jsonschema_validator_test.go (2)
51-55: MissingEnableJSONSchema2020()in object validation test.Unlike other tests in
TestJSONSchema2020Validator_Basic, this object validation test callsVisitJSONwithoutEnableJSONSchema2020(). This means it's testing the built-in validator, not the JSON Schema 2020-12 validator. If this is intentional to verify both paths work, consider adding a comment or testing both explicitly.🔧 Proposed fix: Add EnableJSONSchema2020() for consistency
err := schema.VisitJSON(map[string]any{ "name": "John", "age": 30, - }) + }, EnableJSONSchema2020()) require.NoError(t, err)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@openapi3/schema_jsonschema_validator_test.go` around lines 51 - 55, The object validation call is invoking schema.VisitJSON(...) without enabling the JSON Schema 2020-12 validator, so it exercises the built-in validator instead of the intended TestJSONSchema2020Validator_Basic path; update the test to construct/use the schema with EnableJSONSchema2020() before calling schema.VisitJSON (or add an explicit additional assertion/comment if you intend to test both validators) so the object validation runs against the JSON Schema 2020-12 validator.
389-418: Test function name suggests 2020-12 testing but tests built-in validator.
TestBuiltInValidatorStillWorksis named to test the built-in validator, but line 395-396 and 416 useEnableJSONSchema2020(). This creates mixed testing. Consider either:
- Removing
EnableJSONSchema2020()calls to truly test the built-in validator- Renaming the test to reflect what's actually being tested
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@openapi3/schema_jsonschema_validator_test.go` around lines 389 - 418, The test TestBuiltInValidatorStillWorks mixes built-in and 2020-12 validator usage by passing EnableJSONSchema2020() into schema.VisitJSON calls; make it consistent by removing the EnableJSONSchema2020() option from the two VisitJSON calls (so both string and object subtests exercise the built-in validator), ensuring the test name matches behavior and referencing Schema.VisitJSON, TestBuiltInValidatorStillWorks, and EnableJSONSchema2020 in your change.openapi3/openapi3.go (2)
163-171: Clarify the purpose of deletingoriginKeyfrom Webhooks.Line 169 deletes
originKeyfromx.Webhooks. This appears to be cleaning up internal metadata from the webhooks map, but it differs from the pattern used elsewhere (deleting fromExtensions). IfWebhooksisnil, this could panic.🛡️ Add nil check for safety
// OpenAPI 3.1 fields delete(x.Extensions, "webhooks") delete(x.Extensions, "jsonSchemaDialect") if len(x.Extensions) == 0 { x.Extensions = nil } - delete(x.Webhooks, originKey) + if x.Webhooks != nil { + delete(x.Webhooks, originKey) + } *doc = T(x)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@openapi3/openapi3.go` around lines 163 - 171, The code deletes originKey from x.Webhooks without a nil check which can panic if Webhooks is nil; update the cleanup to mirror the Extensions pattern by verifying x.Webhooks is non-nil (and non-empty if you prefer) before calling delete(x.Webhooks, originKey), and if after deletion x.Webhooks is empty set it to nil; refer to x.Webhooks and originKey in openapi3.go when making this safe deletion.
320-329: Consider validatingjsonSchemaDialectagainst known dialects.The current validation checks that
jsonSchemaDialectis a valid absolute URI with a scheme, which is correct per spec. However, you might consider adding a warning or informational log if the dialect is not a recognized JSON Schema draft (e.g.,https://json-schema.org/draft/2020-12/schema), as an unrecognized dialect could lead to unexpected validation behavior.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@openapi3/openapi3.go` around lines 320 - 329, The current OpenAPI 3.1 jsonSchemaDialect validation (the block that reads doc.JSONSchemaDialect, calls url.Parse and checks u.Scheme) should keep its existing absolute-URI checks but also compare doc.JSONSchemaDialect against a small whitelist of known JSON Schema dialect URIs (e.g., "https://json-schema.org/draft/2020-12/schema", "http://json-schema.org/draft-07/schema#", etc.); if the parsed value is not in that set, emit an informational warning (use the package's logger if available or fmt) noting the unrecognized dialect so callers are aware it may produce unexpected validation behavior. Ensure you reference doc.JSONSchemaDialect in the comparison and leave the existing url.Parse and u.Scheme error behavior unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.github/docs/openapi3.txt:
- Line 18: Replace the absolute phrase "100% backward compatibility" with a
qualified statement to avoid an absolute guarantee; locate the sentence
containing "100% backward compatibility" and change it to tempered wording such
as "maintains strong backward compatibility with OpenAPI 3.0" or "designed to be
largely backward-compatible with OpenAPI 3.0," optionally adding a short caveat
about potential edge-case differences.
- Around line 1985-1989: Update the EnableJSONSchema2020()
SchemaValidationOption docstring to state the fallback behavior: explain that
while enabling this option makes validation prefer the jsonschema library for
JSON Schema 2020-12 (enabling OpenAPI 3.1 features), if
compilation/initialization of jsonschema fails at runtime the system will
gracefully fall back to the built-in validator (and should emit a warning or log
entry); reference the EnableJSONSchema2020() name in the text so users know
where to expect this behavior.
- Around line 598-601: Fix the sentence and spec reference for ExclusiveBound:
rewrite the paragraph for clarity so it reads that ExclusiveBound represents
exclusiveMinimum/exclusiveMaximum and that the type changed between OpenAPI
versions — in OpenAPI 3.0 (JSON Schema Draft 4) it is a boolean that modifies
minimum/maximum, and in OpenAPI 3.1 (JSON Schema 2020-12) it is a number
representing the actual exclusive bound; update the text around the symbol
ExclusiveBound accordingly.
In `@openapi3/issue230_test.go`:
- Around line 492-513: Remove the two empty conditional blocks left in the test:
delete the no-op if statements that call doc30.IsOpenAPI3_1() and
doc31.IsOpenAPI3_1() (the empty "if doc30.IsOpenAPI3_1() { }" and "if
doc31.IsOpenAPI3_1() { }" blocks) inside the "automatic version detection and
configuration" test; if you intended to assert behavior instead, replace each
empty if with an appropriate require/assert call that checks the expected
boolean result of doc30.IsOpenAPI3_1() and doc31.IsOpenAPI3_1().
In `@openapi3/loader.go`:
- Around line 51-54: The Loader struct's anchorIndex and dynamicAnchorIndex maps
are not cleared between document loads, which can leak anchors across runs;
update the resetVisitedPathItemRefs() function to also reset/clear
Loader.anchorIndex and Loader.dynamicAnchorIndex (e.g., reassign empty maps or
delete entries) so both anchor indices are initialized per load; locate the
Loader type and ensure resetVisitedPathItemRefs() resets these maps in addition
to existing visited-path logic.
- Around line 385-393: When resolving an anchor via loader.anchorIndex in the
block that checks if schema, ok := loader.anchorIndex[fragment]; ok and sets
sr.Value = schema, also call the SchemaRef method setRefPath (e.g.,
sr.setRefPath(componentPath or the appropriate ref path) or otherwise set
sr.refPath) on the resolved *SchemaRef before returning componentDoc,
componentPath, nil so the refPath is populated consistently with other
resolution paths; update the branch that returns nil, nil, fmt.Errorf(...) only
after setting refPath when applicable.
In `@openapi3/origin_test.go`:
- Around line 439-441: The file has a stale/misplaced comment above
TestOrigin_ConstAndExamplesStripped that describes a different test
(TestOrigin_OriginExistsInProperties); update the comment to accurately describe
what TestOrigin_ConstAndExamplesStripped verifies (that originKey is stripped
from Const and Examples) or move the existing comment to precede
TestOrigin_OriginExistsInProperties; edit the comment text near the
TestOrigin_ConstAndExamplesStripped function to reflect the actual assertion and
ensure the original "verifies that loading fails when a specification contains a
property named '__origin__'" comment sits with
TestOrigin_OriginExistsInProperties.
In `@openapi3/schema_jsonschema_validator.go`:
- Around line 53-62: The current nullable handling mutates schema["type"] to
["null"] when nullable:true exists without a type, which incorrectly restricts
valid values; update the logic in the nullable branch that inspects schema (the
schema map, keys "nullable" and "type") so that: if schema["type"] is a string
convert to a []any containing the original type plus "null"; if schema["type"]
is an array ensure "null" is present (append only if missing); if there is no
schema["type"] do NOT set type to ["null"] — simply remove the "nullable" key
and leave the schema types unconstrained (or add "null" only when an explicit
type exists).
In `@openapi3/schema.go`:
- Around line 445-455: ExclusiveBound.MarshalJSON currently returns nil,nil when
eb.MarshalYAML() yields a nil value, which is invalid for JSON marshaling;
update ExclusiveBound.MarshalJSON to return a valid JSON representation instead
of nil (e.g., return []byte("null"), nil) or alternatively handle empty
ExclusiveBound at the caller so the field is omitted (ensure changes are made in
the ExclusiveBound.MarshalJSON function to stop returning nil,nil and return
valid JSON bytes or an error).
In `@openapi3/security_scheme.go`:
- Around line 175-176: The SecurityScheme validation currently accepts type
"mutualTLS" regardless of OpenAPI version; change validation so "mutualTLS" is
allowed only for OpenAPI 3.1+. To do this, propagate version context into
SecurityScheme.Validate (add a version parameter or context object) or perform
the gate in validateSecurityRequirement/Route.Spec where the document version is
available: detect if the document is < 3.1 and return a validation error when a
SecurityScheme with Type "mutualTLS" is present; update callers of
SecurityScheme.Validate accordingly (e.g., validateSecurityRequirement) to
supply the version/context and ensure error messages reference "mutualTLS" and
the required OpenAPI version.
---
Nitpick comments:
In `@openapi3/openapi3_version_test.go`:
- Line 11: Remove the package-level ctx variable and instead declare a local
context inside each test that needs it (e.g., add ctx := context.Background() at
the top of TestWebhooksField and inside subtests that call doc.Validate(ctx));
update any references to the package-level ctx to use the local ctx so tests do
not share or shadow a global context.
- Around line 193-217: The Post operation in TestJSONLookupWithWebhooks is
missing a Responses value which will cause validation failures; update the
Webhooks entry inside TestJSONLookupWithWebhooks so the Post Operation includes
a minimal Responses (e.g., set Operation.Responses to a non-empty Responses
instance such as NewResponses() or an explicit &Responses{...} with at least one
response entry) so the constructed doc would also pass doc.Validate(ctx).
In `@openapi3/openapi3.go`:
- Around line 163-171: The code deletes originKey from x.Webhooks without a nil
check which can panic if Webhooks is nil; update the cleanup to mirror the
Extensions pattern by verifying x.Webhooks is non-nil (and non-empty if you
prefer) before calling delete(x.Webhooks, originKey), and if after deletion
x.Webhooks is empty set it to nil; refer to x.Webhooks and originKey in
openapi3.go when making this safe deletion.
- Around line 320-329: The current OpenAPI 3.1 jsonSchemaDialect validation (the
block that reads doc.JSONSchemaDialect, calls url.Parse and checks u.Scheme)
should keep its existing absolute-URI checks but also compare
doc.JSONSchemaDialect against a small whitelist of known JSON Schema dialect
URIs (e.g., "https://json-schema.org/draft/2020-12/schema",
"http://json-schema.org/draft-07/schema#", etc.); if the parsed value is not in
that set, emit an informational warning (use the package's logger if available
or fmt) noting the unrecognized dialect so callers are aware it may produce
unexpected validation behavior. Ensure you reference doc.JSONSchemaDialect in
the comparison and leave the existing url.Parse and u.Scheme error behavior
unchanged.
In `@openapi3/schema_const_test.go`:
- Around line 47-56: The test "null const" incorrectly sets Const: nil (which
currently means "not set") instead of representing an explicit const:null;
update the test to set ConstIsSet: true alongside Const: nil on the Schema
instance (i.e., in the test that constructs &Schema{ Type: &Types{"null"},
Const: nil, ... } add ConstIsSet: true) so that VisitJSON(nil) exercises the
code path for an explicit const:null constraint and the test name matches the
behavior being validated.
- Around line 58-68: Add a test that exercises const comparison with
JSON-decoded input: create the schema.Const by unmarshaling a JSON byte blob
(e.g. []byte(`{"key":"value"}`) into a map[string]any) and then call
Schema.VisitJSON with a value decoded from JSON (not a Go literal) to ensure
visitConstOperation and Schema.VisitJSON correctly compare JSON-decoded maps;
reference the Schema type, its VisitJSON method, and the visitConstOperation
behavior when adding this new subtest (e.g., t.Run("object const from json",
...)) so the test fails if reflect.DeepEqual type-sensitivity causes issues.
In `@openapi3/schema_jsonschema_validator_test.go`:
- Around line 51-55: The object validation call is invoking
schema.VisitJSON(...) without enabling the JSON Schema 2020-12 validator, so it
exercises the built-in validator instead of the intended
TestJSONSchema2020Validator_Basic path; update the test to construct/use the
schema with EnableJSONSchema2020() before calling schema.VisitJSON (or add an
explicit additional assertion/comment if you intend to test both validators) so
the object validation runs against the JSON Schema 2020-12 validator.
- Around line 389-418: The test TestBuiltInValidatorStillWorks mixes built-in
and 2020-12 validator usage by passing EnableJSONSchema2020() into
schema.VisitJSON calls; make it consistent by removing the
EnableJSONSchema2020() option from the two VisitJSON calls (so both string and
object subtests exercise the built-in validator), ensuring the test name matches
behavior and referencing Schema.VisitJSON, TestBuiltInValidatorStillWorks, and
EnableJSONSchema2020 in your change.
In `@openapi3/schema_jsonschema_validator.go`:
- Around line 193-201: The fallback in visitJSONWithJSONSchema silently ignores
errors from newJSONSchemaValidator; update visitJSONWithJSONSchema to capture
the error returned by newJSONSchemaValidator and either log the compilation
error at debug/trace level or return a wrapped error when falling back to
schema.visitJSON, so callers have observability into why the JSON Schema
validator wasn't used (referencing newJSONSchemaValidator,
visitJSONWithJSONSchema, schema.visitJSON and validator.validate to locate the
logic).
- Around line 17-47: The newJSONSchemaValidator function currently marshals,
transforms and compiles a schema on every call (used by
visitJSONWithJSONSchema), causing repeated expensive work; introduce a
thread-safe cache keyed by a stable schema identifier (e.g., the marshaled
schema bytes or a computed hash) to store and reuse *jsonSchemaValidator
instances instead of recompiling each time; implement the cache as a
package-level map protected by sync.RWMutex (or a concurrent-safe structure),
ensure newJSONSchemaValidator checks the cache first, returns a cached validator
when present, and only performs marshal/transform/compile and stores the result
under the lock when missing, and keep the existing function names
(newJSONSchemaValidator and visitJSONWithJSONSchema) as points to integrate the
cache.
- Around line 156-191: The path construction in formatValidationError builds
"path" from verr.InstanceLocation and then combines it with parentPath but loses
the root indicator when parentPath is empty and path == "/", producing an empty
path; fix by normalizing and joining paths: compute path = "/" +
strings.Join(verr.InstanceLocation, "/"), then if parentPath == "" keep path
(and if path == "/" keep it as "/"), otherwise join parentPath and the child
path without producing double slashes (e.g., strip trailing '/' from parentPath
then append child path unless child path == "/"), so that root-level errors
still show "/" and composed paths are well-formed; update any uses of path
(error message and recursive formatValidationError calls) accordingly so
SchemaError.Reason contains the corrected path.
In `@openapi3/schema_validate_31_test.go`:
- Around line 10-96: Add three sub-tests to TestSchemaValidate31SubSchemas that
exercise conditional subschemas: create schemas using If, Then, and Else set to
&SchemaRef{Value: invalidSchema} and call schema.Validate(ctx) expecting an
error; specifically add t.Run cases named "if with invalid sub-schema", "then
with invalid sub-schema", and "else with invalid sub-schema" which each build a
Schema with the respective field (If, Then, Else) pointing at invalidSchema and
assert require.Error(t, err). This ensures the Schema.Validate method's
recursive validation over If/Then/Else (see Schema.Validate) is covered.
In `@openapi3/schema.go`:
- Around line 2832-2867: The patternProperties loop currently compiles regexes
on every validation pass (regexp.Compile(intoGoRegexp(pattern))); change it to
reuse the existing compiledPatterns sync.Map cache used for the pattern field:
attempt a Load for a key derived from the pattern (e.g.,
"patternProperties:"+pattern) and, if missing, compile via
regexp.Compile(intoGoRegexp(pattern)) and store the compiled *regexp.Regexp in
compiledPatterns; keep the current behavior when
settings.patternValidationDisabled is true, and ensure the rest of the logic
around re.MatchString(k) and subsequent visitJSON calls (ppRef.Value.visitJSON)
and SchemaError creation remains unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 5ba83605-300d-4872-8a4c-26f8c8b29267
⛔ Files ignored due to path filters (1)
go.sumis excluded by!**/*.sum
📒 Files selected for processing (35)
.github/docs/openapi3.txtREADME.mdcmd/validate/main.gogo.modopenapi2conv/openapi2_conv.goopenapi3/doc.goopenapi3/example_jsonschema2020_test.goopenapi3/example_validation.goopenapi3/info.goopenapi3/issue230_test.goopenapi3/license.goopenapi3/loader.goopenapi3/loader_31_conditional_test.goopenapi3/loader_31_schema_refs_test.goopenapi3/media_type.goopenapi3/openapi3.goopenapi3/openapi3_version_test.goopenapi3/origin_test.goopenapi3/refs.goopenapi3/refs.tmplopenapi3/schema.goopenapi3/schema_const_test.goopenapi3/schema_if_then_else_test.goopenapi3/schema_jsonschema_validator.goopenapi3/schema_jsonschema_validator_test.goopenapi3/schema_types_test.goopenapi3/schema_validate_31_test.goopenapi3/schema_validation_settings.goopenapi3/security_scheme.goopenapi3/server.goopenapi3/testdata/schema31_conditional.ymlopenapi3/testdata/schema31refs.ymlopenapi3/validation_options.goopenapi3filter/validate_request.goopenapi3filter/validate_response.go
There was a problem hiding this comment.
♻️ Duplicate comments (3)
.github/docs/openapi3.txt (3)
1986-1990:⚠️ Potential issue | 🟡 MinorDocument JSON Schema validator fallback behavior.
Lines 1989-1990 currently imply hard replacement of the built-in validator, but runtime behavior includes fallback when JSON Schema compilation fails.
Suggested edit
- When enabled, validation uses the jsonschema library instead of the built-in - validator. + When enabled, validation prefers the jsonschema library and may fall back + to the built-in validator if JSON Schema compilation fails.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.github/docs/openapi3.txt around lines 1986 - 1990, The docs for EnableJSONSchema2020 / SchemaValidationOption incorrectly state it strictly replaces the built-in validator; update the wording to document that enabling JSON Schema 2020-12 causes the system to attempt validation via the jsonschema library but will fall back to the built-in validator if JSON Schema compilation or initialization fails at runtime, and include a brief note about what kinds of errors trigger the fallback and that a warning/log is emitted when fallback occurs.
598-601:⚠️ Potential issue | 🟡 MinorFix
ExclusiveBoundparagraph grammar and OpenAPI 3.0 schema reference.Lines 598-601 are currently grammatically broken, and the 3.0 schema-basis text is inaccurate. This should be rewritten for clarity and spec accuracy.
Suggested edit
- ExclusiveBound represents exclusiveMinimum/exclusiveMaximum which changed - type between OpenAPI versions. In OpenAPI 3.0 (JSON Schema draft-04): - boolean that modifies minimum/maximum In OpenAPI 3.1 (JSON Schema 2020-12): - number representing the actual exclusive bound + ExclusiveBound represents exclusiveMinimum/exclusiveMaximum, whose type + changed between OpenAPI versions. + In OpenAPI 3.0, it is a boolean modifier for minimum/maximum. + In OpenAPI 3.1 (JSON Schema 2020-12), it is a numeric exclusive bound.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.github/docs/openapi3.txt around lines 598 - 601, Rewrite the "ExclusiveBound" paragraph to fix grammar and correct the schema reference: state that in OpenAPI 3.0 (based on JSON Schema draft-04) exclusiveMinimum/exclusiveMaximum are booleans that modify the adjacent minimum/maximum, and in OpenAPI 3.1 (based on JSON Schema 2020-12) exclusiveMinimum/exclusiveMaximum are numbers representing the actual exclusive bound; update the paragraph text for clarity and split into two clear sentences referencing ExclusiveBound, OpenAPI 3.0 (draft-04) and OpenAPI 3.1 (2020-12).
18-18:⚠️ Potential issue | 🟡 MinorAvoid absolute compatibility guarantee wording.
Line 18 uses “100% backward compatibility,” which is too absolute for public API docs and can become inaccurate with edge cases.
Suggested edit
-The implementation maintains 100% backward compatibility with OpenAPI 3.0. +The implementation is designed to preserve backward compatibility with OpenAPI 3.0.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.github/docs/openapi3.txt at line 18, Replace the absolute phrase "100% backward compatibility with OpenAPI 3.0" with non-absolute wording to avoid a guaranteed claim; locate the sentence containing the exact phrase "100% backward compatibility" and update it to a softer alternative such as "maintains strong backward compatibility with OpenAPI 3.0 in typical scenarios" or "aims to remain backward compatible with OpenAPI 3.0" and optionally add a short caveat about edge cases or potential differences.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In @.github/docs/openapi3.txt:
- Around line 1986-1990: The docs for EnableJSONSchema2020 /
SchemaValidationOption incorrectly state it strictly replaces the built-in
validator; update the wording to document that enabling JSON Schema 2020-12
causes the system to attempt validation via the jsonschema library but will fall
back to the built-in validator if JSON Schema compilation or initialization
fails at runtime, and include a brief note about what kinds of errors trigger
the fallback and that a warning/log is emitted when fallback occurs.
- Around line 598-601: Rewrite the "ExclusiveBound" paragraph to fix grammar and
correct the schema reference: state that in OpenAPI 3.0 (based on JSON Schema
draft-04) exclusiveMinimum/exclusiveMaximum are booleans that modify the
adjacent minimum/maximum, and in OpenAPI 3.1 (based on JSON Schema 2020-12)
exclusiveMinimum/exclusiveMaximum are numbers representing the actual exclusive
bound; update the paragraph text for clarity and split into two clear sentences
referencing ExclusiveBound, OpenAPI 3.0 (draft-04) and OpenAPI 3.1 (2020-12).
- Line 18: Replace the absolute phrase "100% backward compatibility with OpenAPI
3.0" with non-absolute wording to avoid a guaranteed claim; locate the sentence
containing the exact phrase "100% backward compatibility" and update it to a
softer alternative such as "maintains strong backward compatibility with OpenAPI
3.0 in typical scenarios" or "aims to remain backward compatible with OpenAPI
3.0" and optionally add a short caveat about edge cases or potential
differences.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 3b9d7ff0-65e0-4a36-bc7f-023fc943a409
📒 Files selected for processing (1)
.github/docs/openapi3.txt
There was a problem hiding this comment.
🧹 Nitpick comments (1)
openapi3/loader.go (1)
1108-1147: Consider documenting which sibling fields are intentionally supported.The
mergeSiblingFieldsfunction handles annotation siblings (description,title,default,readOnly,writeOnly,deprecated,example,examples,nullable) which aligns with JSON Schema's typical $ref sibling use cases. However, a brief comment explaining the rationale would clarify that structural/validation keywords are intentionally excluded.📝 Suggested documentation
func mergeSiblingFields(base *Schema, siblings map[string]any) *Schema { + // Merge annotation keywords that are commonly used as $ref siblings in OpenAPI 3.1. + // Structural/validation keywords are not merged as they would conflict with the referenced schema. merged := *base for k, v := range siblings {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@openapi3/loader.go` around lines 1108 - 1147, Add a brief explanatory comment above mergeSiblingFields describing that it intentionally only copies annotation siblings (description, title, default, readOnly, writeOnly, deprecated, example, examples, nullable) from the siblings map into a new Schema copy and deliberately excludes structural/validation keywords (e.g., type, properties, items, required, allOf, anyOf, etc.) so callers avoid merging validation into $ref targets; reference the function name mergeSiblingFields and the Schema type in the comment so readers know where this behavior is implemented and why.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@openapi3/loader.go`:
- Around line 1108-1147: Add a brief explanatory comment above
mergeSiblingFields describing that it intentionally only copies annotation
siblings (description, title, default, readOnly, writeOnly, deprecated, example,
examples, nullable) from the siblings map into a new Schema copy and
deliberately excludes structural/validation keywords (e.g., type, properties,
items, required, allOf, anyOf, etc.) so callers avoid merging validation into
$ref targets; reference the function name mergeSiblingFields and the Schema type
in the comment so readers know where this behavior is implemented and why.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: c258e0a7-d287-4321-8292-646154794978
📒 Files selected for processing (7)
.github/docs/openapi3.txtopenapi3/components.goopenapi3/loader.goopenapi3/loader_test.goopenapi3/testdata/components-path-items.ymlopenapi3filter/validate_request_test.gorouters/gorillamux/router_test.go
✅ Files skipped from review due to trivial changes (1)
- openapi3/loader_test.go
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@openapi3filter/components_path_items_maingo_test.go`:
- Around line 35-53: The test builds a headers map with lowercase keys so
ValidateRequestBody's req.Header.Get("Content-Type") returns empty; canonicalize
header names using http.CanonicalHeaderKey before attaching them to the request
and assign as an http.Header (convert the map[string][]string to http.Header) so
httpReq.Header.Get("Content-Type") works as expected; update the block that
fills "headers" and the assignment to "httpReq.Header" (refer to headers,
httpReq, and ValidateRequestBody/req.Header.Get) to perform the canonicalization
and proper type conversion.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 0c2ff51e-0767-4423-a8f0-4bd9be2a3a64
📒 Files selected for processing (2)
openapi3filter/components_path_items_maingo_test.goopenapi3filter/components_path_items_test.go
- 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
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
openapi3/schema.go (1)
1837-1843:⚠️ Potential issue | 🟠 MajorA matched
const: nullstill falls through to the old null check.Even when
visitConstOperation()acceptsnil, the nextcase nilstill delegates tovisitJSONNull(), which rejects unlessnullableortype: nullis set. So the compile-fallback path and any direct built-in validation still reject the only valid instance forconst: null.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@openapi3/schema.go` around lines 1837 - 1843, The code calls schema.visitConstOperation(settings, value) but only returns when it errors, so a successful const check (e.g., const: null) falls through to the subsequent nil case and gets rejected; change the flow to return immediately when visitConstOperation succeeds (i.e., if schema.visitConstOperation(...) returns nil then return nil/short-circuit) so that accepted const values (including const: null) are not re-validated by visitJSONNull or the later checks.
🧹 Nitpick comments (1)
.github/docs/openapi3.txt (1)
21-23: Clarify schema-level vs document-level 3.1 validation optionsThis snippet currently reads as the general OpenAPI 3.1 validation path, but it only shows schema-level validation (
VisitJSON+EnableJSONSchema2020()). Please add the document-level counterpart (doc.Validate(..., openapi3.EnableJSONSchema2020Validation())) here to avoid API misuse.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.github/docs/openapi3.txt around lines 21 - 23, The docs only show schema-level 3.1 validation via schema.VisitJSON(..., openapi3.EnableJSONSchema2020()), so add the document-level counterpart to avoid misuse: mention doc.Validate(doc, openapi3.WithXXX?, openapi3.EnableJSONSchema2020Validation()) (referencing the doc.Validate function and the openapi3.EnableJSONSchema2020Validation option) and explain briefly that VisitJSON + openapi3.EnableJSONSchema2020() is for schema-level checks while doc.Validate(..., openapi3.EnableJSONSchema2020Validation()) is the document-level validation path; include both examples side-by-side so readers know which API to call.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@openapi3/schema_jsonschema_validator.go`:
- Around line 137-140: The jsonSchemaValidator.validate path currently bypasses
per-validation OpenAPI flags in schemaValidationSettings; update
jsonSchemaValidator.validate (and related paths around lines noted) to accept
and honor schemaValidationSettings (or pass them into schema.Validate) so that
visitJSON behavior (readOnly/writeOnly checks, default injection, custom error
formatting) still runs for compiled 3.1 schemas; specifically, modify
jsonSchemaValidator.validate to accept a settings parameter (or reference
v.schemaValidationSettings) and ensure convertJSONSchemaError or the call into
v.schema.Validate uses those flags, mirroring the logic in schema.visitJSON so
that openapi3filter/validate_request.go options are applied for compiled
backends as well.
In `@openapi3/schema.go`:
- Around line 625-628: The marshal and emptiness checks currently ignore an
explicit "const: null" because they test schema.Const != nil; update
Schema.MarshalJSON, IsEmpty(), and any marshal gating logic to use the
ConstIsSet flag (set by UnmarshalJSON) instead of the pointer nil check so that
an explicit null is preserved and visitJSON/visitConstOperation see the keyword;
ensure Schema.MarshalJSON outputs "const": null when ConstIsSet is true even if
schema.Const == nil and adjust the related logic at places that previously gated
on schema.Const (e.g., where visitJSON short-circuits) to consult ConstIsSet.
- Around line 2833-2867: Rewrite the patternProperties loop to mirror the
validator's standard behavior: if ppRef is nil or ppRef.Value is nil return
foundUnresolvedRef(ppRef.Ref) instead of skipping; compile the pattern using
settings.regexCompiler (fall back to intoGoRegexp only if necessary) and respect
settings.patternValidationDisabled by continuing when compilation fails and that
flag is set; otherwise return a SchemaError with Origin set to the compiler
error; after successful compile use re.MatchString(k) and then call
ppRef.Value.visitJSON(settings, v) and handle errors the same way as other
visitors (respecting settings.failfast, markSchemaErrorKey, settings.multiError
and appending MultiError items). Ensure you update references to regexp.Compile
and intoGoRegexp to use settings.regexCompiler throughout.
- Around line 2651-2656: In VisitJSON's loop over schema.PrefixItems, do not
silently continue when a prefix-item reference is nil or its Value is nil;
instead return the same unresolved-ref error used for
items/properties/schema-definition paths (i.e., produce an "unresolved $ref"
style error referencing the prefixItems entry and index i) so callers can't
validate against unresolved refs without Schema.Validate having resolved them;
locate the loop using schema.PrefixItems and replace the continue branch (piRef
== nil || piRef.Value == nil) with returning the unresolved-ref error consistent
with other branches.
---
Outside diff comments:
In `@openapi3/schema.go`:
- Around line 1837-1843: The code calls schema.visitConstOperation(settings,
value) but only returns when it errors, so a successful const check (e.g.,
const: null) falls through to the subsequent nil case and gets rejected; change
the flow to return immediately when visitConstOperation succeeds (i.e., if
schema.visitConstOperation(...) returns nil then return nil/short-circuit) so
that accepted const values (including const: null) are not re-validated by
visitJSONNull or the later checks.
---
Nitpick comments:
In @.github/docs/openapi3.txt:
- Around line 21-23: The docs only show schema-level 3.1 validation via
schema.VisitJSON(..., openapi3.EnableJSONSchema2020()), so add the
document-level counterpart to avoid misuse: mention doc.Validate(doc,
openapi3.WithXXX?, openapi3.EnableJSONSchema2020Validation()) (referencing the
doc.Validate function and the openapi3.EnableJSONSchema2020Validation option)
and explain briefly that VisitJSON + openapi3.EnableJSONSchema2020() is for
schema-level checks while doc.Validate(...,
openapi3.EnableJSONSchema2020Validation()) is the document-level validation
path; include both examples side-by-side so readers know which API to call.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 46bd84be-101c-446c-89ed-2f6415f4d71e
📒 Files selected for processing (9)
.github/docs/openapi3.txtopenapi3/doc.goopenapi3/issue230_test.goopenapi3/loader.goopenapi3/origin_test.goopenapi3/schema.goopenapi3/schema_jsonschema_validator.goopenapi3/schema_validation_settings.goopenapi3filter/components_path_items_maingo_test.go
✅ Files skipped from review due to trivial changes (1)
- openapi3/doc.go
🚧 Files skipped from review as they are similar to previous changes (5)
- openapi3/origin_test.go
- openapi3/schema_validation_settings.go
- openapi3filter/components_path_items_maingo_test.go
- openapi3/loader.go
- openapi3/issue230_test.go
- 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
jarvis9443
left a comment
There was a problem hiding this comment.
Code Review Summary
This is a large PR (+4700/-114 across 43 files) adding OpenAPI 3.1 / JSON Schema 2020-12 support. The scope is ambitious and covers many features correctly. However, I found several significant issues that should be addressed before merging.
High Severity (3)
- Silent fallback masks schema compilation failures —
visitJSONWithJSONSchemasilently falls back to built-in validator when JSON Schema compilation fails, hiding real schema errors from users - $anchor resolution is order-dependent — forward references to anchors fail because the index is populated lazily during traversal
- Sibling-merged schema overwrites $anchor index entry —
mergeSiblingFieldscreates copies that pollute the anchor index with per-reference sibling overrides
Medium Severity (5)
- No caching for schema compilation — every validation call recompiles the JSON Schema, impacting performance in hot paths
- SchemaError fields not populated for JSON Schema path — degraded error output and broken custom formatters
- $anchor index is global across documents — cross-document anchor leakage violates JSON Schema 2020-12 scoping rules
- OAS 3→2 conversion wrong when both
minimumand numericexclusiveMinimumpresent — constraint semantics change during conversion - Missing OAS 3.1 document-level validation — no check that at least one of paths/webhooks/components is present
Test Issues (3)
- "null const" test never exercises const validation
- Fallback test never triggers the fallback code path
TestBuiltInValidatorStillWorksmostly tests the JSON Schema 2020-12 validator
See inline comments for details.
| validator, err := newJSONSchemaValidator(schema) | ||
| if err != nil { | ||
| return schema.visitJSON(settings, value) | ||
| } |
There was a problem hiding this comment.
Bug (High): Silent fallback masks schema compilation failures
When newJSONSchemaValidator fails (e.g., unresolved $ref, invalid schema constructs), this silently falls back to the built-in validator and discards the compilation error entirely.
This is particularly dangerous because:
- It's auto-enabled for all OpenAPI 3.1 users —
validate_request.go:243-244andvalidate_response.go:79automatically addEnableJSONSchema2020()for any 3.1 spec. - The two validators have different semantics. The JSON Schema 2020-12 validator handles keywords the built-in doesn't (
if/then/else,unevaluatedProperties,$dynamicRef). A silent fallback means schemas relying on these keywords silently stop enforcing constraints. - The compilation error itself likely indicates a real problem with the schema. Swallowing it means the user never learns their spec has issues.
The PR claims this change "falls back to built-in validator instead of swallowing errors" — but the fallback is swallowing the error.
Suggestion: Return the compilation error, or at minimum log it.
There was a problem hiding this comment.
The fallback is intentional and necessary for the current design: schemas in VisitJSON are compiled in isolation (standalone JSON object), so document-relative $refs like #/components/schemas/... cannot be resolved by the JSON Schema compiler. This is confirmed by TestIssue594 which fails if the fallback is removed — the sendgrid spec has schemas with internal $ref cross-references in examples. Added an explanatory comment in visitJSONWithJSONSchema (commit f56b22c) documenting why the fallback exists. Resolving this architecturally would require passing document context into the compiler, which is a separate effort.
|
|
||
| // visitJSONWithJSONSchema validates using the JSON Schema 2020-12 validator | ||
| func (schema *Schema) visitJSONWithJSONSchema(settings *schemaValidationSettings, value any) error { | ||
| validator, err := newJSONSchemaValidator(schema) |
There was a problem hiding this comment.
Performance (Medium): Schema recompiled on every validation call — no caching
Every call to visitJSONWithJSONSchema runs the full pipeline: json.Marshal → json.Unmarshal → transformOpenAPIToJSONSchema → jsonschema.NewCompiler() → compiler.Compile(). None of this is cached.
This sits directly in the request/response validation hot path. For an API gateway validating every request against an OpenAPI 3.1 spec, this means the full marshal+compile cycle runs for every parameter and body of every HTTP request.
The unsafe.Pointer cache was rightly removed, but the fix should be a safe caching mechanism (e.g., sync.Once on the Schema struct, or sync.Map keyed by *Schema directly — Go pointers to live objects are stable). The codebase already uses sync.Map for regex caching (compiledPatterns in schema.go:48).
There was a problem hiding this comment.
Deferred to a follow-up issue. Caching the compiled jsonschema.Schema on the *Schema struct (e.g., via sync.Once or sync.Map keyed by pointer) is the right approach but requires care around mutation and the fact that schema objects can be reused across requests. Tracking as a future optimization.
| } | ||
|
|
||
| // formatValidationError recursively formats validation errors | ||
| func formatValidationError(verr *jsonschema.ValidationError, parentPath string) error { |
There was a problem hiding this comment.
Bug (Medium): SchemaError fields not populated — degraded error output and broken custom formatters
formatValidationError creates SchemaError instances with only Reason (and optionally Origin) set. It never sets Value, Schema, SchemaField, or customizeMessageError.
Problems:
- Degraded error messages:
SchemaError.Error()appends schema/value details by default. For leaf errors this produces confusing output withnullwhere details should be. - Custom error formatters silently bypassed: Users can set
SetSchemaErrorMessageCustomizerviacustomSchemaErrorFuncinvalidate_request.go:240-241. The built-in validator propagates this to everySchemaError(30+ call sites). The JSON Schema path never readssettings.customizeMessageError, so custom formatters are silently ignored for all 3.1 validation.
Suggestion: Pass settings into formatValidationError and populate customizeMessageError, Value, and Schema fields.
There was a problem hiding this comment.
Fixed in commit ce1a9ca: formatValidationError now accepts value, schema, and settings parameters. Every SchemaError it creates gets Value, Schema, and customizeMessageError populated from those arguments. This ensures SchemaError.Error() prints the correct schema/value details instead of null, and that user-supplied SetSchemaErrorMessageCustomizer callbacks are invoked for errors from the JSON Schema 2020-12 validation path.
openapi3/loader.go
Outdated
| if loader.anchorIndex == nil { | ||
| loader.anchorIndex = make(map[string]*Schema) | ||
| } | ||
| loader.anchorIndex[value.Anchor] = value |
There was a problem hiding this comment.
Bug (High): $anchor resolution fails for forward references (order-dependent)
The anchorIndex is populated lazily during depth-first traversal in resolveSchemaRef (here at line 984). But resolveComponent checks the index at line 396. If a $ref: "#myAnchor" is encountered before the schema defining $anchor: "myAnchor" in traversal order, the anchor hasn't been indexed yet, and resolution fails.
Since componentNames() iterates schemas alphabetically, any anchor reference where the referencing schema name sorts before the anchor-defining schema name will fail. For example, schema A referencing an anchor defined by schema B always fails.
Suggestion: Build the anchor index in a separate pass before ref resolution begins, or use a deferred resolution approach.
There was a problem hiding this comment.
Partially addressed in commit d4828ce: component schema anchors are now pre-indexed before schema-ref traversal, which removes the alphabetical traversal order dependency for forward references among . A full resource-scoped anchor indexing model is still needed for complete JSON Schema 2020-12 compliance.
There was a problem hiding this comment.
Update: partially addressed in commit d4828ce. Component schema anchors are now pre-indexed before schema-ref traversal, so forward $anchor references among components/schemas are no longer blocked by alphabetical traversal order. A full resource-scoped anchor model is still needed for complete JSON Schema 2020-12 behavior across document boundaries.
| } | ||
|
|
||
| if siblings := component.extraSibling; len(siblings) > 0 && component.Value != nil && doc.IsOpenAPI3_1() { | ||
| component.Value = mergeSiblingFields(component.Value, siblings) |
There was a problem hiding this comment.
Bug (High): Sibling-merged schema overwrites $anchor index entry
When a $ref with sibling fields (e.g., description) targets a schema that defines $anchor, mergeSiblingFields creates a shallow copy that inherits the Anchor field. The code then falls through to line 984 where anchorIndex[value.Anchor] = value overwrites the index with the merged copy — which contains per-reference sibling overrides instead of the original schema.
Any subsequent $anchor reference resolves to the polluted schema with one specific caller's sibling overrides.
Suggestion: Only index anchors when not inside a $ref resolution path (i.e., when component.Ref == ""), or clear the Anchor field on the merged copy before the indexing block.
There was a problem hiding this comment.
Partially fixed in commit d4828ce: anchor indexing now skips -resolution copies (), so sibling-merged schemas no longer overwrite canonical entries with per-reference overrides.
There was a problem hiding this comment.
Update: fixed in commit d4828ce. Anchor indexing now skips $ref resolution copies (component.Ref != ""), so sibling-merged schemas no longer overwrite canonical $anchor entries with per-reference sibling overrides.
| return wrap(err) | ||
| } | ||
| } else { | ||
| } else if !doc.IsOpenAPI3_1() { |
There was a problem hiding this comment.
Bug (Medium): Missing OAS 3.1 document-level validation
The OpenAPI 3.1 specification requires that a document MUST contain at least one of paths, webhooks, or components. This correctly makes paths optional for 3.1, but never checks that at least one of the three is present. A 3.1 document with only openapi + info (no paths, no webhooks, no components) will pass validation, violating the spec.
Suggestion: After the paths validation block, add:
if doc.IsOpenAPI3_1() && doc.Paths == nil && len(doc.Webhooks) == 0 && doc.Components == nil {
return errors.New("an OpenAPI 3.1 document must contain at least one of: paths, webhooks, or components")
}There was a problem hiding this comment.
Fixed in commit f56b22c: added a check in T.Validate() that returns an error when an OAS 3.1 document has none of paths, webhooks, or components.
|
|
||
| // effectiveMin returns the minimum value for OAS 2.0 conversion, considering ExclusiveBound. | ||
| // In OAS 3.1, exclusiveMinimum is a number. In OAS 2.0, it must be in the minimum field. | ||
| func effectiveMin(min *float64, eb openapi3.ExclusiveBound) *float64 { |
There was a problem hiding this comment.
Bug (Medium): OAS 3→2 conversion produces wrong constraints when both minimum and numeric exclusiveMinimum are present
effectiveMin/effectiveMax unconditionally prefer min over eb.Value when both are set. But exclusiveBoundToBool independently returns true when eb.Value != nil. This combination can change the meaning of constraints.
Example: OAS 3.1 schema with {"minimum": 5, "exclusiveMinimum": 3} means value >= 5 AND value > 3, effectively value >= 5. The conversion produces OAS 2.0 minimum: 5, exclusiveMinimum: true, which means value > 5 — a different, stricter constraint.
Suggestion: When both min and eb.Value are set, compare them to determine the tighter bound and set the boolean accordingly.
There was a problem hiding this comment.
Fixed in commit f56b22c: introduced exclusiveMinToBool/exclusiveMaxToBool helpers that pick the tighter bound (numeric exclusiveMinimum/exclusiveMaximum vs minimum/maximum) when converting OAS 3.1 → 2.0 exclusive bounds.
| require.Error(t, err) | ||
| }) | ||
|
|
||
| t.Run("null const", func(t *testing.T) { |
There was a problem hiding this comment.
Test Issue (Medium): "null const" test never exercises const validation
This test has Const: nil with ConstIsSet: false (the default), which means "const is not set". visitConstOperation (schema.go:1942) skips const validation entirely in this case. Moreover, VisitJSON(nil) passes because PermitsNull() short-circuits before the const check is ever reached.
This test would pass regardless of what Const is set to — it never validates const semantics.
Suggestion: Test with ConstIsSet: true:
schema := &Schema{Const: nil, ConstIsSet: true}
err := schema.VisitJSON(nil)
require.NoError(t, err)
err = schema.VisitJSON("not null")
require.Error(t, err)There was a problem hiding this comment.
Fixed in commit f56b22c: the test now uses ConstIsSet: true with Type: &Types{"null"}, so the const check is actually reached and validated. Previously the schema rejected nil before ever reaching the const check.
| }) | ||
| } | ||
|
|
||
| func TestJSONSchema2020Validator_Fallback(t *testing.T) { |
There was a problem hiding this comment.
Test Issue (Medium): Fallback test never triggers the fallback code path
This test uses Schema{Type: &Types{"string"}} — a perfectly valid schema that compiles successfully every time. The fallback path at schema_jsonschema_validator.go:196 (return schema.visitJSON(settings, value)) is never executed.
The test comment says "Create a schema that might cause compilation issues" but a simple {"type": "string"} will never cause a compilation issue with santhosh-tekuri/jsonschema/v6.
Suggestion: Use a schema that actually triggers a compilation error (e.g., invalid $ref), then verify the fallback still produces correct results.
There was a problem hiding this comment.
Fixed in commit f56b22c: the test is now named TestJSONSchemaValidatorFallback and the schema uses an internal $ref (#/components/schemas/Foo) that cannot be resolved by the JSON Schema compiler when compiled in isolation — this reliably triggers the fallback to the built-in validator.
| }) | ||
| } | ||
|
|
||
| func TestBuiltInValidatorStillWorks(t *testing.T) { |
There was a problem hiding this comment.
Test Issue (Medium): TestBuiltInValidatorStillWorks mostly tests the JSON Schema 2020-12 validator
Despite its name, this test uses EnableJSONSchema2020() on 3 of its 4 validation calls. The first sub-test ("string validation with built-in") uses EnableJSONSchema2020() for both assertions, meaning it exclusively tests the JSON Schema 2020-12 code path. If the built-in validator's string type checking regressed, this test would still pass.
Suggestion: Remove EnableJSONSchema2020() from assertions intended to test the built-in validator.
There was a problem hiding this comment.
Fixed in commit f56b22c: the first sub-test in TestBuiltInValidatorStillWorks now omits EnableJSONSchema2020() — it validates using the built-in validator only, confirming the fallback path still works correctly.
openapi3/schema.go
Outdated
| if settings.regexCompiler != nil { | ||
| re, err = settings.regexCompiler(pattern) | ||
| } else { | ||
| re, err = regexp.Compile(intoGoRegexp(pattern)) |
There was a problem hiding this comment.
Performance (Medium): patternProperties regex compiled on every key × pattern × validation call
The existing pattern (string) validation at line 2505 uses the compiledPatterns sync.Map cache:
cpiface, _ := compiledPatterns.Load(schema.Pattern)But the new patternProperties code here compiles a fresh regex on every call:
re, err = regexp.Compile(intoGoRegexp(pattern))This runs inside a nested loop (for each key in the object, for each pattern), resulting in O(keys × patterns) regex compilations per VisitJSON call. For an object with 100 keys and 3 pattern properties, that's 300 regexp.Compile calls per validation.
The caching infrastructure already exists (compiledPatterns sync.Map). This code simply doesn't use it.
Suggestion: Use compiledPatterns.Load(pattern) / compiledPatterns.Store(pattern, re) mirroring the existing pattern for schema.Pattern.
There was a problem hiding this comment.
Fixed in commit f56b22c: the patternProperties loop now uses the compiledPatterns sync.Map (Load before compile, Store after) — the same cache used for schema.Pattern.
|
Bug (Medium): When a schema has schema := &Schema{Const: nil, ConstIsSet: true}
schema.VisitJSON(nil) // BUG: "Value is not nullable"
schema.VisitJSON(nil, EnableJSONSchema2020()) // correct: nil (passes via 2020-12 path)Suggestion: In |
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.
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.
|
Addressed issue comment #1 (comment) in commit
|
…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.
Changes
Cherry-picked from upstream (15 commits)
Scoped Implementation
visitJSONWithJSONSchemafalls back to built-in validator when JSON Schema compilation fails (e.g. unresolved cross-schema$ref) instead of swallowing errors$ref+ sibling merge$reffor OpenAPI 3.1 documents onlypatternPropertiesvisitJSONObjectwith regex matching, named property priority, and proper additionalProperties fallbackprefixItemsi < len(PrefixItems)validate againstPrefixItems[i], remaining againstItemsconst: nullfixConstIsSetfield distinguishes "const not set" from "const: null" via JSON unmarshalling detection; also works for programmatically-set const valuesuseJSONSchema2020guard) per JSON Schema 2020-12 specsync.Mapcache that caused stale validator reuse across GC cycles$anchorresolution$anchorvalues and resolves fragment references that don't start with/$dynamicAnchorindexing$dynamicAnchorvalues for future$dynamicRefresolutionBug fixes discovered during testing
[]stringto[]anyintransformOpenAPIToJSONSchema— the jsonschema compiler requires[]anyfor type arrays$refsibling merge scope: Only merges siblings for OpenAPI 3.1 documents, preserving 3.0 behavior where$refsiblings are ignoredsync.Mapcache keyed byunsafe.Pointer— pointer reuse after GC caused stale validator hitsFiles Modified
openapi3/schema.go—ConstIsSet,patternProperties,prefixItems, format guards,UnmarshalJSONopenapi3/schema_jsonschema_validator.go— fallback fix, nullable[]any, cache removalopenapi3/loader.go—$refsibling merge,$anchor/$dynamicAnchorindexing,mergeSiblingFields()openapi3/refs.go—extraSiblingfield onSchemaRef,UnmarshalJSONsibling captureTesting
go test ./...)go vet ./...cleanSummary by CodeRabbit
OAS 3.1 validate 校验方法支持情况分析
新增特性
prefixItems位置验证patternProperties匹配const: null修复(ConstIsSet)$anchorfragment 解析$dynamicAnchor索引$refmergecomponents/pathItems完整覆盖矩阵
prefixItemspatternPropertiesconst: null$anchor解析$dynamicAnchor索引$refmergetypearrays withnullexclusiveMin/Maxnumericif/then/elsedependentRequiredcontains/min/maxunevaluatedItemsunevaluatedPropertiesdependentSchemaspropertyNamescomponents/pathItems$dynamicRef已知限制
$dynamicRef未解析 —$dynamicAnchor已索引,但 loader 不解析$dynamicRef动态引用if/then/else、contains、unevaluated*等 — 依赖EnableJSONSchema2020()路径,by designpatternProperties: false+additionalProperties边界情况 — 已匹配 pattern 的属性可能被additionalProperties: false误拒另外,本次同步修复了 CodeRabbit 指出的一个测试 bug:
openapi3filter/components_path_items_maingo_test.go:header key 使用了小写("content-type"),导致req.Header.Get("Content-Type")返回空,进而跳过 request body 验证。已改为通过http.CanonicalHeaderKey规范化后存入http.Header,测试现在能真正覆盖 body 校验路径。Breaking Changes
Documentation
Tests