From ed27436f67e3514217d1073744d0888ad75324bc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Oct 2026 17:57:17 +0000 Subject: [PATCH] chore(deps): bump goa.design/goa/v3 from 3.32.0 to 3.34.0 --- updated-dependencies: - dependency-name: goa.design/goa/v3 dependency-version: 3.34.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 7 +- go.sum | 20 +- .../go-openapi/jsonpointer/.golangci.yml | 10 +- .../go-openapi/jsonpointer/CONTRIBUTORS.md | 6 +- .../go-openapi/jsonpointer/README.md | 15 +- .../go-openapi/jsonpointer/errors.go | 38 ++- .../go-openapi/jsonpointer/ifaces.go | 35 +- .../go-openapi/jsonpointer/jsonname/doc.go | 5 + .../jsonpointer/jsonname/go_name_provider.go | 288 +++++++++++++++++ .../go-openapi/jsonpointer/jsonname/ifaces.go | 16 + .../jsonpointer/jsonname/name_provider.go | 184 +++++++++++ .../go-openapi/jsonpointer/options.go | 19 +- .../go-openapi/jsonpointer/pointer.go | 303 ++++++++++++------ .../go-openapi/jsonpointer/revive.toml | 18 ++ vendor/goa.design/goa/v3/pkg/error.go | 114 +++++-- vendor/goa.design/goa/v3/pkg/version.go | 2 +- vendor/modules.txt | 11 +- 17 files changed, 905 insertions(+), 186 deletions(-) create mode 100644 vendor/github.com/go-openapi/jsonpointer/jsonname/doc.go create mode 100644 vendor/github.com/go-openapi/jsonpointer/jsonname/go_name_provider.go create mode 100644 vendor/github.com/go-openapi/jsonpointer/jsonname/ifaces.go create mode 100644 vendor/github.com/go-openapi/jsonpointer/jsonname/name_provider.go create mode 100644 vendor/github.com/go-openapi/jsonpointer/revive.toml diff --git a/go.mod b/go.mod index bd9db4e7aa..290e368762 100644 --- a/go.mod +++ b/go.mod @@ -38,7 +38,7 @@ require ( go.opentelemetry.io/otel v1.46.0 go.uber.org/multierr v1.11.0 go.uber.org/zap v1.28.0 - goa.design/goa/v3 v3.32.0 + goa.design/goa/v3 v3.34.0 golang.org/x/crypto v0.57.0 golang.org/x/term v0.46.0 golang.org/x/text v0.42.0 @@ -81,7 +81,7 @@ require ( github.com/go-errors/errors v1.4.2 // indirect github.com/go-logr/logr v1.4.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-openapi/jsonpointer v0.23.1 // indirect + github.com/go-openapi/jsonpointer v1.0.2 // indirect github.com/go-openapi/jsonreference v0.21.5 // indirect github.com/go-openapi/swag v0.26.0 // indirect github.com/go-openapi/swag/cmdutils v0.26.0 // indirect @@ -96,7 +96,6 @@ require ( github.com/go-openapi/swag/typeutils v0.26.0 // indirect github.com/go-openapi/swag/yamlutils v0.26.0 // indirect github.com/go-openapi/testify/enable/yaml/v2 v2.5.0 // indirect - github.com/go-openapi/testify/v2 v2.5.0 // indirect github.com/go-viper/mapstructure/v2 v2.5.0 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/btree v1.1.3 // indirect @@ -181,7 +180,7 @@ require ( gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect google.golang.org/genproto v0.0.0-20260406210006-6f92a3bedf2d // indirect google.golang.org/genproto/googleapis/api v0.0.0-20260803160001-6ac0973c030d // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260918162117-cecb64721679 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20261005182115-fad411399dd8 // indirect gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index 8ca13e4b78..2791184bfe 100644 --- a/go.sum +++ b/go.sum @@ -164,8 +164,8 @@ github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8= github.com/go-logr/logr v1.4.4/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-openapi/jsonpointer v0.23.1 h1:1HBACs7XIwR2RcmItfdSFlALhGbe6S92p0ry4d1GWg4= -github.com/go-openapi/jsonpointer v0.23.1/go.mod h1:iWRmZTrGn7XwYhtPt/fvdSFj1OfNBngqRT2UG3BxSqY= +github.com/go-openapi/jsonpointer v1.0.2 h1:NbW7xT2b+l7x+JefiPGDsZWsrLk1ogrz4AHK1TTzMt8= +github.com/go-openapi/jsonpointer v1.0.2/go.mod h1:N0rI4MbNKD7xAFTiHtB+5Nhrg2j/wwYg7m7Fmb4/jk8= github.com/go-openapi/jsonreference v0.21.5 h1:6uCGVXU/aNF13AQNggxfysJ+5ZcU4nEAe+pJyVWRdiE= github.com/go-openapi/jsonreference v0.21.5/go.mod h1:u25Bw85sX4E2jzFodh1FOKMTZLcfifd1Q+iKKOUxExw= github.com/go-openapi/swag v0.26.0 h1:GVDXCmfvhfu1BxiHo8/FA+BbKmhecHnG3varjON5/RI= @@ -196,8 +196,8 @@ github.com/go-openapi/swag/yamlutils v0.26.0 h1:H7O8l/8NJJQ/oiReEN+oMpnGMyt8G0hl github.com/go-openapi/swag/yamlutils v0.26.0/go.mod h1:1evKEGAtP37Pkwcc7EWMF0hedX0/x3Rkvei2wtG/TbU= github.com/go-openapi/testify/enable/yaml/v2 v2.5.0 h1:3hZD1fwydvCx/cc1R2uYNQirHqf2s6lqpKV3FcNTURA= github.com/go-openapi/testify/enable/yaml/v2 v2.5.0/go.mod h1:TvDZKBH7ZbMaF3EqH2AwTvNQCmzyZq8K1agRjf1B+Nk= -github.com/go-openapi/testify/v2 v2.5.0 h1:UOCr63aAsMIDydZbZGqo5Ev01D4eydItRbekDuZMJLw= -github.com/go-openapi/testify/v2 v2.5.0/go.mod h1:SgsVHtfooshd0tublTtJ50FPKhujf47YRqauXXOUxfw= +github.com/go-openapi/testify/v2 v2.8.0 h1:19QDx5b57p8KjO2E4sgs/woL4Akp+FGLRlVm2xn0Rho= +github.com/go-openapi/testify/v2 v2.8.0/go.mod h1:4gUN8jC+RqOE2qjW/vDmfyTvl5BCht4qL7oca0odrak= github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro= github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/goccy/kpoward v0.1.0 h1:UcrLMG9rq7NwrMiUc0h+qUyIlvqPzqLiPb+zQEqH8cE= @@ -531,8 +531,8 @@ go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= -goa.design/goa/v3 v3.32.0 h1:gV4JoKs/biOfSsvCyOtKaOJ8oDwC11+1fgUIGTstWaE= -goa.design/goa/v3 v3.32.0/go.mod h1:HJWm5OGSsBK3ygXuo39tyRdFfv5Wf0P/IRuxIaVOl04= +goa.design/goa/v3 v3.34.0 h1:pynlMeN5ZzaqKae0TWYlwWYQCstNd9PDzZL+xCCCjKk= +goa.design/goa/v3 v3.34.0/go.mod h1:drCT8AJJh+AcaCgNICVvJs2XQ6/yqd7lz0qft2dgNL4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.57.0 h1:3ZVCjf8Ggz7zneR/EHRVx68Ctf+2pmIMP2UFhh9cC6M= @@ -603,8 +603,8 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.50.0 h1:c2ifzfcuY7L90lZ2aKd8S4K2NpASF08SZx9ZuJkHmSU= -golang.org/x/tools v0.50.0/go.mod h1:7ulVMw3831Mwi5EZD6RomGyffr4VFjuNYXf2BbCEAV0= +golang.org/x/tools v0.51.0 h1:k4Xc/1Om9jwkBJBo4NVLMSARBoWtK10mx+W5BnXCeAI= +golang.org/x/tools v0.51.0/go.mod h1:9eEncMayCV6zRMGhR5eZEC2iBx98qWcF1HZ9Z7wJOoA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -625,8 +625,8 @@ google.golang.org/genproto v0.0.0-20260406210006-6f92a3bedf2d h1:N1Ec54vZnIPd7Mn google.golang.org/genproto v0.0.0-20260406210006-6f92a3bedf2d/go.mod h1:c2hJ1grtnH0xUiEKGDGkjGNTJ1Hy2LrblyKOHF0sqRM= google.golang.org/genproto/googleapis/api v0.0.0-20260803160001-6ac0973c030d h1:FarXi840EJWSHYTN3ERkADbPWjl307+FGrA22KAVjjc= google.golang.org/genproto/googleapis/api v0.0.0-20260803160001-6ac0973c030d/go.mod h1:K/+WGbmBY7aNW1HDw1fJnKYo10i0DkAX6pows00dLig= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260918162117-cecb64721679 h1:KmqdJU4vrNcxy/6qdg3JduZtalEXrJLspVltnR1cE+8= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260918162117-cecb64721679/go.mod h1:OaIUM3+LpYcK2GXM4FTmhWoIq371Owdr+Cc7/BsYHHc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20261005182115-fad411399dd8 h1:0SXBqli5dnuCKMjI/X3FM226zEaEa0GUcSCJtN1vL2o= +google.golang.org/genproto/googleapis/rpc v0.0.0-20261005182115-fad411399dd8/go.mod h1:OaIUM3+LpYcK2GXM4FTmhWoIq371Owdr+Cc7/BsYHHc= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= diff --git a/vendor/github.com/go-openapi/jsonpointer/.golangci.yml b/vendor/github.com/go-openapi/jsonpointer/.golangci.yml index dc7c96053d..e09106a30d 100644 --- a/vendor/github.com/go-openapi/jsonpointer/.golangci.yml +++ b/vendor/github.com/go-openapi/jsonpointer/.golangci.yml @@ -4,8 +4,12 @@ linters: disable: - depguard - funlen + - goconst - godox + - gomodguard + - gomodguard_v2 - exhaustruct + - exhaustruct_v5 - nlreturn - nonamedreturns - noinlineerr @@ -25,10 +29,12 @@ linters: goconst: min-len: 2 min-occurrences: 3 + gocognit: + min-complexity: 35 cyclop: - max-complexity: 20 + max-complexity: 25 gocyclo: - min-complexity: 20 + min-complexity: 35 exhaustive: default-signifies-exhaustive: true default-case-required: true diff --git a/vendor/github.com/go-openapi/jsonpointer/CONTRIBUTORS.md b/vendor/github.com/go-openapi/jsonpointer/CONTRIBUTORS.md index 9990f4a354..6941823e6c 100644 --- a/vendor/github.com/go-openapi/jsonpointer/CONTRIBUTORS.md +++ b/vendor/github.com/go-openapi/jsonpointer/CONTRIBUTORS.md @@ -4,21 +4,21 @@ | Total Contributors | Total Contributions | | --- | --- | -| 13 | 111 | +| 13 | 136 | | Username | All Time Contribution Count | All Commits | | --- | --- | --- | -| @fredbi | 63 | | +| @fredbi | 87 | | | @casualjim | 33 | | | @magodo | 3 | | | @youyuanwu | 3 | | +| @alexandear | 2 | | | @gaiaz-iusipov | 1 | | | @gbjk | 1 | | | @gordallott | 1 | | | @ianlancetaylor | 1 | | | @mfleader | 1 | | | @Neo2308 | 1 | | -| @alexandear | 1 | | | @olivierlemasle | 1 | | | @testwill | 1 | | diff --git a/vendor/github.com/go-openapi/jsonpointer/README.md b/vendor/github.com/go-openapi/jsonpointer/README.md index 24fbe1bf68..e563fac772 100644 --- a/vendor/github.com/go-openapi/jsonpointer/README.md +++ b/vendor/github.com/go-openapi/jsonpointer/README.md @@ -5,7 +5,7 @@ -[![Release][release-badge]][release-url] [![Go Report Card][gocard-badge]][gocard-url] [![CodeFactor Grade][codefactor-badge]][codefactor-url] [![License][license-badge]][license-url] +[![Release][release-badge]][release-url] [![CodeFactor Grade][codefactor-badge]][codefactor-url] [![License][license-badge]][license-url] [![GoDoc][godoc-badge]][godoc-url] [![Discord Channel][discord-badge]][discord-url] [![go version][goversion-badge]][goversion-url] ![Top language][top-badge] ![Commits since latest release][commits-badge] @@ -16,6 +16,14 @@ An implementation of JSON Pointer for golang, which supports go `struct`. ## Announcements +* **2026-07-07** : landing v1.0.0 + * stable API pledge + +* **2026-06-29** : reinsourced external dependency to swag (v0.24.0) + * module `github.com/go-openapi/swag/jsonname` is source directly here, so we no longer have any external dependency + * `jsonname` was never really used by any other package, so it makes sense to deprecate it away from the `swag` family + and retrofit its functionality here. `jsonpointer` no longer get external dependencies, besides test dependencies. + * **2026-04-15** : added support for trailing "-" for arrays (v0.23.0) * this brings full support of [RFC6901][RFC6901] * this is supported for types relying on the reflection-based implemented @@ -30,12 +38,13 @@ An implementation of JSON Pointer for golang, which supports go `struct`. * the default name provider in use is not fully aligned with go JSON stdlib * exposed an option (or global setting) to change the provider that resolves a struct into json keys * the default behavior is not altered - * a new alternate name provider is added (imported from `go-openapi/swag/jsonname`), aligned with JSON stdlib behavior ## Status API is stable and feature-complete. +The project continues to receive regular updates, bug fixes and hygiene maintenance (CI, linting, etc). + ## Import this library in your project ```cmd @@ -143,8 +152,6 @@ Maintainers can cut a new release by either: [release-badge]: https://badge.fury.io/gh/go-openapi%2Fjsonpointer.svg [release-url]: https://badge.fury.io/gh/go-openapi%2Fjsonpointer -[gocard-badge]: https://goreportcard.com/badge/github.com/go-openapi/jsonpointer -[gocard-url]: https://goreportcard.com/report/github.com/go-openapi/jsonpointer [codefactor-badge]: https://img.shields.io/codefactor/grade/github/go-openapi/jsonpointer [codefactor-url]: https://www.codefactor.io/repository/github/go-openapi/jsonpointer diff --git a/vendor/github.com/go-openapi/jsonpointer/errors.go b/vendor/github.com/go-openapi/jsonpointer/errors.go index 8813474d44..9b54008baa 100644 --- a/vendor/github.com/go-openapi/jsonpointer/errors.go +++ b/vendor/github.com/go-openapi/jsonpointer/errors.go @@ -3,7 +3,10 @@ package jsonpointer -import "fmt" +import ( + "fmt" + "reflect" +) type pointerError string @@ -21,14 +24,15 @@ const ( // ErrUnsupportedValueType indicates that a value of the wrong type is being set. ErrUnsupportedValueType pointerError = "only structs, pointers, maps and slices are supported for setting values" - // ErrDashToken indicates use of the RFC 6901 "-" reference token - // in a context where it cannot be resolved. + // ErrDashToken indicates use of the RFC 6901 "-" reference token in a context where it cannot be + // resolved. + // + // Per RFC 6901 §4 the "-" token refers to the (nonexistent) element after the last array element. + // It may only be used as the terminal token of a [Pointer.Set] against a slice, where it means + // "append". // - // Per RFC 6901 §4 the "-" token refers to the (nonexistent) element - // after the last array element. It may only be used as the terminal - // token of a [Pointer.Set] against a slice, where it means "append". - // Any other use (get, offset, intermediate traversal, non-slice target) - // is an error condition that wraps this sentinel. + // Any other use (get, offset, intermediate traversal, non-slice target) is an error condition that + // wraps this sentinel. ErrDashToken pointerError = `the "-" array token cannot be resolved here` //nolint:gosec // G101 false positive: this is a JSON Pointer reference token, not a credential. ) @@ -46,6 +50,24 @@ func errInvalidReference(token string) error { return fmt.Errorf("invalid token reference %q: %w", token, ErrPointer) } +func errNoField(token string) error { + return fmt.Errorf("object has no field %q: %w", token, ErrPointer) +} + +// errUnreachableField reports a field that exists on the type but cannot be reached on this value, +// because it is promoted through a nil embedded pointer. +func errUnreachableField(name string, cause error) error { + return fmt.Errorf("cannot reach field %q: %w: %w", name, cause, ErrPointer) +} + +// errMapKey reports a reference token that cannot serve as a key of the target map type. +// +// JSON object member names are strings, so only maps keyed by a string type (or by an interface +// type a string satisfies) can be addressed by a JSON pointer. +func errMapKey(token string, mapType reflect.Type) error { + return fmt.Errorf("can't use token %q as a key of map type %v: %w", token, mapType, ErrPointer) +} + func errDashOnGet() error { return fmt.Errorf("cannot resolve %q token on get: %w: %w", dashToken, ErrDashToken, ErrPointer) } diff --git a/vendor/github.com/go-openapi/jsonpointer/ifaces.go b/vendor/github.com/go-openapi/jsonpointer/ifaces.go index 1e56ac0442..31359c48fa 100644 --- a/vendor/github.com/go-openapi/jsonpointer/ifaces.go +++ b/vendor/github.com/go-openapi/jsonpointer/ifaces.go @@ -5,39 +5,42 @@ package jsonpointer import "reflect" -// JSONPointable is an interface for structs to implement, -// when they need to customize the json pointer process or want to avoid the use of reflection. +// JSONPointable is an interface for structs to implement, when they need to customize the json +// pointer process or want to avoid the use of reflection. type JSONPointable interface { // JSONLookup returns a value pointed at this (unescaped) key. JSONLookup(key string) (any, error) } -// JSONSetable is an interface for structs to implement, -// when they need to customize the json pointer process or want to avoid the use of reflection. +// JSONSetable is an interface for structs to implement, when they need to customize the json +// pointer process or want to avoid the use of reflection. // // # Handling of the RFC 6901 "-" token // -// When a type implementing JSONSetable is the terminal parent of a [Pointer.Set] -// call, the library passes the raw reference token to JSONSet without -// interpretation. In particular, the RFC 6901 "-" token (which conventionally -// means "append" for arrays, per RFC 6902) is forwarded verbatim as the key -// argument. Implementations that model an array-like container are expected -// to give "-" the append semantics; implementations that do not should return -// an error wrapping [ErrDashToken] (or [ErrPointer]) for clarity. +// When a type implementing JSONSetable is the terminal parent of a [Pointer.Set] call, the library +// passes the raw reference token to JSONSet without interpretation. // -// Implementations are responsible for any in-place mutation: the library does -// not attempt to rebind the result of JSONSet into a parent container. +// In particular, the RFC 6901 "-" token (which conventionally means "append" for arrays, per RFC +// 6902) is forwarded verbatim as the key argument. +// +// Implementations that model an array-like container are expected to give "-" the append semantics; +// implementations that do not should return an error wrapping [ErrDashToken] (or [ErrPointer]) for +// clarity. +// +// Implementations are responsible for any in-place mutation: the library does not attempt to rebind +// the result of JSONSet into a parent container. type JSONSetable interface { // JSONSet sets the value pointed at the (unescaped) key. // - // The key may be the RFC 6901 "-" token when the pointer targets a - // slice-like member; see the interface documentation for details. + // The key may be the RFC 6901 "-" token when the pointer targets a slice-like member; see the + // interface documentation for details. JSONSet(key string, value any) error } // NameProvider knows how to resolve go struct fields into json names. // -// The default provider is brought by [github.com/go-openapi/swag/jsonname.DefaultJSONNameProvider]. +// The default provider is brought by +// [github.com/go-openapi/jsonpointer/jsonname.DefaultJSONNameProvider]. type NameProvider interface { // GetGoName gets the go name for a json property name GetGoName(subject any, name string) (string, bool) diff --git a/vendor/github.com/go-openapi/jsonpointer/jsonname/doc.go b/vendor/github.com/go-openapi/jsonpointer/jsonname/doc.go new file mode 100644 index 0000000000..79232eaca4 --- /dev/null +++ b/vendor/github.com/go-openapi/jsonpointer/jsonname/doc.go @@ -0,0 +1,5 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +// Package jsonname is a provider of json property names from go properties. +package jsonname diff --git a/vendor/github.com/go-openapi/jsonpointer/jsonname/go_name_provider.go b/vendor/github.com/go-openapi/jsonpointer/jsonname/go_name_provider.go new file mode 100644 index 0000000000..6c93621e5e --- /dev/null +++ b/vendor/github.com/go-openapi/jsonpointer/jsonname/go_name_provider.go @@ -0,0 +1,288 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package jsonname + +import ( + "reflect" + "strings" + "sync" +) + +var _ providerIface = (*GoNameProvider)(nil) + +// GoNameProvider resolves json property names to go struct field names following the same rules as +// the standard library's [encoding/json] package. +// +// Contrary to [NameProvider], it considers exported fields without a json tag, and promotes fields +// from anonymous embedded struct types. +// +// Rules (aligned with encoding/json): +// +// - unexported fields are ignored; +// - a field tagged `json:"-"` is ignored; +// - a field tagged `json:"-,"` is kept under the json name "-" (stdlib quirk); +// - a field tagged `json:""` or with no json tag at all keeps its Go name as json name; +// - anonymous struct fields without an explicit json tag have their fields +// promoted into the parent, following breadth-first depth rules: +// a shallower field wins over a deeper one; at equal depth, a conflict +// discards all conflicting fields unless exactly one has an explicit json tag. +// +// This type is safe for concurrent use. +type GoNameProvider struct { + lock sync.Mutex + index map[reflect.Type]nameIndex +} + +// NewGoNameProvider creates a new [GoNameProvider]. +func NewGoNameProvider() *GoNameProvider { + return &GoNameProvider{ + index: make(map[reflect.Type]nameIndex), + } +} + +// GetJSONNames gets all the json property names for a type. +func (n *GoNameProvider) GetJSONNames(subject any) []string { + n.lock.Lock() + defer n.lock.Unlock() + + tpe := typeOfSubject(subject) + names := n.nameIndexFor(tpe) + + res := make([]string, 0, len(names.jsonNames)) + for k := range names.jsonNames { + res = append(res, k) + } + + return res +} + +// GetJSONName gets the json name for a go property name. +func (n *GoNameProvider) GetJSONName(subject any, name string) (string, bool) { + tpe := typeOfSubject(subject) + + return n.GetJSONNameForType(tpe, name) +} + +// GetJSONNameForType gets the json name for a go property name on a given type. +func (n *GoNameProvider) GetJSONNameForType(tpe reflect.Type, name string) (string, bool) { + n.lock.Lock() + defer n.lock.Unlock() + + names := n.nameIndexFor(tpe) + nme, ok := names.goNames[name] + + return nme, ok +} + +// GetGoName gets the go name for a json property name. +func (n *GoNameProvider) GetGoName(subject any, name string) (string, bool) { + tpe := typeOfSubject(subject) + + return n.GetGoNameForType(tpe, name) +} + +// GetGoNameForType gets the go name for a given type for a json property name. +func (n *GoNameProvider) GetGoNameForType(tpe reflect.Type, name string) (string, bool) { + n.lock.Lock() + defer n.lock.Unlock() + + names := n.nameIndexFor(tpe) + nme, ok := names.jsonNames[name] + + return nme, ok +} + +func (n *GoNameProvider) nameIndexFor(tpe reflect.Type) nameIndex { + if names, ok := n.index[tpe]; ok { + return names + } + + names := buildGoNameIndex(tpe) + n.index[tpe] = names + + return names +} + +// fieldEntry captures a candidate field discovered while walking a struct along with the +// indirection path from the root type (used to resolve conflicts by depth in the same way +// encoding/json does). +type fieldEntry struct { + goName string + jsonName string + index []int + tagged bool +} + +func buildGoNameIndex(tpe reflect.Type) nameIndex { + fields := collectGoFields(tpe) + + idx := make(map[string]string, len(fields)) + reverseIdx := make(map[string]string, len(fields)) + for _, f := range fields { + idx[f.jsonName] = f.goName + reverseIdx[f.goName] = f.jsonName + } + + return nameIndex{jsonNames: idx, goNames: reverseIdx} +} + +// collectGoFields walks tpe breadth-first along anonymous struct fields, +// reproducing the field selection performed by encoding/json.typeFields. +// +//nolint:gocognit // everything is inlined to help the compiler determine what escapes and what doesn't +func collectGoFields(tpe reflect.Type) []fieldEntry { + if tpe == nil || tpe.Kind() != reflect.Struct { + return nil + } + + type queued struct { + typ reflect.Type + index []int + } + + current := []queued{} + next := []queued{{typ: tpe}} + visited := map[reflect.Type]bool{tpe: true} + + var ( + candidates []fieldEntry + count = map[string]int{} + nextCount = map[string]int{} + ) + + for len(next) > 0 { + current, next = next, current[:0] + count, nextCount = nextCount, count + for k := range nextCount { + delete(nextCount, k) + } + + for _, q := range current { + for i := range q.typ.NumField() { + sf := q.typ.Field(i) + + if sf.Anonymous { + ft := sf.Type + if ft.Kind() == reflect.Pointer { + ft = ft.Elem() + } + if !sf.IsExported() && ft.Kind() != reflect.Struct { + continue + } + } else if !sf.IsExported() { + continue + } + + tag := sf.Tag.Get("json") + if tag == "-" { + continue + } + jsonName, _ := parseJSONTag(tag) + tagged := jsonName != "" + + ft := sf.Type + if ft.Kind() == reflect.Pointer { + ft = ft.Elem() + } + + if sf.Anonymous && ft.Kind() == reflect.Struct && !tagged { + if visited[ft] { + continue + } + visited[ft] = true + + index := make([]int, len(q.index)+1) + copy(index, q.index) + index[len(q.index)] = i + next = append(next, queued{typ: ft, index: index}) + + continue + } + + name := jsonName + if name == "" { + name = sf.Name + } + + index := make([]int, len(q.index)+1) + copy(index, q.index) + index[len(q.index)] = i + + candidates = append(candidates, fieldEntry{ + goName: sf.Name, + jsonName: name, + index: index, + tagged: tagged, + }) + nextCount[name]++ + } + } + } + + return dominantFields(candidates) +} + +// dominantFields applies the Go encoding/json conflict resolution rules: at each JSON name, the +// shallowest field wins; at equal depth, a uniquely tagged candidate wins; otherwise all candidates +// for that name are dropped. +func dominantFields(candidates []fieldEntry) []fieldEntry { + byName := make(map[string][]fieldEntry, len(candidates)) + for _, c := range candidates { + byName[c.jsonName] = append(byName[c.jsonName], c) + } + + out := make([]fieldEntry, 0, len(byName)) + for _, group := range byName { + if len(group) == 1 { + out = append(out, group[0]) + + continue + } + + minDepth := len(group[0].index) + for _, c := range group[1:] { + if len(c.index) < minDepth { + minDepth = len(c.index) + } + } + + var shallow []fieldEntry + for _, c := range group { + if len(c.index) == minDepth { + shallow = append(shallow, c) + } + } + + if len(shallow) == 1 { + out = append(out, shallow[0]) + + continue + } + + var tagged []fieldEntry + for _, c := range shallow { + if c.tagged { + tagged = append(tagged, c) + } + } + if len(tagged) == 1 { + out = append(out, tagged[0]) + } + } + + return out +} + +// parseJSONTag returns the name component of a json struct tag and whether it carried any non-name +// option (kept for future-proofing, e.g. "omitempty"). +func parseJSONTag(tag string) (string, string) { + if tag == "" { + return "", "" + } + if before, after, ok := strings.Cut(tag, ","); ok { + return before, after + } + + return tag, "" +} diff --git a/vendor/github.com/go-openapi/jsonpointer/jsonname/ifaces.go b/vendor/github.com/go-openapi/jsonpointer/jsonname/ifaces.go new file mode 100644 index 0000000000..64871f0d27 --- /dev/null +++ b/vendor/github.com/go-openapi/jsonpointer/jsonname/ifaces.go @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package jsonname + +import "reflect" + +// providerIface is an unexported compile-time contract that every name provider in this package is +// expected to satisfy. +// +// It mirrors the interface declared by the main consumer of this module: +// [github.com/go-openapi/jsonpointer.NameProvider]. +type providerIface interface { + GetGoName(subject any, name string) (string, bool) + GetGoNameForType(tpe reflect.Type, name string) (string, bool) +} diff --git a/vendor/github.com/go-openapi/jsonpointer/jsonname/name_provider.go b/vendor/github.com/go-openapi/jsonpointer/jsonname/name_provider.go new file mode 100644 index 0000000000..377c18e2c0 --- /dev/null +++ b/vendor/github.com/go-openapi/jsonpointer/jsonname/name_provider.go @@ -0,0 +1,184 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package jsonname + +import ( + "reflect" + "strings" + "sync" +) + +// DefaultJSONNameProvider is the default cache for types. +var DefaultJSONNameProvider = NewNameProvider() //nolint:gochecknoglobals // default settings, for backward compatible package-level settings + +var _ providerIface = (*NameProvider)(nil) + +// NameProvider represents an object capable of translating from go property names to json property +// names. +// +// This type is thread-safe. +// +// See [github.com/go-openapi/jsonpointer.Pointer] for an example. +type NameProvider struct { + lock *sync.Mutex + index map[reflect.Type]nameIndex +} + +type nameIndex struct { + jsonNames map[string]string + goNames map[string]string +} + +// NewNameProvider creates a new name provider. +func NewNameProvider() *NameProvider { + return &NameProvider{ + lock: &sync.Mutex{}, + index: make(map[reflect.Type]nameIndex), + } +} + +func buildnameIndex(tpe reflect.Type, idx, reverseIdx map[string]string) { + for targetDes := range tpe.Fields() { + + if targetDes.PkgPath != "" { // unexported + continue + } + + if targetDes.Anonymous { // walk embedded structures tree down first + // An embedded field is not necessarily a struct: it may be a pointer to a struct, or a named + // non-struct type (e.g. an embedded named slice or map). Only struct shapes carry promoted + // fields worth indexing; anything else contributes no json name and must not be walked. + if embedded := structTypeOf(targetDes.Type); embedded != nil { + buildnameIndex(embedded, idx, reverseIdx) + } + + continue + } + + if tag := targetDes.Tag.Get("json"); tag != "" { + + parts := strings.Split(tag, ",") + if len(parts) == 0 { + continue + } + + nm := parts[0] + if nm == "-" { + continue + } + if nm == "" { // empty string means we want to use the Go name + nm = targetDes.Name + } + + idx[nm] = targetDes.Name + reverseIdx[targetDes.Name] = nm + } + } +} + +func newNameIndex(tpe reflect.Type) nameIndex { + tpe = structTypeOf(tpe) + if tpe == nil { + // only struct shapes carry json names: anything else indexes to nothing. + return nameIndex{jsonNames: map[string]string{}, goNames: map[string]string{}} + } + + idx := make(map[string]string, tpe.NumField()) + reverseIdx := make(map[string]string, tpe.NumField()) + + buildnameIndex(tpe, idx, reverseIdx) + return nameIndex{jsonNames: idx, goNames: reverseIdx} +} + +// structTypeOf reduces tpe to the struct type it designates, dereferencing a pointer type. +// +// It returns nil when tpe is nil or does not designate a struct, so that callers may treat +// "nothing to index here" uniformly instead of panicking in [reflect.Type.NumField]. +func structTypeOf(tpe reflect.Type) reflect.Type { + if tpe == nil { + return nil + } + + if tpe.Kind() == reflect.Pointer { + tpe = tpe.Elem() + } + + if tpe.Kind() != reflect.Struct { + return nil + } + + return tpe +} + +// typeOfSubject returns the type of a document subject, dereferencing pointers. +// +// It returns nil for an untyped nil subject, which resolves to an empty name index rather than +// panicking in [reflect.Value.Type]. +func typeOfSubject(subject any) reflect.Type { + rValue := reflect.Indirect(reflect.ValueOf(subject)) + if !rValue.IsValid() { + return nil + } + + return rValue.Type() +} + +// GetJSONNames gets all the json property names for a type. +func (n *NameProvider) GetJSONNames(subject any) []string { + n.lock.Lock() + defer n.lock.Unlock() + tpe := typeOfSubject(subject) + names, ok := n.index[tpe] + if !ok { + names = n.makeNameIndex(tpe) + } + + res := make([]string, 0, len(names.jsonNames)) + for k := range names.jsonNames { + res = append(res, k) + } + return res +} + +// GetJSONName gets the json name for a go property name. +func (n *NameProvider) GetJSONName(subject any, name string) (string, bool) { + tpe := typeOfSubject(subject) + return n.GetJSONNameForType(tpe, name) +} + +// GetJSONNameForType gets the json name for a go property name on a given type. +func (n *NameProvider) GetJSONNameForType(tpe reflect.Type, name string) (string, bool) { + n.lock.Lock() + defer n.lock.Unlock() + names, ok := n.index[tpe] + if !ok { + names = n.makeNameIndex(tpe) + } + nme, ok := names.goNames[name] + return nme, ok +} + +// GetGoName gets the go name for a json property name. +func (n *NameProvider) GetGoName(subject any, name string) (string, bool) { + tpe := typeOfSubject(subject) + return n.GetGoNameForType(tpe, name) +} + +// GetGoNameForType gets the go name for a given type for a json property name. +func (n *NameProvider) GetGoNameForType(tpe reflect.Type, name string) (string, bool) { + n.lock.Lock() + defer n.lock.Unlock() + names, ok := n.index[tpe] + if !ok { + names = n.makeNameIndex(tpe) + } + nme, ok := names.jsonNames[name] + return nme, ok +} + +func (n *NameProvider) makeNameIndex(tpe reflect.Type) nameIndex { + names := newNameIndex(tpe) + n.index[tpe] = names + return names +} diff --git a/vendor/github.com/go-openapi/jsonpointer/options.go b/vendor/github.com/go-openapi/jsonpointer/options.go index d52caab222..223c1e5ff0 100644 --- a/vendor/github.com/go-openapi/jsonpointer/options.go +++ b/vendor/github.com/go-openapi/jsonpointer/options.go @@ -6,7 +6,7 @@ package jsonpointer import ( "sync" - "github.com/go-openapi/swag/jsonname" + "github.com/go-openapi/jsonpointer/jsonname" ) // Option to tune the behavior of a JSON [Pointer]. @@ -25,9 +25,9 @@ var ( // // By default, the default provider is [jsonname.DefaultJSONNameProvider]. // -// It is safe to call concurrently with [Pointer.Get], [Pointer.Set], -// [GetForToken] and [SetForToken]. The typical usage is to call it once -// at initialization time. +// It is safe to call concurrently with [Pointer.Get], [Pointer.Set], [GetForToken] and +// [SetForToken]. +// The typical usage is to call it once at initialization time. // // A nil provider is ignored. func SetDefaultNameProvider(provider NameProvider) { @@ -41,16 +41,15 @@ func SetDefaultNameProvider(provider NameProvider) { defaultOptions.provider = provider } -// UseGoNameProvider sets the [NameProvider] as a package-level default -// to the alternative provider [jsonname.GoNameProvider], that covers a few areas -// not supported by the default name provider. +// UseGoNameProvider sets the [NameProvider] as a package-level default to the alternative provider +// [jsonname.GoNameProvider], that covers a few areas not supported by the default name provider. // // This implementation supports untagged exported fields and embedded types in go struct. // It follows strictly the behavior of the JSON standard library regarding field naming conventions. // -// It is safe to call concurrently with [Pointer.Get], [Pointer.Set], -// [GetForToken] and [SetForToken]. The typical usage is to call it once -// at initialization time. +// It is safe to call concurrently with [Pointer.Get], [Pointer.Set], [GetForToken] and +// [SetForToken]. +// The typical usage is to call it once at initialization time. func UseGoNameProvider() { SetDefaultNameProvider(jsonname.NewGoNameProvider()) } diff --git a/vendor/github.com/go-openapi/jsonpointer/pointer.go b/vendor/github.com/go-openapi/jsonpointer/pointer.go index 2369c1827e..d17fa19ac4 100644 --- a/vendor/github.com/go-openapi/jsonpointer/pointer.go +++ b/vendor/github.com/go-openapi/jsonpointer/pointer.go @@ -26,20 +26,25 @@ const ( // // - if a type implements [JSONPointable], its [JSONPointable.JSONLookup] method is used to resolve [Pointer.Get] // - if a type implements [JSONSetable], its [JSONSetable.JSONSet] method is used to resolve [Pointer.Set] -// - a go map[K]V is interpreted as an object, with type K assignable to a string +// - a go map[K]V is interpreted as an object, with K a string type. A named string type is +// converted; a map keyed by anything else (e.g. map[int]V) cannot be addressed by a pointer // - a go slice []T is interpreted as an array // - a go struct is interpreted as an object, with exported fields interpreted as keys -// - promoted fields from an embedded struct are traversed +// - promoted fields from an embedded struct are traversed, including through an embedded pointer +// to a struct. Resolving one on a value whose embedded pointer is nil reports an error // - scalars (e.g. int, float64 ...), channels, functions and go arrays cannot be traversed // // For struct s resolved by reflection, key mappings honor the conventional struct tag `json`. // -// Fields that do not specify a `json` tag, or specify an empty one, or are tagged as `json:"-"` are ignored. +// Fields that do not specify a `json` tag, or specify an empty one, or are tagged as `json:"-"` are +// ignored. // // # Limitations // // - Unlike go standard marshaling, untagged fields do not default to the go field name and are ignored. -// - anonymous fields are not traversed if untagged +// - an anonymous field is walked for the fields it promotes, and its own `json` tag is ignored. +// An anonymous field that is not a struct (e.g. an embedded named slice) promotes nothing and +// is unreachable. type Pointer struct { referenceTokens []string } @@ -61,25 +66,37 @@ func (p *Pointer) Get(document any, opts ...Option) (any, reflect.Kind, error) { return p.get(document, o.provider) } -// Set uses the pointer to set a value from a data type -// that represent a JSON document. +// Set uses the pointer to set a value from a data type that represent a JSON document. // // # Mutation contract // -// Set mutates the provided document in place whenever Go's type system allows -// it: when document is a map, a pointer, or when the targeted value is reached -// through an addressable ancestor (e.g. a struct field traversed via a pointer, -// a slice element). Callers that rely on this in-place behavior may continue -// to ignore the returned document. +// Set mutates the provided document in place whenever Go's type system allows it: when document is +// a map, a pointer, or when the targeted value is reached through an addressable ancestor (e.g. a +// struct field traversed via a pointer, a slice element). +// +// Callers that rely on this in-place behavior may continue to ignore the returned document. // // The returned document is only load-bearing when Set cannot mutate in place. -// This happens in one specific case: appending to a top-level slice passed by -// value (e.g. document of type []T rather than *[]T) via the RFC 6901 "-" -// terminal token. reflect.Append produces a new slice header that the library -// cannot rebind into the caller's variable; the updated document is returned -// instead. Pass *[]T if you want in-place rebind for that case as well. +// +// This happens in one specific case: appending to a top-level slice passed by value (e.g. document +// of type []T rather than *[]T) via the RFC 6901 "-" terminal token. reflect.Append produces a new +// slice header that the library cannot rebind into the caller's variable; the updated document is +// returned instead. +// +// Pass *[]T if you want in-place rebind for that case as well. // // See [ErrDashToken] for the semantics of the "-" token. +// +// # Setting a null value +// +// A nil value (what [encoding/json] decodes a JSON null into) is set as the zero value of the +// target, provided the target can hold nil: an interface, pointer, map, slice, channel or func. +// +// Setting nil on a target that cannot represent it — a string or an int field, an element of a +// []int — reports an error wrapping [ErrPointer] rather than substituting a zero value, which +// would erase the difference between a null and a 0. +// +// Setting a map member to nil keeps the member with a nil value. It does not delete it. func (p *Pointer) Set(document any, value any, opts ...Option) (any, error) { o := optionsWithDefaults(opts) @@ -112,23 +129,23 @@ func (p *Pointer) String() string { return pointerSeparator + strings.Join(p.referenceTokens, pointerSeparator) } -// Offset returns the byte offset, in the raw JSON text of document, of the -// location referenced by this pointer's terminal token. +// Offset returns the byte offset, in the raw JSON text of document, of the location referenced by +// this pointer's terminal token. +// +// Unlike [Pointer.Get] and [Pointer.Set], which operate on a decoded Go value, Offset operates +// directly on the textual JSON source. // -// Unlike [Pointer.Get] and [Pointer.Set], which operate on a decoded Go value, -// Offset operates directly on the textual JSON source. It drives an -// [encoding/json.Decoder] over the string and stops at the terminal token, -// returning the position at which the decoder was about to read that token. +// It drives an [encoding/json.Decoder] over the string and stops at the terminal token, returning +// the position at which the decoder was about to read that token. // -// It is primarily intended for tooling that needs to map a pointer back to a -// region of the original source: reporting line/column for validation or -// parse diagnostics, extracting a sub-document by slicing the raw bytes, or -// highlighting the referenced span in an editor. +// It is primarily intended for tooling that needs to map a pointer back to a region of the original +// source: reporting line/column for validation or parse diagnostics, extracting a sub-document by +// slicing the raw bytes, or highlighting the referenced span in an editor. // // # Offset semantics // -// The meaning of the returned offset depends on whether the terminal token -// addresses an object property or an array element: +// The meaning of the returned offset depends on whether the terminal token addresses an object +// property or an array element: // // - Object property: the offset points to the first byte of the key (its // opening quote character), not to the associated value. For example, @@ -183,16 +200,15 @@ func (p *Pointer) Offset(document string) (int64, error) { return skipJSONSeparator(document, offset), nil } -// skipJSONSeparator advances offset past trailing JSON whitespace and at most -// one value separator (comma) in document, so the result points at the first -// byte of the next JSON token. +// skipJSONSeparator advances offset past trailing JSON whitespace and at most one value separator +// (comma) in document, so the result points at the first byte of the next JSON token. // -// The streaming decoder's InputOffset sits right after the most recently -// consumed token, which between values is the comma (or whitespace) — not -// the following token. Normalizing here keeps Offset's contract uniform: -// for both object keys and array elements, and regardless of position within -// the parent container, the returned offset always points at the first byte -// of the addressed token. +// The streaming decoder's InputOffset sits right after the most recently consumed token, which +// between values is the comma (or whitespace) — not the following token. +// +// Normalizing here keeps Offset's contract uniform: for both object keys and array elements, and +// regardless of position within the parent container, the returned offset always points at the +// first byte of the addressed token. func skipJSONSeparator(document string, offset int64) int64 { n := int64(len(document)) for offset < n && isJSONWhitespace(document[offset]) { @@ -279,14 +295,13 @@ func (p *Pointer) set(node, data any, nameProvider NameProvider) (any, error) { return p.setAt(node, p.referenceTokens, data, nameProvider) } -// setAt recursively walks the token list, setting the data at the terminal -// token and rebinding any new child reference (e.g. a slice header returned -// by an "-" append) into its parent on the way back up. +// setAt recursively walks the token list, setting the data at the terminal token and rebinding any +// new child reference (e.g. a slice header returned by an "-" append) into its parent on the way +// back up. // -// Returning the (possibly new) node at each level is what makes append work -// at any depth without requiring the caller to pass a pointer to the -// containing slice: the new slice header propagates up and each parent -// rebinds it via the appropriate kind-specific setter. +// Returning the (possibly new) node at each level makes append work at any depth without +// requiring the caller to pass a pointer to the containing slice: the new slice header propagates +// up and each parent rebinds it via the appropriate kind-specific setter. func (p *Pointer) setAt(node any, tokens []string, data any, nameProvider NameProvider) (any, error) { decodedToken := Unescape(tokens[0]) @@ -309,15 +324,14 @@ func (p *Pointer) setAt(node any, tokens []string, data any, nameProvider NamePr // rebindChild writes newChild back into node at decodedToken. // -// For cases where the child was already mutated in place (pointer aliasing, -// addressable slice elements) the rebind is a safe no-op. For cases where -// the child was returned by value (map entries holding a slice, slices -// reached through a non-addressable ancestor), the rebind propagates the -// new value into the parent. +// For cases where the child was already mutated in place (pointer aliasing, addressable slice +// elements) the rebind is a safe no-op. +// +// For cases where the child was returned by value (map entries holding a slice, slices reached +// through a non-addressable ancestor), the rebind propagates the new value into the parent. // -// Parents implementing [JSONPointable] are left alone: they took ownership -// of the child via JSONLookup and did not opt into a JSONSet-based rebind -// on intermediate tokens. +// Parents implementing [JSONPointable] are left alone: they took ownership of the child via +// JSONLookup and did not opt into a JSONSet-based rebind on intermediate tokens. func rebindChild(node any, decodedToken string, newChild any, nameProvider NameProvider) (any, error) { if _, ok := node.(JSONPointable); ok { return node, nil @@ -331,7 +345,10 @@ func rebindChild(node any, decodedToken string, newChild any, nameProvider NameP if !ok { return node, fmt.Errorf("object has no field %q: %w", decodedToken, ErrPointer) } - fld := rValue.FieldByName(nm) + fld, err := fieldByName(rValue, nm) + if err != nil { + return node, err + } if !fld.CanSet() { return node, nil } @@ -339,7 +356,16 @@ func rebindChild(node any, decodedToken string, newChild any, nameProvider NameP return node, nil case reflect.Map: - rValue.SetMapIndex(reflect.ValueOf(decodedToken), reflect.ValueOf(newChild)) + kv, err := mapKeyValue(rValue.Type(), decodedToken) + if err != nil { + return node, err + } + nv := reflect.ValueOf(newChild) + if !nv.IsValid() || !nv.Type().AssignableTo(rValue.Type().Elem()) { + // the child was mutated in place: there is nothing to rebind. + return node, nil + } + rValue.SetMapIndex(kv, nv) return node, nil case reflect.Slice: @@ -362,9 +388,9 @@ func rebindChild(node any, decodedToken string, newChild any, nameProvider NameP } } -// assignReflectValue assigns src into dst, unwrapping a pointer when dst -// expects the pointee type. This tolerates the pointer-wrapping performed -// by [typeFromValue] for addressable fields. +// assignReflectValue assigns src into dst, unwrapping a pointer when dst expects the pointee type. +// +// This tolerates the pointer-wrapping performed by [typeFromValue] for addressable fields. func assignReflectValue(dst reflect.Value, src any) { nv := reflect.ValueOf(src) if !nv.IsValid() { @@ -410,10 +436,18 @@ func (p *Pointer) resolveNodeForToken(node any, decodedToken string, nameProvide return nil, fmt.Errorf("object has no field %q: %w", decodedToken, ErrPointer) } - return typeFromValue(rValue.FieldByName(nm)), nil + fld, err := fieldByName(rValue, nm) + if err != nil { + return nil, err + } + + return typeFromValue(fld), nil case reflect.Map: - kv := reflect.ValueOf(decodedToken) + kv, err := mapKeyValue(rValue.Type(), decodedToken) + if err != nil { + return nil, err + } mv := rValue.MapIndex(kv) if !mv.IsValid() { @@ -457,6 +491,80 @@ func isNil(input any) bool { } } +func isNilableKind(kind reflect.Kind) bool { + switch kind { + case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Pointer, reflect.Slice, reflect.UnsafePointer: + return true + default: + return false + } +} + +// fieldByName resolves a (possibly promoted) struct field by its go name. +// +// Unlike [reflect.Value.FieldByName], it reports an error instead of panicking when the field is +// promoted through a nil embedded pointer: the field exists on the type, but there is no value to +// reach it through. +func fieldByName(rValue reflect.Value, nm string) (reflect.Value, error) { + sf, ok := rValue.Type().FieldByName(nm) + if !ok { + return reflect.Value{}, errNoField(nm) + } + + fld, err := rValue.FieldByIndexErr(sf.Index) + if err != nil { + return reflect.Value{}, errUnreachableField(nm, err) + } + + return fld, nil +} + +// mapKeyValue converts a reference token into a value usable as a key of mapType. +// +// JSON object member names are strings, so the map key type must accept one: named string types are +// converted, and anything else (e.g. a map keyed by an integer) is an error rather than a panic in +// [reflect.Value.MapIndex] or [reflect.Value.SetMapIndex]. +func mapKeyValue(mapType reflect.Type, decodedToken string) (reflect.Value, error) { + kv := reflect.ValueOf(decodedToken) + keyType := mapType.Key() + + switch { + case kv.Type().AssignableTo(keyType): + return kv, nil + case keyType.Kind() == reflect.String && kv.Type().ConvertibleTo(keyType): + return kv.Convert(keyType), nil + default: + return reflect.Value{}, errMapKey(decodedToken, mapType) + } +} + +// resolveSetValue converts data into a [reflect.Value] assignable to target. +// +// A nil data value (typically a JSON null) resolves to the zero value of target whenever target can +// hold nil. Targets that cannot represent null (e.g. a string or an int field) yield an error: they +// have no faithful representation of the value being set, and silently substituting a zero value +// would lose that distinction. +// +// verb and what describe the destination for error reporting, e.g. "set" and `field Name with type +// int`. +func resolveSetValue(data any, target reflect.Type, verb, what string) (reflect.Value, error) { + value := reflect.ValueOf(data) + + if !value.IsValid() { + if !isNilableKind(target.Kind()) { + return reflect.Value{}, fmt.Errorf("can't %s null value to %s: %w", verb, what, ErrPointer) + } + + return reflect.Zero(target), nil + } + + if !value.Type().AssignableTo(target) { + return reflect.Value{}, fmt.Errorf("can't %s value with type %T to %s: %w", verb, data, what, ErrPointer) + } + + return value, nil +} + func typeFromValue(v reflect.Value) any { if v.CanAddr() && v.Kind() != reflect.Interface && v.Kind() != reflect.Map && v.Kind() != reflect.Slice && v.Kind() != reflect.Pointer { return v.Addr().Interface() @@ -474,8 +582,8 @@ func GetForToken(document any, decodedToken string, opts ...Option) (any, reflec // SetForToken sets a value for a json pointer token 1 level deep. // -// See [Pointer.Set] for the mutation contract, in particular the handling of -// the RFC 6901 "-" token on slices. +// See [Pointer.Set] for the mutation contract, in particular the handling of the RFC 6901 "-" token +// on slices. func SetForToken(document any, decodedToken string, value any, opts ...Option) (any, error) { o := optionsWithDefaults(opts) @@ -507,12 +615,18 @@ func getSingleImpl(node any, decodedToken string, nameProvider NameProvider) (an return nil, kind, fmt.Errorf("object has no field %q: %w", decodedToken, ErrPointer) } - fld := rValue.FieldByName(nm) + fld, err := fieldByName(rValue, nm) + if err != nil { + return nil, kind, err + } return fld.Interface(), kind, nil case reflect.Map: - kv := reflect.ValueOf(decodedToken) + kv, err := mapKeyValue(rValue.Type(), decodedToken) + if err != nil { + return nil, kind, err + } mv := rValue.MapIndex(kv) if mv.IsValid() { @@ -558,20 +672,21 @@ func setSingleImpl(node, data any, decodedToken string, nameProvider NameProvide case reflect.Struct: nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken) if !ok { - return node, fmt.Errorf("object has no field %q: %w", decodedToken, ErrPointer) + return node, errNoField(decodedToken) } - fld := rValue.FieldByName(nm) + fld, err := fieldByName(rValue, nm) + if err != nil { + return node, err + } if !fld.CanSet() { return node, fmt.Errorf("can't set struct field %s to %v: %w", nm, data, ErrPointer) } - value := reflect.ValueOf(data) - valueType := value.Type() assignedType := fld.Type() - - if !valueType.AssignableTo(assignedType) { - return node, fmt.Errorf("can't set value with type %T to field %s with type %v: %w", data, nm, assignedType, ErrPointer) + value, err := resolveSetValue(data, assignedType, "set", fmt.Sprintf("field %s with type %v", nm, assignedType)) + if err != nil { + return node, err } fld.Set(value) @@ -579,22 +694,35 @@ func setSingleImpl(node, data any, decodedToken string, nameProvider NameProvide return node, nil case reflect.Map: - kv := reflect.ValueOf(decodedToken) - rValue.SetMapIndex(kv, reflect.ValueOf(data)) + kv, err := mapKeyValue(rValue.Type(), decodedToken) + if err != nil { + return node, err + } + + // reflect.Value.SetMapIndex deletes the key when handed the zero Value, so a nil data value + // must be resolved to a typed zero first: setting a member to JSON null keeps the member. + elemType := rValue.Type().Elem() + value, err := resolveSetValue(data, elemType, "set", fmt.Sprintf("map value %q with type %v", decodedToken, elemType)) + if err != nil { + return node, err + } + + rValue.SetMapIndex(kv, value) return node, nil case reflect.Slice: if decodedToken == dashToken { - // RFC 6901 §4 / RFC 6902 append semantics: terminal "-" appends - // the value to the slice. We rebind in place when the slice is - // reachable via an addressable ancestor; otherwise we return the - // new slice header for the parent (or the public Set) to rebind. - value := reflect.ValueOf(data) + // RFC 6901 §4 / RFC 6902 append semantics: terminal "-" appends the value to the slice. + // + // We rebind in place when the slice is reachable via an addressable ancestor; otherwise we + // return the new slice header for the parent (or the public Set) to rebind. elemType := rValue.Type().Elem() - if !value.Type().AssignableTo(elemType) { - return node, fmt.Errorf("can't append value of type %T to slice of %v: %w", data, elemType, ErrPointer) + value, err := resolveSetValue(data, elemType, "append", fmt.Sprintf("slice of %v", elemType)) + if err != nil { + return node, err } + newSlice := reflect.Append(rValue, value) if rValue.CanSet() { rValue.Set(newSlice) @@ -618,12 +746,10 @@ func setSingleImpl(node, data any, decodedToken string, nameProvider NameProvide return node, fmt.Errorf("can't set slice index %s to %v: %w", decodedToken, data, ErrPointer) } - value := reflect.ValueOf(data) - valueType := value.Type() assignedType := elem.Type() - - if !valueType.AssignableTo(assignedType) { - return node, fmt.Errorf("can't set value with type %T to slice element %d with type %v: %w", data, tokenIndex, assignedType, ErrPointer) + value, err := resolveSetValue(data, assignedType, "set", fmt.Sprintf("slice element %d with type %v", tokenIndex, assignedType)) + if err != nil { + return node, err } elem.Set(value) @@ -650,8 +776,8 @@ func offsetSingleObject(dec *json.Decoder, decodedToken string) (int64, error) { return offset, nil } - // Consume the associated value. Scalars are fully read by a single - // Token() call; composite values must be drained. + // Consume the associated value. + // Scalars are fully read by a single Token() call; composite values must be drained. tk, err = dec.Token() if err != nil { return 0, err @@ -736,10 +862,7 @@ func drainSingle(dec *json.Decoder) error { return nil } -// JSON pointer encoding: -// ~0 => ~ -// ~1 => / -// ... and vice versa +// JSON pointer encoding: ~0 => ~ ~1 => / ... and vice versa. const ( encRefTok0 = `~0` diff --git a/vendor/github.com/go-openapi/jsonpointer/revive.toml b/vendor/github.com/go-openapi/jsonpointer/revive.toml new file mode 100644 index 0000000000..40fda4fc6d --- /dev/null +++ b/vendor/github.com/go-openapi/jsonpointer/revive.toml @@ -0,0 +1,18 @@ +# revive configuration used by codefactor.io. +# +# This is redundant with .golangci.yml, since revive is part of golangci-lint, +# but we need to configure it to remain consistent across reporting tools. + +ignore-generated-header = false +severity = "warning" +confidence = 0.8 +error-code = 0 +warning-code = 0 +enable-default-rules = true + +[rule.exported] +disabled = true +[rule.cyclomatic] +arguments = [20] +[rule.cognitive-complexity] +arguments = [20] diff --git a/vendor/goa.design/goa/v3/pkg/error.go b/vendor/goa.design/goa/v3/pkg/error.go index 127caebbeb..71fc60ab87 100644 --- a/vendor/goa.design/goa/v3/pkg/error.go +++ b/vendor/goa.design/goa/v3/pkg/error.go @@ -1,3 +1,6 @@ +// This file constructs runtime and validation errors for generated code and +// service implementations. Merges return a new whole error and keep original +// contribution values; callers can inspect the existing causes through Unwrap. package goa import ( @@ -11,7 +14,10 @@ import ( type ( // ServiceError is the default error type used by the goa package to - // encode and decode error responses. + // encode and decode error responses. Finish setting its public fields and + // constructing its causes before sharing it with readers. MergeErrors does + // not modify its inputs, but direct field writes and external cause behavior + // remain the caller's responsibility. ServiceError struct { // Name is a name for that class of errors. Name string @@ -27,9 +33,10 @@ type ( Temporary bool // Is the error a server-side fault? Fault bool - // History tracks all the individual errors that were built into this error, should - // this error have been merged. - history []*ServiceError + // history holds original contribution values in merge order. Each value + // has its own Field string and no nested history, so History can return + // editable copies without exposing these saved values. + history []ServiceError // err holds the original error if exists. err error } @@ -208,17 +215,24 @@ func NewErrorID() string { return base64.RawURLEncoding.EncodeToString(b) } -// MergeErrors updates an error by merging another into it. It first converts -// other into a ServiceError if not already one. The merge algorithm then: +// MergeErrors returns a fresh *ServiceError for two nonnil inputs without +// modifying either input. Callers must use the returned error. Each input is +// selected with errors.As; an input with no *ServiceError becomes an error-named +// fault containing its original text and cause. // -// * uses the name of err if a ServiceError, the name of other otherwise. +// The result keeps the selected left error's ID and Field pointer. It keeps the +// left Name unless that name is "error", in which case it uses the right Name. +// Its Message joins the selected messages with "; ". Timeout, Temporary and +// Fault are each true only when both selected errors have that trait. // -// * appends both error messages. +// The result joins the existing causes in left-to-right order and retains the +// original contributions for History, including duplicates. No cause is copied +// or modified. Stable finite input cause graphs remain finite because the new +// result cannot be reached from an older input. This does not repair existing +// cycles or control methods and mutations on caller-owned causes. // -// * computes Timeout and Temporary by "and"ing the fields of both errors. -// -// Merge returns the updated error. This makes it possible to return other when -// err is nil. +// If either input is nil, MergeErrors returns the other input exactly; if both +// are nil, it returns nil. func MergeErrors(err, other error) error { if err == nil { if other == nil { @@ -231,32 +245,45 @@ func MergeErrors(err, other error) error { } e := asError(err) o := asError(other) - if e.Name == "error" { - e.Name = o.Name - } - - // Combine error lineage. We only ever put original errors into the history slice, so we - // don't need to worry about gaining intermediate merges. - // - // Do this before we modify ourselves, as History() may include us! - e.history = append(e.History(), o.History()...) - e.err = errors.Join(e.err, o.err) - e.Message = e.Message + "; " + o.Message - e.Timeout = e.Timeout && o.Timeout - e.Temporary = e.Temporary && o.Temporary - e.Fault = e.Fault && o.Fault + // Save both inputs' contributions before combining whole fields. A new + // slice keeps subsequent merges from writing into either input's history. + left := errorContributions(e) + right := errorContributions(o) + merged := *e + merged.history = make([]ServiceError, 0, len(left)+len(right)) + merged.history = append(merged.history, left...) + merged.history = append(merged.history, right...) + if merged.Name == "error" { + merged.Name = o.Name + } + merged.Message = e.Message + "; " + o.Message + merged.Timeout = e.Timeout && o.Timeout + merged.Temporary = e.Temporary && o.Temporary + merged.Fault = e.Fault && o.Fault + merged.err = errors.Join(e.err, o.err) - return e + return &merged } -// History returns the history of error revisions, ignoring the result of any merges. +// History returns detached copies of the original, unmerged contributions in +// left-to-right merge order, preserving duplicates. Each entry retains its +// original Name, ID, Message, traits and cause, has a copied Field value, and +// contains no nested history. Changing the returned slice, entries or Field +// strings does not change saved contributions or the whole error. +// +// For an unmerged error, History returns a detached singleton with the error's +// current fields. A merged error retains contributions as they were included; +// later input or whole-field edits do not rewrite them. Causes remain the exact +// original objects, with behavior owned by their callers. func (e *ServiceError) History() []*ServiceError { - if len(e.history) > 0 { - return e.history + contributions := errorContributions(e) + history := make([]*ServiceError, len(contributions)) + for i, original := range contributions { + entry := copyErrorContribution(original) + history[i] = &entry } - - return []*ServiceError{e} + return history } // Error returns the error message. @@ -270,8 +297,31 @@ func (e *ServiceError) ErrorName() string { return e.Name } // GoaErrorName returns the error name. func (e *ServiceError) GoaErrorName() string { return e.ErrorName() } +// Unwrap returns the existing cause, or the ordered joined causes of a merge. func (e *ServiceError) Unwrap() error { return e.err } +// copyErrorContribution copies an error's fields and Field string, removes its +// nested history, and retains its exact cause so the copy describes one original +// contribution without exposing an owned Field value. +func copyErrorContribution(original ServiceError) ServiceError { + original.history = nil + if original.Field != nil { + field := *original.Field + original.Field = &field + } + return original +} + +// errorContributions supplies the saved original values for a merged error or +// captures an unmerged error's current fields. Its callers read this list and +// allocate their own slices before storing or returning entries. +func errorContributions(e *ServiceError) []ServiceError { + if len(e.history) > 0 { + return e.history + } + return []ServiceError{copyErrorContribution(*e)} +} + func withField(field string, err *ServiceError) *ServiceError { err.Field = &field return err diff --git a/vendor/goa.design/goa/v3/pkg/version.go b/vendor/goa.design/goa/v3/pkg/version.go index 538fc2a495..144a87d5c5 100644 --- a/vendor/goa.design/goa/v3/pkg/version.go +++ b/vendor/goa.design/goa/v3/pkg/version.go @@ -10,7 +10,7 @@ const ( // Major version number Major = 3 // Minor version number - Minor = 32 + Minor = 34 // Build number Build = 0 // Suffix - set to empty string in release tag commits. diff --git a/vendor/modules.txt b/vendor/modules.txt index 4f2fa98171..4fc2e62b91 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -163,9 +163,10 @@ github.com/go-logr/logr/funcr # github.com/go-logr/stdr v1.2.2 ## explicit; go 1.16 github.com/go-logr/stdr -# github.com/go-openapi/jsonpointer v0.23.1 -## explicit; go 1.25.0 +# github.com/go-openapi/jsonpointer v1.0.2 +## explicit; go 1.26.0 github.com/go-openapi/jsonpointer +github.com/go-openapi/jsonpointer/jsonname # github.com/go-openapi/jsonreference v0.21.5 ## explicit; go 1.24.0 github.com/go-openapi/jsonreference @@ -211,8 +212,6 @@ github.com/go-openapi/swag/typeutils github.com/go-openapi/swag/yamlutils # github.com/go-openapi/testify/enable/yaml/v2 v2.5.0 ## explicit; go 1.25.0 -# github.com/go-openapi/testify/v2 v2.5.0 -## explicit; go 1.25.0 # github.com/go-viper/mapstructure/v2 v2.5.0 ## explicit; go 1.18 github.com/go-viper/mapstructure/v2 @@ -858,7 +857,7 @@ go.yaml.in/yaml/v2 # go.yaml.in/yaml/v3 v3.0.5 ## explicit; go 1.16 go.yaml.in/yaml/v3 -# goa.design/goa/v3 v3.32.0 +# goa.design/goa/v3 v3.34.0 ## explicit; go 1.26.0 goa.design/goa/v3/http goa.design/goa/v3/pkg @@ -947,7 +946,7 @@ google.golang.org/genproto/googleapis/api google.golang.org/genproto/googleapis/api/annotations google.golang.org/genproto/googleapis/api/expr/v1alpha1 google.golang.org/genproto/googleapis/api/httpbody -# google.golang.org/genproto/googleapis/rpc v0.0.0-20260918162117-cecb64721679 +# google.golang.org/genproto/googleapis/rpc v0.0.0-20261005182115-fad411399dd8 ## explicit; go 1.26.0 google.golang.org/genproto/googleapis/rpc/errdetails google.golang.org/genproto/googleapis/rpc/status