From 89e887d4b0ad69d93a2204c31ca267a2b694b0e6 Mon Sep 17 00:00:00 2001 From: Lukas Zapletal Date: Sun, 29 Sep 2024 14:07:24 +0200 Subject: [PATCH] build: bump oapi-codegen to 2.4.1 --- .gitignore | 1 + go.mod | 4 +- go.sum | 9 +- internal/clients/composer/openapi.v2.gen.go | 2 +- internal/clients/composer/package.go | 2 +- .../clients/content_sources/openapi.v1.gen.go | 2 +- internal/clients/content_sources/package.go | 2 +- .../clients/provisioning/openapi.v1.gen.go | 2 +- internal/clients/provisioning/package.go | 2 +- .../clients/recommendations/openapi.v3.gen.go | 2 +- internal/clients/recommendations/package.go | 2 +- internal/v1/api.go | 2 +- internal/v1/server.go | 2 +- tools/prepare-source.sh | 14 +- .../getkin/kin-openapi/openapi3/callback.go | 2 +- .../getkin/kin-openapi/openapi3/components.go | 33 +- .../getkin/kin-openapi/openapi3/contact.go | 15 +- .../kin-openapi/openapi3/discriminator.go | 15 +- .../getkin/kin-openapi/openapi3/encoding.go | 15 +- .../getkin/kin-openapi/openapi3/errors.go | 2 +- .../getkin/kin-openapi/openapi3/example.go | 25 +- .../openapi3/example_validation.go | 2 +- .../getkin/kin-openapi/openapi3/extension.go | 2 +- .../kin-openapi/openapi3/external_docs.go | 15 +- .../getkin/kin-openapi/openapi3/header.go | 4 +- .../getkin/kin-openapi/openapi3/helpers.go | 230 +++++- .../getkin/kin-openapi/openapi3/info.go | 18 +- .../kin-openapi/openapi3/internalize_refs.go | 217 ++++-- .../getkin/kin-openapi/openapi3/license.go | 15 +- .../getkin/kin-openapi/openapi3/link.go | 27 +- .../getkin/kin-openapi/openapi3/loader.go | 433 ++++++----- .../kin-openapi/openapi3/loader_uri_reader.go | 2 +- .../getkin/kin-openapi/openapi3/maplike.go | 78 +- .../getkin/kin-openapi/openapi3/marsh.go | 18 +- .../getkin/kin-openapi/openapi3/media_type.go | 21 +- .../getkin/kin-openapi/openapi3/openapi3.go | 22 +- .../getkin/kin-openapi/openapi3/operation.go | 17 +- .../getkin/kin-openapi/openapi3/parameter.go | 47 +- .../getkin/kin-openapi/openapi3/path_item.go | 17 +- .../getkin/kin-openapi/openapi3/paths.go | 17 +- .../getkin/kin-openapi/openapi3/ref.go | 2 + .../getkin/kin-openapi/openapi3/refs.go | 705 +++++++++++++++--- .../getkin/kin-openapi/openapi3/refs.tmpl | 151 ++++ .../kin-openapi/openapi3/refs_test.tmpl | 54 ++ .../kin-openapi/openapi3/request_body.go | 15 +- .../getkin/kin-openapi/openapi3/response.go | 32 +- .../getkin/kin-openapi/openapi3/schema.go | 218 +++--- .../kin-openapi/openapi3/schema_formats.go | 187 +++-- .../kin-openapi/openapi3/security_scheme.go | 45 +- .../getkin/kin-openapi/openapi3/server.go | 30 +- .../getkin/kin-openapi/openapi3/tag.go | 15 +- .../openapi3/testdata/circularRef/base.yml | 2 + .../openapi3/testdata/circularRef/baz.yml | 9 + .../recursiveRef/openapi.yml.internalized.yml | 44 +- .../openapi3/validation_options.go | 21 + .../getkin/kin-openapi/openapi3/xml.go | 15 +- .../kin-openapi/openapi3filter/internal.go | 2 +- .../kin-openapi/openapi3filter/middleware.go | 28 +- .../openapi3filter/req_resp_decoder.go | 187 ++--- .../openapi3filter/req_resp_encoder.go | 4 +- .../openapi3filter/validate_request.go | 27 +- .../openapi3filter/validate_request_input.go | 2 +- .../openapi3filter/validate_response.go | 2 +- .../routers/legacy/pathpattern/node.go | 6 +- vendor/github.com/invopop/yaml/.golangci.toml | 7 +- vendor/github.com/invopop/yaml/fields.go | 5 +- vendor/github.com/invopop/yaml/yaml.go | 14 +- vendor/modules.txt | 4 +- 68 files changed, 2366 insertions(+), 824 deletions(-) create mode 100644 vendor/github.com/getkin/kin-openapi/openapi3/refs.tmpl create mode 100644 vendor/github.com/getkin/kin-openapi/openapi3/refs_test.tmpl create mode 100644 vendor/github.com/getkin/kin-openapi/openapi3/testdata/circularRef/baz.yml diff --git a/.gitignore b/.gitignore index 8e7b6987a..200ef1136 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ dnf-json local.env __debug* coverage* +/bin diff --git a/go.mod b/go.mod index f4ab2e575..ab2e5306f 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/BurntSushi/toml v1.4.0 github.com/Unleash/unleash-client-go/v4 v4.1.3 github.com/aws/aws-sdk-go v1.55.5 - github.com/getkin/kin-openapi v0.124.0 + github.com/getkin/kin-openapi v0.127.0 github.com/getsentry/sentry-go v0.29.0 github.com/google/uuid v1.6.0 github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438 @@ -40,7 +40,7 @@ require ( github.com/golang-jwt/jwt v3.2.2+incompatible // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-retryablehttp v0.7.7 // indirect - github.com/invopop/yaml v0.2.0 // indirect + github.com/invopop/yaml v0.3.1 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect github.com/jackc/puddle/v2 v2.2.2 // indirect diff --git a/go.sum b/go.sum index 4733fa402..27873799c 100644 --- a/go.sum +++ b/go.sum @@ -25,8 +25,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/getkin/kin-openapi v0.124.0 h1:VSFNMB9C9rTKBnQ/fpyDU8ytMTr4dWI9QovSKj9kz/M= -github.com/getkin/kin-openapi v0.124.0/go.mod h1:wb1aSZA/iWmorQP9KTAS/phLj/t17B5jT7+fS8ed9NM= +github.com/getkin/kin-openapi v0.127.0 h1:Mghqi3Dhryf3F8vR370nN67pAERW+3a95vomb3MAREY= +github.com/getkin/kin-openapi v0.127.0/go.mod h1:OZrfXzUfGrNbsKj+xmFBx6E5c6yH3At/tAKSc2UszXM= github.com/getsentry/sentry-go v0.29.0 h1:YtWluuCFg9OfcqnaujpY918N/AhCCwarIDWOYSBAjCA= github.com/getsentry/sentry-go v0.29.0/go.mod h1:jhPesDAL0Q0W2+2YEuVOvdWmVtdsr1+jtBrlDEVWwLY= github.com/go-chi/chi v4.1.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= @@ -71,8 +71,8 @@ github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVH github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU= github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/invopop/yaml v0.2.0 h1:7zky/qH+O0DwAyoobXUqvVBwgBFRxKoQ/3FjcVpjTMY= -github.com/invopop/yaml v0.2.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= +github.com/invopop/yaml v0.3.1 h1:f0+ZpmhfBSS4MhG+4HYseMdJhoeeopbSKbq5Rpeelso= +github.com/invopop/yaml v0.3.1/go.mod h1:PMOp3nn4/12yEZUFfmOuNHJsZToEEOwoWsT+D81KkeA= github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438 h1:Dj0L5fhJ9F82ZJyVOmBx6msDp/kfd1t9GRfny/mfJA0= github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438/go.mod h1:a/s9Lp5W7n/DD0VrVoyJ00FbP2ytTPDVOivvn2bMlds= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= @@ -292,6 +292,5 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/clients/composer/openapi.v2.gen.go b/internal/clients/composer/openapi.v2.gen.go index 24a26e194..8f8ecb82e 100644 --- a/internal/clients/composer/openapi.v2.gen.go +++ b/internal/clients/composer/openapi.v2.gen.go @@ -1,6 +1,6 @@ // Package composer provides primitives to interact with the openapi HTTP API. // -// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.3.0 DO NOT EDIT. +// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.4.1 DO NOT EDIT. package composer import ( diff --git a/internal/clients/composer/package.go b/internal/clients/composer/package.go index c311fbfb6..c76b64195 100644 --- a/internal/clients/composer/package.go +++ b/internal/clients/composer/package.go @@ -1,4 +1,4 @@ -//go:generate go run -mod=mod github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@v2.3.0 --config client.cfg.yaml openapi.v2.yml +//go:generate go run -mod=mod github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen --config client.cfg.yaml openapi.v2.yml // Generated OpenAPI clients for the Composer service. package composer diff --git a/internal/clients/content_sources/openapi.v1.gen.go b/internal/clients/content_sources/openapi.v1.gen.go index 23a31fc3c..638002e25 100644 --- a/internal/clients/content_sources/openapi.v1.gen.go +++ b/internal/clients/content_sources/openapi.v1.gen.go @@ -1,6 +1,6 @@ // Package content_sources provides primitives to interact with the openapi HTTP API. // -// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.3.0 DO NOT EDIT. +// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.4.1 DO NOT EDIT. package content_sources import ( diff --git a/internal/clients/content_sources/package.go b/internal/clients/content_sources/package.go index b0d272a23..99791150c 100644 --- a/internal/clients/content_sources/package.go +++ b/internal/clients/content_sources/package.go @@ -1,4 +1,4 @@ -//go:generate go run -mod=mod github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@v2.3.0 --config client.cfg.yaml content-sources.v1.json +//go:generate go run -mod=mod github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen --config client.cfg.yaml content-sources.v1.json // Generated OpenAPI clients for the Content Sources service. package content_sources diff --git a/internal/clients/provisioning/openapi.v1.gen.go b/internal/clients/provisioning/openapi.v1.gen.go index 77dd853f8..1655a6be7 100644 --- a/internal/clients/provisioning/openapi.v1.gen.go +++ b/internal/clients/provisioning/openapi.v1.gen.go @@ -1,6 +1,6 @@ // Package provisioning provides primitives to interact with the openapi HTTP API. // -// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.3.0 DO NOT EDIT. +// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.4.1 DO NOT EDIT. package provisioning import ( diff --git a/internal/clients/provisioning/package.go b/internal/clients/provisioning/package.go index bb2c11d4d..8e010ec52 100644 --- a/internal/clients/provisioning/package.go +++ b/internal/clients/provisioning/package.go @@ -1,4 +1,4 @@ -//go:generate go run -mod=mod github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@v2.3.0 --config client.cfg.yml provisioning.v1.yml +//go:generate go run -mod=mod github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen --config client.cfg.yml provisioning.v1.yml // Generated OpenAPI clients for the Provisioning service. package provisioning diff --git a/internal/clients/recommendations/openapi.v3.gen.go b/internal/clients/recommendations/openapi.v3.gen.go index 1c6fa2d5a..c368d184d 100644 --- a/internal/clients/recommendations/openapi.v3.gen.go +++ b/internal/clients/recommendations/openapi.v3.gen.go @@ -1,6 +1,6 @@ // Package recommendations provides primitives to interact with the openapi HTTP API. // -// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.3.0 DO NOT EDIT. +// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.4.1 DO NOT EDIT. package recommendations // GetModelVersion200Response defines model for GetModelVersion200Response. diff --git a/internal/clients/recommendations/package.go b/internal/clients/recommendations/package.go index 710eed487..4a7ca5de4 100644 --- a/internal/clients/recommendations/package.go +++ b/internal/clients/recommendations/package.go @@ -1,2 +1,2 @@ -//go:generate go run -mod=mod github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@v2.3.0 --config client.cfg.yml recommendations.v3.json +//go:generate go run -mod=mod github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen --config client.cfg.yml recommendations.v3.json package recommendations diff --git a/internal/v1/api.go b/internal/v1/api.go index caa40a914..c7bb4f43d 100644 --- a/internal/v1/api.go +++ b/internal/v1/api.go @@ -1,6 +1,6 @@ // Package v1 provides primitives to interact with the openapi HTTP API. // -// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.3.0 DO NOT EDIT. +// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.4.1 DO NOT EDIT. package v1 import ( diff --git a/internal/v1/server.go b/internal/v1/server.go index 50e312e2e..6cbc52739 100644 --- a/internal/v1/server.go +++ b/internal/v1/server.go @@ -1,4 +1,4 @@ -//go:generate go run -mod=mod github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@v2.3.0 --config server.cfg.yaml api.yaml +//go:generate go run -mod=mod github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen --config server.cfg.yaml api.yaml package v1 import ( diff --git a/tools/prepare-source.sh b/tools/prepare-source.sh index 959ecdf95..7689d179a 100755 --- a/tools/prepare-source.sh +++ b/tools/prepare-source.sh @@ -2,20 +2,20 @@ set -eux GO_VERSION=1.21.9 +OAPI_VERSION=2.4.1 + GO_BINARY=$(go env GOPATH)/bin/go$GO_VERSION -OAPI_VERSION=2.3.0 +TOOLS_PATH="$(realpath "$(dirname "$0")/../bin")" -# this is the official way to get a different version of golang -# see https://go.dev/doc/manage-install +# Install Go SDK go install golang.org/dl/go$GO_VERSION@latest $GO_BINARY download # Ensure dev tools are installed -which goimports || $GO_BINARY install golang.org/x/tools/cmd/goimports@latest +which goimports >/dev/null || GOBIN=$TOOLS_PATH $GO_BINARY install golang.org/x/tools/cmd/goimports@latest +("$TOOLS_PATH/oapi-codegen" -version | grep "$OAPI_VERSION" >/dev/null) || GOBIN=$TOOLS_PATH $GO_BINARY install github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@v$OAPI_VERSION -# Ensure that all code has been regenerated from its sources with the pinned oapi version -git grep -l "go:generate.*github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen" | grep -v prepare-source.sh | xargs sed -i "s|github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen[v@.0-9]*|github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@v$OAPI_VERSION|g" -$GO_BINARY generate -mod=mod ./... +GOBIN=$TOOLS_PATH $GO_BINARY generate -x -mod=mod ./... # ... the code is formatted correctly, ... goimports -w internal cmd diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/callback.go b/vendor/github.com/getkin/kin-openapi/openapi3/callback.go index 13532b15c..34a6bea35 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/callback.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/callback.go @@ -8,7 +8,7 @@ import ( // Callback is specified by OpenAPI/Swagger standard version 3. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#callback-object type Callback struct { - Extensions map[string]interface{} `json:"-" yaml:"-"` + Extensions map[string]any `json:"-" yaml:"-"` m map[string]*PathItem } diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/components.go b/vendor/github.com/getkin/kin-openapi/openapi3/components.go index 656ea1936..98c4b96c1 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/components.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/components.go @@ -24,7 +24,7 @@ type ( // Components is specified by OpenAPI/Swagger standard version 3. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#components-object type Components struct { - Extensions map[string]interface{} `json:"-" yaml:"-"` + Extensions map[string]any `json:"-" yaml:"-"` Schemas Schemas `json:"schemas,omitempty" yaml:"schemas,omitempty"` Parameters ParametersMap `json:"parameters,omitempty" yaml:"parameters,omitempty"` @@ -43,7 +43,16 @@ func NewComponents() Components { // MarshalJSON returns the JSON encoding of Components. func (components Components) MarshalJSON() ([]byte, error) { - m := make(map[string]interface{}, 9+len(components.Extensions)) + x, err := components.MarshalYAML() + if err != nil { + return nil, err + } + return json.Marshal(x) +} + +// MarshalYAML returns the YAML encoding of Components. +func (components Components) MarshalYAML() (any, error) { + m := make(map[string]any, 9+len(components.Extensions)) for k, v := range components.Extensions { m[k] = v } @@ -74,7 +83,7 @@ func (components Components) MarshalJSON() ([]byte, error) { if x := components.Callbacks; len(x) != 0 { m["callbacks"] = x } - return json.Marshal(m) + return m, nil } // UnmarshalJSON sets Components to a copy of data. @@ -246,7 +255,7 @@ func (components *Components) Validate(ctx context.Context, opts ...ValidationOp var _ jsonpointer.JSONPointable = (*Schemas)(nil) // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable -func (m Schemas) JSONLookup(token string) (interface{}, error) { +func (m Schemas) JSONLookup(token string) (any, error) { if v, ok := m[token]; !ok || v == nil { return nil, fmt.Errorf("no schema %q", token) } else if ref := v.Ref; ref != "" { @@ -259,7 +268,7 @@ func (m Schemas) JSONLookup(token string) (interface{}, error) { var _ jsonpointer.JSONPointable = (*ParametersMap)(nil) // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable -func (m ParametersMap) JSONLookup(token string) (interface{}, error) { +func (m ParametersMap) JSONLookup(token string) (any, error) { if v, ok := m[token]; !ok || v == nil { return nil, fmt.Errorf("no parameter %q", token) } else if ref := v.Ref; ref != "" { @@ -272,7 +281,7 @@ func (m ParametersMap) JSONLookup(token string) (interface{}, error) { var _ jsonpointer.JSONPointable = (*Headers)(nil) // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable -func (m Headers) JSONLookup(token string) (interface{}, error) { +func (m Headers) JSONLookup(token string) (any, error) { if v, ok := m[token]; !ok || v == nil { return nil, fmt.Errorf("no header %q", token) } else if ref := v.Ref; ref != "" { @@ -285,7 +294,7 @@ func (m Headers) JSONLookup(token string) (interface{}, error) { var _ jsonpointer.JSONPointable = (*RequestBodyRef)(nil) // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable -func (m RequestBodies) JSONLookup(token string) (interface{}, error) { +func (m RequestBodies) JSONLookup(token string) (any, error) { if v, ok := m[token]; !ok || v == nil { return nil, fmt.Errorf("no request body %q", token) } else if ref := v.Ref; ref != "" { @@ -298,7 +307,7 @@ func (m RequestBodies) JSONLookup(token string) (interface{}, error) { var _ jsonpointer.JSONPointable = (*ResponseRef)(nil) // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable -func (m ResponseBodies) JSONLookup(token string) (interface{}, error) { +func (m ResponseBodies) JSONLookup(token string) (any, error) { if v, ok := m[token]; !ok || v == nil { return nil, fmt.Errorf("no response body %q", token) } else if ref := v.Ref; ref != "" { @@ -311,7 +320,7 @@ func (m ResponseBodies) JSONLookup(token string) (interface{}, error) { var _ jsonpointer.JSONPointable = (*SecuritySchemes)(nil) // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable -func (m SecuritySchemes) JSONLookup(token string) (interface{}, error) { +func (m SecuritySchemes) JSONLookup(token string) (any, error) { if v, ok := m[token]; !ok || v == nil { return nil, fmt.Errorf("no security scheme body %q", token) } else if ref := v.Ref; ref != "" { @@ -324,7 +333,7 @@ func (m SecuritySchemes) JSONLookup(token string) (interface{}, error) { var _ jsonpointer.JSONPointable = (*Examples)(nil) // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable -func (m Examples) JSONLookup(token string) (interface{}, error) { +func (m Examples) JSONLookup(token string) (any, error) { if v, ok := m[token]; !ok || v == nil { return nil, fmt.Errorf("no example body %q", token) } else if ref := v.Ref; ref != "" { @@ -337,7 +346,7 @@ func (m Examples) JSONLookup(token string) (interface{}, error) { var _ jsonpointer.JSONPointable = (*Links)(nil) // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable -func (m Links) JSONLookup(token string) (interface{}, error) { +func (m Links) JSONLookup(token string) (any, error) { if v, ok := m[token]; !ok || v == nil { return nil, fmt.Errorf("no link body %q", token) } else if ref := v.Ref; ref != "" { @@ -350,7 +359,7 @@ func (m Links) JSONLookup(token string) (interface{}, error) { var _ jsonpointer.JSONPointable = (*Callbacks)(nil) // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable -func (m Callbacks) JSONLookup(token string) (interface{}, error) { +func (m Callbacks) JSONLookup(token string) (any, error) { if v, ok := m[token]; !ok || v == nil { return nil, fmt.Errorf("no callback body %q", token) } else if ref := v.Ref; ref != "" { diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/contact.go b/vendor/github.com/getkin/kin-openapi/openapi3/contact.go index e60d2818a..6c76a6fb6 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/contact.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/contact.go @@ -8,7 +8,7 @@ import ( // Contact is specified by OpenAPI/Swagger standard version 3. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#contact-object type Contact struct { - Extensions map[string]interface{} `json:"-" yaml:"-"` + Extensions map[string]any `json:"-" yaml:"-"` Name string `json:"name,omitempty" yaml:"name,omitempty"` URL string `json:"url,omitempty" yaml:"url,omitempty"` @@ -17,7 +17,16 @@ type Contact struct { // MarshalJSON returns the JSON encoding of Contact. func (contact Contact) MarshalJSON() ([]byte, error) { - m := make(map[string]interface{}, 3+len(contact.Extensions)) + x, err := contact.MarshalYAML() + if err != nil { + return nil, err + } + return json.Marshal(x) +} + +// MarshalYAML returns the YAML encoding of Contact. +func (contact Contact) MarshalYAML() (any, error) { + m := make(map[string]any, 3+len(contact.Extensions)) for k, v := range contact.Extensions { m[k] = v } @@ -30,7 +39,7 @@ func (contact Contact) MarshalJSON() ([]byte, error) { if x := contact.Email; x != "" { m["email"] = x } - return json.Marshal(m) + return m, nil } // UnmarshalJSON sets Contact to a copy of data. diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/discriminator.go b/vendor/github.com/getkin/kin-openapi/openapi3/discriminator.go index abb480741..e8193bd90 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/discriminator.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/discriminator.go @@ -8,7 +8,7 @@ import ( // Discriminator is specified by OpenAPI/Swagger standard version 3. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#discriminator-object type Discriminator struct { - Extensions map[string]interface{} `json:"-" yaml:"-"` + Extensions map[string]any `json:"-" yaml:"-"` PropertyName string `json:"propertyName" yaml:"propertyName"` // required Mapping map[string]string `json:"mapping,omitempty" yaml:"mapping,omitempty"` @@ -16,7 +16,16 @@ type Discriminator struct { // MarshalJSON returns the JSON encoding of Discriminator. func (discriminator Discriminator) MarshalJSON() ([]byte, error) { - m := make(map[string]interface{}, 2+len(discriminator.Extensions)) + x, err := discriminator.MarshalYAML() + if err != nil { + return nil, err + } + return json.Marshal(x) +} + +// MarshalYAML returns the YAML encoding of Discriminator. +func (discriminator Discriminator) MarshalYAML() (any, error) { + m := make(map[string]any, 2+len(discriminator.Extensions)) for k, v := range discriminator.Extensions { m[k] = v } @@ -24,7 +33,7 @@ func (discriminator Discriminator) MarshalJSON() ([]byte, error) { if x := discriminator.Mapping; len(x) != 0 { m["mapping"] = x } - return json.Marshal(m) + return m, nil } // UnmarshalJSON sets Discriminator to a copy of data. diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/encoding.go b/vendor/github.com/getkin/kin-openapi/openapi3/encoding.go index 8e810279c..1bcdaea5e 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/encoding.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/encoding.go @@ -10,7 +10,7 @@ import ( // Encoding is specified by OpenAPI/Swagger 3.0 standard. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#encoding-object type Encoding struct { - Extensions map[string]interface{} `json:"-" yaml:"-"` + Extensions map[string]any `json:"-" yaml:"-"` ContentType string `json:"contentType,omitempty" yaml:"contentType,omitempty"` Headers Headers `json:"headers,omitempty" yaml:"headers,omitempty"` @@ -41,7 +41,16 @@ func (encoding *Encoding) WithHeaderRef(name string, ref *HeaderRef) *Encoding { // MarshalJSON returns the JSON encoding of Encoding. func (encoding Encoding) MarshalJSON() ([]byte, error) { - m := make(map[string]interface{}, 5+len(encoding.Extensions)) + x, err := encoding.MarshalYAML() + if err != nil { + return nil, err + } + return json.Marshal(x) +} + +// MarshalYAML returns the YAML encoding of Encoding. +func (encoding Encoding) MarshalYAML() (any, error) { + m := make(map[string]any, 5+len(encoding.Extensions)) for k, v := range encoding.Extensions { m[k] = v } @@ -60,7 +69,7 @@ func (encoding Encoding) MarshalJSON() ([]byte, error) { if x := encoding.AllowReserved; x { m["allowReserved"] = x } - return json.Marshal(m) + return m, nil } // UnmarshalJSON sets Encoding to a copy of data. diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/errors.go b/vendor/github.com/getkin/kin-openapi/openapi3/errors.go index 74baab9a5..010dc889a 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/errors.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/errors.go @@ -39,7 +39,7 @@ func (me MultiError) Is(target error) bool { } // As allows you to use `errors.As()` to set target to the first error within the multi error that matches the target type -func (me MultiError) As(target interface{}) bool { +func (me MultiError) As(target any) bool { for _, e := range me { if errors.As(e, target) { return true diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/example.go b/vendor/github.com/getkin/kin-openapi/openapi3/example.go index 44e71d827..f9a7a6b07 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/example.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/example.go @@ -9,21 +9,30 @@ import ( // Example is specified by OpenAPI/Swagger 3.0 standard. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#example-object type Example struct { - Extensions map[string]interface{} `json:"-" yaml:"-"` + Extensions map[string]any `json:"-" yaml:"-"` - Summary string `json:"summary,omitempty" yaml:"summary,omitempty"` - Description string `json:"description,omitempty" yaml:"description,omitempty"` - Value interface{} `json:"value,omitempty" yaml:"value,omitempty"` - ExternalValue string `json:"externalValue,omitempty" yaml:"externalValue,omitempty"` + Summary string `json:"summary,omitempty" yaml:"summary,omitempty"` + Description string `json:"description,omitempty" yaml:"description,omitempty"` + Value any `json:"value,omitempty" yaml:"value,omitempty"` + ExternalValue string `json:"externalValue,omitempty" yaml:"externalValue,omitempty"` } -func NewExample(value interface{}) *Example { +func NewExample(value any) *Example { return &Example{Value: value} } // MarshalJSON returns the JSON encoding of Example. func (example Example) MarshalJSON() ([]byte, error) { - m := make(map[string]interface{}, 4+len(example.Extensions)) + x, err := example.MarshalYAML() + if err != nil { + return nil, err + } + return json.Marshal(x) +} + +// MarshalYAML returns the YAML encoding of Example. +func (example Example) MarshalYAML() (any, error) { + m := make(map[string]any, 4+len(example.Extensions)) for k, v := range example.Extensions { m[k] = v } @@ -39,7 +48,7 @@ func (example Example) MarshalJSON() ([]byte, error) { if x := example.ExternalValue; x != "" { m["externalValue"] = x } - return json.Marshal(m) + return m, nil } // UnmarshalJSON sets Example to a copy of data. diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/example_validation.go b/vendor/github.com/getkin/kin-openapi/openapi3/example_validation.go index fb7a1da16..0d105c92d 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/example_validation.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/example_validation.go @@ -2,7 +2,7 @@ package openapi3 import "context" -func validateExampleValue(ctx context.Context, input interface{}, schema *Schema) error { +func validateExampleValue(ctx context.Context, input any, schema *Schema) error { opts := make([]SchemaValidationOption, 0, 2) if vo := getValidationOptions(ctx); vo.examplesValidationAsReq { diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/extension.go b/vendor/github.com/getkin/kin-openapi/openapi3/extension.go index 37f6b01e5..ca86078f2 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/extension.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/extension.go @@ -7,7 +7,7 @@ import ( "strings" ) -func validateExtensions(ctx context.Context, extensions map[string]interface{}) error { // FIXME: newtype + Validate(...) +func validateExtensions(ctx context.Context, extensions map[string]any) error { // FIXME: newtype + Validate(...) allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed var unknowns []string diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/external_docs.go b/vendor/github.com/getkin/kin-openapi/openapi3/external_docs.go index 7190be4b0..bd99511a5 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/external_docs.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/external_docs.go @@ -11,7 +11,7 @@ import ( // ExternalDocs is specified by OpenAPI/Swagger standard version 3. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#external-documentation-object type ExternalDocs struct { - Extensions map[string]interface{} `json:"-" yaml:"-"` + Extensions map[string]any `json:"-" yaml:"-"` Description string `json:"description,omitempty" yaml:"description,omitempty"` URL string `json:"url,omitempty" yaml:"url,omitempty"` @@ -19,7 +19,16 @@ type ExternalDocs struct { // MarshalJSON returns the JSON encoding of ExternalDocs. func (e ExternalDocs) MarshalJSON() ([]byte, error) { - m := make(map[string]interface{}, 2+len(e.Extensions)) + x, err := e.MarshalYAML() + if err != nil { + return nil, err + } + return json.Marshal(x) +} + +// MarshalYAML returns the YAML encoding of ExternalDocs. +func (e ExternalDocs) MarshalYAML() (any, error) { + m := make(map[string]any, 2+len(e.Extensions)) for k, v := range e.Extensions { m[k] = v } @@ -29,7 +38,7 @@ func (e ExternalDocs) MarshalJSON() ([]byte, error) { if x := e.URL; x != "" { m["url"] = x } - return json.Marshal(m) + return m, nil } // UnmarshalJSON sets ExternalDocs to a copy of data. diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/header.go b/vendor/github.com/getkin/kin-openapi/openapi3/header.go index e5eee6ccb..dc542874d 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/header.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/header.go @@ -17,7 +17,7 @@ type Header struct { var _ jsonpointer.JSONPointable = (*Header)(nil) // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable -func (header Header) JSONLookup(token string) (interface{}, error) { +func (header Header) JSONLookup(token string) (any, error) { return header.Parameter.JSONLookup(token) } @@ -32,7 +32,7 @@ func (header *Header) UnmarshalJSON(data []byte) error { } // MarshalYAML returns the JSON encoding of Header. -func (header Header) MarshalYAML() (interface{}, error) { +func (header Header) MarshalYAML() (any, error) { return header.Parameter, nil } diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/helpers.go b/vendor/github.com/getkin/kin-openapi/openapi3/helpers.go index d160eb1e8..d50b3d847 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/helpers.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/helpers.go @@ -2,22 +2,33 @@ package openapi3 import ( "fmt" + "net/url" + "path" + "reflect" "regexp" + "sort" + "strings" + + "github.com/go-openapi/jsonpointer" ) -const identifierPattern = `^[a-zA-Z0-9._-]+$` +const identifierChars = `a-zA-Z0-9._-` -// IdentifierRegExp verifies whether Component object key matches 'identifierPattern' pattern, according to OpenAPI v3.x. +// IdentifierRegExp verifies whether Component object key matches contains just 'identifierChars', according to OpenAPI v3.x. +// InvalidIdentifierCharRegExp matches all characters not contained in 'identifierChars'. // However, to be able supporting legacy OpenAPI v2.x, there is a need to customize above pattern in order not to fail // converted v2-v3 validation -var IdentifierRegExp = regexp.MustCompile(identifierPattern) +var ( + IdentifierRegExp = regexp.MustCompile(`^[` + identifierChars + `]+$`) + InvalidIdentifierCharRegExp = regexp.MustCompile(`[^` + identifierChars + `]`) +) -// ValidateIdentifier returns an error if the given component name does not match IdentifierRegExp. +// ValidateIdentifier returns an error if the given component name does not match [IdentifierRegExp]. func ValidateIdentifier(value string) error { if IdentifierRegExp.MatchString(value) { return nil } - return fmt.Errorf("identifier %q is not supported by OpenAPIv3 standard (regexp: %q)", value, identifierPattern) + return fmt.Errorf("identifier %q is not supported by OpenAPIv3 standard (charset: [%q])", value, identifierChars) } // Float64Ptr is a helper for defining OpenAPI schemas. @@ -39,3 +50,212 @@ func Int64Ptr(value int64) *int64 { func Uint64Ptr(value uint64) *uint64 { return &value } + +// componentNames returns the map keys in a sorted slice. +func componentNames[E any](s map[string]E) []string { + out := make([]string, 0, len(s)) + for i := range s { + out = append(out, i) + } + sort.Strings(out) + return out +} + +// copyURI makes a copy of the pointer. +func copyURI(u *url.URL) *url.URL { + if u == nil { + return nil + } + + c := *u // shallow-copy + return &c +} + +type ComponentRef interface { + RefString() string + RefPath() *url.URL + CollectionName() string +} + +// refersToSameDocument returns if the $ref refers to the same document. +// +// Documents in different directories will have distinct $ref values that resolve to +// the same document. +// For example, consider the 3 files: +// +// /records.yaml +// /root.yaml $ref: records.yaml +// /schema/other.yaml $ref: ../records.yaml +// +// The records.yaml reference in the 2 latter refers to the same document. +func refersToSameDocument(o1 ComponentRef, o2 ComponentRef) bool { + if o1 == nil || o2 == nil { + return false + } + + r1 := o1.RefPath() + r2 := o2.RefPath() + + if r1 == nil || r2 == nil { + return false + } + + // refURL is relative to the working directory & base spec file. + return referenceURIMatch(r1, r2) +} + +// referencesRootDocument returns if the $ref points to the root document of the OpenAPI spec. +// +// If the document has no location, perhaps loaded from data in memory, it always returns false. +func referencesRootDocument(doc *T, ref ComponentRef) bool { + if doc.url == nil || ref == nil || ref.RefPath() == nil { + return false + } + + refURL := *ref.RefPath() + refURL.Fragment = "" + + // Check referenced element was in the root document. + return referenceURIMatch(doc.url, &refURL) +} + +func referenceURIMatch(u1 *url.URL, u2 *url.URL) bool { + s1, s2 := *u1, *u2 + if s1.Scheme == "" { + s1.Scheme = "file" + } + if s2.Scheme == "" { + s2.Scheme = "file" + } + + return s1.String() == s2.String() +} + +// ReferencesComponentInRootDocument returns if the given component reference references +// the same document or element as another component reference in the root document's +// '#/components/'. If it does, it returns the name of it in the form +// '#/components//NameXXX' +// +// Of course given a component from the root document will always match itself. +// +// https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#reference-object +// https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#relative-references-in-urls +// +// Example. Take the spec with directory structure: +// +// openapi.yaml +// schemas/ +// ├─ record.yaml +// ├─ records.yaml +// +// In openapi.yaml we have: +// +// components: +// schemas: +// Record: +// $ref: schemas/record.yaml +// +// Case 1: records.yml references a component in the root document +// +// $ref: ../openapi.yaml#/components/schemas/Record +// +// This would return... +// +// #/components/schemas/Record +// +// Case 2: records.yml indirectly refers to the same schema +// as a schema the root document's '#/components/schemas'. +// +// $ref: ./record.yaml +// +// This would also return... +// +// #/components/schemas/Record +func ReferencesComponentInRootDocument(doc *T, ref ComponentRef) (string, bool) { + if ref == nil || ref.RefString() == "" { + return "", false + } + + // Case 1: + // Something like: ../another-folder/document.json#/myElement + if isRemoteReference(ref.RefString()) && isRootComponentReference(ref.RefString(), ref.CollectionName()) { + // Determine if it is *this* root doc. + if referencesRootDocument(doc, ref) { + _, name, _ := strings.Cut(ref.RefString(), path.Join("#/components/", ref.CollectionName())) + + return path.Join("#/components/", ref.CollectionName(), name), true + } + } + + // If there are no schemas defined in the root document return early. + if doc.Components == nil { + return "", false + } + + collection, _, err := jsonpointer.GetForToken(doc.Components, ref.CollectionName()) + if err != nil { + panic(err) // unreachable + } + + var components map[string]ComponentRef + + componentRefType := reflect.TypeOf(new(ComponentRef)).Elem() + if t := reflect.TypeOf(collection); t.Kind() == reflect.Map && + t.Key().Kind() == reflect.String && + t.Elem().AssignableTo(componentRefType) { + v := reflect.ValueOf(collection) + + components = make(map[string]ComponentRef, v.Len()) + for _, key := range v.MapKeys() { + strct := v.MapIndex(key) + // Type assertion safe, already checked via reflection above. + components[key.Interface().(string)] = strct.Interface().(ComponentRef) + } + } else { + return "", false + } + + // Case 2: + // Something like: ../openapi.yaml#/components/schemas/myElement + for name, s := range components { + // Must be a reference to a YAML file. + if !isWholeDocumentReference(s.RefString()) { + continue + } + + // Is the schema a ref to the same resource. + if !refersToSameDocument(s, ref) { + continue + } + + // Transform the remote ref to the equivalent schema in the root document. + return path.Join("#/components/", ref.CollectionName(), name), true + } + + return "", false +} + +// isElementReference takes a $ref value and checks if it references a specific element. +func isElementReference(ref string) bool { + return ref != "" && !isWholeDocumentReference(ref) +} + +// isSchemaReference takes a $ref value and checks if it references a schema element. +func isRootComponentReference(ref string, compType string) bool { + return isElementReference(ref) && strings.Contains(ref, path.Join("#/components/", compType)) +} + +// isWholeDocumentReference takes a $ref value and checks if it is whole document reference. +func isWholeDocumentReference(ref string) bool { + return ref != "" && !strings.ContainsAny(ref, "#") +} + +// isRemoteReference takes a $ref value and checks if it is remote reference. +func isRemoteReference(ref string) bool { + return ref != "" && !strings.HasPrefix(ref, "#") && !isURLReference(ref) +} + +// isURLReference takes a $ref value and checks if it is URL reference. +func isURLReference(ref string) bool { + return strings.HasPrefix(ref, "http://") || strings.HasPrefix(ref, "https://") || strings.HasPrefix(ref, "//") +} diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/info.go b/vendor/github.com/getkin/kin-openapi/openapi3/info.go index ffcd3b0e3..e2468285c 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/info.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/info.go @@ -9,7 +9,7 @@ import ( // Info is specified by OpenAPI/Swagger standard version 3. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#info-object type Info struct { - Extensions map[string]interface{} `json:"-" yaml:"-"` + Extensions map[string]any `json:"-" yaml:"-"` Title string `json:"title" yaml:"title"` // Required Description string `json:"description,omitempty" yaml:"description,omitempty"` @@ -21,7 +21,19 @@ type Info struct { // MarshalJSON returns the JSON encoding of Info. func (info Info) MarshalJSON() ([]byte, error) { - m := make(map[string]interface{}, 6+len(info.Extensions)) + x, err := info.MarshalYAML() + if err != nil { + return nil, err + } + return json.Marshal(x) +} + +// MarshalYAML returns the YAML encoding of Info. +func (info *Info) MarshalYAML() (any, error) { + if info == nil { + return nil, nil + } + m := make(map[string]any, 6+len(info.Extensions)) for k, v := range info.Extensions { m[k] = v } @@ -39,7 +51,7 @@ func (info Info) MarshalJSON() ([]byte, error) { m["license"] = x } m["version"] = info.Version - return json.Marshal(m) + return m, nil } // UnmarshalJSON sets Info to a copy of data. diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/internalize_refs.go b/vendor/github.com/getkin/kin-openapi/openapi3/internalize_refs.go index e313e5535..01f5dad88 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/internalize_refs.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/internalize_refs.go @@ -2,47 +2,134 @@ package openapi3 import ( "context" - "path/filepath" + "path" "strings" ) -type RefNameResolver func(string) string +// RefNameResolver maps a component to an name that is used as it's internalized name. +// +// The function should avoid name collisions (i.e. be a injective mapping). +// It must only contain characters valid for fixed field names: [IdentifierRegExp]. +type RefNameResolver func(*T, ComponentRef) string // DefaultRefResolver is a default implementation of refNameResolver for the // InternalizeRefs function. // -// If a reference points to an element inside a document, it returns the last -// element in the reference using filepath.Base. Otherwise if the reference points -// to a file, it returns the file name trimmed of all extensions. -func DefaultRefNameResolver(ref string) string { - if ref == "" { - return "" - } - split := strings.SplitN(ref, "#", 2) - if len(split) == 2 { - return filepath.Base(split[1]) - } - ref = split[0] - for ext := filepath.Ext(ref); len(ext) > 0; ext = filepath.Ext(ref) { - ref = strings.TrimSuffix(ref, ext) - } - return filepath.Base(ref) -} +// The external reference is internalized to (hopefully) a unique name. If +// the external reference matches (by path) to another reference in the root +// document then the name of that component is used. +// +// The transformation involves: +// - Cutting the "#/components/" part. +// - Cutting the file extensions (.yaml/.json) from documents. +// - Trimming the common directory with the root spec. +// - Replace invalid characters with with underscores. +// +// This is an injective mapping over a "reasonable" amount of the possible openapi +// spec domain space but is not perfect. There might be edge cases. +func DefaultRefNameResolver(doc *T, ref ComponentRef) string { + if ref.RefString() == "" || ref.RefPath() == nil { + panic("unable to resolve reference to name") + } + + name := ref.RefPath() + + // If refering to a component in the root spec, no need to internalize just use + // the existing component. + // XXX(percivalalb): since this function call is iterating over components behind the + // scenes during an internalization call it actually starts interating over + // new & replaced internalized components. This might caused some edge cases, + // haven't found one yet but this might need to actually be used on a frozen copy + // of doc. + if nameInRoot, found := ReferencesComponentInRootDocument(doc, ref); found { + nameInRoot = strings.TrimPrefix(nameInRoot, "#") + + rootCompURI := copyURI(doc.url) + rootCompURI.Fragment = nameInRoot + name = rootCompURI + } + + filePath, componentPath := name.Path, name.Fragment + + // Cut out the "#/components/" to make the names shorter. + // XXX(percivalalb): This might cause collisions but is worth the brevity. + if b, a, ok := strings.Cut(componentPath, path.Join("components", ref.CollectionName(), "")); ok { + componentPath = path.Join(b, a) + } + + if filePath != "" { + // If the path is the same as the root doc, just remove. + if doc.url != nil && filePath == doc.url.Path { + filePath = "" + } + + // Remove the path extentions to make this JSON/YAML agnostic. + for ext := path.Ext(filePath); len(ext) > 0; ext = path.Ext(filePath) { + filePath = strings.TrimSuffix(filePath, ext) + } + + // Trim the common prefix with the root doc path. + if doc.url != nil { + commonDir := path.Dir(doc.url.Path) + for { + if commonDir == "." { // no common prefix + break + } + + if p, found := cutDirectories(filePath, commonDir); found { + filePath = p + break + } -func schemaNames(s Schemas) []string { - out := make([]string, 0, len(s)) - for i := range s { - out = append(out, i) + commonDir = path.Dir(commonDir) + } + } + } + + var internalizedName string + + // Trim .'s & slashes from start e.g. otherwise ./doc.yaml would end up as __doc + if filePath != "" { + internalizedName = strings.TrimLeft(filePath, "./") } - return out + + if componentPath != "" { + if internalizedName != "" { + internalizedName += "_" + } + + internalizedName += strings.TrimLeft(componentPath, "./") + } + + // Replace invalid characters in component fixed field names. + internalizedName = InvalidIdentifierCharRegExp.ReplaceAllString(internalizedName, "_") + + return internalizedName } -func parametersMapNames(s ParametersMap) []string { - out := make([]string, 0, len(s)) - for i := range s { - out = append(out, i) +// cutDirectories removes the given directories from the start of the path if +// the path is a child. +func cutDirectories(p, dirs string) (string, bool) { + if dirs == "" || p == "" { + return p, false + } + + p = strings.TrimRight(p, "/") + dirs = strings.TrimRight(dirs, "/") + + var sb strings.Builder + sb.Grow(len(ParameterInHeader)) + for _, segments := range strings.Split(p, "/") { + sb.WriteString(segments) + + if sb.String() == p { + return strings.TrimPrefix(p, dirs), true + } + + sb.WriteRune('/') } - return out + + return p, false } func isExternalRef(ref string, parentIsExternal bool) bool { @@ -54,7 +141,7 @@ func (doc *T) addSchemaToSpec(s *SchemaRef, refNameResolver RefNameResolver, par return false } - name := refNameResolver(s.Ref) + name := refNameResolver(doc, s) if doc.Components != nil { if _, ok := doc.Components.Schemas[name]; ok { s.Ref = "#/components/schemas/" + name @@ -77,7 +164,7 @@ func (doc *T) addParameterToSpec(p *ParameterRef, refNameResolver RefNameResolve if p == nil || !isExternalRef(p.Ref, parentIsExternal) { return false } - name := refNameResolver(p.Ref) + name := refNameResolver(doc, p) if doc.Components != nil { if _, ok := doc.Components.Parameters[name]; ok { p.Ref = "#/components/parameters/" + name @@ -100,7 +187,7 @@ func (doc *T) addHeaderToSpec(h *HeaderRef, refNameResolver RefNameResolver, par if h == nil || !isExternalRef(h.Ref, parentIsExternal) { return false } - name := refNameResolver(h.Ref) + name := refNameResolver(doc, h) if doc.Components != nil { if _, ok := doc.Components.Headers[name]; ok { h.Ref = "#/components/headers/" + name @@ -123,7 +210,7 @@ func (doc *T) addRequestBodyToSpec(r *RequestBodyRef, refNameResolver RefNameRes if r == nil || !isExternalRef(r.Ref, parentIsExternal) { return false } - name := refNameResolver(r.Ref) + name := refNameResolver(doc, r) if doc.Components != nil { if _, ok := doc.Components.RequestBodies[name]; ok { r.Ref = "#/components/requestBodies/" + name @@ -146,7 +233,7 @@ func (doc *T) addResponseToSpec(r *ResponseRef, refNameResolver RefNameResolver, if r == nil || !isExternalRef(r.Ref, parentIsExternal) { return false } - name := refNameResolver(r.Ref) + name := refNameResolver(doc, r) if doc.Components != nil { if _, ok := doc.Components.Responses[name]; ok { r.Ref = "#/components/responses/" + name @@ -169,7 +256,7 @@ func (doc *T) addSecuritySchemeToSpec(ss *SecuritySchemeRef, refNameResolver Ref if ss == nil || !isExternalRef(ss.Ref, parentIsExternal) { return } - name := refNameResolver(ss.Ref) + name := refNameResolver(doc, ss) if doc.Components != nil { if _, ok := doc.Components.SecuritySchemes[name]; ok { ss.Ref = "#/components/securitySchemes/" + name @@ -192,7 +279,7 @@ func (doc *T) addExampleToSpec(e *ExampleRef, refNameResolver RefNameResolver, p if e == nil || !isExternalRef(e.Ref, parentIsExternal) { return } - name := refNameResolver(e.Ref) + name := refNameResolver(doc, e) if doc.Components != nil { if _, ok := doc.Components.Examples[name]; ok { e.Ref = "#/components/examples/" + name @@ -215,7 +302,7 @@ func (doc *T) addLinkToSpec(l *LinkRef, refNameResolver RefNameResolver, parentI if l == nil || !isExternalRef(l.Ref, parentIsExternal) { return } - name := refNameResolver(l.Ref) + name := refNameResolver(doc, l) if doc.Components != nil { if _, ok := doc.Components.Links[name]; ok { l.Ref = "#/components/links/" + name @@ -238,7 +325,7 @@ func (doc *T) addCallbackToSpec(c *CallbackRef, refNameResolver RefNameResolver, if c == nil || !isExternalRef(c.Ref, parentIsExternal) { return false } - name := refNameResolver(c.Ref) + name := refNameResolver(doc, c) if doc.Components == nil { doc.Components = &Components{} @@ -264,7 +351,9 @@ func (doc *T) derefSchema(s *Schema, refNameResolver RefNameResolver, parentIsEx } } } - for _, s2 := range s.Properties { + + for _, name := range componentNames(s.Properties) { + s2 := s.Properties[name] isExternal := doc.addSchemaToSpec(s2, refNameResolver, parentIsExternal) if s2 != nil { doc.derefSchema(s2.Value, refNameResolver, isExternal || parentIsExternal) @@ -279,7 +368,8 @@ func (doc *T) derefSchema(s *Schema, refNameResolver RefNameResolver, parentIsEx } func (doc *T) derefHeaders(hs Headers, refNameResolver RefNameResolver, parentIsExternal bool) { - for _, h := range hs { + for _, name := range componentNames(hs) { + h := hs[name] isExternal := doc.addHeaderToSpec(h, refNameResolver, parentIsExternal) if doc.isVisitedHeader(h.Value) { continue @@ -289,26 +379,30 @@ func (doc *T) derefHeaders(hs Headers, refNameResolver RefNameResolver, parentIs } func (doc *T) derefExamples(es Examples, refNameResolver RefNameResolver, parentIsExternal bool) { - for _, e := range es { + for _, name := range componentNames(es) { + e := es[name] doc.addExampleToSpec(e, refNameResolver, parentIsExternal) } } func (doc *T) derefContent(c Content, refNameResolver RefNameResolver, parentIsExternal bool) { - for _, mediatype := range c { + for _, name := range componentNames(c) { + mediatype := c[name] isExternal := doc.addSchemaToSpec(mediatype.Schema, refNameResolver, parentIsExternal) if mediatype.Schema != nil { doc.derefSchema(mediatype.Schema.Value, refNameResolver, isExternal || parentIsExternal) } doc.derefExamples(mediatype.Examples, refNameResolver, parentIsExternal) - for _, e := range mediatype.Encoding { + for _, name := range componentNames(mediatype.Encoding) { + e := mediatype.Encoding[name] doc.derefHeaders(e.Headers, refNameResolver, parentIsExternal) } } } func (doc *T) derefLinks(ls Links, refNameResolver RefNameResolver, parentIsExternal bool) { - for _, l := range ls { + for _, name := range componentNames(ls) { + l := ls[name] doc.addLinkToSpec(l, refNameResolver, parentIsExternal) } } @@ -327,7 +421,8 @@ func (doc *T) derefResponses(rs *Responses, refNameResolver RefNameResolver, par } func (doc *T) derefResponseBodies(es ResponseBodies, refNameResolver RefNameResolver, parentIsExternal bool) { - for _, e := range es { + for _, name := range componentNames(es) { + e := es[name] doc.derefResponse(e, refNameResolver, parentIsExternal) } } @@ -345,21 +440,28 @@ func (doc *T) derefRequestBody(r RequestBody, refNameResolver RefNameResolver, p } func (doc *T) derefPaths(paths map[string]*PathItem, refNameResolver RefNameResolver, parentIsExternal bool) { - for _, ops := range paths { + for _, name := range componentNames(paths) { + ops := paths[name] pathIsExternal := isExternalRef(ops.Ref, parentIsExternal) // inline full operations ops.Ref = "" for _, param := range ops.Parameters { - doc.addParameterToSpec(param, refNameResolver, pathIsExternal) + isExternal := doc.addParameterToSpec(param, refNameResolver, pathIsExternal) + if param.Value != nil { + doc.derefParameter(*param.Value, refNameResolver, pathIsExternal || isExternal) + } } - for _, op := range ops.Operations() { + opsWithMethod := ops.Operations() + for _, name := range componentNames(opsWithMethod) { + op := opsWithMethod[name] isExternal := doc.addRequestBodyToSpec(op.RequestBody, refNameResolver, pathIsExternal) if op.RequestBody != nil && op.RequestBody.Value != nil { doc.derefRequestBody(*op.RequestBody.Value, refNameResolver, pathIsExternal || isExternal) } - for _, cb := range op.Callbacks { + for _, name := range componentNames(op.Callbacks) { + cb := op.Callbacks[name] isExternal := doc.addCallbackToSpec(cb, refNameResolver, pathIsExternal) if cb.Value != nil { cbValue := (*cb.Value).Map() @@ -388,7 +490,7 @@ func (doc *T) derefPaths(paths map[string]*PathItem, refNameResolver RefNameReso // Example: // // doc.InternalizeRefs(context.Background(), nil) -func (doc *T) InternalizeRefs(ctx context.Context, refNameResolver func(ref string) string) { +func (doc *T) InternalizeRefs(ctx context.Context, refNameResolver func(*T, ComponentRef) string) { doc.resetVisited() if refNameResolver == nil { @@ -396,8 +498,7 @@ func (doc *T) InternalizeRefs(ctx context.Context, refNameResolver func(ref stri } if components := doc.Components; components != nil { - names := schemaNames(components.Schemas) - for _, name := range names { + for _, name := range componentNames(components.Schemas) { schema := components.Schemas[name] isExternal := doc.addSchemaToSpec(schema, refNameResolver, false) if schema != nil { @@ -405,8 +506,7 @@ func (doc *T) InternalizeRefs(ctx context.Context, refNameResolver func(ref stri doc.derefSchema(schema.Value, refNameResolver, isExternal) } } - names = parametersMapNames(components.Parameters) - for _, name := range names { + for _, name := range componentNames(components.Parameters) { p := components.Parameters[name] isExternal := doc.addParameterToSpec(p, refNameResolver, false) if p != nil && p.Value != nil { @@ -415,7 +515,8 @@ func (doc *T) InternalizeRefs(ctx context.Context, refNameResolver func(ref stri } } doc.derefHeaders(components.Headers, refNameResolver, false) - for _, req := range components.RequestBodies { + for _, name := range componentNames(components.RequestBodies) { + req := components.RequestBodies[name] isExternal := doc.addRequestBodyToSpec(req, refNameResolver, false) if req != nil && req.Value != nil { req.Ref = "" // always dereference the top level @@ -423,13 +524,15 @@ func (doc *T) InternalizeRefs(ctx context.Context, refNameResolver func(ref stri } } doc.derefResponseBodies(components.Responses, refNameResolver, false) - for _, ss := range components.SecuritySchemes { + for _, name := range componentNames(components.SecuritySchemes) { + ss := components.SecuritySchemes[name] doc.addSecuritySchemeToSpec(ss, refNameResolver, false) } doc.derefExamples(components.Examples, refNameResolver, false) doc.derefLinks(components.Links, refNameResolver, false) - for _, cb := range components.Callbacks { + for _, name := range componentNames(components.Callbacks) { + cb := components.Callbacks[name] isExternal := doc.addCallbackToSpec(cb, refNameResolver, false) if cb != nil && cb.Value != nil { cb.Ref = "" // always dereference the top level diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/license.go b/vendor/github.com/getkin/kin-openapi/openapi3/license.go index 3d2d2f06d..c4f6c8dc3 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/license.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/license.go @@ -9,7 +9,7 @@ import ( // License is specified by OpenAPI/Swagger standard version 3. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#license-object type License struct { - Extensions map[string]interface{} `json:"-" yaml:"-"` + Extensions map[string]any `json:"-" yaml:"-"` Name string `json:"name" yaml:"name"` // Required URL string `json:"url,omitempty" yaml:"url,omitempty"` @@ -17,7 +17,16 @@ type License struct { // MarshalJSON returns the JSON encoding of License. func (license License) MarshalJSON() ([]byte, error) { - m := make(map[string]interface{}, 2+len(license.Extensions)) + x, err := license.MarshalYAML() + if err != nil { + return nil, err + } + return json.Marshal(x) +} + +// MarshalYAML returns the YAML encoding of License. +func (license License) MarshalYAML() (any, error) { + m := make(map[string]any, 2+len(license.Extensions)) for k, v := range license.Extensions { m[k] = v } @@ -25,7 +34,7 @@ func (license License) MarshalJSON() ([]byte, error) { if x := license.URL; x != "" { m["url"] = x } - return json.Marshal(m) + return m, nil } // UnmarshalJSON sets License to a copy of data. diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/link.go b/vendor/github.com/getkin/kin-openapi/openapi3/link.go index 23a8df41b..132f67803 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/link.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/link.go @@ -10,19 +10,28 @@ import ( // Link is specified by OpenAPI/Swagger standard version 3. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#link-object type Link struct { - Extensions map[string]interface{} `json:"-" yaml:"-"` + Extensions map[string]any `json:"-" yaml:"-"` - OperationRef string `json:"operationRef,omitempty" yaml:"operationRef,omitempty"` - OperationID string `json:"operationId,omitempty" yaml:"operationId,omitempty"` - Description string `json:"description,omitempty" yaml:"description,omitempty"` - Parameters map[string]interface{} `json:"parameters,omitempty" yaml:"parameters,omitempty"` - Server *Server `json:"server,omitempty" yaml:"server,omitempty"` - RequestBody interface{} `json:"requestBody,omitempty" yaml:"requestBody,omitempty"` + OperationRef string `json:"operationRef,omitempty" yaml:"operationRef,omitempty"` + OperationID string `json:"operationId,omitempty" yaml:"operationId,omitempty"` + Description string `json:"description,omitempty" yaml:"description,omitempty"` + Parameters map[string]any `json:"parameters,omitempty" yaml:"parameters,omitempty"` + Server *Server `json:"server,omitempty" yaml:"server,omitempty"` + RequestBody any `json:"requestBody,omitempty" yaml:"requestBody,omitempty"` } // MarshalJSON returns the JSON encoding of Link. func (link Link) MarshalJSON() ([]byte, error) { - m := make(map[string]interface{}, 6+len(link.Extensions)) + x, err := link.MarshalYAML() + if err != nil { + return nil, err + } + return json.Marshal(x) +} + +// MarshalYAML returns the YAML encoding of Link. +func (link Link) MarshalYAML() (any, error) { + m := make(map[string]any, 6+len(link.Extensions)) for k, v := range link.Extensions { m[k] = v } @@ -46,7 +55,7 @@ func (link Link) MarshalJSON() ([]byte, error) { m["requestBody"] = x } - return json.Marshal(m) + return m, nil } // UnmarshalJSON sets Link to a copy of data. diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/loader.go b/vendor/github.com/getkin/kin-openapi/openapi3/loader.go index 88ab566ac..31c340761 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/loader.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/loader.go @@ -11,14 +11,10 @@ import ( "path" "path/filepath" "reflect" - "sort" "strconv" "strings" ) -var CircularReferenceError = "kin-openapi bug found: circular schema reference not handled" -var CircularReferenceCounter = 3 - func foundUnresolvedRef(ref string) error { return fmt.Errorf("found unresolved ref: %q", ref) } @@ -44,15 +40,9 @@ type Loader struct { visitedDocuments map[string]*T - visitedCallback map[*Callback]struct{} - visitedExample map[*Example]struct{} - visitedHeader map[*Header]struct{} - visitedLink map[*Link]struct{} - visitedParameter map[*Parameter]struct{} - visitedRequestBody map[*RequestBody]struct{} - visitedResponse map[*Response]struct{} - visitedSchema map[*Schema]struct{} - visitedSecurityScheme map[*SecurityScheme]struct{} + visitedRefs map[string]struct{} + visitedPath []string + backtrack map[string][]func(value any) } // NewLoader returns an empty Loader @@ -64,6 +54,9 @@ func NewLoader() *Loader { func (loader *Loader) resetVisitedPathItemRefs() { loader.visitedPathItemRefs = make(map[string]struct{}) + loader.visitedRefs = make(map[string]struct{}) + loader.visitedPath = nil + loader.backtrack = make(map[string][]func(value any)) } // LoadFromURI loads a spec from a remote URL @@ -93,7 +86,7 @@ func (loader *Loader) allowsExternalRefs(ref string) (err error) { return } -func (loader *Loader) loadSingleElementFromURI(ref string, rootPath *url.URL, element interface{}) (*url.URL, error) { +func (loader *Loader) loadSingleElementFromURI(ref string, rootPath *url.URL, element any) (*url.URL, error) { if err := loader.allowsExternalRefs(ref); err != nil { return nil, err } @@ -178,6 +171,9 @@ func (loader *Loader) loadFromDataWithPathInternal(data []byte, location *url.UR if err := unmarshal(data, doc); err != nil { return nil, err } + + doc.url = copyURI(location) + if err := loader.ResolveRefsIn(doc, location); err != nil { return nil, err } @@ -196,50 +192,50 @@ func (loader *Loader) ResolveRefsIn(doc *T, location *url.URL) (err error) { } if components := doc.Components; components != nil { - for _, component := range components.Headers { + for _, name := range componentNames(components.Headers) { + component := components.Headers[name] if err = loader.resolveHeaderRef(doc, component, location); err != nil { return } } - for _, component := range components.Parameters { + for _, name := range componentNames(components.Parameters) { + component := components.Parameters[name] if err = loader.resolveParameterRef(doc, component, location); err != nil { return } } - for _, component := range components.RequestBodies { + for _, name := range componentNames(components.RequestBodies) { + component := components.RequestBodies[name] if err = loader.resolveRequestBodyRef(doc, component, location); err != nil { return } } - for _, component := range components.Responses { + for _, name := range componentNames(components.Responses) { + component := components.Responses[name] if err = loader.resolveResponseRef(doc, component, location); err != nil { return } } - for _, component := range components.Schemas { + for _, name := range componentNames(components.Schemas) { + component := components.Schemas[name] if err = loader.resolveSchemaRef(doc, component, location, []string{}); err != nil { return } } - for _, component := range components.SecuritySchemes { + for _, name := range componentNames(components.SecuritySchemes) { + component := components.SecuritySchemes[name] if err = loader.resolveSecuritySchemeRef(doc, component, location); err != nil { return } } - - examples := make([]string, 0, len(components.Examples)) - for name := range components.Examples { - examples = append(examples, name) - } - sort.Strings(examples) - for _, name := range examples { + for _, name := range componentNames(components.Examples) { component := components.Examples[name] if err = loader.resolveExampleRef(doc, component, location); err != nil { return } } - - for _, component := range components.Callbacks { + for _, name := range componentNames(components.Callbacks) { + component := components.Callbacks[name] if err = loader.resolveCallbackRef(doc, component, location); err != nil { return } @@ -247,7 +243,9 @@ func (loader *Loader) ResolveRefsIn(doc *T, location *url.URL) (err error) { } // Visit all operations - for _, pathItem := range doc.Paths.Map() { + pathItems := doc.Paths.Map() + for _, name := range componentNames(pathItems) { + pathItem := pathItems[name] if pathItem == nil { continue } @@ -271,7 +269,7 @@ func join(basePath *url.URL, relativePath *url.URL) *url.URL { func resolvePath(basePath *url.URL, componentPath *url.URL) *url.URL { if is_file(componentPath) { // support absolute paths - if componentPath.Path[0] == '/' { + if filepath.IsAbs(componentPath.Path) { return componentPath } return join(basePath, componentPath) @@ -290,16 +288,69 @@ func resolvePathWithRef(ref string, rootPath *url.URL) (*url.URL, error) { return resolvedPath, nil } +func (loader *Loader) resolveRefPath(ref string, path *url.URL) (*url.URL, error) { + if ref != "" && ref[0] == '#' { + path = copyURI(path) + // Resolving internal refs of a doc loaded from memory + // has no path, so just set the Fragment. + if path == nil { + path = new(url.URL) + } + + path.Fragment = ref + return path, nil + } + + if err := loader.allowsExternalRefs(ref); err != nil { + return nil, err + } + + resolvedPath, err := resolvePathWithRef(ref, path) + if err != nil { + return nil, err + } + + return resolvedPath, nil +} + func isSingleRefElement(ref string) bool { return !strings.Contains(ref, "#") } -func (loader *Loader) resolveComponent(doc *T, ref string, path *url.URL, resolved interface{}) ( +func (loader *Loader) visitRef(ref string) { + if loader.visitedRefs == nil { + loader.visitedRefs = make(map[string]struct{}) + loader.backtrack = make(map[string][]func(value any)) + } + loader.visitedPath = append(loader.visitedPath, ref) + loader.visitedRefs[ref] = struct{}{} +} + +func (loader *Loader) unvisitRef(ref string, value any) { + if value != nil { + for _, fn := range loader.backtrack[ref] { + fn(value) + } + } + delete(loader.visitedRefs, ref) + delete(loader.backtrack, ref) + loader.visitedPath = loader.visitedPath[:len(loader.visitedPath)-1] +} + +func (loader *Loader) shouldVisitRef(ref string, fn func(value any)) bool { + if _, ok := loader.visitedRefs[ref]; ok { + loader.backtrack[ref] = append(loader.backtrack[ref], fn) + return false + } + return true +} + +func (loader *Loader) resolveComponent(doc *T, ref string, path *url.URL, resolved any) ( componentDoc *T, componentPath *url.URL, err error, ) { - if componentDoc, ref, componentPath, err = loader.resolveRef(doc, ref, path); err != nil { + if componentDoc, ref, componentPath, err = loader.resolveRefAndDocument(doc, ref, path); err != nil { return nil, nil, err } @@ -315,7 +366,7 @@ func (loader *Loader) resolveComponent(doc *T, ref string, path *url.URL, resolv return nil, nil, fmt.Errorf("expected fragment prefix '#/' in URI %q", ref) } - drill := func(cursor interface{}) (interface{}, error) { + drill := func(cursor any) (any, error) { for _, pathPart := range strings.Split(fragment[1:], "/") { pathPart = unescapeRefString(pathPart) attempted := false @@ -361,7 +412,7 @@ func (loader *Loader) resolveComponent(doc *T, ref string, path *url.URL, resolv } return cursor, nil } - var cursor interface{} + var cursor any if cursor, err = drill(componentDoc); err != nil { if path == nil { return nil, nil, err @@ -380,13 +431,31 @@ func (loader *Loader) resolveComponent(doc *T, ref string, path *url.URL, resolv err = nil } + setPathRef := func(target any) { + if i, ok := target.(interface { + setRefPath(*url.URL) + }); ok { + pathRef := copyURI(componentPath) + // Resolving internal refs of a doc loaded from memory + // has no path, so just set the Fragment. + if pathRef == nil { + pathRef = new(url.URL) + } + pathRef.Fragment = fragment + + i.setRefPath(pathRef) + } + } + switch { case reflect.TypeOf(cursor) == reflect.TypeOf(resolved): + setPathRef(cursor) + reflect.ValueOf(resolved).Elem().Set(reflect.ValueOf(cursor).Elem()) return componentDoc, componentPath, nil - case reflect.TypeOf(cursor) == reflect.TypeOf(map[string]interface{}{}): - codec := func(got, expect interface{}) error { + case reflect.TypeOf(cursor) == reflect.TypeOf(map[string]any{}): + codec := func(got, expect any) error { enc, err := json.Marshal(got) if err != nil { return err @@ -394,6 +463,8 @@ func (loader *Loader) resolveComponent(doc *T, ref string, path *url.URL, resolv if err = json.Unmarshal(enc, expect); err != nil { return err } + + setPathRef(expect) return nil } if err := codec(cursor, resolved); err != nil { @@ -406,7 +477,7 @@ func (loader *Loader) resolveComponent(doc *T, ref string, path *url.URL, resolv } } -func readableType(x interface{}) string { +func readableType(x any) string { switch x.(type) { case *Callback: return "callback object" @@ -435,7 +506,7 @@ func readableType(x interface{}) string { } } -func drillIntoField(cursor interface{}, fieldName string) (interface{}, error) { +func drillIntoField(cursor any, fieldName string) (any, error) { switch val := reflect.Indirect(reflect.ValueOf(cursor)); val.Kind() { case reflect.Map: @@ -476,7 +547,7 @@ func drillIntoField(cursor interface{}, fieldName string) (interface{}, error) { } if hasFields { if ff := val.Type().Field(0); ff.PkgPath == "" && ff.Name == "Extensions" { - extensions := val.Field(0).Interface().(map[string]interface{}) + extensions := val.Field(0).Interface().(map[string]any) if enc, ok := extensions[fieldName]; ok { return enc, nil } @@ -489,21 +560,15 @@ func drillIntoField(cursor interface{}, fieldName string) (interface{}, error) { } } -func (loader *Loader) resolveRef(doc *T, ref string, path *url.URL) (*T, string, *url.URL, error) { +func (loader *Loader) resolveRefAndDocument(doc *T, ref string, path *url.URL) (*T, string, *url.URL, error) { if ref != "" && ref[0] == '#' { return doc, ref, path, nil } - if err := loader.allowsExternalRefs(ref); err != nil { - return nil, "", nil, err - } - - resolvedPath, err := resolvePathWithRef(ref, path) + fragment, resolvedPath, err := loader.resolveRef(ref, path) if err != nil { return nil, "", nil, err } - fragment := "#" + resolvedPath.Fragment - resolvedPath.Fragment = "" if doc, err = loader.loadFromURIInternal(resolvedPath); err != nil { return nil, "", nil, fmt.Errorf("error resolving reference %q: %w", ref, err) @@ -512,6 +577,17 @@ func (loader *Loader) resolveRef(doc *T, ref string, path *url.URL) (*T, string, return doc, fragment, resolvedPath, nil } +func (loader *Loader) resolveRef(ref string, path *url.URL) (string, *url.URL, error) { + resolvedPathRef, err := loader.resolveRefPath(ref, path) + if err != nil { + return "", nil, err + } + + fragment := "#" + resolvedPathRef.Fragment + resolvedPathRef.Fragment = "" + return fragment, resolvedPathRef, nil +} + var ( errMUSTCallback = errors.New("invalid callback: value MUST be an object") errMUSTExample = errors.New("invalid example: value MUST be an object") @@ -530,23 +606,25 @@ func (loader *Loader) resolveHeaderRef(doc *T, component *HeaderRef, documentPat return errMUSTHeader } - if component.Value != nil { - if loader.visitedHeader == nil { - loader.visitedHeader = make(map[*Header]struct{}) + if ref := component.Ref; ref != "" { + if component.Value != nil { + return nil } - if _, ok := loader.visitedHeader[component.Value]; ok { + if !loader.shouldVisitRef(ref, func(value any) { + component.Value = value.(*Header) + refPath, _ := loader.resolveRefPath(ref, documentPath) + component.setRefPath(refPath) + }) { return nil } - loader.visitedHeader[component.Value] = struct{}{} - } - - if ref := component.Ref; ref != "" { + loader.visitRef(ref) if isSingleRefElement(ref) { var header Header if documentPath, err = loader.loadSingleElementFromURI(ref, documentPath, &header); err != nil { return err } component.Value = &header + component.setRefPath(documentPath) } else { var resolved HeaderRef doc, componentPath, err := loader.resolveComponent(doc, ref, documentPath, &resolved) @@ -560,7 +638,9 @@ func (loader *Loader) resolveHeaderRef(doc *T, component *HeaderRef, documentPat return err } component.Value = resolved.Value + component.setRefPath(resolved.RefPath()) } + defer loader.unvisitRef(ref, component.Value) } value := component.Value if value == nil { @@ -580,23 +660,25 @@ func (loader *Loader) resolveParameterRef(doc *T, component *ParameterRef, docum return errMUSTParameter } - if component.Value != nil { - if loader.visitedParameter == nil { - loader.visitedParameter = make(map[*Parameter]struct{}) + if ref := component.Ref; ref != "" { + if component.Value != nil { + return nil } - if _, ok := loader.visitedParameter[component.Value]; ok { + if !loader.shouldVisitRef(ref, func(value any) { + component.Value = value.(*Parameter) + refPath, _ := loader.resolveRefPath(ref, documentPath) + component.setRefPath(refPath) + }) { return nil } - loader.visitedParameter[component.Value] = struct{}{} - } - - if ref := component.Ref; ref != "" { + loader.visitRef(ref) if isSingleRefElement(ref) { var param Parameter if documentPath, err = loader.loadSingleElementFromURI(ref, documentPath, ¶m); err != nil { return err } component.Value = ¶m + component.setRefPath(documentPath) } else { var resolved ParameterRef doc, componentPath, err := loader.resolveComponent(doc, ref, documentPath, &resolved) @@ -610,7 +692,9 @@ func (loader *Loader) resolveParameterRef(doc *T, component *ParameterRef, docum return err } component.Value = resolved.Value + component.setRefPath(resolved.RefPath()) } + defer loader.unvisitRef(ref, component.Value) } value := component.Value if value == nil { @@ -620,7 +704,8 @@ func (loader *Loader) resolveParameterRef(doc *T, component *ParameterRef, docum if value.Content != nil && value.Schema != nil { return errors.New("cannot contain both schema and content in a parameter") } - for _, contentType := range value.Content { + 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 @@ -640,23 +725,25 @@ func (loader *Loader) resolveRequestBodyRef(doc *T, component *RequestBodyRef, d return errMUSTRequestBody } - if component.Value != nil { - if loader.visitedRequestBody == nil { - loader.visitedRequestBody = make(map[*RequestBody]struct{}) + if ref := component.Ref; ref != "" { + if component.Value != nil { + return nil } - if _, ok := loader.visitedRequestBody[component.Value]; ok { + if !loader.shouldVisitRef(ref, func(value any) { + component.Value = value.(*RequestBody) + refPath, _ := loader.resolveRefPath(ref, documentPath) + component.setRefPath(refPath) + }) { return nil } - loader.visitedRequestBody[component.Value] = struct{}{} - } - - if ref := component.Ref; ref != "" { + loader.visitRef(ref) if isSingleRefElement(ref) { var requestBody RequestBody if documentPath, err = loader.loadSingleElementFromURI(ref, documentPath, &requestBody); err != nil { return err } component.Value = &requestBody + component.setRefPath(documentPath) } else { var resolved RequestBodyRef doc, componentPath, err := loader.resolveComponent(doc, ref, documentPath, &resolved) @@ -670,23 +757,21 @@ func (loader *Loader) resolveRequestBodyRef(doc *T, component *RequestBodyRef, d return err } component.Value = resolved.Value + component.setRefPath(resolved.RefPath()) } + defer loader.unvisitRef(ref, component.Value) } value := component.Value if value == nil { return nil } - for _, contentType := range value.Content { + for _, name := range componentNames(value.Content) { + contentType := value.Content[name] if contentType == nil { continue } - examples := make([]string, 0, len(contentType.Examples)) - for name := range contentType.Examples { - examples = append(examples, name) - } - sort.Strings(examples) - for _, name := range examples { + for _, name := range componentNames(contentType.Examples) { example := contentType.Examples[name] if err := loader.resolveExampleRef(doc, example, documentPath); err != nil { return err @@ -707,23 +792,25 @@ func (loader *Loader) resolveResponseRef(doc *T, component *ResponseRef, documen return errMUSTResponse } - if component.Value != nil { - if loader.visitedResponse == nil { - loader.visitedResponse = make(map[*Response]struct{}) + if ref := component.Ref; ref != "" { + if component.Value != nil { + return nil } - if _, ok := loader.visitedResponse[component.Value]; ok { + if !loader.shouldVisitRef(ref, func(value any) { + component.Value = value.(*Response) + refPath, _ := loader.resolveRefPath(ref, documentPath) + component.setRefPath(refPath) + }) { return nil } - loader.visitedResponse[component.Value] = struct{}{} - } - - if ref := component.Ref; ref != "" { + loader.visitRef(ref) if isSingleRefElement(ref) { var resp Response if documentPath, err = loader.loadSingleElementFromURI(ref, documentPath, &resp); err != nil { return err } component.Value = &resp + component.setRefPath(documentPath) } else { var resolved ResponseRef doc, componentPath, err := loader.resolveComponent(doc, ref, documentPath, &resolved) @@ -737,28 +824,27 @@ func (loader *Loader) resolveResponseRef(doc *T, component *ResponseRef, documen return err } component.Value = resolved.Value + component.setRefPath(resolved.RefPath()) } + defer loader.unvisitRef(ref, component.Value) } value := component.Value if value == nil { return nil } - for _, header := range value.Headers { + for _, name := range componentNames(value.Headers) { + header := value.Headers[name] if err := loader.resolveHeaderRef(doc, header, documentPath); err != nil { return err } } - for _, contentType := range value.Content { + for _, name := range componentNames(value.Content) { + contentType := value.Content[name] if contentType == nil { continue } - examples := make([]string, 0, len(contentType.Examples)) - for name := range contentType.Examples { - examples = append(examples, name) - } - sort.Strings(examples) - for _, name := range examples { + for _, name := range componentNames(contentType.Examples) { example := contentType.Examples[name] if err := loader.resolveExampleRef(doc, example, documentPath); err != nil { return err @@ -772,7 +858,8 @@ func (loader *Loader) resolveResponseRef(doc *T, component *ResponseRef, documen contentType.Schema = schema } } - for _, link := range value.Links { + for _, name := range componentNames(value.Links) { + link := value.Links[name] if err := loader.resolveLinkRef(doc, link, documentPath); err != nil { return err } @@ -785,30 +872,26 @@ func (loader *Loader) resolveSchemaRef(doc *T, component *SchemaRef, documentPat return errMUSTSchema } - if component.Value != nil { - if loader.visitedSchema == nil { - loader.visitedSchema = make(map[*Schema]struct{}) + if ref := component.Ref; ref != "" { + if component.Value != nil { + return nil } - if _, ok := loader.visitedSchema[component.Value]; ok { + if !loader.shouldVisitRef(ref, func(value any) { + component.Value = value.(*Schema) + refPath, _ := loader.resolveRefPath(ref, documentPath) + component.setRefPath(refPath) + }) { return nil } - loader.visitedSchema[component.Value] = struct{}{} - } - - if ref := component.Ref; ref != "" { + loader.visitRef(ref) if isSingleRefElement(ref) { var schema Schema if documentPath, err = loader.loadSingleElementFromURI(ref, documentPath, &schema); err != nil { return err } component.Value = &schema + component.setRefPath(documentPath) } else { - if visitedLimit(visited, ref) { - visited = append(visited, ref) - return fmt.Errorf("%s with length %d - %s", CircularReferenceError, len(visited), strings.Join(visited, " -> ")) - } - visited = append(visited, ref) - var resolved SchemaRef doc, componentPath, err := loader.resolveComponent(doc, ref, documentPath, &resolved) if err != nil { @@ -821,11 +904,9 @@ func (loader *Loader) resolveSchemaRef(doc *T, component *SchemaRef, documentPat return err } component.Value = resolved.Value + component.setRefPath(resolved.RefPath()) } - if loader.visitedSchema == nil { - loader.visitedSchema = make(map[*Schema]struct{}) - } - loader.visitedSchema[component.Value] = struct{}{} + defer loader.unvisitRef(ref, component.Value) } value := component.Value if value == nil { @@ -838,7 +919,8 @@ func (loader *Loader) resolveSchemaRef(doc *T, component *SchemaRef, documentPat return err } } - for _, v := range value.Properties { + for _, name := range componentNames(value.Properties) { + v := value.Properties[name] if err := loader.resolveSchemaRef(doc, v, documentPath, visited); err != nil { return err } @@ -876,23 +958,25 @@ func (loader *Loader) resolveSecuritySchemeRef(doc *T, component *SecurityScheme return errMUSTSecurityScheme } - if component.Value != nil { - if loader.visitedSecurityScheme == nil { - loader.visitedSecurityScheme = make(map[*SecurityScheme]struct{}) + if ref := component.Ref; ref != "" { + if component.Value != nil { + return nil } - if _, ok := loader.visitedSecurityScheme[component.Value]; ok { + if !loader.shouldVisitRef(ref, func(value any) { + component.Value = value.(*SecurityScheme) + refPath, _ := loader.resolveRefPath(ref, documentPath) + component.setRefPath(refPath) + }) { return nil } - loader.visitedSecurityScheme[component.Value] = struct{}{} - } - - if ref := component.Ref; ref != "" { + loader.visitRef(ref) if isSingleRefElement(ref) { var scheme SecurityScheme if _, err = loader.loadSingleElementFromURI(ref, documentPath, &scheme); err != nil { return err } component.Value = &scheme + component.setRefPath(documentPath) } else { var resolved SecuritySchemeRef doc, componentPath, err := loader.resolveComponent(doc, ref, documentPath, &resolved) @@ -906,33 +990,33 @@ func (loader *Loader) resolveSecuritySchemeRef(doc *T, component *SecurityScheme return err } component.Value = resolved.Value + component.setRefPath(resolved.RefPath()) } + defer loader.unvisitRef(ref, component.Value) } return nil } func (loader *Loader) resolveExampleRef(doc *T, component *ExampleRef, documentPath *url.URL) (err error) { - if component.isEmpty() { - return errMUSTExample - } - - if component.Value != nil { - if loader.visitedExample == nil { - loader.visitedExample = make(map[*Example]struct{}) + if ref := component.Ref; ref != "" { + if component.Value != nil { + return nil } - if _, ok := loader.visitedExample[component.Value]; ok { + if !loader.shouldVisitRef(ref, func(value any) { + component.Value = value.(*Example) + refPath, _ := loader.resolveRefPath(ref, documentPath) + component.setRefPath(refPath) + }) { return nil } - loader.visitedExample[component.Value] = struct{}{} - } - - if ref := component.Ref; ref != "" { + loader.visitRef(ref) if isSingleRefElement(ref) { var example Example if _, err = loader.loadSingleElementFromURI(ref, documentPath, &example); err != nil { return err } component.Value = &example + component.setRefPath(documentPath) } else { var resolved ExampleRef doc, componentPath, err := loader.resolveComponent(doc, ref, documentPath, &resolved) @@ -946,7 +1030,9 @@ func (loader *Loader) resolveExampleRef(doc *T, component *ExampleRef, documentP return err } component.Value = resolved.Value + component.setRefPath(resolved.RefPath()) } + defer loader.unvisitRef(ref, component.Value) } return nil } @@ -956,23 +1042,25 @@ func (loader *Loader) resolveCallbackRef(doc *T, component *CallbackRef, documen return errMUSTCallback } - if component.Value != nil { - if loader.visitedCallback == nil { - loader.visitedCallback = make(map[*Callback]struct{}) + if ref := component.Ref; ref != "" { + if component.Value != nil { + return nil } - if _, ok := loader.visitedCallback[component.Value]; ok { + if !loader.shouldVisitRef(ref, func(value any) { + component.Value = value.(*Callback) + refPath, _ := loader.resolveRefPath(ref, documentPath) + component.setRefPath(refPath) + }) { return nil } - loader.visitedCallback[component.Value] = struct{}{} - } - - if ref := component.Ref; ref != "" { + loader.visitRef(ref) if isSingleRefElement(ref) { var resolved Callback if documentPath, err = loader.loadSingleElementFromURI(ref, documentPath, &resolved); err != nil { return err } component.Value = &resolved + component.setRefPath(documentPath) } else { var resolved CallbackRef doc, componentPath, err := loader.resolveComponent(doc, ref, documentPath, &resolved) @@ -986,14 +1074,18 @@ func (loader *Loader) resolveCallbackRef(doc *T, component *CallbackRef, documen return err } component.Value = resolved.Value + component.setRefPath(resolved.RefPath()) } + defer loader.unvisitRef(ref, component.Value) } value := component.Value if value == nil { return nil } - for _, pathItem := range value.Map() { + pathItems := value.Map() + for _, name := range componentNames(pathItems) { + pathItem := pathItems[name] if err = loader.resolvePathItemRef(doc, pathItem, documentPath); err != nil { return err } @@ -1006,23 +1098,25 @@ func (loader *Loader) resolveLinkRef(doc *T, component *LinkRef, documentPath *u return errMUSTLink } - if component.Value != nil { - if loader.visitedLink == nil { - loader.visitedLink = make(map[*Link]struct{}) + if ref := component.Ref; ref != "" { + if component.Value != nil { + return nil } - if _, ok := loader.visitedLink[component.Value]; ok { + if !loader.shouldVisitRef(ref, func(value any) { + component.Value = value.(*Link) + refPath, _ := loader.resolveRefPath(ref, documentPath) + component.setRefPath(refPath) + }) { return nil } - loader.visitedLink[component.Value] = struct{}{} - } - - if ref := component.Ref; ref != "" { + loader.visitRef(ref) if isSingleRefElement(ref) { var link Link if _, err = loader.loadSingleElementFromURI(ref, documentPath, &link); err != nil { return err } component.Value = &link + component.setRefPath(documentPath) } else { var resolved LinkRef doc, componentPath, err := loader.resolveComponent(doc, ref, documentPath, &resolved) @@ -1036,7 +1130,9 @@ func (loader *Loader) resolveLinkRef(doc *T, component *LinkRef, documentPath *u return err } component.Value = resolved.Value + component.setRefPath(resolved.RefPath()) } + defer loader.unvisitRef(ref, component.Value) } return nil } @@ -1051,6 +1147,12 @@ func (loader *Loader) resolvePathItemRef(doc *T, pathItem *PathItem, documentPat if !pathItem.isEmpty() { return } + if !loader.shouldVisitRef(ref, func(value any) { + *pathItem = *value.(*PathItem) + }) { + return nil + } + loader.visitRef(ref) if isSingleRefElement(ref) { var p PathItem if documentPath, err = loader.loadSingleElementFromURI(ref, documentPath, &p); err != nil { @@ -1068,6 +1170,7 @@ func (loader *Loader) resolvePathItemRef(doc *T, pathItem *PathItem, documentPat *pathItem = resolved } pathItem.Ref = ref + defer loader.unvisitRef(ref, pathItem) } for _, parameter := range pathItem.Parameters { @@ -1075,7 +1178,9 @@ func (loader *Loader) resolvePathItemRef(doc *T, pathItem *PathItem, documentPat return } } - for _, operation := range pathItem.Operations() { + operations := pathItem.Operations() + for _, name := range componentNames(operations) { + operation := operations[name] for _, parameter := range operation.Parameters { if err = loader.resolveParameterRef(doc, parameter, documentPath); err != nil { return @@ -1086,12 +1191,15 @@ func (loader *Loader) resolvePathItemRef(doc *T, pathItem *PathItem, documentPat return } } - for _, response := range operation.Responses.Map() { + responses := operation.Responses.Map() + for _, name := range componentNames(responses) { + response := responses[name] if err = loader.resolveResponseRef(doc, response, documentPath); err != nil { return } } - for _, callback := range operation.Callbacks { + for _, name := range componentNames(operation.Callbacks) { + callback := operation.Callbacks[name] if err = loader.resolveCallbackRef(doc, callback, documentPath); err != nil { return } @@ -1103,16 +1211,3 @@ func (loader *Loader) resolvePathItemRef(doc *T, pathItem *PathItem, documentPat func unescapeRefString(ref string) string { return strings.Replace(strings.Replace(ref, "~1", "/", -1), "~0", "~", -1) } - -func visitedLimit(visited []string, ref string) bool { - visitedCount := 0 - for _, v := range visited { - if v == ref { - visitedCount++ - if visitedCount >= CircularReferenceCounter { - return true - } - } - } - return false -} diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/loader_uri_reader.go b/vendor/github.com/getkin/kin-openapi/openapi3/loader_uri_reader.go index ba7b5f24a..b023dfb29 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/loader_uri_reader.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/loader_uri_reader.go @@ -79,7 +79,7 @@ func ReadFromFile(loader *Loader, location *url.URL) ([]byte, error) { if !is_file(location) { return nil, ErrURINotSupported } - return os.ReadFile(location.Path) + return os.ReadFile(filepath.FromSlash(location.Path)) } // URIMapCache returns a ReadFromURIFunc that caches the contents read from URI diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/maplike.go b/vendor/github.com/getkin/kin-openapi/openapi3/maplike.go index b27cbf6c5..7b8045c67 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/maplike.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/maplike.go @@ -64,7 +64,7 @@ func (responses *Responses) Map() (m map[string]*ResponseRef) { var _ jsonpointer.JSONPointable = (*Responses)(nil) // JSONLookup implements https://github.com/go-openapi/jsonpointer#JSONPointable -func (responses Responses) JSONLookup(token string) (interface{}, error) { +func (responses Responses) JSONLookup(token string) (any, error) { if v := responses.Value(token); v == nil { vv, _, err := jsonpointer.GetForToken(responses.Extensions, token) return vv, err @@ -76,21 +76,33 @@ func (responses Responses) JSONLookup(token string) (interface{}, error) { } } -// MarshalJSON returns the JSON encoding of Responses. -func (responses *Responses) MarshalJSON() ([]byte, error) { - m := make(map[string]interface{}, responses.Len()+len(responses.Extensions)) +// MarshalYAML returns the YAML encoding of Responses. +func (responses *Responses) MarshalYAML() (any, error) { + if responses == nil { + return nil, nil + } + m := make(map[string]any, responses.Len()+len(responses.Extensions)) for k, v := range responses.Extensions { m[k] = v } for k, v := range responses.Map() { m[k] = v } - return json.Marshal(m) + return m, nil +} + +// MarshalJSON returns the JSON encoding of Responses. +func (responses *Responses) MarshalJSON() ([]byte, error) { + responsesYaml, err := responses.MarshalYAML() + if err != nil { + return nil, err + } + return json.Marshal(responsesYaml) } // UnmarshalJSON sets Responses to a copy of data. func (responses *Responses) UnmarshalJSON(data []byte) (err error) { - var m map[string]interface{} + var m map[string]any if err = json.Unmarshal(data, &m); err != nil { return } @@ -102,7 +114,7 @@ func (responses *Responses) UnmarshalJSON(data []byte) (err error) { sort.Strings(ks) x := Responses{ - Extensions: make(map[string]interface{}), + Extensions: make(map[string]any), m: make(map[string]*ResponseRef, len(m)), } @@ -183,7 +195,7 @@ func (callback *Callback) Map() (m map[string]*PathItem) { var _ jsonpointer.JSONPointable = (*Callback)(nil) // JSONLookup implements https://github.com/go-openapi/jsonpointer#JSONPointable -func (callback Callback) JSONLookup(token string) (interface{}, error) { +func (callback Callback) JSONLookup(token string) (any, error) { if v := callback.Value(token); v == nil { vv, _, err := jsonpointer.GetForToken(callback.Extensions, token) return vv, err @@ -195,21 +207,33 @@ func (callback Callback) JSONLookup(token string) (interface{}, error) { } } -// MarshalJSON returns the JSON encoding of Callback. -func (callback *Callback) MarshalJSON() ([]byte, error) { - m := make(map[string]interface{}, callback.Len()+len(callback.Extensions)) +// MarshalYAML returns the YAML encoding of Callback. +func (callback *Callback) MarshalYAML() (any, error) { + if callback == nil { + return nil, nil + } + m := make(map[string]any, callback.Len()+len(callback.Extensions)) for k, v := range callback.Extensions { m[k] = v } for k, v := range callback.Map() { m[k] = v } - return json.Marshal(m) + return m, nil +} + +// MarshalJSON returns the JSON encoding of Callback. +func (callback *Callback) MarshalJSON() ([]byte, error) { + callbackYaml, err := callback.MarshalYAML() + if err != nil { + return nil, err + } + return json.Marshal(callbackYaml) } // UnmarshalJSON sets Callback to a copy of data. func (callback *Callback) UnmarshalJSON(data []byte) (err error) { - var m map[string]interface{} + var m map[string]any if err = json.Unmarshal(data, &m); err != nil { return } @@ -221,7 +245,7 @@ func (callback *Callback) UnmarshalJSON(data []byte) (err error) { sort.Strings(ks) x := Callback{ - Extensions: make(map[string]interface{}), + Extensions: make(map[string]any), m: make(map[string]*PathItem, len(m)), } @@ -302,7 +326,7 @@ func (paths *Paths) Map() (m map[string]*PathItem) { var _ jsonpointer.JSONPointable = (*Paths)(nil) // JSONLookup implements https://github.com/go-openapi/jsonpointer#JSONPointable -func (paths Paths) JSONLookup(token string) (interface{}, error) { +func (paths Paths) JSONLookup(token string) (any, error) { if v := paths.Value(token); v == nil { vv, _, err := jsonpointer.GetForToken(paths.Extensions, token) return vv, err @@ -314,21 +338,33 @@ func (paths Paths) JSONLookup(token string) (interface{}, error) { } } -// MarshalJSON returns the JSON encoding of Paths. -func (paths *Paths) MarshalJSON() ([]byte, error) { - m := make(map[string]interface{}, paths.Len()+len(paths.Extensions)) +// MarshalYAML returns the YAML encoding of Paths. +func (paths *Paths) MarshalYAML() (any, error) { + if paths == nil { + return nil, nil + } + m := make(map[string]any, paths.Len()+len(paths.Extensions)) for k, v := range paths.Extensions { m[k] = v } for k, v := range paths.Map() { m[k] = v } - return json.Marshal(m) + return m, nil +} + +// MarshalJSON returns the JSON encoding of Paths. +func (paths *Paths) MarshalJSON() ([]byte, error) { + pathsYaml, err := paths.MarshalYAML() + if err != nil { + return nil, err + } + return json.Marshal(pathsYaml) } // UnmarshalJSON sets Paths to a copy of data. func (paths *Paths) UnmarshalJSON(data []byte) (err error) { - var m map[string]interface{} + var m map[string]any if err = json.Unmarshal(data, &m); err != nil { return } @@ -340,7 +376,7 @@ func (paths *Paths) UnmarshalJSON(data []byte) (err error) { sort.Strings(ks) x := Paths{ - Extensions: make(map[string]interface{}), + Extensions: make(map[string]any), m: make(map[string]*PathItem, len(m)), } diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/marsh.go b/vendor/github.com/getkin/kin-openapi/openapi3/marsh.go index 18036ae78..daa937551 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/marsh.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/marsh.go @@ -16,11 +16,19 @@ func unmarshalError(jsonUnmarshalErr error) error { return jsonUnmarshalErr } -func unmarshal(data []byte, v interface{}) error { +func unmarshal(data []byte, v any) error { + var jsonErr, yamlErr error + // See https://github.com/getkin/kin-openapi/issues/680 - if err := json.Unmarshal(data, v); err != nil { - // UnmarshalStrict(data, v) TODO: investigate how ymlv3 handles duplicate map keys - return yaml.Unmarshal(data, v) + if jsonErr = json.Unmarshal(data, v); jsonErr == nil { + return nil + } + + // UnmarshalStrict(data, v) TODO: investigate how ymlv3 handles duplicate map keys + if yamlErr = yaml.Unmarshal(data, v); yamlErr == nil { + return nil } - return nil + + // 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) } 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 e043a7c95..d4466bcf5 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/media_type.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/media_type.go @@ -13,10 +13,10 @@ import ( // MediaType is specified by OpenAPI/Swagger 3.0 standard. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#media-type-object type MediaType struct { - Extensions map[string]interface{} `json:"-" yaml:"-"` + Extensions map[string]any `json:"-" yaml:"-"` Schema *SchemaRef `json:"schema,omitempty" yaml:"schema,omitempty"` - Example interface{} `json:"example,omitempty" yaml:"example,omitempty"` + Example any `json:"example,omitempty" yaml:"example,omitempty"` Examples Examples `json:"examples,omitempty" yaml:"examples,omitempty"` Encoding map[string]*Encoding `json:"encoding,omitempty" yaml:"encoding,omitempty"` } @@ -41,7 +41,7 @@ func (mediaType *MediaType) WithSchemaRef(schema *SchemaRef) *MediaType { return mediaType } -func (mediaType *MediaType) WithExample(name string, value interface{}) *MediaType { +func (mediaType *MediaType) WithExample(name string, value any) *MediaType { example := mediaType.Examples if example == nil { example = make(map[string]*ExampleRef) @@ -65,7 +65,16 @@ func (mediaType *MediaType) WithEncoding(name string, enc *Encoding) *MediaType // MarshalJSON returns the JSON encoding of MediaType. func (mediaType MediaType) MarshalJSON() ([]byte, error) { - m := make(map[string]interface{}, 4+len(mediaType.Extensions)) + x, err := mediaType.MarshalYAML() + if err != nil { + return nil, err + } + return json.Marshal(x) +} + +// MarshalYAML returns the YAML encoding of MediaType. +func (mediaType MediaType) MarshalYAML() (any, error) { + m := make(map[string]any, 4+len(mediaType.Extensions)) for k, v := range mediaType.Extensions { m[k] = v } @@ -81,7 +90,7 @@ func (mediaType MediaType) MarshalJSON() ([]byte, error) { if x := mediaType.Encoding; len(x) != 0 { m["encoding"] = x } - return json.Marshal(m) + return m, nil } // UnmarshalJSON sets MediaType to a copy of data. @@ -149,7 +158,7 @@ func (mediaType *MediaType) Validate(ctx context.Context, opts ...ValidationOpti } // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable -func (mediaType MediaType) JSONLookup(token string) (interface{}, error) { +func (mediaType MediaType) JSONLookup(token string) (any, error) { switch token { case "schema": if mediaType.Schema != nil { diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/openapi3.go b/vendor/github.com/getkin/kin-openapi/openapi3/openapi3.go index 04df3505b..ef1592e8c 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/openapi3.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/openapi3.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "fmt" + "net/url" "github.com/go-openapi/jsonpointer" ) @@ -12,7 +13,7 @@ import ( // T is the root of an OpenAPI v3 document // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#openapi-object type T struct { - Extensions map[string]interface{} `json:"-" yaml:"-"` + Extensions map[string]any `json:"-" yaml:"-"` OpenAPI string `json:"openapi" yaml:"openapi"` // Required Components *Components `json:"components,omitempty" yaml:"components,omitempty"` @@ -24,12 +25,13 @@ type T struct { ExternalDocs *ExternalDocs `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"` visited visitedComponent + url *url.URL } var _ jsonpointer.JSONPointable = (*T)(nil) // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable -func (doc *T) JSONLookup(token string) (interface{}, error) { +func (doc *T) JSONLookup(token string) (any, error) { switch token { case "openapi": return doc.OpenAPI, nil @@ -55,7 +57,19 @@ func (doc *T) JSONLookup(token string) (interface{}, error) { // MarshalJSON returns the JSON encoding of T. func (doc *T) MarshalJSON() ([]byte, error) { - m := make(map[string]interface{}, 4+len(doc.Extensions)) + x, err := doc.MarshalYAML() + if err != nil { + return nil, err + } + return json.Marshal(x) +} + +// MarshalYAML returns the YAML encoding of T. +func (doc *T) MarshalYAML() (any, error) { + if doc == nil { + return nil, nil + } + m := make(map[string]any, 4+len(doc.Extensions)) for k, v := range doc.Extensions { m[k] = v } @@ -77,7 +91,7 @@ func (doc *T) MarshalJSON() ([]byte, error) { if x := doc.ExternalDocs; x != nil { m["externalDocs"] = x } - return json.Marshal(m) + return m, nil } // UnmarshalJSON sets T to a copy of data. diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/operation.go b/vendor/github.com/getkin/kin-openapi/openapi3/operation.go index d859a437c..40abf73c0 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/operation.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/operation.go @@ -13,7 +13,7 @@ import ( // Operation represents "operation" specified by" OpenAPI/Swagger 3.0 standard. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#operation-object type Operation struct { - Extensions map[string]interface{} `json:"-" yaml:"-"` + Extensions map[string]any `json:"-" yaml:"-"` // Optional tags for documentation. Tags []string `json:"tags,omitempty" yaml:"tags,omitempty"` @@ -58,7 +58,16 @@ func NewOperation() *Operation { // MarshalJSON returns the JSON encoding of Operation. func (operation Operation) MarshalJSON() ([]byte, error) { - m := make(map[string]interface{}, 12+len(operation.Extensions)) + x, err := operation.MarshalYAML() + if err != nil { + return nil, err + } + return json.Marshal(x) +} + +// MarshalYAML returns the YAML encoding of Operation. +func (operation Operation) MarshalYAML() (any, error) { + m := make(map[string]any, 12+len(operation.Extensions)) for k, v := range operation.Extensions { m[k] = v } @@ -96,7 +105,7 @@ func (operation Operation) MarshalJSON() ([]byte, error) { if x := operation.ExternalDocs; x != nil { m["externalDocs"] = x } - return json.Marshal(m) + return m, nil } // UnmarshalJSON sets Operation to a copy of data. @@ -127,7 +136,7 @@ func (operation *Operation) UnmarshalJSON(data []byte) error { } // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable -func (operation Operation) JSONLookup(token string) (interface{}, error) { +func (operation Operation) JSONLookup(token string) (any, error) { switch token { case "requestBody": if operation.RequestBody != nil { diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/parameter.go b/vendor/github.com/getkin/kin-openapi/openapi3/parameter.go index f5a157de2..34fe29118 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/parameter.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/parameter.go @@ -17,7 +17,7 @@ type Parameters []*ParameterRef var _ jsonpointer.JSONPointable = (*Parameters)(nil) // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable -func (p Parameters) JSONLookup(token string) (interface{}, error) { +func (p Parameters) JSONLookup(token string) (any, error) { index, err := strconv.Atoi(token) if err != nil { return nil, err @@ -72,21 +72,21 @@ func (parameters Parameters) Validate(ctx context.Context, opts ...ValidationOpt // Parameter is specified by OpenAPI/Swagger 3.0 standard. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#parameter-object type Parameter struct { - Extensions map[string]interface{} `json:"-" yaml:"-"` - - Name string `json:"name,omitempty" yaml:"name,omitempty"` - In string `json:"in,omitempty" yaml:"in,omitempty"` - Description string `json:"description,omitempty" yaml:"description,omitempty"` - Style string `json:"style,omitempty" yaml:"style,omitempty"` - Explode *bool `json:"explode,omitempty" yaml:"explode,omitempty"` - AllowEmptyValue bool `json:"allowEmptyValue,omitempty" yaml:"allowEmptyValue,omitempty"` - AllowReserved bool `json:"allowReserved,omitempty" yaml:"allowReserved,omitempty"` - Deprecated bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"` - Required bool `json:"required,omitempty" yaml:"required,omitempty"` - Schema *SchemaRef `json:"schema,omitempty" yaml:"schema,omitempty"` - Example interface{} `json:"example,omitempty" yaml:"example,omitempty"` - Examples Examples `json:"examples,omitempty" yaml:"examples,omitempty"` - Content Content `json:"content,omitempty" yaml:"content,omitempty"` + Extensions map[string]any `json:"-" yaml:"-"` + + Name string `json:"name,omitempty" yaml:"name,omitempty"` + In string `json:"in,omitempty" yaml:"in,omitempty"` + Description string `json:"description,omitempty" yaml:"description,omitempty"` + Style string `json:"style,omitempty" yaml:"style,omitempty"` + Explode *bool `json:"explode,omitempty" yaml:"explode,omitempty"` + AllowEmptyValue bool `json:"allowEmptyValue,omitempty" yaml:"allowEmptyValue,omitempty"` + AllowReserved bool `json:"allowReserved,omitempty" yaml:"allowReserved,omitempty"` + Deprecated bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"` + Required bool `json:"required,omitempty" yaml:"required,omitempty"` + Schema *SchemaRef `json:"schema,omitempty" yaml:"schema,omitempty"` + Example any `json:"example,omitempty" yaml:"example,omitempty"` + Examples Examples `json:"examples,omitempty" yaml:"examples,omitempty"` + Content Content `json:"content,omitempty" yaml:"content,omitempty"` } var _ jsonpointer.JSONPointable = (*Parameter)(nil) @@ -150,7 +150,16 @@ func (parameter *Parameter) WithSchema(value *Schema) *Parameter { // MarshalJSON returns the JSON encoding of Parameter. func (parameter Parameter) MarshalJSON() ([]byte, error) { - m := make(map[string]interface{}, 13+len(parameter.Extensions)) + x, err := parameter.MarshalYAML() + if err != nil { + return nil, err + } + return json.Marshal(x) +} + +// MarshalYAML returns the YAML encoding of Parameter. +func (parameter Parameter) MarshalYAML() (any, error) { + m := make(map[string]any, 13+len(parameter.Extensions)) for k, v := range parameter.Extensions { m[k] = v } @@ -195,7 +204,7 @@ func (parameter Parameter) MarshalJSON() ([]byte, error) { m["content"] = x } - return json.Marshal(m) + return m, nil } // UnmarshalJSON sets Parameter to a copy of data. @@ -229,7 +238,7 @@ func (parameter *Parameter) UnmarshalJSON(data []byte) error { } // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable -func (parameter Parameter) JSONLookup(token string) (interface{}, error) { +func (parameter Parameter) JSONLookup(token string) (any, error) { switch token { case "schema": if parameter.Schema != nil { diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/path_item.go b/vendor/github.com/getkin/kin-openapi/openapi3/path_item.go index e5dd0fb63..859634fe6 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/path_item.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/path_item.go @@ -11,7 +11,7 @@ import ( // PathItem is specified by OpenAPI/Swagger standard version 3. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#path-item-object type PathItem struct { - Extensions map[string]interface{} `json:"-" yaml:"-"` + Extensions map[string]any `json:"-" yaml:"-"` Ref string `json:"$ref,omitempty" yaml:"$ref,omitempty"` Summary string `json:"summary,omitempty" yaml:"summary,omitempty"` @@ -31,11 +31,20 @@ type PathItem struct { // MarshalJSON returns the JSON encoding of PathItem. func (pathItem PathItem) MarshalJSON() ([]byte, error) { + x, err := pathItem.MarshalYAML() + if err != nil { + return nil, err + } + return json.Marshal(x) +} + +// MarshalYAML returns the YAML encoding of PathItem. +func (pathItem PathItem) MarshalYAML() (any, error) { if ref := pathItem.Ref; ref != "" { - return json.Marshal(Ref{Ref: ref}) + return Ref{Ref: ref}, nil } - m := make(map[string]interface{}, 13+len(pathItem.Extensions)) + m := make(map[string]any, 13+len(pathItem.Extensions)) for k, v := range pathItem.Extensions { m[k] = v } @@ -78,7 +87,7 @@ func (pathItem PathItem) MarshalJSON() ([]byte, error) { if x := pathItem.Parameters; len(x) != 0 { m["parameters"] = x } - return json.Marshal(m) + return m, nil } // UnmarshalJSON sets PathItem to a copy of data. diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/paths.go b/vendor/github.com/getkin/kin-openapi/openapi3/paths.go index daafe71cc..76747412b 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/paths.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/paths.go @@ -10,7 +10,7 @@ import ( // Paths is specified by OpenAPI/Swagger standard version 3. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#paths-object type Paths struct { - Extensions map[string]interface{} `json:"-" yaml:"-"` + Extensions map[string]any `json:"-" yaml:"-"` m map[string]*PathItem } @@ -218,21 +218,6 @@ func (paths *Paths) validateUniqueOperationIDs() error { return nil } -// Support YAML Marshaler interface for gopkg.in/yaml -func (paths *Paths) MarshalYAML() (any, error) { - res := make(map[string]any, len(paths.Extensions)+len(paths.m)) - - for k, v := range paths.Extensions { - res[k] = v - } - - for k, v := range paths.m { - res[k] = v - } - - return res, nil -} - func normalizeTemplatedPath(path string) (string, uint, map[string]struct{}) { if strings.IndexByte(path, '{') < 0 { return path, 0, nil diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/ref.go b/vendor/github.com/getkin/kin-openapi/openapi3/ref.go index a937de4a5..07060731f 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/ref.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/ref.go @@ -1,5 +1,7 @@ package openapi3 +//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 type Ref struct { diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/refs.go b/vendor/github.com/getkin/kin-openapi/openapi3/refs.go index a7e1e3680..d337b0e3d 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/refs.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/refs.go @@ -1,10 +1,13 @@ +// Code generated by go generate; DO NOT EDIT. package openapi3 import ( "context" "encoding/json" "fmt" + "net/url" "sort" + "strings" "github.com/go-openapi/jsonpointer" "github.com/perimeterx/marshmallow" @@ -13,29 +16,55 @@ import ( // CallbackRef represents either a Callback or a $ref to a Callback. // When serializing and both fields are set, Ref is preferred over Value. type CallbackRef struct { + // Extensions only captures fields starting with 'x-' as no other fields + // are allowed by the openapi spec. + Extensions map[string]any + Ref string Value *Callback extra []string + + refPath *url.URL } var _ jsonpointer.JSONPointable = (*CallbackRef)(nil) func (x *CallbackRef) isEmpty() bool { return x == nil || x.Ref == "" && x.Value == nil } +// RefString returns the $ref value. +func (x *CallbackRef) RefString() string { return x.Ref } + +// CollectionName returns the JSON string used for a collection of these components. +func (x *CallbackRef) CollectionName() string { return "callbacks" } + +// RefPath returns the path of the $ref relative to the root document. +func (x *CallbackRef) RefPath() *url.URL { return copyURI(x.refPath) } + +func (x *CallbackRef) setRefPath(u *url.URL) { + // Once the refPath is set don't override. References can be loaded + // multiple times not all with access to the correct path info. + if x.refPath != nil { + return + } + + x.refPath = copyURI(u) +} + // MarshalYAML returns the YAML encoding of CallbackRef. -func (x CallbackRef) MarshalYAML() (interface{}, error) { +func (x CallbackRef) MarshalYAML() (any, error) { if ref := x.Ref; ref != "" { return &Ref{Ref: ref}, nil } - return x.Value, nil + return x.Value.MarshalYAML() } // MarshalJSON returns the JSON encoding of CallbackRef. func (x CallbackRef) MarshalJSON() ([]byte, error) { - if ref := x.Ref; ref != "" { - return json.Marshal(Ref{Ref: ref}) + y, err := x.MarshalYAML() + if err != nil { + return nil, err } - return json.Marshal(x.Value) + return json.Marshal(y) } // UnmarshalJSON sets CallbackRef to a copy of data. @@ -49,6 +78,14 @@ func (x *CallbackRef) UnmarshalJSON(data []byte) error { x.extra = append(x.extra, key) } sort.Strings(x.extra) + for k := range extra { + if !strings.HasPrefix(k, "x-") { + delete(extra, k) + } + } + if len(extra) != 0 { + x.Extensions = extra + } } return nil } @@ -58,8 +95,9 @@ func (x *CallbackRef) UnmarshalJSON(data []byte) error { // Validate returns an error if CallbackRef does not comply with the OpenAPI spec. func (x *CallbackRef) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) + exProhibited := getValidationOptions(ctx).schemaExtensionsInRefProhibited + var extras []string if extra := x.extra; len(extra) != 0 { - extras := make([]string, 0, len(extra)) allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed for _, ex := range extra { if allowed != nil { @@ -67,23 +105,46 @@ func (x *CallbackRef) Validate(ctx context.Context, opts ...ValidationOption) er continue } } - extras = append(extras, ex) + // extras in the Extensions checked below + if _, ok := x.Extensions[ex]; !ok { + extras = append(extras, ex) + } } - if len(extras) != 0 { - return fmt.Errorf("extra sibling fields: %+v", extras) + } + + if extra := x.Extensions; exProhibited && len(extra) != 0 { + allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed + for ex := range extra { + if allowed != nil { + if _, ok := allowed[ex]; ok { + continue + } + } + extras = append(extras, ex) } } + + if len(extras) != 0 { + return fmt.Errorf("extra sibling fields: %+v", extras) + } + if v := x.Value; v != nil { return v.Validate(ctx) } + return foundUnresolvedRef(x.Ref) } // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable -func (x *CallbackRef) JSONLookup(token string) (interface{}, error) { +func (x *CallbackRef) JSONLookup(token string) (any, error) { if token == "$ref" { return x.Ref, nil } + + if v, ok := x.Extensions[token]; ok { + return v, nil + } + ptr, _, err := jsonpointer.GetForToken(x.Value, token) return ptr, err } @@ -91,29 +152,55 @@ func (x *CallbackRef) JSONLookup(token string) (interface{}, error) { // ExampleRef represents either a Example or a $ref to a Example. // When serializing and both fields are set, Ref is preferred over Value. type ExampleRef struct { + // Extensions only captures fields starting with 'x-' as no other fields + // are allowed by the openapi spec. + Extensions map[string]any + Ref string Value *Example extra []string + + refPath *url.URL } var _ jsonpointer.JSONPointable = (*ExampleRef)(nil) func (x *ExampleRef) isEmpty() bool { return x == nil || x.Ref == "" && x.Value == nil } +// RefString returns the $ref value. +func (x *ExampleRef) RefString() string { return x.Ref } + +// CollectionName returns the JSON string used for a collection of these components. +func (x *ExampleRef) CollectionName() string { return "examples" } + +// RefPath returns the path of the $ref relative to the root document. +func (x *ExampleRef) RefPath() *url.URL { return copyURI(x.refPath) } + +func (x *ExampleRef) setRefPath(u *url.URL) { + // Once the refPath is set don't override. References can be loaded + // multiple times not all with access to the correct path info. + if x.refPath != nil { + return + } + + x.refPath = copyURI(u) +} + // MarshalYAML returns the YAML encoding of ExampleRef. -func (x ExampleRef) MarshalYAML() (interface{}, error) { +func (x ExampleRef) MarshalYAML() (any, error) { if ref := x.Ref; ref != "" { return &Ref{Ref: ref}, nil } - return x.Value, nil + return x.Value.MarshalYAML() } // MarshalJSON returns the JSON encoding of ExampleRef. func (x ExampleRef) MarshalJSON() ([]byte, error) { - if ref := x.Ref; ref != "" { - return json.Marshal(Ref{Ref: ref}) + y, err := x.MarshalYAML() + if err != nil { + return nil, err } - return x.Value.MarshalJSON() + return json.Marshal(y) } // UnmarshalJSON sets ExampleRef to a copy of data. @@ -127,6 +214,14 @@ func (x *ExampleRef) UnmarshalJSON(data []byte) error { x.extra = append(x.extra, key) } sort.Strings(x.extra) + for k := range extra { + if !strings.HasPrefix(k, "x-") { + delete(extra, k) + } + } + if len(extra) != 0 { + x.Extensions = extra + } } return nil } @@ -136,8 +231,9 @@ func (x *ExampleRef) UnmarshalJSON(data []byte) error { // Validate returns an error if ExampleRef does not comply with the OpenAPI spec. func (x *ExampleRef) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) + exProhibited := getValidationOptions(ctx).schemaExtensionsInRefProhibited + var extras []string if extra := x.extra; len(extra) != 0 { - extras := make([]string, 0, len(extra)) allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed for _, ex := range extra { if allowed != nil { @@ -145,23 +241,46 @@ func (x *ExampleRef) Validate(ctx context.Context, opts ...ValidationOption) err continue } } - extras = append(extras, ex) + // extras in the Extensions checked below + if _, ok := x.Extensions[ex]; !ok { + extras = append(extras, ex) + } } - if len(extras) != 0 { - return fmt.Errorf("extra sibling fields: %+v", extras) + } + + if extra := x.Extensions; exProhibited && len(extra) != 0 { + allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed + for ex := range extra { + if allowed != nil { + if _, ok := allowed[ex]; ok { + continue + } + } + extras = append(extras, ex) } } + + if len(extras) != 0 { + return fmt.Errorf("extra sibling fields: %+v", extras) + } + if v := x.Value; v != nil { return v.Validate(ctx) } + return foundUnresolvedRef(x.Ref) } // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable -func (x *ExampleRef) JSONLookup(token string) (interface{}, error) { +func (x *ExampleRef) JSONLookup(token string) (any, error) { if token == "$ref" { return x.Ref, nil } + + if v, ok := x.Extensions[token]; ok { + return v, nil + } + ptr, _, err := jsonpointer.GetForToken(x.Value, token) return ptr, err } @@ -169,29 +288,55 @@ func (x *ExampleRef) JSONLookup(token string) (interface{}, error) { // HeaderRef represents either a Header or a $ref to a Header. // When serializing and both fields are set, Ref is preferred over Value. type HeaderRef struct { + // Extensions only captures fields starting with 'x-' as no other fields + // are allowed by the openapi spec. + Extensions map[string]any + Ref string Value *Header extra []string + + refPath *url.URL } var _ jsonpointer.JSONPointable = (*HeaderRef)(nil) func (x *HeaderRef) isEmpty() bool { return x == nil || x.Ref == "" && x.Value == nil } +// RefString returns the $ref value. +func (x *HeaderRef) RefString() string { return x.Ref } + +// CollectionName returns the JSON string used for a collection of these components. +func (x *HeaderRef) CollectionName() string { return "headers" } + +// RefPath returns the path of the $ref relative to the root document. +func (x *HeaderRef) RefPath() *url.URL { return copyURI(x.refPath) } + +func (x *HeaderRef) setRefPath(u *url.URL) { + // Once the refPath is set don't override. References can be loaded + // multiple times not all with access to the correct path info. + if x.refPath != nil { + return + } + + x.refPath = copyURI(u) +} + // MarshalYAML returns the YAML encoding of HeaderRef. -func (x HeaderRef) MarshalYAML() (interface{}, error) { +func (x HeaderRef) MarshalYAML() (any, error) { if ref := x.Ref; ref != "" { return &Ref{Ref: ref}, nil } - return x.Value, nil + return x.Value.MarshalYAML() } // MarshalJSON returns the JSON encoding of HeaderRef. func (x HeaderRef) MarshalJSON() ([]byte, error) { - if ref := x.Ref; ref != "" { - return json.Marshal(Ref{Ref: ref}) + y, err := x.MarshalYAML() + if err != nil { + return nil, err } - return x.Value.MarshalJSON() + return json.Marshal(y) } // UnmarshalJSON sets HeaderRef to a copy of data. @@ -205,6 +350,14 @@ func (x *HeaderRef) UnmarshalJSON(data []byte) error { x.extra = append(x.extra, key) } sort.Strings(x.extra) + for k := range extra { + if !strings.HasPrefix(k, "x-") { + delete(extra, k) + } + } + if len(extra) != 0 { + x.Extensions = extra + } } return nil } @@ -214,8 +367,9 @@ func (x *HeaderRef) UnmarshalJSON(data []byte) error { // Validate returns an error if HeaderRef does not comply with the OpenAPI spec. func (x *HeaderRef) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) + exProhibited := getValidationOptions(ctx).schemaExtensionsInRefProhibited + var extras []string if extra := x.extra; len(extra) != 0 { - extras := make([]string, 0, len(extra)) allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed for _, ex := range extra { if allowed != nil { @@ -223,23 +377,46 @@ func (x *HeaderRef) Validate(ctx context.Context, opts ...ValidationOption) erro continue } } - extras = append(extras, ex) + // extras in the Extensions checked below + if _, ok := x.Extensions[ex]; !ok { + extras = append(extras, ex) + } } - if len(extras) != 0 { - return fmt.Errorf("extra sibling fields: %+v", extras) + } + + if extra := x.Extensions; exProhibited && len(extra) != 0 { + allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed + for ex := range extra { + if allowed != nil { + if _, ok := allowed[ex]; ok { + continue + } + } + extras = append(extras, ex) } } + + if len(extras) != 0 { + return fmt.Errorf("extra sibling fields: %+v", extras) + } + if v := x.Value; v != nil { return v.Validate(ctx) } + return foundUnresolvedRef(x.Ref) } // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable -func (x *HeaderRef) JSONLookup(token string) (interface{}, error) { +func (x *HeaderRef) JSONLookup(token string) (any, error) { if token == "$ref" { return x.Ref, nil } + + if v, ok := x.Extensions[token]; ok { + return v, nil + } + ptr, _, err := jsonpointer.GetForToken(x.Value, token) return ptr, err } @@ -247,29 +424,55 @@ func (x *HeaderRef) JSONLookup(token string) (interface{}, error) { // LinkRef represents either a Link or a $ref to a Link. // When serializing and both fields are set, Ref is preferred over Value. type LinkRef struct { + // Extensions only captures fields starting with 'x-' as no other fields + // are allowed by the openapi spec. + Extensions map[string]any + Ref string Value *Link extra []string + + refPath *url.URL } var _ jsonpointer.JSONPointable = (*LinkRef)(nil) func (x *LinkRef) isEmpty() bool { return x == nil || x.Ref == "" && x.Value == nil } +// RefString returns the $ref value. +func (x *LinkRef) RefString() string { return x.Ref } + +// CollectionName returns the JSON string used for a collection of these components. +func (x *LinkRef) CollectionName() string { return "links" } + +// RefPath returns the path of the $ref relative to the root document. +func (x *LinkRef) RefPath() *url.URL { return copyURI(x.refPath) } + +func (x *LinkRef) setRefPath(u *url.URL) { + // Once the refPath is set don't override. References can be loaded + // multiple times not all with access to the correct path info. + if x.refPath != nil { + return + } + + x.refPath = copyURI(u) +} + // MarshalYAML returns the YAML encoding of LinkRef. -func (x LinkRef) MarshalYAML() (interface{}, error) { +func (x LinkRef) MarshalYAML() (any, error) { if ref := x.Ref; ref != "" { return &Ref{Ref: ref}, nil } - return x.Value, nil + return x.Value.MarshalYAML() } // MarshalJSON returns the JSON encoding of LinkRef. func (x LinkRef) MarshalJSON() ([]byte, error) { - if ref := x.Ref; ref != "" { - return json.Marshal(Ref{Ref: ref}) + y, err := x.MarshalYAML() + if err != nil { + return nil, err } - return x.Value.MarshalJSON() + return json.Marshal(y) } // UnmarshalJSON sets LinkRef to a copy of data. @@ -283,6 +486,14 @@ func (x *LinkRef) UnmarshalJSON(data []byte) error { x.extra = append(x.extra, key) } sort.Strings(x.extra) + for k := range extra { + if !strings.HasPrefix(k, "x-") { + delete(extra, k) + } + } + if len(extra) != 0 { + x.Extensions = extra + } } return nil } @@ -292,8 +503,9 @@ func (x *LinkRef) UnmarshalJSON(data []byte) error { // Validate returns an error if LinkRef does not comply with the OpenAPI spec. func (x *LinkRef) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) + exProhibited := getValidationOptions(ctx).schemaExtensionsInRefProhibited + var extras []string if extra := x.extra; len(extra) != 0 { - extras := make([]string, 0, len(extra)) allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed for _, ex := range extra { if allowed != nil { @@ -301,23 +513,46 @@ func (x *LinkRef) Validate(ctx context.Context, opts ...ValidationOption) error continue } } - extras = append(extras, ex) + // extras in the Extensions checked below + if _, ok := x.Extensions[ex]; !ok { + extras = append(extras, ex) + } } - if len(extras) != 0 { - return fmt.Errorf("extra sibling fields: %+v", extras) + } + + if extra := x.Extensions; exProhibited && len(extra) != 0 { + allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed + for ex := range extra { + if allowed != nil { + if _, ok := allowed[ex]; ok { + continue + } + } + extras = append(extras, ex) } } + + if len(extras) != 0 { + return fmt.Errorf("extra sibling fields: %+v", extras) + } + if v := x.Value; v != nil { return v.Validate(ctx) } + return foundUnresolvedRef(x.Ref) } // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable -func (x *LinkRef) JSONLookup(token string) (interface{}, error) { +func (x *LinkRef) JSONLookup(token string) (any, error) { if token == "$ref" { return x.Ref, nil } + + if v, ok := x.Extensions[token]; ok { + return v, nil + } + ptr, _, err := jsonpointer.GetForToken(x.Value, token) return ptr, err } @@ -325,29 +560,55 @@ func (x *LinkRef) JSONLookup(token string) (interface{}, error) { // ParameterRef represents either a Parameter or a $ref to a Parameter. // When serializing and both fields are set, Ref is preferred over Value. type ParameterRef struct { + // Extensions only captures fields starting with 'x-' as no other fields + // are allowed by the openapi spec. + Extensions map[string]any + Ref string Value *Parameter extra []string + + refPath *url.URL } var _ jsonpointer.JSONPointable = (*ParameterRef)(nil) func (x *ParameterRef) isEmpty() bool { return x == nil || x.Ref == "" && x.Value == nil } +// RefString returns the $ref value. +func (x *ParameterRef) RefString() string { return x.Ref } + +// CollectionName returns the JSON string used for a collection of these components. +func (x *ParameterRef) CollectionName() string { return "parameters" } + +// RefPath returns the path of the $ref relative to the root document. +func (x *ParameterRef) RefPath() *url.URL { return copyURI(x.refPath) } + +func (x *ParameterRef) setRefPath(u *url.URL) { + // Once the refPath is set don't override. References can be loaded + // multiple times not all with access to the correct path info. + if x.refPath != nil { + return + } + + x.refPath = copyURI(u) +} + // MarshalYAML returns the YAML encoding of ParameterRef. -func (x ParameterRef) MarshalYAML() (interface{}, error) { +func (x ParameterRef) MarshalYAML() (any, error) { if ref := x.Ref; ref != "" { return &Ref{Ref: ref}, nil } - return x.Value, nil + return x.Value.MarshalYAML() } // MarshalJSON returns the JSON encoding of ParameterRef. func (x ParameterRef) MarshalJSON() ([]byte, error) { - if ref := x.Ref; ref != "" { - return json.Marshal(Ref{Ref: ref}) + y, err := x.MarshalYAML() + if err != nil { + return nil, err } - return x.Value.MarshalJSON() + return json.Marshal(y) } // UnmarshalJSON sets ParameterRef to a copy of data. @@ -361,6 +622,14 @@ func (x *ParameterRef) UnmarshalJSON(data []byte) error { x.extra = append(x.extra, key) } sort.Strings(x.extra) + for k := range extra { + if !strings.HasPrefix(k, "x-") { + delete(extra, k) + } + } + if len(extra) != 0 { + x.Extensions = extra + } } return nil } @@ -370,8 +639,9 @@ func (x *ParameterRef) UnmarshalJSON(data []byte) error { // Validate returns an error if ParameterRef does not comply with the OpenAPI spec. func (x *ParameterRef) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) + exProhibited := getValidationOptions(ctx).schemaExtensionsInRefProhibited + var extras []string if extra := x.extra; len(extra) != 0 { - extras := make([]string, 0, len(extra)) allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed for _, ex := range extra { if allowed != nil { @@ -379,23 +649,46 @@ func (x *ParameterRef) Validate(ctx context.Context, opts ...ValidationOption) e continue } } - extras = append(extras, ex) + // extras in the Extensions checked below + if _, ok := x.Extensions[ex]; !ok { + extras = append(extras, ex) + } } - if len(extras) != 0 { - return fmt.Errorf("extra sibling fields: %+v", extras) + } + + if extra := x.Extensions; exProhibited && len(extra) != 0 { + allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed + for ex := range extra { + if allowed != nil { + if _, ok := allowed[ex]; ok { + continue + } + } + extras = append(extras, ex) } } + + if len(extras) != 0 { + return fmt.Errorf("extra sibling fields: %+v", extras) + } + if v := x.Value; v != nil { return v.Validate(ctx) } + return foundUnresolvedRef(x.Ref) } // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable -func (x *ParameterRef) JSONLookup(token string) (interface{}, error) { +func (x *ParameterRef) JSONLookup(token string) (any, error) { if token == "$ref" { return x.Ref, nil } + + if v, ok := x.Extensions[token]; ok { + return v, nil + } + ptr, _, err := jsonpointer.GetForToken(x.Value, token) return ptr, err } @@ -403,29 +696,55 @@ func (x *ParameterRef) JSONLookup(token string) (interface{}, error) { // RequestBodyRef represents either a RequestBody or a $ref to a RequestBody. // When serializing and both fields are set, Ref is preferred over Value. type RequestBodyRef struct { + // Extensions only captures fields starting with 'x-' as no other fields + // are allowed by the openapi spec. + Extensions map[string]any + Ref string Value *RequestBody extra []string + + refPath *url.URL } var _ jsonpointer.JSONPointable = (*RequestBodyRef)(nil) func (x *RequestBodyRef) isEmpty() bool { return x == nil || x.Ref == "" && x.Value == nil } +// RefString returns the $ref value. +func (x *RequestBodyRef) RefString() string { return x.Ref } + +// CollectionName returns the JSON string used for a collection of these components. +func (x *RequestBodyRef) CollectionName() string { return "requestBodies" } + +// RefPath returns the path of the $ref relative to the root document. +func (x *RequestBodyRef) RefPath() *url.URL { return copyURI(x.refPath) } + +func (x *RequestBodyRef) setRefPath(u *url.URL) { + // Once the refPath is set don't override. References can be loaded + // multiple times not all with access to the correct path info. + if x.refPath != nil { + return + } + + x.refPath = copyURI(u) +} + // MarshalYAML returns the YAML encoding of RequestBodyRef. -func (x RequestBodyRef) MarshalYAML() (interface{}, error) { +func (x RequestBodyRef) MarshalYAML() (any, error) { if ref := x.Ref; ref != "" { return &Ref{Ref: ref}, nil } - return x.Value, nil + return x.Value.MarshalYAML() } // MarshalJSON returns the JSON encoding of RequestBodyRef. func (x RequestBodyRef) MarshalJSON() ([]byte, error) { - if ref := x.Ref; ref != "" { - return json.Marshal(Ref{Ref: ref}) + y, err := x.MarshalYAML() + if err != nil { + return nil, err } - return x.Value.MarshalJSON() + return json.Marshal(y) } // UnmarshalJSON sets RequestBodyRef to a copy of data. @@ -439,6 +758,14 @@ func (x *RequestBodyRef) UnmarshalJSON(data []byte) error { x.extra = append(x.extra, key) } sort.Strings(x.extra) + for k := range extra { + if !strings.HasPrefix(k, "x-") { + delete(extra, k) + } + } + if len(extra) != 0 { + x.Extensions = extra + } } return nil } @@ -448,8 +775,9 @@ func (x *RequestBodyRef) UnmarshalJSON(data []byte) error { // Validate returns an error if RequestBodyRef does not comply with the OpenAPI spec. func (x *RequestBodyRef) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) + exProhibited := getValidationOptions(ctx).schemaExtensionsInRefProhibited + var extras []string if extra := x.extra; len(extra) != 0 { - extras := make([]string, 0, len(extra)) allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed for _, ex := range extra { if allowed != nil { @@ -457,23 +785,46 @@ func (x *RequestBodyRef) Validate(ctx context.Context, opts ...ValidationOption) continue } } - extras = append(extras, ex) + // extras in the Extensions checked below + if _, ok := x.Extensions[ex]; !ok { + extras = append(extras, ex) + } } - if len(extras) != 0 { - return fmt.Errorf("extra sibling fields: %+v", extras) + } + + if extra := x.Extensions; exProhibited && len(extra) != 0 { + allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed + for ex := range extra { + if allowed != nil { + if _, ok := allowed[ex]; ok { + continue + } + } + extras = append(extras, ex) } } + + if len(extras) != 0 { + return fmt.Errorf("extra sibling fields: %+v", extras) + } + if v := x.Value; v != nil { return v.Validate(ctx) } + return foundUnresolvedRef(x.Ref) } // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable -func (x *RequestBodyRef) JSONLookup(token string) (interface{}, error) { +func (x *RequestBodyRef) JSONLookup(token string) (any, error) { if token == "$ref" { return x.Ref, nil } + + if v, ok := x.Extensions[token]; ok { + return v, nil + } + ptr, _, err := jsonpointer.GetForToken(x.Value, token) return ptr, err } @@ -481,29 +832,55 @@ func (x *RequestBodyRef) JSONLookup(token string) (interface{}, error) { // ResponseRef represents either a Response or a $ref to a Response. // When serializing and both fields are set, Ref is preferred over Value. type ResponseRef struct { + // Extensions only captures fields starting with 'x-' as no other fields + // are allowed by the openapi spec. + Extensions map[string]any + Ref string Value *Response extra []string + + refPath *url.URL } var _ jsonpointer.JSONPointable = (*ResponseRef)(nil) func (x *ResponseRef) isEmpty() bool { return x == nil || x.Ref == "" && x.Value == nil } +// RefString returns the $ref value. +func (x *ResponseRef) RefString() string { return x.Ref } + +// CollectionName returns the JSON string used for a collection of these components. +func (x *ResponseRef) CollectionName() string { return "responses" } + +// RefPath returns the path of the $ref relative to the root document. +func (x *ResponseRef) RefPath() *url.URL { return copyURI(x.refPath) } + +func (x *ResponseRef) setRefPath(u *url.URL) { + // Once the refPath is set don't override. References can be loaded + // multiple times not all with access to the correct path info. + if x.refPath != nil { + return + } + + x.refPath = copyURI(u) +} + // MarshalYAML returns the YAML encoding of ResponseRef. -func (x ResponseRef) MarshalYAML() (interface{}, error) { +func (x ResponseRef) MarshalYAML() (any, error) { if ref := x.Ref; ref != "" { return &Ref{Ref: ref}, nil } - return x.Value, nil + return x.Value.MarshalYAML() } // MarshalJSON returns the JSON encoding of ResponseRef. func (x ResponseRef) MarshalJSON() ([]byte, error) { - if ref := x.Ref; ref != "" { - return json.Marshal(Ref{Ref: ref}) + y, err := x.MarshalYAML() + if err != nil { + return nil, err } - return x.Value.MarshalJSON() + return json.Marshal(y) } // UnmarshalJSON sets ResponseRef to a copy of data. @@ -517,6 +894,14 @@ func (x *ResponseRef) UnmarshalJSON(data []byte) error { x.extra = append(x.extra, key) } sort.Strings(x.extra) + for k := range extra { + if !strings.HasPrefix(k, "x-") { + delete(extra, k) + } + } + if len(extra) != 0 { + x.Extensions = extra + } } return nil } @@ -526,8 +911,9 @@ func (x *ResponseRef) UnmarshalJSON(data []byte) error { // Validate returns an error if ResponseRef does not comply with the OpenAPI spec. func (x *ResponseRef) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) + exProhibited := getValidationOptions(ctx).schemaExtensionsInRefProhibited + var extras []string if extra := x.extra; len(extra) != 0 { - extras := make([]string, 0, len(extra)) allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed for _, ex := range extra { if allowed != nil { @@ -535,23 +921,46 @@ func (x *ResponseRef) Validate(ctx context.Context, opts ...ValidationOption) er continue } } - extras = append(extras, ex) + // extras in the Extensions checked below + if _, ok := x.Extensions[ex]; !ok { + extras = append(extras, ex) + } } - if len(extras) != 0 { - return fmt.Errorf("extra sibling fields: %+v", extras) + } + + if extra := x.Extensions; exProhibited && len(extra) != 0 { + allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed + for ex := range extra { + if allowed != nil { + if _, ok := allowed[ex]; ok { + continue + } + } + extras = append(extras, ex) } } + + if len(extras) != 0 { + return fmt.Errorf("extra sibling fields: %+v", extras) + } + if v := x.Value; v != nil { return v.Validate(ctx) } + return foundUnresolvedRef(x.Ref) } // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable -func (x *ResponseRef) JSONLookup(token string) (interface{}, error) { +func (x *ResponseRef) JSONLookup(token string) (any, error) { if token == "$ref" { return x.Ref, nil } + + if v, ok := x.Extensions[token]; ok { + return v, nil + } + ptr, _, err := jsonpointer.GetForToken(x.Value, token) return ptr, err } @@ -559,29 +968,55 @@ func (x *ResponseRef) JSONLookup(token string) (interface{}, error) { // SchemaRef represents either a Schema or a $ref to a Schema. // When serializing and both fields are set, Ref is preferred over Value. type SchemaRef struct { + // Extensions only captures fields starting with 'x-' as no other fields + // are allowed by the openapi spec. + Extensions map[string]any + Ref string Value *Schema extra []string + + refPath *url.URL } var _ jsonpointer.JSONPointable = (*SchemaRef)(nil) func (x *SchemaRef) isEmpty() bool { return x == nil || x.Ref == "" && x.Value == nil } +// RefString returns the $ref value. +func (x *SchemaRef) RefString() string { return x.Ref } + +// CollectionName returns the JSON string used for a collection of these components. +func (x *SchemaRef) CollectionName() string { return "schemas" } + +// RefPath returns the path of the $ref relative to the root document. +func (x *SchemaRef) RefPath() *url.URL { return copyURI(x.refPath) } + +func (x *SchemaRef) setRefPath(u *url.URL) { + // Once the refPath is set don't override. References can be loaded + // multiple times not all with access to the correct path info. + if x.refPath != nil { + return + } + + x.refPath = copyURI(u) +} + // MarshalYAML returns the YAML encoding of SchemaRef. -func (x SchemaRef) MarshalYAML() (interface{}, error) { +func (x SchemaRef) MarshalYAML() (any, error) { if ref := x.Ref; ref != "" { return &Ref{Ref: ref}, nil } - return x.Value, nil + return x.Value.MarshalYAML() } // MarshalJSON returns the JSON encoding of SchemaRef. func (x SchemaRef) MarshalJSON() ([]byte, error) { - if ref := x.Ref; ref != "" { - return json.Marshal(Ref{Ref: ref}) + y, err := x.MarshalYAML() + if err != nil { + return nil, err } - return x.Value.MarshalJSON() + return json.Marshal(y) } // UnmarshalJSON sets SchemaRef to a copy of data. @@ -595,6 +1030,14 @@ func (x *SchemaRef) UnmarshalJSON(data []byte) error { x.extra = append(x.extra, key) } sort.Strings(x.extra) + for k := range extra { + if !strings.HasPrefix(k, "x-") { + delete(extra, k) + } + } + if len(extra) != 0 { + x.Extensions = extra + } } return nil } @@ -604,8 +1047,9 @@ func (x *SchemaRef) UnmarshalJSON(data []byte) error { // Validate returns an error if SchemaRef does not comply with the OpenAPI spec. func (x *SchemaRef) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) + exProhibited := getValidationOptions(ctx).schemaExtensionsInRefProhibited + var extras []string if extra := x.extra; len(extra) != 0 { - extras := make([]string, 0, len(extra)) allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed for _, ex := range extra { if allowed != nil { @@ -613,23 +1057,46 @@ func (x *SchemaRef) Validate(ctx context.Context, opts ...ValidationOption) erro continue } } - extras = append(extras, ex) + // extras in the Extensions checked below + if _, ok := x.Extensions[ex]; !ok { + extras = append(extras, ex) + } } - if len(extras) != 0 { - return fmt.Errorf("extra sibling fields: %+v", extras) + } + + if extra := x.Extensions; exProhibited && len(extra) != 0 { + allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed + for ex := range extra { + if allowed != nil { + if _, ok := allowed[ex]; ok { + continue + } + } + extras = append(extras, ex) } } + + if len(extras) != 0 { + return fmt.Errorf("extra sibling fields: %+v", extras) + } + if v := x.Value; v != nil { return v.Validate(ctx) } + return foundUnresolvedRef(x.Ref) } // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable -func (x *SchemaRef) JSONLookup(token string) (interface{}, error) { +func (x *SchemaRef) JSONLookup(token string) (any, error) { if token == "$ref" { return x.Ref, nil } + + if v, ok := x.Extensions[token]; ok { + return v, nil + } + ptr, _, err := jsonpointer.GetForToken(x.Value, token) return ptr, err } @@ -637,29 +1104,55 @@ func (x *SchemaRef) JSONLookup(token string) (interface{}, error) { // SecuritySchemeRef represents either a SecurityScheme or a $ref to a SecurityScheme. // When serializing and both fields are set, Ref is preferred over Value. type SecuritySchemeRef struct { + // Extensions only captures fields starting with 'x-' as no other fields + // are allowed by the openapi spec. + Extensions map[string]any + Ref string Value *SecurityScheme extra []string + + refPath *url.URL } var _ jsonpointer.JSONPointable = (*SecuritySchemeRef)(nil) func (x *SecuritySchemeRef) isEmpty() bool { return x == nil || x.Ref == "" && x.Value == nil } +// RefString returns the $ref value. +func (x *SecuritySchemeRef) RefString() string { return x.Ref } + +// CollectionName returns the JSON string used for a collection of these components. +func (x *SecuritySchemeRef) CollectionName() string { return "securitySchemes" } + +// RefPath returns the path of the $ref relative to the root document. +func (x *SecuritySchemeRef) RefPath() *url.URL { return copyURI(x.refPath) } + +func (x *SecuritySchemeRef) setRefPath(u *url.URL) { + // Once the refPath is set don't override. References can be loaded + // multiple times not all with access to the correct path info. + if x.refPath != nil { + return + } + + x.refPath = copyURI(u) +} + // MarshalYAML returns the YAML encoding of SecuritySchemeRef. -func (x SecuritySchemeRef) MarshalYAML() (interface{}, error) { +func (x SecuritySchemeRef) MarshalYAML() (any, error) { if ref := x.Ref; ref != "" { return &Ref{Ref: ref}, nil } - return x.Value, nil + return x.Value.MarshalYAML() } // MarshalJSON returns the JSON encoding of SecuritySchemeRef. func (x SecuritySchemeRef) MarshalJSON() ([]byte, error) { - if ref := x.Ref; ref != "" { - return json.Marshal(Ref{Ref: ref}) + y, err := x.MarshalYAML() + if err != nil { + return nil, err } - return x.Value.MarshalJSON() + return json.Marshal(y) } // UnmarshalJSON sets SecuritySchemeRef to a copy of data. @@ -673,6 +1166,14 @@ func (x *SecuritySchemeRef) UnmarshalJSON(data []byte) error { x.extra = append(x.extra, key) } sort.Strings(x.extra) + for k := range extra { + if !strings.HasPrefix(k, "x-") { + delete(extra, k) + } + } + if len(extra) != 0 { + x.Extensions = extra + } } return nil } @@ -682,8 +1183,9 @@ func (x *SecuritySchemeRef) UnmarshalJSON(data []byte) error { // Validate returns an error if SecuritySchemeRef does not comply with the OpenAPI spec. func (x *SecuritySchemeRef) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) + exProhibited := getValidationOptions(ctx).schemaExtensionsInRefProhibited + var extras []string if extra := x.extra; len(extra) != 0 { - extras := make([]string, 0, len(extra)) allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed for _, ex := range extra { if allowed != nil { @@ -691,23 +1193,46 @@ func (x *SecuritySchemeRef) Validate(ctx context.Context, opts ...ValidationOpti continue } } - extras = append(extras, ex) + // extras in the Extensions checked below + if _, ok := x.Extensions[ex]; !ok { + extras = append(extras, ex) + } } - if len(extras) != 0 { - return fmt.Errorf("extra sibling fields: %+v", extras) + } + + if extra := x.Extensions; exProhibited && len(extra) != 0 { + allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed + for ex := range extra { + if allowed != nil { + if _, ok := allowed[ex]; ok { + continue + } + } + extras = append(extras, ex) } } + + if len(extras) != 0 { + return fmt.Errorf("extra sibling fields: %+v", extras) + } + if v := x.Value; v != nil { return v.Validate(ctx) } + return foundUnresolvedRef(x.Ref) } // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable -func (x *SecuritySchemeRef) JSONLookup(token string) (interface{}, error) { +func (x *SecuritySchemeRef) JSONLookup(token string) (any, error) { if token == "$ref" { return x.Ref, nil } + + if v, ok := x.Extensions[token]; ok { + return v, nil + } + ptr, _, err := jsonpointer.GetForToken(x.Value, token) return ptr, err } diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/refs.tmpl b/vendor/github.com/getkin/kin-openapi/openapi3/refs.tmpl new file mode 100644 index 000000000..a3f5bdab7 --- /dev/null +++ b/vendor/github.com/getkin/kin-openapi/openapi3/refs.tmpl @@ -0,0 +1,151 @@ +// Code generated by go generate; DO NOT EDIT. +package {{ .Package }} + +import ( + "context" + "encoding/json" + "fmt" + "net/url" + "sort" + "strings" + + "github.com/go-openapi/jsonpointer" + "github.com/perimeterx/marshmallow" +) +{{ range $type := .Types }} +// {{ $type.Name }}Ref represents either a {{ $type.Name }} or a $ref to a {{ $type.Name }}. +// When serializing and both fields are set, Ref is preferred over Value. +type {{ $type.Name }}Ref struct { + // Extensions only captures fields starting with 'x-' as no other fields + // are allowed by the openapi spec. + Extensions map[string]any + + Ref string + Value *{{ $type.Name }} + extra []string + + refPath *url.URL +} + +var _ jsonpointer.JSONPointable = (*{{ $type.Name }}Ref)(nil) + +func (x *{{ $type.Name }}Ref) isEmpty() bool { return x == nil || x.Ref == "" && x.Value == nil } + +// RefString returns the $ref value. +func (x *{{ $type.Name }}Ref) RefString() string { return x.Ref } + +// CollectionName returns the JSON string used for a collection of these components. +func (x *{{ $type.Name }}Ref) CollectionName() string { return "{{ $type.CollectionName }}" } + +// RefPath returns the path of the $ref relative to the root document. +func (x *{{ $type.Name }}Ref) RefPath() *url.URL { return copyURI(x.refPath) } + +func (x *{{ $type.Name }}Ref) setRefPath(u *url.URL) { + // Once the refPath is set don't override. References can be loaded + // multiple times not all with access to the correct path info. + if x.refPath != nil { + return + } + + x.refPath = copyURI(u) +} + +// 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}, nil + } + return x.Value.MarshalYAML() +} + +// MarshalJSON returns the JSON encoding of {{ $type.Name }}Ref. +func (x {{ $type.Name }}Ref) MarshalJSON() ([]byte, error) { + y, err := x.MarshalYAML() + if err != nil { + return nil, err + } + return json.Marshal(y) +} + +// UnmarshalJSON sets {{ $type.Name }}Ref to a copy of data. +func (x *{{ $type.Name }}Ref) UnmarshalJSON(data []byte) error { + var refOnly Ref + if extra, err := marshmallow.Unmarshal(data, &refOnly, marshmallow.WithExcludeKnownFieldsFromMap(true)); err == nil && refOnly.Ref != "" { + x.Ref = refOnly.Ref + if len(extra) != 0 { + x.extra = make([]string, 0, len(extra)) + for key := range extra { + x.extra = append(x.extra, key) + } + sort.Strings(x.extra) + for k := range extra { + if !strings.HasPrefix(k, "x-") { + delete(extra, k) + } + } + if len(extra) != 0 { + x.Extensions = extra + } + } + return nil + } + return json.Unmarshal(data, &x.Value) +} + +// Validate returns an error if {{ $type.Name }}Ref does not comply with the OpenAPI spec. +func (x *{{ $type.Name }}Ref) Validate(ctx context.Context, opts ...ValidationOption) error { + ctx = WithValidationOptions(ctx, opts...) + exProhibited := getValidationOptions(ctx).schemaExtensionsInRefProhibited + var extras []string + if extra := x.extra; len(extra) != 0 { + allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed + for _, ex := range extra { + if allowed != nil { + if _, ok := allowed[ex]; ok { + continue + } + } + // extras in the Extensions checked below + if _, ok := x.Extensions[ex]; !ok { + extras = append(extras, ex) + } + } + } + + if extra := x.Extensions; exProhibited && len(extra) != 0 { + allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed + for ex := range extra { + if allowed != nil { + if _, ok := allowed[ex]; ok { + continue + } + } + extras = append(extras, ex) + } + } + + if len(extras) != 0 { + return fmt.Errorf("extra sibling fields: %+v", extras) + } + + if v := x.Value; v != nil { + return v.Validate(ctx) + } + + return foundUnresolvedRef(x.Ref) +} + +// JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable +func (x *{{ $type.Name }}Ref) JSONLookup(token string) (any, error) { + if token == "$ref" { + return x.Ref, nil + } + + if v, ok := x.Extensions[token]; ok { + return v, nil + } + + ptr, _, err := jsonpointer.GetForToken(x.Value, token) + return ptr, err +} +{{ end -}} diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/refs_test.tmpl b/vendor/github.com/getkin/kin-openapi/openapi3/refs_test.tmpl new file mode 100644 index 000000000..634fccf6f --- /dev/null +++ b/vendor/github.com/getkin/kin-openapi/openapi3/refs_test.tmpl @@ -0,0 +1,54 @@ +// Code generated by go generate; DO NOT EDIT. +package {{ .Package }} + +import ( + "context" + "encoding/json" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) +{{ range $type := .Types }} +func Test{{ $type.Name }}Ref_Extensions(t *testing.T) { + data := []byte(`{"$ref":"#/components/schemas/Pet","something":"integer","x-order":1}`) + + ref := {{ $type.Name }}Ref{} + err := json.Unmarshal(data, &ref) + assert.NoError(t, err) + + // captures extension + assert.Equal(t, "#/components/schemas/Pet", ref.Ref) + assert.Equal(t, float64(1), ref.Extensions["x-order"]) + + // does not capture non-extensions + assert.Nil(t, ref.Extensions["something"]) + + // validation + err = ref.Validate(context.Background()) + require.EqualError(t, err, "extra sibling fields: [something]") + + err = ref.Validate(context.Background(), ProhibitExtensionsWithRef()) + require.EqualError(t, err, "extra sibling fields: [something x-order]") + + err = ref.Validate(context.Background(), AllowExtraSiblingFields("something")) + assert.ErrorContains(t, err, "found unresolved ref") // expected since value not defined + + // non-extension not json lookable + _, err = ref.JSONLookup("something") + assert.Error(t, err) +{{ if ne $type.Name "Header" }} + t.Run("extentions in value", func(t *testing.T) { + ref.Value = &{{ $type.Name }}{Extensions: map[string]any{}} + ref.Value.Extensions["x-order"] = 2.0 + + // prefers the value next to the \$ref over the one in the \$ref. + v, err := ref.JSONLookup("x-order") + assert.NoError(t, err) + assert.Equal(t, float64(1), v) + }) +{{ else }} + // Header does not have its own extensions. +{{ end -}} +} +{{ end -}} 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 acd2d0e8c..6d4b8185e 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/request_body.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/request_body.go @@ -9,7 +9,7 @@ import ( // RequestBody is specified by OpenAPI/Swagger 3.0 standard. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#request-body-object type RequestBody struct { - Extensions map[string]interface{} `json:"-" yaml:"-"` + Extensions map[string]any `json:"-" yaml:"-"` Description string `json:"description,omitempty" yaml:"description,omitempty"` Required bool `json:"required,omitempty" yaml:"required,omitempty"` @@ -75,7 +75,16 @@ func (requestBody *RequestBody) GetMediaType(mediaType string) *MediaType { // MarshalJSON returns the JSON encoding of RequestBody. func (requestBody RequestBody) MarshalJSON() ([]byte, error) { - m := make(map[string]interface{}, 3+len(requestBody.Extensions)) + x, err := requestBody.MarshalYAML() + if err != nil { + return nil, err + } + return json.Marshal(x) +} + +// MarshalYAML returns the YAML encoding of RequestBody. +func (requestBody RequestBody) MarshalYAML() (any, error) { + m := make(map[string]any, 3+len(requestBody.Extensions)) for k, v := range requestBody.Extensions { m[k] = v } @@ -88,7 +97,7 @@ func (requestBody RequestBody) MarshalJSON() ([]byte, error) { if x := requestBody.Content; true { m["content"] = x } - return json.Marshal(m) + return m, nil } // UnmarshalJSON sets RequestBody to a copy of data. diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/response.go b/vendor/github.com/getkin/kin-openapi/openapi3/response.go index f69c237b3..af8fda6f7 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/response.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/response.go @@ -11,7 +11,7 @@ import ( // Responses is specified by OpenAPI/Swagger 3.0 standard. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#responses-object type Responses struct { - Extensions map[string]interface{} `json:"-" yaml:"-"` + Extensions map[string]any `json:"-" yaml:"-"` m map[string]*ResponseRef } @@ -98,25 +98,10 @@ func (responses *Responses) Validate(ctx context.Context, opts ...ValidationOpti return validateExtensions(ctx, responses.Extensions) } -// Support YAML Marshaler interface for gopkg.in/yaml -func (responses *Responses) MarshalYAML() (any, error) { - res := make(map[string]any, len(responses.Extensions)+len(responses.m)) - - for k, v := range responses.Extensions { - res[k] = v - } - - for k, v := range responses.m { - res[k] = v - } - - return res, nil -} - // Response is specified by OpenAPI/Swagger 3.0 standard. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#response-object type Response struct { - Extensions map[string]interface{} `json:"-" yaml:"-"` + Extensions map[string]any `json:"-" yaml:"-"` Description *string `json:"description,omitempty" yaml:"description,omitempty"` Headers Headers `json:"headers,omitempty" yaml:"headers,omitempty"` @@ -150,7 +135,16 @@ func (response *Response) WithJSONSchemaRef(schema *SchemaRef) *Response { // MarshalJSON returns the JSON encoding of Response. func (response Response) MarshalJSON() ([]byte, error) { - m := make(map[string]interface{}, 4+len(response.Extensions)) + x, err := response.MarshalYAML() + if err != nil { + return nil, err + } + return json.Marshal(x) +} + +// MarshalYAML returns the YAML encoding of Response. +func (response Response) MarshalYAML() (any, error) { + m := make(map[string]any, 4+len(response.Extensions)) for k, v := range response.Extensions { m[k] = v } @@ -166,7 +160,7 @@ func (response Response) MarshalJSON() ([]byte, error) { if x := response.Links; len(x) != 0 { m["links"] = x } - return json.Marshal(m) + return m, nil } // UnmarshalJSON sets Response to a copy of data. diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/schema.go b/vendor/github.com/getkin/kin-openapi/openapi3/schema.go index ae28afef7..7be6bd38e 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/schema.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/schema.go @@ -28,12 +28,6 @@ const ( TypeObject = "object" TypeString = "string" TypeNull = "null" - - // constants for integer formats - formatMinInt32 = float64(math.MinInt32) - formatMaxInt32 = float64(math.MaxInt32) - formatMinInt64 = float64(math.MinInt64) - formatMaxInt64 = float64(math.MaxInt64) ) var ( @@ -66,7 +60,7 @@ type SchemaRefs []*SchemaRef var _ jsonpointer.JSONPointable = (*SchemaRefs)(nil) // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable -func (s SchemaRefs) JSONLookup(token string) (interface{}, error) { +func (s SchemaRefs) JSONLookup(token string) (any, error) { i, err := strconv.ParseUint(token, 10, 64) if err != nil { return nil, err @@ -87,7 +81,7 @@ func (s SchemaRefs) JSONLookup(token string) (interface{}, error) { // Schema is specified by OpenAPI/Swagger 3.0 standard. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#schema-object type Schema struct { - Extensions map[string]interface{} `json:"-" yaml:"-"` + Extensions map[string]any `json:"-" yaml:"-"` OneOf SchemaRefs `json:"oneOf,omitempty" yaml:"oneOf,omitempty"` AnyOf SchemaRefs `json:"anyOf,omitempty" yaml:"anyOf,omitempty"` @@ -97,9 +91,9 @@ type Schema struct { Title string `json:"title,omitempty" yaml:"title,omitempty"` Format string `json:"format,omitempty" yaml:"format,omitempty"` Description string `json:"description,omitempty" yaml:"description,omitempty"` - Enum []interface{} `json:"enum,omitempty" yaml:"enum,omitempty"` - Default interface{} `json:"default,omitempty" yaml:"default,omitempty"` - Example interface{} `json:"example,omitempty" yaml:"example,omitempty"` + Enum []any `json:"enum,omitempty" yaml:"enum,omitempty"` + Default any `json:"default,omitempty" yaml:"default,omitempty"` + Example any `json:"example,omitempty" yaml:"example,omitempty"` ExternalDocs *ExternalDocs `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"` // Array-related, here for struct compactness @@ -180,7 +174,7 @@ func (pTypes *Types) MarshalJSON() ([]byte, error) { return json.Marshal(x) } -func (pTypes *Types) MarshalYAML() (interface{}, error) { +func (pTypes *Types) MarshalYAML() (any, error) { if pTypes == nil { return nil, nil } @@ -214,7 +208,7 @@ type AdditionalProperties struct { } // MarshalYAML returns the YAML encoding of AdditionalProperties. -func (addProps AdditionalProperties) MarshalYAML() (interface{}, error) { +func (addProps AdditionalProperties) MarshalYAML() (any, error) { if x := addProps.Has; x != nil { if *x { return true, nil @@ -222,28 +216,23 @@ func (addProps AdditionalProperties) MarshalYAML() (interface{}, error) { return false, nil } if x := addProps.Schema; x != nil { - return x.Value, nil + return x.MarshalYAML() } return nil, nil } // MarshalJSON returns the JSON encoding of AdditionalProperties. func (addProps AdditionalProperties) MarshalJSON() ([]byte, error) { - if x := addProps.Has; x != nil { - if *x { - return []byte("true"), nil - } - return []byte("false"), nil - } - if x := addProps.Schema; x != nil { - return json.Marshal(x) + x, err := addProps.MarshalYAML() + if err != nil { + return nil, err } - return nil, nil + return json.Marshal(x) } // UnmarshalJSON sets AdditionalProperties to a copy of data. func (addProps *AdditionalProperties) UnmarshalJSON(data []byte) error { - var x interface{} + var x any if err := json.Unmarshal(data, &x); err != nil { return unmarshalError(err) } @@ -251,7 +240,7 @@ func (addProps *AdditionalProperties) UnmarshalJSON(data []byte) error { case nil: case bool: addProps.Has = &y - case map[string]interface{}: + case map[string]any: if len(y) == 0 { addProps.Schema = &SchemaRef{Value: &Schema{}} } else { @@ -275,7 +264,17 @@ func NewSchema() *Schema { // MarshalJSON returns the JSON encoding of Schema. func (schema Schema) MarshalJSON() ([]byte, error) { - m := make(map[string]interface{}, 36+len(schema.Extensions)) + m, err := schema.MarshalYAML() + if err != nil { + return nil, err + } + + return json.Marshal(m) +} + +// MarshalYAML returns the YAML encoding of Schema. +func (schema Schema) MarshalYAML() (any, error) { + m := make(map[string]any, 36+len(schema.Extensions)) for k, v := range schema.Extensions { m[k] = v } @@ -401,7 +400,7 @@ func (schema Schema) MarshalJSON() ([]byte, error) { m["discriminator"] = x } - return json.Marshal(m) + return m, nil } // UnmarshalJSON sets Schema to a copy of data. @@ -478,7 +477,7 @@ func (schema *Schema) UnmarshalJSON(data []byte) error { } // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable -func (schema Schema) JSONLookup(token string) (interface{}, error) { +func (schema Schema) JSONLookup(token string) (any, error) { switch token { case "additionalProperties": if addProps := schema.AdditionalProperties.Has; addProps != nil { @@ -709,12 +708,12 @@ func (schema *Schema) WithExclusiveMax(value bool) *Schema { return schema } -func (schema *Schema) WithEnum(values ...interface{}) *Schema { +func (schema *Schema) WithEnum(values ...any) *Schema { schema.Enum = values return schema } -func (schema *Schema) WithDefault(defaultValue interface{}) *Schema { +func (schema *Schema) WithDefault(defaultValue any) *Schema { schema.Default = defaultValue return schema } @@ -983,7 +982,7 @@ func (schema *Schema) validate(ctx context.Context, stack []*Schema) ([]*Schema, switch format { case "float", "double": default: - if validationOpts.schemaFormatValidationEnabled { + if _, ok := SchemaNumberFormats[format]; !ok && validationOpts.schemaFormatValidationEnabled { return stack, unsupportedFormat(format) } } @@ -993,7 +992,7 @@ func (schema *Schema) validate(ctx context.Context, stack []*Schema) ([]*Schema, switch format { case "int32", "int64": default: - if validationOpts.schemaFormatValidationEnabled { + if _, ok := SchemaIntegerFormats[format]; !ok && validationOpts.schemaFormatValidationEnabled { return stack, unsupportedFormat(format) } } @@ -1014,7 +1013,6 @@ func (schema *Schema) validate(ctx context.Context, stack []*Schema) ([]*Schema, // Defined in some other specification case "email", "hostname", "ipv4", "ipv6", "uri", "uri-reference": default: - // Try to check for custom defined formats if _, ok := SchemaStringFormats[format]; !ok && validationOpts.schemaFormatValidationEnabled { return stack, unsupportedFormat(format) } @@ -1101,7 +1099,7 @@ func (schema *Schema) validate(ctx context.Context, stack []*Schema) ([]*Schema, return stack, validateExtensions(ctx, schema.Extensions) } -func (schema *Schema) IsMatching(value interface{}) bool { +func (schema *Schema) IsMatching(value any) bool { settings := newSchemaValidationSettings(FailFast()) return schema.visitJSON(settings, value) == nil } @@ -1121,22 +1119,22 @@ func (schema *Schema) IsMatchingJSONString(value string) bool { return schema.visitJSON(settings, value) == nil } -func (schema *Schema) IsMatchingJSONArray(value []interface{}) bool { +func (schema *Schema) IsMatchingJSONArray(value []any) bool { settings := newSchemaValidationSettings(FailFast()) return schema.visitJSON(settings, value) == nil } -func (schema *Schema) IsMatchingJSONObject(value map[string]interface{}) bool { +func (schema *Schema) IsMatchingJSONObject(value map[string]any) bool { settings := newSchemaValidationSettings(FailFast()) return schema.visitJSON(settings, value) == nil } -func (schema *Schema) VisitJSON(value interface{}, opts ...SchemaValidationOption) error { +func (schema *Schema) VisitJSON(value any, opts ...SchemaValidationOption) error { settings := newSchemaValidationSettings(opts...) return schema.visitJSON(settings, value) } -func (schema *Schema) visitJSON(settings *schemaValidationSettings, value interface{}) (err error) { +func (schema *Schema) visitJSON(settings *schemaValidationSettings, value any) (err error) { switch value := value.(type) { case nil: // Don't use VisitJSONNull, as we still want to reach 'visitXOFOperations', since @@ -1201,12 +1199,12 @@ func (schema *Schema) visitJSON(settings *schemaValidationSettings, value interf return schema.visitJSONNumber(settings, value) case string: return schema.visitJSONString(settings, value) - case []interface{}: + case []any: return schema.visitJSONArray(settings, value) - case map[string]interface{}: + case map[string]any: return schema.visitJSONObject(settings, value) - case map[interface{}]interface{}: // for YAML cf. issue #444 - values := make(map[string]interface{}, len(value)) + case map[any]any: // for YAML cf. issue https://github.com/getkin/kin-openapi/issues/444 + values := make(map[string]any, len(value)) for key, v := range value { if k, ok := key.(string); ok { values[k] = v @@ -1220,7 +1218,7 @@ func (schema *Schema) visitJSON(settings *schemaValidationSettings, value interf // Catch slice of non-empty interface type if reflect.TypeOf(value).Kind() == reflect.Slice { valueR := reflect.ValueOf(value) - newValue := make([]interface{}, 0, valueR.Len()) + newValue := make([]any, 0, valueR.Len()) for i := 0; i < valueR.Len(); i++ { newValue = append(newValue, valueR.Index(i).Interface()) } @@ -1236,7 +1234,7 @@ func (schema *Schema) visitJSON(settings *schemaValidationSettings, value interf } } -func (schema *Schema) visitEnumOperation(settings *schemaValidationSettings, value interface{}) (err error) { +func (schema *Schema) visitEnumOperation(settings *schemaValidationSettings, value any) (err error) { if enum := schema.Enum; len(enum) != 0 { for _, v := range enum { switch c := value.(type) { @@ -1273,7 +1271,7 @@ func (schema *Schema) visitEnumOperation(settings *schemaValidationSettings, val return } -func (schema *Schema) visitNotOperation(settings *schemaValidationSettings, value interface{}) (err error) { +func (schema *Schema) visitNotOperation(settings *schemaValidationSettings, value any) (err error) { if ref := schema.Not; ref != nil { v := ref.Value if v == nil { @@ -1296,13 +1294,13 @@ func (schema *Schema) visitNotOperation(settings *schemaValidationSettings, valu // If the XOF operations pass successfully, abort further run of validation, as they will already be satisfied (unless the schema // itself is badly specified -func (schema *Schema) visitXOFOperations(settings *schemaValidationSettings, value interface{}) (err error, run bool) { +func (schema *Schema) visitXOFOperations(settings *schemaValidationSettings, value any) (err error, run bool) { var visitedOneOf, visitedAnyOf, visitedAllOf bool if v := schema.OneOf; len(v) > 0 { var discriminatorRef string if schema.Discriminator != nil { pn := schema.Discriminator.PropertyName - if valuemap, okcheck := value.(map[string]interface{}); okcheck { + if valuemap, okcheck := value.(map[string]any); okcheck { discriminatorVal, okcheck := valuemap[pn] if !okcheck { return &SchemaError{ @@ -1518,39 +1516,56 @@ func (schema *Schema) visitJSONNumber(settings *schemaValidationSettings, value } // formats - if requireInteger && schema.Format != "" { - formatMin := float64(0) - formatMax := float64(0) - switch schema.Format { - case "int32": - formatMin = formatMinInt32 - formatMax = formatMaxInt32 - case "int64": - formatMin = formatMinInt64 - formatMax = formatMaxInt64 - default: - if settings.formatValidationEnabled { - return unsupportedFormat(schema.Format) - } - } - if formatMin != 0 && formatMax != 0 && !(formatMin <= value && value <= formatMax) { - if settings.failfast { - return errSchema - } - err := &SchemaError{ - Value: value, - Schema: schema, - SchemaField: "format", - Reason: fmt.Sprintf("number must be an %s", schema.Format), - customizeMessageError: settings.customizeMessageError, + var formatStrErr string + var formatErr error + format := schema.Format + if format != "" { + if requireInteger { + if f, ok := SchemaIntegerFormats[format]; ok { + if err := f.Validate(int64(value)); err != nil { + var reason string + schemaErr := &SchemaError{} + if errors.As(err, &schemaErr) { + reason = schemaErr.Reason + } else { + reason = err.Error() + } + formatStrErr = fmt.Sprintf(`integer doesn't match the format %q (%v)`, format, reason) + formatErr = fmt.Errorf("integer doesn't match the format %q: %w", format, err) + } } - if !settings.multiError { - return err + } else { + if f, ok := SchemaNumberFormats[format]; ok { + if err := f.Validate(value); err != nil { + var reason string + schemaErr := &SchemaError{} + if errors.As(err, &schemaErr) { + reason = schemaErr.Reason + } else { + reason = err.Error() + } + formatStrErr = fmt.Sprintf(`number doesn't match the format %q (%v)`, format, reason) + formatErr = fmt.Errorf("number doesn't match the format %q: %w", format, err) + } } - me = append(me, err) } } + if formatStrErr != "" || formatErr != nil { + err := &SchemaError{ + Value: value, + Schema: schema, + SchemaField: "format", + Reason: formatStrErr, + Origin: formatErr, + customizeMessageError: settings.customizeMessageError, + } + if !settings.multiError { + return err + } + me = append(me, err) + } + // "exclusiveMinimum" if v := schema.ExclusiveMin; v && !(*schema.Min < value) { if settings.failfast { @@ -1744,23 +1759,16 @@ func (schema *Schema) visitJSONString(settings *schemaValidationSettings, value var formatErr error if format := schema.Format; format != "" { if f, ok := SchemaStringFormats[format]; ok { - switch { - case f.regexp != nil && f.callback == nil: - if cp := f.regexp; !cp.MatchString(value) { - formatStrErr = fmt.Sprintf(`string doesn't match the format %q (regular expression "%s")`, format, cp.String()) - } - case f.regexp == nil && f.callback != nil: - if err := f.callback(value); err != nil { - schemaErr := &SchemaError{} - if errors.As(err, &schemaErr) { - formatStrErr = fmt.Sprintf(`string doesn't match the format %q (%s)`, format, schemaErr.Reason) - } else { - formatStrErr = fmt.Sprintf(`string doesn't match the format %q (%v)`, format, err) - } - formatErr = err + if err := f.Validate(value); err != nil { + var reason string + schemaErr := &SchemaError{} + if errors.As(err, &schemaErr) { + reason = schemaErr.Reason + } else { + reason = err.Error() } - default: - formatStrErr = fmt.Sprintf("corrupted entry %q in SchemaStringFormats", format) + formatStrErr = fmt.Sprintf(`string doesn't match the format %q (%v)`, format, reason) + formatErr = fmt.Errorf("string doesn't match the format %q: %w", format, err) } } } @@ -1787,12 +1795,12 @@ func (schema *Schema) visitJSONString(settings *schemaValidationSettings, value return nil } -func (schema *Schema) VisitJSONArray(value []interface{}) error { +func (schema *Schema) VisitJSONArray(value []any) error { settings := newSchemaValidationSettings() return schema.visitJSONArray(settings, value) } -func (schema *Schema) visitJSONArray(settings *schemaValidationSettings, value []interface{}) error { +func (schema *Schema) visitJSONArray(settings *schemaValidationSettings, value []any) error { if !schema.Type.Permits(TypeArray) { return schema.expectedType(settings, value) } @@ -1886,12 +1894,12 @@ func (schema *Schema) visitJSONArray(settings *schemaValidationSettings, value [ return nil } -func (schema *Schema) VisitJSONObject(value map[string]interface{}) error { +func (schema *Schema) VisitJSONObject(value map[string]any) error { settings := newSchemaValidationSettings() return schema.visitJSONObject(settings, value) } -func (schema *Schema) visitJSONObject(settings *schemaValidationSettings, value map[string]interface{}) error { +func (schema *Schema) visitJSONObject(settings *schemaValidationSettings, value map[string]any) error { if !schema.Type.Permits(TypeObject) { return schema.expectedType(settings, value) } @@ -2070,7 +2078,7 @@ func (schema *Schema) visitJSONObject(settings *schemaValidationSettings, value return nil } -func (schema *Schema) expectedType(settings *schemaValidationSettings, value interface{}) error { +func (schema *Schema) expectedType(settings *schemaValidationSettings, value any) error { if settings.failfast { return errSchema } @@ -2100,7 +2108,7 @@ func (schema *Schema) expectedType(settings *schemaValidationSettings, value int // SchemaError is an error that occurs during schema validation. type SchemaError struct { // Value is the value that failed validation. - Value interface{} + Value any // reversePath is the path to the value that failed validation. reversePath []string // Schema is the schema that failed validation. @@ -2119,14 +2127,16 @@ type SchemaError struct { var _ interface{ Unwrap() error } = SchemaError{} func markSchemaErrorKey(err error, key string) error { - var me multiErrorForOneOf - - if errors.As(err, &me) { - err = me.Unwrap() - } if v, ok := err.(*SchemaError); ok { v.reversePath = append(v.reversePath, key) + if v.Origin != nil { + if unwrapped := errors.Unwrap(v.Origin); unwrapped != nil { + if me, ok := unwrapped.(multiErrorForOneOf); ok { + _ = markSchemaErrorKey(MultiError(me), key) + } + } + } return v } if v, ok := err.(MultiError); ok { @@ -2205,7 +2215,7 @@ func (err SchemaError) Unwrap() error { return err.Origin } -func isSliceOfUniqueItems(xs []interface{}) bool { +func isSliceOfUniqueItems(xs []any) bool { s := len(xs) m := make(map[string]struct{}, s) for _, x := range xs { @@ -2219,7 +2229,7 @@ func isSliceOfUniqueItems(xs []interface{}) bool { // SliceUniqueItemsChecker is an function used to check if an given slice // have unique items. -type SliceUniqueItemsChecker func(items []interface{}) bool +type SliceUniqueItemsChecker func(items []any) bool // By default using predefined func isSliceOfUniqueItems which make use of // json.Marshal to generate a key for map used to check if a given slice diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/schema_formats.go b/vendor/github.com/getkin/kin-openapi/openapi3/schema_formats.go index ea38400c2..023c2669e 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/schema_formats.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/schema_formats.go @@ -2,9 +2,31 @@ package openapi3 import ( "fmt" - "net" + "math" + "net/netip" "regexp" - "strings" +) + +type ( + // FormatValidator is an interface for custom format validators. + FormatValidator[T any] interface { + Validate(value T) error + } + // StringFormatValidator is a type alias for FormatValidator[string] + StringFormatValidator = FormatValidator[string] + // NumberFormatValidator is a type alias for FormatValidator[float64] + NumberFormatValidator = FormatValidator[float64] + // IntegerFormatValidator is a type alias for FormatValidator[int64] + IntegerFormatValidator = FormatValidator[int64] +) + +var ( + // SchemaStringFormats is a map of custom string format validators. + SchemaStringFormats = make(map[string]StringFormatValidator) + // SchemaNumberFormats is a map of custom number format validators. + SchemaNumberFormats = make(map[string]NumberFormatValidator) + // SchemaIntegerFormats is a map of custom integer format validators. + SchemaIntegerFormats = make(map[string]IntegerFormatValidator) ) const ( @@ -14,93 +36,134 @@ const ( // FormatOfStringForEmail pattern catches only some suspiciously wrong-looking email addresses. // Use DefineStringFormat(...) if you need something stricter. FormatOfStringForEmail = `^[^@]+@[^@<>",\s]+$` -) -// FormatCallback performs custom checks on exotic formats -type FormatCallback func(value string) error + // FormatOfStringByte is a regexp for base64-encoded characters, for example, "U3dhZ2dlciByb2Nrcw==" + FormatOfStringByte = `(^$|^[a-zA-Z0-9+/\-_]*=*$)` + + // FormatOfStringDate is a RFC3339 date format regexp, for example "2017-07-21". + FormatOfStringDate = `^[0-9]{4}-(0[0-9]|10|11|12)-([0-2][0-9]|30|31)$` -// Format represents a format validator registered by either DefineStringFormat or DefineStringFormatCallback -type Format struct { - regexp *regexp.Regexp - callback FormatCallback + // FormatOfStringDateTime is a RFC3339 date-time format regexp, for example "2017-07-21T17:32:28Z". + FormatOfStringDateTime = `^[0-9]{4}-(0[0-9]|10|11|12)-([0-2][0-9]|30|31)T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]+)?(Z|(\+|-)[0-9]{2}:[0-9]{2})?$` +) + +func init() { + DefineStringFormatValidator("byte", NewRegexpFormatValidator(FormatOfStringByte)) + DefineStringFormatValidator("date", NewRegexpFormatValidator(FormatOfStringDate)) + DefineStringFormatValidator("date-time", NewRegexpFormatValidator(FormatOfStringDateTime)) + DefineIntegerFormatValidator("int32", NewRangeFormatValidator(int64(math.MinInt32), int64(math.MaxInt32))) + DefineIntegerFormatValidator("int64", NewRangeFormatValidator(int64(math.MinInt64), int64(math.MaxInt64))) } -// SchemaStringFormats allows for validating string formats -var SchemaStringFormats = make(map[string]Format, 4) +// DefineIPv4Format opts in ipv4 format validation on top of OAS 3 spec +func DefineIPv4Format() { + DefineStringFormatValidator("ipv4", NewIPValidator(true)) +} -// DefineStringFormat defines a new regexp pattern for a given format -func DefineStringFormat(name string, pattern string) { - re, err := regexp.Compile(pattern) - if err != nil { - err := fmt.Errorf("format %q has invalid pattern %q: %w", name, pattern, err) - panic(err) - } - SchemaStringFormats[name] = Format{regexp: re} +// DefineIPv6Format opts in ipv6 format validation on top of OAS 3 spec +func DefineIPv6Format() { + DefineStringFormatValidator("ipv6", NewIPValidator(false)) } -// DefineStringFormatCallback adds a validation function for a specific schema format entry -func DefineStringFormatCallback(name string, callback FormatCallback) { - SchemaStringFormats[name] = Format{callback: callback} +type stringRegexpFormatValidator struct { + re *regexp.Regexp } -func validateIP(ip string) error { - parsed := net.ParseIP(ip) - if parsed == nil { - return &SchemaError{ - Value: ip, - Reason: "Not an IP address", - } +func (s stringRegexpFormatValidator) Validate(value string) error { + if !s.re.MatchString(value) { + return fmt.Errorf(`string doesn't match pattern "%s"`, s.re.String()) } return nil } -func validateIPv4(ip string) error { - if err := validateIP(ip); err != nil { - return err - } +type callbackValidator[T any] struct { + fn func(T) error +} - if !(strings.Count(ip, ":") < 2) { - return &SchemaError{ - Value: ip, - Reason: "Not an IPv4 address (it's IPv6)", - } +func (c callbackValidator[T]) Validate(value T) error { + return c.fn(value) +} + +type rangeFormat[T int64 | float64] struct { + min, max T +} + +func (r rangeFormat[T]) Validate(value T) error { + if value < r.min || value > r.max { + return fmt.Errorf("value should be between %v and %v", r.min, r.max) } return nil } -func validateIPv6(ip string) error { - if err := validateIP(ip); err != nil { - return err - } +// NewRangeFormatValidator creates a new FormatValidator that validates the value is within a given range. +func NewRangeFormatValidator[T int64 | float64](min, max T) FormatValidator[T] { + return rangeFormat[T]{min: min, max: max} +} - if !(strings.Count(ip, ":") >= 2) { - return &SchemaError{ - Value: ip, - Reason: "Not an IPv6 address (it's IPv4)", - } +// NewRegexpFormatValidator creates a new FormatValidator that uses a regular expression to validate the value. +func NewRegexpFormatValidator(pattern string) StringFormatValidator { + re, err := regexp.Compile(pattern) + if err != nil { + err := fmt.Errorf("string regexp format has invalid pattern %q: %w", pattern, err) + panic(err) } - return nil + return stringRegexpFormatValidator{re: re} } -func init() { - // Base64 - // The pattern supports base64 and b./ase64url. Padding ('=') is supported. - DefineStringFormat("byte", `(^$|^[a-zA-Z0-9+/\-_]*=*$)`) +// NewCallbackValidator creates a new FormatValidator that uses a callback function to validate the value. +func NewCallbackValidator[T any](fn func(T) error) FormatValidator[T] { + return callbackValidator[T]{fn: fn} +} - // date - DefineStringFormat("date", `^[0-9]{4}-(0[0-9]|10|11|12)-([0-2][0-9]|30|31)$`) +// DefineStringFormatValidator defines a custom format validator for a given string format. +func DefineStringFormatValidator(name string, validator StringFormatValidator) { + SchemaStringFormats[name] = validator +} - // date-time - DefineStringFormat("date-time", `^[0-9]{4}-(0[0-9]|10|11|12)-([0-2][0-9]|30|31)T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]+)?(Z|(\+|-)[0-9]{2}:[0-9]{2})?$`) +// DefineNumberFormatValidator defines a custom format validator for a given number format. +func DefineNumberFormatValidator(name string, validator NumberFormatValidator) { + SchemaNumberFormats[name] = validator +} +// DefineIntegerFormatValidator defines a custom format validator for a given integer format. +func DefineIntegerFormatValidator(name string, validator IntegerFormatValidator) { + SchemaIntegerFormats[name] = validator } -// DefineIPv4Format opts in ipv4 format validation on top of OAS 3 spec -func DefineIPv4Format() { - DefineStringFormatCallback("ipv4", validateIPv4) +// DefineStringFormat defines a regexp pattern for a given string format +// Deprecated: Use openapi3.DefineStringFormatValidator(name, NewRegexpFormatValidator(pattern)) instead. +func DefineStringFormat(name string, pattern string) { + DefineStringFormatValidator(name, NewRegexpFormatValidator(pattern)) } -// DefineIPv6Format opts in ipv6 format validation on top of OAS 3 spec -func DefineIPv6Format() { - DefineStringFormatCallback("ipv6", validateIPv6) +// DefineStringFormatCallback defines a callback function for a given string format +// Deprecated: Use openapi3.DefineStringFormatValidator(name, NewCallbackValidator(fn)) instead. +func DefineStringFormatCallback(name string, callback func(string) error) { + DefineStringFormatValidator(name, NewCallbackValidator(callback)) +} + +// NewIPValidator creates a new FormatValidator that validates the value is an IP address. +func NewIPValidator(isIPv4 bool) FormatValidator[string] { + return callbackValidator[string]{fn: func(ip string) error { + addr, err := netip.ParseAddr(ip) + if err != nil { + return &SchemaError{ + Value: ip, + Reason: "Not an IP address", + } + } + if isIPv4 && !addr.Is4() { + return &SchemaError{ + Value: ip, + Reason: "Not an IPv4 address (it's IPv6)", + } + } + if !isIPv4 && !addr.Is6() { + return &SchemaError{ + Value: ip, + Reason: "Not an IPv6 address (it's IPv4)", + } + } + return nil + }} } 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 c07bfb619..b5c94b618 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/security_scheme.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/security_scheme.go @@ -11,7 +11,7 @@ import ( // SecurityScheme is specified by OpenAPI/Swagger standard version 3. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#security-scheme-object type SecurityScheme struct { - Extensions map[string]interface{} `json:"-" yaml:"-"` + Extensions map[string]any `json:"-" yaml:"-"` Type string `json:"type,omitempty" yaml:"type,omitempty"` Description string `json:"description,omitempty" yaml:"description,omitempty"` @@ -52,7 +52,16 @@ func NewJWTSecurityScheme() *SecurityScheme { // MarshalJSON returns the JSON encoding of SecurityScheme. func (ss SecurityScheme) MarshalJSON() ([]byte, error) { - m := make(map[string]interface{}, 8+len(ss.Extensions)) + x, err := ss.MarshalYAML() + if err != nil { + return nil, err + } + return json.Marshal(x) +} + +// MarshalYAML returns the YAML encoding of SecurityScheme. +func (ss SecurityScheme) MarshalYAML() (any, error) { + m := make(map[string]any, 8+len(ss.Extensions)) for k, v := range ss.Extensions { m[k] = v } @@ -80,7 +89,7 @@ func (ss SecurityScheme) MarshalJSON() ([]byte, error) { if x := ss.OpenIdConnectUrl; x != "" { m["openIdConnectUrl"] = x } - return json.Marshal(m) + return m, nil } // UnmarshalJSON sets SecurityScheme to a copy of data. @@ -206,7 +215,7 @@ func (ss *SecurityScheme) Validate(ctx context.Context, opts ...ValidationOption // OAuthFlows is specified by OpenAPI/Swagger standard version 3. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#oauth-flows-object type OAuthFlows struct { - Extensions map[string]interface{} `json:"-" yaml:"-"` + Extensions map[string]any `json:"-" yaml:"-"` Implicit *OAuthFlow `json:"implicit,omitempty" yaml:"implicit,omitempty"` Password *OAuthFlow `json:"password,omitempty" yaml:"password,omitempty"` @@ -225,7 +234,16 @@ const ( // MarshalJSON returns the JSON encoding of OAuthFlows. func (flows OAuthFlows) MarshalJSON() ([]byte, error) { - m := make(map[string]interface{}, 4+len(flows.Extensions)) + x, err := flows.MarshalYAML() + if err != nil { + return nil, err + } + return json.Marshal(x) +} + +// MarshalYAML returns the YAML encoding of OAuthFlows. +func (flows OAuthFlows) MarshalYAML() (any, error) { + m := make(map[string]any, 4+len(flows.Extensions)) for k, v := range flows.Extensions { m[k] = v } @@ -241,7 +259,7 @@ func (flows OAuthFlows) MarshalJSON() ([]byte, error) { if x := flows.AuthorizationCode; x != nil { m["authorizationCode"] = x } - return json.Marshal(m) + return m, nil } // UnmarshalJSON sets OAuthFlows to a copy of data. @@ -297,7 +315,7 @@ func (flows *OAuthFlows) Validate(ctx context.Context, opts ...ValidationOption) // OAuthFlow is specified by OpenAPI/Swagger standard version 3. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#oauth-flow-object type OAuthFlow struct { - Extensions map[string]interface{} `json:"-" yaml:"-"` + Extensions map[string]any `json:"-" yaml:"-"` AuthorizationURL string `json:"authorizationUrl,omitempty" yaml:"authorizationUrl,omitempty"` TokenURL string `json:"tokenUrl,omitempty" yaml:"tokenUrl,omitempty"` @@ -307,7 +325,16 @@ type OAuthFlow struct { // MarshalJSON returns the JSON encoding of OAuthFlow. func (flow OAuthFlow) MarshalJSON() ([]byte, error) { - m := make(map[string]interface{}, 4+len(flow.Extensions)) + x, err := flow.MarshalYAML() + if err != nil { + return nil, err + } + return json.Marshal(x) +} + +// MarshalYAML returns the YAML encoding of OAuthFlow. +func (flow OAuthFlow) MarshalYAML() (any, error) { + m := make(map[string]any, 4+len(flow.Extensions)) for k, v := range flow.Extensions { m[k] = v } @@ -321,7 +348,7 @@ func (flow OAuthFlow) MarshalJSON() ([]byte, error) { m["refreshUrl"] = x } m["scopes"] = flow.Scopes - return json.Marshal(m) + return m, nil } // UnmarshalJSON sets OAuthFlow to a copy of data. diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/server.go b/vendor/github.com/getkin/kin-openapi/openapi3/server.go index 04e233d51..7a2007f20 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/server.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/server.go @@ -51,7 +51,7 @@ func (servers Servers) MatchURL(parsedURL *url.URL) (*Server, []string, string) // Server is specified by OpenAPI/Swagger standard version 3. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#server-object type Server struct { - Extensions map[string]interface{} `json:"-" yaml:"-"` + Extensions map[string]any `json:"-" yaml:"-"` URL string `json:"url" yaml:"url"` // Required Description string `json:"description,omitempty" yaml:"description,omitempty"` @@ -84,7 +84,16 @@ func (server *Server) BasePath() (string, error) { // MarshalJSON returns the JSON encoding of Server. func (server Server) MarshalJSON() ([]byte, error) { - m := make(map[string]interface{}, 3+len(server.Extensions)) + x, err := server.MarshalYAML() + if err != nil { + return nil, err + } + return json.Marshal(x) +} + +// MarshalYAML returns the YAML encoding of Server. +func (server Server) MarshalYAML() (any, error) { + m := make(map[string]any, 3+len(server.Extensions)) for k, v := range server.Extensions { m[k] = v } @@ -95,7 +104,7 @@ func (server Server) MarshalJSON() ([]byte, error) { if x := server.Variables; len(x) != 0 { m["variables"] = x } - return json.Marshal(m) + return m, nil } // UnmarshalJSON sets Server to a copy of data. @@ -225,7 +234,7 @@ func (server *Server) Validate(ctx context.Context, opts ...ValidationOption) (e // ServerVariable is specified by OpenAPI/Swagger standard version 3. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#server-variable-object type ServerVariable struct { - Extensions map[string]interface{} `json:"-" yaml:"-"` + Extensions map[string]any `json:"-" yaml:"-"` Enum []string `json:"enum,omitempty" yaml:"enum,omitempty"` Default string `json:"default,omitempty" yaml:"default,omitempty"` @@ -234,7 +243,16 @@ type ServerVariable struct { // MarshalJSON returns the JSON encoding of ServerVariable. func (serverVariable ServerVariable) MarshalJSON() ([]byte, error) { - m := make(map[string]interface{}, 4+len(serverVariable.Extensions)) + x, err := serverVariable.MarshalYAML() + if err != nil { + return nil, err + } + return json.Marshal(x) +} + +// MarshalYAML returns the YAML encoding of ServerVariable. +func (serverVariable ServerVariable) MarshalYAML() (any, error) { + m := make(map[string]any, 4+len(serverVariable.Extensions)) for k, v := range serverVariable.Extensions { m[k] = v } @@ -247,7 +265,7 @@ func (serverVariable ServerVariable) MarshalJSON() ([]byte, error) { if x := serverVariable.Description; x != "" { m["description"] = x } - return json.Marshal(m) + return m, nil } // UnmarshalJSON sets ServerVariable to a copy of data. diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/tag.go b/vendor/github.com/getkin/kin-openapi/openapi3/tag.go index eea6462f5..182d0502a 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/tag.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/tag.go @@ -33,7 +33,7 @@ func (tags Tags) Validate(ctx context.Context, opts ...ValidationOption) error { // Tag is specified by OpenAPI/Swagger 3.0 standard. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#tag-object type Tag struct { - Extensions map[string]interface{} `json:"-" yaml:"-"` + Extensions map[string]any `json:"-" yaml:"-"` Name string `json:"name,omitempty" yaml:"name,omitempty"` Description string `json:"description,omitempty" yaml:"description,omitempty"` @@ -42,7 +42,16 @@ type Tag struct { // MarshalJSON returns the JSON encoding of Tag. func (t Tag) MarshalJSON() ([]byte, error) { - m := make(map[string]interface{}, 3+len(t.Extensions)) + x, err := t.MarshalYAML() + if err != nil { + return nil, err + } + return json.Marshal(x) +} + +// MarshalYAML returns the YAML encoding of Tag. +func (t Tag) MarshalYAML() (any, error) { + m := make(map[string]any, 3+len(t.Extensions)) for k, v := range t.Extensions { m[k] = v } @@ -55,7 +64,7 @@ func (t Tag) MarshalJSON() ([]byte, error) { if x := t.ExternalDocs; x != nil { m["externalDocs"] = x } - return json.Marshal(m) + return m, nil } // UnmarshalJSON sets Tag to a copy of data. diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/testdata/circularRef/base.yml b/vendor/github.com/getkin/kin-openapi/openapi3/testdata/circularRef/base.yml index ff8240eb0..897a45f37 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/testdata/circularRef/base.yml +++ b/vendor/github.com/getkin/kin-openapi/openapi3/testdata/circularRef/base.yml @@ -14,3 +14,5 @@ components: properties: foo: $ref: "#/components/schemas/Foo" + Baz: + $ref: "./baz.yml#/BazNested" diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/testdata/circularRef/baz.yml b/vendor/github.com/getkin/kin-openapi/openapi3/testdata/circularRef/baz.yml new file mode 100644 index 000000000..fb8c85420 --- /dev/null +++ b/vendor/github.com/getkin/kin-openapi/openapi3/testdata/circularRef/baz.yml @@ -0,0 +1,9 @@ +BazNested: + type: object + properties: + baz: + $ref: "#/BazNested" + bazArray: + type: array + items: + $ref: "#/BazNested" diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/testdata/recursiveRef/openapi.yml.internalized.yml b/vendor/github.com/getkin/kin-openapi/openapi3/testdata/recursiveRef/openapi.yml.internalized.yml index 0d508527a..33387fdf0 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/testdata/recursiveRef/openapi.yml.internalized.yml +++ b/vendor/github.com/getkin/kin-openapi/openapi3/testdata/recursiveRef/openapi.yml.internalized.yml @@ -1,7 +1,7 @@ { "components": { "parameters": { - "number": { + "parameters_number": { "in": "query", "name": "someNumber", "schema": { @@ -22,6 +22,34 @@ } }, "schemas": { + "components_Bar": { + "example": "bar", + "type": "string" + }, + "components_Cat": { + "properties": { + "cat": { + "$ref": "#/components/schemas/components_Cat" + } + }, + "type": "object" + }, + "components_Foo": { + "properties": { + "bar": { + "$ref": "#/components/schemas/components_Bar" + } + }, + "type": "object" + }, + "components_Foo_Foo2": { + "properties": { + "foo": { + "$ref": "#/components/schemas/components_Foo" + } + }, + "type": "object" + }, "Bar": { "example": "bar", "type": "string" @@ -33,7 +61,7 @@ "Foo": { "properties": { "bar": { - "$ref": "#/components/schemas/Bar" + "$ref": "#/components/schemas/components_Bar" } }, "type": "object" @@ -41,19 +69,15 @@ "Foo2": { "properties": { "foo": { - "$ref": "#/components/schemas/Foo" + "$ref": "#/components/schemas/components_Foo" } }, "type": "object" }, - "error":{ - "title":"ErrorDetails", - "type":"object" - }, "Cat": { "properties": { "cat": { - "$ref": "#/components/schemas/Cat" + "$ref": "#/components/schemas/components_Cat" } }, "type": "object" @@ -86,7 +110,7 @@ "schema": { "properties": { "foo2": { - "$ref": "#/components/schemas/Foo2" + "$ref": "#/components/schemas/components_Foo_Foo2" } }, "type": "object" @@ -102,7 +126,7 @@ }, "parameters": [ { - "$ref": "#/components/parameters/number" + "$ref": "#/components/parameters/parameters_number" } ] } 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 8982594b5..45563256a 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/validation_options.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/validation_options.go @@ -12,6 +12,7 @@ type ValidationOptions struct { schemaDefaultsValidationDisabled bool schemaFormatValidationEnabled bool schemaPatternValidationDisabled bool + schemaExtensionsInRefProhibited bool extraSiblingFieldsAllowed map[string]struct{} } @@ -92,6 +93,26 @@ func DisableExamplesValidation() ValidationOption { } } +// AllowExtensionsWithRef allows extensions (fields starting with 'x-') +// as siblings for $ref fields. This is the default. +// Non-extension fields are prohibited unless allowed explicitly with the +// AllowExtraSiblingFields option. +func AllowExtensionsWithRef() ValidationOption { + return func(options *ValidationOptions) { + options.schemaExtensionsInRefProhibited = false + } +} + +// ProhibitExtensionsWithRef causes the validation to return an +// error if extensions (fields starting with 'x-') are found as +// siblings for $ref fields. Non-extension fields are prohibited +// unless allowed explicitly with the AllowExtraSiblingFields option. +func ProhibitExtensionsWithRef() ValidationOption { + return func(options *ValidationOptions) { + options.schemaExtensionsInRefProhibited = true + } +} + // WithValidationOptions allows adding validation options to a context object that can be used when validating any OpenAPI type. func WithValidationOptions(ctx context.Context, opts ...ValidationOption) context.Context { if len(opts) == 0 { diff --git a/vendor/github.com/getkin/kin-openapi/openapi3/xml.go b/vendor/github.com/getkin/kin-openapi/openapi3/xml.go index 604b607dc..69d1b3483 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3/xml.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3/xml.go @@ -8,7 +8,7 @@ import ( // XML is specified by OpenAPI/Swagger standard version 3. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#xml-object type XML struct { - Extensions map[string]interface{} `json:"-" yaml:"-"` + Extensions map[string]any `json:"-" yaml:"-"` Name string `json:"name,omitempty" yaml:"name,omitempty"` Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"` @@ -19,7 +19,16 @@ type XML struct { // MarshalJSON returns the JSON encoding of XML. func (xml XML) MarshalJSON() ([]byte, error) { - m := make(map[string]interface{}, 5+len(xml.Extensions)) + x, err := xml.MarshalYAML() + if err != nil { + return nil, err + } + return json.Marshal(x) +} + +// MarshalYAML returns the YAML encoding of XML. +func (xml XML) MarshalYAML() (any, error) { + m := make(map[string]any, 5+len(xml.Extensions)) for k, v := range xml.Extensions { m[k] = v } @@ -38,7 +47,7 @@ func (xml XML) MarshalJSON() ([]byte, error) { if x := xml.Wrapped; x { m["wrapped"] = x } - return json.Marshal(m) + return m, nil } // UnmarshalJSON sets XML to a copy of data. diff --git a/vendor/github.com/getkin/kin-openapi/openapi3filter/internal.go b/vendor/github.com/getkin/kin-openapi/openapi3filter/internal.go index 5c6a8a6c6..f807e06f1 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3filter/internal.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3filter/internal.go @@ -13,7 +13,7 @@ func parseMediaType(contentType string) string { return contentType[:i] } -func isNilValue(value interface{}) bool { +func isNilValue(value any) bool { if value == nil { return true } diff --git a/vendor/github.com/getkin/kin-openapi/openapi3filter/middleware.go b/vendor/github.com/getkin/kin-openapi/openapi3filter/middleware.go index 0009d61c4..d20889ed9 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3filter/middleware.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3filter/middleware.go @@ -2,6 +2,7 @@ package openapi3filter import ( "bytes" + "context" "io" "log" "net/http" @@ -19,10 +20,10 @@ type Validator struct { } // ErrFunc handles errors that may occur during validation. -type ErrFunc func(w http.ResponseWriter, status int, code ErrCode, err error) +type ErrFunc func(ctx context.Context, w http.ResponseWriter, status int, code ErrCode, err error) // LogFunc handles log messages that may occur during validation. -type LogFunc func(message string, err error) +type LogFunc func(ctx context.Context, message string, err error) // ErrCode is used for classification of different types of errors that may // occur during validation. These may be used to write an appropriate response @@ -61,10 +62,10 @@ func (e ErrCode) responseText() string { func NewValidator(router routers.Router, options ...ValidatorOption) *Validator { v := &Validator{ router: router, - errFunc: func(w http.ResponseWriter, status int, code ErrCode, _ error) { + errFunc: func(_ context.Context, w http.ResponseWriter, status int, code ErrCode, _ error) { http.Error(w, code.responseText(), status) }, - logFunc: func(message string, err error) { + logFunc: func(_ context.Context, message string, err error) { log.Printf("%s: %v", message, err) }, } @@ -117,10 +118,11 @@ func ValidationOptions(options Options) ValidatorOption { // request and response validation. func (v *Validator) Middleware(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() route, pathParams, err := v.router.FindRoute(r) if err != nil { - v.logFunc("validation error: failed to find route for "+r.URL.String(), err) - v.errFunc(w, http.StatusNotFound, ErrCodeCannotFindRoute, err) + v.logFunc(ctx, "validation error: failed to find route for "+r.URL.String(), err) + v.errFunc(ctx, w, http.StatusNotFound, ErrCodeCannotFindRoute, err) return } requestValidationInput := &RequestValidationInput{ @@ -129,9 +131,9 @@ func (v *Validator) Middleware(h http.Handler) http.Handler { Route: route, Options: &v.options, } - if err = ValidateRequest(r.Context(), requestValidationInput); err != nil { - v.logFunc("invalid request", err) - v.errFunc(w, http.StatusBadRequest, ErrCodeRequestInvalid, err) + if err = ValidateRequest(ctx, requestValidationInput); err != nil { + v.logFunc(ctx, "invalid request", err) + v.errFunc(ctx, w, http.StatusBadRequest, ErrCodeRequestInvalid, err) return } @@ -144,22 +146,22 @@ func (v *Validator) Middleware(h http.Handler) http.Handler { h.ServeHTTP(wr, r) - if err = ValidateResponse(r.Context(), &ResponseValidationInput{ + if err = ValidateResponse(ctx, &ResponseValidationInput{ RequestValidationInput: requestValidationInput, Status: wr.statusCode(), Header: wr.Header(), Body: io.NopCloser(bytes.NewBuffer(wr.bodyContents())), Options: &v.options, }); err != nil { - v.logFunc("invalid response", err) + v.logFunc(ctx, "invalid response", err) if v.strict { - v.errFunc(w, http.StatusInternalServerError, ErrCodeResponseInvalid, err) + v.errFunc(ctx, w, http.StatusInternalServerError, ErrCodeResponseInvalid, err) } return } if err = wr.flushBodyContents(); err != nil { - v.logFunc("failed to write response", err) + v.logFunc(ctx, "failed to write response", err) } }) } diff --git a/vendor/github.com/getkin/kin-openapi/openapi3filter/req_resp_decoder.go b/vendor/github.com/getkin/kin-openapi/openapi3filter/req_resp_decoder.go index 72e9d86d9..e151c9ee7 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3filter/req_resp_decoder.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3filter/req_resp_decoder.go @@ -39,11 +39,11 @@ const ( // ParseError describes errors which happens while parse operation's parameters, requestBody, or response. type ParseError struct { Kind ParseErrorKind - Value interface{} + Value any Reason string Cause error - path []interface{} + path []any } var _ interface{ Unwrap() error } = ParseError{} @@ -92,8 +92,8 @@ func (e ParseError) Unwrap() error { } // Path returns a path to the root cause. -func (e *ParseError) Path() []interface{} { - var path []interface{} +func (e *ParseError) Path() []any { + var path []any if v, ok := e.Cause.(*ParseError); ok { p := v.Path() if len(p) > 0 { @@ -113,7 +113,7 @@ func invalidSerializationMethodErr(sm *openapi3.SerializationMethod) error { // Decodes a parameter defined via the content property as an object. It uses // the user specified decoder, or our build-in decoder for application/json func decodeContentParameter(param *openapi3.Parameter, input *RequestValidationInput) ( - value interface{}, + value any, schema *openapi3.Schema, found bool, err error, @@ -164,7 +164,7 @@ func decodeContentParameter(param *openapi3.Parameter, input *RequestValidationI } func defaultContentParameterDecoder(param *openapi3.Parameter, values []string) ( - outValue interface{}, + outValue any, outSchema *openapi3.Schema, err error, ) { @@ -192,7 +192,7 @@ func defaultContentParameterDecoder(param *openapi3.Parameter, values []string) } outSchema = mt.Schema.Value - unmarshal := func(encoded string, paramSchema *openapi3.SchemaRef) (decoded interface{}, err error) { + unmarshal := func(encoded string, paramSchema *openapi3.SchemaRef) (decoded any, err error) { if err = json.Unmarshal([]byte(encoded), &decoded); err != nil { if paramSchema != nil && !paramSchema.Value.Type.Is("object") { decoded, err = encoded, nil @@ -207,9 +207,9 @@ func defaultContentParameterDecoder(param *openapi3.Parameter, values []string) return } } else { - outArray := make([]interface{}, 0, len(values)) + outArray := make([]any, 0, len(values)) for _, v := range values { - var item interface{} + var item any if item, err = unmarshal(v, outSchema.Items); err != nil { err = fmt.Errorf("error unmarshaling parameter %q", param.Name) return @@ -222,15 +222,15 @@ func defaultContentParameterDecoder(param *openapi3.Parameter, values []string) } type valueDecoder interface { - DecodePrimitive(param string, sm *openapi3.SerializationMethod, schema *openapi3.SchemaRef) (interface{}, bool, error) - DecodeArray(param string, sm *openapi3.SerializationMethod, schema *openapi3.SchemaRef) ([]interface{}, bool, error) - DecodeObject(param string, sm *openapi3.SerializationMethod, schema *openapi3.SchemaRef) (map[string]interface{}, bool, error) + DecodePrimitive(param string, sm *openapi3.SerializationMethod, schema *openapi3.SchemaRef) (any, bool, error) + DecodeArray(param string, sm *openapi3.SerializationMethod, schema *openapi3.SchemaRef) ([]any, bool, error) + DecodeObject(param string, sm *openapi3.SerializationMethod, schema *openapi3.SchemaRef) (map[string]any, bool, error) } // decodeStyledParameter returns a value of an operation's parameter from HTTP request for // parameters defined using the style format, and whether the parameter is supplied in the input. // The function returns ParseError when HTTP request contains an invalid value of a parameter. -func decodeStyledParameter(param *openapi3.Parameter, input *RequestValidationInput) (interface{}, bool, error) { +func decodeStyledParameter(param *openapi3.Parameter, input *RequestValidationInput) (any, bool, error) { sm, err := param.SerializationMethod() if err != nil { return nil, false, err @@ -259,11 +259,11 @@ func decodeStyledParameter(param *openapi3.Parameter, input *RequestValidationIn return decodeValue(dec, param.Name, sm, param.Schema, param.Required) } -func decodeValue(dec valueDecoder, param string, sm *openapi3.SerializationMethod, schema *openapi3.SchemaRef, required bool) (interface{}, bool, error) { +func decodeValue(dec valueDecoder, param string, sm *openapi3.SerializationMethod, schema *openapi3.SchemaRef, required bool) (any, bool, error) { var found bool if len(schema.Value.AllOf) > 0 { - var value interface{} + var value any var err error for _, sr := range schema.Value.AllOf { var f bool @@ -292,7 +292,7 @@ func decodeValue(dec valueDecoder, param string, sm *openapi3.SerializationMetho if len(schema.Value.OneOf) > 0 { isMatched := 0 - var value interface{} + var value any for _, sr := range schema.Value.OneOf { v, f, _ := decodeValue(dec, param, sm, sr, required) found = found || f @@ -316,14 +316,18 @@ func decodeValue(dec valueDecoder, param string, sm *openapi3.SerializationMetho } if schema.Value.Type != nil { - var decodeFn func(param string, sm *openapi3.SerializationMethod, schema *openapi3.SchemaRef) (interface{}, bool, error) + var decodeFn func(param string, sm *openapi3.SerializationMethod, schema *openapi3.SchemaRef) (any, bool, error) switch { case schema.Value.Type.Is("array"): - decodeFn = func(param string, sm *openapi3.SerializationMethod, schema *openapi3.SchemaRef) (interface{}, bool, error) { - return dec.DecodeArray(param, sm, schema) + decodeFn = func(param string, sm *openapi3.SerializationMethod, schema *openapi3.SchemaRef) (any, bool, error) { + res, b, e := dec.DecodeArray(param, sm, schema) + if len(res) == 0 { + return nil, b, e + } + return res, b, e } case schema.Value.Type.Is("object"): - decodeFn = func(param string, sm *openapi3.SerializationMethod, schema *openapi3.SchemaRef) (interface{}, bool, error) { + decodeFn = func(param string, sm *openapi3.SerializationMethod, schema *openapi3.SchemaRef) (any, bool, error) { return dec.DecodeObject(param, sm, schema) } default: @@ -355,7 +359,7 @@ type pathParamDecoder struct { pathParams map[string]string } -func (d *pathParamDecoder) DecodePrimitive(param string, sm *openapi3.SerializationMethod, schema *openapi3.SchemaRef) (interface{}, bool, error) { +func (d *pathParamDecoder) DecodePrimitive(param string, sm *openapi3.SerializationMethod, schema *openapi3.SchemaRef) (any, bool, error) { var prefix string switch sm.Style { case "simple": @@ -385,7 +389,7 @@ func (d *pathParamDecoder) DecodePrimitive(param string, sm *openapi3.Serializat return val, ok, err } -func (d *pathParamDecoder) DecodeArray(param string, sm *openapi3.SerializationMethod, schema *openapi3.SchemaRef) ([]interface{}, bool, error) { +func (d *pathParamDecoder) DecodeArray(param string, sm *openapi3.SerializationMethod, schema *openapi3.SchemaRef) ([]any, bool, error) { var prefix, delim string switch { case sm.Style == "simple": @@ -423,7 +427,7 @@ func (d *pathParamDecoder) DecodeArray(param string, sm *openapi3.SerializationM return val, ok, err } -func (d *pathParamDecoder) DecodeObject(param string, sm *openapi3.SerializationMethod, schema *openapi3.SchemaRef) (map[string]interface{}, bool, error) { +func (d *pathParamDecoder) DecodeObject(param string, sm *openapi3.SerializationMethod, schema *openapi3.SchemaRef) (map[string]any, bool, error) { var prefix, propsDelim, valueDelim string switch { case sm.Style == "simple" && !sm.Explode: @@ -495,7 +499,7 @@ type urlValuesDecoder struct { values url.Values } -func (d *urlValuesDecoder) DecodePrimitive(param string, sm *openapi3.SerializationMethod, schema *openapi3.SchemaRef) (interface{}, bool, error) { +func (d *urlValuesDecoder) DecodePrimitive(param string, sm *openapi3.SerializationMethod, schema *openapi3.SchemaRef) (any, bool, error) { if sm.Style != "form" { return nil, false, invalidSerializationMethodErr(sm) } @@ -513,7 +517,7 @@ func (d *urlValuesDecoder) DecodePrimitive(param string, sm *openapi3.Serializat return val, ok, err } -func (d *urlValuesDecoder) DecodeArray(param string, sm *openapi3.SerializationMethod, schema *openapi3.SchemaRef) ([]interface{}, bool, error) { +func (d *urlValuesDecoder) DecodeArray(param string, sm *openapi3.SerializationMethod, schema *openapi3.SchemaRef) ([]any, bool, error) { if sm.Style == "deepObject" { return nil, false, invalidSerializationMethodErr(sm) } @@ -542,14 +546,14 @@ func (d *urlValuesDecoder) DecodeArray(param string, sm *openapi3.SerializationM // parseArray returns an array that contains items from a raw array. // Every item is parsed as a primitive value. // The function returns an error when an error happened while parse array's items. -func (d *urlValuesDecoder) parseArray(raw []string, sm *openapi3.SerializationMethod, schemaRef *openapi3.SchemaRef) ([]interface{}, error) { - var value []interface{} +func (d *urlValuesDecoder) parseArray(raw []string, sm *openapi3.SerializationMethod, schemaRef *openapi3.SchemaRef) ([]any, error) { + var value []any for i, v := range raw { item, err := d.parseValue(v, schemaRef.Value.Items) if err != nil { if v, ok := err.(*ParseError); ok { - return nil, &ParseError{path: []interface{}{i}, Cause: v} + return nil, &ParseError{path: []any{i}, Cause: v} } return nil, fmt.Errorf("item %d: %w", i, err) } @@ -564,9 +568,9 @@ func (d *urlValuesDecoder) parseArray(raw []string, sm *openapi3.SerializationMe return value, nil } -func (d *urlValuesDecoder) parseValue(v string, schema *openapi3.SchemaRef) (interface{}, error) { +func (d *urlValuesDecoder) parseValue(v string, schema *openapi3.SchemaRef) (any, error) { if len(schema.Value.AllOf) > 0 { - var value interface{} + var value any var err error for _, sr := range schema.Value.AllOf { value, err = d.parseValue(v, sr) @@ -578,7 +582,7 @@ func (d *urlValuesDecoder) parseValue(v string, schema *openapi3.SchemaRef) (int } if len(schema.Value.AnyOf) > 0 { - var value interface{} + var value any var err error for _, sr := range schema.Value.AnyOf { if value, err = d.parseValue(v, sr); err == nil { @@ -591,7 +595,7 @@ func (d *urlValuesDecoder) parseValue(v string, schema *openapi3.SchemaRef) (int if len(schema.Value.OneOf) > 0 { isMatched := 0 - var value interface{} + var value any var err error for _, sr := range schema.Value.OneOf { result, err := d.parseValue(v, sr) @@ -623,7 +627,7 @@ const ( urlDecoderDelimiter = "\x1F" // should not conflict with URL characters ) -func (d *urlValuesDecoder) DecodeObject(param string, sm *openapi3.SerializationMethod, schema *openapi3.SchemaRef) (map[string]interface{}, bool, error) { +func (d *urlValuesDecoder) DecodeObject(param string, sm *openapi3.SerializationMethod, schema *openapi3.SchemaRef) (map[string]any, bool, error) { var propsFn func(url.Values) (map[string]string, error) switch sm.Style { case "form": @@ -650,6 +654,10 @@ func (d *urlValuesDecoder) DecodeObject(param string, sm *openapi3.Serialization propsFn = func(params url.Values) (map[string]string, error) { props := make(map[string]string) for key, values := range params { + if !regexp.MustCompile(fmt.Sprintf(`^%s\[`, regexp.QuoteMeta(param))).MatchString(key) { + continue + } + matches := regexp.MustCompile(`\[(.*?)\]`).FindAllStringSubmatch(key, -1) switch l := len(matches); { case l == 0: @@ -710,7 +718,7 @@ type headerParamDecoder struct { header http.Header } -func (d *headerParamDecoder) DecodePrimitive(param string, sm *openapi3.SerializationMethod, schema *openapi3.SchemaRef) (interface{}, bool, error) { +func (d *headerParamDecoder) DecodePrimitive(param string, sm *openapi3.SerializationMethod, schema *openapi3.SchemaRef) (any, bool, error) { if sm.Style != "simple" { return nil, false, invalidSerializationMethodErr(sm) } @@ -725,7 +733,7 @@ func (d *headerParamDecoder) DecodePrimitive(param string, sm *openapi3.Serializ return val, ok, err } -func (d *headerParamDecoder) DecodeArray(param string, sm *openapi3.SerializationMethod, schema *openapi3.SchemaRef) ([]interface{}, bool, error) { +func (d *headerParamDecoder) DecodeArray(param string, sm *openapi3.SerializationMethod, schema *openapi3.SchemaRef) ([]any, bool, error) { if sm.Style != "simple" { return nil, false, invalidSerializationMethodErr(sm) } @@ -740,7 +748,7 @@ func (d *headerParamDecoder) DecodeArray(param string, sm *openapi3.Serializatio return val, ok, err } -func (d *headerParamDecoder) DecodeObject(param string, sm *openapi3.SerializationMethod, schema *openapi3.SchemaRef) (map[string]interface{}, bool, error) { +func (d *headerParamDecoder) DecodeObject(param string, sm *openapi3.SerializationMethod, schema *openapi3.SchemaRef) (map[string]any, bool, error) { if sm.Style != "simple" { return nil, false, invalidSerializationMethodErr(sm) } @@ -767,7 +775,7 @@ type cookieParamDecoder struct { req *http.Request } -func (d *cookieParamDecoder) DecodePrimitive(param string, sm *openapi3.SerializationMethod, schema *openapi3.SchemaRef) (interface{}, bool, error) { +func (d *cookieParamDecoder) DecodePrimitive(param string, sm *openapi3.SerializationMethod, schema *openapi3.SchemaRef) (any, bool, error) { if sm.Style != "form" { return nil, false, invalidSerializationMethodErr(sm) } @@ -786,7 +794,7 @@ func (d *cookieParamDecoder) DecodePrimitive(param string, sm *openapi3.Serializ return val, found, err } -func (d *cookieParamDecoder) DecodeArray(param string, sm *openapi3.SerializationMethod, schema *openapi3.SchemaRef) ([]interface{}, bool, error) { +func (d *cookieParamDecoder) DecodeArray(param string, sm *openapi3.SerializationMethod, schema *openapi3.SchemaRef) ([]any, bool, error) { if sm.Style != "form" || sm.Explode { return nil, false, invalidSerializationMethodErr(sm) } @@ -804,7 +812,7 @@ func (d *cookieParamDecoder) DecodeArray(param string, sm *openapi3.Serializatio return val, found, err } -func (d *cookieParamDecoder) DecodeObject(param string, sm *openapi3.SerializationMethod, schema *openapi3.SchemaRef) (map[string]interface{}, bool, error) { +func (d *cookieParamDecoder) DecodeObject(param string, sm *openapi3.SerializationMethod, schema *openapi3.SchemaRef) (map[string]any, bool, error) { if sm.Style != "form" || sm.Explode { return nil, false, invalidSerializationMethodErr(sm) } @@ -867,26 +875,26 @@ func propsFromString(src, propDelim, valueDelim string) (map[string]string, erro return props, nil } -func deepGet(m map[string]interface{}, keys ...string) (interface{}, bool) { +func deepGet(m map[string]any, keys ...string) (any, bool) { for _, key := range keys { val, ok := m[key] if !ok { return nil, false } - if m, ok = val.(map[string]interface{}); !ok { + if m, ok = val.(map[string]any); !ok { return val, true } } return m, true } -func deepSet(m map[string]interface{}, keys []string, value interface{}) { +func deepSet(m map[string]any, keys []string, value any) { for i := 0; i < len(keys)-1; i++ { key := keys[i] if _, ok := m[key]; !ok { - m[key] = make(map[string]interface{}) + m[key] = make(map[string]any) } - m = m[key].(map[string]interface{}) + m = m[key].(map[string]any) } m[keys[len(keys)-1]] = value } @@ -912,8 +920,8 @@ func findNestedSchema(parentSchema *openapi3.SchemaRef, keys []string) (*openapi } // makeObject returns an object that contains properties from props. -func makeObject(props map[string]string, schema *openapi3.SchemaRef) (map[string]interface{}, error) { - mobj := make(map[string]interface{}) +func makeObject(props map[string]string, schema *openapi3.SchemaRef) (map[string]any, error) { + mobj := make(map[string]any) for kk, value := range props { keys := strings.Split(kk, urlDecoderDelimiter) @@ -928,7 +936,7 @@ func makeObject(props map[string]string, schema *openapi3.SchemaRef) (map[string if err != nil { return nil, err } - result, ok := r.(map[string]interface{}) + result, ok := r.(map[string]any) if !ok { return nil, &ParseError{Kind: KindOther, Reason: "invalid param object", Value: result} } @@ -937,8 +945,8 @@ func makeObject(props map[string]string, schema *openapi3.SchemaRef) (map[string } // example: map[0:map[key:true] 1:map[key:false]] -> [map[key:true] map[key:false]] -func sliceMapToSlice(m map[string]interface{}) ([]interface{}, error) { - var result []interface{} +func sliceMapToSlice(m map[string]any) ([]any, error) { + var result []any keys := make([]int, 0, len(m)) for k := range m { @@ -966,7 +974,7 @@ func sliceMapToSlice(m map[string]interface{}) ([]interface{}, error) { } // buildResObj constructs an object based on a given schema and param values -func buildResObj(params map[string]interface{}, parentKeys []string, key string, schema *openapi3.SchemaRef) (interface{}, error) { +func buildResObj(params map[string]any, parentKeys []string, key string, schema *openapi3.SchemaRef) (any, error) { mapKeys := parentKeys if key != "" { mapKeys = append(mapKeys, key) @@ -978,7 +986,7 @@ func buildResObj(params map[string]interface{}, parentKeys []string, key string, if !ok { return nil, nil } - t, isMap := paramArr.(map[string]interface{}) + t, isMap := paramArr.(map[string]any) if !isMap { return nil, &ParseError{path: pathFromKeys(mapKeys), Kind: KindInvalidFormat, Reason: "array items must be set with indexes"} } @@ -987,7 +995,7 @@ func buildResObj(params map[string]interface{}, parentKeys []string, key string, if err != nil { return nil, &ParseError{path: pathFromKeys(mapKeys), Kind: KindInvalidFormat, Reason: fmt.Sprintf("could not convert value map to array: %v", err)} } - resultArr := make([]interface{} /*not 0,*/, len(arr)) + resultArr := make([]any /*not 0,*/, len(arr)) for i := range arr { r, err := buildResObj(params, mapKeys, strconv.Itoa(i), schema.Value.Items) if err != nil { @@ -999,10 +1007,10 @@ func buildResObj(params map[string]interface{}, parentKeys []string, key string, } return resultArr, nil case schema.Value.Type.Is("object"): - resultMap := make(map[string]interface{}) + resultMap := make(map[string]any) additPropsSchema := schema.Value.AdditionalProperties.Schema pp, _ := deepGet(params, mapKeys...) - objectParams, ok := pp.(map[string]interface{}) + objectParams, ok := pp.(map[string]any) if !ok { // not the expected type, but return it either way and leave validation up to ValidateParameter return pp, nil @@ -1056,20 +1064,20 @@ func buildResObj(params map[string]interface{}, parentKeys []string, key string, } // buildFromSchemas decodes params with anyOf, oneOf, allOf schemas. -func buildFromSchemas(schemas openapi3.SchemaRefs, params map[string]interface{}, mapKeys []string, key string) (interface{}, error) { - resultMap := make(map[string]interface{}) +func buildFromSchemas(schemas openapi3.SchemaRefs, params map[string]any, mapKeys []string, key string) (any, error) { + resultMap := make(map[string]any) for _, s := range schemas { val, err := buildResObj(params, mapKeys, key, s) if err == nil && val != nil { - if m, ok := val.(map[string]interface{}); ok { + if m, ok := val.(map[string]any); ok { for k, v := range m { resultMap[k] = v } continue } - if a, ok := val.([]interface{}); ok { + if a, ok := val.([]any); ok { if len(a) > 0 { return a, nil } @@ -1095,8 +1103,8 @@ func handlePropParseError(path []string, err error) error { return fmt.Errorf("property %q: %w", strings.Join(path, "."), err) } -func pathFromKeys(kk []string) []interface{} { - path := make([]interface{}, 0, len(kk)) +func pathFromKeys(kk []string) []any { + path := make([]any, 0, len(kk)) for _, v := range kk { path = append(path, v) } @@ -1106,13 +1114,13 @@ func pathFromKeys(kk []string) []interface{} { // parseArray returns an array that contains items from a raw array. // Every item is parsed as a primitive value. // The function returns an error when an error happened while parse array's items. -func parseArray(raw []string, schemaRef *openapi3.SchemaRef) ([]interface{}, error) { - var value []interface{} +func parseArray(raw []string, schemaRef *openapi3.SchemaRef) ([]any, error) { + var value []any for i, v := range raw { item, err := parsePrimitive(v, schemaRef.Value.Items) if err != nil { if v, ok := err.(*ParseError); ok { - return nil, &ParseError{path: []interface{}{i}, Cause: v} + return nil, &ParseError{path: []any{i}, Cause: v} } return nil, fmt.Errorf("item %d: %w", i, err) } @@ -1130,7 +1138,7 @@ func parseArray(raw []string, schemaRef *openapi3.SchemaRef) ([]interface{}, err // parsePrimitive returns a value that is created by parsing a source string to a primitive type // that is specified by a schema. The function returns nil when the source string is empty. // The function panics when a schema has a non-primitive type. -func parsePrimitive(raw string, schema *openapi3.SchemaRef) (v interface{}, err error) { +func parsePrimitive(raw string, schema *openapi3.SchemaRef) (v any, err error) { if raw == "" { return nil, nil } @@ -1142,7 +1150,7 @@ func parsePrimitive(raw string, schema *openapi3.SchemaRef) (v interface{}, err return } -func parsePrimitiveCase(raw string, schema *openapi3.SchemaRef, typ string) (interface{}, error) { +func parsePrimitiveCase(raw string, schema *openapi3.SchemaRef, typ string) (any, error) { switch typ { case "integer": if schema.Value.Format == "int32" { @@ -1180,8 +1188,8 @@ func parsePrimitiveCase(raw string, schema *openapi3.SchemaRef, typ string) (int type EncodingFn func(partName string) *openapi3.Encoding // BodyDecoder is an interface to decode a body of a request or response. -// An implementation must return a value that is a primitive, []interface{}, or map[string]interface{}. -type BodyDecoder func(io.Reader, http.Header, *openapi3.SchemaRef, EncodingFn) (interface{}, error) +// An implementation must return a value that is a primitive, []any, or map[string]any. +type BodyDecoder func(io.Reader, http.Header, *openapi3.SchemaRef, EncodingFn) (any, error) // bodyDecoders contains decoders for supported content types of a body. // By default, there is content type "application/json" is supported only. @@ -1229,7 +1237,7 @@ const prefixUnsupportedCT = "unsupported content type" // The function returns ParseError when a body is invalid. func decodeBody(body io.Reader, header http.Header, schema *openapi3.SchemaRef, encFn EncodingFn) ( string, - interface{}, + any, error, ) { contentType := header.Get(headerCT) @@ -1267,7 +1275,7 @@ func init() { RegisterBodyDecoder("text/plain", plainBodyDecoder) } -func plainBodyDecoder(body io.Reader, header http.Header, schema *openapi3.SchemaRef, encFn EncodingFn) (interface{}, error) { +func plainBodyDecoder(body io.Reader, header http.Header, schema *openapi3.SchemaRef, encFn EncodingFn) (any, error) { data, err := io.ReadAll(body) if err != nil { return nil, &ParseError{Kind: KindInvalidFormat, Cause: err} @@ -1277,8 +1285,8 @@ func plainBodyDecoder(body io.Reader, header http.Header, schema *openapi3.Schem // JSONBodyDecoder decodes a JSON formatted body. It is public so that is easy // to register additional JSON based formats. -func JSONBodyDecoder(body io.Reader, header http.Header, schema *openapi3.SchemaRef, encFn EncodingFn) (interface{}, error) { - var value interface{} +func JSONBodyDecoder(body io.Reader, header http.Header, schema *openapi3.SchemaRef, encFn EncodingFn) (any, error) { + var value any dec := json.NewDecoder(body) dec.UseNumber() if err := dec.Decode(&value); err != nil { @@ -1287,15 +1295,15 @@ func JSONBodyDecoder(body io.Reader, header http.Header, schema *openapi3.Schema return value, nil } -func yamlBodyDecoder(body io.Reader, header http.Header, schema *openapi3.SchemaRef, encFn EncodingFn) (interface{}, error) { - var value interface{} +func yamlBodyDecoder(body io.Reader, header http.Header, schema *openapi3.SchemaRef, encFn EncodingFn) (any, error) { + var value any if err := yaml.NewDecoder(body).Decode(&value); err != nil { return nil, &ParseError{Kind: KindInvalidFormat, Cause: err} } return value, nil } -func urlencodedBodyDecoder(body io.Reader, header http.Header, schema *openapi3.SchemaRef, encFn EncodingFn) (interface{}, error) { +func urlencodedBodyDecoder(body io.Reader, header http.Header, schema *openapi3.SchemaRef, encFn EncodingFn) (any, error) { // Validate schema of request body. // By the OpenAPI 3 specification request body's schema must have type "object". // Properties of the schema describes individual parts of request body. @@ -1326,7 +1334,7 @@ func urlencodedBodyDecoder(body io.Reader, header http.Header, schema *openapi3. } // Make an object value from form values. - obj := make(map[string]interface{}) + obj := make(map[string]any) dec := &urlValuesDecoder{values: values} // Decode schema constructs (allOf, anyOf, oneOf) @@ -1350,7 +1358,7 @@ func urlencodedBodyDecoder(body io.Reader, header http.Header, schema *openapi3. // decodeSchemaConstructs tries to decode properties based on provided schemas. // This function is for decoding purposes only and not for validation. -func decodeSchemaConstructs(dec *urlValuesDecoder, schemas []*openapi3.SchemaRef, obj map[string]interface{}, encFn EncodingFn) error { +func decodeSchemaConstructs(dec *urlValuesDecoder, schemas []*openapi3.SchemaRef, obj map[string]any, encFn EncodingFn) error { for _, schemaRef := range schemas { for name, prop := range schemaRef.Value.Properties { value, _, err := decodeProperty(dec, name, prop, encFn) @@ -1367,11 +1375,11 @@ func decodeSchemaConstructs(dec *urlValuesDecoder, schemas []*openapi3.SchemaRef return nil } -func isEqual(value1, value2 interface{}) bool { +func isEqual(value1, value2 any) bool { return reflect.DeepEqual(value1, value2) } -func decodeProperty(dec valueDecoder, name string, prop *openapi3.SchemaRef, encFn EncodingFn) (interface{}, bool, error) { +func decodeProperty(dec valueDecoder, name string, prop *openapi3.SchemaRef, encFn EncodingFn) (any, bool, error) { var enc *openapi3.Encoding if encFn != nil { enc = encFn(name) @@ -1380,13 +1388,13 @@ func decodeProperty(dec valueDecoder, name string, prop *openapi3.SchemaRef, enc return decodeValue(dec, name, sm, prop, false) } -func multipartBodyDecoder(body io.Reader, header http.Header, schema *openapi3.SchemaRef, encFn EncodingFn) (interface{}, error) { +func multipartBodyDecoder(body io.Reader, header http.Header, schema *openapi3.SchemaRef, encFn EncodingFn) (any, error) { if !schema.Value.Type.Is("object") { return nil, errors.New("unsupported schema of request body") } // Parse form. - values := make(map[string][]interface{}) + values := make(map[string][]any) contentType := header.Get(headerCT) _, params, err := mime.ParseMediaType(contentType) if err != nil { @@ -1449,10 +1457,10 @@ func multipartBodyDecoder(body io.Reader, header http.Header, schema *openapi3.S } } - var value interface{} + var value any if _, value, err = decodeBody(part, http.Header(part.Header), valueSchema, subEncFn); err != nil { if v, ok := err.(*ParseError); ok { - return nil, &ParseError{path: []interface{}{name}, Cause: v} + return nil, &ParseError{path: []any{name}, Cause: v} } return nil, fmt.Errorf("part %s: %w", name, err) } @@ -1483,7 +1491,7 @@ func multipartBodyDecoder(body io.Reader, header http.Header, schema *openapi3.S } // Make an object value from form values. - obj := make(map[string]interface{}) + obj := make(map[string]any) for name, prop := range allTheProperties { vv := values[name] if len(vv) == 0 { @@ -1500,7 +1508,7 @@ func multipartBodyDecoder(body io.Reader, header http.Header, schema *openapi3.S } // FileBodyDecoder is a body decoder that decodes a file body to a string. -func FileBodyDecoder(body io.Reader, header http.Header, schema *openapi3.SchemaRef, encFn EncodingFn) (interface{}, error) { +func FileBodyDecoder(body io.Reader, header http.Header, schema *openapi3.SchemaRef, encFn EncodingFn) (any, error) { data, err := io.ReadAll(body) if err != nil { return nil, err @@ -1509,7 +1517,7 @@ func FileBodyDecoder(body io.Reader, header http.Header, schema *openapi3.Schema } // zipFileBodyDecoder is a body decoder that decodes a zip file body to a string. -func zipFileBodyDecoder(body io.Reader, header http.Header, schema *openapi3.SchemaRef, encFn EncodingFn) (interface{}, error) { +func zipFileBodyDecoder(body io.Reader, header http.Header, schema *openapi3.SchemaRef, encFn EncodingFn) (any, error) { buff := bytes.NewBuffer([]byte{}) size, err := io.Copy(buff, body) if err != nil { @@ -1559,10 +1567,10 @@ func zipFileBodyDecoder(body io.Reader, header http.Header, schema *openapi3.Sch } // csvBodyDecoder is a body decoder that decodes a csv body to a string. -func csvBodyDecoder(body io.Reader, header http.Header, schema *openapi3.SchemaRef, encFn EncodingFn) (interface{}, error) { +func csvBodyDecoder(body io.Reader, header http.Header, schema *openapi3.SchemaRef, encFn EncodingFn) (any, error) { r := csv.NewReader(body) - var content string + var sb strings.Builder for { record, err := r.Read() if err == io.EOF { @@ -1572,8 +1580,9 @@ func csvBodyDecoder(body io.Reader, header http.Header, schema *openapi3.SchemaR return nil, err } - content += strings.Join(record, ",") + "\n" + sb.WriteString(strings.Join(record, ",")) + sb.WriteString("\n") } - return content, nil + return sb.String(), nil } diff --git a/vendor/github.com/getkin/kin-openapi/openapi3filter/req_resp_encoder.go b/vendor/github.com/getkin/kin-openapi/openapi3filter/req_resp_encoder.go index 2ec946afb..5c328c756 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3filter/req_resp_encoder.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3filter/req_resp_encoder.go @@ -6,7 +6,7 @@ import ( "sync" ) -func encodeBody(body interface{}, mediaType string) ([]byte, error) { +func encodeBody(body any, mediaType string) ([]byte, error) { if encoder := RegisteredBodyEncoder(mediaType); encoder != nil { return encoder(body) } @@ -17,7 +17,7 @@ func encodeBody(body interface{}, mediaType string) ([]byte, error) { } // BodyEncoder really is an (encoding/json).Marshaler -type BodyEncoder func(body interface{}) ([]byte, error) +type BodyEncoder func(body any) ([]byte, error) var bodyEncodersM sync.RWMutex var bodyEncoders = map[string]BodyEncoder{ diff --git a/vendor/github.com/getkin/kin-openapi/openapi3filter/validate_request.go b/vendor/github.com/getkin/kin-openapi/openapi3filter/validate_request.go index 7a8b5ca11..296403c9e 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3filter/validate_request.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3filter/validate_request.go @@ -7,6 +7,7 @@ import ( "fmt" "io" "net/http" + "net/url" "sort" "github.com/getkin/kin-openapi/openapi3" @@ -103,6 +104,28 @@ func ValidateRequest(ctx context.Context, input *RequestValidationInput) (err er return } +// appendToQueryValues adds to query parameters each value in the provided slice +func appendToQueryValues[T any](q url.Values, parameterName string, v []T) { + for _, i := range v { + q.Add(parameterName, fmt.Sprintf("%v", i)) + } +} + +// populateDefaultQueryParameters populates default values inside query parameters, while ensuring types are respected +func populateDefaultQueryParameters(q url.Values, parameterName string, value any) { + switch t := value.(type) { + case []string: + appendToQueryValues(q, parameterName, t) + case []float64: + appendToQueryValues(q, parameterName, t) + case []int: + appendToQueryValues(q, parameterName, t) + default: + q.Add(parameterName, fmt.Sprintf("%v", value)) + } + +} + // ValidateParameter validates a parameter's value by JSON schema. // The function returns RequestError with a ParseError cause when unable to parse a value. // The function returns RequestError with ErrInvalidRequired cause when a value of a required parameter is not defined. @@ -121,7 +144,7 @@ func ValidateParameter(ctx context.Context, input *RequestValidationInput, param options = &Options{} } - var value interface{} + var value any var err error var found bool var schema *openapi3.Schema @@ -156,7 +179,7 @@ func ValidateParameter(ctx context.Context, input *RequestValidationInput, param // Next check `parameter.Required && !found` will catch this. case openapi3.ParameterInQuery: q := req.URL.Query() - q.Add(parameter.Name, fmt.Sprintf("%v", value)) + populateDefaultQueryParameters(q, parameter.Name, value) req.URL.RawQuery = q.Encode() case openapi3.ParameterInHeader: req.Header.Add(parameter.Name, fmt.Sprintf("%v", value)) diff --git a/vendor/github.com/getkin/kin-openapi/openapi3filter/validate_request_input.go b/vendor/github.com/getkin/kin-openapi/openapi3filter/validate_request_input.go index 91dd102b6..c7565ebb7 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3filter/validate_request_input.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3filter/validate_request_input.go @@ -17,7 +17,7 @@ import ( // If a query parameter appears multiple times, values[] will have more // than one value, but for all other parameter types it should have just // one. -type ContentParameterDecoder func(param *openapi3.Parameter, values []string) (interface{}, *openapi3.Schema, error) +type ContentParameterDecoder func(param *openapi3.Parameter, values []string) (any, *openapi3.Schema, error) type RequestValidationInput struct { Request *http.Request diff --git a/vendor/github.com/getkin/kin-openapi/openapi3filter/validate_response.go b/vendor/github.com/getkin/kin-openapi/openapi3filter/validate_response.go index 87a8a1119..77f127e73 100644 --- a/vendor/github.com/getkin/kin-openapi/openapi3filter/validate_response.go +++ b/vendor/github.com/getkin/kin-openapi/openapi3filter/validate_response.go @@ -162,7 +162,7 @@ func ValidateResponse(ctx context.Context, input *ResponseValidationInput) error func validateResponseHeader(headerName string, headerRef *openapi3.HeaderRef, input *ResponseValidationInput, opts []openapi3.SchemaValidationOption) error { var err error - var decodedValue interface{} + var decodedValue any var found bool var sm *openapi3.SerializationMethod dec := &headerParamDecoder{header: input.Header} diff --git a/vendor/github.com/getkin/kin-openapi/routers/legacy/pathpattern/node.go b/vendor/github.com/getkin/kin-openapi/routers/legacy/pathpattern/node.go index 3f197df95..75932a26d 100644 --- a/vendor/github.com/getkin/kin-openapi/routers/legacy/pathpattern/node.go +++ b/vendor/github.com/getkin/kin-openapi/routers/legacy/pathpattern/node.go @@ -55,7 +55,7 @@ func PathFromHost(host string, specialDashes bool) string { type Node struct { VariableNames []string - Value interface{} + Value any Suffixes SuffixList } @@ -153,7 +153,7 @@ func (list SuffixList) Swap(i, j int) { list[i], list[j] = b, a } -func (currentNode *Node) MustAdd(path string, value interface{}, options *Options) { +func (currentNode *Node) MustAdd(path string, value any, options *Options) { node, err := currentNode.CreateNode(path, options) if err != nil { panic(err) @@ -161,7 +161,7 @@ func (currentNode *Node) MustAdd(path string, value interface{}, options *Option node.Value = value } -func (currentNode *Node) Add(path string, value interface{}, options *Options) error { +func (currentNode *Node) Add(path string, value any, options *Options) error { node, err := currentNode.CreateNode(path, options) if err != nil { return err diff --git a/vendor/github.com/invopop/yaml/.golangci.toml b/vendor/github.com/invopop/yaml/.golangci.toml index 4a438ca2c..61b2b79a2 100644 --- a/vendor/github.com/invopop/yaml/.golangci.toml +++ b/vendor/github.com/invopop/yaml/.golangci.toml @@ -6,10 +6,11 @@ format = "colored-line-number" [linters] enable = [ - "gocyclo", "unconvert", "goimports", "unused", "varcheck", - "vetshadow", "misspell", "nakedret", "errcheck", "revive", "ineffassign", - "deadcode", "goconst", "vet", "unparam", "gofmt" + "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/invopop/yaml/fields.go b/vendor/github.com/invopop/yaml/fields.go index 52b30c6bd..3fe5f12fd 100644 --- a/vendor/github.com/invopop/yaml/fields.go +++ b/vendor/github.com/invopop/yaml/fields.go @@ -347,8 +347,9 @@ const ( // 4) simpleLetterEqualFold, no specials, no non-letters. // // The letters S and K are special because they map to 3 runes, not just 2: -// * S maps to s and to U+017F 'ſ' Latin small letter long s -// * k maps to K and to U+212A 'K' Kelvin sign +// - S maps to s and to U+017F 'ſ' Latin small letter long s +// - k maps to K and to U+212A 'K' Kelvin sign +// // See http://play.golang.org/p/tTxjOc0OGo // // The returned function is specialized for matching against s and diff --git a/vendor/github.com/invopop/yaml/yaml.go b/vendor/github.com/invopop/yaml/yaml.go index 805d515df..d57dfb10b 100644 --- a/vendor/github.com/invopop/yaml/yaml.go +++ b/vendor/github.com/invopop/yaml/yaml.go @@ -5,7 +5,6 @@ // 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" import ( @@ -98,13 +97,12 @@ func JSONToYAML(j []byte) ([]byte, error) { // passing JSON through this method should be a no-op. // // Things YAML can do that are not supported by JSON: -// * In YAML you can have binary and null keys in your maps. These are invalid -// in JSON. (int and float keys are converted to strings.) -// * Binary data in YAML with the !!binary tag is not supported. If you want to -// use binary data with this library, encode the data as base64 as usual but do -// not use the !!binary tag in your YAML. This will ensure the original base64 -// encoded data makes it all the way through to the JSON. -// +// - In YAML you can have binary and null keys in your maps. These are invalid +// in JSON. (int and float keys are converted to strings.) +// - Binary data in YAML with the !!binary tag is not supported. If you want to +// use binary data with this library, encode the data as base64 as usual but do +// not use the !!binary tag in your YAML. This will ensure the original base64 +// encoded data makes it all the way through to the JSON. func YAMLToJSON(y []byte) ([]byte, error) { //nolint:revive dec := yaml.NewDecoder(bytes.NewReader(y)) return yamlToJSON(dec, nil) diff --git a/vendor/modules.txt b/vendor/modules.txt index 6daaca2fe..e958e208b 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -75,7 +75,7 @@ github.com/cespare/xxhash/v2 github.com/davecgh/go-spew/spew # github.com/fsnotify/fsnotify v1.6.0 ## explicit; go 1.16 -# github.com/getkin/kin-openapi v0.124.0 +# github.com/getkin/kin-openapi v0.127.0 ## explicit; go 1.20 github.com/getkin/kin-openapi/openapi3 github.com/getkin/kin-openapi/openapi3filter @@ -110,7 +110,7 @@ github.com/hashicorp/go-cleanhttp # github.com/hashicorp/go-retryablehttp v0.7.7 ## explicit; go 1.19 github.com/hashicorp/go-retryablehttp -# github.com/invopop/yaml v0.2.0 +# github.com/invopop/yaml v0.3.1 ## explicit; go 1.14 github.com/invopop/yaml # github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438