Update usage message in cmd/validate - #1150
Merged
Merged
Conversation
reuvenharrison
added a commit
to oasdiff/kin-openapi
that referenced
this pull request
Jun 25, 2026
* Update usage message in cmd/validate (getkin#1150) * openapi3: fix determinism when handling discriminator mappings (getkin#1151) * feat: bump Go to 1.26 (getkin#1152) * openapi3: use componentNames in func (*Components) Validate(..) Signed-off-by: Pierre Fenoll <pierrefenoll@gmail.com> * openapi3: use componentNames when validating Extensions Signed-off-by: Pierre Fenoll <pierrefenoll@gmail.com> * openapi3: replace slices.Sort() uses with componentNames() Signed-off-by: Pierre Fenoll <pierrefenoll@gmail.com> * ci: fix running go-generate Signed-off-by: Pierre Fenoll <pierrefenoll@gmail.com> * openapi3: optimize generated refs Validate Signed-off-by: Pierre Fenoll <pierrefenoll@gmail.com> * openapi3: replace slices.Sort() uses with componentNames() in generated refs Signed-off-by: Pierre Fenoll <pierrefenoll@gmail.com> * openapi3: use maps.Copy instead of loops Signed-off-by: Pierre Fenoll <pierrefenoll@gmail.com> * openapi3: use deterministic iteration in more (all?) places Signed-off-by: Pierre Fenoll <pierrefenoll@gmail.com> * refacto: use greppable type declarations Signed-off-by: Pierre Fenoll <pierrefenoll@gmail.com> * refacto: prefer strings.SplitSeq() when range-ing Signed-off-by: Pierre Fenoll <pierrefenoll@gmail.com> * feat: add OpenAPI 3.1 support 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> * fix: resolve $ref in OpenAPI 3.1 schema fields (prefixItems, contains, 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> * fix: recurse into OpenAPI 3.1 fields in transformOpenAPIToJSONSchema 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> * fix: validate sub-schemas in OpenAPI 3.1 schema fields 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> * feat: add const keyword validation to built-in validator Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: improve OpenAPI 3.1 spec compliance - 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> * feat: add remaining JSON Schema 2020-12 keywords and improvements - 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> * style: fix go fmt formatting Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: resolve 8 correctness issues in OpenAPI 3.1 support - 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> * fix: avoid CI lint match in OPEN_ISSUES.md 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> * chore: remove OPEN_ISSUES.md from repo Open issues are tracked in the PR getkin#1125 description instead. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: resolve 8 additional OpenAPI 3.1 issues - 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> * feat: add $schema, $defs keywords and fix remaining issues - 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> * fix: strip __origin__ from Const and Examples in Schema.UnmarshalJSON 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> * fix: strip __origin__ from Encoding, Variables, and Webhooks maps 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> * feat: honour $ref sibling keywords in OAS 3.1 (e.g. deprecated:true) In OpenAPI 3.0 / JSON Schema draft-07, $ref replaces its entire object so sibling keywords are silently ignored. In OpenAPI 3.1 / JSON Schema 2020-12, $ref and sibling keywords are both applied. Changes: - SchemaRef gains a `sibling *Schema` field (generated via refs.tmpl) - UnmarshalJSON populates sibling when non-extension fields appear alongside $ref - resolveSchemaRef applies sibling fields onto a copy of the resolved schema, but only for OAS 3.1+ documents (preserves 3.0 behaviour) - applySiblingSchemaFields overlays known annotation/metadata fields (deprecated, description, title, readOnly, writeOnly, example, externalDocs, default) using the extra[] field list so only explicitly present siblings are applied Test: TestOAS31_RefSiblingKeyword in loader_31_schema_refs_test.go loads a 3.1 spec where status has deprecated:true as a $ref sibling and asserts that the resolved Value carries Deprecated==true. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs: regenerate openapi3.txt after refs.go header update refs.go header changed from "Code generated by go generate; DO NOT EDIT." to "Code generated by go generate using refs.tmpl; DO NOT EDIT refs.go." Update the committed go doc snapshot to match. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: support boolean form for unevaluatedProperties and unevaluatedItems JSON Schema 2020-12 allows unevaluatedProperties and unevaluatedItems to be either a boolean or a schema object (e.g. `unevaluatedProperties: false`). The fields were typed as *SchemaRef, which caused an unmarshal error when encountering the boolean form. Introduce BoolSchema, a type that handles both boolean and schema forms (same pattern as AdditionalProperties), and use it for all three fields. AdditionalProperties becomes a type alias for BoolSchema so existing code continues to compile unchanged. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: regenerate openapi3.txt after BoolSchema type change Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: bump yaml3 to v0.0.12 for unconditional field origin tracking Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test: verify origin tracking for mapping-valued schema fields Adds TestOrigin_MappingFields covering dependentRequired, dependentSchemas, and patternProperties — fields that were missing from Origin.Fields before yaml3 v0.0.12. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: update README changelog entry to v0.136.0 Rename the v0.132.0 entry to v0.136.0 (the upcoming release for this PR), move it above v0.135.0 for chronological order, and add the UnevaluatedItems/UnevaluatedProperties → BoolSchema type change. Addresses fenollp's review on getkin#1125. * openapi3: set OpenAPI version-picking API to doc.IsOpenAPI30, doc.IsOpenAPI31OrLater and doc.OpenAPIMajorMinor Signed-off-by: Pierre Fenoll <pierrefenoll@gmail.com> * openapi3: make sure API is tested from outside its package Signed-off-by: Pierre Fenoll <pierrefenoll@gmail.com> * openapi3: replace EnableJSONSchema2020Validation func with IsOpenAPI31OrLater Signed-off-by: Pierre Fenoll <pierrefenoll@gmail.com> * openapi3: simplify some code Signed-off-by: Pierre Fenoll <pierrefenoll@gmail.com> * openapi3: tune openapi3.1 new fields doc comments Signed-off-by: Pierre Fenoll <pierrefenoll@gmail.com> * openapi3: lets not skip checking errors Signed-off-by: Pierre Fenoll <pierrefenoll@gmail.com> * openapi3: fix impl of (Schema) MarshalYAML() (any, error) Signed-off-by: Pierre Fenoll <pierrefenoll@gmail.com> * openapi3: rename visitJSONWithJSONSchema to useJSONSchema2020 Signed-off-by: Pierre Fenoll <pierrefenoll@gmail.com> * openapi3: fix (License) MarshalYAML() Signed-off-by: Pierre Fenoll <pierrefenoll@gmail.com> * openapi3: fix license field validation, post 3.1 Signed-off-by: Pierre Fenoll <pierrefenoll@gmail.com> * openapi3: fix openapi struct validation, post 3.1 Signed-off-by: Pierre Fenoll <pierrefenoll@gmail.com> * openapi3: fix validating schema.Default Signed-off-by: Pierre Fenoll <pierrefenoll@gmail.com> * fix and optimize generated ref Validate Signed-off-by: Pierre Fenoll <pierrefenoll@gmail.com> * openapi3: fix: schema sibling fields are an OpenAPI3.1 feature Signed-off-by: Pierre Fenoll <pierrefenoll@gmail.com> * openapi3: notes on TestSchemaRefSiblingKeyword Signed-off-by: Pierre Fenoll <pierrefenoll@gmail.com> * openapi3: finally document func (*Schema) VisitJSON(any, ...SchemaValidationOption) error Signed-off-by: Pierre Fenoll <pierrefenoll@gmail.com> * openapi3: revert regression on validating SecurityScheme.Type==mutualTLS for v3.0 Signed-off-by: Pierre Fenoll <pierrefenoll@gmail.com> * openapi3: complete applySiblingSchemaFields with all Schema fields Pierre's FIXME on PR #15: the helper previously covered only 8 fields (deprecated, description, title, readOnly, writeOnly, example, externalDocs, default). Extend to every field in the Schema struct so any sibling keyword alongside $ref in an OAS 3.1 document is correctly overlaid onto the resolved $ref target. Section comments dropped: the entire function runs only when the caller has already checked IsOpenAPI31OrLater(), so splitting cases by OAS version inside the switch was misleading. Co-Authored-By: Claude Opus 4 <noreply@anthropic.com> * openapi3: extract validateExtras helper for all Ref types Split the $ref sibling-fields check out of (*Ref).Validate into a dedicated validateExtras method, generated from refs.tmpl for every Ref type. This lets subsequent patches invoke validateExtras on SchemaRefs referenced from inside a schema tree — i.e. at the sites where Schema.validate recurses into nested SchemaRefs — without duplicating the check logic. Template update + `go generate` regeneration. No behaviour change: Validate still returns the same error for the same inputs; the check is just reachable from additional call sites. Co-Authored-By: Claude Opus 4 <noreply@anthropic.com> * openapi3: validate $ref siblings at nested SchemaRefs, fixing 3.0 regression TestSchemaRefSiblingKeyword/{3.0 false false} (added by @fenollp in this PR) exposed that $ref sibling fields (e.g. description, deprecated) were accepted on nested property schemas in OAS 3.0 when they should have been rejected. Root cause: SchemaRef.validateExtras was invoked only from SchemaRef.Validate, but nested schema validation in Schema.validate recursed directly into ref.Value.Validate, bypassing the ref-level extras check. Invoke ref.validateExtras(ctx) at every nested SchemaRef site: items, properties, additionalProperties, prefixItems, contains, patternProperties, dependentSchemas, $defs, propertyNames, unevaluatedItems, unevaluatedProperties, contentSchema, if/then/else. Drop the t.Skip on TestSchemaRefSiblingKeyword now that all three subcases pass. Co-Authored-By: Claude Opus 4 <noreply@anthropic.com> * openapi3: update TestIssue601 — lxkns.yaml has description siblings to $ref lxkns.yaml is OAS 3.0.2 and uses `description` as a sibling to $ref in many places, which is invalid in OAS 3.0 ($ref replaces the enclosing object; siblings are ignored). Previously this was silently accepted because validateExtras was only invoked on top-level component refs. Now that nested SchemaRefs are validated too, those siblings surface as a validation error and mask the example-type mismatch this test actually targets. Pass AllowExtraSiblingFields("description") so validation proceeds to the example-type assertion the test was written for (same pattern already used in issue513_test.go:264). Co-Authored-By: Claude Opus 4 <noreply@anthropic.com> * openapi3: gate OAS 3.1 schema fields from OAS 3.0 validation Per Pierre's PR #15 review: Schema.validate accepted OAS 3.1 / JSON Schema 2020-12 fields (prefixItems, contains, if/then/else, $defs, $schema, patternProperties, unevaluatedItems, etc.) on OAS 3.0 documents. Add a gate at the top of Schema.validate that rejects each 3.1-only field in 3.0 mode via errFieldFor31Plus. The gate honours AllowExtraSiblingFields so 3.0 documents that resolve external JSON Schema refs (draft-04, draft-07) can opt in to letting $id/$schema/etc. through. This matches the existing opt-in mechanism already used by TestExtraSiblingsInRemoteRef. This whole block becomes a no-op once OAS 3.0 gets its own standalone schema validator. Co-Authored-By: Claude Opus 4 <noreply@anthropic.com> * openapi3: update tests for OAS 3.1 field gate Three tests need adjustment now that OAS 3.1-only schema fields are rejected under OAS 3.0 validation: - TestSchemaIfThenElse_Validate and TestSchemaValidate31SubSchemas exercise 3.1 features (If/Then/Else, PrefixItems) directly via Schema.Validate without a doc, so the isOpenAPI31OrLater flag is never set from a doc version. Pass IsOpenAPI31OrLater() explicitly. - TestIssue495WithDraft04{,Bis} load OAS 3.0 documents that $ref external JSON Schema draft-04 meta-schemas (which contain $id and $schema). Add AllowExtraSiblingFields("$id", "$schema") so the tests assertion about the unresolved inner "#" ref surfaces as intended, matching the pattern already used in issue513_test.go. Co-Authored-By: Claude Opus 4 <noreply@anthropic.com> * fix: handle git ref paths in join() for $ref resolution When resolving relative $ref paths against a base path that uses git ref syntax (e.g. "origin/main:openapi.yaml"), path.Dir() treats the colon as a regular character and returns "origin" instead of recognizing it as the git ref separator. This causes $ref resolution to produce invalid paths like "origin/schemas/pet.yaml" instead of "origin/main:schemas/pet.yaml", breaking multi-file OpenAPI specs loaded via git refs. The fix detects the colon separator and applies path.Dir/path.Join only to the file path portion after the colon, preserving the git ref prefix. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * openapi3: add JoinFunc callback for custom path resolution Replace the colon-specific handling with a general-purpose JoinFunc callback on Loader. This lets callers override how relative $ref paths are resolved against the base path — useful when loading specs from non-filesystem sources (git objects, remote archives, etc.) where the base path follows a different convention than filesystem paths. When JoinFunc is nil (the default), behavior is unchanged. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: regenerate openapi3.txt after JoinFunc addition Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: lowercase example output to pass CI lint Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test: remove redundant TestJoinFunc (covered by ExampleLoader_JoinFunc) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: remove trailing blank line in loader_test.go Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * openapi3: add many many tests with TestV3ApisGuruOpenapiDirectory Signed-off-by: Pierre Fenoll <pierrefenoll@gmail.com> * openapi3: record failures as openapi3/testdata/apis_guru_openapi_directory golden Signed-off-by: Pierre Fenoll <pierrefenoll@gmail.com> * openapi2: add many many tests with TestV2ApisGuruOpenapiDirectory Signed-off-by: Pierre Fenoll <pierrefenoll@gmail.com> * openapi2: record failures as openapi2/testdata/apis_guru_openapi_directory golden Signed-off-by: Pierre Fenoll <pierrefenoll@gmail.com> * openapi3: record failures after rebasing on top of getkin#1125 Signed-off-by: Pierre Fenoll <pierrefenoll@gmail.com> * openapi3: enable testing for 3.1 documents Signed-off-by: Pierre Fenoll <pierrefenoll@gmail.com> * openapi3: record v3.1 load/validation test failures Signed-off-by: Pierre Fenoll <pierrefenoll@gmail.com> * openapi3: skip v3.1 load/validation flaky tests Signed-off-by: Pierre Fenoll <pierrefenoll@gmail.com> * openapi3: remove map-iteration order leaks causing flaky tests (getkin#1158) * openapi2conv: nil-guard components lookup in FromV3SchemaRef (getkin#1156) * fix(openapi2conv): nil-guard components lookup in FromV3SchemaRef FromV3SchemaRef indexes components.Schemas[name] whenever the input schema has a . Several recursive call sites in this file (FromV3RequestBodyFormData among them) already pass components=nil when descending into array items / nested refs, so hitting a ref from that path nil-dereferenced and took down the whole V3→V2 conversion. In practice this fires on real-world inputs like the OpenAI OpenAPI 3.0 spec, where CreateEmbeddingRequest references a sub-schema inside an array of formData items (getkin#1062). Guard the components lookup with a components != nil check. When the components table is not available we fall through to the same 'return the plain ' branch that already handles non-binary schema refs - semantically 'the target schema is not known locally, emit a pass-through ref' - instead of panicking. Runtime behaviour for callers that do pass a non-nil Components is unchanged. Closes getkin#1062 Signed-off-by: SAY-5 <SAY-5@users.noreply.github.com> * test(openapi2conv): regression test for getkin#1062 (per @fenollp) Adds TestIssue1062_FormDataArrayOfRefDoesNotPanic with a minified spec capturing the OpenAI OpenAPI 3 shape that triggered the original report: a multipart/form-data request body whose property is an array whose items are a $ref into #/components/schemas/. Without the fix in this PR, the test panics inside FromV3 (line 723) because FromV3RequestBodyFormData passes components=nil into FromV3SchemaRef, which then nil-derefs on components.Schemas[name]. With the fix, the test passes and the resulting v2 spec contains the expected formData parameter named 'documents'. Signed-off-by: SAY-5 <say.apm35@gmail.com> --------- Signed-off-by: SAY-5 <SAY-5@users.noreply.github.com> Signed-off-by: SAY-5 <say.apm35@gmail.com> Co-authored-by: SAY-5 <SAY-5@users.noreply.github.com> * address various lint errors Signed-off-by: Pierre Fenoll <pierrefenoll@gmail.com> * refacto: replace `openapi3.*Ptr(..)` funcs with `new(..)` Signed-off-by: Pierre Fenoll <pierrefenoll@gmail.com> * refacto(tests): use t.Context instead of context.Background Signed-off-by: Pierre Fenoll <pierrefenoll@gmail.com> * fix and upgrade goimports-reviser Signed-off-by: Pierre Fenoll <pierrefenoll@gmail.com> * revert to go 1.25 and revert cc4f8d9 Signed-off-by: Pierre Fenoll <pierrefenoll@gmail.com> * openapi3gen: clear nullable on exported component bodies When a struct is reached via *T, generateSchemaRefFor sets schema.Nullable=true to encode nullability of that one reference site. With ExportComponentSchemas on, that same body becomes the canonical component definition shared by every $ref site, so the nullable flag leaks into the component itself. Codegen tools (Orval, openapi-typescript) then emit broken types like `interface Foo {...} | null`. Clear the flag right before registering the schema as a component. * openapi3: add test for issue getkin#927 (nullable not respected on $ref schemas) * test: move public-API tests to external _test packages (getkin#1168) Co-authored-by: Tommy Nacass <tommy.nacass@gmail.com> * feat(openapi3): add per-type validation errors with cluster wrappers (getkin#1166) * feat(openapi3conv): canonicalization pass for 3.0 -> 3.x (getkin#1162) * openapi3conv: test Upgrade on many documents (getkin#1169) * feat(openapi3): batch-convert long-tail RequiredFieldError sites (getkin#1170) * feat(openapi3): MutuallyExclusiveFieldsError cluster for forbidden field pairs Converts four mutual-exclusion sites: - example.value vs externalValue - mediaType.example vs examples - license.url vs identifier - link.operationId vs operationRef * feat(openapi3): add SchemaReadOnlyWriteOnlyExclusive leaf to MutuallyExclusiveFieldsError * feat(openapi3): ForbiddenFieldError cluster for set-but-not-allowed fields Converts four 'field MUST NOT be set in this context' sites: - header.name (given by the headers map key) - header.in (implicitly 'header') - OAuth flow authorizationUrl (wrong flow type) - OAuth flow tokenUrl (wrong flow type) * feat(openapi3): RequiredFieldError leaves for parameter.name, responses non-empty, apiKey securityScheme.name * feat(openapi3): ServerURLTemplateError cluster for server URL template failures Converts three server URL template sites: - mismatched { and } - undeclared variables (template/Variables count mismatch) - undeclared variables (declared name not in URL template) * feat(openapi3): EitherFieldRequiredError cluster and SchemaItemsRequired leaf - example.value or externalValue - link.operationId or operationRef - schema.items required when type=array (RequiredFieldError leaf) * feat(openapi3): RequiredFieldError leaves for doc-root info/paths and jsonSchemaDialect URI Three new RequiredFieldError leaves at the doc-validation root: - info (must be an object) - paths (must be an object — 3.0 only) - jsonSchemaDialect (must be an absolute URI with a scheme) * feat(openapi3): SchemaBothFormsExclusive cluster for union-typed schema fields set both ways Three sites in schema.go where a BoolSchema-typed field has both forms set: - additionalProperties - unevaluatedItems - unevaluatedProperties * feat(openapi3): ExactlyOneFieldError + SingleEntryContentError clusters for parameter/header content+schema Four sites: - parameter.go content/schema must be exactly one - parameter.go content map must contain at most one entry - header.go content/schema must be exactly one - header.go content map must contain at most one entry * test: use require.ErrorContains per repo lint * feat(openapi3): WebhookNilError for nil pathitem in T.Validate webhook walk Single-site cluster carrying the offending webhook key name. * docs: regenerate openapi3.txt for combined branch * openapi3gen: skip component export for anonymous types Anonymous nested struct types have an empty reflect.Type.Name(), so generateTypeName returns "". Registering them under that key produces "#/components/schemas/" refs that violate the OpenAPI spec (component keys must match ^[a-zA-Z0-9._-]+$) and break codegen tools like Orval. Inline the schema instead, matching the existing behavior for generics and time.Time. * feat: migrate to oasdiff/yaml v0.1.0 single Unmarshal API + enable DisableTimestamps Bumps: * github.com/oasdiff/yaml v0.0.9 -> v0.1.0 * github.com/oasdiff/yaml3 v0.0.12 -> v0.0.13 yaml v0.1.0 consolidates three unmarshal entry points (Unmarshal, UnmarshalWithOriginTree, UnmarshalWithDecodeOpts) into a single Unmarshal with the signature: Unmarshal(y []byte, o interface{}, decode DecodeOpts, opts ...JSONOpt) (*OriginTree, error) DecodeOpts carries Origin (tracking) and DisableTimestamps (YAML 1.1 implicit-timestamp resolution opt-out, backed by yaml3 v0.0.13). This PR migrates every yaml.Unmarshal / yaml.UnmarshalWithOriginTree call site (4 production sites + 8 test sites) to the new signature, setting DisableTimestamps: true everywhere. Real-world OpenAPI specs use date-shaped strings as map keys (e.g. revision dates like "1344-08-22") which the YAML 1.1 default resolves to time.Time — that resolution is the root cause of validation errors that surface as misleading time.Time-stringified values in error messages, and in some cases breaks string-keyed lookup entirely. Testdata golden files in openapi3/testdata/apis_guru_openapi_directory/ regenerated by the existing golden helper: * Several previously-failing specs now validate cleanly (golden files removed by the helper's os.Remove path). * Others show updated error messages where date-shaped scalars no longer appear as stringified time.Time. `go test ./...` clean across all packages. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * ci: fixup lint after modifications to marsh.go Signed-off-by: Pierre Fenoll <pierrefenoll@gmail.com> * openapi3: re-enable tests disabled due to YAML dates in map keys Signed-off-by: Pierre Fenoll <pierrefenoll@gmail.com> * un-patch YAML serialization of dates (see issue getkin#697) Signed-off-by: Pierre Fenoll <pierrefenoll@gmail.com> * openapi3: typed context errors for Validate() wrapper chain (getkin#1183) * feat(openapi3): typed context errors for Validate() wrapper chain Replaces the 14 fmt.Errorf wrap sites in Validate() with three typed error types carrying their context as structured fields: - SectionContextError{Section, Cause} — wraps an error inside one of the top-level document sections (info, paths, components, security, servers, tags, externalDocs, webhooks, jsonSchemaDialect) - PathContextError{Path, Cause} — wraps an error inside a specific path - OperationContextError{Method, Cause} — wraps an error inside a specific HTTP-method operation Continues the typed-validation-error work from getkin#1166 and getkin#1180: those PRs typed the leaf errors; this one types the wrapper layers that carry doc-tree position around them. Why: today, callers that want to render context separately (which section a finding lives in, which path, which operation) have to parse the rendered error string with regex. errors.As against typed wrappers is the structured equivalent. Backward compatibility: Error() strings are byte-identical to the fmt.Errorf wrappers they replace. Existing consumers parsing the rendered text continue to work unchanged. The typed extraction is purely additive. Wrap sites converted: - openapi3.go: 9 sections (info, paths, components, security, servers, tags, externalDocs, webhooks, jsonSchemaDialect) - paths.go: per-path wrapper (PathContextError) - path_item.go: per-operation wrapper (OperationContextError) - operation.go, tag.go, schema.go: 3 additional externalDocs sites (also use SectionContextError{Section: "external docs"}) Tests cover Error() format byte-stability, Unwrap chain walking, errors.As extraction from a three-layer chain (section + path + operation), and arbitrary non-typed inner causes. All existing tests pass unchanged across openapi2, openapi3, openapi3conv, openapi3filter, openapi3gen, and the routers — confirming the rendered strings are stable. .github/docs/openapi3.txt regenerated via docs.sh. * refactor(openapi3): rename context errors to *ValidationError Addresses review feedback on getkin#1183: - SectionContextError -> SectionValidationError - PathContextError -> PathValidationError - OperationContextError -> OperationValidationError The new names tie these positional wrappers to the ValidationError family they belong to (base ValidationError, cluster types, leaves). Also renames the file to sit in that namespace: section_context_error.go -> validation_error_context.go (+ _test.go). No behavior change: Error() strings and Unwrap() chains are untouched. .github/docs/openapi3.txt regenerated. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * openapi3: track Origin on the document root (T) (getkin#1184) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * openapi3: tests flakiness corrected (getkin#1159) Signed-off-by: Pierre Fenoll <pierrefenoll@gmail.com> * openapi3: aggregate independent validation errors via EnableMultiError (getkin#1185) * openapi3: aggregate independent validation errors via EnableMultiError Today Validate returns the first error it encounters and stops, so a spec with several independent defects only reports one at a time: fix, re-run, see the next, repeat. With the new EnableMultiError ValidationOption, container validators aggregate independent problems and return them together as a flat MultiError. Aggregation happens at the container fan-out points where it is safe to continue past an error: T, Paths, PathItem, Operation, Components, Responses. Leaf validators (Schema, Info, Server, etc.) still fail fast, since continuing past a leaf error can hit a nil deref. The result shape is flat: each top-level MultiError entry is one fully-wrapped chain (SectionValidationError -> PathValidationError -> OperationValidationError -> typed leaf). Consumers iterating the MultiError get one entry per problem; consumers using errors.As / errors.Is work unchanged thanks to MultiError.As / MultiError.Is. By default Validate returns the first error encountered, byte-identical to today: the existing test suite passes through the same code path it always did. * openapi3: keep "return validateExtensions" pattern in container validators CI greps for "return .*validateExtensions" in openapi3/ and asserts the count equals the number of Extensions fields. The previous commit moved those calls to "me.emit(validateExtensions(...))" + "return me.result()", breaking the count. Add a finalize helper on errCollector that emits its argument and returns the accumulated result, so each container's trailing line is a single "return me.finalize(validateExtensions(ctx, X.Extensions))" that both reads cleanly and matches the CI fence regex. * openapi3: address review feedback on EnableMultiError Top-level: - cmd/validate: add --multi flag (mirrors --defaults / --examples / --patterns) - Convert simple leaves to aggregate independent errors: License, Info, Server (singular + plural), Tag (singular + plural), ExternalDocs. Each is sequential independent checks with no guard-then-deref pattern, structurally identical to the existing container validators. Schema, Parameter, MediaType have internal cross-field dependencies and are left for follow-up PRs after per-method analysis. - Rewrite EnableMultiError docstring: drop the explicit fan-out list (would go stale), name the transition explicitly, explain why some leaves are not yet converted, and replace the generic errors.As / errors.Is note with a concrete how-to. Inline: - validate_multi_error_test.go: use require.ErrorContains for chain checks; tighten GreaterOrEqual to Equal where the count is exact. - response.go: route Responses.Validate's empty-responses error through me.emit for consistency with the rest of the multi-error path. - components.go, paths.go, openapi3.go: add comments at the error-path continue statements explaining why we skip descending past a malformed parent key (component name, path key, nil webhook). - error_collector.go (new): move the errCollector type and methods out of validation_options.go so the options file stays focused on options. - openapi3.go: revert to the wrap-variable reassignment pattern per section block; keep wrapSection as the factory. * test: hoist err.Error() out of require.Equal to satisfy CI grep fence * openapi3: wrap plural examples Validate errors in newSchemaValueError Parameter.Validate and MediaType.Validate already use newSchemaValueError for the singular `example:` field, but the plural `examples:` map loop returned a bare fmt.Errorf wrapping the underlying *SchemaError. That meant errors.As(err, &SchemaValueError) didn't match for plural examples, so downstream consumers fell back to a generic catch-all instead of recognising the example-violates-schema cluster. Wrap the plural-loop result in newSchemaValueError("example", ...) too, preserving the example key inside the wrap so the rendered message still names the offending example. Aligns the plural path with the singular path; both now surface as *SchemaValueError to errors.As consumers. Existing example_validation_test.go assertions and the apis_guru golden fixtures gain the "invalid example: " prefix the wrap adds. * openapi3: point Examples Validate failures at the example's value, not the parent For plural Examples entries on Parameter/MediaType, the Validate failure used the parent struct's Origin, which deep-links to the parameter or media-type start (e.g. the `in: query` line) rather than the offending example value. Build the SchemaValueError's Origin from the Example struct's per-field origin (`Origin.Fields["value"]`) when available, falling back to the example's struct origin, then to the parent. No message-format change: existing string assertions continue to hold, only the resolved line/column tightens to the example's `value:` field. * openapi3: keep validating extensions when responses is empty The empty-responses path used 'return me.result()' which short-circuited before validateExtensions could run, so extension errors would never aggregate with the empty-responses finding under multi mode. Drop the early return and let control fall through to the trailing finalize() call; the for loop is a no-op on an empty Responses, so the only behaviour change is that validateExtensions now runs. Spotted by @fenollp in PR review. * test: regression pin for the empty-responses + extension aggregation fix Adds TestResponses_Validate_EmptyAndExtensionAggregate which constructs an empty Responses with a non-x- sibling on Extensions and asserts both findings surface under EnableMultiError. Reverting the previous commit's fall-through edit causes this test to fail (one leaf instead of two). * openapi3: fix validation of duplicated path templates (getkin#1189) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * openapi3: type the remaining bare-error validation sites (getkin#1187) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * [WIP] Origin: carry block end (EndLine/EndColumn) on Key Location gains EndLine/EndColumn; originFromSeq parses the trailing end_delta/end_col yaml3 now emits and sets them on Origin.Key, so each operation's Origin spans the whole endpoint block. Existing Key golden assertions updated. NOTE: go.mod is on a dev-local replace => ../yaml3 (not committed). Repin to the released yaml3 (v0.0.14, once #9/#10 merge) before pushing. * Bump oasdiff/yaml3 to v0.0.14 + oasdiff/yaml to v0.1.1; record Origin block end Picks up Node end positions and the __origin__ block-end span, so Origin.Key now carries EndLine/EndColumn (the span of the block a location heads). Updates the TestOrigin_Example expectation to the corrected flow-collection end (the value {"bar": "baz"} now ends just past its closing brace). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Regenerate openapi3.txt API docs for Origin EndLine/EndColumn --------- Signed-off-by: Pierre Fenoll <pierrefenoll@gmail.com> Signed-off-by: SAY-5 <SAY-5@users.noreply.github.com> Signed-off-by: SAY-5 <say.apm35@gmail.com> Co-authored-by: Pierre Fenoll <pierrefenoll@gmail.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: cloudnativeninja <tommy.nacass@gmail.com> Co-authored-by: Sai Asish Y <say.apm35@gmail.com> Co-authored-by: SAY-5 <SAY-5@users.noreply.github.com> Co-authored-by: 0-don <don.cryptus@gmail.com> Co-authored-by: Brandon Bloom <brandon@brandonbloom.name>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.