From 97487906b936539dfcabf2a413c211079b70cdbf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 12 Jul 2026 22:42:49 +0000 Subject: [PATCH] chore(deps): update dependency: bump github.com/getkin/kin-openapi Bumps [github.com/getkin/kin-openapi](https://github.com/getkin/kin-openapi) from 0.140.0 to 0.142.0. - [Release notes](https://github.com/getkin/kin-openapi/releases) - [Commits](https://github.com/getkin/kin-openapi/compare/v0.140.0...v0.142.0) --- updated-dependencies: - dependency-name: github.com/getkin/kin-openapi dependency-version: 0.142.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 6 +- go.sum | 12 +- .../getkin/kin-openapi/openapi3/doc.go | 9 +- .../getkin/kin-openapi/openapi3/loader.go | 71 ++-- .../getkin/kin-openapi/openapi3/maplike.go | 2 +- .../getkin/kin-openapi/openapi3/media_type.go | 30 +- .../getkin/kin-openapi/openapi3/openapi3.go | 10 +- .../getkin/kin-openapi/openapi3/operation.go | 20 +- .../getkin/kin-openapi/openapi3/origin.go | 79 ++++- .../getkin/kin-openapi/openapi3/response.go | 6 + .../getkin/kin-openapi/openapi3/schema.go | 24 +- .../getkin/kin-openapi/openapi3/tag.go | 15 +- .../kin-openapi/openapi3/validation_error.go | 57 +++- .../openapi3/validation_error_context.go | 52 ++- .../openapi3/validation_options.go | 10 + .../getkin/kin-openapi/openapi3/walk.go | 318 ++++++++++++++++++ vendor/github.com/oasdiff/yaml/.golangci.toml | 16 - vendor/github.com/oasdiff/yaml/.golangci.yml | 26 ++ vendor/github.com/oasdiff/yaml/README.md | 16 +- vendor/github.com/oasdiff/yaml/yaml.go | 2 +- vendor/github.com/oasdiff/yaml3/decode.go | 49 +++ vendor/github.com/oasdiff/yaml3/origin.go | 16 +- vendor/github.com/oasdiff/yaml3/scannerc.go | 9 +- vendor/github.com/oasdiff/yaml3/yaml.go | 8 + vendor/modules.txt | 10 +- 25 files changed, 752 insertions(+), 121 deletions(-) create mode 100644 vendor/github.com/getkin/kin-openapi/openapi3/walk.go delete mode 100644 vendor/github.com/oasdiff/yaml/.golangci.toml create mode 100644 vendor/github.com/oasdiff/yaml/.golangci.yml diff --git a/go.mod b/go.mod index ac48311..9350cf5 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/DistroByte/molecule go 1.26.0 require ( - github.com/getkin/kin-openapi v0.140.0 + github.com/getkin/kin-openapi v0.142.0 github.com/go-chi/chi/v5 v5.3.0 github.com/google/uuid v1.6.0 github.com/hashicorp/nomad/api v0.0.0-20260220212019-daca79db0bd6 @@ -35,8 +35,8 @@ require ( github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.20 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect - github.com/oasdiff/yaml v0.1.0 // indirect - github.com/oasdiff/yaml3 v0.0.13 // indirect + github.com/oasdiff/yaml v0.1.1 // indirect + github.com/oasdiff/yaml3 v0.0.14 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect golang.org/x/sys v0.41.0 // indirect golang.org/x/text v0.34.0 // indirect diff --git a/go.sum b/go.sum index 60508eb..6f71240 100644 --- a/go.sum +++ b/go.sum @@ -9,8 +9,8 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4 github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/getkin/kin-openapi v0.140.0 h1:JFn675aXRFjyiZKa/BFWploGldQlI0gobp4J5k0EZ2g= -github.com/getkin/kin-openapi v0.140.0/go.mod h1:lISrB64F0CPcuDJ3LdtPTMJBY8VENjR9wJBdrcT6J3g= +github.com/getkin/kin-openapi v0.142.0 h1:izj0vBdFprMhitfzaX8sTqztsEQyvwhssBoB6n8NO7w= +github.com/getkin/kin-openapi v0.142.0/go.mod h1:3BH9M9XDe/y9M5DSvEocVYAYq1w0qrhJHjC/vZi0AaY= github.com/go-chi/chi/v5 v5.3.0 h1:halUjDxhshgXHMrao5bB8eNBXo/rnzwr8m5m36glehM= github.com/go-chi/chi/v5 v5.3.0/go.mod h1:R+tYY2hNuVUUjxoPtqUdgBqevM9s9njzkTLutVsOCto= github.com/go-openapi/jsonpointer v0.22.5 h1:8on/0Yp4uTb9f4XvTrM2+1CPrV05QPZXu+rvu2o9jcA= @@ -56,10 +56,10 @@ github.com/mattn/go-runewidth v0.0.20 h1:WcT52H91ZUAwy8+HUkdM3THM6gXqXuLJi9O3rjc github.com/mattn/go-runewidth v0.0.20/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/oasdiff/yaml v0.1.0 h1:0bqZjfKc/8S9urj4JuwepX41WX9EoA6ifhU3SV06cXg= -github.com/oasdiff/yaml v0.1.0/go.mod h1:kOlRmMdL2X3vucLCEQO5u61SU22RysnfXvcttrZA1O0= -github.com/oasdiff/yaml3 v0.0.13 h1:06svmvOHOVBqF81+sY2EUScvUI/iS/vl2VIeUUxZQwg= -github.com/oasdiff/yaml3 v0.0.13/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o= +github.com/oasdiff/yaml v0.1.1 h1:6nHx+pn9gBRM6YpBlFZFQGCCd1nuvqOBtTD3KKTgGxY= +github.com/oasdiff/yaml v0.1.1/go.mod h1:EYJNoyktvWMJ0Hmhx+6qTaqMOsalUaRGT8Sj1hNcegU= +github.com/oasdiff/yaml3 v0.0.14 h1:aLJee3hxBK2H5wdXd9iPcIXb93Nty1Ge0pT171eHtkw= +github.com/oasdiff/yaml3 v0.0.14/go.mod h1:csto2xfDjYccdUn/yw/bPjj/cYTdp6HtFA0J4TWG+gg= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/doc.go b/vendor/github.com/getkin/kin-openapi/openapi3/doc.go index 4179030..83408e3 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/doc.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/doc.go @@ -1,8 +1,9 @@ // Package openapi3 parses and writes OpenAPI 3 specification documents. // -// Supports both OpenAPI 3.0 and OpenAPI 3.1: +// Supports OpenAPI 3.0, OpenAPI 3.1, and OpenAPI 3.2: // - OpenAPI 3.0.x: https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md // - OpenAPI 3.1.x: https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md +// - OpenAPI 3.2.x: https://spec.openapis.org/oas/v3.2.0.html // // OpenAPI 3.1 Features: // - Type arrays with null support (e.g., ["string", "null"]) @@ -11,6 +12,9 @@ // - JSON Schema dialect specification // - SPDX license identifiers // +// OpenAPI 3.2 Features: +// - Media Type Object itemSchema for streaming sequential media types +// // The implementation maintains 100% backward compatibility with OpenAPI 3.0. // // For OpenAPI 3.1 validation, use the JSON Schema 2020-12 validator option: @@ -22,4 +26,7 @@ // if doc.IsOpenAPI31OrLater() { // // Handle OpenAPI 3.1 specific features // } +// if doc.IsOpenAPI32OrLater() { +// // Handle OpenAPI 3.2 specific features +// } package openapi3 diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/loader.go b/vendor/github.com/getkin/kin-openapi/openapi3/loader.go index 1974294..f7fc8cd 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/loader.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/loader.go @@ -756,16 +756,8 @@ func (loader *Loader) resolveParameterRef(doc *T, component *ParameterRef, docum return errors.New("cannot contain both schema and content in a parameter") } for _, name := range componentNames(value.Content) { - contentType := value.Content[name] - if schema := contentType.Schema; schema != nil { - if err := loader.resolveSchemaRef(doc, schema, documentPath, []string{}); err != nil { - return err - } - } - for _, k := range componentNames(contentType.Examples) { - if err := loader.resolveExampleRef(doc, contentType.Examples[k], documentPath); err != nil { - return err - } + if err := loader.resolveMediaTypeRefs(doc, value.Content[name], documentPath); err != nil { + return err } } if schema := value.Schema; schema != nil { @@ -828,21 +820,8 @@ func (loader *Loader) resolveRequestBodyRef(doc *T, component *RequestBodyRef, d } for _, name := range componentNames(value.Content) { - contentType := value.Content[name] - if contentType == nil { - continue - } - for _, name := range componentNames(contentType.Examples) { - example := contentType.Examples[name] - if err := loader.resolveExampleRef(doc, example, documentPath); err != nil { - return err - } - contentType.Examples[name] = example - } - if schema := contentType.Schema; schema != nil { - if err := loader.resolveSchemaRef(doc, schema, documentPath, []string{}); err != nil { - return err - } + if err := loader.resolveMediaTypeRefs(doc, value.Content[name], documentPath); err != nil { + return err } } return nil @@ -901,22 +880,8 @@ func (loader *Loader) resolveResponseRef(doc *T, component *ResponseRef, documen } } for _, name := range componentNames(value.Content) { - contentType := value.Content[name] - if contentType == nil { - continue - } - for _, name := range componentNames(contentType.Examples) { - example := contentType.Examples[name] - if err := loader.resolveExampleRef(doc, example, documentPath); err != nil { - return err - } - contentType.Examples[name] = example - } - if schema := contentType.Schema; schema != nil { - if err := loader.resolveSchemaRef(doc, schema, documentPath, []string{}); err != nil { - return err - } - contentType.Schema = schema + if err := loader.resolveMediaTypeRefs(doc, value.Content[name], documentPath); err != nil { + return err } } for _, name := range componentNames(value.Links) { @@ -928,6 +893,30 @@ func (loader *Loader) resolveResponseRef(doc *T, component *ResponseRef, documen return nil } +func (loader *Loader) resolveMediaTypeRefs(doc *T, mediaType *MediaType, documentPath *url.URL) (err error) { + if mediaType == nil { + return + } + if schema := mediaType.Schema; schema != nil { + if err = loader.resolveSchemaRef(doc, schema, documentPath, []string{}); err != nil { + return + } + } + if itemSchema := mediaType.ItemSchema; itemSchema != nil { + if err = loader.resolveSchemaRef(doc, itemSchema, documentPath, []string{}); err != nil { + return + } + } + for _, name := range componentNames(mediaType.Examples) { + example := mediaType.Examples[name] + if err = loader.resolveExampleRef(doc, example, documentPath); err != nil { + return + } + mediaType.Examples[name] = example + } + return +} + func (loader *Loader) resolveSchemaRef(doc *T, component *SchemaRef, documentPath *url.URL, visited []string) (err error) { if component.isEmpty() { return errMUSTSchema diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/maplike.go b/vendor/github.com/getkin/kin-openapi/openapi3/maplike.go index 3a97be1..f651b52 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/maplike.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/maplike.go @@ -80,7 +80,7 @@ func (responses Responses) JSONLookup(token string) (any, error) { // MarshalYAML returns the YAML encoding of Responses. func (responses *Responses) MarshalYAML() (any, error) { - if responses == nil { + if responses == nil || responses.isExplicitlyNull() { return nil, nil } m := make(map[string]any, responses.Len()+len(responses.Extensions)) diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/media_type.go b/vendor/github.com/getkin/kin-openapi/openapi3/media_type.go index c571589..842b320 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/media_type.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/media_type.go @@ -14,10 +14,11 @@ type MediaType struct { Extensions map[string]any `json:"-" yaml:"-"` Origin *Origin `json:"-" yaml:"-"` - Schema *SchemaRef `json:"schema,omitempty" yaml:"schema,omitempty"` - Example any `json:"example,omitempty" yaml:"example,omitempty"` - Examples Examples `json:"examples,omitempty" yaml:"examples,omitempty"` - Encoding Encodings `json:"encoding,omitempty" yaml:"encoding,omitempty"` + Schema *SchemaRef `json:"schema,omitempty" yaml:"schema,omitempty"` + ItemSchema *SchemaRef `json:"itemSchema,omitempty" yaml:"itemSchema,omitempty"` // OpenAPI >=3.2 + Example any `json:"example,omitempty" yaml:"example,omitempty"` + Examples Examples `json:"examples,omitempty" yaml:"examples,omitempty"` + Encoding Encodings `json:"encoding,omitempty" yaml:"encoding,omitempty"` } var _ jsonpointer.JSONPointable = (*MediaType)(nil) @@ -73,11 +74,14 @@ func (mediaType MediaType) MarshalJSON() ([]byte, error) { // MarshalYAML returns the YAML encoding of MediaType. func (mediaType MediaType) MarshalYAML() (any, error) { - m := make(map[string]any, 4+len(mediaType.Extensions)) + m := make(map[string]any, 5+len(mediaType.Extensions)) maps.Copy(m, mediaType.Extensions) if x := mediaType.Schema; x != nil { m["schema"] = x } + if x := mediaType.ItemSchema; x != nil { + m["itemSchema"] = x + } if x := mediaType.Example; x != nil { m["example"] = x } @@ -99,6 +103,7 @@ func (mediaType *MediaType) UnmarshalJSON(data []byte) error { } _ = json.Unmarshal(data, &x.Extensions) delete(x.Extensions, "schema") + delete(x.Extensions, "itemSchema") delete(x.Extensions, "example") delete(x.Extensions, "examples") delete(x.Extensions, "encoding") @@ -148,6 +153,14 @@ func (mediaType *MediaType) Validate(ctx context.Context, opts ...ValidationOpti } } } + if itemSchema := mediaType.ItemSchema; itemSchema != nil { + if !getValidationOptions(ctx).isOpenAPI32OrLater { + return errFieldFor32Plus("itemSchema", mediaType.Origin) + } + if err := itemSchema.Validate(ctx); err != nil { + return err + } + } return validateExtensions(ctx, mediaType.Extensions, mediaType.Origin) } @@ -164,6 +177,13 @@ func (mediaType MediaType) JSONLookup(token string) (any, error) { } case "example": return mediaType.Example, nil + case "itemSchema": + if mediaType.ItemSchema != nil { + if mediaType.ItemSchema.Ref != "" { + return &Ref{Ref: mediaType.ItemSchema.Ref}, nil + } + return mediaType.ItemSchema.Value, nil + } case "examples": return mediaType.Examples, nil case "encoding": diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/openapi3.go b/vendor/github.com/getkin/kin-openapi/openapi3/openapi3.go index 267064f..287327f 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/openapi3.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/openapi3.go @@ -53,8 +53,11 @@ func (doc *T) IsOpenAPI31OrLater() bool { return slices.Contains([]string{"3.1", "3.2"}, doc.OpenAPIMajorMinor()) } -func errFieldFor31Plus(field string, origin *Origin) error { - return newFieldFor31Plus(field, origin) +// IsOpenAPI32OrLater returns whether doc is an OpenAPI document version >=3.2. +// Returns true for 3.2, 3.2.0, ... +// And false for cases where IsOpenAPI31OrLater returns true for 3.1.x and for invalid strings. +func (doc *T) IsOpenAPI32OrLater() bool { + return doc.OpenAPIMajorMinor() == "3.2" } func errValueOfFieldFor31Plus(value any, field string) error { @@ -266,6 +269,9 @@ func (doc *T) Validate(ctx context.Context, opts ...ValidationOption) error { if doc.IsOpenAPI31OrLater() { opts = append(opts, IsOpenAPI31OrLater()) } + if doc.IsOpenAPI32OrLater() { + opts = append(opts, IsOpenAPI32OrLater()) + } ctx = WithValidationOptions(ctx, opts...) me := newErrCollector(ctx) diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/operation.go b/vendor/github.com/getkin/kin-openapi/openapi3/operation.go index 1ec78cd..5122410 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/operation.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/operation.go @@ -33,8 +33,8 @@ type Operation struct { // Optional body parameter. RequestBody *RequestBodyRef `json:"requestBody,omitempty" yaml:"requestBody,omitempty"` - // Responses. - Responses *Responses `json:"responses" yaml:"responses"` // Required + // Responses are required in OpenAPI 3.0 and optional in OpenAPI 3.1 and later. + Responses *Responses `json:"responses" yaml:"responses"` // Optional callbacks Callbacks Callbacks `json:"callbacks,omitempty" yaml:"callbacks,omitempty"` @@ -87,7 +87,9 @@ func (operation Operation) MarshalYAML() (any, error) { if x := operation.RequestBody; x != nil { m["requestBody"] = x } - m["responses"] = operation.Responses + if x := operation.Responses; x != nil { + m["responses"] = x + } if x := operation.Callbacks; len(x) != 0 { m["callbacks"] = x } @@ -113,7 +115,11 @@ func (operation *Operation) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, &x); err != nil { return unmarshalError(err) } + _ = json.Unmarshal(data, &x.Extensions) + if value, present := x.Extensions["responses"]; present && value == nil { + x.Responses = &Responses{explicitlyNull: true} + } delete(x.Extensions, "tags") delete(x.Extensions, "summary") delete(x.Extensions, "description") @@ -203,12 +209,14 @@ func (operation *Operation) Validate(ctx context.Context, opts ...ValidationOpti } } - if v := operation.Responses; v != nil { + if v := operation.Responses; v != nil && !v.isExplicitlyNull() { if err := me.emit(v.Validate(ctx)); err != nil { return err } - } else if err := me.emit(newOperationResponsesRequired(operation.Origin)); err != nil { - return err + } else if v != nil || !getValidationOptions(ctx).isOpenAPI31OrLater { + if err := me.emit(newOperationResponsesRequired(operation.Origin)); err != nil { + return err + } } if v := operation.ExternalDocs; v != nil { diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/origin.go b/vendor/github.com/getkin/kin-openapi/openapi3/origin.go index 1403bd8..c6890e8 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/origin.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/origin.go @@ -2,6 +2,7 @@ package openapi3 import ( "reflect" + "sort" "strings" "github.com/oasdiff/yaml" @@ -27,6 +28,13 @@ type Location struct { Line int `json:"line,omitempty" yaml:"line,omitempty"` Column int `json:"column,omitempty" yaml:"column,omitempty"` Name string `json:"name,omitempty" yaml:"name,omitempty"` + + // EndLine and EndColumn mark the end of the block this location heads (set + // only on Origin.Key). For an operation or schema this spans the whole + // block, so a consumer can extract the entire element from its source. + // Both are zero when the underlying YAML carried no end information. + EndLine int `json:"endLine,omitempty" yaml:"endLine,omitempty"` + EndColumn int `json:"endColumn,omitempty" yaml:"endColumn,omitempty"` } // originFromSeq parses the compact []any sequence produced by yaml3's addOrigin. @@ -99,6 +107,17 @@ func originFromSeq(s []any) *Origin { o.Sequences[sname] = locs } } + + // Trailing block end (yaml3 >= the end-position release): end_delta, end_col. + // Reconstruct the end of the whole block on Origin.Key so a consumer can + // extract the entire element. Older origin sequences omit these, leaving + // EndLine/EndColumn zero. end_col == 0 means no end information was recorded. + if o.Key != nil && idx+1 < len(s) { + if endCol := toInt(s[idx+1]); endCol > 0 { + o.Key.EndLine = keyLine + toInt(s[idx]) + o.Key.EndColumn = endCol + } + } return o } @@ -113,6 +132,48 @@ func toInt(v any) int { return 0 } +// isScalarValuedMapField reports whether v is a non-empty map whose element +// type is a scalar (string, bool, or a numeric kind). Such a map decodes +// without an Origin field of its own, unlike a pointer- or struct-valued map +// whose elements each carry their own Origin. +func isScalarValuedMapField(v reflect.Value) bool { + if v.Kind() != reflect.Map || v.IsNil() || v.Len() == 0 { + return false + } + switch v.Type().Elem().Kind() { + case reflect.String, reflect.Bool, + reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, + reflect.Float32, reflect.Float64: + return true + } + return false +} + +// recordMapKeyLocations copies the map-key locations from a scalar-valued map's +// own subtree onto parentOrigin.Sequences[field], so each key is addressable by +// name (the same shape used for sequence items). It is a no-op when the child +// carries no origin data. Keys are sorted for deterministic output. +func recordMapKeyLocations(parentOrigin *Origin, field string, childTree *yaml.OriginTree) { + s, ok := childTree.Origin.([]any) + if !ok { + return + } + childOrigin := originFromSeq(s) + if childOrigin == nil || len(childOrigin.Fields) == 0 { + return + } + locs := make([]Location, 0, len(childOrigin.Fields)) + for _, loc := range childOrigin.Fields { + locs = append(locs, loc) + } + sort.Slice(locs, func(i, j int) bool { return locs[i].Name < locs[j].Name }) + if parentOrigin.Sequences == nil { + parentOrigin.Sequences = make(map[string][]Location) + } + parentOrigin.Sequences[field] = locs +} + // applyOrigins walks a Go struct tree and a parallel OriginTree, setting // Origin fields on each struct from the extracted origin data. func applyOrigins(v any, tree *yaml.OriginTree) { @@ -150,12 +211,14 @@ func applyOriginsToStruct(val reflect.Value, ptr reflect.Value, tree *yaml.Origi typ := val.Type() // Set Origin field for structs whose Origin field has a "-" json tag. + var structOrigin *Origin if tree.Origin != nil { if sf, ok := typ.FieldByName("Origin"); ok && sf.Type == originPtrType { tag := sf.Tag.Get("json") if tag == "-" { if s, ok := tree.Origin.([]any); ok { - val.FieldByName("Origin").Set(reflect.ValueOf(originFromSeq(s))) + structOrigin = originFromSeq(s) + val.FieldByName("Origin").Set(reflect.ValueOf(structOrigin)) } } } @@ -172,9 +235,19 @@ func applyOriginsToStruct(val reflect.Value, ptr reflect.Value, tree *yaml.Origi continue } childTree := tree.Fields[tag] - if childTree != nil { - applyOriginsToValue(val.Field(i), childTree) + if childTree == nil { + continue + } + // A scalar-valued map (e.g. OAuth scopes: map[string]string) decodes into + // a Go map that has no Origin field of its own, so its per-key locations — + // present in the child subtree — would otherwise be lost. Record them on + // this struct's Origin as a named sequence so a consumer can locate each + // entry by key. Object- or pointer-valued maps are excluded: their values + // carry their own Origin via the recursion below. + if structOrigin != nil && isScalarValuedMapField(val.Field(i)) { + recordMapKeyLocations(structOrigin, tag, childTree) } + applyOriginsToValue(val.Field(i), childTree) } // Handle wrapper types whose inner struct has no json tag: diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/response.go b/vendor/github.com/getkin/kin-openapi/openapi3/response.go index ddc3a7b..87a52fe 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/response.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/response.go @@ -14,6 +14,8 @@ type Responses struct { Origin *Origin `json:"-" yaml:"-"` m map[string]*ResponseRef + + explicitlyNull bool } // NewResponses builds a responses object with response objects in insertion order. @@ -26,6 +28,10 @@ func NewResponses(opts ...NewResponsesOption) *Responses { return responses } +func (responses *Responses) isExplicitlyNull() bool { + return responses != nil && responses.explicitlyNull && responses.m == nil && len(responses.Extensions) == 0 +} + // NewResponsesOption describes options to NewResponses func type NewResponsesOption func(*Responses) diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/schema.go b/vendor/github.com/getkin/kin-openapi/openapi3/schema.go index 1f5eebd..2934971 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/schema.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/schema.go @@ -1406,6 +1406,18 @@ func (schema *Schema) validate(ctx context.Context, stack []*Schema) ([]*Schema, return stack, newSchemaReadOnlyWriteOnlyExclusive(schema.Origin) } + // The elements of `required` MUST be unique (JSON Schema 2020-12 §6.5.3 + // for OAS 3.1, draft-04 for OAS 3.0). + if len(schema.Required) > 1 { + seen := make(map[string]struct{}, len(schema.Required)) + for _, name := range schema.Required { + if _, dup := seen[name]; dup { + return stack, &DuplicateRequiredFieldError{Field: name, Origin: schema.Origin} + } + seen[name] = struct{}{} + } + } + // Reject fields that only exist in OAS 3.1 / JSON Schema 2020-12 when the // document is OAS 3.0. Fields explicitly allowed via AllowExtraSiblingFields // are skipped (opt-in escape hatch for 3.0 docs that reference external @@ -1554,7 +1566,7 @@ func (schema *Schema) validate(ctx context.Context, stack []*Schema) ([]*Schema, var err error if stack, err = v.validate(ctx, stack); err != nil { - return stack, err + return stack, &SchemaCombinatorElementValidationError{Combinator: "oneOf", Cause: err} } } @@ -1566,7 +1578,7 @@ func (schema *Schema) validate(ctx context.Context, stack []*Schema) ([]*Schema, var err error if stack, err = v.validate(ctx, stack); err != nil { - return stack, err + return stack, &SchemaCombinatorElementValidationError{Combinator: "anyOf", Cause: err} } } @@ -1578,7 +1590,7 @@ func (schema *Schema) validate(ctx context.Context, stack []*Schema) ([]*Schema, var err error if stack, err = v.validate(ctx, stack); err != nil { - return stack, err + return stack, &SchemaCombinatorElementValidationError{Combinator: "allOf", Cause: err} } } @@ -3165,9 +3177,9 @@ func deepCopyJSONValue(v any) any { } return cp case []any: - cp := make([]any, len(v)) - for i, val := range v { - cp[i] = deepCopyJSONValue(val) + cp := make([]any, 0, len(v)) + for _, val := range v { + cp = append(cp, deepCopyJSONValue(val)) } return cp default: diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/tag.go b/vendor/github.com/getkin/kin-openapi/openapi3/tag.go index c022a25..55b302f 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/tag.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/tag.go @@ -23,8 +23,21 @@ func (tags Tags) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) me := newErrCollector(ctx) + // Each tag name in the list MUST be unique (OpenAPI Object). Empty names + // are left to per-tag validation, not flagged as duplicates here. + seen := make(map[string]struct{}, len(tags)) + for _, v := range tags { - if err := me.emit(v.Validate(ctx)); err != nil { + if v.Name != "" { + if _, dup := seen[v.Name]; dup { + if err := me.emit(&DuplicateTagError{Name: v.Name, Origin: v.Origin}); err != nil { + return err + } + } + seen[v.Name] = struct{}{} + } + wrap := func(e error) error { return &TagValidationError{Name: v.Name, Cause: e} } + if err := me.emitWrapped(wrap, v.Validate(ctx)); err != nil { return err } } diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/validation_error.go b/vendor/github.com/getkin/kin-openapi/openapi3/validation_error.go index 8929a1f..3bdb72d 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/validation_error.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/validation_error.go @@ -535,6 +535,35 @@ func (e *DuplicateParameterError) Error() string { return fmt.Sprintf("more than one %q parameter has name %q", e.In, e.Name) } +// DuplicateRequiredFieldError clusters "duplicate field in required" failures. +// The elements of a schema's `required` array MUST be unique (JSON Schema +// 2020-12 §6.5.3 for OpenAPI 3.1, draft-04 for OpenAPI 3.0). +type DuplicateRequiredFieldError struct { + // Field is the field name listed more than once in `required`. + Field string + // Origin is the source location of the offending schema when the + // document was loaded with Loader.IncludeOrigin = true. + Origin *Origin +} + +func (e *DuplicateRequiredFieldError) Error() string { + return fmt.Sprintf("duplicate field %q in required", e.Field) +} + +// DuplicateTagError clusters "more than one tag has name X" failures. Each tag +// name in the document-root `tags` list MUST be unique (OpenAPI Object). +type DuplicateTagError struct { + // Name is the tag name that appears more than once. + Name string + // Origin is the source location of the offending tag when the document + // was loaded with Loader.IncludeOrigin = true. + Origin *Origin +} + +func (e *DuplicateTagError) Error() string { + return fmt.Sprintf("more than one tag has name %q", e.Name) +} + // InvalidSerializationMethodError clusters "serialization method with // style=X and explode=Y is not supported by Z" failures. Fires for // invalid (style, explode) combinations on encodings, parameters, @@ -1371,13 +1400,12 @@ func exampleValueOrigin(ex *Example, fallback *Origin) *Origin { } // newFieldVersionMismatch wraps leaf in a FieldVersionMismatchError for the -// given field at minimum version 3.1. Used by per-call-site constructors -// (newInfoSummaryFieldFor31Plus, etc.) and by the dispatch helper -// newFieldFor31Plus that schema.go's reject closure goes through. -func newFieldVersionMismatch(field string, leaf error, origin *Origin) error { +// given field at minimum version. Used by per-call-site constructors +// (newInfoSummaryFieldFor31Plus, etc.) and by dispatch helpers. +func newFieldVersionMismatch(field, minVersion string, leaf error, origin *Origin) error { return &FieldVersionMismatchError{ Field: field, - MinVersion: "3.1", + MinVersion: minVersion, Cause: leaf, Origin: origin, } @@ -1391,25 +1419,25 @@ func newFieldVersionMismatch(field string, leaf error, origin *Origin) error { func newInfoSummaryFieldFor31Plus(origin *Origin) error { const msg = "field summary is for OpenAPI >=3.1" return newFieldVersionMismatch("summary", - &InfoSummaryFieldFor31Plus{ValidationError{Message: msg}}, origin) + "3.1", &InfoSummaryFieldFor31Plus{ValidationError{Message: msg}}, origin) } func newLicenseIdentifierFieldFor31Plus(origin *Origin) error { const msg = "field identifier is for OpenAPI >=3.1" return newFieldVersionMismatch("identifier", - &LicenseIdentifierFieldFor31Plus{ValidationError{Message: msg}}, origin) + "3.1", &LicenseIdentifierFieldFor31Plus{ValidationError{Message: msg}}, origin) } func newWebhooksFieldFor31Plus(origin *Origin) error { const msg = "field webhooks is for OpenAPI >=3.1" return newFieldVersionMismatch("webhooks", - &WebhooksFieldFor31Plus{ValidationError{Message: msg}}, origin) + "3.1", &WebhooksFieldFor31Plus{ValidationError{Message: msg}}, origin) } func newJSONSchemaDialectFieldFor31Plus(origin *Origin) error { const msg = "field jsonschemadialect is for OpenAPI >=3.1" return newFieldVersionMismatch("jsonschemadialect", - &JSONSchemaDialectFieldFor31Plus{ValidationError{Message: msg}}, origin) + "3.1", &JSONSchemaDialectFieldFor31Plus{ValidationError{Message: msg}}, origin) } // fieldFor31PlusLeaves maps field names (as passed to errFieldFor31Plus) @@ -1447,7 +1475,7 @@ var fieldFor31PlusLeaves = map[string]func(msg string) error{ "$dynamicRef": func(m string) error { return &DynamicRefFieldFor31Plus{ValidationError{Message: m}} }, } -// newFieldFor31Plus dispatches errFieldFor31Plus's per-field message +// errFieldFor31Plus dispatches errFieldFor31Plus's per-field message // to the right typed leaf and wraps it in a FieldVersionMismatchError. // Fields not in fieldFor31PlusLeaves fall back to a bare // *ValidationError so the caller still gets a stable Message and the @@ -1455,7 +1483,7 @@ var fieldFor31PlusLeaves = map[string]func(msg string) error{ // // Reached only from schema.go's reject closure with a runtime field // name; the four non-schema sites use direct constructors instead. -func newFieldFor31Plus(field string, origin *Origin) error { +func errFieldFor31Plus(field string, origin *Origin) error { msg := "field " + field + " is for OpenAPI >=3.1" var leaf error if ctor, ok := fieldFor31PlusLeaves[field]; ok { @@ -1463,7 +1491,12 @@ func newFieldFor31Plus(field string, origin *Origin) error { } else { leaf = &ValidationError{Message: msg} } - return newFieldVersionMismatch(field, leaf, origin) + return newFieldVersionMismatch(field, "3.1", leaf, origin) +} + +func errFieldFor32Plus(field string, origin *Origin) error { + msg := "field " + field + " is for OpenAPI >=3.2" + return newFieldVersionMismatch(field, "3.2", &ValidationError{Message: msg}, origin) } func newPathParameterRequired(param string, origin *Origin) error { diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/validation_error_context.go b/vendor/github.com/getkin/kin-openapi/openapi3/validation_error_context.go index 3172230..50da3dc 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/validation_error_context.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/validation_error_context.go @@ -26,8 +26,9 @@ // MediaTypeExampleValidationError, WebhookValidationError, // ParameterFieldValidationError, ParameterExampleValidationError, // SecuritySchemeFlowValidationError, OAuthFlowValidationError, -// OAuthFlowFieldValidationError. Cover a specific validation -// surface inside a section. +// OAuthFlowFieldValidationError, SchemaCombinatorElementValidationError, +// TagValidationError. Cover a specific validation surface inside a +// section. // // Both scopes follow the same shape: // @@ -250,3 +251,50 @@ func (e *OAuthFlowFieldValidationError) Error() string { } func (e *OAuthFlowFieldValidationError) Unwrap() error { return e.Cause } + +// SchemaCombinatorElementValidationError wraps a validation error +// originating in one of the sub-schemas listed under a schema's oneOf, +// anyOf, or allOf keyword. Combinator names which keyword the offending +// element belongs to ("oneOf", "anyOf", "allOf"). The wrapper adds the +// combinator scope; the actual failure lives in Cause. +type SchemaCombinatorElementValidationError struct { + // Combinator is the keyword whose element failed ("oneOf", "anyOf", + // "allOf"). + Combinator string + Cause error +} + +func (e *SchemaCombinatorElementValidationError) Error() string { + // Collapse a run of same-combinator wrappers so deeply nested + // allOf/anyOf/oneOf does not stutter "invalid allOf element: " once per + // nesting level. The typed chain is untouched (Unwrap and errors.As still + // see every level); only the rendered message drops the repeats. A run of + // a different combinator is preserved, so an allOf inside a oneOf still + // shows both scopes. + cause := e.Cause + for { + inner, ok := cause.(*SchemaCombinatorElementValidationError) + if !ok || inner.Combinator != e.Combinator { + break + } + cause = inner.Cause + } + return fmt.Sprintf("invalid %s element: %v", e.Combinator, cause) +} + +func (e *SchemaCombinatorElementValidationError) Unwrap() error { return e.Cause } + +// TagValidationError wraps a validation error originating inside a single +// tag in the document-root Tags list. Name is the tag's `name:` value, so +// callers can tell which tag failed without parsing the rendered message. +type TagValidationError struct { + // Name is the tag's `name:` value. + Name string + Cause error +} + +func (e *TagValidationError) Error() string { + return fmt.Sprintf("tag %q: %v", e.Name, e.Cause) +} + +func (e *TagValidationError) Unwrap() error { return e.Cause } diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/validation_options.go b/vendor/github.com/getkin/kin-openapi/openapi3/validation_options.go index fd2be87..a7ad0f3 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/validation_options.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/validation_options.go @@ -15,6 +15,7 @@ type ValidationOptions struct { schemaExtensionsInRefProhibited bool jsonSchema2020ValidationEnabled bool isOpenAPI31OrLater bool + isOpenAPI32OrLater bool multiErrorEnabled bool regexCompilerFunc RegexCompilerFunc extraSiblingFieldsAllowed map[string]struct{} @@ -42,6 +43,15 @@ func IsOpenAPI31OrLater() ValidationOption { } } +// IsOpenAPI32OrLater enables validation for OpenAPI 3.2 documents. +func IsOpenAPI32OrLater() ValidationOption { + return func(options *ValidationOptions) { + options.isOpenAPI31OrLater = true + options.isOpenAPI32OrLater = true + options.jsonSchema2020ValidationEnabled = true + } +} + // EnableSchemaFormatValidation makes Validate not return an error when validating documents that mention schema formats that are not defined by the OpenAPIv3 specification. // By default, schema format validation is disabled. func EnableSchemaFormatValidation() ValidationOption { diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/walk.go b/vendor/github.com/getkin/kin-openapi/openapi3/walk.go new file mode 100644 index 0000000..8d7b956 --- /dev/null +++ b/vendor/github.com/getkin/kin-openapi/openapi3/walk.go @@ -0,0 +1,318 @@ +package openapi3 + +import ( + "errors" + "maps" + "slices" + "strconv" + "strings" +) + +// SkipSubtree, when returned by a WalkSchemasFunc, tells WalkSchemas not to +// descend into the current schema's sub-schemas. The schema itself has already +// been visited. Any other non-nil error stops the walk and is returned by +// WalkSchemas. It mirrors filepath.SkipDir. +var SkipSubtree = errors.New("skip schema subtree") + +// WalkSchemasFunc is called once for each schema visited by WalkSchemas. +// +// jsonPointer is the RFC 6901 JSON Pointer of the schema within the document, +// e.g. "/components/schemas/Pet/properties/tag" or +// "/paths/~1pets/get/responses/200/content/application~1json/schema". It is +// file-agnostic: once the loader has resolved external $refs the document is a +// single tree, so the pointer addresses a position in that tree and never +// encodes a source file. schema is non-nil and schema.Value is non-nil; the +// callback may modify schema.Value in place, so WalkSchemas serves transformers +// and not only read-only inspection. +// +// Returning SkipSubtree skips this schema's sub-schemas; returning any other +// error aborts the walk and is returned by WalkSchemas. +type WalkSchemasFunc func(jsonPointer string, schema *SchemaRef) error + +// WalkSchemas visits every schema reachable from the document exactly once, +// invoking fn for each. It follows resolved $ref targets (schema.Value) and +// guards against reference cycles, so each distinct *Schema is visited a single +// time regardless of how many references point at it. Maps are visited in +// sorted key order, so the traversal is deterministic. +// +// It covers schemas under components (schemas, parameters, headers, request +// bodies, responses, callbacks), the paths and their operations (parameters, +// request bodies, responses, headers, callbacks), and webhooks, then recurses +// through every sub-schema keyword: properties, items, itemSchema, +// allOf/anyOf/oneOf, not, additionalProperties, prefixItems, contains, patternProperties, +// dependentSchemas, propertyNames, if/then/else, and $defs. +// +// It is useful for validation, code generation, schema transformation, +// $ref/dependency analysis, and documentation: any consumer that needs to act +// on every schema in a document without re-deriving the (easy to get wrong) +// traversal itself. +func (doc *T) WalkSchemas(fn WalkSchemasFunc) error { + if doc == nil { + return nil + } + w := schemaWalker{fn: fn, seen: make(map[*Schema]struct{})} + return w.document(doc) +} + +type schemaWalker struct { + fn WalkSchemasFunc + seen map[*Schema]struct{} +} + +// escapeRefString escapes a single JSON Pointer reference token per RFC 6901: +// '~' becomes '~0' and '/' becomes '~1'. It is the inverse of unescapeRefString. +func escapeRefString(s string) string { + if !strings.ContainsAny(s, "~/") { + return s + } + return strings.NewReplacer("~", "~0", "/", "~1").Replace(s) +} + +func (w *schemaWalker) document(doc *T) error { + if c := doc.Components; c != nil { + for _, name := range slices.Sorted(maps.Keys(c.Schemas)) { + if err := w.schemaRef("/components/schemas/"+escapeRefString(name), c.Schemas[name]); err != nil { + return err + } + } + for _, name := range slices.Sorted(maps.Keys(c.Parameters)) { + if err := w.parameter("/components/parameters/"+escapeRefString(name), c.Parameters[name]); err != nil { + return err + } + } + for _, name := range slices.Sorted(maps.Keys(c.Headers)) { + if err := w.header("/components/headers/"+escapeRefString(name), c.Headers[name]); err != nil { + return err + } + } + for _, name := range slices.Sorted(maps.Keys(c.RequestBodies)) { + if rbr := c.RequestBodies[name]; rbr != nil && rbr.Value != nil { + if err := w.content("/components/requestBodies/"+escapeRefString(name)+"/content", rbr.Value.Content); err != nil { + return err + } + } + } + for _, name := range slices.Sorted(maps.Keys(c.Responses)) { + if err := w.response("/components/responses/"+escapeRefString(name), c.Responses[name]); err != nil { + return err + } + } + for _, name := range slices.Sorted(maps.Keys(c.Callbacks)) { + if cbr := c.Callbacks[name]; cbr != nil && cbr.Value != nil { + if err := w.callback("/components/callbacks/"+escapeRefString(name), cbr.Value); err != nil { + return err + } + } + } + } + if doc.Paths != nil { + items := doc.Paths.Map() + for _, path := range slices.Sorted(maps.Keys(items)) { + if err := w.pathItem("/paths/"+escapeRefString(path), items[path]); err != nil { + return err + } + } + } + for _, name := range slices.Sorted(maps.Keys(doc.Webhooks)) { + if err := w.pathItem("/webhooks/"+escapeRefString(name), doc.Webhooks[name]); err != nil { + return err + } + } + return nil +} + +func (w *schemaWalker) pathItem(ptr string, item *PathItem) error { + if item == nil { + return nil + } + for i, pr := range item.Parameters { + if err := w.parameter(ptr+"/parameters/"+strconv.Itoa(i), pr); err != nil { + return err + } + } + ops := item.Operations() + for _, method := range slices.Sorted(maps.Keys(ops)) { + if err := w.operation(ptr+"/"+strings.ToLower(method), ops[method]); err != nil { + return err + } + } + return nil +} + +func (w *schemaWalker) operation(ptr string, op *Operation) error { + if op == nil { + return nil + } + for i, pr := range op.Parameters { + if err := w.parameter(ptr+"/parameters/"+strconv.Itoa(i), pr); err != nil { + return err + } + } + if op.RequestBody != nil && op.RequestBody.Value != nil { + if err := w.content(ptr+"/requestBody/content", op.RequestBody.Value.Content); err != nil { + return err + } + } + if op.Responses != nil { + responses := op.Responses.Map() + for _, code := range slices.Sorted(maps.Keys(responses)) { + if err := w.response(ptr+"/responses/"+escapeRefString(code), responses[code]); err != nil { + return err + } + } + } + for _, name := range slices.Sorted(maps.Keys(op.Callbacks)) { + if cbr := op.Callbacks[name]; cbr != nil && cbr.Value != nil { + if err := w.callback(ptr+"/callbacks/"+escapeRefString(name), cbr.Value); err != nil { + return err + } + } + } + return nil +} + +func (w *schemaWalker) callback(ptr string, cb *Callback) error { + items := cb.Map() + for _, expr := range slices.Sorted(maps.Keys(items)) { + if err := w.pathItem(ptr+"/"+escapeRefString(expr), items[expr]); err != nil { + return err + } + } + return nil +} + +func (w *schemaWalker) parameter(ptr string, pr *ParameterRef) error { + if pr == nil || pr.Value == nil { + return nil + } + if err := w.schemaRef(ptr+"/schema", pr.Value.Schema); err != nil { + return err + } + return w.content(ptr+"/content", pr.Value.Content) +} + +func (w *schemaWalker) header(ptr string, hr *HeaderRef) error { + if hr == nil || hr.Value == nil { + return nil + } + if err := w.schemaRef(ptr+"/schema", hr.Value.Schema); err != nil { + return err + } + return w.content(ptr+"/content", hr.Value.Content) +} + +func (w *schemaWalker) response(ptr string, rr *ResponseRef) error { + if rr == nil || rr.Value == nil { + return nil + } + for _, name := range slices.Sorted(maps.Keys(rr.Value.Headers)) { + if err := w.header(ptr+"/headers/"+escapeRefString(name), rr.Value.Headers[name]); err != nil { + return err + } + } + return w.content(ptr+"/content", rr.Value.Content) +} + +func (w *schemaWalker) content(ptr string, content Content) error { + for _, mediaType := range slices.Sorted(maps.Keys(content)) { + media := content[mediaType] + if media == nil { + continue + } + if err := w.schemaRef(ptr+"/"+escapeRefString(mediaType)+"/schema", media.Schema); err != nil { + return err + } + if err := w.schemaRef(ptr+"/"+escapeRefString(mediaType)+"/itemSchema", media.ItemSchema); err != nil { + return err + } + } + return nil +} + +func (w *schemaWalker) schemaRefs(ptr string, refs SchemaRefs) error { + for i, sub := range refs { + if err := w.schemaRef(ptr+"/"+strconv.Itoa(i), sub); err != nil { + return err + } + } + return nil +} + +func (w *schemaWalker) schemaRef(ptr string, sr *SchemaRef) error { + if sr == nil || sr.Value == nil { + return nil + } + s := sr.Value + if _, ok := w.seen[s]; ok { + // Already visited (shared $ref target or reference cycle). + return nil + } + w.seen[s] = struct{}{} + + if err := w.fn(ptr, sr); err != nil { + if errors.Is(err, SkipSubtree) { + return nil + } + return err + } + + for _, name := range slices.Sorted(maps.Keys(s.Properties)) { + if err := w.schemaRef(ptr+"/properties/"+escapeRefString(name), s.Properties[name]); err != nil { + return err + } + } + if err := w.schemaRef(ptr+"/items", s.Items); err != nil { + return err + } + if s.AdditionalProperties.Schema != nil { + if err := w.schemaRef(ptr+"/additionalProperties", s.AdditionalProperties.Schema); err != nil { + return err + } + } + if err := w.schemaRefs(ptr+"/allOf", s.AllOf); err != nil { + return err + } + if err := w.schemaRefs(ptr+"/anyOf", s.AnyOf); err != nil { + return err + } + if err := w.schemaRefs(ptr+"/oneOf", s.OneOf); err != nil { + return err + } + if err := w.schemaRef(ptr+"/not", s.Not); err != nil { + return err + } + if err := w.schemaRefs(ptr+"/prefixItems", s.PrefixItems); err != nil { + return err + } + if err := w.schemaRef(ptr+"/contains", s.Contains); err != nil { + return err + } + for _, name := range slices.Sorted(maps.Keys(s.PatternProperties)) { + if err := w.schemaRef(ptr+"/patternProperties/"+escapeRefString(name), s.PatternProperties[name]); err != nil { + return err + } + } + for _, name := range slices.Sorted(maps.Keys(s.DependentSchemas)) { + if err := w.schemaRef(ptr+"/dependentSchemas/"+escapeRefString(name), s.DependentSchemas[name]); err != nil { + return err + } + } + if err := w.schemaRef(ptr+"/propertyNames", s.PropertyNames); err != nil { + return err + } + if err := w.schemaRef(ptr+"/if", s.If); err != nil { + return err + } + if err := w.schemaRef(ptr+"/then", s.Then); err != nil { + return err + } + if err := w.schemaRef(ptr+"/else", s.Else); err != nil { + return err + } + for _, name := range slices.Sorted(maps.Keys(s.Defs)) { + if err := w.schemaRef(ptr+"/$defs/"+escapeRefString(name), s.Defs[name]); err != nil { + return err + } + } + return nil +} diff --git a/vendor/github.com/oasdiff/yaml/.golangci.toml b/vendor/github.com/oasdiff/yaml/.golangci.toml deleted file mode 100644 index 61b2b79..0000000 --- a/vendor/github.com/oasdiff/yaml/.golangci.toml +++ /dev/null @@ -1,16 +0,0 @@ -[run] -timeout = "120s" - -[output] -format = "colored-line-number" - -[linters] -enable = [ - "gocyclo", "unconvert", "goimports", "unused", "unused", - "vetshadow", "nakedret", "errcheck", "revive", "ineffassign", - "goconst", "vet", "unparam", "gofmt" -] - -[issues] -exclude-use-default = false - diff --git a/vendor/github.com/oasdiff/yaml/.golangci.yml b/vendor/github.com/oasdiff/yaml/.golangci.yml new file mode 100644 index 0000000..1324e85 --- /dev/null +++ b/vendor/github.com/oasdiff/yaml/.golangci.yml @@ -0,0 +1,26 @@ +version: "2" + +run: + timeout: 120s + +linters: + enable: + - errcheck + - goconst + - gocyclo + - govet + - ineffassign + - nakedret + - revive + - unconvert + - unparam + - unused + settings: + govet: + enable: + - shadow + +formatters: + enable: + - gofmt + - goimports diff --git a/vendor/github.com/oasdiff/yaml/README.md b/vendor/github.com/oasdiff/yaml/README.md index a40f33d..dd6036e 100644 --- a/vendor/github.com/oasdiff/yaml/README.md +++ b/vendor/github.com/oasdiff/yaml/README.md @@ -1,9 +1,9 @@ # YAML marshaling and unmarshaling support for Go -[![Lint](https://github.com/invopop/yaml/actions/workflows/lint.yaml/badge.svg)](https://github.com/invopop/yaml/actions/workflows/lint.yaml) -[![Test Go](https://github.com/invopop/yaml/actions/workflows/test.yaml/badge.svg)](https://github.com/invopop/yaml/actions/workflows/test.yaml) -[![Go Report Card](https://goreportcard.com/badge/github.com/invopop/yaml)](https://goreportcard.com/report/github.com/invopop/yaml) -![Latest Tag](https://img.shields.io/github/v/tag/invopop/yaml) +[![Lint](https://github.com/oasdiff/yaml/actions/workflows/lint.yaml/badge.svg)](https://github.com/oasdiff/yaml/actions/workflows/lint.yaml) +[![Test Go](https://github.com/oasdiff/yaml/actions/workflows/test.yaml/badge.svg)](https://github.com/oasdiff/yaml/actions/workflows/test.yaml) +[![Go Report Card](https://goreportcard.com/badge/github.com/oasdiff/yaml)](https://goreportcard.com/report/github.com/oasdiff/yaml) +![Latest Tag](https://img.shields.io/github/v/tag/oasdiff/yaml) ## Fork This fork is an improved version of the invopop/yaml package, designed to include line and column location information for YAML elements during unmarshalling. @@ -63,13 +63,13 @@ GOOD: To install, run: ``` -$ go get github.com/invopop/yaml +$ go get github.com/oasdiff/yaml ``` And import using: ``` -import "github.com/invopop/yaml" +import "github.com/oasdiff/yaml" ``` Usage is very similar to the JSON library: @@ -80,7 +80,7 @@ package main import ( "fmt" - "github.com/invopop/yaml" + "github.com/oasdiff/yaml" ) type Person struct { @@ -124,7 +124,7 @@ package main import ( "fmt" - "github.com/invopop/yaml" + "github.com/oasdiff/yaml" ) func main() { diff --git a/vendor/github.com/oasdiff/yaml/yaml.go b/vendor/github.com/oasdiff/yaml/yaml.go index 86882f2..3ec5366 100644 --- a/vendor/github.com/oasdiff/yaml/yaml.go +++ b/vendor/github.com/oasdiff/yaml/yaml.go @@ -5,7 +5,7 @@ // uses json.Marshal and json.Unmarshal to convert to or from the struct. This // means that it effectively reuses the JSON struct tags as well as the custom // JSON methods MarshalJSON and UnmarshalJSON unlike go-yaml. -package yaml // import "github.com/invopop/yaml" +package yaml // import "github.com/oasdiff/yaml" import ( "bytes" diff --git a/vendor/github.com/oasdiff/yaml3/decode.go b/vendor/github.com/oasdiff/yaml3/decode.go index 4a6e076..f36d6bf 100644 --- a/vendor/github.com/oasdiff/yaml3/decode.go +++ b/vendor/github.com/oasdiff/yaml3/decode.go @@ -187,6 +187,13 @@ func (p *parser) node(kind Kind, defaultTag, tag, value string) *Node { if !p.textless { n.Line = p.event.start_mark.line + 1 n.Column = p.event.start_mark.column + 1 + // end_mark is the position just past this event. For scalars and aliases + // it already spans the whole node. For mappings and sequences this is the + // MAPPING-START/SEQUENCE-START event, so it only marks the start for now; + // mapping()/sequence() overwrite it from the matching END event so the + // span covers the whole block. + n.EndLine = p.event.end_mark.line + 1 + n.EndColumn = p.event.end_mark.column + 1 n.HeadComment = string(p.event.head_comment) n.LineComment = string(p.event.line_comment) n.FootComment = string(p.event.foot_comment) @@ -264,6 +271,26 @@ func (p *parser) sequence() *Node { } n.LineComment = string(p.event.line_comment) n.FootComment = string(p.event.foot_comment) + // End at the last item's end so the span reaches the end of the actual + // content, consistent with scalars/aliases. The SEQUENCE-END token sits at + // the start of the following line after a block dedent, which would + // overshoot the element. Empty sequences fall back to that token's mark. + if !p.textless { + if n.Style&FlowStyle != 0 { + // Flow collections close with an explicit `}`/`]`; the END token's + // mark is just past that delimiter, so the span covers the whole + // collection (and stays consistent with the empty-flow fallback, + // which has no last child and uses the same mark). + n.EndLine = p.event.end_mark.line + 1 + n.EndColumn = p.event.end_mark.column + 1 + } else if len(n.Content) > 0 { + last := n.Content[len(n.Content)-1] + n.EndLine, n.EndColumn = last.EndLine, last.EndColumn + } else { + n.EndLine = p.event.end_mark.line + 1 + n.EndColumn = p.event.end_mark.column + 1 + } + } p.expect(yaml_SEQUENCE_END_EVENT) return n } @@ -304,6 +331,28 @@ func (p *parser) mapping() *Node { n.Content[len(n.Content)-2].FootComment = n.FootComment n.FootComment = "" } + // End at the last entry's value end so the span reaches the end of the + // actual content, consistent with scalars/aliases. The MAPPING-END token + // sits at the start of the following line after a block dedent, which would + // overshoot the element. Empty mappings fall back to that token's mark. + // (Origin __origin__ nodes are appended later, during decode, so the last + // element here is a real value.) + if !p.textless { + if n.Style&FlowStyle != 0 { + // Flow collections close with an explicit `}`/`]`; the END token's + // mark is just past that delimiter, so the span covers the whole + // collection (and stays consistent with the empty-flow fallback, + // which has no last child and uses the same mark). + n.EndLine = p.event.end_mark.line + 1 + n.EndColumn = p.event.end_mark.column + 1 + } else if len(n.Content) > 0 { + last := n.Content[len(n.Content)-1] + n.EndLine, n.EndColumn = last.EndLine, last.EndColumn + } else { + n.EndLine = p.event.end_mark.line + 1 + n.EndColumn = p.event.end_mark.column + 1 + } + } p.expect(yaml_MAPPING_END_EVENT) return n } diff --git a/vendor/github.com/oasdiff/yaml3/origin.go b/vendor/github.com/oasdiff/yaml3/origin.go index 3f2f0a7..2edec47 100644 --- a/vendor/github.com/oasdiff/yaml3/origin.go +++ b/vendor/github.com/oasdiff/yaml3/origin.go @@ -33,7 +33,7 @@ func addOriginInMap(key, n *Node, file string) *Node { // addOrigin injects a compact __origin__ sequence into the mapping node n. // -// Format: [file, key_name, key_line, key_col, nf, f1_name, f1_delta, f1_col, ..., ns, s1_name, s1_count, s1_l0_delta, s1_c0, ...] +// Format: [file, key_name, key_line, key_col, nf, f1_name, f1_delta, f1_col, ..., ns, s1_name, s1_count, s1_l0_delta, s1_c0, ..., end_delta, end_col] // // - file: source file path // - key_name: the YAML key whose value is this mapping @@ -42,6 +42,10 @@ func addOriginInMap(key, n *Node, file string) *Node { // - per field: name (string), line delta from key_line (int), column (int) // - ns: number of sequence fields that have item locations // - per sequence: name (string), item count (int), then count × (line delta, col) +// - end_delta, end_col: end of the whole mapping block — line delta from +// key_line and absolute column of the position just past its last content. +// Appended last so a consumer that stops after the sequences section +// simply ignores it (backward compatible). func addOrigin(key, n *Node, file string) *Node { if isOrigin(key) { return n @@ -108,6 +112,16 @@ func buildOriginSeq(key, n *Node, file string) []*Node { nodes = append(nodes, fieldNodes...) nodes = append(nodes, intNode(ns)) nodes = append(nodes, seqNodes...) + + // Block end: line delta from key_line and absolute end column of the whole + // mapping. Lets a consumer reconstruct the full block span + // [key_line, key_line+end_delta] -- e.g. an entire endpoint operation block. + endDelta, endCol := 0, 0 + if n.EndLine > 0 { + endDelta = n.EndLine - key.Line + endCol = n.EndColumn + } + nodes = append(nodes, intNode(endDelta), intNode(endCol)) return nodes } diff --git a/vendor/github.com/oasdiff/yaml3/scannerc.go b/vendor/github.com/oasdiff/yaml3/scannerc.go index ca00701..7ee4f10 100644 --- a/vendor/github.com/oasdiff/yaml3/scannerc.go +++ b/vendor/github.com/oasdiff/yaml3/scannerc.go @@ -2309,6 +2309,12 @@ func yaml_parser_scan_block_scalar(parser *yaml_parser_t, token *yaml_token_t, l return false } var leading_blank, trailing_blank bool + // content_end_mark tracks the position just past the last content character + // (captured after each content line is read, before its line break is + // consumed). The block scalar's end belongs here: end_mark otherwise advances + // to the start of the next line, which overshoots the span into the following + // node. Initialized to end_mark so an empty block scalar is unchanged. + content_end_mark := end_mark for parser.mark.column == indent && !is_z(parser.buffer, parser.buffer_pos) { // We are at the beginning of a non-empty line. @@ -2340,6 +2346,7 @@ func yaml_parser_scan_block_scalar(parser *yaml_parser_t, token *yaml_token_t, l return false } } + content_end_mark = parser.mark // Consume the line break. if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { @@ -2366,7 +2373,7 @@ func yaml_parser_scan_block_scalar(parser *yaml_parser_t, token *yaml_token_t, l *token = yaml_token_t{ typ: yaml_SCALAR_TOKEN, start_mark: start_mark, - end_mark: end_mark, + end_mark: content_end_mark, value: s, style: yaml_LITERAL_SCALAR_STYLE, } diff --git a/vendor/github.com/oasdiff/yaml3/yaml.go b/vendor/github.com/oasdiff/yaml3/yaml.go index 4f91be8..f8095e3 100644 --- a/vendor/github.com/oasdiff/yaml3/yaml.go +++ b/vendor/github.com/oasdiff/yaml3/yaml.go @@ -439,6 +439,14 @@ type Node struct { // These fields are not respected when encoding the node. Line int Column int + + // EndLine and EndColumn hold the position just past the end of the node in + // the decoded YAML text. For a mapping or sequence this spans the whole + // block (so a caller can extract the entire collection), not just its first + // line. Like Line and Column, these are 1-based and are not respected when + // encoding the node. + EndLine int + EndColumn int } // IsZero returns whether the node has all of its fields unset. diff --git a/vendor/modules.txt b/vendor/modules.txt index d41bd12..3242c1a 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -4,7 +4,7 @@ github.com/clipperhouse/uax29/v2/graphemes # github.com/davecgh/go-spew v1.1.1 ## explicit github.com/davecgh/go-spew/spew -# github.com/getkin/kin-openapi v0.140.0 +# github.com/getkin/kin-openapi v0.142.0 ## explicit; go 1.25 github.com/getkin/kin-openapi/openapi3 # github.com/go-chi/chi/v5 v5.3.0 @@ -75,11 +75,11 @@ github.com/mattn/go-runewidth # github.com/mitchellh/go-homedir v1.1.0 ## explicit github.com/mitchellh/go-homedir -# github.com/oasdiff/yaml v0.1.0 -## explicit; go 1.22.5 +# github.com/oasdiff/yaml v0.1.1 +## explicit; go 1.25 github.com/oasdiff/yaml -# github.com/oasdiff/yaml3 v0.0.13 -## explicit; go 1.22.5 +# github.com/oasdiff/yaml3 v0.0.14 +## explicit; go 1.25 github.com/oasdiff/yaml3 # github.com/pmezard/go-difflib v1.0.0 ## explicit