diff --git a/go.mod b/go.mod index ac48311..e80fc05 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.143.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..5a4f44b 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.143.0 h1:mIrOpir9J5x2m1vdree2rhuJ/GYGwbTVBp1kuSCJ62Y= +github.com/getkin/kin-openapi v0.143.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/callback.go b/vendor/github.com/getkin/kin-openapi/openapi3/callback.go index 407f730..6350d79 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/callback.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/callback.go @@ -47,9 +47,3 @@ func (callback *Callback) Validate(ctx context.Context, opts ...ValidationOption return validateExtensions(ctx, callback.Extensions, callback.Origin) } - -// UnmarshalJSON sets Callbacks to a copy of data. -func (callbacks *Callbacks) UnmarshalJSON(data []byte) (err error) { - *callbacks, err = unmarshalStringMapP[CallbackRef](data) - return -} diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/content.go b/vendor/github.com/getkin/kin-openapi/openapi3/content.go index 8bf7921..ab79147 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/content.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/content.go @@ -115,9 +115,3 @@ func (content Content) Validate(ctx context.Context, opts ...ValidationOption) e } return nil } - -// UnmarshalJSON sets Content to a copy of data. -func (content *Content) UnmarshalJSON(data []byte) (err error) { - *content, err = unmarshalStringMapP[MediaType](data) - return -} diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/discriminator.go b/vendor/github.com/getkin/kin-openapi/openapi3/discriminator.go index d797701..64219d8 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/discriminator.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/discriminator.go @@ -13,7 +13,7 @@ type Discriminator struct { Origin *Origin `json:"-" yaml:"-"` PropertyName string `json:"propertyName" yaml:"propertyName"` // required - Mapping StringMap[MappingRef] `json:"mapping,omitempty" yaml:"mapping,omitempty"` + Mapping map[string]MappingRef `json:"mapping,omitempty" yaml:"mapping,omitempty"` } // MappingRef is a ref to a Schema objects. Unlike SchemaRefs it is serialised 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/encoding.go b/vendor/github.com/getkin/kin-openapi/openapi3/encoding.go index fd9316a..1094488 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/encoding.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/encoding.go @@ -26,12 +26,6 @@ func NewEncoding() *Encoding { // Encodings is a map of encoding objects keyed by field name. type Encodings map[string]*Encoding -// UnmarshalJSON sets Encodings to a copy of data. -func (encodings *Encodings) UnmarshalJSON(data []byte) (err error) { - *encodings, err = unmarshalStringMapP[Encoding](data) - return -} - func (encoding *Encoding) WithHeader(name string, header *Header) *Encoding { return encoding.WithHeaderRef(name, &HeaderRef{ Value: header, diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/example.go b/vendor/github.com/getkin/kin-openapi/openapi3/example.go index eeb4c6d..eeda137 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/example.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/example.go @@ -82,9 +82,3 @@ func (example *Example) Validate(ctx context.Context, opts ...ValidationOption) return validateExtensions(ctx, example.Extensions, example.Origin) } - -// UnmarshalJSON sets Examples to a copy of data. -func (examples *Examples) UnmarshalJSON(data []byte) (err error) { - *examples, err = unmarshalStringMapP[ExampleRef](data) - return -} diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/header.go b/vendor/github.com/getkin/kin-openapi/openapi3/header.go index 466c8ea..36b74dd 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/header.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/header.go @@ -92,9 +92,3 @@ func (header *Header) Validate(ctx context.Context, opts ...ValidationOption) er } return nil } - -// UnmarshalJSON sets Headers to a copy of data. -func (headers *Headers) UnmarshalJSON(data []byte) (err error) { - *headers, err = unmarshalStringMapP[HeaderRef](data) - return -} diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/link.go b/vendor/github.com/getkin/kin-openapi/openapi3/link.go index 60f78a0..eafeb42 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/link.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/link.go @@ -91,9 +91,3 @@ func (link *Link) Validate(ctx context.Context, opts ...ValidationOption) error return validateExtensions(ctx, link.Extensions, link.Origin) } - -// UnmarshalJSON sets Links to a copy of data. -func (links *Links) UnmarshalJSON(data []byte) (err error) { - *links, err = unmarshalStringMapP[LinkRef](data) - return -} diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/loader.go b/vendor/github.com/getkin/kin-openapi/openapi3/loader.go index 1974294..2f2480c 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/loader.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/loader.go @@ -64,6 +64,13 @@ type Loader struct { visitedDocuments map[string]*T + // originTrees retains each loaded document's origin tree, keyed by the + // document itself so insert and lookup cannot disagree, populated when + // IncludeOrigin is set. resolveComponent uses it to re-attach origins to + // components that lose them in the generic-map path, without re-reading + // or re-parsing the file. + originTrees map[*T]*originTree + visitedRefs map[string]struct{} visitedPath []string backtrack map[string][]func(value any) @@ -133,13 +140,25 @@ func (loader *Loader) loadSingleElementFromURI(ref string, rootPath *url.URL, el if err != nil { return nil, err } - if err := unmarshal(data, element, loader.IncludeOrigin, resolvedPath); err != nil { + if _, err := unmarshal(data, element, loader.IncludeOrigin, resolvedPath); err != nil { return nil, err } return resolvedPath, nil } +// rememberOriginTree retains doc's origin tree for attachOriginToResolved. +// tree is nil when IncludeOrigin is off or the data took the json path. +func (loader *Loader) rememberOriginTree(doc *T, tree *originTree) { + if tree == nil { + return + } + if loader.originTrees == nil { + loader.originTrees = make(map[*T]*originTree) + } + loader.originTrees[doc] = tree +} + func (loader *Loader) readURL(location *url.URL) ([]byte, error) { if f := loader.ReadFromURIFunc; f != nil { return f(loader, location) @@ -169,9 +188,11 @@ func (loader *Loader) LoadFromIoReader(reader io.Reader) (*T, error) { func (loader *Loader) LoadFromData(data []byte) (*T, error) { loader.resetVisitedPathItemRefs() doc := &T{} - if err := unmarshal(data, doc, loader.IncludeOrigin, nil); err != nil { + tree, err := unmarshal(data, doc, loader.IncludeOrigin, nil) + if err != nil { return nil, err } + loader.rememberOriginTree(doc, tree) if err := loader.ResolveRefsIn(doc, nil); err != nil { return nil, err } @@ -198,9 +219,11 @@ func (loader *Loader) loadFromDataWithPathInternal(data []byte, location *url.UR doc := &T{} loader.visitedDocuments[uri] = doc - if err := unmarshal(data, doc, loader.IncludeOrigin, location); err != nil { + tree, err := unmarshal(data, doc, loader.IncludeOrigin, location) + if err != nil { return nil, err } + loader.rememberOriginTree(doc, tree) doc.url = copyURI(location) @@ -468,7 +491,7 @@ func (loader *Loader) resolveComponent(doc *T, ref string, path *url.URL, resolv if err2 != nil { return nil, nil, err } - if err2 = unmarshal(data, &cursor, loader.IncludeOrigin, path); err2 != nil { + if _, err2 = unmarshal(data, &cursor, loader.IncludeOrigin, path); err2 != nil { return nil, nil, err } if cursor, err2 = drill(cursor); err2 != nil || cursor == nil { @@ -516,6 +539,10 @@ func (loader *Loader) resolveComponent(doc *T, ref string, path *url.URL, resolv if err := codec(cursor, resolved); err != nil { return nil, nil, fmt.Errorf("bad data in %q (expecting %s)", ref, readableType(resolved)) } + // The value came from a generic map in T.Extensions (a $ref to an + // arbitrary top-level key), so the json round-trip above stripped its + // origins. Re-attach them from the file, best-effort. + loader.attachOriginToResolved(resolved, componentDoc, fragment) return componentDoc, componentPath, nil default: @@ -523,6 +550,33 @@ func (loader *Loader) resolveComponent(doc *T, ref string, path *url.URL, resolv } } +// attachOriginToResolved re-attaches source origins to a component resolved +// through the generic-map path: a $ref to a schema under an arbitrary top-level +// key lands in T.Extensions, and the json round-trip in resolveComponent strips +// its origin. It walks the document's retained origin tree (see originTrees, +// populated when the document was first unmarshaled: no re-read, no re-parse) +// down to the ref fragment and applies that subtree, so the object carries the +// same origins a typed resolution would, with the original file's line numbers. +// Best-effort: a missing tree or fragment leaves the object without origins. +func (loader *Loader) attachOriginToResolved(resolved any, componentDoc *T, fragment string) { + if !loader.IncludeOrigin { + return + } + tree := loader.originTrees[componentDoc] + if tree == nil { + return + } + for part := range strings.SplitSeq(strings.Trim(fragment, "/"), "/") { + if part == "" { + continue + } + if tree = tree.Fields[unescapeRefString(part)]; tree == nil { + return + } + } + applyOrigins(resolved, tree) +} + func readableType(x any) string { switch x.(type) { case *Callback: @@ -647,7 +701,17 @@ var ( errMUSTSecurityScheme = errors.New("invalid securityScheme: value MUST be an object") ) +func applyHeaderRefMetadata(value *Header, component *HeaderRef, isOpenAPI31OrLater bool) { + if !isOpenAPI31OrLater || value == nil || component.Description == nil { + return + } + + value.Description = *component.Description +} + func (loader *Loader) resolveHeaderRef(doc *T, component *HeaderRef, documentPath *url.URL) (err error) { + isOpenAPI31OrLater := doc.IsOpenAPI31OrLater() + if component.isEmpty() { return errMUSTHeader } @@ -658,6 +722,7 @@ func (loader *Loader) resolveHeaderRef(doc *T, component *HeaderRef, documentPat } if !loader.shouldVisitRef(ref, func(value any) { component.Value = value.(*Header) + applyHeaderRefMetadata(component.Value, component, isOpenAPI31OrLater) refPath, _ := loader.resolveRefPath(ref, documentPath) component.setRefPath(refPath) }) { @@ -687,6 +752,7 @@ func (loader *Loader) resolveHeaderRef(doc *T, component *HeaderRef, documentPat component.setRefPath(resolved.RefPath()) } defer loader.unvisitRef(ref, component.Value) + applyHeaderRefMetadata(component.Value, component, isOpenAPI31OrLater) } value := component.Value if value == nil { @@ -706,7 +772,17 @@ func (loader *Loader) resolveHeaderRef(doc *T, component *HeaderRef, documentPat return nil } +func applyParameterRefMetadata(value *Parameter, component *ParameterRef, isOpenAPI31OrLater bool) { + if !isOpenAPI31OrLater || value == nil || component.Description == nil { + return + } + + value.Description = *component.Description +} + func (loader *Loader) resolveParameterRef(doc *T, component *ParameterRef, documentPath *url.URL) (err error) { + isOpenAPI31OrLater := doc.IsOpenAPI31OrLater() + if component.isEmpty() { return errMUSTParameter } @@ -717,6 +793,7 @@ func (loader *Loader) resolveParameterRef(doc *T, component *ParameterRef, docum } if !loader.shouldVisitRef(ref, func(value any) { component.Value = value.(*Parameter) + applyParameterRefMetadata(component.Value, component, isOpenAPI31OrLater) refPath, _ := loader.resolveRefPath(ref, documentPath) component.setRefPath(refPath) }) { @@ -746,6 +823,7 @@ func (loader *Loader) resolveParameterRef(doc *T, component *ParameterRef, docum component.setRefPath(resolved.RefPath()) } defer loader.unvisitRef(ref, component.Value) + applyParameterRefMetadata(component.Value, component, isOpenAPI31OrLater) } value := component.Value if value == nil { @@ -756,16 +834,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 { @@ -781,7 +851,17 @@ func (loader *Loader) resolveParameterRef(doc *T, component *ParameterRef, docum return nil } +func applyRequestBodyRefMetadata(value *RequestBody, component *RequestBodyRef, isOpenAPI31OrLater bool) { + if !isOpenAPI31OrLater || value == nil || component.Description == nil { + return + } + + value.Description = *component.Description +} + func (loader *Loader) resolveRequestBodyRef(doc *T, component *RequestBodyRef, documentPath *url.URL) (err error) { + isOpenAPI31OrLater := doc.IsOpenAPI31OrLater() + if component.isEmpty() { return errMUSTRequestBody } @@ -792,6 +872,7 @@ func (loader *Loader) resolveRequestBodyRef(doc *T, component *RequestBodyRef, d } if !loader.shouldVisitRef(ref, func(value any) { component.Value = value.(*RequestBody) + applyRequestBodyRefMetadata(component.Value, component, isOpenAPI31OrLater) refPath, _ := loader.resolveRefPath(ref, documentPath) component.setRefPath(refPath) }) { @@ -821,6 +902,7 @@ func (loader *Loader) resolveRequestBodyRef(doc *T, component *RequestBodyRef, d component.setRefPath(resolved.RefPath()) } defer loader.unvisitRef(ref, component.Value) + applyRequestBodyRefMetadata(component.Value, component, isOpenAPI31OrLater) } value := component.Value if value == nil { @@ -828,27 +910,24 @@ 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 } +func applyResponseRefMetadata(value *Response, component *ResponseRef, isOpenAPI31OrLater bool) { + if !isOpenAPI31OrLater || value == nil || component.Description == nil { + return + } + + value.Description = component.Description +} + func (loader *Loader) resolveResponseRef(doc *T, component *ResponseRef, documentPath *url.URL) (err error) { + isOpenAPI31OrLater := doc.IsOpenAPI31OrLater() + if component.isEmpty() { return errMUSTResponse } @@ -859,6 +938,7 @@ func (loader *Loader) resolveResponseRef(doc *T, component *ResponseRef, documen } if !loader.shouldVisitRef(ref, func(value any) { component.Value = value.(*Response) + applyResponseRefMetadata(component.Value, component, isOpenAPI31OrLater) refPath, _ := loader.resolveRefPath(ref, documentPath) component.setRefPath(refPath) }) { @@ -888,6 +968,7 @@ func (loader *Loader) resolveResponseRef(doc *T, component *ResponseRef, documen component.setRefPath(resolved.RefPath()) } defer loader.unvisitRef(ref, component.Value) + applyResponseRefMetadata(component.Value, component, isOpenAPI31OrLater) } value := component.Value if value == nil { @@ -901,22 +982,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 +995,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 @@ -1109,7 +1200,17 @@ func (loader *Loader) resolveSchemaRef(doc *T, component *SchemaRef, documentPat return nil } +func applySecuritySchemeRefMetadata(value *SecurityScheme, component *SecuritySchemeRef, isOpenAPI31OrLater bool) { + if !isOpenAPI31OrLater || value == nil || component.Description == nil { + return + } + + value.Description = *component.Description +} + func (loader *Loader) resolveSecuritySchemeRef(doc *T, component *SecuritySchemeRef, documentPath *url.URL) (err error) { + isOpenAPI31OrLater := doc.IsOpenAPI31OrLater() + if component.isEmpty() { return errMUSTSecurityScheme } @@ -1120,6 +1221,7 @@ func (loader *Loader) resolveSecuritySchemeRef(doc *T, component *SecurityScheme } if !loader.shouldVisitRef(ref, func(value any) { component.Value = value.(*SecurityScheme) + applySecuritySchemeRefMetadata(component.Value, component, isOpenAPI31OrLater) refPath, _ := loader.resolveRefPath(ref, documentPath) component.setRefPath(refPath) }) { @@ -1149,17 +1251,34 @@ func (loader *Loader) resolveSecuritySchemeRef(doc *T, component *SecurityScheme component.setRefPath(resolved.RefPath()) } defer loader.unvisitRef(ref, component.Value) + applySecuritySchemeRefMetadata(component.Value, component, isOpenAPI31OrLater) } return nil } +func applyExampleRefMetadata(value *Example, component *ExampleRef, isOpenAPI31OrLater bool) { + if !isOpenAPI31OrLater || value == nil || (component.Summary == nil && component.Description == nil) { + return + } + + if component.Summary != nil { + value.Summary = *component.Summary + } + if component.Description != nil { + value.Description = *component.Description + } +} + func (loader *Loader) resolveExampleRef(doc *T, component *ExampleRef, documentPath *url.URL) (err error) { + isOpenAPI31OrLater := doc.IsOpenAPI31OrLater() + if ref := component.Ref; ref != "" { if component.Value != nil { return nil } if !loader.shouldVisitRef(ref, func(value any) { component.Value = value.(*Example) + applyExampleRefMetadata(component.Value, component, isOpenAPI31OrLater) refPath, _ := loader.resolveRefPath(ref, documentPath) component.setRefPath(refPath) }) { @@ -1189,6 +1308,7 @@ func (loader *Loader) resolveExampleRef(doc *T, component *ExampleRef, documentP component.setRefPath(resolved.RefPath()) } defer loader.unvisitRef(ref, component.Value) + applyExampleRefMetadata(component.Value, component, isOpenAPI31OrLater) } return nil } @@ -1249,7 +1369,17 @@ func (loader *Loader) resolveCallbackRef(doc *T, component *CallbackRef, documen return nil } +func applyLinkRefMetadata(value *Link, component *LinkRef, isOpenAPI31OrLater bool) { + if !isOpenAPI31OrLater || value == nil || component.Description == nil { + return + } + + value.Description = *component.Description +} + func (loader *Loader) resolveLinkRef(doc *T, component *LinkRef, documentPath *url.URL) (err error) { + isOpenAPI31OrLater := doc.IsOpenAPI31OrLater() + if component.isEmpty() { return errMUSTLink } @@ -1260,6 +1390,7 @@ func (loader *Loader) resolveLinkRef(doc *T, component *LinkRef, documentPath *u } if !loader.shouldVisitRef(ref, func(value any) { component.Value = value.(*Link) + applyLinkRefMetadata(component.Value, component, isOpenAPI31OrLater) refPath, _ := loader.resolveRefPath(ref, documentPath) component.setRefPath(refPath) }) { @@ -1289,6 +1420,7 @@ func (loader *Loader) resolveLinkRef(doc *T, component *LinkRef, documentPath *u component.setRefPath(resolved.RefPath()) } defer loader.unvisitRef(ref, component.Value) + applyLinkRefMetadata(component.Value, component, isOpenAPI31OrLater) } return nil } 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/marsh.go b/vendor/github.com/getkin/kin-openapi/openapi3/marsh.go index 6483fa9..f895e6e 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/marsh.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/marsh.go @@ -17,12 +17,15 @@ func unmarshalError(jsonUnmarshalErr error) error { return jsonUnmarshalErr } -func unmarshal(data []byte, v any, includeOrigin bool, location *url.URL) error { +// unmarshal decodes data into v. It returns the document origin tree when +// includeOrigin is set and the data took the yaml path (json input carries no +// origins), so the caller can retain it (see Loader.originTrees). +func unmarshal(data []byte, v any, includeOrigin bool, location *url.URL) (*originTree, error) { var jsonErr, yamlErr error // See https://github.com/getkin/kin-openapi/issues/680 if jsonErr = json.Unmarshal(data, v); jsonErr == nil { - return nil + return nil, nil } // UnmarshalStrict(data, v) TODO: investigate how ymlv3 handles duplicate map keys @@ -35,11 +38,11 @@ func unmarshal(data []byte, v any, includeOrigin bool, location *url.URL) error DisableTimestamps: true, }); err == nil { applyOrigins(v, tree) - return nil + return tree, nil } else { yamlErr = err } // If both unmarshaling attempts fail, return a new error that includes both errors - return fmt.Errorf("failed to unmarshal data: json error: %v, yaml error: %v", jsonErr, yamlErr) + return nil, fmt.Errorf("failed to unmarshal data: json error: %v, yaml error: %v", jsonErr, yamlErr) } 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..e070260 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,13 +103,13 @@ 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") if len(x.Extensions) == 0 { x.Extensions = nil } - delete(x.Encoding, originKey) *mediaType = MediaType(x) return nil } @@ -148,6 +152,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 +176,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..9671b91 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 { @@ -174,7 +177,6 @@ func (doc *T) UnmarshalJSON(data []byte) error { if len(x.Extensions) == 0 { x.Extensions = nil } - delete(x.Webhooks, originKey) *doc = T(x) return nil } @@ -266,6 +268,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..75e1e70 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/origin.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/origin.go @@ -2,13 +2,12 @@ package openapi3 import ( "reflect" + "sort" "strings" "github.com/oasdiff/yaml" ) -const originKey = "__origin__" - var originPtrType = reflect.TypeFor[*Origin]() // Origin contains the origin of a collection. @@ -27,6 +26,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 +105,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 +130,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 +209,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 +233,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: @@ -253,3 +324,7 @@ func jsonTagName(f reflect.StructField) string { name, _, _ := strings.Cut(tag, ",") return name } + +// originTree aliases the decoder-side origin tree, so the loader and marsh can +// carry it without referencing the yaml package directly. +type originTree = yaml.OriginTree diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/parameter.go b/vendor/github.com/getkin/kin-openapi/openapi3/parameter.go index b1010a6..2b99626 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/parameter.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/parameter.go @@ -409,9 +409,3 @@ func (parameter *Parameter) Validate(ctx context.Context, opts ...ValidationOpti return validateExtensions(ctx, parameter.Extensions, parameter.Origin) } - -// UnmarshalJSON sets ParametersMap to a copy of data. -func (parametersMap *ParametersMap) UnmarshalJSON(data []byte) (err error) { - *parametersMap, err = unmarshalStringMapP[ParameterRef](data) - return -} diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/ref.go b/vendor/github.com/getkin/kin-openapi/openapi3/ref.go index 095178d..6d2ae82 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/ref.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/ref.go @@ -8,12 +8,15 @@ import ( //go:generate go run refsgenerator.go -// Ref is specified by OpenAPI/Swagger 3.0 standard. -// See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#reference-object +// Ref represents the common fields of an OpenAPI Reference Object. +// Summary and Description are supported by OpenAPI 3.1 and later. +// See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.2.md#reference-object type Ref struct { - Ref string `json:"$ref" yaml:"$ref"` - Extensions map[string]any `json:"-" yaml:"-"` - Origin *Origin `json:"-" yaml:"-"` + Ref string `json:"$ref" yaml:"$ref"` + Summary *string `json:"summary,omitempty" yaml:"summary,omitempty"` + Description *string `json:"description,omitempty" yaml:"description,omitempty"` + Extensions map[string]any `json:"-" yaml:"-"` + Origin *Origin `json:"-" yaml:"-"` } // MarshalYAML returns the YAML encoding of Ref. @@ -23,6 +26,12 @@ func (x Ref) MarshalYAML() (any, error) { if x := x.Ref; x != "" { m["$ref"] = x } + if x.Summary != nil { + m["summary"] = *x.Summary + } + if x.Description != nil { + m["description"] = *x.Description + } return m, nil } diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/refs.go b/vendor/github.com/getkin/kin-openapi/openapi3/refs.go index 2a4066f..c04d305 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/refs.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/refs.go @@ -17,6 +17,9 @@ type CallbackRef struct { // are allowed by the openapi spec. Extensions map[string]any Origin *Origin `json:"-" yaml:"-"` + // Reference Object summary and description are supported in OpenAPI 3.1+. + Summary *string `json:"summary,omitempty" yaml:"summary,omitempty"` + Description *string `json:"description,omitempty" yaml:"description,omitempty"` Ref string Value *Callback @@ -51,7 +54,12 @@ func (x *CallbackRef) setRefPath(u *url.URL) { // MarshalYAML returns the YAML encoding of CallbackRef. func (x CallbackRef) MarshalYAML() (any, error) { if ref := x.Ref; ref != "" { - return &Ref{Ref: ref, Extensions: x.Extensions}, nil + return &Ref{ + Ref: ref, + Extensions: x.Extensions, + Summary: x.Summary, + Description: x.Description, + }, nil } return x.Value.MarshalYAML() } @@ -74,6 +82,10 @@ func (x *CallbackRef) UnmarshalJSON(data []byte) error { delete(extra, "$ref") x.Ref = refOnly.Ref x.Origin = refOnly.Origin + x.Summary = refOnly.Summary + x.Description = refOnly.Description + delete(extra, "summary") + delete(extra, "description") if len(extra) != 0 { x.extra = componentNames(extra) for k := range extra { @@ -100,6 +112,14 @@ func (x *CallbackRef) validateExtras(ctx context.Context) error { if allowed == nil { allowed = make(map[string]struct{}) } + if !validationOpts.isOpenAPI31OrLater { + if _, ok := allowed["description"]; x.Description != nil && !ok { + extras = append(extras, "description") + } + if _, ok := allowed["summary"]; x.Summary != nil && !ok { + extras = append(extras, "summary") + } + } for _, ex := range x.extra { if _, ok := allowed[ex]; !ok { if _, ok := x.Extensions[ex]; !ok { @@ -159,6 +179,9 @@ type ExampleRef struct { // are allowed by the openapi spec. Extensions map[string]any Origin *Origin `json:"-" yaml:"-"` + // Reference Object summary and description are supported in OpenAPI 3.1+. + Summary *string `json:"summary,omitempty" yaml:"summary,omitempty"` + Description *string `json:"description,omitempty" yaml:"description,omitempty"` Ref string Value *Example @@ -193,7 +216,12 @@ func (x *ExampleRef) setRefPath(u *url.URL) { // MarshalYAML returns the YAML encoding of ExampleRef. func (x ExampleRef) MarshalYAML() (any, error) { if ref := x.Ref; ref != "" { - return &Ref{Ref: ref, Extensions: x.Extensions}, nil + return &Ref{ + Ref: ref, + Extensions: x.Extensions, + Summary: x.Summary, + Description: x.Description, + }, nil } return x.Value.MarshalYAML() } @@ -216,6 +244,10 @@ func (x *ExampleRef) UnmarshalJSON(data []byte) error { delete(extra, "$ref") x.Ref = refOnly.Ref x.Origin = refOnly.Origin + x.Summary = refOnly.Summary + x.Description = refOnly.Description + delete(extra, "summary") + delete(extra, "description") if len(extra) != 0 { x.extra = componentNames(extra) for k := range extra { @@ -242,6 +274,14 @@ func (x *ExampleRef) validateExtras(ctx context.Context) error { if allowed == nil { allowed = make(map[string]struct{}) } + if !validationOpts.isOpenAPI31OrLater { + if _, ok := allowed["description"]; x.Description != nil && !ok { + extras = append(extras, "description") + } + if _, ok := allowed["summary"]; x.Summary != nil && !ok { + extras = append(extras, "summary") + } + } for _, ex := range x.extra { if _, ok := allowed[ex]; !ok { if _, ok := x.Extensions[ex]; !ok { @@ -301,6 +341,9 @@ type HeaderRef struct { // are allowed by the openapi spec. Extensions map[string]any Origin *Origin `json:"-" yaml:"-"` + // Reference Object summary and description are supported in OpenAPI 3.1+. + Summary *string `json:"summary,omitempty" yaml:"summary,omitempty"` + Description *string `json:"description,omitempty" yaml:"description,omitempty"` Ref string Value *Header @@ -335,7 +378,12 @@ func (x *HeaderRef) setRefPath(u *url.URL) { // MarshalYAML returns the YAML encoding of HeaderRef. func (x HeaderRef) MarshalYAML() (any, error) { if ref := x.Ref; ref != "" { - return &Ref{Ref: ref, Extensions: x.Extensions}, nil + return &Ref{ + Ref: ref, + Extensions: x.Extensions, + Summary: x.Summary, + Description: x.Description, + }, nil } return x.Value.MarshalYAML() } @@ -358,6 +406,10 @@ func (x *HeaderRef) UnmarshalJSON(data []byte) error { delete(extra, "$ref") x.Ref = refOnly.Ref x.Origin = refOnly.Origin + x.Summary = refOnly.Summary + x.Description = refOnly.Description + delete(extra, "summary") + delete(extra, "description") if len(extra) != 0 { x.extra = componentNames(extra) for k := range extra { @@ -384,6 +436,14 @@ func (x *HeaderRef) validateExtras(ctx context.Context) error { if allowed == nil { allowed = make(map[string]struct{}) } + if !validationOpts.isOpenAPI31OrLater { + if _, ok := allowed["description"]; x.Description != nil && !ok { + extras = append(extras, "description") + } + if _, ok := allowed["summary"]; x.Summary != nil && !ok { + extras = append(extras, "summary") + } + } for _, ex := range x.extra { if _, ok := allowed[ex]; !ok { if _, ok := x.Extensions[ex]; !ok { @@ -443,6 +503,9 @@ type LinkRef struct { // are allowed by the openapi spec. Extensions map[string]any Origin *Origin `json:"-" yaml:"-"` + // Reference Object summary and description are supported in OpenAPI 3.1+. + Summary *string `json:"summary,omitempty" yaml:"summary,omitempty"` + Description *string `json:"description,omitempty" yaml:"description,omitempty"` Ref string Value *Link @@ -477,7 +540,12 @@ func (x *LinkRef) setRefPath(u *url.URL) { // MarshalYAML returns the YAML encoding of LinkRef. func (x LinkRef) MarshalYAML() (any, error) { if ref := x.Ref; ref != "" { - return &Ref{Ref: ref, Extensions: x.Extensions}, nil + return &Ref{ + Ref: ref, + Extensions: x.Extensions, + Summary: x.Summary, + Description: x.Description, + }, nil } return x.Value.MarshalYAML() } @@ -500,6 +568,10 @@ func (x *LinkRef) UnmarshalJSON(data []byte) error { delete(extra, "$ref") x.Ref = refOnly.Ref x.Origin = refOnly.Origin + x.Summary = refOnly.Summary + x.Description = refOnly.Description + delete(extra, "summary") + delete(extra, "description") if len(extra) != 0 { x.extra = componentNames(extra) for k := range extra { @@ -526,6 +598,14 @@ func (x *LinkRef) validateExtras(ctx context.Context) error { if allowed == nil { allowed = make(map[string]struct{}) } + if !validationOpts.isOpenAPI31OrLater { + if _, ok := allowed["description"]; x.Description != nil && !ok { + extras = append(extras, "description") + } + if _, ok := allowed["summary"]; x.Summary != nil && !ok { + extras = append(extras, "summary") + } + } for _, ex := range x.extra { if _, ok := allowed[ex]; !ok { if _, ok := x.Extensions[ex]; !ok { @@ -585,6 +665,9 @@ type ParameterRef struct { // are allowed by the openapi spec. Extensions map[string]any Origin *Origin `json:"-" yaml:"-"` + // Reference Object summary and description are supported in OpenAPI 3.1+. + Summary *string `json:"summary,omitempty" yaml:"summary,omitempty"` + Description *string `json:"description,omitempty" yaml:"description,omitempty"` Ref string Value *Parameter @@ -619,7 +702,12 @@ func (x *ParameterRef) setRefPath(u *url.URL) { // MarshalYAML returns the YAML encoding of ParameterRef. func (x ParameterRef) MarshalYAML() (any, error) { if ref := x.Ref; ref != "" { - return &Ref{Ref: ref, Extensions: x.Extensions}, nil + return &Ref{ + Ref: ref, + Extensions: x.Extensions, + Summary: x.Summary, + Description: x.Description, + }, nil } return x.Value.MarshalYAML() } @@ -642,6 +730,10 @@ func (x *ParameterRef) UnmarshalJSON(data []byte) error { delete(extra, "$ref") x.Ref = refOnly.Ref x.Origin = refOnly.Origin + x.Summary = refOnly.Summary + x.Description = refOnly.Description + delete(extra, "summary") + delete(extra, "description") if len(extra) != 0 { x.extra = componentNames(extra) for k := range extra { @@ -668,6 +760,14 @@ func (x *ParameterRef) validateExtras(ctx context.Context) error { if allowed == nil { allowed = make(map[string]struct{}) } + if !validationOpts.isOpenAPI31OrLater { + if _, ok := allowed["description"]; x.Description != nil && !ok { + extras = append(extras, "description") + } + if _, ok := allowed["summary"]; x.Summary != nil && !ok { + extras = append(extras, "summary") + } + } for _, ex := range x.extra { if _, ok := allowed[ex]; !ok { if _, ok := x.Extensions[ex]; !ok { @@ -727,6 +827,9 @@ type RequestBodyRef struct { // are allowed by the openapi spec. Extensions map[string]any Origin *Origin `json:"-" yaml:"-"` + // Reference Object summary and description are supported in OpenAPI 3.1+. + Summary *string `json:"summary,omitempty" yaml:"summary,omitempty"` + Description *string `json:"description,omitempty" yaml:"description,omitempty"` Ref string Value *RequestBody @@ -761,7 +864,12 @@ func (x *RequestBodyRef) setRefPath(u *url.URL) { // MarshalYAML returns the YAML encoding of RequestBodyRef. func (x RequestBodyRef) MarshalYAML() (any, error) { if ref := x.Ref; ref != "" { - return &Ref{Ref: ref, Extensions: x.Extensions}, nil + return &Ref{ + Ref: ref, + Extensions: x.Extensions, + Summary: x.Summary, + Description: x.Description, + }, nil } return x.Value.MarshalYAML() } @@ -784,6 +892,10 @@ func (x *RequestBodyRef) UnmarshalJSON(data []byte) error { delete(extra, "$ref") x.Ref = refOnly.Ref x.Origin = refOnly.Origin + x.Summary = refOnly.Summary + x.Description = refOnly.Description + delete(extra, "summary") + delete(extra, "description") if len(extra) != 0 { x.extra = componentNames(extra) for k := range extra { @@ -810,6 +922,14 @@ func (x *RequestBodyRef) validateExtras(ctx context.Context) error { if allowed == nil { allowed = make(map[string]struct{}) } + if !validationOpts.isOpenAPI31OrLater { + if _, ok := allowed["description"]; x.Description != nil && !ok { + extras = append(extras, "description") + } + if _, ok := allowed["summary"]; x.Summary != nil && !ok { + extras = append(extras, "summary") + } + } for _, ex := range x.extra { if _, ok := allowed[ex]; !ok { if _, ok := x.Extensions[ex]; !ok { @@ -869,6 +989,9 @@ type ResponseRef struct { // are allowed by the openapi spec. Extensions map[string]any Origin *Origin `json:"-" yaml:"-"` + // Reference Object summary and description are supported in OpenAPI 3.1+. + Summary *string `json:"summary,omitempty" yaml:"summary,omitempty"` + Description *string `json:"description,omitempty" yaml:"description,omitempty"` Ref string Value *Response @@ -903,7 +1026,12 @@ func (x *ResponseRef) setRefPath(u *url.URL) { // MarshalYAML returns the YAML encoding of ResponseRef. func (x ResponseRef) MarshalYAML() (any, error) { if ref := x.Ref; ref != "" { - return &Ref{Ref: ref, Extensions: x.Extensions}, nil + return &Ref{ + Ref: ref, + Extensions: x.Extensions, + Summary: x.Summary, + Description: x.Description, + }, nil } return x.Value.MarshalYAML() } @@ -926,6 +1054,10 @@ func (x *ResponseRef) UnmarshalJSON(data []byte) error { delete(extra, "$ref") x.Ref = refOnly.Ref x.Origin = refOnly.Origin + x.Summary = refOnly.Summary + x.Description = refOnly.Description + delete(extra, "summary") + delete(extra, "description") if len(extra) != 0 { x.extra = componentNames(extra) for k := range extra { @@ -952,6 +1084,14 @@ func (x *ResponseRef) validateExtras(ctx context.Context) error { if allowed == nil { allowed = make(map[string]struct{}) } + if !validationOpts.isOpenAPI31OrLater { + if _, ok := allowed["description"]; x.Description != nil && !ok { + extras = append(extras, "description") + } + if _, ok := allowed["summary"]; x.Summary != nil && !ok { + extras = append(extras, "summary") + } + } for _, ex := range x.extra { if _, ok := allowed[ex]; !ok { if _, ok := x.Extensions[ex]; !ok { @@ -1048,7 +1188,10 @@ func (x *SchemaRef) setRefPath(u *url.URL) { // MarshalYAML returns the YAML encoding of SchemaRef. func (x SchemaRef) MarshalYAML() (any, error) { if ref := x.Ref; ref != "" { - return &Ref{Ref: ref, Extensions: x.Extensions}, nil + return &Ref{ + Ref: ref, + Extensions: x.Extensions, + }, nil } return x.Value.MarshalYAML() } @@ -1174,6 +1317,9 @@ type SecuritySchemeRef struct { // are allowed by the openapi spec. Extensions map[string]any Origin *Origin `json:"-" yaml:"-"` + // Reference Object summary and description are supported in OpenAPI 3.1+. + Summary *string `json:"summary,omitempty" yaml:"summary,omitempty"` + Description *string `json:"description,omitempty" yaml:"description,omitempty"` Ref string Value *SecurityScheme @@ -1208,7 +1354,12 @@ func (x *SecuritySchemeRef) setRefPath(u *url.URL) { // MarshalYAML returns the YAML encoding of SecuritySchemeRef. func (x SecuritySchemeRef) MarshalYAML() (any, error) { if ref := x.Ref; ref != "" { - return &Ref{Ref: ref, Extensions: x.Extensions}, nil + return &Ref{ + Ref: ref, + Extensions: x.Extensions, + Summary: x.Summary, + Description: x.Description, + }, nil } return x.Value.MarshalYAML() } @@ -1231,6 +1382,10 @@ func (x *SecuritySchemeRef) UnmarshalJSON(data []byte) error { delete(extra, "$ref") x.Ref = refOnly.Ref x.Origin = refOnly.Origin + x.Summary = refOnly.Summary + x.Description = refOnly.Description + delete(extra, "summary") + delete(extra, "description") if len(extra) != 0 { x.extra = componentNames(extra) for k := range extra { @@ -1257,6 +1412,14 @@ func (x *SecuritySchemeRef) validateExtras(ctx context.Context) error { if allowed == nil { allowed = make(map[string]struct{}) } + if !validationOpts.isOpenAPI31OrLater { + if _, ok := allowed["description"]; x.Description != nil && !ok { + extras = append(extras, "description") + } + if _, ok := allowed["summary"]; x.Summary != nil && !ok { + extras = append(extras, "summary") + } + } for _, ex := range x.extra { if _, ok := allowed[ex]; !ok { if _, ok := x.Extensions[ex]; !ok { diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/refs.tmpl b/vendor/github.com/getkin/kin-openapi/openapi3/refs.tmpl index fdff1c9..1b20b44 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/refs.tmpl +++ b/vendor/github.com/getkin/kin-openapi/openapi3/refs.tmpl @@ -18,6 +18,12 @@ type {{ $type.Name }}Ref struct { Extensions map[string]any Origin *Origin `json:"-" yaml:"-"` + {{- if ne $type.Name "Schema" }} + // Reference Object summary and description are supported in OpenAPI 3.1+. + Summary *string `json:"summary,omitempty" yaml:"summary,omitempty"` + Description *string `json:"description,omitempty" yaml:"description,omitempty"` + {{- end }} + Ref string Value *{{ $type.Name }} extra []string @@ -56,7 +62,14 @@ func (x *{{ $type.Name }}Ref) setRefPath(u *url.URL) { // MarshalYAML returns the YAML encoding of {{ $type.Name }}Ref. func (x {{ $type.Name }}Ref) MarshalYAML() (any, error) { if ref := x.Ref; ref != "" { - return &Ref{Ref: ref, Extensions: x.Extensions}, nil + return &Ref{ + Ref: ref, + Extensions: x.Extensions, + {{- if ne $type.Name "Schema" }} + Summary: x.Summary, + Description: x.Description, + {{- end }} + }, nil } return x.Value.MarshalYAML() } @@ -79,6 +92,12 @@ func (x *{{ $type.Name }}Ref) UnmarshalJSON(data []byte) error { delete(extra, "$ref") x.Ref = refOnly.Ref x.Origin = refOnly.Origin + {{- if ne $type.Name "Schema" }} + x.Summary = refOnly.Summary + x.Description = refOnly.Description + delete(extra, "summary") + delete(extra, "description") + {{- end }} if len(extra) != 0 { x.extra = componentNames(extra) {{- if eq $type.Name "Schema" }} @@ -123,6 +142,16 @@ func (x *{{ $type.Name }}Ref) validateExtras(ctx context.Context) error { if allowed == nil { allowed = make(map[string]struct{}) } + {{- if ne $type.Name "Schema" }} + if !validationOpts.isOpenAPI31OrLater { + if _, ok := allowed["description"]; x.Description != nil && !ok { + extras = append(extras, "description") + } + if _, ok := allowed["summary"]; x.Summary != nil && !ok { + extras = append(extras, "summary") + } + } + {{- end }} for _, ex := range x.extra { if _, ok := allowed[ex]; !ok { if _, ok := x.Extensions[ex]; !ok { diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/request_body.go b/vendor/github.com/getkin/kin-openapi/openapi3/request_body.go index e8c52a1..55c9b97 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/request_body.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/request_body.go @@ -135,9 +135,3 @@ func (requestBody *RequestBody) Validate(ctx context.Context, opts ...Validation return validateExtensions(ctx, requestBody.Extensions, requestBody.Origin) } - -// UnmarshalJSON sets RequestBodies to a copy of data. -func (requestBodies *RequestBodies) UnmarshalJSON(data []byte) (err error) { - *requestBodies, err = unmarshalStringMapP[RequestBodyRef](data) - return -} diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/response.go b/vendor/github.com/getkin/kin-openapi/openapi3/response.go index ddc3a7b..2cbf833 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) @@ -211,9 +217,3 @@ func (response *Response) Validate(ctx context.Context, opts ...ValidationOption return validateExtensions(ctx, response.Extensions, response.Origin) } - -// UnmarshalJSON sets ResponseBodies to a copy of data. -func (responseBodies *ResponseBodies) UnmarshalJSON(data []byte) (err error) { - *responseBodies, err = unmarshalStringMapP[ResponseRef](data) - return -} diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/schema.go b/vendor/github.com/getkin/kin-openapi/openapi3/schema.go index 1f5eebd..4388a39 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,18 +3177,12 @@ 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: return v // string, float64, bool, nil — all immutable } } - -// UnmarshalJSON sets Schemas to a copy of data. -func (schemas *Schemas) UnmarshalJSON(data []byte) (err error) { - *schemas, err = unmarshalStringMapP[SchemaRef](data) - return -} diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/security_requirements.go b/vendor/github.com/getkin/kin-openapi/openapi3/security_requirements.go index 36b6401..3f5bd95 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/security_requirements.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/security_requirements.go @@ -49,9 +49,3 @@ func (security *SecurityRequirement) Validate(ctx context.Context, opts ...Valid return nil } - -// UnmarshalJSON sets SecurityRequirement to a copy of data. -func (security *SecurityRequirement) UnmarshalJSON(data []byte) (err error) { - *security, err = unmarshalStringMap[[]string](data) - return -} diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/security_scheme.go b/vendor/github.com/getkin/kin-openapi/openapi3/security_scheme.go index efe7f20..0905b61 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/security_scheme.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/security_scheme.go @@ -325,7 +325,7 @@ type OAuthFlow struct { AuthorizationURL string `json:"authorizationUrl,omitempty" yaml:"authorizationUrl,omitempty"` TokenURL string `json:"tokenUrl,omitempty" yaml:"tokenUrl,omitempty"` RefreshURL string `json:"refreshUrl,omitempty" yaml:"refreshUrl,omitempty"` - Scopes StringMap[string] `json:"scopes" yaml:"scopes"` // required + Scopes map[string]string `json:"scopes" yaml:"scopes"` // required } // MarshalJSON returns the JSON encoding of OAuthFlow. @@ -426,9 +426,3 @@ func (flow *OAuthFlow) validate(ctx context.Context, typ oAuthFlowType, opts ... return flow.Validate(ctx, opts...) } - -// UnmarshalJSON sets SecuritySchemes to a copy of data. -func (securitySchemes *SecuritySchemes) UnmarshalJSON(data []byte) (err error) { - *securitySchemes, err = unmarshalStringMapP[SecuritySchemeRef](data) - return -} diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/server.go b/vendor/github.com/getkin/kin-openapi/openapi3/server.go index 181eace..ca460d7 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/server.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/server.go @@ -119,7 +119,6 @@ func (server *Server) UnmarshalJSON(data []byte) error { if len(x.Extensions) == 0 { x.Extensions = nil } - delete(x.Variables, originKey) *server = Server(x) return nil } @@ -244,12 +243,6 @@ func (server *Server) Validate(ctx context.Context, opts ...ValidationOption) er // ServerVariables is a map of ServerVariable objects keyed by variable name. type ServerVariables map[string]*ServerVariable -// UnmarshalJSON sets ServerVariables to a copy of data. -func (serverVariables *ServerVariables) UnmarshalJSON(data []byte) (err error) { - *serverVariables, err = unmarshalStringMapP[ServerVariable](data) - return -} - type ServerVariable struct { Extensions map[string]any `json:"-" yaml:"-"` Origin *Origin `json:"-" yaml:"-"` diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/stringmap.go b/vendor/github.com/getkin/kin-openapi/openapi3/stringmap.go deleted file mode 100644 index f416899..0000000 --- a/vendor/github.com/getkin/kin-openapi/openapi3/stringmap.go +++ /dev/null @@ -1,64 +0,0 @@ -package openapi3 - -import "encoding/json" - -// StringMap is a map[string]string that ignores the origin in the underlying json representation. -type StringMap[V any] map[string]V - -// UnmarshalJSON sets StringMap to a copy of data. -func (stringMap *StringMap[V]) UnmarshalJSON(data []byte) (err error) { - *stringMap, err = unmarshalStringMap[V](data) - return -} - -// unmarshalStringMapP unmarshals given json into a map[string]*V -func unmarshalStringMapP[V any](data []byte) (map[string]*V, error) { - var m map[string]any - if err := json.Unmarshal(data, &m); err != nil { - return nil, err - } - - result := make(map[string]*V, len(m)) - for _, k := range componentNames(m) { - value, err := deepCast[V](m[k]) - if err != nil { - return nil, err - } - result[k] = value - } - - return result, nil -} - -// unmarshalStringMap unmarshals given json into a map[string]V -func unmarshalStringMap[V any](data []byte) (map[string]V, error) { - var m map[string]any - if err := json.Unmarshal(data, &m); err != nil { - return nil, err - } - - result := make(map[string]V, len(m)) - for _, k := range componentNames(m) { - value, err := deepCast[V](m[k]) - if err != nil { - return nil, err - } - result[k] = *value - } - - return result, nil -} - -// deepCast casts any value to a value of type V. -func deepCast[V any](value any) (*V, error) { - data, err := json.Marshal(value) - if err != nil { - return nil, err - } - - var result V - if err = json.Unmarshal(data, &result); err != nil { - return nil, err - } - return &result, nil -} 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_code.go b/vendor/github.com/getkin/kin-openapi/openapi3/validation_code.go new file mode 100644 index 0000000..f2daa34 --- /dev/null +++ b/vendor/github.com/getkin/kin-openapi/openapi3/validation_code.go @@ -0,0 +1,258 @@ +package openapi3 + +import "slices" + +// CodedError is implemented by every typed validation error. Code returns a +// stable, kebab-case identifier for the validation rule that failed (e.g. +// "operation-responses-required"), so consumers can suppress specific +// findings, assign per-rule severities, emit machine-readable diagnostics +// (e.g. the LSP Diagnostic.code field), or link to per-rule documentation +// without depending on message text. +// +// Every code is declared as a literal on its error type; nothing is derived. +// Codes are a contract: message wording and Go type names may change freely, +// codes may not. Renaming or removing a code is a breaking change and is +// recorded in the README changelog. The full set is available from +// ValidationErrorCodes. +// +// Like net.Error, this interface exists as an assertion target for consumers; +// the package itself constructs concrete error types and does not consume it. +// Reach the code of a wrapped error with errors.As: +// +// var coded openapi3.CodedError +// if errors.As(err, &coded) { +// fmt.Println(coded.Code()) +// } +type CodedError interface { + error + Code() string +} + +func (e *AnchorFieldFor31Plus) Code() string { return "anchor-field-for-3-1-plus" } +func (e *APIKeyInInvalidError) Code() string { return "security-scheme-apikey-in-invalid" } +func (e *APIKeySecuritySchemeNameRequired) Code() string { return "security-scheme-name-required" } +func (e *CommentFieldFor31Plus) Code() string { return "comment-field-for-3-1-plus" } +func (e *ConflictingPathsError) Code() string { return "conflicting-paths" } +func (e *ConstFieldFor31Plus) Code() string { return "const-field-for-3-1-plus" } +func (e *ContainsFieldFor31Plus) Code() string { return "contains-field-for-3-1-plus" } +func (e *ContentEncodingFieldFor31Plus) Code() string { return "content-encoding-field-for-3-1-plus" } +func (e *ContentMediaTypeFieldFor31Plus) Code() string { + return "content-media-type-field-for-3-1-plus" +} +func (e *ContentSchemaFieldFor31Plus) Code() string { return "content-schema-field-for-3-1-plus" } +func (e *DefaultViolatesSchema) Code() string { return "default-violates-schema" } +func (e *DefsFieldFor31Plus) Code() string { return "defs-field-for-3-1-plus" } +func (e *DependentRequiredFieldFor31Plus) Code() string { + return "dependent-required-field-for-3-1-plus" +} +func (e *DependentSchemasFieldFor31Plus) Code() string { return "dependent-schemas-field-for-3-1-plus" } +func (e *DuplicateOperationIDError) Code() string { return "duplicate-operation-id" } +func (e *DuplicateParameterError) Code() string { return "duplicate-parameter" } +func (e *DuplicateRequiredFieldError) Code() string { return "duplicate-required-field" } +func (e *DuplicateTagError) Code() string { return "duplicate-tag" } +func (e *DynamicAnchorFieldFor31Plus) Code() string { return "dynamic-anchor-field-for-3-1-plus" } +func (e *DynamicRefFieldFor31Plus) Code() string { return "dynamic-ref-field-for-3-1-plus" } +func (e *ElseFieldFor31Plus) Code() string { return "else-field-for-3-1-plus" } +func (e *ExamplesFieldFor31Plus) Code() string { return "examples-field-for-3-1-plus" } +func (e *ExampleValueExternalValueExclusive) Code() string { + return "value-external-value-mutually-exclusive" +} +func (e *ExampleValueOrExternalValueRequired) Code() string { + return "value-or-external-value-required" +} +func (e *ExampleViolatesSchema) Code() string { return "example-violates-schema" } +func (e *ExternalDocsURLRequired) Code() string { return "external-docs-url-required" } +func (e *ExtraSiblingFieldsError) Code() string { return "extra-sibling-fields" } +func (e *HeaderContentSchemaExactlyOne) Code() string { return "content-or-schema-exactly-one" } +func (e *HeaderContentSingleEntry) Code() string { return "header-content-single-entry" } +func (e *HeaderInForbidden) Code() string { return "in-forbidden" } +func (e *HeaderNameForbidden) Code() string { return "name-forbidden" } +func (e *IDFieldFor31Plus) Code() string { return "id-field-for-3-1-plus" } +func (e *IfFieldFor31Plus) Code() string { return "if-field-for-3-1-plus" } +func (e *InfoRequired) Code() string { return "info-required" } +func (e *InfoSummaryFieldFor31Plus) Code() string { return "summary-field-for-3-1-plus" } +func (e *InfoTitleRequired) Code() string { return "info-title-required" } +func (e *InfoVersionRequired) Code() string { return "info-version-required" } +func (e *InvalidHTTPSchemeError) Code() string { return "security-scheme-http-scheme-invalid" } +func (e *InvalidParameterInError) Code() string { return "parameter-in-invalid" } +func (e *InvalidSecuritySchemeTypeError) Code() string { return "security-scheme-type-invalid" } +func (e *InvalidSerializationMethodError) Code() string { return "serialization-method-invalid" } +func (e *ItemSchemaFieldFor32Plus) Code() string { return "item-schema-field-for-3-2-plus" } +func (e *JSONSchemaDialectAbsoluteURIRequired) Code() string { return "json-schema-dialect-required" } +func (e *JSONSchemaDialectFieldFor31Plus) Code() string { + return "jsonschemadialect-field-for-3-1-plus" +} +func (e *LicenseIdentifierFieldFor31Plus) Code() string { return "identifier-field-for-3-1-plus" } +func (e *LicenseNameRequired) Code() string { return "license-name-required" } +func (e *LicenseURLIdentifierExclusive) Code() string { return "url-identifier-mutually-exclusive" } +func (e *LinkOperationIDOrRefRequired) Code() string { return "operation-id-or-operation-ref-required" } +func (e *LinkOperationIDRefExclusive) Code() string { + return "operation-id-operation-ref-mutually-exclusive" +} +func (e *MaxContainsFieldFor31Plus) Code() string { return "max-contains-field-for-3-1-plus" } +func (e *MediaTypeExampleExamplesExclusive) Code() string { + return "example-examples-mutually-exclusive" +} +func (e *MinContainsFieldFor31Plus) Code() string { return "min-contains-field-for-3-1-plus" } +func (e *OAuthFlowAuthorizationURLForbidden) Code() string { return "authorization-url-forbidden" } +func (e *OAuthFlowAuthorizationURLRequired) Code() string { + return "oauth-flow-authorization-url-required" +} +func (e *OAuthFlowScopesRequired) Code() string { return "oauth-flow-scopes-required" } +func (e *OAuthFlowTokenURLForbidden) Code() string { return "token-url-forbidden" } +func (e *OAuthFlowTokenURLRequired) Code() string { return "oauth-flow-token-url-required" } +func (e *OpenAPIVersionRequired) Code() string { return "openapi-required" } +func (e *OpenIDConnectURLRequired) Code() string { return "openid-connect-url-required" } +func (e *OperationResponsesRequired) Code() string { return "operation-responses-required" } +func (e *ParameterContentSchemaExactlyOne) Code() string { return "content-or-schema-exactly-one" } +func (e *ParameterContentSingleEntry) Code() string { return "parameter-content-single-entry" } +func (e *ParameterExampleAndExamplesExclusive) Code() string { + return "example-examples-mutually-exclusive" +} +func (e *ParameterNameRequired) Code() string { return "parameter-name-required" } +func (e *PathMustStartWithSlashError) Code() string { return "path-must-start-with-slash" } +func (e *PathParameterRequiredError) Code() string { return "path-parameter-required" } +func (e *PathParametersError) Code() string { return "path-parameters-mismatch" } +func (e *PathsRequired) Code() string { return "paths-required" } +func (e *PatternPropertiesFieldFor31Plus) Code() string { + return "pattern-properties-field-for-3-1-plus" +} +func (e *PrefixItemsFieldFor31Plus) Code() string { return "prefix-items-field-for-3-1-plus" } +func (e *PropertyNamesFieldFor31Plus) Code() string { return "property-names-field-for-3-1-plus" } +func (e *RequestBodyContentRequired) Code() string { return "request-body-content-required" } +func (e *ResponseDescriptionRequired) Code() string { return "response-description-required" } +func (e *ResponsesNonEmptyRequired) Code() string { return "responses-required" } +func (e *SchemaAdditionalPropertiesBothForms) Code() string { + return "additional-properties-both-forms-exclusive" +} +func (e *SchemaFieldFor31Plus) Code() string { return "schema-field-for-3-1-plus" } +func (e *SchemaItemsRequired) Code() string { return "schema-items-required" } +func (e *SchemaPatternRegexError) Code() string { return "schema-pattern-regex-invalid" } +func (e *SchemaReadOnlyWriteOnlyExclusive) Code() string { + return "read-only-write-only-mutually-exclusive" +} +func (e *SchemaTypeError) Code() string { return "schema-type-unsupported" } +func (e *SchemaUnevaluatedItemsBothForms) Code() string { + return "unevaluated-items-both-forms-exclusive" +} +func (e *SchemaUnevaluatedPropertiesBothForms) Code() string { + return "unevaluated-properties-both-forms-exclusive" +} +func (e *SecuritySchemeBearerFormatForbidden) Code() string { return "bearer-format-forbidden" } +func (e *SecuritySchemeFlowsForbidden) Code() string { return "flows-forbidden" } +func (e *SecuritySchemeFlowsRequired) Code() string { return "flows-required" } +func (e *SecuritySchemeInForbidden) Code() string { return "in-forbidden" } +func (e *SecuritySchemeNameForbidden) Code() string { return "name-forbidden" } +func (e *ServerURLRequired) Code() string { return "server-url-required" } +func (e *ServerURLTemplateError) Code() string { return "server-url-template-invalid" } +func (e *ServerVariableDefaultRequired) Code() string { return "default-required" } +func (e *ThenFieldFor31Plus) Code() string { return "then-field-for-3-1-plus" } +func (e *UnevaluatedItemsFieldFor31Plus) Code() string { return "unevaluated-items-field-for-3-1-plus" } +func (e *UnevaluatedPropertiesFieldFor31Plus) Code() string { + return "unevaluated-properties-field-for-3-1-plus" +} +func (e *UnresolvedRefError) Code() string { return "unresolved-ref" } +func (e *WebhookNilError) Code() string { return "webhook-nil" } +func (e *WebhooksFieldFor31Plus) Code() string { return "webhooks-field-for-3-1-plus" } + +// ValidationErrorCodes returns every code a validation error can carry, +// sorted. TestValidationErrorCodes keeps it in sync with the errors the +// validators emit. +func ValidationErrorCodes() []string { + return slices.Clone(validationErrorCodes) +} + +var validationErrorCodes = []string{ + "additional-properties-both-forms-exclusive", + "anchor-field-for-3-1-plus", + "authorization-url-forbidden", + "bearer-format-forbidden", + "comment-field-for-3-1-plus", + "conflicting-paths", + "const-field-for-3-1-plus", + "contains-field-for-3-1-plus", + "content-encoding-field-for-3-1-plus", + "content-media-type-field-for-3-1-plus", + "content-or-schema-exactly-one", + "content-schema-field-for-3-1-plus", + "default-required", + "default-violates-schema", + "defs-field-for-3-1-plus", + "dependent-required-field-for-3-1-plus", + "dependent-schemas-field-for-3-1-plus", + "duplicate-operation-id", + "duplicate-parameter", + "duplicate-required-field", + "duplicate-tag", + "dynamic-anchor-field-for-3-1-plus", + "dynamic-ref-field-for-3-1-plus", + "else-field-for-3-1-plus", + "example-examples-mutually-exclusive", + "example-violates-schema", + "examples-field-for-3-1-plus", + "external-docs-url-required", + "extra-sibling-fields", + "flows-forbidden", + "flows-required", + "header-content-single-entry", + "id-field-for-3-1-plus", + "identifier-field-for-3-1-plus", + "if-field-for-3-1-plus", + "in-forbidden", + "info-required", + "info-title-required", + "info-version-required", + "item-schema-field-for-3-2-plus", + "json-schema-dialect-required", + "jsonschemadialect-field-for-3-1-plus", + "license-name-required", + "max-contains-field-for-3-1-plus", + "min-contains-field-for-3-1-plus", + "name-forbidden", + "oauth-flow-authorization-url-required", + "oauth-flow-scopes-required", + "oauth-flow-token-url-required", + "openapi-required", + "openid-connect-url-required", + "operation-id-operation-ref-mutually-exclusive", + "operation-id-or-operation-ref-required", + "operation-responses-required", + "parameter-content-single-entry", + "parameter-in-invalid", + "parameter-name-required", + "path-must-start-with-slash", + "path-parameter-required", + "path-parameters-mismatch", + "paths-required", + "pattern-properties-field-for-3-1-plus", + "prefix-items-field-for-3-1-plus", + "property-names-field-for-3-1-plus", + "read-only-write-only-mutually-exclusive", + "request-body-content-required", + "response-description-required", + "responses-required", + "schema-field-for-3-1-plus", + "schema-items-required", + "schema-pattern-regex-invalid", + "schema-type-unsupported", + "security-scheme-apikey-in-invalid", + "security-scheme-http-scheme-invalid", + "security-scheme-name-required", + "security-scheme-type-invalid", + "serialization-method-invalid", + "server-url-required", + "server-url-template-invalid", + "summary-field-for-3-1-plus", + "then-field-for-3-1-plus", + "token-url-forbidden", + "unevaluated-items-both-forms-exclusive", + "unevaluated-items-field-for-3-1-plus", + "unevaluated-properties-both-forms-exclusive", + "unevaluated-properties-field-for-3-1-plus", + "unresolved-ref", + "url-identifier-mutually-exclusive", + "value-external-value-mutually-exclusive", + "value-or-external-value-required", + "webhook-nil", + "webhooks-field-for-3-1-plus", +} 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..f2954af 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, @@ -1347,12 +1376,43 @@ func newAPIKeySecuritySchemeNameRequired(origin *Origin) error { &APIKeySecuritySchemeNameRequired{ValidationError{Message: msg}}, origin) } +// ExampleViolatesSchema and DefaultViolatesSchema mark which schema value kind +// failed. They embed SchemaValueError, and their As exposes it, so errors.As +// with a *SchemaValueError target keeps matching. +type ExampleViolatesSchema struct{ SchemaValueError } + +func (e *ExampleViolatesSchema) As(target any) bool { + return asSchemaValueError(target, &e.SchemaValueError) +} + +type DefaultViolatesSchema struct{ SchemaValueError } + +func (e *DefaultViolatesSchema) As(target any) bool { + return asSchemaValueError(target, &e.SchemaValueError) +} + +func asSchemaValueError(target any, sve *SchemaValueError) bool { + t, ok := target.(**SchemaValueError) + if !ok { + return false + } + *t = sve + return true +} + // newSchemaValueError wraps the result of schema.VisitJSON in a // *SchemaValueError cluster, identifying which schema sub-field // (example, default, ...) carried the offending value. cause is // either a *SchemaError or a MultiError of them. func newSchemaValueError(valueKind string, cause error, origin *Origin) error { - return &SchemaValueError{ValueKind: valueKind, Cause: cause, Origin: origin} + sve := SchemaValueError{ValueKind: valueKind, Cause: cause, Origin: origin} + switch valueKind { + case "example": + return &ExampleViolatesSchema{sve} + case "default": + return &DefaultViolatesSchema{sve} + } + return &sve } // exampleValueOrigin returns an Origin pinned to the example's `value:` @@ -1371,13 +1431,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 +1450,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 +1506,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 +1514,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 +1522,28 @@ func newFieldFor31Plus(field string, origin *Origin) error { } else { leaf = &ValidationError{Message: msg} } - return newFieldVersionMismatch(field, leaf, origin) + return newFieldVersionMismatch(field, "3.1", leaf, origin) +} + +type ItemSchemaFieldFor32Plus struct{ ValidationError } + +func (e *ItemSchemaFieldFor32Plus) As(target any) bool { + return asValidationError(target, &e.ValidationError) +} + +var fieldFor32PlusLeaves = map[string]func(msg string) error{ + "itemSchema": func(m string) error { return &ItemSchemaFieldFor32Plus{ValidationError{Message: m}} }, +} + +func errFieldFor32Plus(field string, origin *Origin) error { + msg := "field " + field + " is for OpenAPI >=3.2" + var leaf error + if ctor, ok := fieldFor32PlusLeaves[field]; ok { + leaf = ctor(msg) + } else { + leaf = &ValidationError{Message: msg} + } + return newFieldVersionMismatch(field, "3.2", leaf, 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_parameters.go b/vendor/github.com/getkin/kin-openapi/openapi3/walk_parameters.go new file mode 100644 index 0000000..d60edb6 --- /dev/null +++ b/vendor/github.com/getkin/kin-openapi/openapi3/walk_parameters.go @@ -0,0 +1,128 @@ +package openapi3 + +import ( + "maps" + "slices" + "strconv" + "strings" +) + +// WalkParametersFunc is called once for each parameter visited by +// WalkParameters. +// +// jsonPointer is the RFC 6901 JSON Pointer of the parameter within the +// document, e.g. "/components/parameters/Limit" or +// "/paths/~1pets/get/parameters/0". For a parameter referenced from several +// places it is the pointer of the first visit, and components are visited +// first, so a shared parameter is reported at its definition. param is +// non-nil and param.Value is non-nil; the callback may modify param.Value in +// place, so WalkParameters serves transformers and not only read-only +// inspection. +// +// Returning a non-nil error aborts the walk and is returned by +// WalkParameters. +type WalkParametersFunc func(jsonPointer string, param *ParameterRef) error + +// WalkParameters visits every parameter reachable from the document exactly +// once, invoking fn for each. It follows resolved $ref targets (param.Value), +// so each distinct *Parameter 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 components.parameters, path items, operations, callbacks, and +// webhooks. It is the parameter counterpart of WalkSchemas: useful for +// validation, linting, parameter transformation, and documentation, without +// re-deriving the (easy to get wrong) traversal. +func (doc *T) WalkParameters(fn WalkParametersFunc) error { + if doc == nil { + return nil + } + w := parameterWalker{fn: fn, seen: make(map[*Parameter]struct{})} + return w.document(doc) +} + +type parameterWalker struct { + fn WalkParametersFunc + seen map[*Parameter]struct{} +} + +func (w *parameterWalker) document(doc *T) error { + if c := doc.Components; c != nil { + 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.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 *parameterWalker) 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)) { + op := ops[method] + opPtr := ptr + "/" + strings.ToLower(method) + for i, pr := range op.Parameters { + if err := w.parameter(opPtr+"/parameters/"+strconv.Itoa(i), pr); 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(opPtr+"/callbacks/"+escapeRefString(name), cbr.Value); err != nil { + return err + } + } + } + } + return nil +} + +func (w *parameterWalker) 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 *parameterWalker) parameter(ptr string, pr *ParameterRef) error { + if pr == nil || pr.Value == nil { + return nil + } + if _, ok := w.seen[pr.Value]; ok { + return nil + } + w.seen[pr.Value] = struct{}{} + return w.fn(ptr, pr) +} diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/walk_schemas.go b/vendor/github.com/getkin/kin-openapi/openapi3/walk_schemas.go new file mode 100644 index 0000000..8d7b956 --- /dev/null +++ b/vendor/github.com/getkin/kin-openapi/openapi3/walk_schemas.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..826a33d 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.143.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