Update dependencies and tests
This commit updates the dependencies, vendor files and updates tests to take into account changes to the DB driver. Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
This commit is contained in:
parent
069bdd8b6b
commit
97d03dd38d
693 changed files with 86307 additions and 28214 deletions
2
vendor/github.com/bradleyfalzon/ghinstallation/v2/transport.go
generated
vendored
2
vendor/github.com/bradleyfalzon/ghinstallation/v2/transport.go
generated
vendored
|
|
@ -13,7 +13,7 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-github/v57/github"
|
||||
"github.com/google/go-github/v60/github"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
|
|||
2
vendor/github.com/cespare/xxhash/v2/README.md
generated
vendored
2
vendor/github.com/cespare/xxhash/v2/README.md
generated
vendored
|
|
@ -70,3 +70,5 @@ benchstat <(go test -benchtime 500ms -count 15 -bench 'Sum64$')
|
|||
- [VictoriaMetrics](https://github.com/VictoriaMetrics/VictoriaMetrics)
|
||||
- [FreeCache](https://github.com/coocood/freecache)
|
||||
- [FastCache](https://github.com/VictoriaMetrics/fastcache)
|
||||
- [Ristretto](https://github.com/dgraph-io/ristretto)
|
||||
- [Badger](https://github.com/dgraph-io/badger)
|
||||
|
|
|
|||
29
vendor/github.com/cespare/xxhash/v2/xxhash.go
generated
vendored
29
vendor/github.com/cespare/xxhash/v2/xxhash.go
generated
vendored
|
|
@ -19,10 +19,13 @@ const (
|
|||
// Store the primes in an array as well.
|
||||
//
|
||||
// The consts are used when possible in Go code to avoid MOVs but we need a
|
||||
// contiguous array of the assembly code.
|
||||
// contiguous array for the assembly code.
|
||||
var primes = [...]uint64{prime1, prime2, prime3, prime4, prime5}
|
||||
|
||||
// Digest implements hash.Hash64.
|
||||
//
|
||||
// Note that a zero-valued Digest is not ready to receive writes.
|
||||
// Call Reset or create a Digest using New before calling other methods.
|
||||
type Digest struct {
|
||||
v1 uint64
|
||||
v2 uint64
|
||||
|
|
@ -33,19 +36,31 @@ type Digest struct {
|
|||
n int // how much of mem is used
|
||||
}
|
||||
|
||||
// New creates a new Digest that computes the 64-bit xxHash algorithm.
|
||||
// New creates a new Digest with a zero seed.
|
||||
func New() *Digest {
|
||||
return NewWithSeed(0)
|
||||
}
|
||||
|
||||
// NewWithSeed creates a new Digest with the given seed.
|
||||
func NewWithSeed(seed uint64) *Digest {
|
||||
var d Digest
|
||||
d.Reset()
|
||||
d.ResetWithSeed(seed)
|
||||
return &d
|
||||
}
|
||||
|
||||
// Reset clears the Digest's state so that it can be reused.
|
||||
// It uses a seed value of zero.
|
||||
func (d *Digest) Reset() {
|
||||
d.v1 = primes[0] + prime2
|
||||
d.v2 = prime2
|
||||
d.v3 = 0
|
||||
d.v4 = -primes[0]
|
||||
d.ResetWithSeed(0)
|
||||
}
|
||||
|
||||
// ResetWithSeed clears the Digest's state so that it can be reused.
|
||||
// It uses the given seed to initialize the state.
|
||||
func (d *Digest) ResetWithSeed(seed uint64) {
|
||||
d.v1 = seed + prime1 + prime2
|
||||
d.v2 = seed + prime2
|
||||
d.v3 = seed
|
||||
d.v4 = seed - prime1
|
||||
d.total = 0
|
||||
d.n = 0
|
||||
}
|
||||
|
|
|
|||
2
vendor/github.com/cespare/xxhash/v2/xxhash_asm.go
generated
vendored
2
vendor/github.com/cespare/xxhash/v2/xxhash_asm.go
generated
vendored
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
package xxhash
|
||||
|
||||
// Sum64 computes the 64-bit xxHash digest of b.
|
||||
// Sum64 computes the 64-bit xxHash digest of b with a zero seed.
|
||||
//
|
||||
//go:noescape
|
||||
func Sum64(b []byte) uint64
|
||||
|
|
|
|||
2
vendor/github.com/cespare/xxhash/v2/xxhash_other.go
generated
vendored
2
vendor/github.com/cespare/xxhash/v2/xxhash_other.go
generated
vendored
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
package xxhash
|
||||
|
||||
// Sum64 computes the 64-bit xxHash digest of b.
|
||||
// Sum64 computes the 64-bit xxHash digest of b with a zero seed.
|
||||
func Sum64(b []byte) uint64 {
|
||||
// A simpler version would be
|
||||
// d := New()
|
||||
|
|
|
|||
2
vendor/github.com/cespare/xxhash/v2/xxhash_safe.go
generated
vendored
2
vendor/github.com/cespare/xxhash/v2/xxhash_safe.go
generated
vendored
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
package xxhash
|
||||
|
||||
// Sum64String computes the 64-bit xxHash digest of s.
|
||||
// Sum64String computes the 64-bit xxHash digest of s with a zero seed.
|
||||
func Sum64String(s string) uint64 {
|
||||
return Sum64([]byte(s))
|
||||
}
|
||||
|
|
|
|||
2
vendor/github.com/cespare/xxhash/v2/xxhash_unsafe.go
generated
vendored
2
vendor/github.com/cespare/xxhash/v2/xxhash_unsafe.go
generated
vendored
|
|
@ -33,7 +33,7 @@ import (
|
|||
//
|
||||
// See https://github.com/golang/go/issues/42739 for discussion.
|
||||
|
||||
// Sum64String computes the 64-bit xxHash digest of s.
|
||||
// Sum64String computes the 64-bit xxHash digest of s with a zero seed.
|
||||
// It may be faster than Sum64([]byte(s)) by avoiding a copy.
|
||||
func Sum64String(s string) uint64 {
|
||||
b := *(*[]byte)(unsafe.Pointer(&sliceHeader{s, len(s)}))
|
||||
|
|
|
|||
67
vendor/github.com/go-logr/logr/README.md
generated
vendored
67
vendor/github.com/go-logr/logr/README.md
generated
vendored
|
|
@ -91,11 +91,12 @@ logr design but also left out some parts and changed others:
|
|||
| Adding a name to a logger | `WithName` | no API |
|
||||
| Modify verbosity of log entries in a call chain | `V` | no API |
|
||||
| Grouping of key/value pairs | not supported | `WithGroup`, `GroupValue` |
|
||||
| Pass context for extracting additional values | no API | API variants like `InfoCtx` |
|
||||
|
||||
The high-level slog API is explicitly meant to be one of many different APIs
|
||||
that can be layered on top of a shared `slog.Handler`. logr is one such
|
||||
alternative API, with [interoperability](#slog-interoperability) provided by the [`slogr`](slogr)
|
||||
package.
|
||||
alternative API, with [interoperability](#slog-interoperability) provided by
|
||||
some conversion functions.
|
||||
|
||||
### Inspiration
|
||||
|
||||
|
|
@ -145,24 +146,24 @@ There are implementations for the following logging libraries:
|
|||
## slog interoperability
|
||||
|
||||
Interoperability goes both ways, using the `logr.Logger` API with a `slog.Handler`
|
||||
and using the `slog.Logger` API with a `logr.LogSink`. [slogr](./slogr) provides `NewLogr` and
|
||||
`NewSlogHandler` API calls to convert between a `logr.Logger` and a `slog.Handler`.
|
||||
and using the `slog.Logger` API with a `logr.LogSink`. `FromSlogHandler` and
|
||||
`ToSlogHandler` convert between a `logr.Logger` and a `slog.Handler`.
|
||||
As usual, `slog.New` can be used to wrap such a `slog.Handler` in the high-level
|
||||
slog API. `slogr` itself leaves that to the caller.
|
||||
slog API.
|
||||
|
||||
## Using a `logr.Sink` as backend for slog
|
||||
### Using a `logr.LogSink` as backend for slog
|
||||
|
||||
Ideally, a logr sink implementation should support both logr and slog by
|
||||
implementing both the normal logr interface(s) and `slogr.SlogSink`. Because
|
||||
implementing both the normal logr interface(s) and `SlogSink`. Because
|
||||
of a conflict in the parameters of the common `Enabled` method, it is [not
|
||||
possible to implement both slog.Handler and logr.Sink in the same
|
||||
type](https://github.com/golang/go/issues/59110).
|
||||
|
||||
If both are supported, log calls can go from the high-level APIs to the backend
|
||||
without the need to convert parameters. `NewLogr` and `NewSlogHandler` can
|
||||
without the need to convert parameters. `FromSlogHandler` and `ToSlogHandler` can
|
||||
convert back and forth without adding additional wrappers, with one exception:
|
||||
when `Logger.V` was used to adjust the verbosity for a `slog.Handler`, then
|
||||
`NewSlogHandler` has to use a wrapper which adjusts the verbosity for future
|
||||
`ToSlogHandler` has to use a wrapper which adjusts the verbosity for future
|
||||
log calls.
|
||||
|
||||
Such an implementation should also support values that implement specific
|
||||
|
|
@ -187,13 +188,13 @@ Not supporting slog has several drawbacks:
|
|||
These drawbacks are severe enough that applications using a mixture of slog and
|
||||
logr should switch to a different backend.
|
||||
|
||||
## Using a `slog.Handler` as backend for logr
|
||||
### Using a `slog.Handler` as backend for logr
|
||||
|
||||
Using a plain `slog.Handler` without support for logr works better than the
|
||||
other direction:
|
||||
- All logr verbosity levels can be mapped 1:1 to their corresponding slog level
|
||||
by negating them.
|
||||
- Stack unwinding is done by the `slogr.SlogSink` and the resulting program
|
||||
- Stack unwinding is done by the `SlogSink` and the resulting program
|
||||
counter is passed to the `slog.Handler`.
|
||||
- Names added via `Logger.WithName` are gathered and recorded in an additional
|
||||
attribute with `logger` as key and the names separated by slash as value.
|
||||
|
|
@ -205,27 +206,39 @@ ideally support both `logr.Marshaler` and `slog.Valuer`. If compatibility
|
|||
with logr implementations without slog support is not important, then
|
||||
`slog.Valuer` is sufficient.
|
||||
|
||||
## Context support for slog
|
||||
### Context support for slog
|
||||
|
||||
Storing a logger in a `context.Context` is not supported by
|
||||
slog. `logr.NewContext` and `logr.FromContext` can be used with slog like this
|
||||
to fill this gap:
|
||||
slog. `NewContextWithSlogLogger` and `FromContextAsSlogLogger` can be
|
||||
used to fill this gap. They store and retrieve a `slog.Logger` pointer
|
||||
under the same context key that is also used by `NewContext` and
|
||||
`FromContext` for `logr.Logger` value.
|
||||
|
||||
func HandlerFromContext(ctx context.Context) slog.Handler {
|
||||
logger, err := logr.FromContext(ctx)
|
||||
if err == nil {
|
||||
return slogr.NewSlogHandler(logger)
|
||||
}
|
||||
return slog.Default().Handler()
|
||||
}
|
||||
When `NewContextWithSlogLogger` is followed by `FromContext`, the latter will
|
||||
automatically convert the `slog.Logger` to a
|
||||
`logr.Logger`. `FromContextAsSlogLogger` does the same for the other direction.
|
||||
|
||||
func ContextWithHandler(ctx context.Context, handler slog.Handler) context.Context {
|
||||
return logr.NewContext(ctx, slogr.NewLogr(handler))
|
||||
}
|
||||
With this approach, binaries which use either slog or logr are as efficient as
|
||||
possible with no unnecessary allocations. This is also why the API stores a
|
||||
`slog.Logger` pointer: when storing a `slog.Handler`, creating a `slog.Logger`
|
||||
on retrieval would need to allocate one.
|
||||
|
||||
The downside is that storing and retrieving a `slog.Handler` needs more
|
||||
allocations compared to using a `logr.Logger`. Therefore the recommendation is
|
||||
to use the `logr.Logger` API in code which uses contextual logging.
|
||||
The downside is that switching back and forth needs more allocations. Because
|
||||
logr is the API that is already in use by different packages, in particular
|
||||
Kubernetes, the recommendation is to use the `logr.Logger` API in code which
|
||||
uses contextual logging.
|
||||
|
||||
An alternative to adding values to a logger and storing that logger in the
|
||||
context is to store the values in the context and to configure a logging
|
||||
backend to extract those values when emitting log entries. This only works when
|
||||
log calls are passed the context, which is not supported by the logr API.
|
||||
|
||||
With the slog API, it is possible, but not
|
||||
required. https://github.com/veqryn/slog-context is a package for slog which
|
||||
provides additional support code for this approach. It also contains wrappers
|
||||
for the context functions in logr, so developers who prefer to not use the logr
|
||||
APIs directly can use those instead and the resulting code will still be
|
||||
interoperable with logr.
|
||||
|
||||
## FAQ
|
||||
|
||||
|
|
|
|||
33
vendor/github.com/go-logr/logr/context.go
generated
vendored
Normal file
33
vendor/github.com/go-logr/logr/context.go
generated
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
Copyright 2023 The logr Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package logr
|
||||
|
||||
// contextKey is how we find Loggers in a context.Context. With Go < 1.21,
|
||||
// the value is always a Logger value. With Go >= 1.21, the value can be a
|
||||
// Logger value or a slog.Logger pointer.
|
||||
type contextKey struct{}
|
||||
|
||||
// notFoundError exists to carry an IsNotFound method.
|
||||
type notFoundError struct{}
|
||||
|
||||
func (notFoundError) Error() string {
|
||||
return "no logr.Logger was present"
|
||||
}
|
||||
|
||||
func (notFoundError) IsNotFound() bool {
|
||||
return true
|
||||
}
|
||||
49
vendor/github.com/go-logr/logr/context_noslog.go
generated
vendored
Normal file
49
vendor/github.com/go-logr/logr/context_noslog.go
generated
vendored
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
//go:build !go1.21
|
||||
// +build !go1.21
|
||||
|
||||
/*
|
||||
Copyright 2019 The logr Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package logr
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
// FromContext returns a Logger from ctx or an error if no Logger is found.
|
||||
func FromContext(ctx context.Context) (Logger, error) {
|
||||
if v, ok := ctx.Value(contextKey{}).(Logger); ok {
|
||||
return v, nil
|
||||
}
|
||||
|
||||
return Logger{}, notFoundError{}
|
||||
}
|
||||
|
||||
// FromContextOrDiscard returns a Logger from ctx. If no Logger is found, this
|
||||
// returns a Logger that discards all log messages.
|
||||
func FromContextOrDiscard(ctx context.Context) Logger {
|
||||
if v, ok := ctx.Value(contextKey{}).(Logger); ok {
|
||||
return v
|
||||
}
|
||||
|
||||
return Discard()
|
||||
}
|
||||
|
||||
// NewContext returns a new Context, derived from ctx, which carries the
|
||||
// provided Logger.
|
||||
func NewContext(ctx context.Context, logger Logger) context.Context {
|
||||
return context.WithValue(ctx, contextKey{}, logger)
|
||||
}
|
||||
83
vendor/github.com/go-logr/logr/context_slog.go
generated
vendored
Normal file
83
vendor/github.com/go-logr/logr/context_slog.go
generated
vendored
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
//go:build go1.21
|
||||
// +build go1.21
|
||||
|
||||
/*
|
||||
Copyright 2019 The logr Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package logr
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
)
|
||||
|
||||
// FromContext returns a Logger from ctx or an error if no Logger is found.
|
||||
func FromContext(ctx context.Context) (Logger, error) {
|
||||
v := ctx.Value(contextKey{})
|
||||
if v == nil {
|
||||
return Logger{}, notFoundError{}
|
||||
}
|
||||
|
||||
switch v := v.(type) {
|
||||
case Logger:
|
||||
return v, nil
|
||||
case *slog.Logger:
|
||||
return FromSlogHandler(v.Handler()), nil
|
||||
default:
|
||||
// Not reached.
|
||||
panic(fmt.Sprintf("unexpected value type for logr context key: %T", v))
|
||||
}
|
||||
}
|
||||
|
||||
// FromContextAsSlogLogger returns a slog.Logger from ctx or nil if no such Logger is found.
|
||||
func FromContextAsSlogLogger(ctx context.Context) *slog.Logger {
|
||||
v := ctx.Value(contextKey{})
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch v := v.(type) {
|
||||
case Logger:
|
||||
return slog.New(ToSlogHandler(v))
|
||||
case *slog.Logger:
|
||||
return v
|
||||
default:
|
||||
// Not reached.
|
||||
panic(fmt.Sprintf("unexpected value type for logr context key: %T", v))
|
||||
}
|
||||
}
|
||||
|
||||
// FromContextOrDiscard returns a Logger from ctx. If no Logger is found, this
|
||||
// returns a Logger that discards all log messages.
|
||||
func FromContextOrDiscard(ctx context.Context) Logger {
|
||||
if logger, err := FromContext(ctx); err == nil {
|
||||
return logger
|
||||
}
|
||||
return Discard()
|
||||
}
|
||||
|
||||
// NewContext returns a new Context, derived from ctx, which carries the
|
||||
// provided Logger.
|
||||
func NewContext(ctx context.Context, logger Logger) context.Context {
|
||||
return context.WithValue(ctx, contextKey{}, logger)
|
||||
}
|
||||
|
||||
// NewContextWithSlogLogger returns a new Context, derived from ctx, which carries the
|
||||
// provided slog.Logger.
|
||||
func NewContextWithSlogLogger(ctx context.Context, logger *slog.Logger) context.Context {
|
||||
return context.WithValue(ctx, contextKey{}, logger)
|
||||
}
|
||||
189
vendor/github.com/go-logr/logr/funcr/funcr.go
generated
vendored
189
vendor/github.com/go-logr/logr/funcr/funcr.go
generated
vendored
|
|
@ -100,6 +100,11 @@ type Options struct {
|
|||
// details, see docs for Go's time.Layout.
|
||||
TimestampFormat string
|
||||
|
||||
// LogInfoLevel tells funcr what key to use to log the info level.
|
||||
// If not specified, the info level will be logged as "level".
|
||||
// If this is set to "", the info level will not be logged at all.
|
||||
LogInfoLevel *string
|
||||
|
||||
// Verbosity tells funcr which V logs to produce. Higher values enable
|
||||
// more logs. Info logs at or below this level will be written, while logs
|
||||
// above this level will be discarded.
|
||||
|
|
@ -213,6 +218,10 @@ func newFormatter(opts Options, outfmt outputFormat) Formatter {
|
|||
if opts.MaxLogDepth == 0 {
|
||||
opts.MaxLogDepth = defaultMaxLogDepth
|
||||
}
|
||||
if opts.LogInfoLevel == nil {
|
||||
opts.LogInfoLevel = new(string)
|
||||
*opts.LogInfoLevel = "level"
|
||||
}
|
||||
f := Formatter{
|
||||
outputFormat: outfmt,
|
||||
prefix: "",
|
||||
|
|
@ -227,12 +236,15 @@ func newFormatter(opts Options, outfmt outputFormat) Formatter {
|
|||
// implementation. It should be constructed with NewFormatter. Some of
|
||||
// its methods directly implement logr.LogSink.
|
||||
type Formatter struct {
|
||||
outputFormat outputFormat
|
||||
prefix string
|
||||
values []any
|
||||
valuesStr string
|
||||
depth int
|
||||
opts *Options
|
||||
outputFormat outputFormat
|
||||
prefix string
|
||||
values []any
|
||||
valuesStr string
|
||||
parentValuesStr string
|
||||
depth int
|
||||
opts *Options
|
||||
group string // for slog groups
|
||||
groupDepth int
|
||||
}
|
||||
|
||||
// outputFormat indicates which outputFormat to use.
|
||||
|
|
@ -253,33 +265,62 @@ func (f Formatter) render(builtins, args []any) string {
|
|||
// Empirically bytes.Buffer is faster than strings.Builder for this.
|
||||
buf := bytes.NewBuffer(make([]byte, 0, 1024))
|
||||
if f.outputFormat == outputJSON {
|
||||
buf.WriteByte('{')
|
||||
buf.WriteByte('{') // for the whole line
|
||||
}
|
||||
|
||||
vals := builtins
|
||||
if hook := f.opts.RenderBuiltinsHook; hook != nil {
|
||||
vals = hook(f.sanitize(vals))
|
||||
}
|
||||
f.flatten(buf, vals, false, false) // keys are ours, no need to escape
|
||||
continuing := len(builtins) > 0
|
||||
if len(f.valuesStr) > 0 {
|
||||
|
||||
if f.parentValuesStr != "" {
|
||||
if continuing {
|
||||
if f.outputFormat == outputJSON {
|
||||
buf.WriteByte(',')
|
||||
} else {
|
||||
buf.WriteByte(' ')
|
||||
}
|
||||
buf.WriteByte(f.comma())
|
||||
}
|
||||
buf.WriteString(f.parentValuesStr)
|
||||
continuing = true
|
||||
buf.WriteString(f.valuesStr)
|
||||
}
|
||||
|
||||
groupDepth := f.groupDepth
|
||||
if f.group != "" {
|
||||
if f.valuesStr != "" || len(args) != 0 {
|
||||
if continuing {
|
||||
buf.WriteByte(f.comma())
|
||||
}
|
||||
buf.WriteString(f.quoted(f.group, true)) // escape user-provided keys
|
||||
buf.WriteByte(f.colon())
|
||||
buf.WriteByte('{') // for the group
|
||||
continuing = false
|
||||
} else {
|
||||
// The group was empty
|
||||
groupDepth--
|
||||
}
|
||||
}
|
||||
|
||||
if f.valuesStr != "" {
|
||||
if continuing {
|
||||
buf.WriteByte(f.comma())
|
||||
}
|
||||
buf.WriteString(f.valuesStr)
|
||||
continuing = true
|
||||
}
|
||||
|
||||
vals = args
|
||||
if hook := f.opts.RenderArgsHook; hook != nil {
|
||||
vals = hook(f.sanitize(vals))
|
||||
}
|
||||
f.flatten(buf, vals, continuing, true) // escape user-provided keys
|
||||
if f.outputFormat == outputJSON {
|
||||
buf.WriteByte('}')
|
||||
|
||||
for i := 0; i < groupDepth; i++ {
|
||||
buf.WriteByte('}') // for the groups
|
||||
}
|
||||
|
||||
if f.outputFormat == outputJSON {
|
||||
buf.WriteByte('}') // for the whole line
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
|
|
@ -298,9 +339,16 @@ func (f Formatter) flatten(buf *bytes.Buffer, kvList []any, continuing bool, esc
|
|||
if len(kvList)%2 != 0 {
|
||||
kvList = append(kvList, noValue)
|
||||
}
|
||||
copied := false
|
||||
for i := 0; i < len(kvList); i += 2 {
|
||||
k, ok := kvList[i].(string)
|
||||
if !ok {
|
||||
if !copied {
|
||||
newList := make([]any, len(kvList))
|
||||
copy(newList, kvList)
|
||||
kvList = newList
|
||||
copied = true
|
||||
}
|
||||
k = f.nonStringKey(kvList[i])
|
||||
kvList[i] = k
|
||||
}
|
||||
|
|
@ -308,7 +356,7 @@ func (f Formatter) flatten(buf *bytes.Buffer, kvList []any, continuing bool, esc
|
|||
|
||||
if i > 0 || continuing {
|
||||
if f.outputFormat == outputJSON {
|
||||
buf.WriteByte(',')
|
||||
buf.WriteByte(f.comma())
|
||||
} else {
|
||||
// In theory the format could be something we don't understand. In
|
||||
// practice, we control it, so it won't be.
|
||||
|
|
@ -316,24 +364,35 @@ func (f Formatter) flatten(buf *bytes.Buffer, kvList []any, continuing bool, esc
|
|||
}
|
||||
}
|
||||
|
||||
if escapeKeys {
|
||||
buf.WriteString(prettyString(k))
|
||||
} else {
|
||||
// this is faster
|
||||
buf.WriteByte('"')
|
||||
buf.WriteString(k)
|
||||
buf.WriteByte('"')
|
||||
}
|
||||
if f.outputFormat == outputJSON {
|
||||
buf.WriteByte(':')
|
||||
} else {
|
||||
buf.WriteByte('=')
|
||||
}
|
||||
buf.WriteString(f.quoted(k, escapeKeys))
|
||||
buf.WriteByte(f.colon())
|
||||
buf.WriteString(f.pretty(v))
|
||||
}
|
||||
return kvList
|
||||
}
|
||||
|
||||
func (f Formatter) quoted(str string, escape bool) string {
|
||||
if escape {
|
||||
return prettyString(str)
|
||||
}
|
||||
// this is faster
|
||||
return `"` + str + `"`
|
||||
}
|
||||
|
||||
func (f Formatter) comma() byte {
|
||||
if f.outputFormat == outputJSON {
|
||||
return ','
|
||||
}
|
||||
return ' '
|
||||
}
|
||||
|
||||
func (f Formatter) colon() byte {
|
||||
if f.outputFormat == outputJSON {
|
||||
return ':'
|
||||
}
|
||||
return '='
|
||||
}
|
||||
|
||||
func (f Formatter) pretty(value any) string {
|
||||
return f.prettyWithFlags(value, 0, 0)
|
||||
}
|
||||
|
|
@ -407,12 +466,12 @@ func (f Formatter) prettyWithFlags(value any, flags uint32, depth int) string {
|
|||
}
|
||||
for i := 0; i < len(v); i += 2 {
|
||||
if i > 0 {
|
||||
buf.WriteByte(',')
|
||||
buf.WriteByte(f.comma())
|
||||
}
|
||||
k, _ := v[i].(string) // sanitize() above means no need to check success
|
||||
// arbitrary keys might need escaping
|
||||
buf.WriteString(prettyString(k))
|
||||
buf.WriteByte(':')
|
||||
buf.WriteByte(f.colon())
|
||||
buf.WriteString(f.prettyWithFlags(v[i+1], 0, depth+1))
|
||||
}
|
||||
if flags&flagRawStruct == 0 {
|
||||
|
|
@ -481,7 +540,7 @@ func (f Formatter) prettyWithFlags(value any, flags uint32, depth int) string {
|
|||
continue
|
||||
}
|
||||
if printComma {
|
||||
buf.WriteByte(',')
|
||||
buf.WriteByte(f.comma())
|
||||
}
|
||||
printComma = true // if we got here, we are rendering a field
|
||||
if fld.Anonymous && fld.Type.Kind() == reflect.Struct && name == "" {
|
||||
|
|
@ -492,10 +551,8 @@ func (f Formatter) prettyWithFlags(value any, flags uint32, depth int) string {
|
|||
name = fld.Name
|
||||
}
|
||||
// field names can't contain characters which need escaping
|
||||
buf.WriteByte('"')
|
||||
buf.WriteString(name)
|
||||
buf.WriteByte('"')
|
||||
buf.WriteByte(':')
|
||||
buf.WriteString(f.quoted(name, false))
|
||||
buf.WriteByte(f.colon())
|
||||
buf.WriteString(f.prettyWithFlags(v.Field(i).Interface(), 0, depth+1))
|
||||
}
|
||||
if flags&flagRawStruct == 0 {
|
||||
|
|
@ -520,7 +577,7 @@ func (f Formatter) prettyWithFlags(value any, flags uint32, depth int) string {
|
|||
buf.WriteByte('[')
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
if i > 0 {
|
||||
buf.WriteByte(',')
|
||||
buf.WriteByte(f.comma())
|
||||
}
|
||||
e := v.Index(i)
|
||||
buf.WriteString(f.prettyWithFlags(e.Interface(), 0, depth+1))
|
||||
|
|
@ -534,7 +591,7 @@ func (f Formatter) prettyWithFlags(value any, flags uint32, depth int) string {
|
|||
i := 0
|
||||
for it.Next() {
|
||||
if i > 0 {
|
||||
buf.WriteByte(',')
|
||||
buf.WriteByte(f.comma())
|
||||
}
|
||||
// If a map key supports TextMarshaler, use it.
|
||||
keystr := ""
|
||||
|
|
@ -556,7 +613,7 @@ func (f Formatter) prettyWithFlags(value any, flags uint32, depth int) string {
|
|||
}
|
||||
}
|
||||
buf.WriteString(keystr)
|
||||
buf.WriteByte(':')
|
||||
buf.WriteByte(f.colon())
|
||||
buf.WriteString(f.prettyWithFlags(it.Value().Interface(), 0, depth+1))
|
||||
i++
|
||||
}
|
||||
|
|
@ -706,6 +763,53 @@ func (f Formatter) sanitize(kvList []any) []any {
|
|||
return kvList
|
||||
}
|
||||
|
||||
// startGroup opens a new group scope (basically a sub-struct), which locks all
|
||||
// the current saved values and starts them anew. This is needed to satisfy
|
||||
// slog.
|
||||
func (f *Formatter) startGroup(group string) {
|
||||
// Unnamed groups are just inlined.
|
||||
if group == "" {
|
||||
return
|
||||
}
|
||||
|
||||
// Any saved values can no longer be changed.
|
||||
buf := bytes.NewBuffer(make([]byte, 0, 1024))
|
||||
continuing := false
|
||||
|
||||
if f.parentValuesStr != "" {
|
||||
buf.WriteString(f.parentValuesStr)
|
||||
continuing = true
|
||||
}
|
||||
|
||||
if f.group != "" && f.valuesStr != "" {
|
||||
if continuing {
|
||||
buf.WriteByte(f.comma())
|
||||
}
|
||||
buf.WriteString(f.quoted(f.group, true)) // escape user-provided keys
|
||||
buf.WriteByte(f.colon())
|
||||
buf.WriteByte('{') // for the group
|
||||
continuing = false
|
||||
}
|
||||
|
||||
if f.valuesStr != "" {
|
||||
if continuing {
|
||||
buf.WriteByte(f.comma())
|
||||
}
|
||||
buf.WriteString(f.valuesStr)
|
||||
}
|
||||
|
||||
// NOTE: We don't close the scope here - that's done later, when a log line
|
||||
// is actually rendered (because we have N scopes to close).
|
||||
|
||||
f.parentValuesStr = buf.String()
|
||||
|
||||
// Start collecting new values.
|
||||
f.group = group
|
||||
f.groupDepth++
|
||||
f.valuesStr = ""
|
||||
f.values = nil
|
||||
}
|
||||
|
||||
// Init configures this Formatter from runtime info, such as the call depth
|
||||
// imposed by logr itself.
|
||||
// Note that this receiver is a pointer, so depth can be saved.
|
||||
|
|
@ -740,7 +844,10 @@ func (f Formatter) FormatInfo(level int, msg string, kvList []any) (prefix, args
|
|||
if policy := f.opts.LogCaller; policy == All || policy == Info {
|
||||
args = append(args, "caller", f.caller())
|
||||
}
|
||||
args = append(args, "level", level, "msg", msg)
|
||||
if key := *f.opts.LogInfoLevel; key != "" {
|
||||
args = append(args, key, level)
|
||||
}
|
||||
args = append(args, "msg", msg)
|
||||
return prefix, f.render(args, kvList)
|
||||
}
|
||||
|
||||
|
|
|
|||
105
vendor/github.com/go-logr/logr/funcr/slogsink.go
generated
vendored
Normal file
105
vendor/github.com/go-logr/logr/funcr/slogsink.go
generated
vendored
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
//go:build go1.21
|
||||
// +build go1.21
|
||||
|
||||
/*
|
||||
Copyright 2023 The logr Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package funcr
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
)
|
||||
|
||||
var _ logr.SlogSink = &fnlogger{}
|
||||
|
||||
const extraSlogSinkDepth = 3 // 2 for slog, 1 for SlogSink
|
||||
|
||||
func (l fnlogger) Handle(_ context.Context, record slog.Record) error {
|
||||
kvList := make([]any, 0, 2*record.NumAttrs())
|
||||
record.Attrs(func(attr slog.Attr) bool {
|
||||
kvList = attrToKVs(attr, kvList)
|
||||
return true
|
||||
})
|
||||
|
||||
if record.Level >= slog.LevelError {
|
||||
l.WithCallDepth(extraSlogSinkDepth).Error(nil, record.Message, kvList...)
|
||||
} else {
|
||||
level := l.levelFromSlog(record.Level)
|
||||
l.WithCallDepth(extraSlogSinkDepth).Info(level, record.Message, kvList...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l fnlogger) WithAttrs(attrs []slog.Attr) logr.SlogSink {
|
||||
kvList := make([]any, 0, 2*len(attrs))
|
||||
for _, attr := range attrs {
|
||||
kvList = attrToKVs(attr, kvList)
|
||||
}
|
||||
l.AddValues(kvList)
|
||||
return &l
|
||||
}
|
||||
|
||||
func (l fnlogger) WithGroup(name string) logr.SlogSink {
|
||||
l.startGroup(name)
|
||||
return &l
|
||||
}
|
||||
|
||||
// attrToKVs appends a slog.Attr to a logr-style kvList. It handle slog Groups
|
||||
// and other details of slog.
|
||||
func attrToKVs(attr slog.Attr, kvList []any) []any {
|
||||
attrVal := attr.Value.Resolve()
|
||||
if attrVal.Kind() == slog.KindGroup {
|
||||
groupVal := attrVal.Group()
|
||||
grpKVs := make([]any, 0, 2*len(groupVal))
|
||||
for _, attr := range groupVal {
|
||||
grpKVs = attrToKVs(attr, grpKVs)
|
||||
}
|
||||
if attr.Key == "" {
|
||||
// slog says we have to inline these
|
||||
kvList = append(kvList, grpKVs...)
|
||||
} else {
|
||||
kvList = append(kvList, attr.Key, PseudoStruct(grpKVs))
|
||||
}
|
||||
} else if attr.Key != "" {
|
||||
kvList = append(kvList, attr.Key, attrVal.Any())
|
||||
}
|
||||
|
||||
return kvList
|
||||
}
|
||||
|
||||
// levelFromSlog adjusts the level by the logger's verbosity and negates it.
|
||||
// It ensures that the result is >= 0. This is necessary because the result is
|
||||
// passed to a LogSink and that API did not historically document whether
|
||||
// levels could be negative or what that meant.
|
||||
//
|
||||
// Some example usage:
|
||||
//
|
||||
// logrV0 := getMyLogger()
|
||||
// logrV2 := logrV0.V(2)
|
||||
// slogV2 := slog.New(logr.ToSlogHandler(logrV2))
|
||||
// slogV2.Debug("msg") // =~ logrV2.V(4) =~ logrV0.V(6)
|
||||
// slogV2.Info("msg") // =~ logrV2.V(0) =~ logrV0.V(2)
|
||||
// slogv2.Warn("msg") // =~ logrV2.V(-4) =~ logrV0.V(0)
|
||||
func (l fnlogger) levelFromSlog(level slog.Level) int {
|
||||
result := -level
|
||||
if result < 0 {
|
||||
result = 0 // because LogSink doesn't expect negative V levels
|
||||
}
|
||||
return int(result)
|
||||
}
|
||||
43
vendor/github.com/go-logr/logr/logr.go
generated
vendored
43
vendor/github.com/go-logr/logr/logr.go
generated
vendored
|
|
@ -207,10 +207,6 @@ limitations under the License.
|
|||
// those.
|
||||
package logr
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
// New returns a new Logger instance. This is primarily used by libraries
|
||||
// implementing LogSink, rather than end users. Passing a nil sink will create
|
||||
// a Logger which discards all log lines.
|
||||
|
|
@ -410,45 +406,6 @@ func (l Logger) IsZero() bool {
|
|||
return l.sink == nil
|
||||
}
|
||||
|
||||
// contextKey is how we find Loggers in a context.Context.
|
||||
type contextKey struct{}
|
||||
|
||||
// FromContext returns a Logger from ctx or an error if no Logger is found.
|
||||
func FromContext(ctx context.Context) (Logger, error) {
|
||||
if v, ok := ctx.Value(contextKey{}).(Logger); ok {
|
||||
return v, nil
|
||||
}
|
||||
|
||||
return Logger{}, notFoundError{}
|
||||
}
|
||||
|
||||
// notFoundError exists to carry an IsNotFound method.
|
||||
type notFoundError struct{}
|
||||
|
||||
func (notFoundError) Error() string {
|
||||
return "no logr.Logger was present"
|
||||
}
|
||||
|
||||
func (notFoundError) IsNotFound() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// FromContextOrDiscard returns a Logger from ctx. If no Logger is found, this
|
||||
// returns a Logger that discards all log messages.
|
||||
func FromContextOrDiscard(ctx context.Context) Logger {
|
||||
if v, ok := ctx.Value(contextKey{}).(Logger); ok {
|
||||
return v
|
||||
}
|
||||
|
||||
return Discard()
|
||||
}
|
||||
|
||||
// NewContext returns a new Context, derived from ctx, which carries the
|
||||
// provided Logger.
|
||||
func NewContext(ctx context.Context, logger Logger) context.Context {
|
||||
return context.WithValue(ctx, contextKey{}, logger)
|
||||
}
|
||||
|
||||
// RuntimeInfo holds information that the logr "core" library knows which
|
||||
// LogSinks might want to know.
|
||||
type RuntimeInfo struct {
|
||||
|
|
|
|||
192
vendor/github.com/go-logr/logr/sloghandler.go
generated
vendored
Normal file
192
vendor/github.com/go-logr/logr/sloghandler.go
generated
vendored
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
//go:build go1.21
|
||||
// +build go1.21
|
||||
|
||||
/*
|
||||
Copyright 2023 The logr Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package logr
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
)
|
||||
|
||||
type slogHandler struct {
|
||||
// May be nil, in which case all logs get discarded.
|
||||
sink LogSink
|
||||
// Non-nil if sink is non-nil and implements SlogSink.
|
||||
slogSink SlogSink
|
||||
|
||||
// groupPrefix collects values from WithGroup calls. It gets added as
|
||||
// prefix to value keys when handling a log record.
|
||||
groupPrefix string
|
||||
|
||||
// levelBias can be set when constructing the handler to influence the
|
||||
// slog.Level of log records. A positive levelBias reduces the
|
||||
// slog.Level value. slog has no API to influence this value after the
|
||||
// handler got created, so it can only be set indirectly through
|
||||
// Logger.V.
|
||||
levelBias slog.Level
|
||||
}
|
||||
|
||||
var _ slog.Handler = &slogHandler{}
|
||||
|
||||
// groupSeparator is used to concatenate WithGroup names and attribute keys.
|
||||
const groupSeparator = "."
|
||||
|
||||
// GetLevel is used for black box unit testing.
|
||||
func (l *slogHandler) GetLevel() slog.Level {
|
||||
return l.levelBias
|
||||
}
|
||||
|
||||
func (l *slogHandler) Enabled(_ context.Context, level slog.Level) bool {
|
||||
return l.sink != nil && (level >= slog.LevelError || l.sink.Enabled(l.levelFromSlog(level)))
|
||||
}
|
||||
|
||||
func (l *slogHandler) Handle(ctx context.Context, record slog.Record) error {
|
||||
if l.slogSink != nil {
|
||||
// Only adjust verbosity level of log entries < slog.LevelError.
|
||||
if record.Level < slog.LevelError {
|
||||
record.Level -= l.levelBias
|
||||
}
|
||||
return l.slogSink.Handle(ctx, record)
|
||||
}
|
||||
|
||||
// No need to check for nil sink here because Handle will only be called
|
||||
// when Enabled returned true.
|
||||
|
||||
kvList := make([]any, 0, 2*record.NumAttrs())
|
||||
record.Attrs(func(attr slog.Attr) bool {
|
||||
kvList = attrToKVs(attr, l.groupPrefix, kvList)
|
||||
return true
|
||||
})
|
||||
if record.Level >= slog.LevelError {
|
||||
l.sinkWithCallDepth().Error(nil, record.Message, kvList...)
|
||||
} else {
|
||||
level := l.levelFromSlog(record.Level)
|
||||
l.sinkWithCallDepth().Info(level, record.Message, kvList...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// sinkWithCallDepth adjusts the stack unwinding so that when Error or Info
|
||||
// are called by Handle, code in slog gets skipped.
|
||||
//
|
||||
// This offset currently (Go 1.21.0) works for calls through
|
||||
// slog.New(ToSlogHandler(...)). There's no guarantee that the call
|
||||
// chain won't change. Wrapping the handler will also break unwinding. It's
|
||||
// still better than not adjusting at all....
|
||||
//
|
||||
// This cannot be done when constructing the handler because FromSlogHandler needs
|
||||
// access to the original sink without this adjustment. A second copy would
|
||||
// work, but then WithAttrs would have to be called for both of them.
|
||||
func (l *slogHandler) sinkWithCallDepth() LogSink {
|
||||
if sink, ok := l.sink.(CallDepthLogSink); ok {
|
||||
return sink.WithCallDepth(2)
|
||||
}
|
||||
return l.sink
|
||||
}
|
||||
|
||||
func (l *slogHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
|
||||
if l.sink == nil || len(attrs) == 0 {
|
||||
return l
|
||||
}
|
||||
|
||||
clone := *l
|
||||
if l.slogSink != nil {
|
||||
clone.slogSink = l.slogSink.WithAttrs(attrs)
|
||||
clone.sink = clone.slogSink
|
||||
} else {
|
||||
kvList := make([]any, 0, 2*len(attrs))
|
||||
for _, attr := range attrs {
|
||||
kvList = attrToKVs(attr, l.groupPrefix, kvList)
|
||||
}
|
||||
clone.sink = l.sink.WithValues(kvList...)
|
||||
}
|
||||
return &clone
|
||||
}
|
||||
|
||||
func (l *slogHandler) WithGroup(name string) slog.Handler {
|
||||
if l.sink == nil {
|
||||
return l
|
||||
}
|
||||
if name == "" {
|
||||
// slog says to inline empty groups
|
||||
return l
|
||||
}
|
||||
clone := *l
|
||||
if l.slogSink != nil {
|
||||
clone.slogSink = l.slogSink.WithGroup(name)
|
||||
clone.sink = clone.slogSink
|
||||
} else {
|
||||
clone.groupPrefix = addPrefix(clone.groupPrefix, name)
|
||||
}
|
||||
return &clone
|
||||
}
|
||||
|
||||
// attrToKVs appends a slog.Attr to a logr-style kvList. It handle slog Groups
|
||||
// and other details of slog.
|
||||
func attrToKVs(attr slog.Attr, groupPrefix string, kvList []any) []any {
|
||||
attrVal := attr.Value.Resolve()
|
||||
if attrVal.Kind() == slog.KindGroup {
|
||||
groupVal := attrVal.Group()
|
||||
grpKVs := make([]any, 0, 2*len(groupVal))
|
||||
prefix := groupPrefix
|
||||
if attr.Key != "" {
|
||||
prefix = addPrefix(groupPrefix, attr.Key)
|
||||
}
|
||||
for _, attr := range groupVal {
|
||||
grpKVs = attrToKVs(attr, prefix, grpKVs)
|
||||
}
|
||||
kvList = append(kvList, grpKVs...)
|
||||
} else if attr.Key != "" {
|
||||
kvList = append(kvList, addPrefix(groupPrefix, attr.Key), attrVal.Any())
|
||||
}
|
||||
|
||||
return kvList
|
||||
}
|
||||
|
||||
func addPrefix(prefix, name string) string {
|
||||
if prefix == "" {
|
||||
return name
|
||||
}
|
||||
if name == "" {
|
||||
return prefix
|
||||
}
|
||||
return prefix + groupSeparator + name
|
||||
}
|
||||
|
||||
// levelFromSlog adjusts the level by the logger's verbosity and negates it.
|
||||
// It ensures that the result is >= 0. This is necessary because the result is
|
||||
// passed to a LogSink and that API did not historically document whether
|
||||
// levels could be negative or what that meant.
|
||||
//
|
||||
// Some example usage:
|
||||
//
|
||||
// logrV0 := getMyLogger()
|
||||
// logrV2 := logrV0.V(2)
|
||||
// slogV2 := slog.New(logr.ToSlogHandler(logrV2))
|
||||
// slogV2.Debug("msg") // =~ logrV2.V(4) =~ logrV0.V(6)
|
||||
// slogV2.Info("msg") // =~ logrV2.V(0) =~ logrV0.V(2)
|
||||
// slogv2.Warn("msg") // =~ logrV2.V(-4) =~ logrV0.V(0)
|
||||
func (l *slogHandler) levelFromSlog(level slog.Level) int {
|
||||
result := -level
|
||||
result += l.levelBias // in case the original Logger had a V level
|
||||
if result < 0 {
|
||||
result = 0 // because LogSink doesn't expect negative V levels
|
||||
}
|
||||
return int(result)
|
||||
}
|
||||
100
vendor/github.com/go-logr/logr/slogr.go
generated
vendored
Normal file
100
vendor/github.com/go-logr/logr/slogr.go
generated
vendored
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
//go:build go1.21
|
||||
// +build go1.21
|
||||
|
||||
/*
|
||||
Copyright 2023 The logr Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package logr
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
)
|
||||
|
||||
// FromSlogHandler returns a Logger which writes to the slog.Handler.
|
||||
//
|
||||
// The logr verbosity level is mapped to slog levels such that V(0) becomes
|
||||
// slog.LevelInfo and V(4) becomes slog.LevelDebug.
|
||||
func FromSlogHandler(handler slog.Handler) Logger {
|
||||
if handler, ok := handler.(*slogHandler); ok {
|
||||
if handler.sink == nil {
|
||||
return Discard()
|
||||
}
|
||||
return New(handler.sink).V(int(handler.levelBias))
|
||||
}
|
||||
return New(&slogSink{handler: handler})
|
||||
}
|
||||
|
||||
// ToSlogHandler returns a slog.Handler which writes to the same sink as the Logger.
|
||||
//
|
||||
// The returned logger writes all records with level >= slog.LevelError as
|
||||
// error log entries with LogSink.Error, regardless of the verbosity level of
|
||||
// the Logger:
|
||||
//
|
||||
// logger := <some Logger with 0 as verbosity level>
|
||||
// slog.New(ToSlogHandler(logger.V(10))).Error(...) -> logSink.Error(...)
|
||||
//
|
||||
// The level of all other records gets reduced by the verbosity
|
||||
// level of the Logger and the result is negated. If it happens
|
||||
// to be negative, then it gets replaced by zero because a LogSink
|
||||
// is not expected to handled negative levels:
|
||||
//
|
||||
// slog.New(ToSlogHandler(logger)).Debug(...) -> logger.GetSink().Info(level=4, ...)
|
||||
// slog.New(ToSlogHandler(logger)).Warning(...) -> logger.GetSink().Info(level=0, ...)
|
||||
// slog.New(ToSlogHandler(logger)).Info(...) -> logger.GetSink().Info(level=0, ...)
|
||||
// slog.New(ToSlogHandler(logger.V(4))).Info(...) -> logger.GetSink().Info(level=4, ...)
|
||||
func ToSlogHandler(logger Logger) slog.Handler {
|
||||
if sink, ok := logger.GetSink().(*slogSink); ok && logger.GetV() == 0 {
|
||||
return sink.handler
|
||||
}
|
||||
|
||||
handler := &slogHandler{sink: logger.GetSink(), levelBias: slog.Level(logger.GetV())}
|
||||
if slogSink, ok := handler.sink.(SlogSink); ok {
|
||||
handler.slogSink = slogSink
|
||||
}
|
||||
return handler
|
||||
}
|
||||
|
||||
// SlogSink is an optional interface that a LogSink can implement to support
|
||||
// logging through the slog.Logger or slog.Handler APIs better. It then should
|
||||
// also support special slog values like slog.Group. When used as a
|
||||
// slog.Handler, the advantages are:
|
||||
//
|
||||
// - stack unwinding gets avoided in favor of logging the pre-recorded PC,
|
||||
// as intended by slog
|
||||
// - proper grouping of key/value pairs via WithGroup
|
||||
// - verbosity levels > slog.LevelInfo can be recorded
|
||||
// - less overhead
|
||||
//
|
||||
// Both APIs (Logger and slog.Logger/Handler) then are supported equally
|
||||
// well. Developers can pick whatever API suits them better and/or mix
|
||||
// packages which use either API in the same binary with a common logging
|
||||
// implementation.
|
||||
//
|
||||
// This interface is necessary because the type implementing the LogSink
|
||||
// interface cannot also implement the slog.Handler interface due to the
|
||||
// different prototype of the common Enabled method.
|
||||
//
|
||||
// An implementation could support both interfaces in two different types, but then
|
||||
// additional interfaces would be needed to convert between those types in FromSlogHandler
|
||||
// and ToSlogHandler.
|
||||
type SlogSink interface {
|
||||
LogSink
|
||||
|
||||
Handle(ctx context.Context, record slog.Record) error
|
||||
WithAttrs(attrs []slog.Attr) SlogSink
|
||||
WithGroup(name string) SlogSink
|
||||
}
|
||||
120
vendor/github.com/go-logr/logr/slogsink.go
generated
vendored
Normal file
120
vendor/github.com/go-logr/logr/slogsink.go
generated
vendored
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
//go:build go1.21
|
||||
// +build go1.21
|
||||
|
||||
/*
|
||||
Copyright 2023 The logr Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package logr
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"runtime"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
_ LogSink = &slogSink{}
|
||||
_ CallDepthLogSink = &slogSink{}
|
||||
_ Underlier = &slogSink{}
|
||||
)
|
||||
|
||||
// Underlier is implemented by the LogSink returned by NewFromLogHandler.
|
||||
type Underlier interface {
|
||||
// GetUnderlying returns the Handler used by the LogSink.
|
||||
GetUnderlying() slog.Handler
|
||||
}
|
||||
|
||||
const (
|
||||
// nameKey is used to log the `WithName` values as an additional attribute.
|
||||
nameKey = "logger"
|
||||
|
||||
// errKey is used to log the error parameter of Error as an additional attribute.
|
||||
errKey = "err"
|
||||
)
|
||||
|
||||
type slogSink struct {
|
||||
callDepth int
|
||||
name string
|
||||
handler slog.Handler
|
||||
}
|
||||
|
||||
func (l *slogSink) Init(info RuntimeInfo) {
|
||||
l.callDepth = info.CallDepth
|
||||
}
|
||||
|
||||
func (l *slogSink) GetUnderlying() slog.Handler {
|
||||
return l.handler
|
||||
}
|
||||
|
||||
func (l *slogSink) WithCallDepth(depth int) LogSink {
|
||||
newLogger := *l
|
||||
newLogger.callDepth += depth
|
||||
return &newLogger
|
||||
}
|
||||
|
||||
func (l *slogSink) Enabled(level int) bool {
|
||||
return l.handler.Enabled(context.Background(), slog.Level(-level))
|
||||
}
|
||||
|
||||
func (l *slogSink) Info(level int, msg string, kvList ...interface{}) {
|
||||
l.log(nil, msg, slog.Level(-level), kvList...)
|
||||
}
|
||||
|
||||
func (l *slogSink) Error(err error, msg string, kvList ...interface{}) {
|
||||
l.log(err, msg, slog.LevelError, kvList...)
|
||||
}
|
||||
|
||||
func (l *slogSink) log(err error, msg string, level slog.Level, kvList ...interface{}) {
|
||||
var pcs [1]uintptr
|
||||
// skip runtime.Callers, this function, Info/Error, and all helper functions above that.
|
||||
runtime.Callers(3+l.callDepth, pcs[:])
|
||||
|
||||
record := slog.NewRecord(time.Now(), level, msg, pcs[0])
|
||||
if l.name != "" {
|
||||
record.AddAttrs(slog.String(nameKey, l.name))
|
||||
}
|
||||
if err != nil {
|
||||
record.AddAttrs(slog.Any(errKey, err))
|
||||
}
|
||||
record.Add(kvList...)
|
||||
_ = l.handler.Handle(context.Background(), record)
|
||||
}
|
||||
|
||||
func (l slogSink) WithName(name string) LogSink {
|
||||
if l.name != "" {
|
||||
l.name += "/"
|
||||
}
|
||||
l.name += name
|
||||
return &l
|
||||
}
|
||||
|
||||
func (l slogSink) WithValues(kvList ...interface{}) LogSink {
|
||||
l.handler = l.handler.WithAttrs(kvListToAttrs(kvList...))
|
||||
return &l
|
||||
}
|
||||
|
||||
func kvListToAttrs(kvList ...interface{}) []slog.Attr {
|
||||
// We don't need the record itself, only its Add method.
|
||||
record := slog.NewRecord(time.Time{}, 0, "", 0)
|
||||
record.Add(kvList...)
|
||||
attrs := make([]slog.Attr, 0, record.NumAttrs())
|
||||
record.Attrs(func(attr slog.Attr) bool {
|
||||
attrs = append(attrs, attr)
|
||||
return true
|
||||
})
|
||||
return attrs
|
||||
}
|
||||
17
vendor/github.com/go-openapi/analysis/flatten.go
generated
vendored
17
vendor/github.com/go-openapi/analysis/flatten.go
generated
vendored
|
|
@ -267,6 +267,12 @@ func nameInlinedSchemas(opts *FlattenOpts) error {
|
|||
}
|
||||
|
||||
func removeUnused(opts *FlattenOpts) {
|
||||
for removeUnusedSinglePass(opts) {
|
||||
// continue until no unused definition remains
|
||||
}
|
||||
}
|
||||
|
||||
func removeUnusedSinglePass(opts *FlattenOpts) (hasRemoved bool) {
|
||||
expected := make(map[string]struct{})
|
||||
for k := range opts.Swagger().Definitions {
|
||||
expected[path.Join(definitionsPath, jsonpointer.Escape(k))] = struct{}{}
|
||||
|
|
@ -277,6 +283,7 @@ func removeUnused(opts *FlattenOpts) {
|
|||
}
|
||||
|
||||
for k := range expected {
|
||||
hasRemoved = true
|
||||
debugLog("removing unused definition %s", path.Base(k))
|
||||
if opts.Verbose {
|
||||
log.Printf("info: removing unused definition: %s", path.Base(k))
|
||||
|
|
@ -285,6 +292,8 @@ func removeUnused(opts *FlattenOpts) {
|
|||
}
|
||||
|
||||
opts.Spec.reload() // re-analyze
|
||||
|
||||
return hasRemoved
|
||||
}
|
||||
|
||||
func importKnownRef(entry sortref.RefRevIdx, refStr, newName string, opts *FlattenOpts) error {
|
||||
|
|
@ -331,7 +340,7 @@ func importNewRef(entry sortref.RefRevIdx, refStr string, opts *FlattenOpts) err
|
|||
}
|
||||
|
||||
// generate a unique name - isOAIGen means that a naming conflict was resolved by changing the name
|
||||
newName, isOAIGen = uniqifyName(opts.Swagger().Definitions, nameFromRef(entry.Ref))
|
||||
newName, isOAIGen = uniqifyName(opts.Swagger().Definitions, nameFromRef(entry.Ref, opts))
|
||||
debugLog("new name for [%s]: %s - with name conflict:%t", strings.Join(entry.Keys, ", "), newName, isOAIGen)
|
||||
|
||||
opts.flattenContext.resolved[refStr] = newName
|
||||
|
|
@ -649,6 +658,7 @@ func namePointers(opts *FlattenOpts) error {
|
|||
|
||||
refsToReplace := make(map[string]SchemaRef, len(opts.Spec.references.schemas))
|
||||
for k, ref := range opts.Spec.references.allRefs {
|
||||
debugLog("name pointers: %q => %#v", k, ref)
|
||||
if path.Dir(ref.String()) == definitionsPath {
|
||||
// this a ref to a top-level definition: ok
|
||||
continue
|
||||
|
|
@ -766,6 +776,10 @@ func flattenAnonPointer(key string, v SchemaRef, refsToReplace map[string]Schema
|
|||
|
||||
// identifying edge case when the namer did nothing because we point to a non-schema object
|
||||
// no definition is created and we expand the $ref for all callers
|
||||
debugLog("decide what to do with the schema pointed to: asch.IsSimpleSchema=%t, len(callers)=%d, parts.IsSharedParam=%t, parts.IsSharedResponse=%t",
|
||||
asch.IsSimpleSchema, len(callers), parts.IsSharedParam(), parts.IsSharedResponse(),
|
||||
)
|
||||
|
||||
if (!asch.IsSimpleSchema || len(callers) > 1) && !parts.IsSharedParam() && !parts.IsSharedResponse() {
|
||||
debugLog("replace JSON pointer at [%s] by definition: %s", key, v.Ref.String())
|
||||
if err := namer.Name(v.Ref.String(), v.Schema, asch); err != nil {
|
||||
|
|
@ -788,6 +802,7 @@ func flattenAnonPointer(key string, v SchemaRef, refsToReplace map[string]Schema
|
|||
return nil
|
||||
}
|
||||
|
||||
// everything that is a simple schema and not factorizable is expanded
|
||||
debugLog("expand JSON pointer for key=%s", key)
|
||||
|
||||
if err := replace.UpdateRefWithSchema(opts.Swagger(), key, v.Schema); err != nil {
|
||||
|
|
|
|||
37
vendor/github.com/go-openapi/analysis/flatten_name.go
generated
vendored
37
vendor/github.com/go-openapi/analysis/flatten_name.go
generated
vendored
|
|
@ -33,12 +33,14 @@ func (isn *InlineSchemaNamer) Name(key string, schema *spec.Schema, aschema *Ana
|
|||
}
|
||||
|
||||
// create unique name
|
||||
newName, isOAIGen := uniqifyName(isn.Spec.Definitions, swag.ToJSONName(name))
|
||||
mangle := mangler(isn.opts)
|
||||
newName, isOAIGen := uniqifyName(isn.Spec.Definitions, mangle(name))
|
||||
|
||||
// clone schema
|
||||
sch := schutils.Clone(schema)
|
||||
|
||||
// replace values on schema
|
||||
debugLog("rewriting schema to ref: key=%s with new name: %s", key, newName)
|
||||
if err := replace.RewriteSchemaToRef(isn.Spec, key,
|
||||
spec.MustCreateRef(path.Join(definitionsPath, newName))); err != nil {
|
||||
return fmt.Errorf("error while creating definition %q from inline schema: %w", newName, err)
|
||||
|
|
@ -149,13 +151,15 @@ func namesFromKey(parts sortref.SplitKey, aschema *AnalyzedSchema, operations ma
|
|||
startIndex int
|
||||
)
|
||||
|
||||
if parts.IsOperation() {
|
||||
switch {
|
||||
case parts.IsOperation():
|
||||
baseNames, startIndex = namesForOperation(parts, operations)
|
||||
}
|
||||
|
||||
// definitions
|
||||
if parts.IsDefinition() {
|
||||
case parts.IsDefinition():
|
||||
baseNames, startIndex = namesForDefinition(parts)
|
||||
default:
|
||||
// this a non-standard pointer: build a name by concatenating its parts
|
||||
baseNames = [][]string{parts}
|
||||
startIndex = len(baseNames) + 1
|
||||
}
|
||||
|
||||
result := make([]string, 0, len(baseNames))
|
||||
|
|
@ -169,6 +173,7 @@ func namesFromKey(parts sortref.SplitKey, aschema *AnalyzedSchema, operations ma
|
|||
}
|
||||
sort.Strings(result)
|
||||
|
||||
debugLog("names from parts: %v => %v", parts, result)
|
||||
return result
|
||||
}
|
||||
|
||||
|
|
@ -256,10 +261,20 @@ func partAdder(aschema *AnalyzedSchema) sortref.PartAdder {
|
|||
}
|
||||
}
|
||||
|
||||
func nameFromRef(ref spec.Ref) string {
|
||||
func mangler(o *FlattenOpts) func(string) string {
|
||||
if o.KeepNames {
|
||||
return func(in string) string { return in }
|
||||
}
|
||||
|
||||
return swag.ToJSONName
|
||||
}
|
||||
|
||||
func nameFromRef(ref spec.Ref, o *FlattenOpts) string {
|
||||
mangle := mangler(o)
|
||||
|
||||
u := ref.GetURL()
|
||||
if u.Fragment != "" {
|
||||
return swag.ToJSONName(path.Base(u.Fragment))
|
||||
return mangle(path.Base(u.Fragment))
|
||||
}
|
||||
|
||||
if u.Path != "" {
|
||||
|
|
@ -267,14 +282,14 @@ func nameFromRef(ref spec.Ref) string {
|
|||
if bn != "" && bn != "/" {
|
||||
ext := path.Ext(bn)
|
||||
if ext != "" {
|
||||
return swag.ToJSONName(bn[:len(bn)-len(ext)])
|
||||
return mangle(bn[:len(bn)-len(ext)])
|
||||
}
|
||||
|
||||
return swag.ToJSONName(bn)
|
||||
return mangle(bn)
|
||||
}
|
||||
}
|
||||
|
||||
return swag.ToJSONName(strings.ReplaceAll(u.Host, ".", " "))
|
||||
return mangle(strings.ReplaceAll(u.Host, ".", " "))
|
||||
}
|
||||
|
||||
// GenLocation indicates from which section of the specification (models or operations) a definition has been created.
|
||||
|
|
|
|||
1
vendor/github.com/go-openapi/analysis/flatten_options.go
generated
vendored
1
vendor/github.com/go-openapi/analysis/flatten_options.go
generated
vendored
|
|
@ -26,6 +26,7 @@ type FlattenOpts struct {
|
|||
Verbose bool // enable some reporting on possible name conflicts detected
|
||||
RemoveUnused bool // When true, remove unused parameters, responses and definitions after expansion/flattening
|
||||
ContinueOnError bool // Continue when spec expansion issues are found
|
||||
KeepNames bool // Do not attempt to jsonify names from references when flattening
|
||||
|
||||
/* Extra keys */
|
||||
_ struct{} // require keys
|
||||
|
|
|
|||
4
vendor/github.com/go-openapi/analysis/internal/debug/debug.go
generated
vendored
4
vendor/github.com/go-openapi/analysis/internal/debug/debug.go
generated
vendored
|
|
@ -29,7 +29,7 @@ var (
|
|||
// GetLogger provides a prefix debug logger
|
||||
func GetLogger(prefix string, debug bool) func(string, ...interface{}) {
|
||||
if debug {
|
||||
logger := log.New(output, fmt.Sprintf("%s:", prefix), log.LstdFlags)
|
||||
logger := log.New(output, prefix+":", log.LstdFlags)
|
||||
|
||||
return func(msg string, args ...interface{}) {
|
||||
_, file1, pos1, _ := runtime.Caller(1)
|
||||
|
|
@ -37,5 +37,5 @@ func GetLogger(prefix string, debug bool) func(string, ...interface{}) {
|
|||
}
|
||||
}
|
||||
|
||||
return func(msg string, args ...interface{}) {}
|
||||
return func(_ string, _ ...interface{}) {}
|
||||
}
|
||||
|
|
|
|||
38
vendor/github.com/go-openapi/analysis/internal/flatten/replace/replace.go
generated
vendored
38
vendor/github.com/go-openapi/analysis/internal/flatten/replace/replace.go
generated
vendored
|
|
@ -1,6 +1,7 @@
|
|||
package replace
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
|
|
@ -40,6 +41,8 @@ func RewriteSchemaToRef(sp *spec.Swagger, key string, ref spec.Ref) error {
|
|||
if refable.Schema != nil {
|
||||
refable.Schema = &spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}}
|
||||
}
|
||||
case map[string]interface{}: // this happens e.g. if a schema points to an extension unmarshaled as map[string]interface{}
|
||||
return rewriteParentRef(sp, key, ref)
|
||||
default:
|
||||
return fmt.Errorf("no schema with ref found at %s for %T", key, value)
|
||||
}
|
||||
|
|
@ -120,6 +123,9 @@ func rewriteParentRef(sp *spec.Swagger, key string, ref spec.Ref) error {
|
|||
case spec.SchemaProperties:
|
||||
container[entry] = spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}}
|
||||
|
||||
case *interface{}:
|
||||
*container = spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}}
|
||||
|
||||
// NOTE: can't have case *spec.SchemaOrBool = parent in this case is *Schema
|
||||
|
||||
default:
|
||||
|
|
@ -385,8 +391,9 @@ DOWNREF:
|
|||
err := asSchema.UnmarshalJSON(asJSON)
|
||||
if err != nil {
|
||||
return nil,
|
||||
fmt.Errorf("invalid type for resolved JSON pointer %s. Expected a schema a, got: %T",
|
||||
currentRef.String(), value)
|
||||
fmt.Errorf("invalid type for resolved JSON pointer %s. Expected a schema a, got: %T (%v)",
|
||||
currentRef.String(), value, err,
|
||||
)
|
||||
}
|
||||
warnings = append(warnings, fmt.Sprintf("found $ref %q (response) interpreted as schema", currentRef.String()))
|
||||
|
||||
|
|
@ -402,8 +409,9 @@ DOWNREF:
|
|||
var asSchema spec.Schema
|
||||
if err := asSchema.UnmarshalJSON(asJSON); err != nil {
|
||||
return nil,
|
||||
fmt.Errorf("invalid type for resolved JSON pointer %s. Expected a schema a, got: %T",
|
||||
currentRef.String(), value)
|
||||
fmt.Errorf("invalid type for resolved JSON pointer %s. Expected a schema a, got: %T (%v)",
|
||||
currentRef.String(), value, err,
|
||||
)
|
||||
}
|
||||
|
||||
warnings = append(warnings, fmt.Sprintf("found $ref %q (parameter) interpreted as schema", currentRef.String()))
|
||||
|
|
@ -414,9 +422,25 @@ DOWNREF:
|
|||
currentRef = asSchema.Ref
|
||||
|
||||
default:
|
||||
return nil,
|
||||
fmt.Errorf("unhandled type to resolve JSON pointer %s. Expected a Schema, got: %T",
|
||||
currentRef.String(), value)
|
||||
// fallback: attempts to resolve the pointer as a schema
|
||||
if refable == nil {
|
||||
break DOWNREF
|
||||
}
|
||||
|
||||
asJSON, _ := json.Marshal(refable)
|
||||
var asSchema spec.Schema
|
||||
if err := asSchema.UnmarshalJSON(asJSON); err != nil {
|
||||
return nil,
|
||||
fmt.Errorf("unhandled type to resolve JSON pointer %s. Expected a Schema, got: %T (%v)",
|
||||
currentRef.String(), value, err,
|
||||
)
|
||||
}
|
||||
warnings = append(warnings, fmt.Sprintf("found $ref %q (%T) interpreted as schema", currentRef.String(), refable))
|
||||
|
||||
if asSchema.Ref.String() == "" {
|
||||
break DOWNREF
|
||||
}
|
||||
currentRef = asSchema.Ref
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
2
vendor/github.com/go-openapi/analysis/internal/flatten/sortref/keys.go
generated
vendored
2
vendor/github.com/go-openapi/analysis/internal/flatten/sortref/keys.go
generated
vendored
|
|
@ -69,7 +69,7 @@ func KeyParts(key string) SplitKey {
|
|||
return res
|
||||
}
|
||||
|
||||
// SplitKey holds of the parts of a /-separated key, soi that their location may be determined.
|
||||
// SplitKey holds of the parts of a /-separated key, so that their location may be determined.
|
||||
type SplitKey []string
|
||||
|
||||
// IsDefinition is true when the split key is in the #/definitions section of a spec
|
||||
|
|
|
|||
14
vendor/github.com/go-openapi/analysis/mixin.go
generated
vendored
14
vendor/github.com/go-openapi/analysis/mixin.go
generated
vendored
|
|
@ -53,7 +53,7 @@ import (
|
|||
// collisions.
|
||||
func Mixin(primary *spec.Swagger, mixins ...*spec.Swagger) []string {
|
||||
skipped := make([]string, 0, len(mixins))
|
||||
opIds := getOpIds(primary)
|
||||
opIDs := getOpIDs(primary)
|
||||
initPrimary(primary)
|
||||
|
||||
for i, m := range mixins {
|
||||
|
|
@ -74,7 +74,7 @@ func Mixin(primary *spec.Swagger, mixins ...*spec.Swagger) []string {
|
|||
skipped = append(skipped, mergeDefinitions(primary, m)...)
|
||||
|
||||
// merging paths requires a map of operationIDs to work with
|
||||
skipped = append(skipped, mergePaths(primary, m, opIds, i)...)
|
||||
skipped = append(skipped, mergePaths(primary, m, opIDs, i)...)
|
||||
|
||||
skipped = append(skipped, mergeParameters(primary, m)...)
|
||||
|
||||
|
|
@ -84,9 +84,9 @@ func Mixin(primary *spec.Swagger, mixins ...*spec.Swagger) []string {
|
|||
return skipped
|
||||
}
|
||||
|
||||
// getOpIds extracts all the paths.<path>.operationIds from the given
|
||||
// getOpIDs extracts all the paths.<path>.operationIds from the given
|
||||
// spec and returns them as the keys in a map with 'true' values.
|
||||
func getOpIds(s *spec.Swagger) map[string]bool {
|
||||
func getOpIDs(s *spec.Swagger) map[string]bool {
|
||||
rv := make(map[string]bool)
|
||||
if s.Paths == nil {
|
||||
return rv
|
||||
|
|
@ -179,7 +179,7 @@ func mergeDefinitions(primary *spec.Swagger, m *spec.Swagger) (skipped []string)
|
|||
return
|
||||
}
|
||||
|
||||
func mergePaths(primary *spec.Swagger, m *spec.Swagger, opIds map[string]bool, mixIndex int) (skipped []string) {
|
||||
func mergePaths(primary *spec.Swagger, m *spec.Swagger, opIDs map[string]bool, mixIndex int) (skipped []string) {
|
||||
if m.Paths != nil {
|
||||
for k, v := range m.Paths.Paths {
|
||||
if _, exists := primary.Paths.Paths[k]; exists {
|
||||
|
|
@ -198,10 +198,10 @@ func mergePaths(primary *spec.Swagger, m *spec.Swagger, opIds map[string]bool, m
|
|||
// all the proivded specs are already unique.
|
||||
piops := pathItemOps(v)
|
||||
for _, piop := range piops {
|
||||
if opIds[piop.ID] {
|
||||
if opIDs[piop.ID] {
|
||||
piop.ID = fmt.Sprintf("%v%v%v", piop.ID, "Mixin", mixIndex)
|
||||
}
|
||||
opIds[piop.ID] = true
|
||||
opIDs[piop.ID] = true
|
||||
}
|
||||
primary.Paths.Paths[k] = v
|
||||
}
|
||||
|
|
|
|||
4
vendor/github.com/go-openapi/analysis/schema.go
generated
vendored
4
vendor/github.com/go-openapi/analysis/schema.go
generated
vendored
|
|
@ -1,7 +1,7 @@
|
|||
package analysis
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"errors"
|
||||
|
||||
"github.com/go-openapi/spec"
|
||||
"github.com/go-openapi/strfmt"
|
||||
|
|
@ -19,7 +19,7 @@ type SchemaOpts struct {
|
|||
// patterns.
|
||||
func Schema(opts SchemaOpts) (*AnalyzedSchema, error) {
|
||||
if opts.Schema == nil {
|
||||
return nil, fmt.Errorf("no schema to analyze")
|
||||
return nil, errors.New("no schema to analyze")
|
||||
}
|
||||
|
||||
a := &AnalyzedSchema{
|
||||
|
|
|
|||
26
vendor/github.com/go-openapi/jsonpointer/pointer.go
generated
vendored
26
vendor/github.com/go-openapi/jsonpointer/pointer.go
generated
vendored
|
|
@ -110,16 +110,36 @@ func SetForToken(document any, decodedToken string, value any) (any, error) {
|
|||
return document, setSingleImpl(document, value, decodedToken, swag.DefaultJSONNameProvider)
|
||||
}
|
||||
|
||||
func isNil(input any) bool {
|
||||
if input == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
kind := reflect.TypeOf(input).Kind()
|
||||
switch kind { //nolint:exhaustive
|
||||
case reflect.Ptr, reflect.Map, reflect.Slice, reflect.Chan:
|
||||
return reflect.ValueOf(input).IsNil()
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func getSingleImpl(node any, decodedToken string, nameProvider *swag.NameProvider) (any, reflect.Kind, error) {
|
||||
rValue := reflect.Indirect(reflect.ValueOf(node))
|
||||
kind := rValue.Kind()
|
||||
if isNil(node) {
|
||||
return nil, kind, fmt.Errorf("nil value has not field %q", decodedToken)
|
||||
}
|
||||
|
||||
if rValue.Type().Implements(jsonPointableType) {
|
||||
r, err := node.(JSONPointable).JSONLookup(decodedToken)
|
||||
switch typed := node.(type) {
|
||||
case JSONPointable:
|
||||
r, err := typed.JSONLookup(decodedToken)
|
||||
if err != nil {
|
||||
return nil, kind, err
|
||||
}
|
||||
return r, kind, nil
|
||||
case *any: // case of a pointer to interface, that is not resolved by reflect.Indirect
|
||||
return getSingleImpl(*typed, decodedToken, nameProvider)
|
||||
}
|
||||
|
||||
switch kind { //nolint:exhaustive
|
||||
|
|
@ -244,7 +264,7 @@ func (p *Pointer) set(node, data any, nameProvider *swag.NameProvider) error {
|
|||
knd := reflect.ValueOf(node).Kind()
|
||||
|
||||
if knd != reflect.Ptr && knd != reflect.Struct && knd != reflect.Map && knd != reflect.Slice && knd != reflect.Array {
|
||||
return fmt.Errorf("only structs, pointers, maps and slices are supported for setting values")
|
||||
return errors.New("only structs, pointers, maps and slices are supported for setting values")
|
||||
}
|
||||
|
||||
if nameProvider == nil {
|
||||
|
|
|
|||
2
vendor/github.com/go-openapi/loads/README.md
generated
vendored
2
vendor/github.com/go-openapi/loads/README.md
generated
vendored
|
|
@ -1,4 +1,4 @@
|
|||
# Loads OAI specs [](https://github.com/go-openapi/loads/actions?query=workflow%3A"go+test") [](https://codecov.io/gh/go-openapi/lods)
|
||||
# Loads OAI specs [](https://github.com/go-openapi/loads/actions?query=workflow%3A"go+test") [](https://codecov.io/gh/go-openapi/loads)
|
||||
|
||||
[](https://raw.githubusercontent.com/go-openapi/loads/master/LICENSE) [](http://godoc.org/github.com/go-openapi/loads)
|
||||
[](https://goreportcard.com/report/github.com/go-openapi/loads)
|
||||
|
|
|
|||
3
vendor/github.com/go-openapi/loads/TODO.md
generated
vendored
3
vendor/github.com/go-openapi/loads/TODO.md
generated
vendored
|
|
@ -1,3 +0,0 @@
|
|||
[x] why not filepath in JSONDoc()
|
||||
[] integration tests package
|
||||
[] relint
|
||||
2
vendor/github.com/go-openapi/loads/loaders.go
generated
vendored
2
vendor/github.com/go-openapi/loads/loaders.go
generated
vendored
|
|
@ -21,7 +21,7 @@ var (
|
|||
func init() {
|
||||
jsonLoader := &loader{
|
||||
DocLoaderWithMatch: DocLoaderWithMatch{
|
||||
Match: func(pth string) bool {
|
||||
Match: func(_ string) bool {
|
||||
return true
|
||||
},
|
||||
Fn: JSONDoc,
|
||||
|
|
|
|||
3
vendor/github.com/go-openapi/loads/spec.go
generated
vendored
3
vendor/github.com/go-openapi/loads/spec.go
generated
vendored
|
|
@ -248,7 +248,8 @@ func (d *Document) ResetDefinitions() *Document {
|
|||
|
||||
// Pristine creates a new pristine document instance based on the input data
|
||||
func (d *Document) Pristine() *Document {
|
||||
dd, _ := Analyzed(d.Raw(), d.Version())
|
||||
raw, _ := json.Marshal(d.Spec())
|
||||
dd, _ := Analyzed(raw, d.Version())
|
||||
dd.pathLoader = d.pathLoader
|
||||
dd.specFilePath = d.specFilePath
|
||||
|
||||
|
|
|
|||
131
vendor/github.com/go-openapi/runtime/bytestream.go
generated
vendored
131
vendor/github.com/go-openapi/runtime/bytestream.go
generated
vendored
|
|
@ -38,9 +38,16 @@ type byteStreamOpts struct {
|
|||
Close bool
|
||||
}
|
||||
|
||||
// ByteStreamConsumer creates a consumer for byte streams,
|
||||
// takes a Writer/BinaryUnmarshaler interface or binary slice by reference,
|
||||
// and reads from the provided reader
|
||||
// ByteStreamConsumer creates a consumer for byte streams.
|
||||
//
|
||||
// The consumer consumes from a provided reader into the data passed by reference.
|
||||
//
|
||||
// Supported output underlying types and interfaces, prioritized in this order:
|
||||
// - io.ReaderFrom (for maximum control)
|
||||
// - io.Writer (performs io.Copy)
|
||||
// - encoding.BinaryUnmarshaler
|
||||
// - *string
|
||||
// - *[]byte
|
||||
func ByteStreamConsumer(opts ...byteStreamOpt) Consumer {
|
||||
var vals byteStreamOpts
|
||||
for _, opt := range opts {
|
||||
|
|
@ -51,10 +58,13 @@ func ByteStreamConsumer(opts ...byteStreamOpt) Consumer {
|
|||
if reader == nil {
|
||||
return errors.New("ByteStreamConsumer requires a reader") // early exit
|
||||
}
|
||||
if data == nil {
|
||||
return errors.New("nil destination for ByteStreamConsumer")
|
||||
}
|
||||
|
||||
closer := defaultCloser
|
||||
if vals.Close {
|
||||
if cl, ok := reader.(io.Closer); ok {
|
||||
if cl, isReaderCloser := reader.(io.Closer); isReaderCloser {
|
||||
closer = cl.Close
|
||||
}
|
||||
}
|
||||
|
|
@ -62,34 +72,56 @@ func ByteStreamConsumer(opts ...byteStreamOpt) Consumer {
|
|||
_ = closer()
|
||||
}()
|
||||
|
||||
if wrtr, ok := data.(io.Writer); ok {
|
||||
_, err := io.Copy(wrtr, reader)
|
||||
if readerFrom, isReaderFrom := data.(io.ReaderFrom); isReaderFrom {
|
||||
_, err := readerFrom.ReadFrom(reader)
|
||||
return err
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
if writer, isDataWriter := data.(io.Writer); isDataWriter {
|
||||
_, err := io.Copy(writer, reader)
|
||||
return err
|
||||
}
|
||||
|
||||
// buffers input before writing to data
|
||||
var buf bytes.Buffer
|
||||
_, err := buf.ReadFrom(reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b := buf.Bytes()
|
||||
|
||||
if bu, ok := data.(encoding.BinaryUnmarshaler); ok {
|
||||
return bu.UnmarshalBinary(b)
|
||||
}
|
||||
switch destinationPointer := data.(type) {
|
||||
case encoding.BinaryUnmarshaler:
|
||||
return destinationPointer.UnmarshalBinary(b)
|
||||
case *any:
|
||||
switch (*destinationPointer).(type) {
|
||||
case string:
|
||||
*destinationPointer = string(b)
|
||||
|
||||
return nil
|
||||
|
||||
case []byte:
|
||||
*destinationPointer = b
|
||||
|
||||
if data != nil {
|
||||
if str, ok := data.(*string); ok {
|
||||
*str = string(b)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
default:
|
||||
// check for the underlying type to be pointer to []byte or string,
|
||||
if ptr := reflect.TypeOf(data); ptr.Kind() != reflect.Ptr {
|
||||
return errors.New("destination must be a pointer")
|
||||
}
|
||||
|
||||
if t := reflect.TypeOf(data); data != nil && t.Kind() == reflect.Ptr {
|
||||
v := reflect.Indirect(reflect.ValueOf(data))
|
||||
if t = v.Type(); t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Uint8 {
|
||||
t := v.Type()
|
||||
|
||||
switch {
|
||||
case t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Uint8:
|
||||
v.SetBytes(b)
|
||||
return nil
|
||||
|
||||
case t.Kind() == reflect.String:
|
||||
v.SetString(string(b))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -98,21 +130,35 @@ func ByteStreamConsumer(opts ...byteStreamOpt) Consumer {
|
|||
})
|
||||
}
|
||||
|
||||
// ByteStreamProducer creates a producer for byte streams,
|
||||
// takes a Reader/BinaryMarshaler interface or binary slice,
|
||||
// and writes to a writer (essentially a pipe)
|
||||
// ByteStreamProducer creates a producer for byte streams.
|
||||
//
|
||||
// The producer takes input data then writes to an output writer (essentially as a pipe).
|
||||
//
|
||||
// Supported input underlying types and interfaces, prioritized in this order:
|
||||
// - io.WriterTo (for maximum control)
|
||||
// - io.Reader (performs io.Copy). A ReadCloser is closed before exiting.
|
||||
// - encoding.BinaryMarshaler
|
||||
// - error (writes as a string)
|
||||
// - []byte
|
||||
// - string
|
||||
// - struct, other slices: writes as JSON
|
||||
func ByteStreamProducer(opts ...byteStreamOpt) Producer {
|
||||
var vals byteStreamOpts
|
||||
for _, opt := range opts {
|
||||
opt(&vals)
|
||||
}
|
||||
|
||||
return ProducerFunc(func(writer io.Writer, data interface{}) error {
|
||||
if writer == nil {
|
||||
return errors.New("ByteStreamProducer requires a writer") // early exit
|
||||
}
|
||||
if data == nil {
|
||||
return errors.New("nil data for ByteStreamProducer")
|
||||
}
|
||||
|
||||
closer := defaultCloser
|
||||
if vals.Close {
|
||||
if cl, ok := writer.(io.Closer); ok {
|
||||
if cl, isWriterCloser := writer.(io.Closer); isWriterCloser {
|
||||
closer = cl.Close
|
||||
}
|
||||
}
|
||||
|
|
@ -120,46 +166,51 @@ func ByteStreamProducer(opts ...byteStreamOpt) Producer {
|
|||
_ = closer()
|
||||
}()
|
||||
|
||||
if rc, ok := data.(io.ReadCloser); ok {
|
||||
if rc, isDataCloser := data.(io.ReadCloser); isDataCloser {
|
||||
defer rc.Close()
|
||||
}
|
||||
|
||||
if rdr, ok := data.(io.Reader); ok {
|
||||
_, err := io.Copy(writer, rdr)
|
||||
switch origin := data.(type) {
|
||||
case io.WriterTo:
|
||||
_, err := origin.WriteTo(writer)
|
||||
return err
|
||||
}
|
||||
|
||||
if bm, ok := data.(encoding.BinaryMarshaler); ok {
|
||||
bytes, err := bm.MarshalBinary()
|
||||
case io.Reader:
|
||||
_, err := io.Copy(writer, origin)
|
||||
return err
|
||||
|
||||
case encoding.BinaryMarshaler:
|
||||
bytes, err := origin.MarshalBinary()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = writer.Write(bytes)
|
||||
return err
|
||||
}
|
||||
|
||||
if data != nil {
|
||||
if str, ok := data.(string); ok {
|
||||
_, err := writer.Write([]byte(str))
|
||||
return err
|
||||
}
|
||||
|
||||
if e, ok := data.(error); ok {
|
||||
_, err := writer.Write([]byte(e.Error()))
|
||||
return err
|
||||
}
|
||||
case error:
|
||||
_, err := writer.Write([]byte(origin.Error()))
|
||||
return err
|
||||
|
||||
default:
|
||||
v := reflect.Indirect(reflect.ValueOf(data))
|
||||
if t := v.Type(); t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Uint8 {
|
||||
t := v.Type()
|
||||
|
||||
switch {
|
||||
case t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Uint8:
|
||||
_, err := writer.Write(v.Bytes())
|
||||
return err
|
||||
}
|
||||
if t := v.Type(); t.Kind() == reflect.Struct || t.Kind() == reflect.Slice {
|
||||
|
||||
case t.Kind() == reflect.String:
|
||||
_, err := writer.Write([]byte(v.String()))
|
||||
return err
|
||||
|
||||
case t.Kind() == reflect.Struct || t.Kind() == reflect.Slice:
|
||||
b, err := swag.WriteJSON(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = writer.Write(b)
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
4
vendor/github.com/go-openapi/runtime/client/request.go
generated
vendored
4
vendor/github.com/go-openapi/runtime/client/request.go
generated
vendored
|
|
@ -36,7 +36,7 @@ import (
|
|||
)
|
||||
|
||||
// NewRequest creates a new swagger http client request
|
||||
func newRequest(method, pathPattern string, writer runtime.ClientRequestWriter) (*request, error) {
|
||||
func newRequest(method, pathPattern string, writer runtime.ClientRequestWriter) *request {
|
||||
return &request{
|
||||
pathPattern: pathPattern,
|
||||
method: method,
|
||||
|
|
@ -45,7 +45,7 @@ func newRequest(method, pathPattern string, writer runtime.ClientRequestWriter)
|
|||
query: make(url.Values),
|
||||
timeout: DefaultTimeout,
|
||||
getBody: getRequestBuffer,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Request represents a swagger client request.
|
||||
|
|
|
|||
56
vendor/github.com/go-openapi/runtime/client/runtime.go
generated
vendored
56
vendor/github.com/go-openapi/runtime/client/runtime.go
generated
vendored
|
|
@ -22,6 +22,7 @@ import (
|
|||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"mime"
|
||||
"net/http"
|
||||
|
|
@ -31,12 +32,13 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/opentracing/opentracing-go"
|
||||
|
||||
"github.com/go-openapi/runtime"
|
||||
"github.com/go-openapi/runtime/logger"
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/go-openapi/runtime/yamlpc"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/opentracing/opentracing-go"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -142,7 +144,7 @@ func TLSClientAuth(opts TLSClientOptions) (*tls.Config, error) {
|
|||
return nil, fmt.Errorf("tls client priv key: %v", err)
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("tls client priv key: unsupported key type")
|
||||
return nil, errors.New("tls client priv key: unsupported key type")
|
||||
}
|
||||
|
||||
block = pem.Block{Type: "PRIVATE KEY", Bytes: keyBytes}
|
||||
|
|
@ -378,14 +380,11 @@ func (r *Runtime) EnableConnectionReuse() {
|
|||
func (r *Runtime) createHttpRequest(operation *runtime.ClientOperation) (*request, *http.Request, error) { //nolint:revive,stylecheck
|
||||
params, _, auth := operation.Params, operation.Reader, operation.AuthInfo
|
||||
|
||||
request, err := newRequest(operation.Method, operation.PathPattern, params)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
request := newRequest(operation.Method, operation.PathPattern, params)
|
||||
|
||||
var accept []string
|
||||
accept = append(accept, operation.ProducesMediaTypes...)
|
||||
if err = request.SetHeaderParam(runtime.HeaderAccept, accept...); err != nil {
|
||||
if err := request.SetHeaderParam(runtime.HeaderAccept, accept...); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
|
|
@ -457,27 +456,36 @@ func (r *Runtime) Submit(operation *runtime.ClientOperation) (interface{}, error
|
|||
r.logger.Debugf("%s\n", string(b))
|
||||
}
|
||||
|
||||
var hasTimeout bool
|
||||
pctx := operation.Context
|
||||
if pctx == nil {
|
||||
pctx = r.Context
|
||||
} else {
|
||||
hasTimeout = true
|
||||
var parentCtx context.Context
|
||||
switch {
|
||||
case operation.Context != nil:
|
||||
parentCtx = operation.Context
|
||||
case r.Context != nil:
|
||||
parentCtx = r.Context
|
||||
default:
|
||||
parentCtx = context.Background()
|
||||
}
|
||||
if pctx == nil {
|
||||
pctx = context.Background()
|
||||
}
|
||||
var ctx context.Context
|
||||
var cancel context.CancelFunc
|
||||
if hasTimeout {
|
||||
ctx, cancel = context.WithCancel(pctx)
|
||||
|
||||
var (
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
)
|
||||
if request.timeout == 0 {
|
||||
// There may be a deadline in the context passed to the operation.
|
||||
// Otherwise, there is no timeout set.
|
||||
ctx, cancel = context.WithCancel(parentCtx)
|
||||
} else {
|
||||
ctx, cancel = context.WithTimeout(pctx, request.timeout)
|
||||
// Sets the timeout passed from request params (by default runtime.DefaultTimeout).
|
||||
// If there is already a deadline in the parent context, the shortest will
|
||||
// apply.
|
||||
ctx, cancel = context.WithTimeout(parentCtx, request.timeout)
|
||||
}
|
||||
defer cancel()
|
||||
|
||||
client := operation.Client
|
||||
if client == nil {
|
||||
var client *http.Client
|
||||
if operation.Client != nil {
|
||||
client = operation.Client
|
||||
} else {
|
||||
client = r.client
|
||||
}
|
||||
req = req.WithContext(ctx)
|
||||
|
|
|
|||
337
vendor/github.com/go-openapi/runtime/csv.go
generated
vendored
337
vendor/github.com/go-openapi/runtime/csv.go
generated
vendored
|
|
@ -16,62 +16,335 @@ package runtime
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding"
|
||||
"encoding/csv"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
// CSVConsumer creates a new CSV consumer
|
||||
func CSVConsumer() Consumer {
|
||||
// CSVConsumer creates a new CSV consumer.
|
||||
//
|
||||
// The consumer consumes CSV records from a provided reader into the data passed by reference.
|
||||
//
|
||||
// CSVOpts options may be specified to alter the default CSV behavior on the reader and the writer side (e.g. separator, skip header, ...).
|
||||
// The defaults are those of the standard library's csv.Reader and csv.Writer.
|
||||
//
|
||||
// Supported output underlying types and interfaces, prioritized in this order:
|
||||
// - *csv.Writer
|
||||
// - CSVWriter (writer options are ignored)
|
||||
// - io.Writer (as raw bytes)
|
||||
// - io.ReaderFrom (as raw bytes)
|
||||
// - encoding.BinaryUnmarshaler (as raw bytes)
|
||||
// - *[][]string (as a collection of records)
|
||||
// - *[]byte (as raw bytes)
|
||||
// - *string (a raw bytes)
|
||||
//
|
||||
// The consumer prioritizes situations where buffering the input is not required.
|
||||
func CSVConsumer(opts ...CSVOpt) Consumer {
|
||||
o := csvOptsWithDefaults(opts)
|
||||
|
||||
return ConsumerFunc(func(reader io.Reader, data interface{}) error {
|
||||
if reader == nil {
|
||||
return errors.New("CSVConsumer requires a reader")
|
||||
}
|
||||
if data == nil {
|
||||
return errors.New("nil destination for CSVConsumer")
|
||||
}
|
||||
|
||||
csvReader := csv.NewReader(reader)
|
||||
writer, ok := data.(io.Writer)
|
||||
if !ok {
|
||||
return errors.New("data type must be io.Writer")
|
||||
}
|
||||
csvWriter := csv.NewWriter(writer)
|
||||
records, err := csvReader.ReadAll()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, r := range records {
|
||||
if err := csvWriter.Write(r); err != nil {
|
||||
return err
|
||||
o.applyToReader(csvReader)
|
||||
closer := defaultCloser
|
||||
if o.closeStream {
|
||||
if cl, isReaderCloser := reader.(io.Closer); isReaderCloser {
|
||||
closer = cl.Close
|
||||
}
|
||||
}
|
||||
defer func() {
|
||||
_ = closer()
|
||||
}()
|
||||
|
||||
switch destination := data.(type) {
|
||||
case *csv.Writer:
|
||||
csvWriter := destination
|
||||
o.applyToWriter(csvWriter)
|
||||
|
||||
return pipeCSV(csvWriter, csvReader, o)
|
||||
|
||||
case CSVWriter:
|
||||
csvWriter := destination
|
||||
// no writer options available
|
||||
|
||||
return pipeCSV(csvWriter, csvReader, o)
|
||||
|
||||
case io.Writer:
|
||||
csvWriter := csv.NewWriter(destination)
|
||||
o.applyToWriter(csvWriter)
|
||||
|
||||
return pipeCSV(csvWriter, csvReader, o)
|
||||
|
||||
case io.ReaderFrom:
|
||||
var buf bytes.Buffer
|
||||
csvWriter := csv.NewWriter(&buf)
|
||||
o.applyToWriter(csvWriter)
|
||||
if err := bufferedCSV(csvWriter, csvReader, o); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := destination.ReadFrom(&buf)
|
||||
|
||||
return err
|
||||
|
||||
case encoding.BinaryUnmarshaler:
|
||||
var buf bytes.Buffer
|
||||
csvWriter := csv.NewWriter(&buf)
|
||||
o.applyToWriter(csvWriter)
|
||||
if err := bufferedCSV(csvWriter, csvReader, o); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return destination.UnmarshalBinary(buf.Bytes())
|
||||
|
||||
default:
|
||||
// support *[][]string, *[]byte, *string
|
||||
if ptr := reflect.TypeOf(data); ptr.Kind() != reflect.Ptr {
|
||||
return errors.New("destination must be a pointer")
|
||||
}
|
||||
|
||||
v := reflect.Indirect(reflect.ValueOf(data))
|
||||
t := v.Type()
|
||||
|
||||
switch {
|
||||
case t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Slice && t.Elem().Elem().Kind() == reflect.String:
|
||||
csvWriter := &csvRecordsWriter{}
|
||||
// writer options are ignored
|
||||
if err := pipeCSV(csvWriter, csvReader, o); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
v.Grow(len(csvWriter.records))
|
||||
v.SetCap(len(csvWriter.records)) // in case Grow was unnessary, trim down the capacity
|
||||
v.SetLen(len(csvWriter.records))
|
||||
reflect.Copy(v, reflect.ValueOf(csvWriter.records))
|
||||
|
||||
return nil
|
||||
|
||||
case t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Uint8:
|
||||
var buf bytes.Buffer
|
||||
csvWriter := csv.NewWriter(&buf)
|
||||
o.applyToWriter(csvWriter)
|
||||
if err := bufferedCSV(csvWriter, csvReader, o); err != nil {
|
||||
return err
|
||||
}
|
||||
v.SetBytes(buf.Bytes())
|
||||
|
||||
return nil
|
||||
|
||||
case t.Kind() == reflect.String:
|
||||
var buf bytes.Buffer
|
||||
csvWriter := csv.NewWriter(&buf)
|
||||
o.applyToWriter(csvWriter)
|
||||
if err := bufferedCSV(csvWriter, csvReader, o); err != nil {
|
||||
return err
|
||||
}
|
||||
v.SetString(buf.String())
|
||||
|
||||
return nil
|
||||
|
||||
default:
|
||||
return fmt.Errorf("%v (%T) is not supported by the CSVConsumer, %s",
|
||||
data, data, "can be resolved by supporting CSVWriter/Writer/BinaryUnmarshaler interface",
|
||||
)
|
||||
}
|
||||
}
|
||||
csvWriter.Flush()
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// CSVProducer creates a new CSV producer
|
||||
func CSVProducer() Producer {
|
||||
// CSVProducer creates a new CSV producer.
|
||||
//
|
||||
// The producer takes input data then writes as CSV to an output writer (essentially as a pipe).
|
||||
//
|
||||
// Supported input underlying types and interfaces, prioritized in this order:
|
||||
// - *csv.Reader
|
||||
// - CSVReader (reader options are ignored)
|
||||
// - io.Reader
|
||||
// - io.WriterTo
|
||||
// - encoding.BinaryMarshaler
|
||||
// - [][]string
|
||||
// - []byte
|
||||
// - string
|
||||
//
|
||||
// The producer prioritizes situations where buffering the input is not required.
|
||||
func CSVProducer(opts ...CSVOpt) Producer {
|
||||
o := csvOptsWithDefaults(opts)
|
||||
|
||||
return ProducerFunc(func(writer io.Writer, data interface{}) error {
|
||||
if writer == nil {
|
||||
return errors.New("CSVProducer requires a writer")
|
||||
}
|
||||
|
||||
dataBytes, ok := data.([]byte)
|
||||
if !ok {
|
||||
return errors.New("data type must be byte array")
|
||||
if data == nil {
|
||||
return errors.New("nil data for CSVProducer")
|
||||
}
|
||||
|
||||
csvReader := csv.NewReader(bytes.NewBuffer(dataBytes))
|
||||
records, err := csvReader.ReadAll()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
csvWriter := csv.NewWriter(writer)
|
||||
for _, r := range records {
|
||||
if err := csvWriter.Write(r); err != nil {
|
||||
return err
|
||||
o.applyToWriter(csvWriter)
|
||||
closer := defaultCloser
|
||||
if o.closeStream {
|
||||
if cl, isWriterCloser := writer.(io.Closer); isWriterCloser {
|
||||
closer = cl.Close
|
||||
}
|
||||
}
|
||||
defer func() {
|
||||
_ = closer()
|
||||
}()
|
||||
|
||||
if rc, isDataCloser := data.(io.ReadCloser); isDataCloser {
|
||||
defer rc.Close()
|
||||
}
|
||||
|
||||
switch origin := data.(type) {
|
||||
case *csv.Reader:
|
||||
csvReader := origin
|
||||
o.applyToReader(csvReader)
|
||||
|
||||
return pipeCSV(csvWriter, csvReader, o)
|
||||
|
||||
case CSVReader:
|
||||
csvReader := origin
|
||||
// no reader options available
|
||||
|
||||
return pipeCSV(csvWriter, csvReader, o)
|
||||
|
||||
case io.Reader:
|
||||
csvReader := csv.NewReader(origin)
|
||||
o.applyToReader(csvReader)
|
||||
|
||||
return pipeCSV(csvWriter, csvReader, o)
|
||||
|
||||
case io.WriterTo:
|
||||
// async piping of the writes performed by WriteTo
|
||||
r, w := io.Pipe()
|
||||
csvReader := csv.NewReader(r)
|
||||
o.applyToReader(csvReader)
|
||||
|
||||
pipe, _ := errgroup.WithContext(context.Background())
|
||||
pipe.Go(func() error {
|
||||
_, err := origin.WriteTo(w)
|
||||
_ = w.Close()
|
||||
return err
|
||||
})
|
||||
|
||||
pipe.Go(func() error {
|
||||
defer func() {
|
||||
_ = r.Close()
|
||||
}()
|
||||
|
||||
return pipeCSV(csvWriter, csvReader, o)
|
||||
})
|
||||
|
||||
return pipe.Wait()
|
||||
|
||||
case encoding.BinaryMarshaler:
|
||||
buf, err := origin.MarshalBinary()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rdr := bytes.NewBuffer(buf)
|
||||
csvReader := csv.NewReader(rdr)
|
||||
|
||||
return bufferedCSV(csvWriter, csvReader, o)
|
||||
|
||||
default:
|
||||
// support [][]string, []byte, string (or pointers to those)
|
||||
v := reflect.Indirect(reflect.ValueOf(data))
|
||||
t := v.Type()
|
||||
|
||||
switch {
|
||||
case t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Slice && t.Elem().Elem().Kind() == reflect.String:
|
||||
csvReader := &csvRecordsWriter{
|
||||
records: make([][]string, v.Len()),
|
||||
}
|
||||
reflect.Copy(reflect.ValueOf(csvReader.records), v)
|
||||
|
||||
return pipeCSV(csvWriter, csvReader, o)
|
||||
|
||||
case t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Uint8:
|
||||
buf := bytes.NewBuffer(v.Bytes())
|
||||
csvReader := csv.NewReader(buf)
|
||||
o.applyToReader(csvReader)
|
||||
|
||||
return bufferedCSV(csvWriter, csvReader, o)
|
||||
|
||||
case t.Kind() == reflect.String:
|
||||
buf := bytes.NewBufferString(v.String())
|
||||
csvReader := csv.NewReader(buf)
|
||||
o.applyToReader(csvReader)
|
||||
|
||||
return bufferedCSV(csvWriter, csvReader, o)
|
||||
|
||||
default:
|
||||
return fmt.Errorf("%v (%T) is not supported by the CSVProducer, %s",
|
||||
data, data, "can be resolved by supporting CSVReader/Reader/BinaryMarshaler interface",
|
||||
)
|
||||
}
|
||||
}
|
||||
csvWriter.Flush()
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// pipeCSV copies CSV records from a CSV reader to a CSV writer
|
||||
func pipeCSV(csvWriter CSVWriter, csvReader CSVReader, opts csvOpts) error {
|
||||
for ; opts.skippedLines > 0; opts.skippedLines-- {
|
||||
_, err := csvReader.Read()
|
||||
if err != nil {
|
||||
if errors.Is(err, io.EOF) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
record, err := csvReader.Read()
|
||||
if err != nil {
|
||||
if errors.Is(err, io.EOF) {
|
||||
break
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
if err := csvWriter.Write(record); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
csvWriter.Flush()
|
||||
|
||||
return csvWriter.Error()
|
||||
}
|
||||
|
||||
// bufferedCSV copies CSV records from a CSV reader to a CSV writer,
|
||||
// by first reading all records then writing them at once.
|
||||
func bufferedCSV(csvWriter *csv.Writer, csvReader *csv.Reader, opts csvOpts) error {
|
||||
for ; opts.skippedLines > 0; opts.skippedLines-- {
|
||||
_, err := csvReader.Read()
|
||||
if err != nil {
|
||||
if errors.Is(err, io.EOF) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
records, err := csvReader.ReadAll()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return csvWriter.WriteAll(records)
|
||||
}
|
||||
|
|
|
|||
121
vendor/github.com/go-openapi/runtime/csv_options.go
generated
vendored
Normal file
121
vendor/github.com/go-openapi/runtime/csv_options.go
generated
vendored
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
package runtime
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"io"
|
||||
)
|
||||
|
||||
// CSVOpts alter the behavior of the CSV consumer or producer.
|
||||
type CSVOpt func(*csvOpts)
|
||||
|
||||
type csvOpts struct {
|
||||
csvReader csv.Reader
|
||||
csvWriter csv.Writer
|
||||
skippedLines int
|
||||
closeStream bool
|
||||
}
|
||||
|
||||
// WithCSVReaderOpts specifies the options to csv.Reader
|
||||
// when reading CSV.
|
||||
func WithCSVReaderOpts(reader csv.Reader) CSVOpt {
|
||||
return func(o *csvOpts) {
|
||||
o.csvReader = reader
|
||||
}
|
||||
}
|
||||
|
||||
// WithCSVWriterOpts specifies the options to csv.Writer
|
||||
// when writing CSV.
|
||||
func WithCSVWriterOpts(writer csv.Writer) CSVOpt {
|
||||
return func(o *csvOpts) {
|
||||
o.csvWriter = writer
|
||||
}
|
||||
}
|
||||
|
||||
// WithCSVSkipLines will skip header lines.
|
||||
func WithCSVSkipLines(skipped int) CSVOpt {
|
||||
return func(o *csvOpts) {
|
||||
o.skippedLines = skipped
|
||||
}
|
||||
}
|
||||
|
||||
func WithCSVClosesStream() CSVOpt {
|
||||
return func(o *csvOpts) {
|
||||
o.closeStream = true
|
||||
}
|
||||
}
|
||||
|
||||
func (o csvOpts) applyToReader(in *csv.Reader) {
|
||||
if o.csvReader.Comma != 0 {
|
||||
in.Comma = o.csvReader.Comma
|
||||
}
|
||||
if o.csvReader.Comment != 0 {
|
||||
in.Comment = o.csvReader.Comment
|
||||
}
|
||||
if o.csvReader.FieldsPerRecord != 0 {
|
||||
in.FieldsPerRecord = o.csvReader.FieldsPerRecord
|
||||
}
|
||||
|
||||
in.LazyQuotes = o.csvReader.LazyQuotes
|
||||
in.TrimLeadingSpace = o.csvReader.TrimLeadingSpace
|
||||
in.ReuseRecord = o.csvReader.ReuseRecord
|
||||
}
|
||||
|
||||
func (o csvOpts) applyToWriter(in *csv.Writer) {
|
||||
if o.csvWriter.Comma != 0 {
|
||||
in.Comma = o.csvWriter.Comma
|
||||
}
|
||||
in.UseCRLF = o.csvWriter.UseCRLF
|
||||
}
|
||||
|
||||
func csvOptsWithDefaults(opts []CSVOpt) csvOpts {
|
||||
var o csvOpts
|
||||
for _, apply := range opts {
|
||||
apply(&o)
|
||||
}
|
||||
|
||||
return o
|
||||
}
|
||||
|
||||
type CSVWriter interface {
|
||||
Write([]string) error
|
||||
Flush()
|
||||
Error() error
|
||||
}
|
||||
|
||||
type CSVReader interface {
|
||||
Read() ([]string, error)
|
||||
}
|
||||
|
||||
var (
|
||||
_ CSVWriter = &csvRecordsWriter{}
|
||||
_ CSVReader = &csvRecordsWriter{}
|
||||
)
|
||||
|
||||
// csvRecordsWriter is an internal container to move CSV records back and forth
|
||||
type csvRecordsWriter struct {
|
||||
i int
|
||||
records [][]string
|
||||
}
|
||||
|
||||
func (w *csvRecordsWriter) Write(record []string) error {
|
||||
w.records = append(w.records, record)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *csvRecordsWriter) Read() ([]string, error) {
|
||||
if w.i >= len(w.records) {
|
||||
return nil, io.EOF
|
||||
}
|
||||
defer func() {
|
||||
w.i++
|
||||
}()
|
||||
|
||||
return w.records[w.i], nil
|
||||
}
|
||||
|
||||
func (w *csvRecordsWriter) Flush() {}
|
||||
|
||||
func (w *csvRecordsWriter) Error() error {
|
||||
return nil
|
||||
}
|
||||
2
vendor/github.com/go-openapi/runtime/logger/standard.go
generated
vendored
2
vendor/github.com/go-openapi/runtime/logger/standard.go
generated
vendored
|
|
@ -5,6 +5,8 @@ import (
|
|||
"os"
|
||||
)
|
||||
|
||||
var _ Logger = StandardLogger{}
|
||||
|
||||
type StandardLogger struct{}
|
||||
|
||||
func (StandardLogger) Printf(format string, args ...interface{}) {
|
||||
|
|
|
|||
161
vendor/github.com/go-openapi/runtime/middleware/context.go
generated
vendored
161
vendor/github.com/go-openapi/runtime/middleware/context.go
generated
vendored
|
|
@ -18,6 +18,8 @@ import (
|
|||
stdContext "context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
|
|
@ -35,12 +37,21 @@ import (
|
|||
|
||||
// Debug when true turns on verbose logging
|
||||
var Debug = logger.DebugEnabled()
|
||||
|
||||
// Logger is the standard libray logger used for printing debug messages
|
||||
var Logger logger.Logger = logger.StandardLogger{}
|
||||
|
||||
func debugLog(format string, args ...interface{}) { //nolint:goprintffuncname
|
||||
if Debug {
|
||||
Logger.Printf(format, args...)
|
||||
func debugLogfFunc(lg logger.Logger) func(string, ...any) {
|
||||
if logger.DebugEnabled() {
|
||||
if lg == nil {
|
||||
return Logger.Debugf
|
||||
}
|
||||
|
||||
return lg.Debugf
|
||||
}
|
||||
|
||||
// muted logger
|
||||
return func(_ string, _ ...any) {}
|
||||
}
|
||||
|
||||
// A Builder can create middlewares
|
||||
|
|
@ -73,10 +84,11 @@ func (fn ResponderFunc) WriteResponse(rw http.ResponseWriter, pr runtime.Produce
|
|||
// used throughout to store request context with the standard context attached
|
||||
// to the http.Request
|
||||
type Context struct {
|
||||
spec *loads.Document
|
||||
analyzer *analysis.Spec
|
||||
api RoutableAPI
|
||||
router Router
|
||||
spec *loads.Document
|
||||
analyzer *analysis.Spec
|
||||
api RoutableAPI
|
||||
router Router
|
||||
debugLogf func(string, ...any) // a logging function to debug context and all components using it
|
||||
}
|
||||
|
||||
type routableUntypedAPI struct {
|
||||
|
|
@ -189,7 +201,9 @@ func (r *routableUntypedAPI) DefaultConsumes() string {
|
|||
return r.defaultConsumes
|
||||
}
|
||||
|
||||
// NewRoutableContext creates a new context for a routable API
|
||||
// NewRoutableContext creates a new context for a routable API.
|
||||
//
|
||||
// If a nil Router is provided, the DefaultRouter (denco-based) will be used.
|
||||
func NewRoutableContext(spec *loads.Document, routableAPI RoutableAPI, routes Router) *Context {
|
||||
var an *analysis.Spec
|
||||
if spec != nil {
|
||||
|
|
@ -199,26 +213,40 @@ func NewRoutableContext(spec *loads.Document, routableAPI RoutableAPI, routes Ro
|
|||
return NewRoutableContextWithAnalyzedSpec(spec, an, routableAPI, routes)
|
||||
}
|
||||
|
||||
// NewRoutableContextWithAnalyzedSpec is like NewRoutableContext but takes in input the analysed spec too
|
||||
// NewRoutableContextWithAnalyzedSpec is like NewRoutableContext but takes as input an already analysed spec.
|
||||
//
|
||||
// If a nil Router is provided, the DefaultRouter (denco-based) will be used.
|
||||
func NewRoutableContextWithAnalyzedSpec(spec *loads.Document, an *analysis.Spec, routableAPI RoutableAPI, routes Router) *Context {
|
||||
// Either there are no spec doc and analysis, or both of them.
|
||||
if !((spec == nil && an == nil) || (spec != nil && an != nil)) {
|
||||
panic(errors.New(http.StatusInternalServerError, "routable context requires either both spec doc and analysis, or none of them"))
|
||||
}
|
||||
|
||||
ctx := &Context{spec: spec, api: routableAPI, analyzer: an, router: routes}
|
||||
return ctx
|
||||
return &Context{
|
||||
spec: spec,
|
||||
api: routableAPI,
|
||||
analyzer: an,
|
||||
router: routes,
|
||||
debugLogf: debugLogfFunc(nil),
|
||||
}
|
||||
}
|
||||
|
||||
// NewContext creates a new context wrapper
|
||||
// NewContext creates a new context wrapper.
|
||||
//
|
||||
// If a nil Router is provided, the DefaultRouter (denco-based) will be used.
|
||||
func NewContext(spec *loads.Document, api *untyped.API, routes Router) *Context {
|
||||
var an *analysis.Spec
|
||||
if spec != nil {
|
||||
an = analysis.New(spec.Spec())
|
||||
}
|
||||
ctx := &Context{spec: spec, analyzer: an}
|
||||
ctx := &Context{
|
||||
spec: spec,
|
||||
analyzer: an,
|
||||
router: routes,
|
||||
debugLogf: debugLogfFunc(nil),
|
||||
}
|
||||
ctx.api = newRoutableUntypedAPI(spec, api, ctx)
|
||||
ctx.router = routes
|
||||
|
||||
return ctx
|
||||
}
|
||||
|
||||
|
|
@ -282,6 +310,13 @@ func (c *Context) BasePath() string {
|
|||
return c.spec.BasePath()
|
||||
}
|
||||
|
||||
// SetLogger allows for injecting a logger to catch debug entries.
|
||||
//
|
||||
// The logger is enabled in DEBUG mode only.
|
||||
func (c *Context) SetLogger(lg logger.Logger) {
|
||||
c.debugLogf = debugLogfFunc(lg)
|
||||
}
|
||||
|
||||
// RequiredProduces returns the accepted content types for responses
|
||||
func (c *Context) RequiredProduces() []string {
|
||||
return c.analyzer.RequiredProduces()
|
||||
|
|
@ -299,6 +334,7 @@ func (c *Context) BindValidRequest(request *http.Request, route *MatchedRoute, b
|
|||
if err != nil {
|
||||
res = append(res, err)
|
||||
} else {
|
||||
c.debugLogf("validating content type for %q against [%s]", ct, strings.Join(route.Consumes, ", "))
|
||||
if err := validateContentType(route.Consumes, ct); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
|
@ -397,16 +433,16 @@ func (c *Context) ResponseFormat(r *http.Request, offers []string) (string, *htt
|
|||
var rCtx = r.Context()
|
||||
|
||||
if v, ok := rCtx.Value(ctxResponseFormat).(string); ok {
|
||||
debugLog("[%s %s] found response format %q in context", r.Method, r.URL.Path, v)
|
||||
c.debugLogf("[%s %s] found response format %q in context", r.Method, r.URL.Path, v)
|
||||
return v, r
|
||||
}
|
||||
|
||||
format := NegotiateContentType(r, offers, "")
|
||||
if format != "" {
|
||||
debugLog("[%s %s] set response format %q in context", r.Method, r.URL.Path, format)
|
||||
c.debugLogf("[%s %s] set response format %q in context", r.Method, r.URL.Path, format)
|
||||
r = r.WithContext(stdContext.WithValue(rCtx, ctxResponseFormat, format))
|
||||
}
|
||||
debugLog("[%s %s] negotiated response format %q", r.Method, r.URL.Path, format)
|
||||
c.debugLogf("[%s %s] negotiated response format %q", r.Method, r.URL.Path, format)
|
||||
return format, r
|
||||
}
|
||||
|
||||
|
|
@ -469,7 +505,7 @@ func (c *Context) BindAndValidate(request *http.Request, matched *MatchedRoute)
|
|||
var rCtx = request.Context()
|
||||
|
||||
if v, ok := rCtx.Value(ctxBoundParams).(*validation); ok {
|
||||
debugLog("got cached validation (valid: %t)", len(v.result) == 0)
|
||||
c.debugLogf("got cached validation (valid: %t)", len(v.result) == 0)
|
||||
if len(v.result) > 0 {
|
||||
return v.bound, request, errors.CompositeValidationError(v.result...)
|
||||
}
|
||||
|
|
@ -481,7 +517,7 @@ func (c *Context) BindAndValidate(request *http.Request, matched *MatchedRoute)
|
|||
if len(result.result) > 0 {
|
||||
return result.bound, request, errors.CompositeValidationError(result.result...)
|
||||
}
|
||||
debugLog("no validation errors found")
|
||||
c.debugLogf("no validation errors found")
|
||||
return result.bound, request, nil
|
||||
}
|
||||
|
||||
|
|
@ -492,7 +528,7 @@ func (c *Context) NotFound(rw http.ResponseWriter, r *http.Request) {
|
|||
|
||||
// Respond renders the response after doing some content negotiation
|
||||
func (c *Context) Respond(rw http.ResponseWriter, r *http.Request, produces []string, route *MatchedRoute, data interface{}) {
|
||||
debugLog("responding to %s %s with produces: %v", r.Method, r.URL.Path, produces)
|
||||
c.debugLogf("responding to %s %s with produces: %v", r.Method, r.URL.Path, produces)
|
||||
offers := []string{}
|
||||
for _, mt := range produces {
|
||||
if mt != c.api.DefaultProduces() {
|
||||
|
|
@ -501,7 +537,7 @@ func (c *Context) Respond(rw http.ResponseWriter, r *http.Request, produces []st
|
|||
}
|
||||
// the default producer is last so more specific producers take precedence
|
||||
offers = append(offers, c.api.DefaultProduces())
|
||||
debugLog("offers: %v", offers)
|
||||
c.debugLogf("offers: %v", offers)
|
||||
|
||||
var format string
|
||||
format, r = c.ResponseFormat(r, offers)
|
||||
|
|
@ -584,45 +620,92 @@ func (c *Context) Respond(rw http.ResponseWriter, r *http.Request, produces []st
|
|||
c.api.ServeErrorFor(route.Operation.ID)(rw, r, errors.New(http.StatusInternalServerError, "can't produce response"))
|
||||
}
|
||||
|
||||
func (c *Context) APIHandlerSwaggerUI(builder Builder) http.Handler {
|
||||
// APIHandlerSwaggerUI returns a handler to serve the API.
|
||||
//
|
||||
// This handler includes a swagger spec, router and the contract defined in the swagger spec.
|
||||
//
|
||||
// A spec UI (SwaggerUI) is served at {API base path}/docs and the spec document at /swagger.json
|
||||
// (these can be modified with uiOptions).
|
||||
func (c *Context) APIHandlerSwaggerUI(builder Builder, opts ...UIOption) http.Handler {
|
||||
b := builder
|
||||
if b == nil {
|
||||
b = PassthroughBuilder
|
||||
}
|
||||
|
||||
var title string
|
||||
sp := c.spec.Spec()
|
||||
if sp != nil && sp.Info != nil && sp.Info.Title != "" {
|
||||
title = sp.Info.Title
|
||||
}
|
||||
specPath, uiOpts, specOpts := c.uiOptionsForHandler(opts)
|
||||
var swaggerUIOpts SwaggerUIOpts
|
||||
fromCommonToAnyOptions(uiOpts, &swaggerUIOpts)
|
||||
|
||||
swaggerUIOpts := SwaggerUIOpts{
|
||||
BasePath: c.BasePath(),
|
||||
Title: title,
|
||||
}
|
||||
|
||||
return Spec("", c.spec.Raw(), SwaggerUI(swaggerUIOpts, c.RoutesHandler(b)))
|
||||
return Spec(specPath, c.spec.Raw(), SwaggerUI(swaggerUIOpts, c.RoutesHandler(b)), specOpts...)
|
||||
}
|
||||
|
||||
// APIHandler returns a handler to serve the API, this includes a swagger spec, router and the contract defined in the swagger spec
|
||||
func (c *Context) APIHandler(builder Builder) http.Handler {
|
||||
// APIHandlerRapiDoc returns a handler to serve the API.
|
||||
//
|
||||
// This handler includes a swagger spec, router and the contract defined in the swagger spec.
|
||||
//
|
||||
// A spec UI (RapiDoc) is served at {API base path}/docs and the spec document at /swagger.json
|
||||
// (these can be modified with uiOptions).
|
||||
func (c *Context) APIHandlerRapiDoc(builder Builder, opts ...UIOption) http.Handler {
|
||||
b := builder
|
||||
if b == nil {
|
||||
b = PassthroughBuilder
|
||||
}
|
||||
|
||||
specPath, uiOpts, specOpts := c.uiOptionsForHandler(opts)
|
||||
var rapidocUIOpts RapiDocOpts
|
||||
fromCommonToAnyOptions(uiOpts, &rapidocUIOpts)
|
||||
|
||||
return Spec(specPath, c.spec.Raw(), RapiDoc(rapidocUIOpts, c.RoutesHandler(b)), specOpts...)
|
||||
}
|
||||
|
||||
// APIHandler returns a handler to serve the API.
|
||||
//
|
||||
// This handler includes a swagger spec, router and the contract defined in the swagger spec.
|
||||
//
|
||||
// A spec UI (Redoc) is served at {API base path}/docs and the spec document at /swagger.json
|
||||
// (these can be modified with uiOptions).
|
||||
func (c *Context) APIHandler(builder Builder, opts ...UIOption) http.Handler {
|
||||
b := builder
|
||||
if b == nil {
|
||||
b = PassthroughBuilder
|
||||
}
|
||||
|
||||
specPath, uiOpts, specOpts := c.uiOptionsForHandler(opts)
|
||||
var redocOpts RedocOpts
|
||||
fromCommonToAnyOptions(uiOpts, &redocOpts)
|
||||
|
||||
return Spec(specPath, c.spec.Raw(), Redoc(redocOpts, c.RoutesHandler(b)), specOpts...)
|
||||
}
|
||||
|
||||
func (c Context) uiOptionsForHandler(opts []UIOption) (string, uiOptions, []SpecOption) {
|
||||
var title string
|
||||
sp := c.spec.Spec()
|
||||
if sp != nil && sp.Info != nil && sp.Info.Title != "" {
|
||||
title = sp.Info.Title
|
||||
}
|
||||
|
||||
redocOpts := RedocOpts{
|
||||
BasePath: c.BasePath(),
|
||||
Title: title,
|
||||
// default options (may be overridden)
|
||||
optsForContext := []UIOption{
|
||||
WithUIBasePath(c.BasePath()),
|
||||
WithUITitle(title),
|
||||
}
|
||||
optsForContext = append(optsForContext, opts...)
|
||||
uiOpts := uiOptionsWithDefaults(optsForContext)
|
||||
|
||||
// If spec URL is provided, there is a non-default path to serve the spec.
|
||||
// This makes sure that the UI middleware is aligned with the Spec middleware.
|
||||
u, _ := url.Parse(uiOpts.SpecURL)
|
||||
var specPath string
|
||||
if u != nil {
|
||||
specPath = u.Path
|
||||
}
|
||||
|
||||
return Spec("", c.spec.Raw(), Redoc(redocOpts, c.RoutesHandler(b)))
|
||||
pth, doc := path.Split(specPath)
|
||||
if pth == "." {
|
||||
pth = ""
|
||||
}
|
||||
|
||||
return pth, uiOpts, []SpecOption{WithSpecDocument(doc)}
|
||||
}
|
||||
|
||||
// RoutesHandler returns a handler to serve the API, just the routes and the contract defined in the swagger spec
|
||||
|
|
|
|||
30
vendor/github.com/go-openapi/runtime/middleware/denco/router.go
generated
vendored
30
vendor/github.com/go-openapi/runtime/middleware/denco/router.go
generated
vendored
|
|
@ -2,6 +2,7 @@
|
|||
package denco
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
|
@ -29,13 +30,13 @@ const (
|
|||
|
||||
// Router represents a URL router.
|
||||
type Router struct {
|
||||
param *doubleArray
|
||||
// SizeHint expects the maximum number of path parameters in records to Build.
|
||||
// SizeHint will be used to determine the capacity of the memory to allocate.
|
||||
// By default, SizeHint will be determined from given records to Build.
|
||||
SizeHint int
|
||||
|
||||
static map[string]interface{}
|
||||
param *doubleArray
|
||||
}
|
||||
|
||||
// New returns a new Router.
|
||||
|
|
@ -71,7 +72,7 @@ func (rt *Router) Lookup(path string) (data interface{}, params Params, found bo
|
|||
func (rt *Router) Build(records []Record) error {
|
||||
statics, params := makeRecords(records)
|
||||
if len(params) > MaxSize {
|
||||
return fmt.Errorf("denco: too many records")
|
||||
return errors.New("denco: too many records")
|
||||
}
|
||||
if rt.SizeHint < 0 {
|
||||
rt.SizeHint = 0
|
||||
|
|
@ -197,24 +198,29 @@ func (da *doubleArray) lookup(path string, params []Param, idx int) (*node, []Pa
|
|||
if next := nextIndex(da.bc[idx].Base(), TerminationCharacter); next < len(da.bc) && da.bc[next].Check() == TerminationCharacter {
|
||||
return da.node[da.bc[next].Base()], params, true
|
||||
}
|
||||
|
||||
BACKTRACKING:
|
||||
for j := len(indices) - 1; j >= 0; j-- {
|
||||
i, idx := int(indices[j]>>32), int(indices[j]&0xffffffff)
|
||||
if da.bc[idx].IsSingleParam() {
|
||||
idx := nextIndex(da.bc[idx].Base(), ParamCharacter) //nolint:govet
|
||||
if idx >= len(da.bc) {
|
||||
nextIdx := nextIndex(da.bc[idx].Base(), ParamCharacter)
|
||||
if nextIdx >= len(da.bc) {
|
||||
break
|
||||
}
|
||||
|
||||
next := NextSeparator(path, i)
|
||||
params := append(params, Param{Value: path[i:next]}) //nolint:govet
|
||||
if nd, params, found := da.lookup(path[next:], params, idx); found { //nolint:govet
|
||||
return nd, params, true
|
||||
nextParams := params
|
||||
nextParams = append(nextParams, Param{Value: path[i:next]})
|
||||
if nd, nextNextParams, found := da.lookup(path[next:], nextParams, nextIdx); found {
|
||||
return nd, nextNextParams, true
|
||||
}
|
||||
}
|
||||
|
||||
if da.bc[idx].IsWildcardParam() {
|
||||
idx := nextIndex(da.bc[idx].Base(), WildcardCharacter) //nolint:govet
|
||||
params := append(params, Param{Value: path[i:]}) //nolint:govet
|
||||
return da.node[da.bc[idx].Base()], params, true
|
||||
nextIdx := nextIndex(da.bc[idx].Base(), WildcardCharacter)
|
||||
nextParams := params
|
||||
nextParams = append(nextParams, Param{Value: path[i:]})
|
||||
return da.node[da.bc[nextIdx].Base()], nextParams, true
|
||||
}
|
||||
}
|
||||
return nil, nil, false
|
||||
|
|
@ -326,7 +332,7 @@ func (da *doubleArray) arrange(records []*record, idx, depth int, usedBase map[i
|
|||
}
|
||||
base = da.findBase(siblings, idx, usedBase)
|
||||
if base > MaxSize {
|
||||
return -1, nil, nil, fmt.Errorf("denco: too many elements of internal slice")
|
||||
return -1, nil, nil, errors.New("denco: too many elements of internal slice")
|
||||
}
|
||||
da.setBase(idx, base)
|
||||
return base, siblings, leaf, err
|
||||
|
|
@ -387,7 +393,7 @@ func makeSiblings(records []*record, depth int) (sib []sibling, leaf *record, er
|
|||
case pc == c:
|
||||
continue
|
||||
default:
|
||||
return nil, nil, fmt.Errorf("denco: BUG: routing table hasn't been sorted")
|
||||
return nil, nil, errors.New("denco: BUG: routing table hasn't been sorted")
|
||||
}
|
||||
if n > 0 {
|
||||
sib[n-1].end = i
|
||||
|
|
|
|||
10
vendor/github.com/go-openapi/runtime/middleware/go18.go
generated
vendored
10
vendor/github.com/go-openapi/runtime/middleware/go18.go
generated
vendored
|
|
@ -1,10 +0,0 @@
|
|||
//go:build go1.8
|
||||
// +build go1.8
|
||||
|
||||
package middleware
|
||||
|
||||
import "net/url"
|
||||
|
||||
func pathUnescape(path string) (string, error) {
|
||||
return url.PathUnescape(path)
|
||||
}
|
||||
9
vendor/github.com/go-openapi/runtime/middleware/pre_go18.go
generated
vendored
9
vendor/github.com/go-openapi/runtime/middleware/pre_go18.go
generated
vendored
|
|
@ -1,9 +0,0 @@
|
|||
// +build !go1.8
|
||||
|
||||
package middleware
|
||||
|
||||
import "net/url"
|
||||
|
||||
func pathUnescape(path string) (string, error) {
|
||||
return url.QueryUnescape(path)
|
||||
}
|
||||
70
vendor/github.com/go-openapi/runtime/middleware/rapidoc.go
generated
vendored
70
vendor/github.com/go-openapi/runtime/middleware/rapidoc.go
generated
vendored
|
|
@ -1,4 +1,3 @@
|
|||
//nolint:dupl
|
||||
package middleware
|
||||
|
||||
import (
|
||||
|
|
@ -11,66 +10,57 @@ import (
|
|||
|
||||
// RapiDocOpts configures the RapiDoc middlewares
|
||||
type RapiDocOpts struct {
|
||||
// BasePath for the UI path, defaults to: /
|
||||
// BasePath for the UI, defaults to: /
|
||||
BasePath string
|
||||
// Path combines with BasePath for the full UI path, defaults to: docs
|
||||
|
||||
// Path combines with BasePath to construct the path to the UI, defaults to: "docs".
|
||||
Path string
|
||||
// SpecURL the url to find the spec for
|
||||
|
||||
// SpecURL is the URL of the spec document.
|
||||
//
|
||||
// Defaults to: /swagger.json
|
||||
SpecURL string
|
||||
// RapiDocURL for the js that generates the rapidoc site, defaults to: https://cdn.jsdelivr.net/npm/rapidoc/bundles/rapidoc.standalone.js
|
||||
RapiDocURL string
|
||||
|
||||
// Title for the documentation site, default to: API documentation
|
||||
Title string
|
||||
|
||||
// Template specifies a custom template to serve the UI
|
||||
Template string
|
||||
|
||||
// RapiDocURL points to the js asset that generates the rapidoc site.
|
||||
//
|
||||
// Defaults to https://unpkg.com/rapidoc/dist/rapidoc-min.js
|
||||
RapiDocURL string
|
||||
}
|
||||
|
||||
// EnsureDefaults in case some options are missing
|
||||
func (r *RapiDocOpts) EnsureDefaults() {
|
||||
if r.BasePath == "" {
|
||||
r.BasePath = "/"
|
||||
}
|
||||
if r.Path == "" {
|
||||
r.Path = defaultDocsPath
|
||||
}
|
||||
if r.SpecURL == "" {
|
||||
r.SpecURL = defaultDocsURL
|
||||
}
|
||||
common := toCommonUIOptions(r)
|
||||
common.EnsureDefaults()
|
||||
fromCommonToAnyOptions(common, r)
|
||||
|
||||
// rapidoc-specifics
|
||||
if r.RapiDocURL == "" {
|
||||
r.RapiDocURL = rapidocLatest
|
||||
}
|
||||
if r.Title == "" {
|
||||
r.Title = defaultDocsTitle
|
||||
if r.Template == "" {
|
||||
r.Template = rapidocTemplate
|
||||
}
|
||||
}
|
||||
|
||||
// RapiDoc creates a middleware to serve a documentation site for a swagger spec.
|
||||
//
|
||||
// This allows for altering the spec before starting the http listener.
|
||||
func RapiDoc(opts RapiDocOpts, next http.Handler) http.Handler {
|
||||
opts.EnsureDefaults()
|
||||
|
||||
pth := path.Join(opts.BasePath, opts.Path)
|
||||
tmpl := template.Must(template.New("rapidoc").Parse(rapidocTemplate))
|
||||
tmpl := template.Must(template.New("rapidoc").Parse(opts.Template))
|
||||
assets := bytes.NewBuffer(nil)
|
||||
if err := tmpl.Execute(assets, opts); err != nil {
|
||||
panic(fmt.Errorf("cannot execute template: %w", err))
|
||||
}
|
||||
|
||||
buf := bytes.NewBuffer(nil)
|
||||
_ = tmpl.Execute(buf, opts)
|
||||
b := buf.Bytes()
|
||||
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path == pth {
|
||||
rw.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
|
||||
_, _ = rw.Write(b)
|
||||
return
|
||||
}
|
||||
|
||||
if next == nil {
|
||||
rw.Header().Set("Content-Type", "text/plain")
|
||||
rw.WriteHeader(http.StatusNotFound)
|
||||
_, _ = rw.Write([]byte(fmt.Sprintf("%q not found", pth)))
|
||||
return
|
||||
}
|
||||
next.ServeHTTP(rw, r)
|
||||
})
|
||||
return serveUI(pth, assets.Bytes(), next)
|
||||
}
|
||||
|
||||
const (
|
||||
|
|
|
|||
69
vendor/github.com/go-openapi/runtime/middleware/redoc.go
generated
vendored
69
vendor/github.com/go-openapi/runtime/middleware/redoc.go
generated
vendored
|
|
@ -1,4 +1,3 @@
|
|||
//nolint:dupl
|
||||
package middleware
|
||||
|
||||
import (
|
||||
|
|
@ -11,66 +10,58 @@ import (
|
|||
|
||||
// RedocOpts configures the Redoc middlewares
|
||||
type RedocOpts struct {
|
||||
// BasePath for the UI path, defaults to: /
|
||||
// BasePath for the UI, defaults to: /
|
||||
BasePath string
|
||||
// Path combines with BasePath for the full UI path, defaults to: docs
|
||||
|
||||
// Path combines with BasePath to construct the path to the UI, defaults to: "docs".
|
||||
Path string
|
||||
// SpecURL the url to find the spec for
|
||||
|
||||
// SpecURL is the URL of the spec document.
|
||||
//
|
||||
// Defaults to: /swagger.json
|
||||
SpecURL string
|
||||
// RedocURL for the js that generates the redoc site, defaults to: https://cdn.jsdelivr.net/npm/redoc/bundles/redoc.standalone.js
|
||||
RedocURL string
|
||||
|
||||
// Title for the documentation site, default to: API documentation
|
||||
Title string
|
||||
|
||||
// Template specifies a custom template to serve the UI
|
||||
Template string
|
||||
|
||||
// RedocURL points to the js that generates the redoc site.
|
||||
//
|
||||
// Defaults to: https://cdn.jsdelivr.net/npm/redoc/bundles/redoc.standalone.js
|
||||
RedocURL string
|
||||
}
|
||||
|
||||
// EnsureDefaults in case some options are missing
|
||||
func (r *RedocOpts) EnsureDefaults() {
|
||||
if r.BasePath == "" {
|
||||
r.BasePath = "/"
|
||||
}
|
||||
if r.Path == "" {
|
||||
r.Path = defaultDocsPath
|
||||
}
|
||||
if r.SpecURL == "" {
|
||||
r.SpecURL = defaultDocsURL
|
||||
}
|
||||
common := toCommonUIOptions(r)
|
||||
common.EnsureDefaults()
|
||||
fromCommonToAnyOptions(common, r)
|
||||
|
||||
// redoc-specifics
|
||||
if r.RedocURL == "" {
|
||||
r.RedocURL = redocLatest
|
||||
}
|
||||
if r.Title == "" {
|
||||
r.Title = defaultDocsTitle
|
||||
if r.Template == "" {
|
||||
r.Template = redocTemplate
|
||||
}
|
||||
}
|
||||
|
||||
// Redoc creates a middleware to serve a documentation site for a swagger spec.
|
||||
//
|
||||
// This allows for altering the spec before starting the http listener.
|
||||
func Redoc(opts RedocOpts, next http.Handler) http.Handler {
|
||||
opts.EnsureDefaults()
|
||||
|
||||
pth := path.Join(opts.BasePath, opts.Path)
|
||||
tmpl := template.Must(template.New("redoc").Parse(redocTemplate))
|
||||
tmpl := template.Must(template.New("redoc").Parse(opts.Template))
|
||||
assets := bytes.NewBuffer(nil)
|
||||
if err := tmpl.Execute(assets, opts); err != nil {
|
||||
panic(fmt.Errorf("cannot execute template: %w", err))
|
||||
}
|
||||
|
||||
buf := bytes.NewBuffer(nil)
|
||||
_ = tmpl.Execute(buf, opts)
|
||||
b := buf.Bytes()
|
||||
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path == pth {
|
||||
rw.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
|
||||
_, _ = rw.Write(b)
|
||||
return
|
||||
}
|
||||
|
||||
if next == nil {
|
||||
rw.Header().Set("Content-Type", "text/plain")
|
||||
rw.WriteHeader(http.StatusNotFound)
|
||||
_, _ = rw.Write([]byte(fmt.Sprintf("%q not found", pth)))
|
||||
return
|
||||
}
|
||||
next.ServeHTTP(rw, r)
|
||||
})
|
||||
return serveUI(pth, assets.Bytes(), next)
|
||||
}
|
||||
|
||||
const (
|
||||
|
|
|
|||
21
vendor/github.com/go-openapi/runtime/middleware/request.go
generated
vendored
21
vendor/github.com/go-openapi/runtime/middleware/request.go
generated
vendored
|
|
@ -19,10 +19,10 @@ import (
|
|||
"reflect"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/runtime"
|
||||
"github.com/go-openapi/runtime/logger"
|
||||
"github.com/go-openapi/spec"
|
||||
"github.com/go-openapi/strfmt"
|
||||
|
||||
"github.com/go-openapi/runtime"
|
||||
)
|
||||
|
||||
// UntypedRequestBinder binds and validates the data from a http request
|
||||
|
|
@ -31,6 +31,7 @@ type UntypedRequestBinder struct {
|
|||
Parameters map[string]spec.Parameter
|
||||
Formats strfmt.Registry
|
||||
paramBinders map[string]*untypedParamBinder
|
||||
debugLogf func(string, ...any) // a logging function to debug context and all components using it
|
||||
}
|
||||
|
||||
// NewUntypedRequestBinder creates a new binder for reading a request.
|
||||
|
|
@ -44,6 +45,7 @@ func NewUntypedRequestBinder(parameters map[string]spec.Parameter, spec *spec.Sw
|
|||
paramBinders: binders,
|
||||
Spec: spec,
|
||||
Formats: formats,
|
||||
debugLogf: debugLogfFunc(nil),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -52,10 +54,10 @@ func (o *UntypedRequestBinder) Bind(request *http.Request, routeParams RoutePara
|
|||
val := reflect.Indirect(reflect.ValueOf(data))
|
||||
isMap := val.Kind() == reflect.Map
|
||||
var result []error
|
||||
debugLog("binding %d parameters for %s %s", len(o.Parameters), request.Method, request.URL.EscapedPath())
|
||||
o.debugLogf("binding %d parameters for %s %s", len(o.Parameters), request.Method, request.URL.EscapedPath())
|
||||
for fieldName, param := range o.Parameters {
|
||||
binder := o.paramBinders[fieldName]
|
||||
debugLog("binding parameter %s for %s %s", fieldName, request.Method, request.URL.EscapedPath())
|
||||
o.debugLogf("binding parameter %s for %s %s", fieldName, request.Method, request.URL.EscapedPath())
|
||||
var target reflect.Value
|
||||
if !isMap {
|
||||
binder.Name = fieldName
|
||||
|
|
@ -102,3 +104,14 @@ func (o *UntypedRequestBinder) Bind(request *http.Request, routeParams RoutePara
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetLogger allows for injecting a logger to catch debug entries.
|
||||
//
|
||||
// The logger is enabled in DEBUG mode only.
|
||||
func (o *UntypedRequestBinder) SetLogger(lg logger.Logger) {
|
||||
o.debugLogf = debugLogfFunc(lg)
|
||||
}
|
||||
|
||||
func (o *UntypedRequestBinder) setDebugLogf(fn func(string, ...any)) {
|
||||
o.debugLogf = fn
|
||||
}
|
||||
|
|
|
|||
103
vendor/github.com/go-openapi/runtime/middleware/router.go
generated
vendored
103
vendor/github.com/go-openapi/runtime/middleware/router.go
generated
vendored
|
|
@ -17,10 +17,12 @@ package middleware
|
|||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
fpath "path"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/go-openapi/runtime/logger"
|
||||
"github.com/go-openapi/runtime/security"
|
||||
"github.com/go-openapi/swag"
|
||||
|
||||
|
|
@ -67,10 +69,10 @@ func (r RouteParams) GetOK(name string) ([]string, bool, bool) {
|
|||
return nil, false, false
|
||||
}
|
||||
|
||||
// NewRouter creates a new context aware router middleware
|
||||
// NewRouter creates a new context-aware router middleware
|
||||
func NewRouter(ctx *Context, next http.Handler) http.Handler {
|
||||
if ctx.router == nil {
|
||||
ctx.router = DefaultRouter(ctx.spec, ctx.api)
|
||||
ctx.router = DefaultRouter(ctx.spec, ctx.api, WithDefaultRouterLoggerFunc(ctx.debugLogf))
|
||||
}
|
||||
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
|
|
@ -103,41 +105,75 @@ type RoutableAPI interface {
|
|||
DefaultConsumes() string
|
||||
}
|
||||
|
||||
// Router represents a swagger aware router
|
||||
// Router represents a swagger-aware router
|
||||
type Router interface {
|
||||
Lookup(method, path string) (*MatchedRoute, bool)
|
||||
OtherMethods(method, path string) []string
|
||||
}
|
||||
|
||||
type defaultRouteBuilder struct {
|
||||
spec *loads.Document
|
||||
analyzer *analysis.Spec
|
||||
api RoutableAPI
|
||||
records map[string][]denco.Record
|
||||
spec *loads.Document
|
||||
analyzer *analysis.Spec
|
||||
api RoutableAPI
|
||||
records map[string][]denco.Record
|
||||
debugLogf func(string, ...any) // a logging function to debug context and all components using it
|
||||
}
|
||||
|
||||
type defaultRouter struct {
|
||||
spec *loads.Document
|
||||
routers map[string]*denco.Router
|
||||
spec *loads.Document
|
||||
routers map[string]*denco.Router
|
||||
debugLogf func(string, ...any) // a logging function to debug context and all components using it
|
||||
}
|
||||
|
||||
func newDefaultRouteBuilder(spec *loads.Document, api RoutableAPI) *defaultRouteBuilder {
|
||||
func newDefaultRouteBuilder(spec *loads.Document, api RoutableAPI, opts ...DefaultRouterOpt) *defaultRouteBuilder {
|
||||
var o defaultRouterOpts
|
||||
for _, apply := range opts {
|
||||
apply(&o)
|
||||
}
|
||||
if o.debugLogf == nil {
|
||||
o.debugLogf = debugLogfFunc(nil) // defaults to standard logger
|
||||
}
|
||||
|
||||
return &defaultRouteBuilder{
|
||||
spec: spec,
|
||||
analyzer: analysis.New(spec.Spec()),
|
||||
api: api,
|
||||
records: make(map[string][]denco.Record),
|
||||
spec: spec,
|
||||
analyzer: analysis.New(spec.Spec()),
|
||||
api: api,
|
||||
records: make(map[string][]denco.Record),
|
||||
debugLogf: o.debugLogf,
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultRouter creates a default implemenation of the router
|
||||
func DefaultRouter(spec *loads.Document, api RoutableAPI) Router {
|
||||
builder := newDefaultRouteBuilder(spec, api)
|
||||
// DefaultRouterOpt allows to inject optional behavior to the default router.
|
||||
type DefaultRouterOpt func(*defaultRouterOpts)
|
||||
|
||||
type defaultRouterOpts struct {
|
||||
debugLogf func(string, ...any)
|
||||
}
|
||||
|
||||
// WithDefaultRouterLogger sets the debug logger for the default router.
|
||||
//
|
||||
// This is enabled only in DEBUG mode.
|
||||
func WithDefaultRouterLogger(lg logger.Logger) DefaultRouterOpt {
|
||||
return func(o *defaultRouterOpts) {
|
||||
o.debugLogf = debugLogfFunc(lg)
|
||||
}
|
||||
}
|
||||
|
||||
// WithDefaultRouterLoggerFunc sets a logging debug method for the default router.
|
||||
func WithDefaultRouterLoggerFunc(fn func(string, ...any)) DefaultRouterOpt {
|
||||
return func(o *defaultRouterOpts) {
|
||||
o.debugLogf = fn
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultRouter creates a default implementation of the router
|
||||
func DefaultRouter(spec *loads.Document, api RoutableAPI, opts ...DefaultRouterOpt) Router {
|
||||
builder := newDefaultRouteBuilder(spec, api, opts...)
|
||||
if spec != nil {
|
||||
for method, paths := range builder.analyzer.Operations() {
|
||||
for path, operation := range paths {
|
||||
fp := fpath.Join(spec.BasePath(), path)
|
||||
debugLog("adding route %s %s %q", method, fp, operation.ID)
|
||||
builder.debugLogf("adding route %s %s %q", method, fp, operation.ID)
|
||||
builder.AddRoute(method, fp, operation)
|
||||
}
|
||||
}
|
||||
|
|
@ -319,24 +355,24 @@ func (m *MatchedRoute) NeedsAuth() bool {
|
|||
|
||||
func (d *defaultRouter) Lookup(method, path string) (*MatchedRoute, bool) {
|
||||
mth := strings.ToUpper(method)
|
||||
debugLog("looking up route for %s %s", method, path)
|
||||
d.debugLogf("looking up route for %s %s", method, path)
|
||||
if Debug {
|
||||
if len(d.routers) == 0 {
|
||||
debugLog("there are no known routers")
|
||||
d.debugLogf("there are no known routers")
|
||||
}
|
||||
for meth := range d.routers {
|
||||
debugLog("got a router for %s", meth)
|
||||
d.debugLogf("got a router for %s", meth)
|
||||
}
|
||||
}
|
||||
if router, ok := d.routers[mth]; ok {
|
||||
if m, rp, ok := router.Lookup(fpath.Clean(path)); ok && m != nil {
|
||||
if entry, ok := m.(*routeEntry); ok {
|
||||
debugLog("found a route for %s %s with %d parameters", method, path, len(entry.Parameters))
|
||||
d.debugLogf("found a route for %s %s with %d parameters", method, path, len(entry.Parameters))
|
||||
var params RouteParams
|
||||
for _, p := range rp {
|
||||
v, err := pathUnescape(p.Value)
|
||||
v, err := url.PathUnescape(p.Value)
|
||||
if err != nil {
|
||||
debugLog("failed to escape %q: %v", p.Value, err)
|
||||
d.debugLogf("failed to escape %q: %v", p.Value, err)
|
||||
v = p.Value
|
||||
}
|
||||
// a workaround to handle fragment/composing parameters until they are supported in denco router
|
||||
|
|
@ -356,10 +392,10 @@ func (d *defaultRouter) Lookup(method, path string) (*MatchedRoute, bool) {
|
|||
return &MatchedRoute{routeEntry: *entry, Params: params}, true
|
||||
}
|
||||
} else {
|
||||
debugLog("couldn't find a route by path for %s %s", method, path)
|
||||
d.debugLogf("couldn't find a route by path for %s %s", method, path)
|
||||
}
|
||||
} else {
|
||||
debugLog("couldn't find a route by method for %s %s", method, path)
|
||||
d.debugLogf("couldn't find a route by method for %s %s", method, path)
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
|
@ -378,6 +414,10 @@ func (d *defaultRouter) OtherMethods(method, path string) []string {
|
|||
return methods
|
||||
}
|
||||
|
||||
func (d *defaultRouter) SetLogger(lg logger.Logger) {
|
||||
d.debugLogf = debugLogfFunc(lg)
|
||||
}
|
||||
|
||||
// convert swagger parameters per path segment into a denco parameter as multiple parameters per segment are not supported in denco
|
||||
var pathConverter = regexp.MustCompile(`{(.+?)}([^/]*)`)
|
||||
|
||||
|
|
@ -413,7 +453,7 @@ func (d *defaultRouteBuilder) AddRoute(method, path string, operation *spec.Oper
|
|||
bp = bp[:len(bp)-1]
|
||||
}
|
||||
|
||||
debugLog("operation: %#v", *operation)
|
||||
d.debugLogf("operation: %#v", *operation)
|
||||
if handler, ok := d.api.HandlerFor(method, strings.TrimPrefix(path, bp)); ok {
|
||||
consumes := d.analyzer.ConsumesFor(operation)
|
||||
produces := d.analyzer.ProducesFor(operation)
|
||||
|
|
@ -428,6 +468,8 @@ func (d *defaultRouteBuilder) AddRoute(method, path string, operation *spec.Oper
|
|||
produces = append(produces, defProduces)
|
||||
}
|
||||
|
||||
requestBinder := NewUntypedRequestBinder(parameters, d.spec.Spec(), d.api.Formats())
|
||||
requestBinder.setDebugLogf(d.debugLogf)
|
||||
record := denco.NewRecord(pathConverter.ReplaceAllString(path, ":$1"), &routeEntry{
|
||||
BasePath: bp,
|
||||
PathPattern: path,
|
||||
|
|
@ -439,7 +481,7 @@ func (d *defaultRouteBuilder) AddRoute(method, path string, operation *spec.Oper
|
|||
Producers: d.api.ProducersFor(normalizeOffers(produces)),
|
||||
Parameters: parameters,
|
||||
Formats: d.api.Formats(),
|
||||
Binder: NewUntypedRequestBinder(parameters, d.spec.Spec(), d.api.Formats()),
|
||||
Binder: requestBinder,
|
||||
Authenticators: d.buildAuthenticators(operation),
|
||||
Authorizer: d.api.Authorizer(),
|
||||
})
|
||||
|
|
@ -482,7 +524,8 @@ func (d *defaultRouteBuilder) Build() *defaultRouter {
|
|||
routers[method] = router
|
||||
}
|
||||
return &defaultRouter{
|
||||
spec: d.spec,
|
||||
routers: routers,
|
||||
spec: d.spec,
|
||||
routers: routers,
|
||||
debugLogf: d.debugLogf,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
77
vendor/github.com/go-openapi/runtime/middleware/spec.go
generated
vendored
77
vendor/github.com/go-openapi/runtime/middleware/spec.go
generated
vendored
|
|
@ -19,29 +19,84 @@ import (
|
|||
"path"
|
||||
)
|
||||
|
||||
// Spec creates a middleware to serve a swagger spec.
|
||||
const (
|
||||
contentTypeHeader = "Content-Type"
|
||||
applicationJSON = "application/json"
|
||||
)
|
||||
|
||||
// SpecOption can be applied to the Spec serving middleware
|
||||
type SpecOption func(*specOptions)
|
||||
|
||||
var defaultSpecOptions = specOptions{
|
||||
Path: "",
|
||||
Document: "swagger.json",
|
||||
}
|
||||
|
||||
type specOptions struct {
|
||||
Path string
|
||||
Document string
|
||||
}
|
||||
|
||||
func specOptionsWithDefaults(opts []SpecOption) specOptions {
|
||||
o := defaultSpecOptions
|
||||
for _, apply := range opts {
|
||||
apply(&o)
|
||||
}
|
||||
|
||||
return o
|
||||
}
|
||||
|
||||
// Spec creates a middleware to serve a swagger spec as a JSON document.
|
||||
//
|
||||
// This allows for altering the spec before starting the http listener.
|
||||
// This can be useful if you want to serve the swagger spec from another path than /swagger.json
|
||||
func Spec(basePath string, b []byte, next http.Handler) http.Handler {
|
||||
//
|
||||
// The basePath argument indicates the path of the spec document (defaults to "/").
|
||||
// Additional SpecOption can be used to change the name of the document (defaults to "swagger.json").
|
||||
func Spec(basePath string, b []byte, next http.Handler, opts ...SpecOption) http.Handler {
|
||||
if basePath == "" {
|
||||
basePath = "/"
|
||||
}
|
||||
pth := path.Join(basePath, "swagger.json")
|
||||
o := specOptionsWithDefaults(opts)
|
||||
pth := path.Join(basePath, o.Path, o.Document)
|
||||
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path == pth {
|
||||
rw.Header().Set("Content-Type", "application/json")
|
||||
if path.Clean(r.URL.Path) == pth {
|
||||
rw.Header().Set(contentTypeHeader, applicationJSON)
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
//#nosec
|
||||
_, _ = rw.Write(b)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if next == nil {
|
||||
rw.Header().Set("Content-Type", "application/json")
|
||||
rw.WriteHeader(http.StatusNotFound)
|
||||
if next != nil {
|
||||
next.ServeHTTP(rw, r)
|
||||
|
||||
return
|
||||
}
|
||||
next.ServeHTTP(rw, r)
|
||||
|
||||
rw.Header().Set(contentTypeHeader, applicationJSON)
|
||||
rw.WriteHeader(http.StatusNotFound)
|
||||
})
|
||||
}
|
||||
|
||||
// WithSpecPath sets the path to be joined to the base path of the Spec middleware.
|
||||
//
|
||||
// This is empty by default.
|
||||
func WithSpecPath(pth string) SpecOption {
|
||||
return func(o *specOptions) {
|
||||
o.Path = pth
|
||||
}
|
||||
}
|
||||
|
||||
// WithSpecDocument sets the name of the JSON document served as a spec.
|
||||
//
|
||||
// By default, this is "swagger.json"
|
||||
func WithSpecDocument(doc string) SpecOption {
|
||||
return func(o *specOptions) {
|
||||
if doc == "" {
|
||||
return
|
||||
}
|
||||
|
||||
o.Document = doc
|
||||
}
|
||||
}
|
||||
|
|
|
|||
87
vendor/github.com/go-openapi/runtime/middleware/swaggerui.go
generated
vendored
87
vendor/github.com/go-openapi/runtime/middleware/swaggerui.go
generated
vendored
|
|
@ -8,40 +8,65 @@ import (
|
|||
"path"
|
||||
)
|
||||
|
||||
// SwaggerUIOpts configures the Swaggerui middlewares
|
||||
// SwaggerUIOpts configures the SwaggerUI middleware
|
||||
type SwaggerUIOpts struct {
|
||||
// BasePath for the UI path, defaults to: /
|
||||
// BasePath for the API, defaults to: /
|
||||
BasePath string
|
||||
// Path combines with BasePath for the full UI path, defaults to: docs
|
||||
|
||||
// Path combines with BasePath to construct the path to the UI, defaults to: "docs".
|
||||
Path string
|
||||
// SpecURL the url to find the spec for
|
||||
|
||||
// SpecURL is the URL of the spec document.
|
||||
//
|
||||
// Defaults to: /swagger.json
|
||||
SpecURL string
|
||||
|
||||
// Title for the documentation site, default to: API documentation
|
||||
Title string
|
||||
|
||||
// Template specifies a custom template to serve the UI
|
||||
Template string
|
||||
|
||||
// OAuthCallbackURL the url called after OAuth2 login
|
||||
OAuthCallbackURL string
|
||||
|
||||
// The three components needed to embed swagger-ui
|
||||
SwaggerURL string
|
||||
|
||||
// SwaggerURL points to the js that generates the SwaggerUI site.
|
||||
//
|
||||
// Defaults to: https://unpkg.com/swagger-ui-dist/swagger-ui-bundle.js
|
||||
SwaggerURL string
|
||||
|
||||
SwaggerPresetURL string
|
||||
SwaggerStylesURL string
|
||||
|
||||
Favicon32 string
|
||||
Favicon16 string
|
||||
|
||||
// Title for the documentation site, default to: API documentation
|
||||
Title string
|
||||
}
|
||||
|
||||
// EnsureDefaults in case some options are missing
|
||||
func (r *SwaggerUIOpts) EnsureDefaults() {
|
||||
if r.BasePath == "" {
|
||||
r.BasePath = "/"
|
||||
r.ensureDefaults()
|
||||
|
||||
if r.Template == "" {
|
||||
r.Template = swaggeruiTemplate
|
||||
}
|
||||
if r.Path == "" {
|
||||
r.Path = defaultDocsPath
|
||||
}
|
||||
if r.SpecURL == "" {
|
||||
r.SpecURL = defaultDocsURL
|
||||
}
|
||||
|
||||
func (r *SwaggerUIOpts) EnsureDefaultsOauth2() {
|
||||
r.ensureDefaults()
|
||||
|
||||
if r.Template == "" {
|
||||
r.Template = swaggerOAuthTemplate
|
||||
}
|
||||
}
|
||||
|
||||
func (r *SwaggerUIOpts) ensureDefaults() {
|
||||
common := toCommonUIOptions(r)
|
||||
common.EnsureDefaults()
|
||||
fromCommonToAnyOptions(common, r)
|
||||
|
||||
// swaggerui-specifics
|
||||
if r.OAuthCallbackURL == "" {
|
||||
r.OAuthCallbackURL = path.Join(r.BasePath, r.Path, "oauth2-callback")
|
||||
}
|
||||
|
|
@ -60,40 +85,22 @@ func (r *SwaggerUIOpts) EnsureDefaults() {
|
|||
if r.Favicon32 == "" {
|
||||
r.Favicon32 = swaggerFavicon32Latest
|
||||
}
|
||||
if r.Title == "" {
|
||||
r.Title = defaultDocsTitle
|
||||
}
|
||||
}
|
||||
|
||||
// SwaggerUI creates a middleware to serve a documentation site for a swagger spec.
|
||||
//
|
||||
// This allows for altering the spec before starting the http listener.
|
||||
func SwaggerUI(opts SwaggerUIOpts, next http.Handler) http.Handler {
|
||||
opts.EnsureDefaults()
|
||||
|
||||
pth := path.Join(opts.BasePath, opts.Path)
|
||||
tmpl := template.Must(template.New("swaggerui").Parse(swaggeruiTemplate))
|
||||
tmpl := template.Must(template.New("swaggerui").Parse(opts.Template))
|
||||
assets := bytes.NewBuffer(nil)
|
||||
if err := tmpl.Execute(assets, opts); err != nil {
|
||||
panic(fmt.Errorf("cannot execute template: %w", err))
|
||||
}
|
||||
|
||||
buf := bytes.NewBuffer(nil)
|
||||
_ = tmpl.Execute(buf, &opts)
|
||||
b := buf.Bytes()
|
||||
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
if path.Join(r.URL.Path) == pth {
|
||||
rw.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
|
||||
_, _ = rw.Write(b)
|
||||
return
|
||||
}
|
||||
|
||||
if next == nil {
|
||||
rw.Header().Set("Content-Type", "text/plain")
|
||||
rw.WriteHeader(http.StatusNotFound)
|
||||
_, _ = rw.Write([]byte(fmt.Sprintf("%q not found", pth)))
|
||||
return
|
||||
}
|
||||
next.ServeHTTP(rw, r)
|
||||
})
|
||||
return serveUI(pth, assets.Bytes(), next)
|
||||
}
|
||||
|
||||
const (
|
||||
|
|
|
|||
31
vendor/github.com/go-openapi/runtime/middleware/swaggerui_oauth2.go
generated
vendored
31
vendor/github.com/go-openapi/runtime/middleware/swaggerui_oauth2.go
generated
vendored
|
|
@ -4,37 +4,20 @@ import (
|
|||
"bytes"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"path"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
func SwaggerUIOAuth2Callback(opts SwaggerUIOpts, next http.Handler) http.Handler {
|
||||
opts.EnsureDefaults()
|
||||
opts.EnsureDefaultsOauth2()
|
||||
|
||||
pth := opts.OAuthCallbackURL
|
||||
tmpl := template.Must(template.New("swaggeroauth").Parse(swaggerOAuthTemplate))
|
||||
tmpl := template.Must(template.New("swaggeroauth").Parse(opts.Template))
|
||||
assets := bytes.NewBuffer(nil)
|
||||
if err := tmpl.Execute(assets, opts); err != nil {
|
||||
panic(fmt.Errorf("cannot execute template: %w", err))
|
||||
}
|
||||
|
||||
buf := bytes.NewBuffer(nil)
|
||||
_ = tmpl.Execute(buf, &opts)
|
||||
b := buf.Bytes()
|
||||
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
if path.Join(r.URL.Path) == pth {
|
||||
rw.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
|
||||
_, _ = rw.Write(b)
|
||||
return
|
||||
}
|
||||
|
||||
if next == nil {
|
||||
rw.Header().Set("Content-Type", "text/plain")
|
||||
rw.WriteHeader(http.StatusNotFound)
|
||||
_, _ = rw.Write([]byte(fmt.Sprintf("%q not found", pth)))
|
||||
return
|
||||
}
|
||||
next.ServeHTTP(rw, r)
|
||||
})
|
||||
return serveUI(pth, assets.Bytes(), next)
|
||||
}
|
||||
|
||||
const (
|
||||
|
|
|
|||
8
vendor/github.com/go-openapi/runtime/middleware/ui_defaults.go
generated
vendored
8
vendor/github.com/go-openapi/runtime/middleware/ui_defaults.go
generated
vendored
|
|
@ -1,8 +0,0 @@
|
|||
package middleware
|
||||
|
||||
const (
|
||||
// constants that are common to all UI-serving middlewares
|
||||
defaultDocsPath = "docs"
|
||||
defaultDocsURL = "/swagger.json"
|
||||
defaultDocsTitle = "API Documentation"
|
||||
)
|
||||
173
vendor/github.com/go-openapi/runtime/middleware/ui_options.go
generated
vendored
Normal file
173
vendor/github.com/go-openapi/runtime/middleware/ui_options.go
generated
vendored
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
// constants that are common to all UI-serving middlewares
|
||||
defaultDocsPath = "docs"
|
||||
defaultDocsURL = "/swagger.json"
|
||||
defaultDocsTitle = "API Documentation"
|
||||
)
|
||||
|
||||
// uiOptions defines common options for UI serving middlewares.
|
||||
type uiOptions struct {
|
||||
// BasePath for the UI, defaults to: /
|
||||
BasePath string
|
||||
|
||||
// Path combines with BasePath to construct the path to the UI, defaults to: "docs".
|
||||
Path string
|
||||
|
||||
// SpecURL is the URL of the spec document.
|
||||
//
|
||||
// Defaults to: /swagger.json
|
||||
SpecURL string
|
||||
|
||||
// Title for the documentation site, default to: API documentation
|
||||
Title string
|
||||
|
||||
// Template specifies a custom template to serve the UI
|
||||
Template string
|
||||
}
|
||||
|
||||
// toCommonUIOptions converts any UI option type to retain the common options.
|
||||
//
|
||||
// This uses gob encoding/decoding to convert common fields from one struct to another.
|
||||
func toCommonUIOptions(opts interface{}) uiOptions {
|
||||
var buf bytes.Buffer
|
||||
enc := gob.NewEncoder(&buf)
|
||||
dec := gob.NewDecoder(&buf)
|
||||
var o uiOptions
|
||||
err := enc.Encode(opts)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = dec.Decode(&o)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return o
|
||||
}
|
||||
|
||||
func fromCommonToAnyOptions[T any](source uiOptions, target *T) {
|
||||
var buf bytes.Buffer
|
||||
enc := gob.NewEncoder(&buf)
|
||||
dec := gob.NewDecoder(&buf)
|
||||
err := enc.Encode(source)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = dec.Decode(target)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// UIOption can be applied to UI serving middleware, such as Context.APIHandler or
|
||||
// Context.APIHandlerSwaggerUI to alter the defaut behavior.
|
||||
type UIOption func(*uiOptions)
|
||||
|
||||
func uiOptionsWithDefaults(opts []UIOption) uiOptions {
|
||||
var o uiOptions
|
||||
for _, apply := range opts {
|
||||
apply(&o)
|
||||
}
|
||||
|
||||
return o
|
||||
}
|
||||
|
||||
// WithUIBasePath sets the base path from where to serve the UI assets.
|
||||
//
|
||||
// By default, Context middleware sets this value to the API base path.
|
||||
func WithUIBasePath(base string) UIOption {
|
||||
return func(o *uiOptions) {
|
||||
if !strings.HasPrefix(base, "/") {
|
||||
base = "/" + base
|
||||
}
|
||||
o.BasePath = base
|
||||
}
|
||||
}
|
||||
|
||||
// WithUIPath sets the path from where to serve the UI assets (i.e. /{basepath}/{path}.
|
||||
func WithUIPath(pth string) UIOption {
|
||||
return func(o *uiOptions) {
|
||||
o.Path = pth
|
||||
}
|
||||
}
|
||||
|
||||
// WithUISpecURL sets the path from where to serve swagger spec document.
|
||||
//
|
||||
// This may be specified as a full URL or a path.
|
||||
//
|
||||
// By default, this is "/swagger.json"
|
||||
func WithUISpecURL(specURL string) UIOption {
|
||||
return func(o *uiOptions) {
|
||||
o.SpecURL = specURL
|
||||
}
|
||||
}
|
||||
|
||||
// WithUITitle sets the title of the UI.
|
||||
//
|
||||
// By default, Context middleware sets this value to the title found in the API spec.
|
||||
func WithUITitle(title string) UIOption {
|
||||
return func(o *uiOptions) {
|
||||
o.Title = title
|
||||
}
|
||||
}
|
||||
|
||||
// WithTemplate allows to set a custom template for the UI.
|
||||
//
|
||||
// UI middleware will panic if the template does not parse or execute properly.
|
||||
func WithTemplate(tpl string) UIOption {
|
||||
return func(o *uiOptions) {
|
||||
o.Template = tpl
|
||||
}
|
||||
}
|
||||
|
||||
// EnsureDefaults in case some options are missing
|
||||
func (r *uiOptions) EnsureDefaults() {
|
||||
if r.BasePath == "" {
|
||||
r.BasePath = "/"
|
||||
}
|
||||
if r.Path == "" {
|
||||
r.Path = defaultDocsPath
|
||||
}
|
||||
if r.SpecURL == "" {
|
||||
r.SpecURL = defaultDocsURL
|
||||
}
|
||||
if r.Title == "" {
|
||||
r.Title = defaultDocsTitle
|
||||
}
|
||||
}
|
||||
|
||||
// serveUI creates a middleware that serves a templated asset as text/html.
|
||||
func serveUI(pth string, assets []byte, next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
if path.Clean(r.URL.Path) == pth {
|
||||
rw.Header().Set(contentTypeHeader, "text/html; charset=utf-8")
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
_, _ = rw.Write(assets)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if next != nil {
|
||||
next.ServeHTTP(rw, r)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
rw.Header().Set(contentTypeHeader, "text/plain")
|
||||
rw.WriteHeader(http.StatusNotFound)
|
||||
_, _ = rw.Write([]byte(fmt.Sprintf("%q not found", pth)))
|
||||
})
|
||||
}
|
||||
12
vendor/github.com/go-openapi/runtime/middleware/validation.go
generated
vendored
12
vendor/github.com/go-openapi/runtime/middleware/validation.go
generated
vendored
|
|
@ -35,7 +35,6 @@ type validation struct {
|
|||
|
||||
// ContentType validates the content type of a request
|
||||
func validateContentType(allowed []string, actual string) error {
|
||||
debugLog("validating content type for %q against [%s]", actual, strings.Join(allowed, ", "))
|
||||
if len(allowed) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -57,13 +56,13 @@ func validateContentType(allowed []string, actual string) error {
|
|||
}
|
||||
|
||||
func validateRequest(ctx *Context, request *http.Request, route *MatchedRoute) *validation {
|
||||
debugLog("validating request %s %s", request.Method, request.URL.EscapedPath())
|
||||
validate := &validation{
|
||||
context: ctx,
|
||||
request: request,
|
||||
route: route,
|
||||
bound: make(map[string]interface{}),
|
||||
}
|
||||
validate.debugLogf("validating request %s %s", request.Method, request.URL.EscapedPath())
|
||||
|
||||
validate.contentType()
|
||||
if len(validate.result) == 0 {
|
||||
|
|
@ -76,8 +75,12 @@ func validateRequest(ctx *Context, request *http.Request, route *MatchedRoute) *
|
|||
return validate
|
||||
}
|
||||
|
||||
func (v *validation) debugLogf(format string, args ...any) {
|
||||
v.context.debugLogf(format, args...)
|
||||
}
|
||||
|
||||
func (v *validation) parameters() {
|
||||
debugLog("validating request parameters for %s %s", v.request.Method, v.request.URL.EscapedPath())
|
||||
v.debugLogf("validating request parameters for %s %s", v.request.Method, v.request.URL.EscapedPath())
|
||||
if result := v.route.Binder.Bind(v.request, v.route.Params, v.route.Consumer, v.bound); result != nil {
|
||||
if result.Error() == "validation failure list" {
|
||||
for _, e := range result.(*errors.Validation).Value.([]interface{}) {
|
||||
|
|
@ -91,7 +94,7 @@ func (v *validation) parameters() {
|
|||
|
||||
func (v *validation) contentType() {
|
||||
if len(v.result) == 0 && runtime.HasBody(v.request) {
|
||||
debugLog("validating body content type for %s %s", v.request.Method, v.request.URL.EscapedPath())
|
||||
v.debugLogf("validating body content type for %s %s", v.request.Method, v.request.URL.EscapedPath())
|
||||
ct, _, req, err := v.context.ContentType(v.request)
|
||||
if err != nil {
|
||||
v.result = append(v.result, err)
|
||||
|
|
@ -100,6 +103,7 @@ func (v *validation) contentType() {
|
|||
}
|
||||
|
||||
if len(v.result) == 0 {
|
||||
v.debugLogf("validating content type for %q against [%s]", ct, strings.Join(v.route.Consumes, ", "))
|
||||
if err := validateContentType(v.route.Consumes, ct); err != nil {
|
||||
v.result = append(v.result, err)
|
||||
}
|
||||
|
|
|
|||
23
vendor/github.com/go-openapi/spec/README.md
generated
vendored
23
vendor/github.com/go-openapi/spec/README.md
generated
vendored
|
|
@ -29,3 +29,26 @@ The object model for OpenAPI specification documents.
|
|||
> This [discussion thread](https://github.com/go-openapi/spec/issues/21) relates the full story.
|
||||
>
|
||||
> An early attempt to support Swagger 3 may be found at: https://github.com/go-openapi/spec3
|
||||
|
||||
* Does the unmarshaling support YAML?
|
||||
|
||||
> Not directly. The exposed types know only how to unmarshal from JSON.
|
||||
>
|
||||
> In order to load a YAML document as a Swagger spec, you need to use the loaders provided by
|
||||
> github.com/go-openapi/loads
|
||||
>
|
||||
> Take a look at the example there: https://pkg.go.dev/github.com/go-openapi/loads#example-Spec
|
||||
>
|
||||
> See also https://github.com/go-openapi/spec/issues/164
|
||||
|
||||
* How can I validate a spec?
|
||||
|
||||
> Validation is provided by [the validate package](http://github.com/go-openapi/validate)
|
||||
|
||||
* Why do we have an `ID` field for `Schema` which is not part of the swagger spec?
|
||||
|
||||
> We found jsonschema compatibility more important: since `id` in jsonschema influences
|
||||
> how `$ref` are resolved.
|
||||
> This `id` does not conflict with any property named `id`.
|
||||
>
|
||||
> See also https://github.com/go-openapi/spec/issues/23
|
||||
|
|
|
|||
58
vendor/github.com/go-openapi/spec/expander.go
generated
vendored
58
vendor/github.com/go-openapi/spec/expander.go
generated
vendored
|
|
@ -57,7 +57,7 @@ func ExpandSpec(spec *Swagger, options *ExpandOptions) error {
|
|||
if !options.SkipSchemas {
|
||||
for key, definition := range spec.Definitions {
|
||||
parentRefs := make([]string, 0, 10)
|
||||
parentRefs = append(parentRefs, fmt.Sprintf("#/definitions/%s", key))
|
||||
parentRefs = append(parentRefs, "#/definitions/"+key)
|
||||
|
||||
def, err := expandSchema(definition, parentRefs, resolver, specBasePath)
|
||||
if resolver.shouldStopOnError(err) {
|
||||
|
|
@ -213,7 +213,19 @@ func expandSchema(target Schema, parentRefs []string, resolver *schemaLoader, ba
|
|||
}
|
||||
|
||||
if target.Ref.String() != "" {
|
||||
return expandSchemaRef(target, parentRefs, resolver, basePath)
|
||||
if !resolver.options.SkipSchemas {
|
||||
return expandSchemaRef(target, parentRefs, resolver, basePath)
|
||||
}
|
||||
|
||||
// when "expand" with SkipSchema, we just rebase the existing $ref without replacing
|
||||
// the full schema.
|
||||
rebasedRef, err := NewRef(normalizeURI(target.Ref.String(), basePath))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
target.Ref = denormalizeRef(&rebasedRef, resolver.context.basePath, resolver.context.rootID)
|
||||
|
||||
return &target, nil
|
||||
}
|
||||
|
||||
for k := range target.Definitions {
|
||||
|
|
@ -525,21 +537,25 @@ func getRefAndSchema(input interface{}) (*Ref, *Schema, error) {
|
|||
}
|
||||
|
||||
func expandParameterOrResponse(input interface{}, resolver *schemaLoader, basePath string) error {
|
||||
ref, _, err := getRefAndSchema(input)
|
||||
ref, sch, err := getRefAndSchema(input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ref == nil {
|
||||
if ref == nil && sch == nil { // nothing to do
|
||||
return nil
|
||||
}
|
||||
|
||||
parentRefs := make([]string, 0, 10)
|
||||
if err = resolver.deref(input, parentRefs, basePath); resolver.shouldStopOnError(err) {
|
||||
return err
|
||||
if ref != nil {
|
||||
// dereference this $ref
|
||||
if err = resolver.deref(input, parentRefs, basePath); resolver.shouldStopOnError(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
ref, sch, _ = getRefAndSchema(input)
|
||||
}
|
||||
|
||||
ref, sch, _ := getRefAndSchema(input)
|
||||
if ref.String() != "" {
|
||||
transitiveResolver := resolver.transitiveResolver(basePath, *ref)
|
||||
basePath = resolver.updateBasePath(transitiveResolver, basePath)
|
||||
|
|
@ -551,6 +567,7 @@ func expandParameterOrResponse(input interface{}, resolver *schemaLoader, basePa
|
|||
if ref != nil {
|
||||
*ref = Ref{}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -560,38 +577,29 @@ func expandParameterOrResponse(input interface{}, resolver *schemaLoader, basePa
|
|||
return ern
|
||||
}
|
||||
|
||||
switch {
|
||||
case resolver.isCircular(&rebasedRef, basePath, parentRefs...):
|
||||
if resolver.isCircular(&rebasedRef, basePath, parentRefs...) {
|
||||
// this is a circular $ref: stop expansion
|
||||
if !resolver.options.AbsoluteCircularRef {
|
||||
sch.Ref = denormalizeRef(&rebasedRef, resolver.context.basePath, resolver.context.rootID)
|
||||
} else {
|
||||
sch.Ref = rebasedRef
|
||||
}
|
||||
case !resolver.options.SkipSchemas:
|
||||
// schema expanded to a $ref in another root
|
||||
sch.Ref = rebasedRef
|
||||
debugLog("rebased to: %s", sch.Ref.String())
|
||||
default:
|
||||
// skip schema expansion but rebase $ref to schema
|
||||
sch.Ref = denormalizeRef(&rebasedRef, resolver.context.basePath, resolver.context.rootID)
|
||||
}
|
||||
}
|
||||
|
||||
// $ref expansion or rebasing is performed by expandSchema below
|
||||
if ref != nil {
|
||||
*ref = Ref{}
|
||||
}
|
||||
|
||||
// expand schema
|
||||
if !resolver.options.SkipSchemas {
|
||||
s, err := expandSchema(*sch, parentRefs, resolver, basePath)
|
||||
if resolver.shouldStopOnError(err) {
|
||||
return err
|
||||
}
|
||||
if s == nil {
|
||||
// guard for when continuing on error
|
||||
return nil
|
||||
}
|
||||
// yes, we do it even if options.SkipSchema is true: we have to go down that rabbit hole and rebase nested $ref)
|
||||
s, err := expandSchema(*sch, parentRefs, resolver, basePath)
|
||||
if resolver.shouldStopOnError(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
if s != nil { // guard for when continuing on error
|
||||
*sch = *s
|
||||
}
|
||||
|
||||
|
|
|
|||
56
vendor/github.com/go-openapi/strfmt/default.go
generated
vendored
56
vendor/github.com/go-openapi/strfmt/default.go
generated
vendored
|
|
@ -25,6 +25,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/asaskevich/govalidator"
|
||||
"github.com/google/uuid"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
)
|
||||
|
||||
|
|
@ -57,24 +58,35 @@ const (
|
|||
// - long top-level domain names (e.g. example.london) are permitted
|
||||
// - symbol unicode points are permitted (e.g. emoji) (not for top-level domain)
|
||||
HostnamePattern = `^([a-zA-Z0-9\p{S}\p{L}]((-?[a-zA-Z0-9\p{S}\p{L}]{0,62})?)|([a-zA-Z0-9\p{S}\p{L}](([a-zA-Z0-9-\p{S}\p{L}]{0,61}[a-zA-Z0-9\p{S}\p{L}])?)(\.)){1,}([a-zA-Z\p{L}]){2,63})$`
|
||||
// UUIDPattern Regex for UUID that allows uppercase
|
||||
UUIDPattern = `(?i)^[0-9a-f]{8}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{12}$`
|
||||
// UUID3Pattern Regex for UUID3 that allows uppercase
|
||||
UUID3Pattern = `(?i)^[0-9a-f]{8}-?[0-9a-f]{4}-?3[0-9a-f]{3}-?[0-9a-f]{4}-?[0-9a-f]{12}$`
|
||||
// UUID4Pattern Regex for UUID4 that allows uppercase
|
||||
UUID4Pattern = `(?i)^[0-9a-f]{8}-?[0-9a-f]{4}-?4[0-9a-f]{3}-?[89ab][0-9a-f]{3}-?[0-9a-f]{12}$`
|
||||
// UUID5Pattern Regex for UUID5 that allows uppercase
|
||||
UUID5Pattern = `(?i)^[0-9a-f]{8}-?[0-9a-f]{4}-?5[0-9a-f]{3}-?[89ab][0-9a-f]{3}-?[0-9a-f]{12}$`
|
||||
|
||||
// json null type
|
||||
jsonNull = "null"
|
||||
)
|
||||
|
||||
const (
|
||||
// UUIDPattern Regex for UUID that allows uppercase
|
||||
//
|
||||
// Deprecated: strfmt no longer uses regular expressions to validate UUIDs.
|
||||
UUIDPattern = `(?i)(^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$)|(^[0-9a-f]{32}$)`
|
||||
|
||||
// UUID3Pattern Regex for UUID3 that allows uppercase
|
||||
//
|
||||
// Deprecated: strfmt no longer uses regular expressions to validate UUIDs.
|
||||
UUID3Pattern = `(?i)(^[0-9a-f]{8}-[0-9a-f]{4}-3[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$)|(^[0-9a-f]{12}3[0-9a-f]{3}?[0-9a-f]{16}$)`
|
||||
|
||||
// UUID4Pattern Regex for UUID4 that allows uppercase
|
||||
//
|
||||
// Deprecated: strfmt no longer uses regular expressions to validate UUIDs.
|
||||
UUID4Pattern = `(?i)(^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$)|(^[0-9a-f]{12}4[0-9a-f]{3}[89ab][0-9a-f]{15}$)`
|
||||
|
||||
// UUID5Pattern Regex for UUID5 that allows uppercase
|
||||
//
|
||||
// Deprecated: strfmt no longer uses regular expressions to validate UUIDs.
|
||||
UUID5Pattern = `(?i)(^[0-9a-f]{8}-[0-9a-f]{4}-5[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$)|(^[0-9a-f]{12}5[0-9a-f]{3}[89ab][0-9a-f]{15}$)`
|
||||
)
|
||||
|
||||
var (
|
||||
rxHostname = regexp.MustCompile(HostnamePattern)
|
||||
rxUUID = regexp.MustCompile(UUIDPattern)
|
||||
rxUUID3 = regexp.MustCompile(UUID3Pattern)
|
||||
rxUUID4 = regexp.MustCompile(UUID4Pattern)
|
||||
rxUUID5 = regexp.MustCompile(UUID5Pattern)
|
||||
)
|
||||
|
||||
// IsHostname returns true when the string is a valid hostname
|
||||
|
|
@ -99,24 +111,28 @@ func IsHostname(str string) bool {
|
|||
return valid
|
||||
}
|
||||
|
||||
// IsUUID returns true is the string matches a UUID, upper case is allowed
|
||||
// IsUUID returns true is the string matches a UUID (in any version, including v6 and v7), upper case is allowed
|
||||
func IsUUID(str string) bool {
|
||||
return rxUUID.MatchString(str)
|
||||
_, err := uuid.Parse(str)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// IsUUID3 returns true is the string matches a UUID, upper case is allowed
|
||||
// IsUUID3 returns true is the string matches a UUID v3, upper case is allowed
|
||||
func IsUUID3(str string) bool {
|
||||
return rxUUID3.MatchString(str)
|
||||
id, err := uuid.Parse(str)
|
||||
return err == nil && id.Version() == uuid.Version(3)
|
||||
}
|
||||
|
||||
// IsUUID4 returns true is the string matches a UUID, upper case is allowed
|
||||
// IsUUID4 returns true is the string matches a UUID v4, upper case is allowed
|
||||
func IsUUID4(str string) bool {
|
||||
return rxUUID4.MatchString(str)
|
||||
id, err := uuid.Parse(str)
|
||||
return err == nil && id.Version() == uuid.Version(4)
|
||||
}
|
||||
|
||||
// IsUUID5 returns true is the string matches a UUID, upper case is allowed
|
||||
// IsUUID5 returns true is the string matches a UUID v5, upper case is allowed
|
||||
func IsUUID5(str string) bool {
|
||||
return rxUUID5.MatchString(str)
|
||||
id, err := uuid.Parse(str)
|
||||
return err == nil && id.Version() == uuid.Version(5)
|
||||
}
|
||||
|
||||
// IsEmail validates an email address.
|
||||
|
|
|
|||
3
vendor/github.com/go-openapi/strfmt/format.go
generated
vendored
3
vendor/github.com/go-openapi/strfmt/format.go
generated
vendored
|
|
@ -16,6 +16,7 @@ package strfmt
|
|||
|
||||
import (
|
||||
"encoding"
|
||||
stderrors "errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
|
@ -117,7 +118,7 @@ func (f *defaultFormats) MapStructureHookFunc() mapstructure.DecodeHookFunc {
|
|||
case "datetime":
|
||||
input := data
|
||||
if len(input) == 0 {
|
||||
return nil, fmt.Errorf("empty string is an invalid datetime format")
|
||||
return nil, stderrors.New("empty string is an invalid datetime format")
|
||||
}
|
||||
return ParseDateTime(input)
|
||||
case "duration":
|
||||
|
|
|
|||
52
vendor/github.com/go-openapi/swag/BENCHMARK.md
generated
vendored
Normal file
52
vendor/github.com/go-openapi/swag/BENCHMARK.md
generated
vendored
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
# Benchmarks
|
||||
|
||||
## Name mangling utilities
|
||||
|
||||
```bash
|
||||
go test -bench XXX -run XXX -benchtime 30s
|
||||
```
|
||||
|
||||
### Benchmarks at b3e7a5386f996177e4808f11acb2aa93a0f660df
|
||||
|
||||
```
|
||||
goos: linux
|
||||
goarch: amd64
|
||||
pkg: github.com/go-openapi/swag
|
||||
cpu: Intel(R) Core(TM) i5-6200U CPU @ 2.30GHz
|
||||
BenchmarkToXXXName/ToGoName-4 862623 44101 ns/op 10450 B/op 732 allocs/op
|
||||
BenchmarkToXXXName/ToVarName-4 853656 40728 ns/op 10468 B/op 734 allocs/op
|
||||
BenchmarkToXXXName/ToFileName-4 1268312 27813 ns/op 9785 B/op 617 allocs/op
|
||||
BenchmarkToXXXName/ToCommandName-4 1276322 27903 ns/op 9785 B/op 617 allocs/op
|
||||
BenchmarkToXXXName/ToHumanNameLower-4 895334 40354 ns/op 10472 B/op 731 allocs/op
|
||||
BenchmarkToXXXName/ToHumanNameTitle-4 882441 40678 ns/op 10566 B/op 749 allocs/op
|
||||
```
|
||||
|
||||
### Benchmarks after PR #79
|
||||
|
||||
~ x10 performance improvement and ~ /100 memory allocations.
|
||||
|
||||
```
|
||||
goos: linux
|
||||
goarch: amd64
|
||||
pkg: github.com/go-openapi/swag
|
||||
cpu: Intel(R) Core(TM) i5-6200U CPU @ 2.30GHz
|
||||
BenchmarkToXXXName/ToGoName-4 9595830 3991 ns/op 42 B/op 5 allocs/op
|
||||
BenchmarkToXXXName/ToVarName-4 9194276 3984 ns/op 62 B/op 7 allocs/op
|
||||
BenchmarkToXXXName/ToFileName-4 17002711 2123 ns/op 147 B/op 7 allocs/op
|
||||
BenchmarkToXXXName/ToCommandName-4 16772926 2111 ns/op 147 B/op 7 allocs/op
|
||||
BenchmarkToXXXName/ToHumanNameLower-4 9788331 3749 ns/op 92 B/op 6 allocs/op
|
||||
BenchmarkToXXXName/ToHumanNameTitle-4 9188260 3941 ns/op 104 B/op 6 allocs/op
|
||||
```
|
||||
|
||||
```
|
||||
goos: linux
|
||||
goarch: amd64
|
||||
pkg: github.com/go-openapi/swag
|
||||
cpu: AMD Ryzen 7 5800X 8-Core Processor
|
||||
BenchmarkToXXXName/ToGoName-16 18527378 1972 ns/op 42 B/op 5 allocs/op
|
||||
BenchmarkToXXXName/ToVarName-16 15552692 2093 ns/op 62 B/op 7 allocs/op
|
||||
BenchmarkToXXXName/ToFileName-16 32161176 1117 ns/op 147 B/op 7 allocs/op
|
||||
BenchmarkToXXXName/ToCommandName-16 32256634 1137 ns/op 147 B/op 7 allocs/op
|
||||
BenchmarkToXXXName/ToHumanNameLower-16 18599661 1946 ns/op 92 B/op 6 allocs/op
|
||||
BenchmarkToXXXName/ToHumanNameTitle-16 17581353 2054 ns/op 105 B/op 6 allocs/op
|
||||
```
|
||||
139
vendor/github.com/go-openapi/swag/initialism_index.go
generated
vendored
139
vendor/github.com/go-openapi/swag/initialism_index.go
generated
vendored
|
|
@ -16,9 +16,130 @@ package swag
|
|||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
// commonInitialisms are common acronyms that are kept as whole uppercased words.
|
||||
commonInitialisms *indexOfInitialisms
|
||||
|
||||
// initialisms is a slice of sorted initialisms
|
||||
initialisms []string
|
||||
|
||||
// a copy of initialisms pre-baked as []rune
|
||||
initialismsRunes [][]rune
|
||||
initialismsUpperCased [][]rune
|
||||
|
||||
isInitialism func(string) bool
|
||||
|
||||
maxAllocMatches int
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Taken from https://github.com/golang/lint/blob/3390df4df2787994aea98de825b964ac7944b817/lint.go#L732-L769
|
||||
configuredInitialisms := map[string]bool{
|
||||
"ACL": true,
|
||||
"API": true,
|
||||
"ASCII": true,
|
||||
"CPU": true,
|
||||
"CSS": true,
|
||||
"DNS": true,
|
||||
"EOF": true,
|
||||
"GUID": true,
|
||||
"HTML": true,
|
||||
"HTTPS": true,
|
||||
"HTTP": true,
|
||||
"ID": true,
|
||||
"IP": true,
|
||||
"IPv4": true,
|
||||
"IPv6": true,
|
||||
"JSON": true,
|
||||
"LHS": true,
|
||||
"OAI": true,
|
||||
"QPS": true,
|
||||
"RAM": true,
|
||||
"RHS": true,
|
||||
"RPC": true,
|
||||
"SLA": true,
|
||||
"SMTP": true,
|
||||
"SQL": true,
|
||||
"SSH": true,
|
||||
"TCP": true,
|
||||
"TLS": true,
|
||||
"TTL": true,
|
||||
"UDP": true,
|
||||
"UI": true,
|
||||
"UID": true,
|
||||
"UUID": true,
|
||||
"URI": true,
|
||||
"URL": true,
|
||||
"UTF8": true,
|
||||
"VM": true,
|
||||
"XML": true,
|
||||
"XMPP": true,
|
||||
"XSRF": true,
|
||||
"XSS": true,
|
||||
}
|
||||
|
||||
// a thread-safe index of initialisms
|
||||
commonInitialisms = newIndexOfInitialisms().load(configuredInitialisms)
|
||||
initialisms = commonInitialisms.sorted()
|
||||
initialismsRunes = asRunes(initialisms)
|
||||
initialismsUpperCased = asUpperCased(initialisms)
|
||||
maxAllocMatches = maxAllocHeuristic(initialismsRunes)
|
||||
|
||||
// a test function
|
||||
isInitialism = commonInitialisms.isInitialism
|
||||
}
|
||||
|
||||
func asRunes(in []string) [][]rune {
|
||||
out := make([][]rune, len(in))
|
||||
for i, initialism := range in {
|
||||
out[i] = []rune(initialism)
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func asUpperCased(in []string) [][]rune {
|
||||
out := make([][]rune, len(in))
|
||||
|
||||
for i, initialism := range in {
|
||||
out[i] = []rune(upper(trim(initialism)))
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func maxAllocHeuristic(in [][]rune) int {
|
||||
heuristic := make(map[rune]int)
|
||||
for _, initialism := range in {
|
||||
heuristic[initialism[0]]++
|
||||
}
|
||||
|
||||
var maxAlloc int
|
||||
for _, val := range heuristic {
|
||||
if val > maxAlloc {
|
||||
maxAlloc = val
|
||||
}
|
||||
}
|
||||
|
||||
return maxAlloc
|
||||
}
|
||||
|
||||
// AddInitialisms add additional initialisms
|
||||
func AddInitialisms(words ...string) {
|
||||
for _, word := range words {
|
||||
// commonInitialisms[upper(word)] = true
|
||||
commonInitialisms.add(upper(word))
|
||||
}
|
||||
// sort again
|
||||
initialisms = commonInitialisms.sorted()
|
||||
initialismsRunes = asRunes(initialisms)
|
||||
initialismsUpperCased = asUpperCased(initialisms)
|
||||
}
|
||||
|
||||
// indexOfInitialisms is a thread-safe implementation of the sorted index of initialisms.
|
||||
// Since go1.9, this may be implemented with sync.Map.
|
||||
type indexOfInitialisms struct {
|
||||
|
|
@ -55,7 +176,7 @@ func (m *indexOfInitialisms) add(key string) *indexOfInitialisms {
|
|||
func (m *indexOfInitialisms) sorted() (result []string) {
|
||||
m.sortMutex.Lock()
|
||||
defer m.sortMutex.Unlock()
|
||||
m.index.Range(func(key, value interface{}) bool {
|
||||
m.index.Range(func(key, _ interface{}) bool {
|
||||
k := key.(string)
|
||||
result = append(result, k)
|
||||
return true
|
||||
|
|
@ -63,3 +184,19 @@ func (m *indexOfInitialisms) sorted() (result []string) {
|
|||
sort.Sort(sort.Reverse(byInitialism(result)))
|
||||
return
|
||||
}
|
||||
|
||||
type byInitialism []string
|
||||
|
||||
func (s byInitialism) Len() int {
|
||||
return len(s)
|
||||
}
|
||||
func (s byInitialism) Swap(i, j int) {
|
||||
s[i], s[j] = s[j], s[i]
|
||||
}
|
||||
func (s byInitialism) Less(i, j int) bool {
|
||||
if len(s[i]) != len(s[j]) {
|
||||
return len(s[i]) < len(s[j])
|
||||
}
|
||||
|
||||
return strings.Compare(s[i], s[j]) > 0
|
||||
}
|
||||
|
|
|
|||
72
vendor/github.com/go-openapi/swag/name_lexem.go
generated
vendored
72
vendor/github.com/go-openapi/swag/name_lexem.go
generated
vendored
|
|
@ -14,74 +14,80 @@
|
|||
|
||||
package swag
|
||||
|
||||
import "unicode"
|
||||
import (
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
type (
|
||||
nameLexem interface {
|
||||
GetUnsafeGoName() string
|
||||
GetOriginal() string
|
||||
IsInitialism() bool
|
||||
}
|
||||
lexemKind uint8
|
||||
|
||||
initialismNameLexem struct {
|
||||
nameLexem struct {
|
||||
original string
|
||||
matchedInitialism string
|
||||
}
|
||||
|
||||
casualNameLexem struct {
|
||||
original string
|
||||
kind lexemKind
|
||||
}
|
||||
)
|
||||
|
||||
func newInitialismNameLexem(original, matchedInitialism string) *initialismNameLexem {
|
||||
return &initialismNameLexem{
|
||||
const (
|
||||
lexemKindCasualName lexemKind = iota
|
||||
lexemKindInitialismName
|
||||
)
|
||||
|
||||
func newInitialismNameLexem(original, matchedInitialism string) nameLexem {
|
||||
return nameLexem{
|
||||
kind: lexemKindInitialismName,
|
||||
original: original,
|
||||
matchedInitialism: matchedInitialism,
|
||||
}
|
||||
}
|
||||
|
||||
func newCasualNameLexem(original string) *casualNameLexem {
|
||||
return &casualNameLexem{
|
||||
func newCasualNameLexem(original string) nameLexem {
|
||||
return nameLexem{
|
||||
kind: lexemKindCasualName,
|
||||
original: original,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *initialismNameLexem) GetUnsafeGoName() string {
|
||||
return l.matchedInitialism
|
||||
}
|
||||
func (l nameLexem) GetUnsafeGoName() string {
|
||||
if l.kind == lexemKindInitialismName {
|
||||
return l.matchedInitialism
|
||||
}
|
||||
|
||||
var (
|
||||
first rune
|
||||
rest string
|
||||
)
|
||||
|
||||
func (l *casualNameLexem) GetUnsafeGoName() string {
|
||||
var first rune
|
||||
var rest string
|
||||
for i, orig := range l.original {
|
||||
if i == 0 {
|
||||
first = orig
|
||||
continue
|
||||
}
|
||||
|
||||
if i > 0 {
|
||||
rest = l.original[i:]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if len(l.original) > 1 {
|
||||
return string(unicode.ToUpper(first)) + lower(rest)
|
||||
b := poolOfBuffers.BorrowBuffer(utf8.UTFMax + len(rest))
|
||||
defer func() {
|
||||
poolOfBuffers.RedeemBuffer(b)
|
||||
}()
|
||||
b.WriteRune(unicode.ToUpper(first))
|
||||
b.WriteString(lower(rest))
|
||||
return b.String()
|
||||
}
|
||||
|
||||
return l.original
|
||||
}
|
||||
|
||||
func (l *initialismNameLexem) GetOriginal() string {
|
||||
func (l nameLexem) GetOriginal() string {
|
||||
return l.original
|
||||
}
|
||||
|
||||
func (l *casualNameLexem) GetOriginal() string {
|
||||
return l.original
|
||||
}
|
||||
|
||||
func (l *initialismNameLexem) IsInitialism() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (l *casualNameLexem) IsInitialism() bool {
|
||||
return false
|
||||
func (l nameLexem) IsInitialism() bool {
|
||||
return l.kind == lexemKindInitialismName
|
||||
}
|
||||
|
|
|
|||
490
vendor/github.com/go-openapi/swag/split.go
generated
vendored
490
vendor/github.com/go-openapi/swag/split.go
generated
vendored
|
|
@ -15,124 +15,269 @@
|
|||
package swag
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"sync"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
var nameReplaceTable = map[rune]string{
|
||||
'@': "At ",
|
||||
'&': "And ",
|
||||
'|': "Pipe ",
|
||||
'$': "Dollar ",
|
||||
'!': "Bang ",
|
||||
'-': "",
|
||||
'_': "",
|
||||
}
|
||||
|
||||
type (
|
||||
splitter struct {
|
||||
postSplitInitialismCheck bool
|
||||
initialisms []string
|
||||
initialismsRunes [][]rune
|
||||
initialismsUpperCased [][]rune // initialisms cached in their trimmed, upper-cased version
|
||||
postSplitInitialismCheck bool
|
||||
}
|
||||
|
||||
splitterOption func(*splitter) *splitter
|
||||
splitterOption func(*splitter)
|
||||
|
||||
initialismMatch struct {
|
||||
body []rune
|
||||
start, end int
|
||||
complete bool
|
||||
}
|
||||
initialismMatches []initialismMatch
|
||||
)
|
||||
|
||||
// split calls the splitter; splitter provides more control and post options
|
||||
func split(str string) []string {
|
||||
lexems := newSplitter().split(str)
|
||||
result := make([]string, 0, len(lexems))
|
||||
type (
|
||||
// memory pools of temporary objects.
|
||||
//
|
||||
// These are used to recycle temporarily allocated objects
|
||||
// and relieve the GC from undue pressure.
|
||||
|
||||
for _, lexem := range lexems {
|
||||
matchesPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
|
||||
buffersPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
|
||||
lexemsPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
|
||||
splittersPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
// poolOfMatches holds temporary slices for recycling during the initialism match process
|
||||
poolOfMatches = matchesPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
s := make(initialismMatches, 0, maxAllocMatches)
|
||||
|
||||
return &s
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
poolOfBuffers = buffersPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
return new(bytes.Buffer)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
poolOfLexems = lexemsPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
s := make([]nameLexem, 0, maxAllocMatches)
|
||||
|
||||
return &s
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
poolOfSplitters = splittersPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
s := newSplitter()
|
||||
|
||||
return &s
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// nameReplaceTable finds a word representation for special characters.
|
||||
func nameReplaceTable(r rune) (string, bool) {
|
||||
switch r {
|
||||
case '@':
|
||||
return "At ", true
|
||||
case '&':
|
||||
return "And ", true
|
||||
case '|':
|
||||
return "Pipe ", true
|
||||
case '$':
|
||||
return "Dollar ", true
|
||||
case '!':
|
||||
return "Bang ", true
|
||||
case '-':
|
||||
return "", true
|
||||
case '_':
|
||||
return "", true
|
||||
default:
|
||||
return "", false
|
||||
}
|
||||
}
|
||||
|
||||
// split calls the splitter.
|
||||
//
|
||||
// Use newSplitter for more control and options
|
||||
func split(str string) []string {
|
||||
s := poolOfSplitters.BorrowSplitter()
|
||||
lexems := s.split(str)
|
||||
result := make([]string, 0, len(*lexems))
|
||||
|
||||
for _, lexem := range *lexems {
|
||||
result = append(result, lexem.GetOriginal())
|
||||
}
|
||||
poolOfLexems.RedeemLexems(lexems)
|
||||
poolOfSplitters.RedeemSplitter(s)
|
||||
|
||||
return result
|
||||
|
||||
}
|
||||
|
||||
func (s *splitter) split(str string) []nameLexem {
|
||||
return s.toNameLexems(str)
|
||||
}
|
||||
|
||||
func newSplitter(options ...splitterOption) *splitter {
|
||||
splitter := &splitter{
|
||||
func newSplitter(options ...splitterOption) splitter {
|
||||
s := splitter{
|
||||
postSplitInitialismCheck: false,
|
||||
initialisms: initialisms,
|
||||
initialismsRunes: initialismsRunes,
|
||||
initialismsUpperCased: initialismsUpperCased,
|
||||
}
|
||||
|
||||
for _, option := range options {
|
||||
splitter = option(splitter)
|
||||
option(&s)
|
||||
}
|
||||
|
||||
return splitter
|
||||
}
|
||||
|
||||
// withPostSplitInitialismCheck allows to catch initialisms after main split process
|
||||
func withPostSplitInitialismCheck(s *splitter) *splitter {
|
||||
s.postSplitInitialismCheck = true
|
||||
return s
|
||||
}
|
||||
|
||||
type (
|
||||
initialismMatch struct {
|
||||
start, end int
|
||||
body []rune
|
||||
complete bool
|
||||
}
|
||||
initialismMatches []*initialismMatch
|
||||
)
|
||||
// withPostSplitInitialismCheck allows to catch initialisms after main split process
|
||||
func withPostSplitInitialismCheck(s *splitter) {
|
||||
s.postSplitInitialismCheck = true
|
||||
}
|
||||
|
||||
func (s *splitter) toNameLexems(name string) []nameLexem {
|
||||
func (p matchesPool) BorrowMatches() *initialismMatches {
|
||||
s := p.Get().(*initialismMatches)
|
||||
*s = (*s)[:0] // reset slice, keep allocated capacity
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (p buffersPool) BorrowBuffer(size int) *bytes.Buffer {
|
||||
s := p.Get().(*bytes.Buffer)
|
||||
s.Reset()
|
||||
|
||||
if s.Cap() < size {
|
||||
s.Grow(size)
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (p lexemsPool) BorrowLexems() *[]nameLexem {
|
||||
s := p.Get().(*[]nameLexem)
|
||||
*s = (*s)[:0] // reset slice, keep allocated capacity
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (p splittersPool) BorrowSplitter(options ...splitterOption) *splitter {
|
||||
s := p.Get().(*splitter)
|
||||
s.postSplitInitialismCheck = false // reset options
|
||||
for _, apply := range options {
|
||||
apply(s)
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (p matchesPool) RedeemMatches(s *initialismMatches) {
|
||||
p.Put(s)
|
||||
}
|
||||
|
||||
func (p buffersPool) RedeemBuffer(s *bytes.Buffer) {
|
||||
p.Put(s)
|
||||
}
|
||||
|
||||
func (p lexemsPool) RedeemLexems(s *[]nameLexem) {
|
||||
p.Put(s)
|
||||
}
|
||||
|
||||
func (p splittersPool) RedeemSplitter(s *splitter) {
|
||||
p.Put(s)
|
||||
}
|
||||
|
||||
func (m initialismMatch) isZero() bool {
|
||||
return m.start == 0 && m.end == 0
|
||||
}
|
||||
|
||||
func (s splitter) split(name string) *[]nameLexem {
|
||||
nameRunes := []rune(name)
|
||||
matches := s.gatherInitialismMatches(nameRunes)
|
||||
if matches == nil {
|
||||
return poolOfLexems.BorrowLexems()
|
||||
}
|
||||
|
||||
return s.mapMatchesToNameLexems(nameRunes, matches)
|
||||
}
|
||||
|
||||
func (s *splitter) gatherInitialismMatches(nameRunes []rune) initialismMatches {
|
||||
matches := make(initialismMatches, 0)
|
||||
func (s splitter) gatherInitialismMatches(nameRunes []rune) *initialismMatches {
|
||||
var matches *initialismMatches
|
||||
|
||||
for currentRunePosition, currentRune := range nameRunes {
|
||||
newMatches := make(initialismMatches, 0, len(matches))
|
||||
// recycle these allocations as we loop over runes
|
||||
// with such recycling, only 2 slices should be allocated per call
|
||||
// instead of o(n).
|
||||
newMatches := poolOfMatches.BorrowMatches()
|
||||
|
||||
// check current initialism matches
|
||||
for _, match := range matches {
|
||||
if keepCompleteMatch := match.complete; keepCompleteMatch {
|
||||
newMatches = append(newMatches, match)
|
||||
continue
|
||||
}
|
||||
|
||||
// drop failed match
|
||||
currentMatchRune := match.body[currentRunePosition-match.start]
|
||||
if !s.initialismRuneEqual(currentMatchRune, currentRune) {
|
||||
continue
|
||||
}
|
||||
|
||||
// try to complete ongoing match
|
||||
if currentRunePosition-match.start == len(match.body)-1 {
|
||||
// we are close; the next step is to check the symbol ahead
|
||||
// if it is a small letter, then it is not the end of match
|
||||
// but beginning of the next word
|
||||
|
||||
if currentRunePosition < len(nameRunes)-1 {
|
||||
nextRune := nameRunes[currentRunePosition+1]
|
||||
if newWord := unicode.IsLower(nextRune); newWord {
|
||||
// oh ok, it was the start of a new word
|
||||
continue
|
||||
}
|
||||
if matches != nil { // skip first iteration
|
||||
for _, match := range *matches {
|
||||
if keepCompleteMatch := match.complete; keepCompleteMatch {
|
||||
*newMatches = append(*newMatches, match)
|
||||
continue
|
||||
}
|
||||
|
||||
match.complete = true
|
||||
match.end = currentRunePosition
|
||||
}
|
||||
// drop failed match
|
||||
currentMatchRune := match.body[currentRunePosition-match.start]
|
||||
if currentMatchRune != currentRune {
|
||||
continue
|
||||
}
|
||||
|
||||
newMatches = append(newMatches, match)
|
||||
// try to complete ongoing match
|
||||
if currentRunePosition-match.start == len(match.body)-1 {
|
||||
// we are close; the next step is to check the symbol ahead
|
||||
// if it is a small letter, then it is not the end of match
|
||||
// but beginning of the next word
|
||||
|
||||
if currentRunePosition < len(nameRunes)-1 {
|
||||
nextRune := nameRunes[currentRunePosition+1]
|
||||
if newWord := unicode.IsLower(nextRune); newWord {
|
||||
// oh ok, it was the start of a new word
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
match.complete = true
|
||||
match.end = currentRunePosition
|
||||
}
|
||||
|
||||
*newMatches = append(*newMatches, match)
|
||||
}
|
||||
}
|
||||
|
||||
// check for new initialism matches
|
||||
for _, initialism := range s.initialisms {
|
||||
initialismRunes := []rune(initialism)
|
||||
if s.initialismRuneEqual(initialismRunes[0], currentRune) {
|
||||
newMatches = append(newMatches, &initialismMatch{
|
||||
for i := range s.initialisms {
|
||||
initialismRunes := s.initialismsRunes[i]
|
||||
if initialismRunes[0] == currentRune {
|
||||
*newMatches = append(*newMatches, initialismMatch{
|
||||
start: currentRunePosition,
|
||||
body: initialismRunes,
|
||||
complete: false,
|
||||
|
|
@ -140,24 +285,28 @@ func (s *splitter) gatherInitialismMatches(nameRunes []rune) initialismMatches {
|
|||
}
|
||||
}
|
||||
|
||||
if matches != nil {
|
||||
poolOfMatches.RedeemMatches(matches)
|
||||
}
|
||||
matches = newMatches
|
||||
}
|
||||
|
||||
// up to the caller to redeem this last slice
|
||||
return matches
|
||||
}
|
||||
|
||||
func (s *splitter) mapMatchesToNameLexems(nameRunes []rune, matches initialismMatches) []nameLexem {
|
||||
nameLexems := make([]nameLexem, 0)
|
||||
func (s splitter) mapMatchesToNameLexems(nameRunes []rune, matches *initialismMatches) *[]nameLexem {
|
||||
nameLexems := poolOfLexems.BorrowLexems()
|
||||
|
||||
var lastAcceptedMatch *initialismMatch
|
||||
for _, match := range matches {
|
||||
var lastAcceptedMatch initialismMatch
|
||||
for _, match := range *matches {
|
||||
if !match.complete {
|
||||
continue
|
||||
}
|
||||
|
||||
if firstMatch := lastAcceptedMatch == nil; firstMatch {
|
||||
nameLexems = append(nameLexems, s.breakCasualString(nameRunes[:match.start])...)
|
||||
nameLexems = append(nameLexems, s.breakInitialism(string(match.body)))
|
||||
if firstMatch := lastAcceptedMatch.isZero(); firstMatch {
|
||||
s.appendBrokenDownCasualString(nameLexems, nameRunes[:match.start])
|
||||
*nameLexems = append(*nameLexems, s.breakInitialism(string(match.body)))
|
||||
|
||||
lastAcceptedMatch = match
|
||||
|
||||
|
|
@ -169,63 +318,66 @@ func (s *splitter) mapMatchesToNameLexems(nameRunes []rune, matches initialismMa
|
|||
}
|
||||
|
||||
middle := nameRunes[lastAcceptedMatch.end+1 : match.start]
|
||||
nameLexems = append(nameLexems, s.breakCasualString(middle)...)
|
||||
nameLexems = append(nameLexems, s.breakInitialism(string(match.body)))
|
||||
s.appendBrokenDownCasualString(nameLexems, middle)
|
||||
*nameLexems = append(*nameLexems, s.breakInitialism(string(match.body)))
|
||||
|
||||
lastAcceptedMatch = match
|
||||
}
|
||||
|
||||
// we have not found any accepted matches
|
||||
if lastAcceptedMatch == nil {
|
||||
return s.breakCasualString(nameRunes)
|
||||
if lastAcceptedMatch.isZero() {
|
||||
*nameLexems = (*nameLexems)[:0]
|
||||
s.appendBrokenDownCasualString(nameLexems, nameRunes)
|
||||
} else if lastAcceptedMatch.end+1 != len(nameRunes) {
|
||||
rest := nameRunes[lastAcceptedMatch.end+1:]
|
||||
s.appendBrokenDownCasualString(nameLexems, rest)
|
||||
}
|
||||
|
||||
if lastAcceptedMatch.end+1 != len(nameRunes) {
|
||||
rest := nameRunes[lastAcceptedMatch.end+1:]
|
||||
nameLexems = append(nameLexems, s.breakCasualString(rest)...)
|
||||
}
|
||||
poolOfMatches.RedeemMatches(matches)
|
||||
|
||||
return nameLexems
|
||||
}
|
||||
|
||||
func (s *splitter) initialismRuneEqual(a, b rune) bool {
|
||||
return a == b
|
||||
}
|
||||
|
||||
func (s *splitter) breakInitialism(original string) nameLexem {
|
||||
func (s splitter) breakInitialism(original string) nameLexem {
|
||||
return newInitialismNameLexem(original, original)
|
||||
}
|
||||
|
||||
func (s *splitter) breakCasualString(str []rune) []nameLexem {
|
||||
segments := make([]nameLexem, 0)
|
||||
currentSegment := ""
|
||||
func (s splitter) appendBrokenDownCasualString(segments *[]nameLexem, str []rune) {
|
||||
currentSegment := poolOfBuffers.BorrowBuffer(len(str)) // unlike strings.Builder, bytes.Buffer initial storage can reused
|
||||
defer func() {
|
||||
poolOfBuffers.RedeemBuffer(currentSegment)
|
||||
}()
|
||||
|
||||
addCasualNameLexem := func(original string) {
|
||||
segments = append(segments, newCasualNameLexem(original))
|
||||
*segments = append(*segments, newCasualNameLexem(original))
|
||||
}
|
||||
|
||||
addInitialismNameLexem := func(original, match string) {
|
||||
segments = append(segments, newInitialismNameLexem(original, match))
|
||||
*segments = append(*segments, newInitialismNameLexem(original, match))
|
||||
}
|
||||
|
||||
addNameLexem := func(original string) {
|
||||
if s.postSplitInitialismCheck {
|
||||
for _, initialism := range s.initialisms {
|
||||
if upper(initialism) == upper(original) {
|
||||
addInitialismNameLexem(original, initialism)
|
||||
var addNameLexem func(string)
|
||||
if s.postSplitInitialismCheck {
|
||||
addNameLexem = func(original string) {
|
||||
for i := range s.initialisms {
|
||||
if isEqualFoldIgnoreSpace(s.initialismsUpperCased[i], original) {
|
||||
addInitialismNameLexem(original, s.initialisms[i])
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addCasualNameLexem(original)
|
||||
addCasualNameLexem(original)
|
||||
}
|
||||
} else {
|
||||
addNameLexem = addCasualNameLexem
|
||||
}
|
||||
|
||||
for _, rn := range string(str) {
|
||||
if replace, found := nameReplaceTable[rn]; found {
|
||||
if currentSegment != "" {
|
||||
addNameLexem(currentSegment)
|
||||
currentSegment = ""
|
||||
for _, rn := range str {
|
||||
if replace, found := nameReplaceTable(rn); found {
|
||||
if currentSegment.Len() > 0 {
|
||||
addNameLexem(currentSegment.String())
|
||||
currentSegment.Reset()
|
||||
}
|
||||
|
||||
if replace != "" {
|
||||
|
|
@ -236,27 +388,121 @@ func (s *splitter) breakCasualString(str []rune) []nameLexem {
|
|||
}
|
||||
|
||||
if !unicode.In(rn, unicode.L, unicode.M, unicode.N, unicode.Pc) {
|
||||
if currentSegment != "" {
|
||||
addNameLexem(currentSegment)
|
||||
currentSegment = ""
|
||||
if currentSegment.Len() > 0 {
|
||||
addNameLexem(currentSegment.String())
|
||||
currentSegment.Reset()
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if unicode.IsUpper(rn) {
|
||||
if currentSegment != "" {
|
||||
addNameLexem(currentSegment)
|
||||
if currentSegment.Len() > 0 {
|
||||
addNameLexem(currentSegment.String())
|
||||
}
|
||||
currentSegment = ""
|
||||
currentSegment.Reset()
|
||||
}
|
||||
|
||||
currentSegment += string(rn)
|
||||
currentSegment.WriteRune(rn)
|
||||
}
|
||||
|
||||
if currentSegment != "" {
|
||||
addNameLexem(currentSegment)
|
||||
if currentSegment.Len() > 0 {
|
||||
addNameLexem(currentSegment.String())
|
||||
}
|
||||
|
||||
return segments
|
||||
}
|
||||
|
||||
// isEqualFoldIgnoreSpace is the same as strings.EqualFold, but
|
||||
// it ignores leading and trailing blank spaces in the compared
|
||||
// string.
|
||||
//
|
||||
// base is assumed to be composed of upper-cased runes, and be already
|
||||
// trimmed.
|
||||
//
|
||||
// This code is heavily inspired from strings.EqualFold.
|
||||
func isEqualFoldIgnoreSpace(base []rune, str string) bool {
|
||||
var i, baseIndex int
|
||||
// equivalent to b := []byte(str), but without data copy
|
||||
b := hackStringBytes(str)
|
||||
|
||||
for i < len(b) {
|
||||
if c := b[i]; c < utf8.RuneSelf {
|
||||
// fast path for ASCII
|
||||
if c != ' ' && c != '\t' {
|
||||
break
|
||||
}
|
||||
i++
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
// unicode case
|
||||
r, size := utf8.DecodeRune(b[i:])
|
||||
if !unicode.IsSpace(r) {
|
||||
break
|
||||
}
|
||||
i += size
|
||||
}
|
||||
|
||||
if i >= len(b) {
|
||||
return len(base) == 0
|
||||
}
|
||||
|
||||
for _, baseRune := range base {
|
||||
if i >= len(b) {
|
||||
break
|
||||
}
|
||||
|
||||
if c := b[i]; c < utf8.RuneSelf {
|
||||
// single byte rune case (ASCII)
|
||||
if baseRune >= utf8.RuneSelf {
|
||||
return false
|
||||
}
|
||||
|
||||
baseChar := byte(baseRune)
|
||||
if c != baseChar &&
|
||||
!('a' <= c && c <= 'z' && c-'a'+'A' == baseChar) {
|
||||
return false
|
||||
}
|
||||
|
||||
baseIndex++
|
||||
i++
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
// unicode case
|
||||
r, size := utf8.DecodeRune(b[i:])
|
||||
if unicode.ToUpper(r) != baseRune {
|
||||
return false
|
||||
}
|
||||
baseIndex++
|
||||
i += size
|
||||
}
|
||||
|
||||
if baseIndex != len(base) {
|
||||
return false
|
||||
}
|
||||
|
||||
// all passed: now we should only have blanks
|
||||
for i < len(b) {
|
||||
if c := b[i]; c < utf8.RuneSelf {
|
||||
// fast path for ASCII
|
||||
if c != ' ' && c != '\t' {
|
||||
return false
|
||||
}
|
||||
i++
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
// unicode case
|
||||
r, size := utf8.DecodeRune(b[i:])
|
||||
if !unicode.IsSpace(r) {
|
||||
return false
|
||||
}
|
||||
|
||||
i += size
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
8
vendor/github.com/go-openapi/swag/string_bytes.go
generated
vendored
Normal file
8
vendor/github.com/go-openapi/swag/string_bytes.go
generated
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
package swag
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// hackStringBytes returns the (unsafe) underlying bytes slice of a string.
|
||||
func hackStringBytes(str string) []byte {
|
||||
return unsafe.Slice(unsafe.StringData(str), len(str))
|
||||
}
|
||||
206
vendor/github.com/go-openapi/swag/util.go
generated
vendored
206
vendor/github.com/go-openapi/swag/util.go
generated
vendored
|
|
@ -18,76 +18,25 @@ import (
|
|||
"reflect"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// commonInitialisms are common acronyms that are kept as whole uppercased words.
|
||||
var commonInitialisms *indexOfInitialisms
|
||||
|
||||
// initialisms is a slice of sorted initialisms
|
||||
var initialisms []string
|
||||
|
||||
var isInitialism func(string) bool
|
||||
|
||||
// GoNamePrefixFunc sets an optional rule to prefix go names
|
||||
// which do not start with a letter.
|
||||
//
|
||||
// The prefix function is assumed to return a string that starts with an upper case letter.
|
||||
//
|
||||
// e.g. to help convert "123" into "{prefix}123"
|
||||
//
|
||||
// The default is to prefix with "X"
|
||||
var GoNamePrefixFunc func(string) string
|
||||
|
||||
func init() {
|
||||
// Taken from https://github.com/golang/lint/blob/3390df4df2787994aea98de825b964ac7944b817/lint.go#L732-L769
|
||||
var configuredInitialisms = map[string]bool{
|
||||
"ACL": true,
|
||||
"API": true,
|
||||
"ASCII": true,
|
||||
"CPU": true,
|
||||
"CSS": true,
|
||||
"DNS": true,
|
||||
"EOF": true,
|
||||
"GUID": true,
|
||||
"HTML": true,
|
||||
"HTTPS": true,
|
||||
"HTTP": true,
|
||||
"ID": true,
|
||||
"IP": true,
|
||||
"IPv4": true,
|
||||
"IPv6": true,
|
||||
"JSON": true,
|
||||
"LHS": true,
|
||||
"OAI": true,
|
||||
"QPS": true,
|
||||
"RAM": true,
|
||||
"RHS": true,
|
||||
"RPC": true,
|
||||
"SLA": true,
|
||||
"SMTP": true,
|
||||
"SQL": true,
|
||||
"SSH": true,
|
||||
"TCP": true,
|
||||
"TLS": true,
|
||||
"TTL": true,
|
||||
"UDP": true,
|
||||
"UI": true,
|
||||
"UID": true,
|
||||
"UUID": true,
|
||||
"URI": true,
|
||||
"URL": true,
|
||||
"UTF8": true,
|
||||
"VM": true,
|
||||
"XML": true,
|
||||
"XMPP": true,
|
||||
"XSRF": true,
|
||||
"XSS": true,
|
||||
func prefixFunc(name, in string) string {
|
||||
if GoNamePrefixFunc == nil {
|
||||
return "X" + in
|
||||
}
|
||||
|
||||
// a thread-safe index of initialisms
|
||||
commonInitialisms = newIndexOfInitialisms().load(configuredInitialisms)
|
||||
initialisms = commonInitialisms.sorted()
|
||||
|
||||
// a test function
|
||||
isInitialism = commonInitialisms.isInitialism
|
||||
return GoNamePrefixFunc(name) + in
|
||||
}
|
||||
|
||||
const (
|
||||
|
|
@ -156,25 +105,9 @@ func SplitByFormat(data, format string) []string {
|
|||
return result
|
||||
}
|
||||
|
||||
type byInitialism []string
|
||||
|
||||
func (s byInitialism) Len() int {
|
||||
return len(s)
|
||||
}
|
||||
func (s byInitialism) Swap(i, j int) {
|
||||
s[i], s[j] = s[j], s[i]
|
||||
}
|
||||
func (s byInitialism) Less(i, j int) bool {
|
||||
if len(s[i]) != len(s[j]) {
|
||||
return len(s[i]) < len(s[j])
|
||||
}
|
||||
|
||||
return strings.Compare(s[i], s[j]) > 0
|
||||
}
|
||||
|
||||
// Removes leading whitespaces
|
||||
func trim(str string) string {
|
||||
return strings.Trim(str, " ")
|
||||
return strings.TrimSpace(str)
|
||||
}
|
||||
|
||||
// Shortcut to strings.ToUpper()
|
||||
|
|
@ -188,15 +121,20 @@ func lower(str string) string {
|
|||
}
|
||||
|
||||
// Camelize an uppercased word
|
||||
func Camelize(word string) (camelized string) {
|
||||
func Camelize(word string) string {
|
||||
camelized := poolOfBuffers.BorrowBuffer(len(word))
|
||||
defer func() {
|
||||
poolOfBuffers.RedeemBuffer(camelized)
|
||||
}()
|
||||
|
||||
for pos, ru := range []rune(word) {
|
||||
if pos > 0 {
|
||||
camelized += string(unicode.ToLower(ru))
|
||||
camelized.WriteRune(unicode.ToLower(ru))
|
||||
} else {
|
||||
camelized += string(unicode.ToUpper(ru))
|
||||
camelized.WriteRune(unicode.ToUpper(ru))
|
||||
}
|
||||
}
|
||||
return
|
||||
return camelized.String()
|
||||
}
|
||||
|
||||
// ToFileName lowercases and underscores a go type name
|
||||
|
|
@ -224,33 +162,40 @@ func ToCommandName(name string) string {
|
|||
|
||||
// ToHumanNameLower represents a code name as a human series of words
|
||||
func ToHumanNameLower(name string) string {
|
||||
in := newSplitter(withPostSplitInitialismCheck).split(name)
|
||||
out := make([]string, 0, len(in))
|
||||
s := poolOfSplitters.BorrowSplitter(withPostSplitInitialismCheck)
|
||||
in := s.split(name)
|
||||
poolOfSplitters.RedeemSplitter(s)
|
||||
out := make([]string, 0, len(*in))
|
||||
|
||||
for _, w := range in {
|
||||
for _, w := range *in {
|
||||
if !w.IsInitialism() {
|
||||
out = append(out, lower(w.GetOriginal()))
|
||||
} else {
|
||||
out = append(out, w.GetOriginal())
|
||||
out = append(out, trim(w.GetOriginal()))
|
||||
}
|
||||
}
|
||||
poolOfLexems.RedeemLexems(in)
|
||||
|
||||
return strings.Join(out, " ")
|
||||
}
|
||||
|
||||
// ToHumanNameTitle represents a code name as a human series of words with the first letters titleized
|
||||
func ToHumanNameTitle(name string) string {
|
||||
in := newSplitter(withPostSplitInitialismCheck).split(name)
|
||||
s := poolOfSplitters.BorrowSplitter(withPostSplitInitialismCheck)
|
||||
in := s.split(name)
|
||||
poolOfSplitters.RedeemSplitter(s)
|
||||
|
||||
out := make([]string, 0, len(in))
|
||||
for _, w := range in {
|
||||
original := w.GetOriginal()
|
||||
out := make([]string, 0, len(*in))
|
||||
for _, w := range *in {
|
||||
original := trim(w.GetOriginal())
|
||||
if !w.IsInitialism() {
|
||||
out = append(out, Camelize(original))
|
||||
} else {
|
||||
out = append(out, original)
|
||||
}
|
||||
}
|
||||
poolOfLexems.RedeemLexems(in)
|
||||
|
||||
return strings.Join(out, " ")
|
||||
}
|
||||
|
||||
|
|
@ -264,7 +209,7 @@ func ToJSONName(name string) string {
|
|||
out = append(out, lower(w))
|
||||
continue
|
||||
}
|
||||
out = append(out, Camelize(w))
|
||||
out = append(out, Camelize(trim(w)))
|
||||
}
|
||||
return strings.Join(out, "")
|
||||
}
|
||||
|
|
@ -283,35 +228,70 @@ func ToVarName(name string) string {
|
|||
|
||||
// ToGoName translates a swagger name which can be underscored or camel cased to a name that golint likes
|
||||
func ToGoName(name string) string {
|
||||
lexems := newSplitter(withPostSplitInitialismCheck).split(name)
|
||||
s := poolOfSplitters.BorrowSplitter(withPostSplitInitialismCheck)
|
||||
lexems := s.split(name)
|
||||
poolOfSplitters.RedeemSplitter(s)
|
||||
defer func() {
|
||||
poolOfLexems.RedeemLexems(lexems)
|
||||
}()
|
||||
lexemes := *lexems
|
||||
|
||||
result := ""
|
||||
for _, lexem := range lexems {
|
||||
if len(lexemes) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
result := poolOfBuffers.BorrowBuffer(len(name))
|
||||
defer func() {
|
||||
poolOfBuffers.RedeemBuffer(result)
|
||||
}()
|
||||
|
||||
// check if not starting with a letter, upper case
|
||||
firstPart := lexemes[0].GetUnsafeGoName()
|
||||
if lexemes[0].IsInitialism() {
|
||||
firstPart = upper(firstPart)
|
||||
}
|
||||
|
||||
if c := firstPart[0]; c < utf8.RuneSelf {
|
||||
// ASCII
|
||||
switch {
|
||||
case 'A' <= c && c <= 'Z':
|
||||
result.WriteString(firstPart)
|
||||
case 'a' <= c && c <= 'z':
|
||||
result.WriteByte(c - 'a' + 'A')
|
||||
result.WriteString(firstPart[1:])
|
||||
default:
|
||||
result.WriteString(prefixFunc(name, firstPart))
|
||||
// NOTE: no longer check if prefixFunc returns a string that starts with uppercase:
|
||||
// assume this is always the case
|
||||
}
|
||||
} else {
|
||||
// unicode
|
||||
firstRune, _ := utf8.DecodeRuneInString(firstPart)
|
||||
switch {
|
||||
case !unicode.IsLetter(firstRune):
|
||||
result.WriteString(prefixFunc(name, firstPart))
|
||||
case !unicode.IsUpper(firstRune):
|
||||
result.WriteString(prefixFunc(name, firstPart))
|
||||
/*
|
||||
result.WriteRune(unicode.ToUpper(firstRune))
|
||||
result.WriteString(firstPart[offset:])
|
||||
*/
|
||||
default:
|
||||
result.WriteString(firstPart)
|
||||
}
|
||||
}
|
||||
|
||||
for _, lexem := range lexemes[1:] {
|
||||
goName := lexem.GetUnsafeGoName()
|
||||
|
||||
// to support old behavior
|
||||
if lexem.IsInitialism() {
|
||||
goName = upper(goName)
|
||||
}
|
||||
result += goName
|
||||
result.WriteString(goName)
|
||||
}
|
||||
|
||||
if len(result) > 0 {
|
||||
// Only prefix with X when the first character isn't an ascii letter
|
||||
first := []rune(result)[0]
|
||||
if !unicode.IsLetter(first) || (first > unicode.MaxASCII && !unicode.IsUpper(first)) {
|
||||
if GoNamePrefixFunc == nil {
|
||||
return "X" + result
|
||||
}
|
||||
result = GoNamePrefixFunc(name) + result
|
||||
}
|
||||
first = []rune(result)[0]
|
||||
if unicode.IsLetter(first) && !unicode.IsUpper(first) {
|
||||
result = string(append([]rune{unicode.ToUpper(first)}, []rune(result)[1:]...))
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
return result.String()
|
||||
}
|
||||
|
||||
// ContainsStrings searches a slice of strings for a case-sensitive match
|
||||
|
|
@ -376,16 +356,6 @@ func IsZero(data interface{}) bool {
|
|||
}
|
||||
}
|
||||
|
||||
// AddInitialisms add additional initialisms
|
||||
func AddInitialisms(words ...string) {
|
||||
for _, word := range words {
|
||||
// commonInitialisms[upper(word)] = true
|
||||
commonInitialisms.add(upper(word))
|
||||
}
|
||||
// sort again
|
||||
initialisms = commonInitialisms.sorted()
|
||||
}
|
||||
|
||||
// CommandLineOptionsGroup represents a group of user-defined command line options
|
||||
type CommandLineOptionsGroup struct {
|
||||
ShortDescription string
|
||||
|
|
|
|||
38
vendor/github.com/go-openapi/swag/yaml.go
generated
vendored
38
vendor/github.com/go-openapi/swag/yaml.go
generated
vendored
|
|
@ -16,8 +16,11 @@ package swag
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
||||
"github.com/mailru/easyjson/jlexer"
|
||||
|
|
@ -48,7 +51,7 @@ func BytesToYAMLDoc(data []byte) (interface{}, error) {
|
|||
return nil, err
|
||||
}
|
||||
if document.Kind != yaml.DocumentNode || len(document.Content) != 1 || document.Content[0].Kind != yaml.MappingNode {
|
||||
return nil, fmt.Errorf("only YAML documents that are objects are supported")
|
||||
return nil, errors.New("only YAML documents that are objects are supported")
|
||||
}
|
||||
return &document, nil
|
||||
}
|
||||
|
|
@ -245,7 +248,27 @@ func (s JSONMapSlice) MarshalYAML() (interface{}, error) {
|
|||
return yaml.Marshal(&n)
|
||||
}
|
||||
|
||||
func isNil(input interface{}) bool {
|
||||
if input == nil {
|
||||
return true
|
||||
}
|
||||
kind := reflect.TypeOf(input).Kind()
|
||||
switch kind { //nolint:exhaustive
|
||||
case reflect.Ptr, reflect.Map, reflect.Slice, reflect.Chan:
|
||||
return reflect.ValueOf(input).IsNil()
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func json2yaml(item interface{}) (*yaml.Node, error) {
|
||||
if isNil(item) {
|
||||
return &yaml.Node{
|
||||
Kind: yaml.ScalarNode,
|
||||
Value: "null",
|
||||
}, nil
|
||||
}
|
||||
|
||||
switch val := item.(type) {
|
||||
case JSONMapSlice:
|
||||
var n yaml.Node
|
||||
|
|
@ -265,7 +288,14 @@ func json2yaml(item interface{}) (*yaml.Node, error) {
|
|||
case map[string]interface{}:
|
||||
var n yaml.Node
|
||||
n.Kind = yaml.MappingNode
|
||||
for k, v := range val {
|
||||
keys := make([]string, 0, len(val))
|
||||
for k := range val {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
for _, k := range keys {
|
||||
v := val[k]
|
||||
childNode, err := json2yaml(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -318,9 +348,9 @@ func json2yaml(item interface{}) (*yaml.Node, error) {
|
|||
Tag: yamlBoolScalar,
|
||||
Value: strconv.FormatBool(val),
|
||||
}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unhandled type: %T", val)
|
||||
}
|
||||
|
||||
return nil, nil //nolint:nilnil
|
||||
}
|
||||
|
||||
// JSONMapItem represents the value of a key in a JSON object held by JSONMapSlice
|
||||
|
|
|
|||
31
vendor/github.com/go-openapi/validate/BENCHMARK.md
generated
vendored
Normal file
31
vendor/github.com/go-openapi/validate/BENCHMARK.md
generated
vendored
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
# Benchmark
|
||||
|
||||
Validating the Kubernetes Swagger API
|
||||
|
||||
## v0.22.6: 60,000,000 allocs
|
||||
```
|
||||
goos: linux
|
||||
goarch: amd64
|
||||
pkg: github.com/go-openapi/validate
|
||||
cpu: AMD Ryzen 7 5800X 8-Core Processor
|
||||
Benchmark_KubernetesSpec/validating_kubernetes_API-16 1 8549863982 ns/op 7067424936 B/op 59583275 allocs/op
|
||||
```
|
||||
|
||||
## After refact PR: minor but noticable improvements: 25,000,000 allocs
|
||||
```
|
||||
go test -bench Spec
|
||||
goos: linux
|
||||
goarch: amd64
|
||||
pkg: github.com/go-openapi/validate
|
||||
cpu: AMD Ryzen 7 5800X 8-Core Processor
|
||||
Benchmark_KubernetesSpec/validating_kubernetes_API-16 1 4064535557 ns/op 3379715592 B/op 25320330 allocs/op
|
||||
```
|
||||
|
||||
## After reduce GC pressure PR: 17,000,000 allocs
|
||||
```
|
||||
goos: linux
|
||||
goarch: amd64
|
||||
pkg: github.com/go-openapi/validate
|
||||
cpu: AMD Ryzen 7 5800X 8-Core Processor
|
||||
Benchmark_KubernetesSpec/validating_kubernetes_API-16 1 3758414145 ns/op 2593881496 B/op 17111373 allocs/op
|
||||
```
|
||||
106
vendor/github.com/go-openapi/validate/default_validator.go
generated
vendored
106
vendor/github.com/go-openapi/validate/default_validator.go
generated
vendored
|
|
@ -25,48 +25,55 @@ import (
|
|||
// According to Swagger spec, default values MUST validate their schema.
|
||||
type defaultValidator struct {
|
||||
SpecValidator *SpecValidator
|
||||
visitedSchemas map[string]bool
|
||||
visitedSchemas map[string]struct{}
|
||||
schemaOptions *SchemaValidatorOptions
|
||||
}
|
||||
|
||||
// resetVisited resets the internal state of visited schemas
|
||||
func (d *defaultValidator) resetVisited() {
|
||||
d.visitedSchemas = map[string]bool{}
|
||||
if d.visitedSchemas == nil {
|
||||
d.visitedSchemas = make(map[string]struct{})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// TODO(go1.21): clear(ex.visitedSchemas)
|
||||
for k := range d.visitedSchemas {
|
||||
delete(d.visitedSchemas, k)
|
||||
}
|
||||
}
|
||||
|
||||
func isVisited(path string, visitedSchemas map[string]bool) bool {
|
||||
found := visitedSchemas[path]
|
||||
if !found {
|
||||
// search for overlapping paths
|
||||
frags := strings.Split(path, ".")
|
||||
if len(frags) < 2 {
|
||||
// shortcut exit on smaller paths
|
||||
return found
|
||||
func isVisited(path string, visitedSchemas map[string]struct{}) bool {
|
||||
_, found := visitedSchemas[path]
|
||||
if found {
|
||||
return true
|
||||
}
|
||||
|
||||
// search for overlapping paths
|
||||
var (
|
||||
parent string
|
||||
suffix string
|
||||
)
|
||||
for i := len(path) - 2; i >= 0; i-- {
|
||||
r := path[i]
|
||||
if r != '.' {
|
||||
continue
|
||||
}
|
||||
last := len(frags) - 1
|
||||
var currentFragStr, parent string
|
||||
for i := range frags {
|
||||
if i == 0 {
|
||||
currentFragStr = frags[last]
|
||||
} else {
|
||||
currentFragStr = strings.Join([]string{frags[last-i], currentFragStr}, ".")
|
||||
}
|
||||
if i < last {
|
||||
parent = strings.Join(frags[0:last-i], ".")
|
||||
} else {
|
||||
parent = ""
|
||||
}
|
||||
if strings.HasSuffix(parent, currentFragStr) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
|
||||
parent = path[0:i]
|
||||
suffix = path[i+1:]
|
||||
|
||||
if strings.HasSuffix(parent, suffix) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return found
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// beingVisited asserts a schema is being visited
|
||||
func (d *defaultValidator) beingVisited(path string) {
|
||||
d.visitedSchemas[path] = true
|
||||
d.visitedSchemas[path] = struct{}{}
|
||||
}
|
||||
|
||||
// isVisited tells if a path has already been visited
|
||||
|
|
@ -75,8 +82,9 @@ func (d *defaultValidator) isVisited(path string) bool {
|
|||
}
|
||||
|
||||
// Validate validates the default values declared in the swagger spec
|
||||
func (d *defaultValidator) Validate() (errs *Result) {
|
||||
errs = new(Result)
|
||||
func (d *defaultValidator) Validate() *Result {
|
||||
errs := pools.poolOfResults.BorrowResult() // will redeem when merged
|
||||
|
||||
if d == nil || d.SpecValidator == nil {
|
||||
return errs
|
||||
}
|
||||
|
|
@ -89,7 +97,7 @@ func (d *defaultValidator) validateDefaultValueValidAgainstSchema() *Result {
|
|||
// every default value that is specified must validate against the schema for that property
|
||||
// headers, items, parameters, schema
|
||||
|
||||
res := new(Result)
|
||||
res := pools.poolOfResults.BorrowResult() // will redeem when merged
|
||||
s := d.SpecValidator
|
||||
|
||||
for method, pathItem := range s.expandedAnalyzer().Operations() {
|
||||
|
|
@ -107,10 +115,12 @@ func (d *defaultValidator) validateDefaultValueValidAgainstSchema() *Result {
|
|||
// default values provided must validate against their inline definition (no explicit schema)
|
||||
if param.Default != nil && param.Schema == nil {
|
||||
// check param default value is valid
|
||||
red := NewParamValidator(¶m, s.KnownFormats).Validate(param.Default) //#nosec
|
||||
red := newParamValidator(¶m, s.KnownFormats, d.schemaOptions).Validate(param.Default) //#nosec
|
||||
if red.HasErrorsOrWarnings() {
|
||||
res.AddErrors(defaultValueDoesNotValidateMsg(param.Name, param.In))
|
||||
res.Merge(red)
|
||||
} else if red.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(red)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -120,6 +130,8 @@ func (d *defaultValidator) validateDefaultValueValidAgainstSchema() *Result {
|
|||
if red.HasErrorsOrWarnings() {
|
||||
res.AddErrors(defaultValueItemsDoesNotValidateMsg(param.Name, param.In))
|
||||
res.Merge(red)
|
||||
} else if red.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(red)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -129,6 +141,8 @@ func (d *defaultValidator) validateDefaultValueValidAgainstSchema() *Result {
|
|||
if red.HasErrorsOrWarnings() {
|
||||
res.AddErrors(defaultValueDoesNotValidateMsg(param.Name, param.In))
|
||||
res.Merge(red)
|
||||
} else if red.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(red)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -154,7 +168,7 @@ func (d *defaultValidator) validateDefaultValueValidAgainstSchema() *Result {
|
|||
// reset explored schemas to get depth-first recursive-proof exploration
|
||||
d.resetVisited()
|
||||
for nm, sch := range s.spec.Spec().Definitions {
|
||||
res.Merge(d.validateDefaultValueSchemaAgainstSchema(fmt.Sprintf("definitions.%s", nm), "body", &sch)) //#nosec
|
||||
res.Merge(d.validateDefaultValueSchemaAgainstSchema("definitions."+nm, "body", &sch)) //#nosec
|
||||
}
|
||||
}
|
||||
return res
|
||||
|
|
@ -176,10 +190,12 @@ func (d *defaultValidator) validateDefaultInResponse(resp *spec.Response, respon
|
|||
d.resetVisited()
|
||||
|
||||
if h.Default != nil {
|
||||
red := NewHeaderValidator(nm, &h, s.KnownFormats).Validate(h.Default) //#nosec
|
||||
red := newHeaderValidator(nm, &h, s.KnownFormats, d.schemaOptions).Validate(h.Default) //#nosec
|
||||
if red.HasErrorsOrWarnings() {
|
||||
res.AddErrors(defaultValueHeaderDoesNotValidateMsg(operationID, nm, responseName))
|
||||
res.Merge(red)
|
||||
} else if red.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(red)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -189,6 +205,8 @@ func (d *defaultValidator) validateDefaultInResponse(resp *spec.Response, respon
|
|||
if red.HasErrorsOrWarnings() {
|
||||
res.AddErrors(defaultValueHeaderItemsDoesNotValidateMsg(operationID, nm, responseName))
|
||||
res.Merge(red)
|
||||
} else if red.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(red)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -208,6 +226,8 @@ func (d *defaultValidator) validateDefaultInResponse(resp *spec.Response, respon
|
|||
// Additional message to make sure the context of the error is not lost
|
||||
res.AddErrors(defaultValueInDoesNotValidateMsg(operationID, responseName))
|
||||
res.Merge(red)
|
||||
} else if red.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(red)
|
||||
}
|
||||
}
|
||||
return res
|
||||
|
|
@ -219,11 +239,13 @@ func (d *defaultValidator) validateDefaultValueSchemaAgainstSchema(path, in stri
|
|||
return nil
|
||||
}
|
||||
d.beingVisited(path)
|
||||
res := new(Result)
|
||||
res := pools.poolOfResults.BorrowResult()
|
||||
s := d.SpecValidator
|
||||
|
||||
if schema.Default != nil {
|
||||
res.Merge(NewSchemaValidator(schema, s.spec.Spec(), path+".default", s.KnownFormats, SwaggerSchema(true)).Validate(schema.Default))
|
||||
res.Merge(
|
||||
newSchemaValidator(schema, s.spec.Spec(), path+".default", s.KnownFormats, d.schemaOptions).Validate(schema.Default),
|
||||
)
|
||||
}
|
||||
if schema.Items != nil {
|
||||
if schema.Items.Schema != nil {
|
||||
|
|
@ -241,7 +263,7 @@ func (d *defaultValidator) validateDefaultValueSchemaAgainstSchema(path, in stri
|
|||
}
|
||||
if schema.AdditionalItems != nil && schema.AdditionalItems.Schema != nil {
|
||||
// NOTE: we keep validating values, even though additionalItems is not supported by Swagger 2.0 (and 3.0 as well)
|
||||
res.Merge(d.validateDefaultValueSchemaAgainstSchema(fmt.Sprintf("%s.additionalItems", path), in, schema.AdditionalItems.Schema))
|
||||
res.Merge(d.validateDefaultValueSchemaAgainstSchema(path+".additionalItems", in, schema.AdditionalItems.Schema))
|
||||
}
|
||||
for propName, prop := range schema.Properties {
|
||||
res.Merge(d.validateDefaultValueSchemaAgainstSchema(path+"."+propName, in, &prop)) //#nosec
|
||||
|
|
@ -250,7 +272,7 @@ func (d *defaultValidator) validateDefaultValueSchemaAgainstSchema(path, in stri
|
|||
res.Merge(d.validateDefaultValueSchemaAgainstSchema(path+"."+propName, in, &prop)) //#nosec
|
||||
}
|
||||
if schema.AdditionalProperties != nil && schema.AdditionalProperties.Schema != nil {
|
||||
res.Merge(d.validateDefaultValueSchemaAgainstSchema(fmt.Sprintf("%s.additionalProperties", path), in, schema.AdditionalProperties.Schema))
|
||||
res.Merge(d.validateDefaultValueSchemaAgainstSchema(path+".additionalProperties", in, schema.AdditionalProperties.Schema))
|
||||
}
|
||||
if schema.AllOf != nil {
|
||||
for i, aoSch := range schema.AllOf {
|
||||
|
|
@ -263,11 +285,13 @@ func (d *defaultValidator) validateDefaultValueSchemaAgainstSchema(path, in stri
|
|||
// TODO: Temporary duplicated code. Need to refactor with examples
|
||||
|
||||
func (d *defaultValidator) validateDefaultValueItemsAgainstSchema(path, in string, root interface{}, items *spec.Items) *Result {
|
||||
res := new(Result)
|
||||
res := pools.poolOfResults.BorrowResult()
|
||||
s := d.SpecValidator
|
||||
if items != nil {
|
||||
if items.Default != nil {
|
||||
res.Merge(newItemsValidator(path, in, items, root, s.KnownFormats).Validate(0, items.Default))
|
||||
res.Merge(
|
||||
newItemsValidator(path, in, items, root, s.KnownFormats, d.schemaOptions).Validate(0, items.Default),
|
||||
)
|
||||
}
|
||||
if items.Items != nil {
|
||||
res.Merge(d.validateDefaultValueItemsAgainstSchema(path+"[0].default", in, root, items.Items))
|
||||
|
|
|
|||
62
vendor/github.com/go-openapi/validate/example_validator.go
generated
vendored
62
vendor/github.com/go-openapi/validate/example_validator.go
generated
vendored
|
|
@ -23,17 +23,27 @@ import (
|
|||
// ExampleValidator validates example values defined in a spec
|
||||
type exampleValidator struct {
|
||||
SpecValidator *SpecValidator
|
||||
visitedSchemas map[string]bool
|
||||
visitedSchemas map[string]struct{}
|
||||
schemaOptions *SchemaValidatorOptions
|
||||
}
|
||||
|
||||
// resetVisited resets the internal state of visited schemas
|
||||
func (ex *exampleValidator) resetVisited() {
|
||||
ex.visitedSchemas = map[string]bool{}
|
||||
if ex.visitedSchemas == nil {
|
||||
ex.visitedSchemas = make(map[string]struct{})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// TODO(go1.21): clear(ex.visitedSchemas)
|
||||
for k := range ex.visitedSchemas {
|
||||
delete(ex.visitedSchemas, k)
|
||||
}
|
||||
}
|
||||
|
||||
// beingVisited asserts a schema is being visited
|
||||
func (ex *exampleValidator) beingVisited(path string) {
|
||||
ex.visitedSchemas[path] = true
|
||||
ex.visitedSchemas[path] = struct{}{}
|
||||
}
|
||||
|
||||
// isVisited tells if a path has already been visited
|
||||
|
|
@ -48,8 +58,9 @@ func (ex *exampleValidator) isVisited(path string) bool {
|
|||
// - schemas
|
||||
// - individual property
|
||||
// - responses
|
||||
func (ex *exampleValidator) Validate() (errs *Result) {
|
||||
errs = new(Result)
|
||||
func (ex *exampleValidator) Validate() *Result {
|
||||
errs := pools.poolOfResults.BorrowResult()
|
||||
|
||||
if ex == nil || ex.SpecValidator == nil {
|
||||
return errs
|
||||
}
|
||||
|
|
@ -64,7 +75,7 @@ func (ex *exampleValidator) validateExampleValueValidAgainstSchema() *Result {
|
|||
// in: schemas, properties, object, items
|
||||
// not in: headers, parameters without schema
|
||||
|
||||
res := new(Result)
|
||||
res := pools.poolOfResults.BorrowResult()
|
||||
s := ex.SpecValidator
|
||||
|
||||
for method, pathItem := range s.expandedAnalyzer().Operations() {
|
||||
|
|
@ -82,10 +93,12 @@ func (ex *exampleValidator) validateExampleValueValidAgainstSchema() *Result {
|
|||
// default values provided must validate against their inline definition (no explicit schema)
|
||||
if param.Example != nil && param.Schema == nil {
|
||||
// check param default value is valid
|
||||
red := NewParamValidator(¶m, s.KnownFormats).Validate(param.Example) //#nosec
|
||||
red := newParamValidator(¶m, s.KnownFormats, ex.schemaOptions).Validate(param.Example) //#nosec
|
||||
if red.HasErrorsOrWarnings() {
|
||||
res.AddWarnings(exampleValueDoesNotValidateMsg(param.Name, param.In))
|
||||
res.MergeAsWarnings(red)
|
||||
} else if red.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(red)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -95,6 +108,8 @@ func (ex *exampleValidator) validateExampleValueValidAgainstSchema() *Result {
|
|||
if red.HasErrorsOrWarnings() {
|
||||
res.AddWarnings(exampleValueItemsDoesNotValidateMsg(param.Name, param.In))
|
||||
res.Merge(red)
|
||||
} else if red.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(red)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -104,6 +119,8 @@ func (ex *exampleValidator) validateExampleValueValidAgainstSchema() *Result {
|
|||
if red.HasErrorsOrWarnings() {
|
||||
res.AddWarnings(exampleValueDoesNotValidateMsg(param.Name, param.In))
|
||||
res.Merge(red)
|
||||
} else if red.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(red)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -129,7 +146,7 @@ func (ex *exampleValidator) validateExampleValueValidAgainstSchema() *Result {
|
|||
// reset explored schemas to get depth-first recursive-proof exploration
|
||||
ex.resetVisited()
|
||||
for nm, sch := range s.spec.Spec().Definitions {
|
||||
res.Merge(ex.validateExampleValueSchemaAgainstSchema(fmt.Sprintf("definitions.%s", nm), "body", &sch)) //#nosec
|
||||
res.Merge(ex.validateExampleValueSchemaAgainstSchema("definitions."+nm, "body", &sch)) //#nosec
|
||||
}
|
||||
}
|
||||
return res
|
||||
|
|
@ -151,10 +168,12 @@ func (ex *exampleValidator) validateExampleInResponse(resp *spec.Response, respo
|
|||
ex.resetVisited()
|
||||
|
||||
if h.Example != nil {
|
||||
red := NewHeaderValidator(nm, &h, s.KnownFormats).Validate(h.Example) //#nosec
|
||||
red := newHeaderValidator(nm, &h, s.KnownFormats, ex.schemaOptions).Validate(h.Example) //#nosec
|
||||
if red.HasErrorsOrWarnings() {
|
||||
res.AddWarnings(exampleValueHeaderDoesNotValidateMsg(operationID, nm, responseName))
|
||||
res.MergeAsWarnings(red)
|
||||
} else if red.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(red)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -164,6 +183,8 @@ func (ex *exampleValidator) validateExampleInResponse(resp *spec.Response, respo
|
|||
if red.HasErrorsOrWarnings() {
|
||||
res.AddWarnings(exampleValueHeaderItemsDoesNotValidateMsg(operationID, nm, responseName))
|
||||
res.MergeAsWarnings(red)
|
||||
} else if red.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(red)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -183,13 +204,17 @@ func (ex *exampleValidator) validateExampleInResponse(resp *spec.Response, respo
|
|||
// Additional message to make sure the context of the error is not lost
|
||||
res.AddWarnings(exampleValueInDoesNotValidateMsg(operationID, responseName))
|
||||
res.Merge(red)
|
||||
} else if red.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(red)
|
||||
}
|
||||
}
|
||||
|
||||
if response.Examples != nil {
|
||||
if response.Schema != nil {
|
||||
if example, ok := response.Examples["application/json"]; ok {
|
||||
res.MergeAsWarnings(NewSchemaValidator(response.Schema, s.spec.Spec(), path+".examples", s.KnownFormats, SwaggerSchema(true)).Validate(example))
|
||||
res.MergeAsWarnings(
|
||||
newSchemaValidator(response.Schema, s.spec.Spec(), path+".examples", s.KnownFormats, s.schemaOptions).Validate(example),
|
||||
)
|
||||
} else {
|
||||
// TODO: validate other media types too
|
||||
res.AddWarnings(examplesMimeNotSupportedMsg(operationID, responseName))
|
||||
|
|
@ -208,10 +233,12 @@ func (ex *exampleValidator) validateExampleValueSchemaAgainstSchema(path, in str
|
|||
}
|
||||
ex.beingVisited(path)
|
||||
s := ex.SpecValidator
|
||||
res := new(Result)
|
||||
res := pools.poolOfResults.BorrowResult()
|
||||
|
||||
if schema.Example != nil {
|
||||
res.MergeAsWarnings(NewSchemaValidator(schema, s.spec.Spec(), path+".example", s.KnownFormats, SwaggerSchema(true)).Validate(schema.Example))
|
||||
res.MergeAsWarnings(
|
||||
newSchemaValidator(schema, s.spec.Spec(), path+".example", s.KnownFormats, ex.schemaOptions).Validate(schema.Example),
|
||||
)
|
||||
}
|
||||
if schema.Items != nil {
|
||||
if schema.Items.Schema != nil {
|
||||
|
|
@ -229,7 +256,7 @@ func (ex *exampleValidator) validateExampleValueSchemaAgainstSchema(path, in str
|
|||
}
|
||||
if schema.AdditionalItems != nil && schema.AdditionalItems.Schema != nil {
|
||||
// NOTE: we keep validating values, even though additionalItems is unsupported in Swagger 2.0 (and 3.0 as well)
|
||||
res.Merge(ex.validateExampleValueSchemaAgainstSchema(fmt.Sprintf("%s.additionalItems", path), in, schema.AdditionalItems.Schema))
|
||||
res.Merge(ex.validateExampleValueSchemaAgainstSchema(path+".additionalItems", in, schema.AdditionalItems.Schema))
|
||||
}
|
||||
for propName, prop := range schema.Properties {
|
||||
res.Merge(ex.validateExampleValueSchemaAgainstSchema(path+"."+propName, in, &prop)) //#nosec
|
||||
|
|
@ -238,7 +265,7 @@ func (ex *exampleValidator) validateExampleValueSchemaAgainstSchema(path, in str
|
|||
res.Merge(ex.validateExampleValueSchemaAgainstSchema(path+"."+propName, in, &prop)) //#nosec
|
||||
}
|
||||
if schema.AdditionalProperties != nil && schema.AdditionalProperties.Schema != nil {
|
||||
res.Merge(ex.validateExampleValueSchemaAgainstSchema(fmt.Sprintf("%s.additionalProperties", path), in, schema.AdditionalProperties.Schema))
|
||||
res.Merge(ex.validateExampleValueSchemaAgainstSchema(path+".additionalProperties", in, schema.AdditionalProperties.Schema))
|
||||
}
|
||||
if schema.AllOf != nil {
|
||||
for i, aoSch := range schema.AllOf {
|
||||
|
|
@ -252,11 +279,13 @@ func (ex *exampleValidator) validateExampleValueSchemaAgainstSchema(path, in str
|
|||
//
|
||||
|
||||
func (ex *exampleValidator) validateExampleValueItemsAgainstSchema(path, in string, root interface{}, items *spec.Items) *Result {
|
||||
res := new(Result)
|
||||
res := pools.poolOfResults.BorrowResult()
|
||||
s := ex.SpecValidator
|
||||
if items != nil {
|
||||
if items.Example != nil {
|
||||
res.MergeAsWarnings(newItemsValidator(path, in, items, root, s.KnownFormats).Validate(0, items.Example))
|
||||
res.MergeAsWarnings(
|
||||
newItemsValidator(path, in, items, root, s.KnownFormats, ex.schemaOptions).Validate(0, items.Example),
|
||||
)
|
||||
}
|
||||
if items.Items != nil {
|
||||
res.Merge(ex.validateExampleValueItemsAgainstSchema(path+"[0].example", in, root, items.Items))
|
||||
|
|
@ -265,5 +294,6 @@ func (ex *exampleValidator) validateExampleValueItemsAgainstSchema(path, in stri
|
|||
res.AddErrors(invalidPatternInMsg(path, in, items.Pattern))
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
|
|
|||
78
vendor/github.com/go-openapi/validate/formats.go
generated
vendored
78
vendor/github.com/go-openapi/validate/formats.go
generated
vendored
|
|
@ -22,10 +22,32 @@ import (
|
|||
)
|
||||
|
||||
type formatValidator struct {
|
||||
Format string
|
||||
Path string
|
||||
In string
|
||||
Format string
|
||||
KnownFormats strfmt.Registry
|
||||
Options *SchemaValidatorOptions
|
||||
}
|
||||
|
||||
func newFormatValidator(path, in, format string, formats strfmt.Registry, opts *SchemaValidatorOptions) *formatValidator {
|
||||
if opts == nil {
|
||||
opts = new(SchemaValidatorOptions)
|
||||
}
|
||||
|
||||
var f *formatValidator
|
||||
if opts.recycleValidators {
|
||||
f = pools.poolOfFormatValidators.BorrowValidator()
|
||||
} else {
|
||||
f = new(formatValidator)
|
||||
}
|
||||
|
||||
f.Path = path
|
||||
f.In = in
|
||||
f.Format = format
|
||||
f.KnownFormats = formats
|
||||
f.Options = opts
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *formatValidator) SetPath(path string) {
|
||||
|
|
@ -33,37 +55,45 @@ func (f *formatValidator) SetPath(path string) {
|
|||
}
|
||||
|
||||
func (f *formatValidator) Applies(source interface{}, kind reflect.Kind) bool {
|
||||
doit := func() bool {
|
||||
if source == nil {
|
||||
return false
|
||||
}
|
||||
switch source := source.(type) {
|
||||
case *spec.Items:
|
||||
return kind == reflect.String && f.KnownFormats.ContainsName(source.Format)
|
||||
case *spec.Parameter:
|
||||
return kind == reflect.String && f.KnownFormats.ContainsName(source.Format)
|
||||
case *spec.Schema:
|
||||
return kind == reflect.String && f.KnownFormats.ContainsName(source.Format)
|
||||
case *spec.Header:
|
||||
return kind == reflect.String && f.KnownFormats.ContainsName(source.Format)
|
||||
}
|
||||
if source == nil || f.KnownFormats == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
switch source := source.(type) {
|
||||
case *spec.Items:
|
||||
return kind == reflect.String && f.KnownFormats.ContainsName(source.Format)
|
||||
case *spec.Parameter:
|
||||
return kind == reflect.String && f.KnownFormats.ContainsName(source.Format)
|
||||
case *spec.Schema:
|
||||
return kind == reflect.String && f.KnownFormats.ContainsName(source.Format)
|
||||
case *spec.Header:
|
||||
return kind == reflect.String && f.KnownFormats.ContainsName(source.Format)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
r := doit()
|
||||
debugLog("format validator for %q applies %t for %T (kind: %v)\n", f.Path, r, source, kind)
|
||||
return r
|
||||
}
|
||||
|
||||
func (f *formatValidator) Validate(val interface{}) *Result {
|
||||
result := new(Result)
|
||||
debugLog("validating \"%v\" against format: %s", val, f.Format)
|
||||
if f.Options.recycleValidators {
|
||||
defer func() {
|
||||
f.redeem()
|
||||
}()
|
||||
}
|
||||
|
||||
var result *Result
|
||||
if f.Options.recycleResult {
|
||||
result = pools.poolOfResults.BorrowResult()
|
||||
} else {
|
||||
result = new(Result)
|
||||
}
|
||||
|
||||
if err := FormatOf(f.Path, f.In, f.Format, val.(string), f.KnownFormats); err != nil {
|
||||
result.AddErrors(err)
|
||||
}
|
||||
|
||||
if result.HasErrors() {
|
||||
return result
|
||||
}
|
||||
return nil
|
||||
return result
|
||||
}
|
||||
|
||||
func (f *formatValidator) redeem() {
|
||||
pools.poolOfFormatValidators.RedeemValidator(f)
|
||||
}
|
||||
|
|
|
|||
15
vendor/github.com/go-openapi/validate/helpers.go
generated
vendored
15
vendor/github.com/go-openapi/validate/helpers.go
generated
vendored
|
|
@ -101,9 +101,17 @@ type errorHelper struct {
|
|||
// A collection of unexported helpers for error construction
|
||||
}
|
||||
|
||||
func (h *errorHelper) sErr(err errors.Error) *Result {
|
||||
func (h *errorHelper) sErr(err errors.Error, recycle bool) *Result {
|
||||
// Builds a Result from standard errors.Error
|
||||
return &Result{Errors: []error{err}}
|
||||
var result *Result
|
||||
if recycle {
|
||||
result = pools.poolOfResults.BorrowResult()
|
||||
} else {
|
||||
result = new(Result)
|
||||
}
|
||||
result.Errors = []error{err}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (h *errorHelper) addPointerError(res *Result, err error, ref string, fromPath string) *Result {
|
||||
|
|
@ -225,7 +233,7 @@ func (h *paramHelper) safeExpandedParamsFor(path, method, operationID string, re
|
|||
operation.Parameters = resolvedParams
|
||||
|
||||
for _, ppr := range s.expandedAnalyzer().SafeParamsFor(method, path,
|
||||
func(p spec.Parameter, err error) bool {
|
||||
func(_ spec.Parameter, err error) bool {
|
||||
// since params have already been expanded, there are few causes for error
|
||||
res.AddErrors(someParametersBrokenMsg(path, method, operationID))
|
||||
// original error from analyzer
|
||||
|
|
@ -306,6 +314,7 @@ func (r *responseHelper) expandResponseRef(
|
|||
errorHelp.addPointerError(res, err, response.Ref.String(), path)
|
||||
return nil, res
|
||||
}
|
||||
|
||||
return response, res
|
||||
}
|
||||
|
||||
|
|
|
|||
480
vendor/github.com/go-openapi/validate/object_validator.go
generated
vendored
480
vendor/github.com/go-openapi/validate/object_validator.go
generated
vendored
|
|
@ -15,8 +15,8 @@
|
|||
package validate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
|
|
@ -35,62 +35,116 @@ type objectValidator struct {
|
|||
PatternProperties map[string]spec.Schema
|
||||
Root interface{}
|
||||
KnownFormats strfmt.Registry
|
||||
Options SchemaValidatorOptions
|
||||
Options *SchemaValidatorOptions
|
||||
splitPath []string
|
||||
}
|
||||
|
||||
func newObjectValidator(path, in string,
|
||||
maxProperties, minProperties *int64, required []string, properties spec.SchemaProperties,
|
||||
additionalProperties *spec.SchemaOrBool, patternProperties spec.SchemaProperties,
|
||||
root interface{}, formats strfmt.Registry, opts *SchemaValidatorOptions) *objectValidator {
|
||||
if opts == nil {
|
||||
opts = new(SchemaValidatorOptions)
|
||||
}
|
||||
|
||||
var v *objectValidator
|
||||
if opts.recycleValidators {
|
||||
v = pools.poolOfObjectValidators.BorrowValidator()
|
||||
} else {
|
||||
v = new(objectValidator)
|
||||
}
|
||||
|
||||
v.Path = path
|
||||
v.In = in
|
||||
v.MaxProperties = maxProperties
|
||||
v.MinProperties = minProperties
|
||||
v.Required = required
|
||||
v.Properties = properties
|
||||
v.AdditionalProperties = additionalProperties
|
||||
v.PatternProperties = patternProperties
|
||||
v.Root = root
|
||||
v.KnownFormats = formats
|
||||
v.Options = opts
|
||||
v.splitPath = strings.Split(v.Path, ".")
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
func (o *objectValidator) SetPath(path string) {
|
||||
o.Path = path
|
||||
o.splitPath = strings.Split(path, ".")
|
||||
}
|
||||
|
||||
func (o *objectValidator) Applies(source interface{}, kind reflect.Kind) bool {
|
||||
// TODO: this should also work for structs
|
||||
// there is a problem in the type validator where it will be unhappy about null values
|
||||
// so that requires more testing
|
||||
r := reflect.TypeOf(source) == specSchemaType && (kind == reflect.Map || kind == reflect.Struct)
|
||||
debugLog("object validator for %q applies %t for %T (kind: %v)\n", o.Path, r, source, kind)
|
||||
return r
|
||||
_, isSchema := source.(*spec.Schema)
|
||||
return isSchema && (kind == reflect.Map || kind == reflect.Struct)
|
||||
}
|
||||
|
||||
func (o *objectValidator) isProperties() bool {
|
||||
p := strings.Split(o.Path, ".")
|
||||
p := o.splitPath
|
||||
return len(p) > 1 && p[len(p)-1] == jsonProperties && p[len(p)-2] != jsonProperties
|
||||
}
|
||||
|
||||
func (o *objectValidator) isDefault() bool {
|
||||
p := strings.Split(o.Path, ".")
|
||||
p := o.splitPath
|
||||
return len(p) > 1 && p[len(p)-1] == jsonDefault && p[len(p)-2] != jsonDefault
|
||||
}
|
||||
|
||||
func (o *objectValidator) isExample() bool {
|
||||
p := strings.Split(o.Path, ".")
|
||||
p := o.splitPath
|
||||
return len(p) > 1 && (p[len(p)-1] == swaggerExample || p[len(p)-1] == swaggerExamples) && p[len(p)-2] != swaggerExample
|
||||
}
|
||||
|
||||
func (o *objectValidator) checkArrayMustHaveItems(res *Result, val map[string]interface{}) {
|
||||
// for swagger 2.0 schemas, there is an additional constraint to have array items defined explicitly.
|
||||
// with pure jsonschema draft 4, one may have arrays with undefined items (i.e. any type).
|
||||
if t, typeFound := val[jsonType]; typeFound {
|
||||
if tpe, ok := t.(string); ok && tpe == arrayType {
|
||||
if item, itemsKeyFound := val[jsonItems]; !itemsKeyFound {
|
||||
res.AddErrors(errors.Required(jsonItems, o.Path, item))
|
||||
}
|
||||
}
|
||||
if val == nil {
|
||||
return
|
||||
}
|
||||
|
||||
t, typeFound := val[jsonType]
|
||||
if !typeFound {
|
||||
return
|
||||
}
|
||||
|
||||
tpe, isString := t.(string)
|
||||
if !isString || tpe != arrayType {
|
||||
return
|
||||
}
|
||||
|
||||
item, itemsKeyFound := val[jsonItems]
|
||||
if itemsKeyFound {
|
||||
return
|
||||
}
|
||||
|
||||
res.AddErrors(errors.Required(jsonItems, o.Path, item))
|
||||
}
|
||||
|
||||
func (o *objectValidator) checkItemsMustBeTypeArray(res *Result, val map[string]interface{}) {
|
||||
if !o.isProperties() && !o.isDefault() && !o.isExample() {
|
||||
if _, itemsKeyFound := val[jsonItems]; itemsKeyFound {
|
||||
t, typeFound := val[jsonType]
|
||||
if typeFound {
|
||||
if tpe, ok := t.(string); !ok || tpe != arrayType {
|
||||
res.AddErrors(errors.InvalidType(o.Path, o.In, arrayType, nil))
|
||||
}
|
||||
} else {
|
||||
// there is no type
|
||||
res.AddErrors(errors.Required(jsonType, o.Path, t))
|
||||
}
|
||||
}
|
||||
if val == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if o.isProperties() || o.isDefault() || o.isExample() {
|
||||
return
|
||||
}
|
||||
|
||||
_, itemsKeyFound := val[jsonItems]
|
||||
if !itemsKeyFound {
|
||||
return
|
||||
}
|
||||
|
||||
t, typeFound := val[jsonType]
|
||||
if !typeFound {
|
||||
// there is no type
|
||||
res.AddErrors(errors.Required(jsonType, o.Path, t))
|
||||
}
|
||||
|
||||
if tpe, isString := t.(string); !isString || tpe != arrayType {
|
||||
res.AddErrors(errors.InvalidType(o.Path, o.In, arrayType, nil))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -104,176 +158,274 @@ func (o *objectValidator) precheck(res *Result, val map[string]interface{}) {
|
|||
}
|
||||
|
||||
func (o *objectValidator) Validate(data interface{}) *Result {
|
||||
val := data.(map[string]interface{})
|
||||
// TODO: guard against nil data
|
||||
if o.Options.recycleValidators {
|
||||
defer func() {
|
||||
o.redeem()
|
||||
}()
|
||||
}
|
||||
|
||||
var val map[string]interface{}
|
||||
if data != nil {
|
||||
var ok bool
|
||||
val, ok = data.(map[string]interface{})
|
||||
if !ok {
|
||||
return errorHelp.sErr(invalidObjectMsg(o.Path, o.In), o.Options.recycleResult)
|
||||
}
|
||||
}
|
||||
numKeys := int64(len(val))
|
||||
|
||||
if o.MinProperties != nil && numKeys < *o.MinProperties {
|
||||
return errorHelp.sErr(errors.TooFewProperties(o.Path, o.In, *o.MinProperties))
|
||||
return errorHelp.sErr(errors.TooFewProperties(o.Path, o.In, *o.MinProperties), o.Options.recycleResult)
|
||||
}
|
||||
if o.MaxProperties != nil && numKeys > *o.MaxProperties {
|
||||
return errorHelp.sErr(errors.TooManyProperties(o.Path, o.In, *o.MaxProperties))
|
||||
return errorHelp.sErr(errors.TooManyProperties(o.Path, o.In, *o.MaxProperties), o.Options.recycleResult)
|
||||
}
|
||||
|
||||
res := new(Result)
|
||||
var res *Result
|
||||
if o.Options.recycleResult {
|
||||
res = pools.poolOfResults.BorrowResult()
|
||||
} else {
|
||||
res = new(Result)
|
||||
}
|
||||
|
||||
o.precheck(res, val)
|
||||
|
||||
// check validity of field names
|
||||
if o.AdditionalProperties != nil && !o.AdditionalProperties.Allows {
|
||||
// Case: additionalProperties: false
|
||||
for k := range val {
|
||||
_, regularProperty := o.Properties[k]
|
||||
matched := false
|
||||
|
||||
for pk := range o.PatternProperties {
|
||||
if matches, _ := regexp.MatchString(pk, k); matches {
|
||||
matched = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !regularProperty && k != "$schema" && k != "id" && !matched {
|
||||
// Special properties "$schema" and "id" are ignored
|
||||
res.AddErrors(errors.PropertyNotAllowed(o.Path, o.In, k))
|
||||
|
||||
// BUG(fredbi): This section should move to a part dedicated to spec validation as
|
||||
// it will conflict with regular schemas where a property "headers" is defined.
|
||||
|
||||
//
|
||||
// Croaks a more explicit message on top of the standard one
|
||||
// on some recognized cases.
|
||||
//
|
||||
// NOTE: edge cases with invalid type assertion are simply ignored here.
|
||||
// NOTE: prefix your messages here by "IMPORTANT!" so there are not filtered
|
||||
// by higher level callers (the IMPORTANT! tag will be eventually
|
||||
// removed).
|
||||
if k == "headers" && val[k] != nil {
|
||||
// $ref is forbidden in header
|
||||
if headers, mapOk := val[k].(map[string]interface{}); mapOk {
|
||||
for headerKey, headerBody := range headers {
|
||||
if headerBody != nil {
|
||||
if headerSchema, mapOfMapOk := headerBody.(map[string]interface{}); mapOfMapOk {
|
||||
if _, found := headerSchema["$ref"]; found {
|
||||
var msg string
|
||||
if refString, stringOk := headerSchema["$ref"].(string); stringOk {
|
||||
msg = strings.Join([]string{", one may not use $ref=\":", refString, "\""}, "")
|
||||
}
|
||||
res.AddErrors(refNotAllowedInHeaderMsg(o.Path, headerKey, msg))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
case "$ref":
|
||||
if val[k] != nil {
|
||||
// TODO: check context of that ref: warn about siblings, check against invalid context
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
o.validateNoAdditionalProperties(val, res)
|
||||
} else {
|
||||
// Cases: no additionalProperties (implying: true), or additionalProperties: true, or additionalProperties: { <<schema>> }
|
||||
for key, value := range val {
|
||||
_, regularProperty := o.Properties[key]
|
||||
|
||||
// Validates property against "patternProperties" if applicable
|
||||
// BUG(fredbi): succeededOnce is always false
|
||||
|
||||
// NOTE: how about regular properties which do not match patternProperties?
|
||||
matched, succeededOnce, _ := o.validatePatternProperty(key, value, res)
|
||||
|
||||
if !(regularProperty || matched || succeededOnce) {
|
||||
|
||||
// Cases: properties which are not regular properties and have not been matched by the PatternProperties validator
|
||||
if o.AdditionalProperties != nil && o.AdditionalProperties.Schema != nil {
|
||||
// AdditionalProperties as Schema
|
||||
r := NewSchemaValidator(o.AdditionalProperties.Schema, o.Root, o.Path+"."+key, o.KnownFormats, o.Options.Options()...).Validate(value)
|
||||
res.mergeForField(data.(map[string]interface{}), key, r)
|
||||
} else if regularProperty && !(matched || succeededOnce) {
|
||||
// TODO: this is dead code since regularProperty=false here
|
||||
res.AddErrors(errors.FailedAllPatternProperties(o.Path, o.In, key))
|
||||
}
|
||||
}
|
||||
}
|
||||
// Valid cases: additionalProperties: true or undefined
|
||||
// Cases: empty additionalProperties (implying: true), or additionalProperties: true, or additionalProperties: { <<schema>> }
|
||||
o.validateAdditionalProperties(val, res)
|
||||
}
|
||||
|
||||
createdFromDefaults := map[string]bool{}
|
||||
|
||||
// Property types:
|
||||
// - regular Property
|
||||
for pName := range o.Properties {
|
||||
pSchema := o.Properties[pName] // one instance per iteration
|
||||
rName := pName
|
||||
if o.Path != "" {
|
||||
rName = o.Path + "." + pName
|
||||
}
|
||||
|
||||
// Recursively validates each property against its schema
|
||||
if v, ok := val[pName]; ok {
|
||||
r := NewSchemaValidator(&pSchema, o.Root, rName, o.KnownFormats, o.Options.Options()...).Validate(v)
|
||||
res.mergeForField(data.(map[string]interface{}), pName, r)
|
||||
} else if pSchema.Default != nil {
|
||||
// If a default value is defined, creates the property from defaults
|
||||
// NOTE: JSON schema does not enforce default values to be valid against schema. Swagger does.
|
||||
createdFromDefaults[pName] = true
|
||||
res.addPropertySchemata(data.(map[string]interface{}), pName, &pSchema)
|
||||
}
|
||||
}
|
||||
|
||||
// Check required properties
|
||||
if len(o.Required) > 0 {
|
||||
for _, k := range o.Required {
|
||||
if v, ok := val[k]; !ok && !createdFromDefaults[k] {
|
||||
res.AddErrors(errors.Required(o.Path+"."+k, o.In, v))
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
o.validatePropertiesSchema(val, res)
|
||||
|
||||
// Check patternProperties
|
||||
// TODO: it looks like we have done that twice in many cases
|
||||
for key, value := range val {
|
||||
_, regularProperty := o.Properties[key]
|
||||
matched, _ /*succeededOnce*/, patterns := o.validatePatternProperty(key, value, res)
|
||||
if !regularProperty && (matched /*|| succeededOnce*/) {
|
||||
for _, pName := range patterns {
|
||||
if v, ok := o.PatternProperties[pName]; ok {
|
||||
r := NewSchemaValidator(&v, o.Root, o.Path+"."+key, o.KnownFormats, o.Options.Options()...).Validate(value)
|
||||
res.mergeForField(data.(map[string]interface{}), key, r)
|
||||
}
|
||||
matched, _, patterns := o.validatePatternProperty(key, value, res) // applies to regular properties as well
|
||||
if regularProperty || !matched {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, pName := range patterns {
|
||||
if v, ok := o.PatternProperties[pName]; ok {
|
||||
r := newSchemaValidator(&v, o.Root, o.Path+"."+key, o.KnownFormats, o.Options).Validate(value)
|
||||
res.mergeForField(data.(map[string]interface{}), key, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func (o *objectValidator) validateNoAdditionalProperties(val map[string]interface{}, res *Result) {
|
||||
for k := range val {
|
||||
if k == "$schema" || k == "id" {
|
||||
// special properties "$schema" and "id" are ignored
|
||||
continue
|
||||
}
|
||||
|
||||
_, regularProperty := o.Properties[k]
|
||||
if regularProperty {
|
||||
continue
|
||||
}
|
||||
|
||||
matched := false
|
||||
for pk := range o.PatternProperties {
|
||||
re, err := compileRegexp(pk)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if matches := re.MatchString(k); matches {
|
||||
matched = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if matched {
|
||||
continue
|
||||
}
|
||||
|
||||
res.AddErrors(errors.PropertyNotAllowed(o.Path, o.In, k))
|
||||
|
||||
// BUG(fredbi): This section should move to a part dedicated to spec validation as
|
||||
// it will conflict with regular schemas where a property "headers" is defined.
|
||||
|
||||
//
|
||||
// Croaks a more explicit message on top of the standard one
|
||||
// on some recognized cases.
|
||||
//
|
||||
// NOTE: edge cases with invalid type assertion are simply ignored here.
|
||||
// NOTE: prefix your messages here by "IMPORTANT!" so there are not filtered
|
||||
// by higher level callers (the IMPORTANT! tag will be eventually
|
||||
// removed).
|
||||
if k != "headers" || val[k] == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// $ref is forbidden in header
|
||||
headers, mapOk := val[k].(map[string]interface{})
|
||||
if !mapOk {
|
||||
continue
|
||||
}
|
||||
|
||||
for headerKey, headerBody := range headers {
|
||||
if headerBody == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
headerSchema, mapOfMapOk := headerBody.(map[string]interface{})
|
||||
if !mapOfMapOk {
|
||||
continue
|
||||
}
|
||||
|
||||
_, found := headerSchema["$ref"]
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
|
||||
refString, stringOk := headerSchema["$ref"].(string)
|
||||
if !stringOk {
|
||||
continue
|
||||
}
|
||||
|
||||
msg := strings.Join([]string{", one may not use $ref=\":", refString, "\""}, "")
|
||||
res.AddErrors(refNotAllowedInHeaderMsg(o.Path, headerKey, msg))
|
||||
/*
|
||||
case "$ref":
|
||||
if val[k] != nil {
|
||||
// TODO: check context of that ref: warn about siblings, check against invalid context
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (o *objectValidator) validateAdditionalProperties(val map[string]interface{}, res *Result) {
|
||||
for key, value := range val {
|
||||
_, regularProperty := o.Properties[key]
|
||||
if regularProperty {
|
||||
continue
|
||||
}
|
||||
|
||||
// Validates property against "patternProperties" if applicable
|
||||
// BUG(fredbi): succeededOnce is always false
|
||||
|
||||
// NOTE: how about regular properties which do not match patternProperties?
|
||||
matched, succeededOnce, _ := o.validatePatternProperty(key, value, res)
|
||||
if matched || succeededOnce {
|
||||
continue
|
||||
}
|
||||
|
||||
if o.AdditionalProperties == nil || o.AdditionalProperties.Schema == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Cases: properties which are not regular properties and have not been matched by the PatternProperties validator
|
||||
// AdditionalProperties as Schema
|
||||
r := newSchemaValidator(o.AdditionalProperties.Schema, o.Root, o.Path+"."+key, o.KnownFormats, o.Options).Validate(value)
|
||||
res.mergeForField(val, key, r)
|
||||
}
|
||||
// Valid cases: additionalProperties: true or undefined
|
||||
}
|
||||
|
||||
func (o *objectValidator) validatePropertiesSchema(val map[string]interface{}, res *Result) {
|
||||
createdFromDefaults := map[string]struct{}{}
|
||||
|
||||
// Property types:
|
||||
// - regular Property
|
||||
pSchema := pools.poolOfSchemas.BorrowSchema() // recycle a spec.Schema object which lifespan extends only to the validation of properties
|
||||
defer func() {
|
||||
pools.poolOfSchemas.RedeemSchema(pSchema)
|
||||
}()
|
||||
|
||||
for pName := range o.Properties {
|
||||
*pSchema = o.Properties[pName]
|
||||
var rName string
|
||||
if o.Path == "" {
|
||||
rName = pName
|
||||
} else {
|
||||
rName = o.Path + "." + pName
|
||||
}
|
||||
|
||||
// Recursively validates each property against its schema
|
||||
v, ok := val[pName]
|
||||
if ok {
|
||||
r := newSchemaValidator(pSchema, o.Root, rName, o.KnownFormats, o.Options).Validate(v)
|
||||
res.mergeForField(val, pName, r)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if pSchema.Default != nil {
|
||||
// if a default value is defined, creates the property from defaults
|
||||
// NOTE: JSON schema does not enforce default values to be valid against schema. Swagger does.
|
||||
createdFromDefaults[pName] = struct{}{}
|
||||
if !o.Options.skipSchemataResult {
|
||||
res.addPropertySchemata(val, pName, pSchema) // this shallow-clones the content of the pSchema pointer
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(o.Required) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Check required properties
|
||||
for _, k := range o.Required {
|
||||
v, ok := val[k]
|
||||
if ok {
|
||||
continue
|
||||
}
|
||||
_, isCreatedFromDefaults := createdFromDefaults[k]
|
||||
if isCreatedFromDefaults {
|
||||
continue
|
||||
}
|
||||
|
||||
res.AddErrors(errors.Required(fmt.Sprintf("%s.%s", o.Path, k), o.In, v))
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: succeededOnce is not used anywhere
|
||||
func (o *objectValidator) validatePatternProperty(key string, value interface{}, result *Result) (bool, bool, []string) {
|
||||
matched := false
|
||||
succeededOnce := false
|
||||
var patterns []string
|
||||
|
||||
for k, schema := range o.PatternProperties {
|
||||
sch := schema
|
||||
if match, _ := regexp.MatchString(k, key); match {
|
||||
patterns = append(patterns, k)
|
||||
matched = true
|
||||
validator := NewSchemaValidator(&sch, o.Root, o.Path+"."+key, o.KnownFormats, o.Options.Options()...)
|
||||
|
||||
res := validator.Validate(value)
|
||||
result.Merge(res)
|
||||
}
|
||||
if len(o.PatternProperties) == 0 {
|
||||
return false, false, nil
|
||||
}
|
||||
|
||||
// BUG(fredbi): can't get to here. Should remove dead code (commented out).
|
||||
matched := false
|
||||
succeededOnce := false
|
||||
patterns := make([]string, 0, len(o.PatternProperties))
|
||||
|
||||
// if succeededOnce {
|
||||
// result.Inc()
|
||||
// }
|
||||
schema := pools.poolOfSchemas.BorrowSchema()
|
||||
defer func() {
|
||||
pools.poolOfSchemas.RedeemSchema(schema)
|
||||
}()
|
||||
|
||||
for k := range o.PatternProperties {
|
||||
re, err := compileRegexp(k)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
match := re.MatchString(key)
|
||||
if !match {
|
||||
continue
|
||||
}
|
||||
|
||||
*schema = o.PatternProperties[k]
|
||||
patterns = append(patterns, k)
|
||||
matched = true
|
||||
validator := newSchemaValidator(schema, o.Root, fmt.Sprintf("%s.%s", o.Path, key), o.KnownFormats, o.Options)
|
||||
|
||||
res := validator.Validate(value)
|
||||
result.Merge(res)
|
||||
}
|
||||
|
||||
return matched, succeededOnce, patterns
|
||||
}
|
||||
|
||||
func (o *objectValidator) redeem() {
|
||||
pools.poolOfObjectValidators.RedeemValidator(o)
|
||||
}
|
||||
|
|
|
|||
1
vendor/github.com/go-openapi/validate/options.go
generated
vendored
1
vendor/github.com/go-openapi/validate/options.go
generated
vendored
|
|
@ -31,6 +31,7 @@ type Opts struct {
|
|||
// GET:/v1/{shelve} and GET:/v1/{book}, where the IDs are "shelve/*" and
|
||||
// /"shelve/*/book/*" respectively.
|
||||
StrictPathParamUniqueness bool
|
||||
SkipSchemataResult bool
|
||||
}
|
||||
|
||||
var (
|
||||
|
|
|
|||
366
vendor/github.com/go-openapi/validate/pools.go
generated
vendored
Normal file
366
vendor/github.com/go-openapi/validate/pools.go
generated
vendored
Normal file
|
|
@ -0,0 +1,366 @@
|
|||
//go:build !validatedebug
|
||||
|
||||
package validate
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/go-openapi/spec"
|
||||
)
|
||||
|
||||
var pools allPools
|
||||
|
||||
func init() {
|
||||
resetPools()
|
||||
}
|
||||
|
||||
func resetPools() {
|
||||
// NOTE: for testing purpose, we might want to reset pools after calling Validate twice.
|
||||
// The pool is corrupted in that case: calling Put twice inserts a duplicate in the pool
|
||||
// and further calls to Get are mishandled.
|
||||
|
||||
pools = allPools{
|
||||
poolOfSchemaValidators: schemaValidatorsPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
s := &SchemaValidator{}
|
||||
|
||||
return s
|
||||
},
|
||||
},
|
||||
},
|
||||
poolOfObjectValidators: objectValidatorsPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
s := &objectValidator{}
|
||||
|
||||
return s
|
||||
},
|
||||
},
|
||||
},
|
||||
poolOfSliceValidators: sliceValidatorsPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
s := &schemaSliceValidator{}
|
||||
|
||||
return s
|
||||
},
|
||||
},
|
||||
},
|
||||
poolOfItemsValidators: itemsValidatorsPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
s := &itemsValidator{}
|
||||
|
||||
return s
|
||||
},
|
||||
},
|
||||
},
|
||||
poolOfBasicCommonValidators: basicCommonValidatorsPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
s := &basicCommonValidator{}
|
||||
|
||||
return s
|
||||
},
|
||||
},
|
||||
},
|
||||
poolOfHeaderValidators: headerValidatorsPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
s := &HeaderValidator{}
|
||||
|
||||
return s
|
||||
},
|
||||
},
|
||||
},
|
||||
poolOfParamValidators: paramValidatorsPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
s := &ParamValidator{}
|
||||
|
||||
return s
|
||||
},
|
||||
},
|
||||
},
|
||||
poolOfBasicSliceValidators: basicSliceValidatorsPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
s := &basicSliceValidator{}
|
||||
|
||||
return s
|
||||
},
|
||||
},
|
||||
},
|
||||
poolOfNumberValidators: numberValidatorsPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
s := &numberValidator{}
|
||||
|
||||
return s
|
||||
},
|
||||
},
|
||||
},
|
||||
poolOfStringValidators: stringValidatorsPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
s := &stringValidator{}
|
||||
|
||||
return s
|
||||
},
|
||||
},
|
||||
},
|
||||
poolOfSchemaPropsValidators: schemaPropsValidatorsPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
s := &schemaPropsValidator{}
|
||||
|
||||
return s
|
||||
},
|
||||
},
|
||||
},
|
||||
poolOfFormatValidators: formatValidatorsPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
s := &formatValidator{}
|
||||
|
||||
return s
|
||||
},
|
||||
},
|
||||
},
|
||||
poolOfTypeValidators: typeValidatorsPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
s := &typeValidator{}
|
||||
|
||||
return s
|
||||
},
|
||||
},
|
||||
},
|
||||
poolOfSchemas: schemasPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
s := &spec.Schema{}
|
||||
|
||||
return s
|
||||
},
|
||||
},
|
||||
},
|
||||
poolOfResults: resultsPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
s := &Result{}
|
||||
|
||||
return s
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type (
|
||||
allPools struct {
|
||||
// memory pools for all validator objects.
|
||||
//
|
||||
// Each pool can be borrowed from and redeemed to.
|
||||
poolOfSchemaValidators schemaValidatorsPool
|
||||
poolOfObjectValidators objectValidatorsPool
|
||||
poolOfSliceValidators sliceValidatorsPool
|
||||
poolOfItemsValidators itemsValidatorsPool
|
||||
poolOfBasicCommonValidators basicCommonValidatorsPool
|
||||
poolOfHeaderValidators headerValidatorsPool
|
||||
poolOfParamValidators paramValidatorsPool
|
||||
poolOfBasicSliceValidators basicSliceValidatorsPool
|
||||
poolOfNumberValidators numberValidatorsPool
|
||||
poolOfStringValidators stringValidatorsPool
|
||||
poolOfSchemaPropsValidators schemaPropsValidatorsPool
|
||||
poolOfFormatValidators formatValidatorsPool
|
||||
poolOfTypeValidators typeValidatorsPool
|
||||
poolOfSchemas schemasPool
|
||||
poolOfResults resultsPool
|
||||
}
|
||||
|
||||
schemaValidatorsPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
|
||||
objectValidatorsPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
|
||||
sliceValidatorsPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
|
||||
itemsValidatorsPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
|
||||
basicCommonValidatorsPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
|
||||
headerValidatorsPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
|
||||
paramValidatorsPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
|
||||
basicSliceValidatorsPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
|
||||
numberValidatorsPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
|
||||
stringValidatorsPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
|
||||
schemaPropsValidatorsPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
|
||||
formatValidatorsPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
|
||||
typeValidatorsPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
|
||||
schemasPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
|
||||
resultsPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
)
|
||||
|
||||
func (p schemaValidatorsPool) BorrowValidator() *SchemaValidator {
|
||||
return p.Get().(*SchemaValidator)
|
||||
}
|
||||
|
||||
func (p schemaValidatorsPool) RedeemValidator(s *SchemaValidator) {
|
||||
// NOTE: s might be nil. In that case, Put is a noop.
|
||||
p.Put(s)
|
||||
}
|
||||
|
||||
func (p objectValidatorsPool) BorrowValidator() *objectValidator {
|
||||
return p.Get().(*objectValidator)
|
||||
}
|
||||
|
||||
func (p objectValidatorsPool) RedeemValidator(s *objectValidator) {
|
||||
p.Put(s)
|
||||
}
|
||||
|
||||
func (p sliceValidatorsPool) BorrowValidator() *schemaSliceValidator {
|
||||
return p.Get().(*schemaSliceValidator)
|
||||
}
|
||||
|
||||
func (p sliceValidatorsPool) RedeemValidator(s *schemaSliceValidator) {
|
||||
p.Put(s)
|
||||
}
|
||||
|
||||
func (p itemsValidatorsPool) BorrowValidator() *itemsValidator {
|
||||
return p.Get().(*itemsValidator)
|
||||
}
|
||||
|
||||
func (p itemsValidatorsPool) RedeemValidator(s *itemsValidator) {
|
||||
p.Put(s)
|
||||
}
|
||||
|
||||
func (p basicCommonValidatorsPool) BorrowValidator() *basicCommonValidator {
|
||||
return p.Get().(*basicCommonValidator)
|
||||
}
|
||||
|
||||
func (p basicCommonValidatorsPool) RedeemValidator(s *basicCommonValidator) {
|
||||
p.Put(s)
|
||||
}
|
||||
|
||||
func (p headerValidatorsPool) BorrowValidator() *HeaderValidator {
|
||||
return p.Get().(*HeaderValidator)
|
||||
}
|
||||
|
||||
func (p headerValidatorsPool) RedeemValidator(s *HeaderValidator) {
|
||||
p.Put(s)
|
||||
}
|
||||
|
||||
func (p paramValidatorsPool) BorrowValidator() *ParamValidator {
|
||||
return p.Get().(*ParamValidator)
|
||||
}
|
||||
|
||||
func (p paramValidatorsPool) RedeemValidator(s *ParamValidator) {
|
||||
p.Put(s)
|
||||
}
|
||||
|
||||
func (p basicSliceValidatorsPool) BorrowValidator() *basicSliceValidator {
|
||||
return p.Get().(*basicSliceValidator)
|
||||
}
|
||||
|
||||
func (p basicSliceValidatorsPool) RedeemValidator(s *basicSliceValidator) {
|
||||
p.Put(s)
|
||||
}
|
||||
|
||||
func (p numberValidatorsPool) BorrowValidator() *numberValidator {
|
||||
return p.Get().(*numberValidator)
|
||||
}
|
||||
|
||||
func (p numberValidatorsPool) RedeemValidator(s *numberValidator) {
|
||||
p.Put(s)
|
||||
}
|
||||
|
||||
func (p stringValidatorsPool) BorrowValidator() *stringValidator {
|
||||
return p.Get().(*stringValidator)
|
||||
}
|
||||
|
||||
func (p stringValidatorsPool) RedeemValidator(s *stringValidator) {
|
||||
p.Put(s)
|
||||
}
|
||||
|
||||
func (p schemaPropsValidatorsPool) BorrowValidator() *schemaPropsValidator {
|
||||
return p.Get().(*schemaPropsValidator)
|
||||
}
|
||||
|
||||
func (p schemaPropsValidatorsPool) RedeemValidator(s *schemaPropsValidator) {
|
||||
p.Put(s)
|
||||
}
|
||||
|
||||
func (p formatValidatorsPool) BorrowValidator() *formatValidator {
|
||||
return p.Get().(*formatValidator)
|
||||
}
|
||||
|
||||
func (p formatValidatorsPool) RedeemValidator(s *formatValidator) {
|
||||
p.Put(s)
|
||||
}
|
||||
|
||||
func (p typeValidatorsPool) BorrowValidator() *typeValidator {
|
||||
return p.Get().(*typeValidator)
|
||||
}
|
||||
|
||||
func (p typeValidatorsPool) RedeemValidator(s *typeValidator) {
|
||||
p.Put(s)
|
||||
}
|
||||
|
||||
func (p schemasPool) BorrowSchema() *spec.Schema {
|
||||
return p.Get().(*spec.Schema)
|
||||
}
|
||||
|
||||
func (p schemasPool) RedeemSchema(s *spec.Schema) {
|
||||
p.Put(s)
|
||||
}
|
||||
|
||||
func (p resultsPool) BorrowResult() *Result {
|
||||
return p.Get().(*Result).cleared()
|
||||
}
|
||||
|
||||
func (p resultsPool) RedeemResult(s *Result) {
|
||||
if s == emptyResult {
|
||||
return
|
||||
}
|
||||
p.Put(s)
|
||||
}
|
||||
1012
vendor/github.com/go-openapi/validate/pools_debug.go
generated
vendored
Normal file
1012
vendor/github.com/go-openapi/validate/pools_debug.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
123
vendor/github.com/go-openapi/validate/result.go
generated
vendored
123
vendor/github.com/go-openapi/validate/result.go
generated
vendored
|
|
@ -15,7 +15,7 @@
|
|||
package validate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
stderrors "errors"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
|
|
@ -23,6 +23,8 @@ import (
|
|||
"github.com/go-openapi/spec"
|
||||
)
|
||||
|
||||
var emptyResult = &Result{MatchCount: 1}
|
||||
|
||||
// Result represents a validation result set, composed of
|
||||
// errors and warnings.
|
||||
//
|
||||
|
|
@ -50,8 +52,10 @@ type Result struct {
|
|||
// Schemata for slice items
|
||||
itemSchemata []itemSchemata
|
||||
|
||||
cachedFieldSchemta map[FieldKey][]*spec.Schema
|
||||
cachedItemSchemata map[ItemKey][]*spec.Schema
|
||||
cachedFieldSchemata map[FieldKey][]*spec.Schema
|
||||
cachedItemSchemata map[ItemKey][]*spec.Schema
|
||||
|
||||
wantsRedeemOnMerge bool
|
||||
}
|
||||
|
||||
// FieldKey is a pair of an object and a field, usable as a key for a map.
|
||||
|
|
@ -116,6 +120,9 @@ func (r *Result) Merge(others ...*Result) *Result {
|
|||
}
|
||||
r.mergeWithoutRootSchemata(other)
|
||||
r.rootObjectSchemata.Append(other.rootObjectSchemata)
|
||||
if other.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(other)
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
|
@ -133,8 +140,8 @@ func (r *Result) RootObjectSchemata() []*spec.Schema {
|
|||
|
||||
// FieldSchemata returns the schemata which apply to fields in objects.
|
||||
func (r *Result) FieldSchemata() map[FieldKey][]*spec.Schema {
|
||||
if r.cachedFieldSchemta != nil {
|
||||
return r.cachedFieldSchemta
|
||||
if r.cachedFieldSchemata != nil {
|
||||
return r.cachedFieldSchemata
|
||||
}
|
||||
|
||||
ret := make(map[FieldKey][]*spec.Schema, len(r.fieldSchemata))
|
||||
|
|
@ -146,7 +153,8 @@ func (r *Result) FieldSchemata() map[FieldKey][]*spec.Schema {
|
|||
ret[key] = append(ret[key], fs.schemata.multiple...)
|
||||
}
|
||||
}
|
||||
r.cachedFieldSchemta = ret
|
||||
r.cachedFieldSchemata = ret
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
|
|
@ -170,7 +178,7 @@ func (r *Result) ItemSchemata() map[ItemKey][]*spec.Schema {
|
|||
}
|
||||
|
||||
func (r *Result) resetCaches() {
|
||||
r.cachedFieldSchemta = nil
|
||||
r.cachedFieldSchemata = nil
|
||||
r.cachedItemSchemata = nil
|
||||
}
|
||||
|
||||
|
|
@ -187,12 +195,16 @@ func (r *Result) mergeForField(obj map[string]interface{}, field string, other *
|
|||
if r.fieldSchemata == nil {
|
||||
r.fieldSchemata = make([]fieldSchemata, len(obj))
|
||||
}
|
||||
// clone other schemata, as other is about to be redeemed to the pool
|
||||
r.fieldSchemata = append(r.fieldSchemata, fieldSchemata{
|
||||
obj: obj,
|
||||
field: field,
|
||||
schemata: other.rootObjectSchemata,
|
||||
schemata: other.rootObjectSchemata.Clone(),
|
||||
})
|
||||
}
|
||||
if other.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(other)
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
|
@ -210,29 +222,38 @@ func (r *Result) mergeForSlice(slice reflect.Value, i int, other *Result) *Resul
|
|||
if r.itemSchemata == nil {
|
||||
r.itemSchemata = make([]itemSchemata, slice.Len())
|
||||
}
|
||||
// clone other schemata, as other is about to be redeemed to the pool
|
||||
r.itemSchemata = append(r.itemSchemata, itemSchemata{
|
||||
slice: slice,
|
||||
index: i,
|
||||
schemata: other.rootObjectSchemata,
|
||||
schemata: other.rootObjectSchemata.Clone(),
|
||||
})
|
||||
}
|
||||
|
||||
if other.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(other)
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// addRootObjectSchemata adds the given schemata for the root object of the result.
|
||||
// The slice schemata might be reused. I.e. do not modify it after being added to a result.
|
||||
//
|
||||
// Since the slice schemata might be reused, it is shallow-cloned before saving it into the result.
|
||||
func (r *Result) addRootObjectSchemata(s *spec.Schema) {
|
||||
r.rootObjectSchemata.Append(schemata{one: s})
|
||||
clone := *s
|
||||
r.rootObjectSchemata.Append(schemata{one: &clone})
|
||||
}
|
||||
|
||||
// addPropertySchemata adds the given schemata for the object and field.
|
||||
// The slice schemata might be reused. I.e. do not modify it after being added to a result.
|
||||
//
|
||||
// Since the slice schemata might be reused, it is shallow-cloned before saving it into the result.
|
||||
func (r *Result) addPropertySchemata(obj map[string]interface{}, fld string, schema *spec.Schema) {
|
||||
if r.fieldSchemata == nil {
|
||||
r.fieldSchemata = make([]fieldSchemata, 0, len(obj))
|
||||
}
|
||||
r.fieldSchemata = append(r.fieldSchemata, fieldSchemata{obj: obj, field: fld, schemata: schemata{one: schema}})
|
||||
clone := *schema
|
||||
r.fieldSchemata = append(r.fieldSchemata, fieldSchemata{obj: obj, field: fld, schemata: schemata{one: &clone}})
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -255,17 +276,21 @@ func (r *Result) mergeWithoutRootSchemata(other *Result) {
|
|||
|
||||
if other.fieldSchemata != nil {
|
||||
if r.fieldSchemata == nil {
|
||||
r.fieldSchemata = other.fieldSchemata
|
||||
} else {
|
||||
r.fieldSchemata = append(r.fieldSchemata, other.fieldSchemata...)
|
||||
r.fieldSchemata = make([]fieldSchemata, 0, len(other.fieldSchemata))
|
||||
}
|
||||
for _, field := range other.fieldSchemata {
|
||||
field.schemata = field.schemata.Clone()
|
||||
r.fieldSchemata = append(r.fieldSchemata, field)
|
||||
}
|
||||
}
|
||||
|
||||
if other.itemSchemata != nil {
|
||||
if r.itemSchemata == nil {
|
||||
r.itemSchemata = other.itemSchemata
|
||||
} else {
|
||||
r.itemSchemata = append(r.itemSchemata, other.itemSchemata...)
|
||||
r.itemSchemata = make([]itemSchemata, 0, len(other.itemSchemata))
|
||||
}
|
||||
for _, field := range other.itemSchemata {
|
||||
field.schemata = field.schemata.Clone()
|
||||
r.itemSchemata = append(r.itemSchemata, field)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -280,6 +305,9 @@ func (r *Result) MergeAsErrors(others ...*Result) *Result {
|
|||
r.AddErrors(other.Errors...)
|
||||
r.AddErrors(other.Warnings...)
|
||||
r.MatchCount += other.MatchCount
|
||||
if other.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(other)
|
||||
}
|
||||
}
|
||||
}
|
||||
return r
|
||||
|
|
@ -295,6 +323,9 @@ func (r *Result) MergeAsWarnings(others ...*Result) *Result {
|
|||
r.AddWarnings(other.Errors...)
|
||||
r.AddWarnings(other.Warnings...)
|
||||
r.MatchCount += other.MatchCount
|
||||
if other.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(other)
|
||||
}
|
||||
}
|
||||
}
|
||||
return r
|
||||
|
|
@ -356,16 +387,21 @@ func (r *Result) keepRelevantErrors() *Result {
|
|||
strippedErrors := []error{}
|
||||
for _, e := range r.Errors {
|
||||
if strings.HasPrefix(e.Error(), "IMPORTANT!") {
|
||||
strippedErrors = append(strippedErrors, fmt.Errorf(strings.TrimPrefix(e.Error(), "IMPORTANT!")))
|
||||
strippedErrors = append(strippedErrors, stderrors.New(strings.TrimPrefix(e.Error(), "IMPORTANT!")))
|
||||
}
|
||||
}
|
||||
strippedWarnings := []error{}
|
||||
for _, e := range r.Warnings {
|
||||
if strings.HasPrefix(e.Error(), "IMPORTANT!") {
|
||||
strippedWarnings = append(strippedWarnings, fmt.Errorf(strings.TrimPrefix(e.Error(), "IMPORTANT!")))
|
||||
strippedWarnings = append(strippedWarnings, stderrors.New(strings.TrimPrefix(e.Error(), "IMPORTANT!")))
|
||||
}
|
||||
}
|
||||
strippedResult := new(Result)
|
||||
var strippedResult *Result
|
||||
if r.wantsRedeemOnMerge {
|
||||
strippedResult = pools.poolOfResults.BorrowResult()
|
||||
} else {
|
||||
strippedResult = new(Result)
|
||||
}
|
||||
strippedResult.Errors = strippedErrors
|
||||
strippedResult.Warnings = strippedWarnings
|
||||
return strippedResult
|
||||
|
|
@ -427,6 +463,27 @@ func (r *Result) AsError() error {
|
|||
return errors.CompositeValidationError(r.Errors...)
|
||||
}
|
||||
|
||||
func (r *Result) cleared() *Result {
|
||||
// clear the Result to be reusable. Keep allocated capacity.
|
||||
r.Errors = r.Errors[:0]
|
||||
r.Warnings = r.Warnings[:0]
|
||||
r.MatchCount = 0
|
||||
r.data = nil
|
||||
r.rootObjectSchemata.one = nil
|
||||
r.rootObjectSchemata.multiple = r.rootObjectSchemata.multiple[:0]
|
||||
r.fieldSchemata = r.fieldSchemata[:0]
|
||||
r.itemSchemata = r.itemSchemata[:0]
|
||||
for k := range r.cachedFieldSchemata {
|
||||
delete(r.cachedFieldSchemata, k)
|
||||
}
|
||||
for k := range r.cachedItemSchemata {
|
||||
delete(r.cachedItemSchemata, k)
|
||||
}
|
||||
r.wantsRedeemOnMerge = true // mark this result as eligible for redeem when merged into another
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// schemata is an arbitrary number of schemata. It does a distinction between zero,
|
||||
// one and many schemata to avoid slice allocations.
|
||||
type schemata struct {
|
||||
|
|
@ -453,7 +510,7 @@ func (s *schemata) Slice() []*spec.Schema {
|
|||
return s.multiple
|
||||
}
|
||||
|
||||
// appendSchemata appends the schemata in other to s. It mutated s in-place.
|
||||
// appendSchemata appends the schemata in other to s. It mutates s in-place.
|
||||
func (s *schemata) Append(other schemata) {
|
||||
if other.one == nil && len(other.multiple) == 0 {
|
||||
return
|
||||
|
|
@ -484,3 +541,23 @@ func (s *schemata) Append(other schemata) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s schemata) Clone() schemata {
|
||||
var clone schemata
|
||||
|
||||
if s.one != nil {
|
||||
clone.one = new(spec.Schema)
|
||||
*clone.one = *s.one
|
||||
}
|
||||
|
||||
if len(s.multiple) > 0 {
|
||||
clone.multiple = make([]*spec.Schema, len(s.multiple))
|
||||
for idx := 0; idx < len(s.multiple); idx++ {
|
||||
sp := new(spec.Schema)
|
||||
*sp = *s.multiple[idx]
|
||||
clone.multiple[idx] = sp
|
||||
}
|
||||
}
|
||||
|
||||
return clone
|
||||
}
|
||||
|
|
|
|||
258
vendor/github.com/go-openapi/validate/schema.go
generated
vendored
258
vendor/github.com/go-openapi/validate/schema.go
generated
vendored
|
|
@ -24,32 +24,32 @@ import (
|
|||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
var (
|
||||
specSchemaType = reflect.TypeOf(&spec.Schema{})
|
||||
specParameterType = reflect.TypeOf(&spec.Parameter{})
|
||||
specHeaderType = reflect.TypeOf(&spec.Header{})
|
||||
// specItemsType = reflect.TypeOf(&spec.Items{})
|
||||
)
|
||||
|
||||
// SchemaValidator validates data against a JSON schema
|
||||
type SchemaValidator struct {
|
||||
Path string
|
||||
in string
|
||||
Schema *spec.Schema
|
||||
validators []valueValidator
|
||||
validators [8]valueValidator
|
||||
Root interface{}
|
||||
KnownFormats strfmt.Registry
|
||||
Options SchemaValidatorOptions
|
||||
Options *SchemaValidatorOptions
|
||||
}
|
||||
|
||||
// AgainstSchema validates the specified data against the provided schema, using a registry of supported formats.
|
||||
//
|
||||
// When no pre-parsed *spec.Schema structure is provided, it uses a JSON schema as default. See example.
|
||||
func AgainstSchema(schema *spec.Schema, data interface{}, formats strfmt.Registry, options ...Option) error {
|
||||
res := NewSchemaValidator(schema, nil, "", formats, options...).Validate(data)
|
||||
res := NewSchemaValidator(schema, nil, "", formats,
|
||||
append(options, WithRecycleValidators(true), withRecycleResults(true))...,
|
||||
).Validate(data)
|
||||
defer func() {
|
||||
pools.poolOfResults.RedeemResult(res)
|
||||
}()
|
||||
|
||||
if res.HasErrors() {
|
||||
return errors.CompositeValidationError(res.Errors...)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -57,6 +57,15 @@ func AgainstSchema(schema *spec.Schema, data interface{}, formats strfmt.Registr
|
|||
//
|
||||
// Panics if the provided schema is invalid.
|
||||
func NewSchemaValidator(schema *spec.Schema, rootSchema interface{}, root string, formats strfmt.Registry, options ...Option) *SchemaValidator {
|
||||
opts := new(SchemaValidatorOptions)
|
||||
for _, o := range options {
|
||||
o(opts)
|
||||
}
|
||||
|
||||
return newSchemaValidator(schema, rootSchema, root, formats, opts)
|
||||
}
|
||||
|
||||
func newSchemaValidator(schema *spec.Schema, rootSchema interface{}, root string, formats strfmt.Registry, opts *SchemaValidatorOptions) *SchemaValidator {
|
||||
if schema == nil {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -72,17 +81,26 @@ func NewSchemaValidator(schema *spec.Schema, rootSchema interface{}, root string
|
|||
panic(msg)
|
||||
}
|
||||
}
|
||||
s := SchemaValidator{
|
||||
Path: root,
|
||||
in: "body",
|
||||
Schema: schema,
|
||||
Root: rootSchema,
|
||||
KnownFormats: formats,
|
||||
Options: SchemaValidatorOptions{}}
|
||||
for _, o := range options {
|
||||
o(&s.Options)
|
||||
|
||||
if opts == nil {
|
||||
opts = new(SchemaValidatorOptions)
|
||||
}
|
||||
s.validators = []valueValidator{
|
||||
|
||||
var s *SchemaValidator
|
||||
if opts.recycleValidators {
|
||||
s = pools.poolOfSchemaValidators.BorrowValidator()
|
||||
} else {
|
||||
s = new(SchemaValidator)
|
||||
}
|
||||
|
||||
s.Path = root
|
||||
s.in = "body"
|
||||
s.Schema = schema
|
||||
s.Root = rootSchema
|
||||
s.Options = opts
|
||||
s.KnownFormats = formats
|
||||
|
||||
s.validators = [8]valueValidator{
|
||||
s.typeValidator(),
|
||||
s.schemaPropsValidator(),
|
||||
s.stringValidator(),
|
||||
|
|
@ -92,7 +110,8 @@ func NewSchemaValidator(schema *spec.Schema, rootSchema interface{}, root string
|
|||
s.commonValidator(),
|
||||
s.objectValidator(),
|
||||
}
|
||||
return &s
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// SetPath sets the path for this schema valdiator
|
||||
|
|
@ -108,17 +127,39 @@ func (s *SchemaValidator) Applies(source interface{}, _ reflect.Kind) bool {
|
|||
|
||||
// Validate validates the data against the schema
|
||||
func (s *SchemaValidator) Validate(data interface{}) *Result {
|
||||
result := &Result{data: data}
|
||||
if s == nil {
|
||||
return result
|
||||
return emptyResult
|
||||
}
|
||||
if s.Schema != nil {
|
||||
|
||||
if s.Options.recycleValidators {
|
||||
defer func() {
|
||||
s.redeemChildren()
|
||||
s.redeem() // one-time use validator
|
||||
}()
|
||||
}
|
||||
|
||||
var result *Result
|
||||
if s.Options.recycleResult {
|
||||
result = pools.poolOfResults.BorrowResult()
|
||||
result.data = data
|
||||
} else {
|
||||
result = &Result{data: data}
|
||||
}
|
||||
|
||||
if s.Schema != nil && !s.Options.skipSchemataResult {
|
||||
result.addRootObjectSchemata(s.Schema)
|
||||
}
|
||||
|
||||
if data == nil {
|
||||
// early exit with minimal validation
|
||||
result.Merge(s.validators[0].Validate(data)) // type validator
|
||||
result.Merge(s.validators[6].Validate(data)) // common validator
|
||||
|
||||
if s.Options.recycleValidators {
|
||||
s.validators[0] = nil
|
||||
s.validators[6] = nil
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
|
|
@ -147,6 +188,7 @@ func (s *SchemaValidator) Validate(data interface{}) *Result {
|
|||
if erri != nil {
|
||||
result.AddErrors(invalidTypeConversionMsg(s.Path, erri))
|
||||
result.Inc()
|
||||
|
||||
return result
|
||||
}
|
||||
d = in
|
||||
|
|
@ -155,6 +197,7 @@ func (s *SchemaValidator) Validate(data interface{}) *Result {
|
|||
if errf != nil {
|
||||
result.AddErrors(invalidTypeConversionMsg(s.Path, errf))
|
||||
result.Inc()
|
||||
|
||||
return result
|
||||
}
|
||||
d = nf
|
||||
|
|
@ -164,14 +207,26 @@ func (s *SchemaValidator) Validate(data interface{}) *Result {
|
|||
kind = tpe.Kind()
|
||||
}
|
||||
|
||||
for _, v := range s.validators {
|
||||
for idx, v := range s.validators {
|
||||
if !v.Applies(s.Schema, kind) {
|
||||
debugLog("%T does not apply for %v", v, kind)
|
||||
if s.Options.recycleValidators {
|
||||
// Validate won't be called, so relinquish this validator
|
||||
if redeemableChildren, ok := v.(interface{ redeemChildren() }); ok {
|
||||
redeemableChildren.redeemChildren()
|
||||
}
|
||||
if redeemable, ok := v.(interface{ redeem() }); ok {
|
||||
redeemable.redeem()
|
||||
}
|
||||
s.validators[idx] = nil // prevents further (unsafe) usage
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
err := v.Validate(d)
|
||||
result.Merge(err)
|
||||
result.Merge(v.Validate(d))
|
||||
if s.Options.recycleValidators {
|
||||
s.validators[idx] = nil // prevents further (unsafe) usage
|
||||
}
|
||||
result.Inc()
|
||||
}
|
||||
result.Inc()
|
||||
|
|
@ -180,81 +235,120 @@ func (s *SchemaValidator) Validate(data interface{}) *Result {
|
|||
}
|
||||
|
||||
func (s *SchemaValidator) typeValidator() valueValidator {
|
||||
return &typeValidator{Type: s.Schema.Type, Nullable: s.Schema.Nullable, Format: s.Schema.Format, In: s.in, Path: s.Path}
|
||||
return newTypeValidator(
|
||||
s.Path,
|
||||
s.in,
|
||||
s.Schema.Type,
|
||||
s.Schema.Nullable,
|
||||
s.Schema.Format,
|
||||
s.Options,
|
||||
)
|
||||
}
|
||||
|
||||
func (s *SchemaValidator) commonValidator() valueValidator {
|
||||
return &basicCommonValidator{
|
||||
Path: s.Path,
|
||||
In: s.in,
|
||||
Enum: s.Schema.Enum,
|
||||
}
|
||||
return newBasicCommonValidator(
|
||||
s.Path,
|
||||
s.in,
|
||||
s.Schema.Default,
|
||||
s.Schema.Enum,
|
||||
s.Options,
|
||||
)
|
||||
}
|
||||
|
||||
func (s *SchemaValidator) sliceValidator() valueValidator {
|
||||
return &schemaSliceValidator{
|
||||
Path: s.Path,
|
||||
In: s.in,
|
||||
MaxItems: s.Schema.MaxItems,
|
||||
MinItems: s.Schema.MinItems,
|
||||
UniqueItems: s.Schema.UniqueItems,
|
||||
AdditionalItems: s.Schema.AdditionalItems,
|
||||
Items: s.Schema.Items,
|
||||
Root: s.Root,
|
||||
KnownFormats: s.KnownFormats,
|
||||
Options: s.Options,
|
||||
}
|
||||
return newSliceValidator(
|
||||
s.Path,
|
||||
s.in,
|
||||
s.Schema.MaxItems,
|
||||
s.Schema.MinItems,
|
||||
s.Schema.UniqueItems,
|
||||
s.Schema.AdditionalItems,
|
||||
s.Schema.Items,
|
||||
s.Root,
|
||||
s.KnownFormats,
|
||||
s.Options,
|
||||
)
|
||||
}
|
||||
|
||||
func (s *SchemaValidator) numberValidator() valueValidator {
|
||||
return &numberValidator{
|
||||
Path: s.Path,
|
||||
In: s.in,
|
||||
Default: s.Schema.Default,
|
||||
MultipleOf: s.Schema.MultipleOf,
|
||||
Maximum: s.Schema.Maximum,
|
||||
ExclusiveMaximum: s.Schema.ExclusiveMaximum,
|
||||
Minimum: s.Schema.Minimum,
|
||||
ExclusiveMinimum: s.Schema.ExclusiveMinimum,
|
||||
}
|
||||
return newNumberValidator(
|
||||
s.Path,
|
||||
s.in,
|
||||
s.Schema.Default,
|
||||
s.Schema.MultipleOf,
|
||||
s.Schema.Maximum,
|
||||
s.Schema.ExclusiveMaximum,
|
||||
s.Schema.Minimum,
|
||||
s.Schema.ExclusiveMinimum,
|
||||
"",
|
||||
"",
|
||||
s.Options,
|
||||
)
|
||||
}
|
||||
|
||||
func (s *SchemaValidator) stringValidator() valueValidator {
|
||||
return &stringValidator{
|
||||
Path: s.Path,
|
||||
In: s.in,
|
||||
MaxLength: s.Schema.MaxLength,
|
||||
MinLength: s.Schema.MinLength,
|
||||
Pattern: s.Schema.Pattern,
|
||||
}
|
||||
return newStringValidator(
|
||||
s.Path,
|
||||
s.in,
|
||||
nil,
|
||||
false,
|
||||
false,
|
||||
s.Schema.MaxLength,
|
||||
s.Schema.MinLength,
|
||||
s.Schema.Pattern,
|
||||
s.Options,
|
||||
)
|
||||
}
|
||||
|
||||
func (s *SchemaValidator) formatValidator() valueValidator {
|
||||
return &formatValidator{
|
||||
Path: s.Path,
|
||||
In: s.in,
|
||||
Format: s.Schema.Format,
|
||||
KnownFormats: s.KnownFormats,
|
||||
}
|
||||
return newFormatValidator(
|
||||
s.Path,
|
||||
s.in,
|
||||
s.Schema.Format,
|
||||
s.KnownFormats,
|
||||
s.Options,
|
||||
)
|
||||
}
|
||||
|
||||
func (s *SchemaValidator) schemaPropsValidator() valueValidator {
|
||||
sch := s.Schema
|
||||
return newSchemaPropsValidator(s.Path, s.in, sch.AllOf, sch.OneOf, sch.AnyOf, sch.Not, sch.Dependencies, s.Root, s.KnownFormats, s.Options.Options()...)
|
||||
return newSchemaPropsValidator(
|
||||
s.Path, s.in, sch.AllOf, sch.OneOf, sch.AnyOf, sch.Not, sch.Dependencies, s.Root, s.KnownFormats,
|
||||
s.Options,
|
||||
)
|
||||
}
|
||||
|
||||
func (s *SchemaValidator) objectValidator() valueValidator {
|
||||
return &objectValidator{
|
||||
Path: s.Path,
|
||||
In: s.in,
|
||||
MaxProperties: s.Schema.MaxProperties,
|
||||
MinProperties: s.Schema.MinProperties,
|
||||
Required: s.Schema.Required,
|
||||
Properties: s.Schema.Properties,
|
||||
AdditionalProperties: s.Schema.AdditionalProperties,
|
||||
PatternProperties: s.Schema.PatternProperties,
|
||||
Root: s.Root,
|
||||
KnownFormats: s.KnownFormats,
|
||||
Options: s.Options,
|
||||
return newObjectValidator(
|
||||
s.Path,
|
||||
s.in,
|
||||
s.Schema.MaxProperties,
|
||||
s.Schema.MinProperties,
|
||||
s.Schema.Required,
|
||||
s.Schema.Properties,
|
||||
s.Schema.AdditionalProperties,
|
||||
s.Schema.PatternProperties,
|
||||
s.Root,
|
||||
s.KnownFormats,
|
||||
s.Options,
|
||||
)
|
||||
}
|
||||
|
||||
func (s *SchemaValidator) redeem() {
|
||||
pools.poolOfSchemaValidators.RedeemValidator(s)
|
||||
}
|
||||
|
||||
func (s *SchemaValidator) redeemChildren() {
|
||||
for i, validator := range s.validators {
|
||||
if validator == nil {
|
||||
continue
|
||||
}
|
||||
if redeemableChildren, ok := validator.(interface{ redeemChildren() }); ok {
|
||||
redeemableChildren.redeemChildren()
|
||||
}
|
||||
if redeemable, ok := validator.(interface{ redeem() }); ok {
|
||||
redeemable.redeem()
|
||||
}
|
||||
s.validators[i] = nil // free up allocated children if not in pool
|
||||
}
|
||||
}
|
||||
|
|
|
|||
31
vendor/github.com/go-openapi/validate/schema_option.go
generated
vendored
31
vendor/github.com/go-openapi/validate/schema_option.go
generated
vendored
|
|
@ -18,6 +18,9 @@ package validate
|
|||
type SchemaValidatorOptions struct {
|
||||
EnableObjectArrayTypeCheck bool
|
||||
EnableArrayMustHaveItemsCheck bool
|
||||
recycleValidators bool
|
||||
recycleResult bool
|
||||
skipSchemataResult bool
|
||||
}
|
||||
|
||||
// Option sets optional rules for schema validation
|
||||
|
|
@ -45,10 +48,36 @@ func SwaggerSchema(enable bool) Option {
|
|||
}
|
||||
}
|
||||
|
||||
// Options returns current options
|
||||
// WithRecycleValidators saves memory allocations and makes validators
|
||||
// available for a single use of Validate() only.
|
||||
//
|
||||
// When a validator is recycled, called MUST not call the Validate() method twice.
|
||||
func WithRecycleValidators(enable bool) Option {
|
||||
return func(svo *SchemaValidatorOptions) {
|
||||
svo.recycleValidators = enable
|
||||
}
|
||||
}
|
||||
|
||||
func withRecycleResults(enable bool) Option {
|
||||
return func(svo *SchemaValidatorOptions) {
|
||||
svo.recycleResult = enable
|
||||
}
|
||||
}
|
||||
|
||||
// WithSkipSchemataResult skips the deep audit payload stored in validation Result
|
||||
func WithSkipSchemataResult(enable bool) Option {
|
||||
return func(svo *SchemaValidatorOptions) {
|
||||
svo.skipSchemataResult = enable
|
||||
}
|
||||
}
|
||||
|
||||
// Options returns the current set of options
|
||||
func (svo SchemaValidatorOptions) Options() []Option {
|
||||
return []Option{
|
||||
EnableObjectArrayTypeCheck(svo.EnableObjectArrayTypeCheck),
|
||||
EnableArrayMustHaveItemsCheck(svo.EnableArrayMustHaveItemsCheck),
|
||||
WithRecycleValidators(svo.recycleValidators),
|
||||
withRecycleResults(svo.recycleResult),
|
||||
WithSkipSchemataResult(svo.skipSchemataResult),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
436
vendor/github.com/go-openapi/validate/schema_props.go
generated
vendored
436
vendor/github.com/go-openapi/validate/schema_props.go
generated
vendored
|
|
@ -30,211 +30,327 @@ type schemaPropsValidator struct {
|
|||
AnyOf []spec.Schema
|
||||
Not *spec.Schema
|
||||
Dependencies spec.Dependencies
|
||||
anyOfValidators []SchemaValidator
|
||||
allOfValidators []SchemaValidator
|
||||
oneOfValidators []SchemaValidator
|
||||
anyOfValidators []*SchemaValidator
|
||||
allOfValidators []*SchemaValidator
|
||||
oneOfValidators []*SchemaValidator
|
||||
notValidator *SchemaValidator
|
||||
Root interface{}
|
||||
KnownFormats strfmt.Registry
|
||||
Options SchemaValidatorOptions
|
||||
Options *SchemaValidatorOptions
|
||||
}
|
||||
|
||||
func (s *schemaPropsValidator) SetPath(path string) {
|
||||
s.Path = path
|
||||
}
|
||||
|
||||
func newSchemaPropsValidator(path string, in string, allOf, oneOf, anyOf []spec.Schema, not *spec.Schema, deps spec.Dependencies, root interface{}, formats strfmt.Registry, options ...Option) *schemaPropsValidator {
|
||||
anyValidators := make([]SchemaValidator, 0, len(anyOf))
|
||||
for _, v := range anyOf {
|
||||
v := v
|
||||
anyValidators = append(anyValidators, *NewSchemaValidator(&v, root, path, formats, options...))
|
||||
func newSchemaPropsValidator(
|
||||
path string, in string, allOf, oneOf, anyOf []spec.Schema, not *spec.Schema, deps spec.Dependencies, root interface{}, formats strfmt.Registry,
|
||||
opts *SchemaValidatorOptions) *schemaPropsValidator {
|
||||
if opts == nil {
|
||||
opts = new(SchemaValidatorOptions)
|
||||
}
|
||||
allValidators := make([]SchemaValidator, 0, len(allOf))
|
||||
for _, v := range allOf {
|
||||
v := v
|
||||
allValidators = append(allValidators, *NewSchemaValidator(&v, root, path, formats, options...))
|
||||
|
||||
anyValidators := make([]*SchemaValidator, 0, len(anyOf))
|
||||
for i := range anyOf {
|
||||
anyValidators = append(anyValidators, newSchemaValidator(&anyOf[i], root, path, formats, opts))
|
||||
}
|
||||
oneValidators := make([]SchemaValidator, 0, len(oneOf))
|
||||
for _, v := range oneOf {
|
||||
v := v
|
||||
oneValidators = append(oneValidators, *NewSchemaValidator(&v, root, path, formats, options...))
|
||||
allValidators := make([]*SchemaValidator, 0, len(allOf))
|
||||
for i := range allOf {
|
||||
allValidators = append(allValidators, newSchemaValidator(&allOf[i], root, path, formats, opts))
|
||||
}
|
||||
oneValidators := make([]*SchemaValidator, 0, len(oneOf))
|
||||
for i := range oneOf {
|
||||
oneValidators = append(oneValidators, newSchemaValidator(&oneOf[i], root, path, formats, opts))
|
||||
}
|
||||
|
||||
var notValidator *SchemaValidator
|
||||
if not != nil {
|
||||
notValidator = NewSchemaValidator(not, root, path, formats, options...)
|
||||
notValidator = newSchemaValidator(not, root, path, formats, opts)
|
||||
}
|
||||
|
||||
schOptions := &SchemaValidatorOptions{}
|
||||
for _, o := range options {
|
||||
o(schOptions)
|
||||
}
|
||||
return &schemaPropsValidator{
|
||||
Path: path,
|
||||
In: in,
|
||||
AllOf: allOf,
|
||||
OneOf: oneOf,
|
||||
AnyOf: anyOf,
|
||||
Not: not,
|
||||
Dependencies: deps,
|
||||
anyOfValidators: anyValidators,
|
||||
allOfValidators: allValidators,
|
||||
oneOfValidators: oneValidators,
|
||||
notValidator: notValidator,
|
||||
Root: root,
|
||||
KnownFormats: formats,
|
||||
Options: *schOptions,
|
||||
var s *schemaPropsValidator
|
||||
if opts.recycleValidators {
|
||||
s = pools.poolOfSchemaPropsValidators.BorrowValidator()
|
||||
} else {
|
||||
s = new(schemaPropsValidator)
|
||||
}
|
||||
|
||||
s.Path = path
|
||||
s.In = in
|
||||
s.AllOf = allOf
|
||||
s.OneOf = oneOf
|
||||
s.AnyOf = anyOf
|
||||
s.Not = not
|
||||
s.Dependencies = deps
|
||||
s.anyOfValidators = anyValidators
|
||||
s.allOfValidators = allValidators
|
||||
s.oneOfValidators = oneValidators
|
||||
s.notValidator = notValidator
|
||||
s.Root = root
|
||||
s.KnownFormats = formats
|
||||
s.Options = opts
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *schemaPropsValidator) Applies(source interface{}, kind reflect.Kind) bool {
|
||||
r := reflect.TypeOf(source) == specSchemaType
|
||||
debugLog("schema props validator for %q applies %t for %T (kind: %v)\n", s.Path, r, source, kind)
|
||||
return r
|
||||
func (s *schemaPropsValidator) Applies(source interface{}, _ reflect.Kind) bool {
|
||||
_, isSchema := source.(*spec.Schema)
|
||||
return isSchema
|
||||
}
|
||||
|
||||
func (s *schemaPropsValidator) Validate(data interface{}) *Result {
|
||||
mainResult := new(Result)
|
||||
var mainResult *Result
|
||||
if s.Options.recycleResult {
|
||||
mainResult = pools.poolOfResults.BorrowResult()
|
||||
} else {
|
||||
mainResult = new(Result)
|
||||
}
|
||||
|
||||
// Intermediary error results
|
||||
|
||||
// IMPORTANT! messages from underlying validators
|
||||
keepResultAnyOf := new(Result)
|
||||
keepResultOneOf := new(Result)
|
||||
keepResultAllOf := new(Result)
|
||||
var keepResultAnyOf, keepResultOneOf, keepResultAllOf *Result
|
||||
|
||||
if s.Options.recycleValidators {
|
||||
defer func() {
|
||||
s.redeemChildren()
|
||||
s.redeem()
|
||||
|
||||
// results are redeemed when merged
|
||||
}()
|
||||
}
|
||||
|
||||
// Validates at least one in anyOf schemas
|
||||
var firstSuccess *Result
|
||||
if len(s.anyOfValidators) > 0 {
|
||||
var bestFailures *Result
|
||||
succeededOnce := false
|
||||
for _, anyOfSchema := range s.anyOfValidators {
|
||||
result := anyOfSchema.Validate(data)
|
||||
// We keep inner IMPORTANT! errors no matter what MatchCount tells us
|
||||
keepResultAnyOf.Merge(result.keepRelevantErrors())
|
||||
if result.IsValid() {
|
||||
bestFailures = nil
|
||||
succeededOnce = true
|
||||
if firstSuccess == nil {
|
||||
firstSuccess = result
|
||||
}
|
||||
keepResultAnyOf = new(Result)
|
||||
break
|
||||
}
|
||||
// MatchCount is used to select errors from the schema with most positive checks
|
||||
if bestFailures == nil || result.MatchCount > bestFailures.MatchCount {
|
||||
bestFailures = result
|
||||
}
|
||||
}
|
||||
|
||||
if !succeededOnce {
|
||||
mainResult.AddErrors(mustValidateAtLeastOneSchemaMsg(s.Path))
|
||||
}
|
||||
if bestFailures != nil {
|
||||
mainResult.Merge(bestFailures)
|
||||
} else if firstSuccess != nil {
|
||||
mainResult.Merge(firstSuccess)
|
||||
}
|
||||
keepResultAnyOf = pools.poolOfResults.BorrowResult()
|
||||
s.validateAnyOf(data, mainResult, keepResultAnyOf)
|
||||
}
|
||||
|
||||
// Validates exactly one in oneOf schemas
|
||||
if len(s.oneOfValidators) > 0 {
|
||||
var bestFailures *Result
|
||||
var firstSuccess *Result
|
||||
validated := 0
|
||||
|
||||
for _, oneOfSchema := range s.oneOfValidators {
|
||||
result := oneOfSchema.Validate(data)
|
||||
// We keep inner IMPORTANT! errors no matter what MatchCount tells us
|
||||
keepResultOneOf.Merge(result.keepRelevantErrors())
|
||||
if result.IsValid() {
|
||||
validated++
|
||||
bestFailures = nil
|
||||
if firstSuccess == nil {
|
||||
firstSuccess = result
|
||||
}
|
||||
keepResultOneOf = new(Result)
|
||||
continue
|
||||
}
|
||||
// MatchCount is used to select errors from the schema with most positive checks
|
||||
if validated == 0 && (bestFailures == nil || result.MatchCount > bestFailures.MatchCount) {
|
||||
bestFailures = result
|
||||
}
|
||||
}
|
||||
|
||||
if validated != 1 {
|
||||
var additionalMsg string
|
||||
if validated == 0 {
|
||||
additionalMsg = "Found none valid"
|
||||
} else {
|
||||
additionalMsg = fmt.Sprintf("Found %d valid alternatives", validated)
|
||||
}
|
||||
|
||||
mainResult.AddErrors(mustValidateOnlyOneSchemaMsg(s.Path, additionalMsg))
|
||||
if bestFailures != nil {
|
||||
mainResult.Merge(bestFailures)
|
||||
}
|
||||
} else if firstSuccess != nil {
|
||||
mainResult.Merge(firstSuccess)
|
||||
}
|
||||
keepResultOneOf = pools.poolOfResults.BorrowResult()
|
||||
s.validateOneOf(data, mainResult, keepResultOneOf)
|
||||
}
|
||||
|
||||
// Validates all of allOf schemas
|
||||
if len(s.allOfValidators) > 0 {
|
||||
validated := 0
|
||||
|
||||
for _, allOfSchema := range s.allOfValidators {
|
||||
result := allOfSchema.Validate(data)
|
||||
// We keep inner IMPORTANT! errors no matter what MatchCount tells us
|
||||
keepResultAllOf.Merge(result.keepRelevantErrors())
|
||||
// keepResultAllOf.Merge(result)
|
||||
if result.IsValid() {
|
||||
validated++
|
||||
}
|
||||
mainResult.Merge(result)
|
||||
}
|
||||
|
||||
if validated != len(s.allOfValidators) {
|
||||
additionalMsg := ""
|
||||
if validated == 0 {
|
||||
additionalMsg = ". None validated"
|
||||
}
|
||||
|
||||
mainResult.AddErrors(mustValidateAllSchemasMsg(s.Path, additionalMsg))
|
||||
}
|
||||
keepResultAllOf = pools.poolOfResults.BorrowResult()
|
||||
s.validateAllOf(data, mainResult, keepResultAllOf)
|
||||
}
|
||||
|
||||
if s.notValidator != nil {
|
||||
result := s.notValidator.Validate(data)
|
||||
// We keep inner IMPORTANT! errors no matter what MatchCount tells us
|
||||
if result.IsValid() {
|
||||
mainResult.AddErrors(mustNotValidatechemaMsg(s.Path))
|
||||
}
|
||||
s.validateNot(data, mainResult)
|
||||
}
|
||||
|
||||
if s.Dependencies != nil && len(s.Dependencies) > 0 && reflect.TypeOf(data).Kind() == reflect.Map {
|
||||
val := data.(map[string]interface{})
|
||||
for key := range val {
|
||||
if dep, ok := s.Dependencies[key]; ok {
|
||||
|
||||
if dep.Schema != nil {
|
||||
mainResult.Merge(NewSchemaValidator(dep.Schema, s.Root, s.Path+"."+key, s.KnownFormats, s.Options.Options()...).Validate(data))
|
||||
continue
|
||||
}
|
||||
|
||||
if len(dep.Property) > 0 {
|
||||
for _, depKey := range dep.Property {
|
||||
if _, ok := val[depKey]; !ok {
|
||||
mainResult.AddErrors(hasADependencyMsg(s.Path, depKey))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
s.validateDependencies(data, mainResult)
|
||||
}
|
||||
|
||||
mainResult.Inc()
|
||||
|
||||
// In the end we retain best failures for schema validation
|
||||
// plus, if any, composite errors which may explain special cases (tagged as IMPORTANT!).
|
||||
return mainResult.Merge(keepResultAllOf, keepResultOneOf, keepResultAnyOf)
|
||||
}
|
||||
|
||||
func (s *schemaPropsValidator) validateAnyOf(data interface{}, mainResult, keepResultAnyOf *Result) {
|
||||
// Validates at least one in anyOf schemas
|
||||
var bestFailures *Result
|
||||
|
||||
for i, anyOfSchema := range s.anyOfValidators {
|
||||
result := anyOfSchema.Validate(data)
|
||||
if s.Options.recycleValidators {
|
||||
s.anyOfValidators[i] = nil
|
||||
}
|
||||
// We keep inner IMPORTANT! errors no matter what MatchCount tells us
|
||||
keepResultAnyOf.Merge(result.keepRelevantErrors()) // merges (and redeems) a new instance of Result
|
||||
|
||||
if result.IsValid() {
|
||||
if bestFailures != nil && bestFailures.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(bestFailures)
|
||||
}
|
||||
|
||||
_ = keepResultAnyOf.cleared()
|
||||
mainResult.Merge(result)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// MatchCount is used to select errors from the schema with most positive checks
|
||||
if bestFailures == nil || result.MatchCount > bestFailures.MatchCount {
|
||||
if bestFailures != nil && bestFailures.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(bestFailures)
|
||||
}
|
||||
bestFailures = result
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if result.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(result) // this result is ditched
|
||||
}
|
||||
}
|
||||
|
||||
mainResult.AddErrors(mustValidateAtLeastOneSchemaMsg(s.Path))
|
||||
mainResult.Merge(bestFailures)
|
||||
}
|
||||
|
||||
func (s *schemaPropsValidator) validateOneOf(data interface{}, mainResult, keepResultOneOf *Result) {
|
||||
// Validates exactly one in oneOf schemas
|
||||
var (
|
||||
firstSuccess, bestFailures *Result
|
||||
validated int
|
||||
)
|
||||
|
||||
for i, oneOfSchema := range s.oneOfValidators {
|
||||
result := oneOfSchema.Validate(data)
|
||||
if s.Options.recycleValidators {
|
||||
s.oneOfValidators[i] = nil
|
||||
}
|
||||
|
||||
// We keep inner IMPORTANT! errors no matter what MatchCount tells us
|
||||
keepResultOneOf.Merge(result.keepRelevantErrors()) // merges (and redeems) a new instance of Result
|
||||
|
||||
if result.IsValid() {
|
||||
validated++
|
||||
_ = keepResultOneOf.cleared()
|
||||
|
||||
if firstSuccess == nil {
|
||||
firstSuccess = result
|
||||
} else if result.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(result) // this result is ditched
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
// MatchCount is used to select errors from the schema with most positive checks
|
||||
if validated == 0 && (bestFailures == nil || result.MatchCount > bestFailures.MatchCount) {
|
||||
if bestFailures != nil && bestFailures.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(bestFailures)
|
||||
}
|
||||
bestFailures = result
|
||||
} else if result.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(result) // this result is ditched
|
||||
}
|
||||
}
|
||||
|
||||
switch validated {
|
||||
case 0:
|
||||
mainResult.AddErrors(mustValidateOnlyOneSchemaMsg(s.Path, "Found none valid"))
|
||||
mainResult.Merge(bestFailures)
|
||||
// firstSucess necessarily nil
|
||||
case 1:
|
||||
mainResult.Merge(firstSuccess)
|
||||
if bestFailures != nil && bestFailures.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(bestFailures)
|
||||
}
|
||||
default:
|
||||
mainResult.AddErrors(mustValidateOnlyOneSchemaMsg(s.Path, fmt.Sprintf("Found %d valid alternatives", validated)))
|
||||
mainResult.Merge(bestFailures)
|
||||
if firstSuccess != nil && firstSuccess.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(firstSuccess)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *schemaPropsValidator) validateAllOf(data interface{}, mainResult, keepResultAllOf *Result) {
|
||||
// Validates all of allOf schemas
|
||||
var validated int
|
||||
|
||||
for i, allOfSchema := range s.allOfValidators {
|
||||
result := allOfSchema.Validate(data)
|
||||
if s.Options.recycleValidators {
|
||||
s.allOfValidators[i] = nil
|
||||
}
|
||||
// We keep inner IMPORTANT! errors no matter what MatchCount tells us
|
||||
keepResultAllOf.Merge(result.keepRelevantErrors())
|
||||
if result.IsValid() {
|
||||
validated++
|
||||
}
|
||||
mainResult.Merge(result)
|
||||
}
|
||||
|
||||
switch validated {
|
||||
case 0:
|
||||
mainResult.AddErrors(mustValidateAllSchemasMsg(s.Path, ". None validated"))
|
||||
case len(s.allOfValidators):
|
||||
default:
|
||||
mainResult.AddErrors(mustValidateAllSchemasMsg(s.Path, ""))
|
||||
}
|
||||
}
|
||||
|
||||
func (s *schemaPropsValidator) validateNot(data interface{}, mainResult *Result) {
|
||||
result := s.notValidator.Validate(data)
|
||||
if s.Options.recycleValidators {
|
||||
s.notValidator = nil
|
||||
}
|
||||
// We keep inner IMPORTANT! errors no matter what MatchCount tells us
|
||||
if result.IsValid() {
|
||||
mainResult.AddErrors(mustNotValidatechemaMsg(s.Path))
|
||||
}
|
||||
if result.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(result) // this result is ditched
|
||||
}
|
||||
}
|
||||
|
||||
func (s *schemaPropsValidator) validateDependencies(data interface{}, mainResult *Result) {
|
||||
val := data.(map[string]interface{})
|
||||
for key := range val {
|
||||
dep, ok := s.Dependencies[key]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if dep.Schema != nil {
|
||||
mainResult.Merge(
|
||||
newSchemaValidator(dep.Schema, s.Root, s.Path+"."+key, s.KnownFormats, s.Options).Validate(data),
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
if len(dep.Property) > 0 {
|
||||
for _, depKey := range dep.Property {
|
||||
if _, ok := val[depKey]; !ok {
|
||||
mainResult.AddErrors(hasADependencyMsg(s.Path, depKey))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *schemaPropsValidator) redeem() {
|
||||
pools.poolOfSchemaPropsValidators.RedeemValidator(s)
|
||||
}
|
||||
|
||||
func (s *schemaPropsValidator) redeemChildren() {
|
||||
for _, v := range s.anyOfValidators {
|
||||
if v == nil {
|
||||
continue
|
||||
}
|
||||
v.redeemChildren()
|
||||
v.redeem()
|
||||
}
|
||||
s.anyOfValidators = nil
|
||||
|
||||
for _, v := range s.allOfValidators {
|
||||
if v == nil {
|
||||
continue
|
||||
}
|
||||
v.redeemChildren()
|
||||
v.redeem()
|
||||
}
|
||||
s.allOfValidators = nil
|
||||
|
||||
for _, v := range s.oneOfValidators {
|
||||
if v == nil {
|
||||
continue
|
||||
}
|
||||
v.redeemChildren()
|
||||
v.redeem()
|
||||
}
|
||||
s.oneOfValidators = nil
|
||||
|
||||
if s.notValidator != nil {
|
||||
s.notValidator.redeemChildren()
|
||||
s.notValidator.redeem()
|
||||
s.notValidator = nil
|
||||
}
|
||||
}
|
||||
|
|
|
|||
57
vendor/github.com/go-openapi/validate/slice_validator.go
generated
vendored
57
vendor/github.com/go-openapi/validate/slice_validator.go
generated
vendored
|
|
@ -32,7 +32,36 @@ type schemaSliceValidator struct {
|
|||
Items *spec.SchemaOrArray
|
||||
Root interface{}
|
||||
KnownFormats strfmt.Registry
|
||||
Options SchemaValidatorOptions
|
||||
Options *SchemaValidatorOptions
|
||||
}
|
||||
|
||||
func newSliceValidator(path, in string,
|
||||
maxItems, minItems *int64, uniqueItems bool,
|
||||
additionalItems *spec.SchemaOrBool, items *spec.SchemaOrArray,
|
||||
root interface{}, formats strfmt.Registry, opts *SchemaValidatorOptions) *schemaSliceValidator {
|
||||
if opts == nil {
|
||||
opts = new(SchemaValidatorOptions)
|
||||
}
|
||||
|
||||
var v *schemaSliceValidator
|
||||
if opts.recycleValidators {
|
||||
v = pools.poolOfSliceValidators.BorrowValidator()
|
||||
} else {
|
||||
v = new(schemaSliceValidator)
|
||||
}
|
||||
|
||||
v.Path = path
|
||||
v.In = in
|
||||
v.MaxItems = maxItems
|
||||
v.MinItems = minItems
|
||||
v.UniqueItems = uniqueItems
|
||||
v.AdditionalItems = additionalItems
|
||||
v.Items = items
|
||||
v.Root = root
|
||||
v.KnownFormats = formats
|
||||
v.Options = opts
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
func (s *schemaSliceValidator) SetPath(path string) {
|
||||
|
|
@ -46,7 +75,18 @@ func (s *schemaSliceValidator) Applies(source interface{}, kind reflect.Kind) bo
|
|||
}
|
||||
|
||||
func (s *schemaSliceValidator) Validate(data interface{}) *Result {
|
||||
result := new(Result)
|
||||
if s.Options.recycleValidators {
|
||||
defer func() {
|
||||
s.redeem()
|
||||
}()
|
||||
}
|
||||
|
||||
var result *Result
|
||||
if s.Options.recycleResult {
|
||||
result = pools.poolOfResults.BorrowResult()
|
||||
} else {
|
||||
result = new(Result)
|
||||
}
|
||||
if data == nil {
|
||||
return result
|
||||
}
|
||||
|
|
@ -54,8 +94,8 @@ func (s *schemaSliceValidator) Validate(data interface{}) *Result {
|
|||
size := val.Len()
|
||||
|
||||
if s.Items != nil && s.Items.Schema != nil {
|
||||
validator := NewSchemaValidator(s.Items.Schema, s.Root, s.Path, s.KnownFormats, s.Options.Options()...)
|
||||
for i := 0; i < size; i++ {
|
||||
validator := newSchemaValidator(s.Items.Schema, s.Root, s.Path, s.KnownFormats, s.Options)
|
||||
validator.SetPath(fmt.Sprintf("%s.%d", s.Path, i))
|
||||
value := val.Index(i)
|
||||
result.mergeForSlice(val, i, validator.Validate(value.Interface()))
|
||||
|
|
@ -66,10 +106,11 @@ func (s *schemaSliceValidator) Validate(data interface{}) *Result {
|
|||
if s.Items != nil && len(s.Items.Schemas) > 0 {
|
||||
itemsSize = len(s.Items.Schemas)
|
||||
for i := 0; i < itemsSize; i++ {
|
||||
validator := NewSchemaValidator(&s.Items.Schemas[i], s.Root, fmt.Sprintf("%s.%d", s.Path, i), s.KnownFormats, s.Options.Options()...)
|
||||
if val.Len() <= i {
|
||||
if size <= i {
|
||||
break
|
||||
}
|
||||
|
||||
validator := newSchemaValidator(&s.Items.Schemas[i], s.Root, fmt.Sprintf("%s.%d", s.Path, i), s.KnownFormats, s.Options)
|
||||
result.mergeForSlice(val, i, validator.Validate(val.Index(i).Interface()))
|
||||
}
|
||||
}
|
||||
|
|
@ -79,7 +120,7 @@ func (s *schemaSliceValidator) Validate(data interface{}) *Result {
|
|||
}
|
||||
if s.AdditionalItems.Schema != nil {
|
||||
for i := itemsSize; i < size-itemsSize+1; i++ {
|
||||
validator := NewSchemaValidator(s.AdditionalItems.Schema, s.Root, fmt.Sprintf("%s.%d", s.Path, i), s.KnownFormats, s.Options.Options()...)
|
||||
validator := newSchemaValidator(s.AdditionalItems.Schema, s.Root, fmt.Sprintf("%s.%d", s.Path, i), s.KnownFormats, s.Options)
|
||||
result.mergeForSlice(val, i, validator.Validate(val.Index(i).Interface()))
|
||||
}
|
||||
}
|
||||
|
|
@ -103,3 +144,7 @@ func (s *schemaSliceValidator) Validate(data interface{}) *Result {
|
|||
result.Inc()
|
||||
return result
|
||||
}
|
||||
|
||||
func (s *schemaSliceValidator) redeem() {
|
||||
pools.poolOfSliceValidators.RedeemValidator(s)
|
||||
}
|
||||
|
|
|
|||
130
vendor/github.com/go-openapi/validate/spec.go
generated
vendored
130
vendor/github.com/go-openapi/validate/spec.go
generated
vendored
|
|
@ -15,6 +15,8 @@
|
|||
package validate
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
|
@ -26,6 +28,7 @@ import (
|
|||
"github.com/go-openapi/loads"
|
||||
"github.com/go-openapi/spec"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// Spec validates an OpenAPI 2.0 specification document.
|
||||
|
|
@ -52,25 +55,38 @@ func Spec(doc *loads.Document, formats strfmt.Registry) error {
|
|||
|
||||
// SpecValidator validates a swagger 2.0 spec
|
||||
type SpecValidator struct {
|
||||
schema *spec.Schema // swagger 2.0 schema
|
||||
spec *loads.Document
|
||||
analyzer *analysis.Spec
|
||||
expanded *loads.Document
|
||||
KnownFormats strfmt.Registry
|
||||
Options Opts // validation options
|
||||
schema *spec.Schema // swagger 2.0 schema
|
||||
spec *loads.Document
|
||||
analyzer *analysis.Spec
|
||||
expanded *loads.Document
|
||||
KnownFormats strfmt.Registry
|
||||
Options Opts // validation options
|
||||
schemaOptions *SchemaValidatorOptions
|
||||
}
|
||||
|
||||
// NewSpecValidator creates a new swagger spec validator instance
|
||||
func NewSpecValidator(schema *spec.Schema, formats strfmt.Registry) *SpecValidator {
|
||||
// schema options that apply to all called validators
|
||||
schemaOptions := new(SchemaValidatorOptions)
|
||||
for _, o := range []Option{
|
||||
SwaggerSchema(true),
|
||||
WithRecycleValidators(true),
|
||||
// withRecycleResults(true),
|
||||
} {
|
||||
o(schemaOptions)
|
||||
}
|
||||
|
||||
return &SpecValidator{
|
||||
schema: schema,
|
||||
KnownFormats: formats,
|
||||
Options: defaultOpts,
|
||||
schema: schema,
|
||||
KnownFormats: formats,
|
||||
Options: defaultOpts,
|
||||
schemaOptions: schemaOptions,
|
||||
}
|
||||
}
|
||||
|
||||
// Validate validates the swagger spec
|
||||
func (s *SpecValidator) Validate(data interface{}) (*Result, *Result) {
|
||||
s.schemaOptions.skipSchemataResult = s.Options.SkipSchemataResult
|
||||
var sd *loads.Document
|
||||
errs, warnings := new(Result), new(Result)
|
||||
|
||||
|
|
@ -84,11 +100,8 @@ func (s *SpecValidator) Validate(data interface{}) (*Result, *Result) {
|
|||
s.spec = sd
|
||||
s.analyzer = analysis.New(sd.Spec())
|
||||
|
||||
// Swagger schema validator
|
||||
schv := NewSchemaValidator(s.schema, nil, "", s.KnownFormats, SwaggerSchema(true))
|
||||
var obj interface{}
|
||||
|
||||
// Raw spec unmarshalling errors
|
||||
var obj interface{}
|
||||
if err := json.Unmarshal(sd.Raw(), &obj); err != nil {
|
||||
// NOTE: under normal conditions, the *load.Document has been already unmarshalled
|
||||
// So this one is just a paranoid check on the behavior of the spec package
|
||||
|
|
@ -102,6 +115,8 @@ func (s *SpecValidator) Validate(data interface{}) (*Result, *Result) {
|
|||
warnings.AddErrors(errs.Warnings...)
|
||||
}()
|
||||
|
||||
// Swagger schema validator
|
||||
schv := newSchemaValidator(s.schema, nil, "", s.KnownFormats, s.schemaOptions)
|
||||
errs.Merge(schv.Validate(obj)) // error -
|
||||
// There may be a point in continuing to try and determine more accurate errors
|
||||
if !s.Options.ContinueOnErrors && errs.HasErrors() {
|
||||
|
|
@ -129,13 +144,13 @@ func (s *SpecValidator) Validate(data interface{}) (*Result, *Result) {
|
|||
}
|
||||
|
||||
// Values provided as default MUST validate their schema
|
||||
df := &defaultValidator{SpecValidator: s}
|
||||
df := &defaultValidator{SpecValidator: s, schemaOptions: s.schemaOptions}
|
||||
errs.Merge(df.Validate())
|
||||
|
||||
// Values provided as examples MUST validate their schema
|
||||
// Value provided as examples in a response without schema generate a warning
|
||||
// Known limitations: examples in responses for mime type not application/json are ignored (warning)
|
||||
ex := &exampleValidator{SpecValidator: s}
|
||||
ex := &exampleValidator{SpecValidator: s, schemaOptions: s.schemaOptions}
|
||||
errs.Merge(ex.Validate())
|
||||
|
||||
errs.Merge(s.validateNonEmptyPathParamNames())
|
||||
|
|
@ -147,22 +162,27 @@ func (s *SpecValidator) Validate(data interface{}) (*Result, *Result) {
|
|||
}
|
||||
|
||||
func (s *SpecValidator) validateNonEmptyPathParamNames() *Result {
|
||||
res := new(Result)
|
||||
res := pools.poolOfResults.BorrowResult()
|
||||
if s.spec.Spec().Paths == nil {
|
||||
// There is no Paths object: error
|
||||
res.AddErrors(noValidPathMsg())
|
||||
} else {
|
||||
if s.spec.Spec().Paths.Paths == nil {
|
||||
// Paths may be empty: warning
|
||||
res.AddWarnings(noValidPathMsg())
|
||||
} else {
|
||||
for k := range s.spec.Spec().Paths.Paths {
|
||||
if strings.Contains(k, "{}") {
|
||||
res.AddErrors(emptyPathParameterMsg(k))
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
if s.spec.Spec().Paths.Paths == nil {
|
||||
// Paths may be empty: warning
|
||||
res.AddWarnings(noValidPathMsg())
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
for k := range s.spec.Spec().Paths.Paths {
|
||||
if strings.Contains(k, "{}") {
|
||||
res.AddErrors(emptyPathParameterMsg(k))
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
|
|
@ -176,7 +196,7 @@ func (s *SpecValidator) validateDuplicateOperationIDs() *Result {
|
|||
// fallback on possible incomplete picture because of previous errors
|
||||
analyzer = s.analyzer
|
||||
}
|
||||
res := new(Result)
|
||||
res := pools.poolOfResults.BorrowResult()
|
||||
known := make(map[string]int)
|
||||
for _, v := range analyzer.OperationIDs() {
|
||||
if v != "" {
|
||||
|
|
@ -198,7 +218,7 @@ type dupProp struct {
|
|||
|
||||
func (s *SpecValidator) validateDuplicatePropertyNames() *Result {
|
||||
// definition can't declare a property that's already defined by one of its ancestors
|
||||
res := new(Result)
|
||||
res := pools.poolOfResults.BorrowResult()
|
||||
for k, sch := range s.spec.Spec().Definitions {
|
||||
if len(sch.AllOf) == 0 {
|
||||
continue
|
||||
|
|
@ -247,7 +267,7 @@ func (s *SpecValidator) validateSchemaPropertyNames(nm string, sch spec.Schema,
|
|||
|
||||
schn := nm
|
||||
schc := &sch
|
||||
res := new(Result)
|
||||
res := pools.poolOfResults.BorrowResult()
|
||||
|
||||
for schc.Ref.String() != "" {
|
||||
// gather property names
|
||||
|
|
@ -284,7 +304,7 @@ func (s *SpecValidator) validateSchemaPropertyNames(nm string, sch spec.Schema,
|
|||
}
|
||||
|
||||
func (s *SpecValidator) validateCircularAncestry(nm string, sch spec.Schema, knowns map[string]struct{}) ([]string, *Result) {
|
||||
res := new(Result)
|
||||
res := pools.poolOfResults.BorrowResult()
|
||||
|
||||
if sch.Ref.String() == "" && len(sch.AllOf) == 0 { // Safeguard. We should not be able to actually get there
|
||||
return nil, res
|
||||
|
|
@ -334,7 +354,7 @@ func (s *SpecValidator) validateCircularAncestry(nm string, sch spec.Schema, kno
|
|||
|
||||
func (s *SpecValidator) validateItems() *Result {
|
||||
// validate parameter, items, schema and response objects for presence of item if type is array
|
||||
res := new(Result)
|
||||
res := pools.poolOfResults.BorrowResult()
|
||||
|
||||
for method, pi := range s.analyzer.Operations() {
|
||||
for path, op := range pi {
|
||||
|
|
@ -393,7 +413,7 @@ func (s *SpecValidator) validateItems() *Result {
|
|||
|
||||
// Verifies constraints on array type
|
||||
func (s *SpecValidator) validateSchemaItems(schema spec.Schema, prefix, opID string) *Result {
|
||||
res := new(Result)
|
||||
res := pools.poolOfResults.BorrowResult()
|
||||
if !schema.Type.Contains(arrayType) {
|
||||
return res
|
||||
}
|
||||
|
|
@ -417,7 +437,7 @@ func (s *SpecValidator) validateSchemaItems(schema spec.Schema, prefix, opID str
|
|||
func (s *SpecValidator) validatePathParamPresence(path string, fromPath, fromOperation []string) *Result {
|
||||
// Each defined operation path parameters must correspond to a named element in the API's path pattern.
|
||||
// (For example, you cannot have a path parameter named id for the following path /pets/{petId} but you must have a path parameter named petId.)
|
||||
res := new(Result)
|
||||
res := pools.poolOfResults.BorrowResult()
|
||||
for _, l := range fromPath {
|
||||
var matched bool
|
||||
for _, r := range fromOperation {
|
||||
|
|
@ -473,7 +493,7 @@ func (s *SpecValidator) validateReferencedParameters() *Result {
|
|||
if len(expected) == 0 {
|
||||
return nil
|
||||
}
|
||||
result := new(Result)
|
||||
result := pools.poolOfResults.BorrowResult()
|
||||
for k := range expected {
|
||||
result.AddWarnings(unusedParamMsg(k))
|
||||
}
|
||||
|
|
@ -498,7 +518,7 @@ func (s *SpecValidator) validateReferencedResponses() *Result {
|
|||
if len(expected) == 0 {
|
||||
return nil
|
||||
}
|
||||
result := new(Result)
|
||||
result := pools.poolOfResults.BorrowResult()
|
||||
for k := range expected {
|
||||
result.AddWarnings(unusedResponseMsg(k))
|
||||
}
|
||||
|
|
@ -533,7 +553,7 @@ func (s *SpecValidator) validateReferencedDefinitions() *Result {
|
|||
|
||||
func (s *SpecValidator) validateRequiredDefinitions() *Result {
|
||||
// Each property listed in the required array must be defined in the properties of the model
|
||||
res := new(Result)
|
||||
res := pools.poolOfResults.BorrowResult()
|
||||
|
||||
DEFINITIONS:
|
||||
for d, schema := range s.spec.Spec().Definitions {
|
||||
|
|
@ -552,7 +572,7 @@ DEFINITIONS:
|
|||
|
||||
func (s *SpecValidator) validateRequiredProperties(path, in string, v *spec.Schema) *Result {
|
||||
// Takes care of recursive property definitions, which may be nested in additionalProperties schemas
|
||||
res := new(Result)
|
||||
res := pools.poolOfResults.BorrowResult()
|
||||
propertyMatch := false
|
||||
patternMatch := false
|
||||
additionalPropertiesMatch := false
|
||||
|
|
@ -618,7 +638,7 @@ func (s *SpecValidator) validateParameters() *Result {
|
|||
// - parameters with pattern property must specify valid patterns
|
||||
// - $ref in parameters must resolve
|
||||
// - path param must be required
|
||||
res := new(Result)
|
||||
res := pools.poolOfResults.BorrowResult()
|
||||
rexGarbledPathSegment := mustCompileRegexp(`.*[{}\s]+.*`)
|
||||
for method, pi := range s.expandedAnalyzer().Operations() {
|
||||
methodPaths := make(map[string]map[string]string)
|
||||
|
|
@ -657,7 +677,23 @@ func (s *SpecValidator) validateParameters() *Result {
|
|||
// TODO: should be done after param expansion
|
||||
res.Merge(s.checkUniqueParams(path, method, op))
|
||||
|
||||
// pick the root schema from the swagger specification which describes a parameter
|
||||
origSchema, ok := s.schema.Definitions["parameter"]
|
||||
if !ok {
|
||||
panic("unexpected swagger schema: missing #/definitions/parameter")
|
||||
}
|
||||
// clone it once to avoid expanding a global schema (e.g. swagger spec)
|
||||
paramSchema, err := deepCloneSchema(origSchema)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("can't clone schema: %v", err))
|
||||
}
|
||||
|
||||
for _, pr := range paramHelp.safeExpandedParamsFor(path, method, op.ID, res, s) {
|
||||
// An expanded parameter must validate the Parameter schema (an unexpanded $ref always passes high-level schema validation)
|
||||
schv := newSchemaValidator(¶mSchema, s.schema, fmt.Sprintf("%s.%s.parameters.%s", path, method, pr.Name), s.KnownFormats, s.schemaOptions)
|
||||
obj := swag.ToDynamicJSON(pr)
|
||||
res.Merge(schv.Validate(obj))
|
||||
|
||||
// Validate pattern regexp for parameters with a Pattern property
|
||||
if _, err := compileRegexp(pr.Pattern); err != nil {
|
||||
res.AddErrors(invalidPatternInParamMsg(op.ID, pr.Name, pr.Pattern))
|
||||
|
|
@ -739,7 +775,7 @@ func (s *SpecValidator) validateParameters() *Result {
|
|||
|
||||
func (s *SpecValidator) validateReferencesValid() *Result {
|
||||
// each reference must point to a valid object
|
||||
res := new(Result)
|
||||
res := pools.poolOfResults.BorrowResult()
|
||||
for _, r := range s.analyzer.AllRefs() {
|
||||
if !r.IsValidURI(s.spec.SpecFilePath()) { // Safeguard - spec should always yield a valid URI
|
||||
res.AddErrors(invalidRefMsg(r.String()))
|
||||
|
|
@ -765,7 +801,7 @@ func (s *SpecValidator) checkUniqueParams(path, method string, op *spec.Operatio
|
|||
// However, there are some issues with such a factorization:
|
||||
// - analysis does not seem to fully expand params
|
||||
// - param keys may be altered by x-go-name
|
||||
res := new(Result)
|
||||
res := pools.poolOfResults.BorrowResult()
|
||||
pnames := make(map[string]struct{})
|
||||
|
||||
if op.Parameters != nil { // Safeguard
|
||||
|
|
@ -800,3 +836,17 @@ func (s *SpecValidator) expandedAnalyzer() *analysis.Spec {
|
|||
}
|
||||
return s.analyzer
|
||||
}
|
||||
|
||||
func deepCloneSchema(src spec.Schema) (spec.Schema, error) {
|
||||
var b bytes.Buffer
|
||||
if err := gob.NewEncoder(&b).Encode(src); err != nil {
|
||||
return spec.Schema{}, err
|
||||
}
|
||||
|
||||
var dst spec.Schema
|
||||
if err := gob.NewDecoder(&b).Decode(&dst); err != nil {
|
||||
return spec.Schema{}, err
|
||||
}
|
||||
|
||||
return dst, nil
|
||||
}
|
||||
|
|
|
|||
5
vendor/github.com/go-openapi/validate/spec_messages.go
generated
vendored
5
vendor/github.com/go-openapi/validate/spec_messages.go
generated
vendored
|
|
@ -187,6 +187,8 @@ const (
|
|||
|
||||
// UnusedResponseWarning ...
|
||||
UnusedResponseWarning = "response %q is not used anywhere"
|
||||
|
||||
InvalidObject = "expected an object in %q.%s"
|
||||
)
|
||||
|
||||
// Additional error codes
|
||||
|
|
@ -347,6 +349,9 @@ func invalidParameterDefinitionAsSchemaMsg(path, method, operationID string) err
|
|||
func parameterValidationTypeMismatchMsg(param, path, typ string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, ParamValidationTypeMismatch, param, path, typ)
|
||||
}
|
||||
func invalidObjectMsg(path, in string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, InvalidObject, path, in)
|
||||
}
|
||||
|
||||
// disabled
|
||||
//
|
||||
|
|
|
|||
70
vendor/github.com/go-openapi/validate/type.go
generated
vendored
70
vendor/github.com/go-openapi/validate/type.go
generated
vendored
|
|
@ -25,11 +25,34 @@ import (
|
|||
)
|
||||
|
||||
type typeValidator struct {
|
||||
Path string
|
||||
In string
|
||||
Type spec.StringOrArray
|
||||
Nullable bool
|
||||
Format string
|
||||
In string
|
||||
Path string
|
||||
Options *SchemaValidatorOptions
|
||||
}
|
||||
|
||||
func newTypeValidator(path, in string, typ spec.StringOrArray, nullable bool, format string, opts *SchemaValidatorOptions) *typeValidator {
|
||||
if opts == nil {
|
||||
opts = new(SchemaValidatorOptions)
|
||||
}
|
||||
|
||||
var t *typeValidator
|
||||
if opts.recycleValidators {
|
||||
t = pools.poolOfTypeValidators.BorrowValidator()
|
||||
} else {
|
||||
t = new(typeValidator)
|
||||
}
|
||||
|
||||
t.Path = path
|
||||
t.In = in
|
||||
t.Type = typ
|
||||
t.Nullable = nullable
|
||||
t.Format = format
|
||||
t.Options = opts
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
func (t *typeValidator) schemaInfoForType(data interface{}) (string, string) {
|
||||
|
|
@ -125,23 +148,33 @@ func (t *typeValidator) SetPath(path string) {
|
|||
t.Path = path
|
||||
}
|
||||
|
||||
func (t *typeValidator) Applies(source interface{}, kind reflect.Kind) bool {
|
||||
func (t *typeValidator) Applies(source interface{}, _ reflect.Kind) bool {
|
||||
// typeValidator applies to Schema, Parameter and Header objects
|
||||
stpe := reflect.TypeOf(source)
|
||||
r := (len(t.Type) > 0 || t.Format != "") && (stpe == specSchemaType || stpe == specParameterType || stpe == specHeaderType)
|
||||
debugLog("type validator for %q applies %t for %T (kind: %v)\n", t.Path, r, source, kind)
|
||||
return r
|
||||
switch source.(type) {
|
||||
case *spec.Schema:
|
||||
case *spec.Parameter:
|
||||
case *spec.Header:
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
||||
return (len(t.Type) > 0 || t.Format != "")
|
||||
}
|
||||
|
||||
func (t *typeValidator) Validate(data interface{}) *Result {
|
||||
result := new(Result)
|
||||
result.Inc()
|
||||
if t.Options.recycleValidators {
|
||||
defer func() {
|
||||
t.redeem()
|
||||
}()
|
||||
}
|
||||
|
||||
if data == nil {
|
||||
// nil or zero value for the passed structure require Type: null
|
||||
if len(t.Type) > 0 && !t.Type.Contains(nullType) && !t.Nullable { // TODO: if a property is not required it also passes this
|
||||
return errorHelp.sErr(errors.InvalidType(t.Path, t.In, strings.Join(t.Type, ","), nullType))
|
||||
return errorHelp.sErr(errors.InvalidType(t.Path, t.In, strings.Join(t.Type, ","), nullType), t.Options.recycleResult)
|
||||
}
|
||||
return result
|
||||
|
||||
return emptyResult
|
||||
}
|
||||
|
||||
// check if the type matches, should be used in every validator chain as first item
|
||||
|
|
@ -151,8 +184,6 @@ func (t *typeValidator) Validate(data interface{}) *Result {
|
|||
// infer schema type (JSON) and format from passed data type
|
||||
schType, format := t.schemaInfoForType(data)
|
||||
|
||||
debugLog("path: %s, schType: %s, format: %s, expType: %s, expFmt: %s, kind: %s", t.Path, schType, format, t.Type, t.Format, val.Kind().String())
|
||||
|
||||
// check numerical types
|
||||
// TODO: check unsigned ints
|
||||
// TODO: check json.Number (see schema.go)
|
||||
|
|
@ -163,15 +194,20 @@ func (t *typeValidator) Validate(data interface{}) *Result {
|
|||
|
||||
if kind != reflect.String && kind != reflect.Slice && t.Format != "" && !(t.Type.Contains(schType) || format == t.Format || isFloatInt || isIntFloat || isLowerInt || isLowerFloat) {
|
||||
// TODO: test case
|
||||
return errorHelp.sErr(errors.InvalidType(t.Path, t.In, t.Format, format))
|
||||
return errorHelp.sErr(errors.InvalidType(t.Path, t.In, t.Format, format), t.Options.recycleResult)
|
||||
}
|
||||
|
||||
if !(t.Type.Contains(numberType) || t.Type.Contains(integerType)) && t.Format != "" && (kind == reflect.String || kind == reflect.Slice) {
|
||||
return result
|
||||
return emptyResult
|
||||
}
|
||||
|
||||
if !(t.Type.Contains(schType) || isFloatInt || isIntFloat) {
|
||||
return errorHelp.sErr(errors.InvalidType(t.Path, t.In, strings.Join(t.Type, ","), schType))
|
||||
return errorHelp.sErr(errors.InvalidType(t.Path, t.In, strings.Join(t.Type, ","), schType), t.Options.recycleResult)
|
||||
}
|
||||
return result
|
||||
|
||||
return emptyResult
|
||||
}
|
||||
|
||||
func (t *typeValidator) redeem() {
|
||||
pools.poolOfTypeValidators.RedeemValidator(t)
|
||||
}
|
||||
|
|
|
|||
936
vendor/github.com/go-openapi/validate/validator.go
generated
vendored
936
vendor/github.com/go-openapi/validate/validator.go
generated
vendored
File diff suppressed because it is too large
Load diff
16
vendor/github.com/go-sql-driver/mysql/AUTHORS
generated
vendored
16
vendor/github.com/go-sql-driver/mysql/AUTHORS
generated
vendored
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
Aaron Hopkins <go-sql-driver at die.net>
|
||||
Achille Roussel <achille.roussel at gmail.com>
|
||||
Aidan <aidan.liu at pingcap.com>
|
||||
Alex Snast <alexsn at fb.com>
|
||||
Alexey Palazhchenko <alexey.palazhchenko at gmail.com>
|
||||
Andrew Reid <andrew.reid at tixtrack.com>
|
||||
|
|
@ -20,12 +21,14 @@ Animesh Ray <mail.rayanimesh at gmail.com>
|
|||
Arne Hormann <arnehormann at gmail.com>
|
||||
Ariel Mashraki <ariel at mashraki.co.il>
|
||||
Asta Xie <xiemengjun at gmail.com>
|
||||
Brian Hendriks <brian at dolthub.com>
|
||||
Bulat Gaifullin <gaifullinbf at gmail.com>
|
||||
Caine Jette <jette at alum.mit.edu>
|
||||
Carlos Nieto <jose.carlos at menteslibres.net>
|
||||
Chris Kirkland <chriskirkland at github.com>
|
||||
Chris Moos <chris at tech9computers.com>
|
||||
Craig Wilson <craiggwilson at gmail.com>
|
||||
Daemonxiao <735462752 at qq.com>
|
||||
Daniel Montoya <dsmontoyam at gmail.com>
|
||||
Daniel Nichter <nil at codenode.com>
|
||||
Daniël van Eeden <git at myname.nl>
|
||||
|
|
@ -33,9 +36,11 @@ Dave Protasowski <dprotaso at gmail.com>
|
|||
DisposaBoy <disposaboy at dby.me>
|
||||
Egor Smolyakov <egorsmkv at gmail.com>
|
||||
Erwan Martin <hello at erwan.io>
|
||||
Evan Elias <evan at skeema.net>
|
||||
Evan Shaw <evan at vendhq.com>
|
||||
Frederick Mayle <frederickmayle at gmail.com>
|
||||
Gustavo Kristic <gkristic at gmail.com>
|
||||
Gusted <postmaster at gusted.xyz>
|
||||
Hajime Nakagami <nakagami at gmail.com>
|
||||
Hanno Braun <mail at hannobraun.com>
|
||||
Henri Yandell <flamefew at gmail.com>
|
||||
|
|
@ -47,8 +52,11 @@ INADA Naoki <songofacandy at gmail.com>
|
|||
Jacek Szwec <szwec.jacek at gmail.com>
|
||||
James Harr <james.harr at gmail.com>
|
||||
Janek Vedock <janekvedock at comcast.net>
|
||||
Jason Ng <oblitorum at gmail.com>
|
||||
Jean-Yves Pellé <jy at pelle.link>
|
||||
Jeff Hodges <jeff at somethingsimilar.com>
|
||||
Jeffrey Charles <jeffreycharles at gmail.com>
|
||||
Jennifer Purevsuren <jennifer at dolthub.com>
|
||||
Jerome Meyer <jxmeyer at gmail.com>
|
||||
Jiajia Zhong <zhong2plus at gmail.com>
|
||||
Jian Zhen <zhenjl at gmail.com>
|
||||
|
|
@ -74,9 +82,11 @@ Maciej Zimnoch <maciej.zimnoch at codilime.com>
|
|||
Michael Woolnough <michael.woolnough at gmail.com>
|
||||
Nathanial Murphy <nathanial.murphy at gmail.com>
|
||||
Nicola Peduzzi <thenikso at gmail.com>
|
||||
Oliver Bone <owbone at github.com>
|
||||
Olivier Mengué <dolmen at cpan.org>
|
||||
oscarzhao <oscarzhaosl at gmail.com>
|
||||
Paul Bonser <misterpib at gmail.com>
|
||||
Paulius Lozys <pauliuslozys at gmail.com>
|
||||
Peter Schultz <peter.schultz at classmarkets.com>
|
||||
Phil Porada <philporada at gmail.com>
|
||||
Rebecca Chin <rchin at pivotal.io>
|
||||
|
|
@ -95,6 +105,7 @@ Stan Putrya <root.vagner at gmail.com>
|
|||
Stanley Gunawan <gunawan.stanley at gmail.com>
|
||||
Steven Hartland <steven.hartland at multiplay.co.uk>
|
||||
Tan Jinhua <312841925 at qq.com>
|
||||
Tetsuro Aoki <t.aoki1130 at gmail.com>
|
||||
Thomas Wodarek <wodarekwebpage at gmail.com>
|
||||
Tim Ruffles <timruffles at gmail.com>
|
||||
Tom Jenkinson <tom at tjenkinson.me>
|
||||
|
|
@ -104,6 +115,7 @@ Xiangyu Hu <xiangyu.hu at outlook.com>
|
|||
Xiaobing Jiang <s7v7nislands at gmail.com>
|
||||
Xiuming Chen <cc at cxm.cc>
|
||||
Xuehong Chan <chanxuehong at gmail.com>
|
||||
Zhang Xiang <angwerzx at 126.com>
|
||||
Zhenye Xie <xiezhenye at gmail.com>
|
||||
Zhixin Wen <john.wenzhixin at gmail.com>
|
||||
Ziheng Lyu <zihenglv at gmail.com>
|
||||
|
|
@ -113,14 +125,18 @@ Ziheng Lyu <zihenglv at gmail.com>
|
|||
Barracuda Networks, Inc.
|
||||
Counting Ltd.
|
||||
DigitalOcean Inc.
|
||||
Dolthub Inc.
|
||||
dyves labs AG
|
||||
Facebook Inc.
|
||||
GitHub Inc.
|
||||
Google Inc.
|
||||
InfoSum Ltd.
|
||||
Keybase Inc.
|
||||
Microsoft Corp.
|
||||
Multiplay Ltd.
|
||||
Percona LLC
|
||||
PingCAP Inc.
|
||||
Pivotal Inc.
|
||||
Shattered Silicon Ltd.
|
||||
Stripe Inc.
|
||||
Zendesk Inc.
|
||||
|
|
|
|||
48
vendor/github.com/go-sql-driver/mysql/CHANGELOG.md
generated
vendored
48
vendor/github.com/go-sql-driver/mysql/CHANGELOG.md
generated
vendored
|
|
@ -1,3 +1,45 @@
|
|||
## Version 1.8.1 (2024-03-26)
|
||||
|
||||
Bugfixes:
|
||||
|
||||
- fix race condition when context is canceled in [#1562](https://github.com/go-sql-driver/mysql/pull/1562) and [#1570](https://github.com/go-sql-driver/mysql/pull/1570)
|
||||
|
||||
## Version 1.8.0 (2024-03-09)
|
||||
|
||||
Major Changes:
|
||||
|
||||
- Use `SET NAMES charset COLLATE collation`. by @methane in [#1437](https://github.com/go-sql-driver/mysql/pull/1437)
|
||||
- Older go-mysql-driver used `collation_id` in the handshake packet. But it caused collation mismatch in some situation.
|
||||
- If you don't specify charset nor collation, go-mysql-driver sends `SET NAMES utf8mb4` for new connection. This uses server's default collation for utf8mb4.
|
||||
- If you specify charset, go-mysql-driver sends `SET NAMES <charset>`. This uses the server's default collation for `<charset>`.
|
||||
- If you specify collation and/or charset, go-mysql-driver sends `SET NAMES charset COLLATE collation`.
|
||||
- PathEscape dbname in DSN. by @methane in [#1432](https://github.com/go-sql-driver/mysql/pull/1432)
|
||||
- This is backward incompatible in rare case. Check your DSN.
|
||||
- Drop Go 1.13-17 support by @methane in [#1420](https://github.com/go-sql-driver/mysql/pull/1420)
|
||||
- Use Go 1.18+
|
||||
- Parse numbers on text protocol too by @methane in [#1452](https://github.com/go-sql-driver/mysql/pull/1452)
|
||||
- When text protocol is used, go-mysql-driver passed bare `[]byte` to database/sql for avoid unnecessary allocation and conversion.
|
||||
- If user specified `*any` to `Scan()`, database/sql passed the `[]byte` into the target variable.
|
||||
- This confused users because most user doesn't know when text/binary protocol used.
|
||||
- go-mysql-driver 1.8 converts integer/float values into int64/double even in text protocol. This doesn't increase allocation compared to `[]byte` and conversion cost is negatable.
|
||||
- New options start using the Functional Option Pattern to avoid increasing technical debt in the Config object. Future version may introduce Functional Option for existing options, but not for now.
|
||||
- Make TimeTruncate functional option by @methane in [1552](https://github.com/go-sql-driver/mysql/pull/1552)
|
||||
- Add BeforeConnect callback to configuration object by @ItalyPaleAle in [#1469](https://github.com/go-sql-driver/mysql/pull/1469)
|
||||
|
||||
|
||||
Other changes:
|
||||
|
||||
- Adding DeregisterDialContext to prevent memory leaks with dialers we don't need anymore by @jypelle in https://github.com/go-sql-driver/mysql/pull/1422
|
||||
- Make logger configurable per connection by @frozenbonito in https://github.com/go-sql-driver/mysql/pull/1408
|
||||
- Fix ColumnType.DatabaseTypeName for mediumint unsigned by @evanelias in https://github.com/go-sql-driver/mysql/pull/1428
|
||||
- Add connection attributes by @Daemonxiao in https://github.com/go-sql-driver/mysql/pull/1389
|
||||
- Stop `ColumnTypeScanType()` from returning `sql.RawBytes` by @methane in https://github.com/go-sql-driver/mysql/pull/1424
|
||||
- Exec() now provides access to status of multiple statements. by @mherr-google in https://github.com/go-sql-driver/mysql/pull/1309
|
||||
- Allow to change (or disable) the default driver name for registration by @dolmen in https://github.com/go-sql-driver/mysql/pull/1499
|
||||
- Add default connection attribute '_server_host' by @oblitorum in https://github.com/go-sql-driver/mysql/pull/1506
|
||||
- QueryUnescape DSN ConnectionAttribute value by @zhangyangyu in https://github.com/go-sql-driver/mysql/pull/1470
|
||||
- Add client_ed25519 authentication by @Gusted in https://github.com/go-sql-driver/mysql/pull/1518
|
||||
|
||||
## Version 1.7.1 (2023-04-25)
|
||||
|
||||
Changes:
|
||||
|
|
@ -162,7 +204,7 @@ New Features:
|
|||
|
||||
- Enable microsecond resolution on TIME, DATETIME and TIMESTAMP (#249)
|
||||
- Support for returning table alias on Columns() (#289, #359, #382)
|
||||
- Placeholder interpolation, can be actived with the DSN parameter `interpolateParams=true` (#309, #318, #490)
|
||||
- Placeholder interpolation, can be activated with the DSN parameter `interpolateParams=true` (#309, #318, #490)
|
||||
- Support for uint64 parameters with high bit set (#332, #345)
|
||||
- Cleartext authentication plugin support (#327)
|
||||
- Exported ParseDSN function and the Config struct (#403, #419, #429)
|
||||
|
|
@ -206,7 +248,7 @@ Changes:
|
|||
- Also exported the MySQLWarning type
|
||||
- mysqlConn.Close returns the first error encountered instead of ignoring all errors
|
||||
- writePacket() automatically writes the packet size to the header
|
||||
- readPacket() uses an iterative approach instead of the recursive approach to merge splitted packets
|
||||
- readPacket() uses an iterative approach instead of the recursive approach to merge split packets
|
||||
|
||||
New Features:
|
||||
|
||||
|
|
@ -254,7 +296,7 @@ Bugfixes:
|
|||
|
||||
- Fixed MySQL 4.1 support: MySQL 4.1 sends packets with lengths which differ from the specification
|
||||
- Convert to DB timezone when inserting `time.Time`
|
||||
- Splitted packets (more than 16MB) are now merged correctly
|
||||
- Split packets (more than 16MB) are now merged correctly
|
||||
- Fixed false positive `io.EOF` errors when the data was fully read
|
||||
- Avoid panics on reuse of closed connections
|
||||
- Fixed empty string producing false nil values
|
||||
|
|
|
|||
78
vendor/github.com/go-sql-driver/mysql/README.md
generated
vendored
78
vendor/github.com/go-sql-driver/mysql/README.md
generated
vendored
|
|
@ -40,15 +40,23 @@ A MySQL-Driver for Go's [database/sql](https://golang.org/pkg/database/sql/) pac
|
|||
* Optional placeholder interpolation
|
||||
|
||||
## Requirements
|
||||
* Go 1.13 or higher. We aim to support the 3 latest versions of Go.
|
||||
* MySQL (4.1+), MariaDB, Percona Server, Google CloudSQL or Sphinx (2.2.3+)
|
||||
|
||||
* Go 1.19 or higher. We aim to support the 3 latest versions of Go.
|
||||
* MySQL (5.7+) and MariaDB (10.3+) are supported.
|
||||
* [TiDB](https://github.com/pingcap/tidb) is supported by PingCAP.
|
||||
* Do not ask questions about TiDB in our issue tracker or forum.
|
||||
* [Document](https://docs.pingcap.com/tidb/v6.1/dev-guide-sample-application-golang)
|
||||
* [Forum](https://ask.pingcap.com/)
|
||||
* go-mysql would work with Percona Server, Google CloudSQL or Sphinx (2.2.3+).
|
||||
* Maintainers won't support them. Do not expect issues are investigated and resolved by maintainers.
|
||||
* Investigate issues yourself and please send a pull request to fix it.
|
||||
|
||||
---------------------------------------
|
||||
|
||||
## Installation
|
||||
Simple install the package to your [$GOPATH](https://github.com/golang/go/wiki/GOPATH "GOPATH") with the [go tool](https://golang.org/cmd/go/ "go command") from shell:
|
||||
```bash
|
||||
$ go get -u github.com/go-sql-driver/mysql
|
||||
go get -u github.com/go-sql-driver/mysql
|
||||
```
|
||||
Make sure [Git is installed](https://git-scm.com/downloads) on your machine and in your system's `PATH`.
|
||||
|
||||
|
|
@ -114,6 +122,12 @@ This has the same effect as an empty DSN string:
|
|||
|
||||
```
|
||||
|
||||
`dbname` is escaped by [PathEscape()](https://pkg.go.dev/net/url#PathEscape) since v1.8.0. If your database name is `dbname/withslash`, it becomes:
|
||||
|
||||
```
|
||||
/dbname%2Fwithslash
|
||||
```
|
||||
|
||||
Alternatively, [Config.FormatDSN](https://godoc.org/github.com/go-sql-driver/mysql#Config.FormatDSN) can be used to create a DSN string by filling a struct.
|
||||
|
||||
#### Password
|
||||
|
|
@ -121,7 +135,7 @@ Passwords can consist of any character. Escaping is **not** necessary.
|
|||
|
||||
#### Protocol
|
||||
See [net.Dial](https://golang.org/pkg/net/#Dial) for more information which networks are available.
|
||||
In general you should use an Unix domain socket if available and TCP otherwise for best performance.
|
||||
In general you should use a Unix domain socket if available and TCP otherwise for best performance.
|
||||
|
||||
#### Address
|
||||
For TCP and UDP networks, addresses have the form `host[:port]`.
|
||||
|
|
@ -145,7 +159,7 @@ Default: false
|
|||
```
|
||||
|
||||
`allowAllFiles=true` disables the file allowlist for `LOAD DATA LOCAL INFILE` and allows *all* files.
|
||||
[*Might be insecure!*](http://dev.mysql.com/doc/refman/5.7/en/load-data-local.html)
|
||||
[*Might be insecure!*](https://dev.mysql.com/doc/refman/8.0/en/load-data.html#load-data-local)
|
||||
|
||||
##### `allowCleartextPasswords`
|
||||
|
||||
|
|
@ -194,10 +208,9 @@ Valid Values: <name>
|
|||
Default: none
|
||||
```
|
||||
|
||||
Sets the charset used for client-server interaction (`"SET NAMES <value>"`). If multiple charsets are set (separated by a comma), the following charset is used if setting the charset failes. This enables for example support for `utf8mb4` ([introduced in MySQL 5.5.3](http://dev.mysql.com/doc/refman/5.5/en/charset-unicode-utf8mb4.html)) with fallback to `utf8` for older servers (`charset=utf8mb4,utf8`).
|
||||
Sets the charset used for client-server interaction (`"SET NAMES <value>"`). If multiple charsets are set (separated by a comma), the following charset is used if setting the charset fails. This enables for example support for `utf8mb4` ([introduced in MySQL 5.5.3](http://dev.mysql.com/doc/refman/5.5/en/charset-unicode-utf8mb4.html)) with fallback to `utf8` for older servers (`charset=utf8mb4,utf8`).
|
||||
|
||||
Usage of the `charset` parameter is discouraged because it issues additional queries to the server.
|
||||
Unless you need the fallback behavior, please use `collation` instead.
|
||||
See also [Unicode Support](#unicode-support).
|
||||
|
||||
##### `checkConnLiveness`
|
||||
|
||||
|
|
@ -226,6 +239,7 @@ The default collation (`utf8mb4_general_ci`) is supported from MySQL 5.5. You s
|
|||
|
||||
Collations for charset "ucs2", "utf16", "utf16le", and "utf32" can not be used ([ref](https://dev.mysql.com/doc/refman/5.7/en/charset-connection.html#charset-connection-impermissible-client-charset)).
|
||||
|
||||
See also [Unicode Support](#unicode-support).
|
||||
|
||||
##### `clientFoundRows`
|
||||
|
||||
|
|
@ -279,6 +293,15 @@ Note that this sets the location for time.Time values but does not change MySQL'
|
|||
|
||||
Please keep in mind, that param values must be [url.QueryEscape](https://golang.org/pkg/net/url/#QueryEscape)'ed. Alternatively you can manually replace the `/` with `%2F`. For example `US/Pacific` would be `loc=US%2FPacific`.
|
||||
|
||||
##### `timeTruncate`
|
||||
|
||||
```
|
||||
Type: duration
|
||||
Default: 0
|
||||
```
|
||||
|
||||
[Truncate time values](https://pkg.go.dev/time#Duration.Truncate) to the specified duration. The value must be a decimal number with a unit suffix (*"ms"*, *"s"*, *"m"*, *"h"*), such as *"30s"*, *"0.5m"* or *"1m30s"*.
|
||||
|
||||
##### `maxAllowedPacket`
|
||||
```
|
||||
Type: decimal number
|
||||
|
|
@ -295,9 +318,25 @@ Valid Values: true, false
|
|||
Default: false
|
||||
```
|
||||
|
||||
Allow multiple statements in one query. While this allows batch queries, it also greatly increases the risk of SQL injections. Only the result of the first query is returned, all other results are silently discarded.
|
||||
Allow multiple statements in one query. This can be used to bach multiple queries. Use [Rows.NextResultSet()](https://pkg.go.dev/database/sql#Rows.NextResultSet) to get result of the second and subsequent queries.
|
||||
|
||||
When `multiStatements` is used, `?` parameters must only be used in the first statement.
|
||||
When `multiStatements` is used, `?` parameters must only be used in the first statement. [interpolateParams](#interpolateparams) can be used to avoid this limitation unless prepared statement is used explicitly.
|
||||
|
||||
It's possible to access the last inserted ID and number of affected rows for multiple statements by using `sql.Conn.Raw()` and the `mysql.Result`. For example:
|
||||
|
||||
```go
|
||||
conn, _ := db.Conn(ctx)
|
||||
conn.Raw(func(conn any) error {
|
||||
ex := conn.(driver.Execer)
|
||||
res, err := ex.Exec(`
|
||||
UPDATE point SET x = 1 WHERE y = 2;
|
||||
UPDATE point SET x = 2 WHERE y = 3;
|
||||
`, nil)
|
||||
// Both slices have 2 elements.
|
||||
log.Print(res.(mysql.Result).AllRowsAffected())
|
||||
log.Print(res.(mysql.Result).AllLastInsertIds())
|
||||
})
|
||||
```
|
||||
|
||||
##### `parseTime`
|
||||
|
||||
|
|
@ -393,6 +432,15 @@ Default: 0
|
|||
|
||||
I/O write timeout. The value must be a decimal number with a unit suffix (*"ms"*, *"s"*, *"m"*, *"h"*), such as *"30s"*, *"0.5m"* or *"1m30s"*.
|
||||
|
||||
##### `connectionAttributes`
|
||||
|
||||
```
|
||||
Type: comma-delimited string of user-defined "key:value" pairs
|
||||
Valid Values: (<name1>:<value1>,<name2>:<value2>,...)
|
||||
Default: none
|
||||
```
|
||||
|
||||
[Connection attributes](https://dev.mysql.com/doc/refman/8.0/en/performance-schema-connection-attribute-tables.html) are key-value pairs that application programs can pass to the server at connect time.
|
||||
|
||||
##### System Variables
|
||||
|
||||
|
|
@ -465,7 +513,7 @@ user:password@/
|
|||
The connection pool is managed by Go's database/sql package. For details on how to configure the size of the pool and how long connections stay in the pool see `*DB.SetMaxOpenConns`, `*DB.SetMaxIdleConns`, and `*DB.SetConnMaxLifetime` in the [database/sql documentation](https://golang.org/pkg/database/sql/). The read, write, and dial timeouts for each individual connection are configured with the DSN parameters [`readTimeout`](#readtimeout), [`writeTimeout`](#writetimeout), and [`timeout`](#timeout), respectively.
|
||||
|
||||
## `ColumnType` Support
|
||||
This driver supports the [`ColumnType` interface](https://golang.org/pkg/database/sql/#ColumnType) introduced in Go 1.8, with the exception of [`ColumnType.Length()`](https://golang.org/pkg/database/sql/#ColumnType.Length), which is currently not supported. All Unsigned database type names will be returned `UNSIGNED ` with `INT`, `TINYINT`, `SMALLINT`, `BIGINT`.
|
||||
This driver supports the [`ColumnType` interface](https://golang.org/pkg/database/sql/#ColumnType) introduced in Go 1.8, with the exception of [`ColumnType.Length()`](https://golang.org/pkg/database/sql/#ColumnType.Length), which is currently not supported. All Unsigned database type names will be returned `UNSIGNED ` with `INT`, `TINYINT`, `SMALLINT`, `MEDIUMINT`, `BIGINT`.
|
||||
|
||||
## `context.Context` Support
|
||||
Go 1.8 added `database/sql` support for `context.Context`. This driver supports query timeouts and cancellation via contexts.
|
||||
|
|
@ -478,7 +526,7 @@ For this feature you need direct access to the package. Therefore you must chang
|
|||
import "github.com/go-sql-driver/mysql"
|
||||
```
|
||||
|
||||
Files must be explicitly allowed by registering them with `mysql.RegisterLocalFile(filepath)` (recommended) or the allowlist check must be deactivated by using the DSN parameter `allowAllFiles=true` ([*Might be insecure!*](http://dev.mysql.com/doc/refman/5.7/en/load-data-local.html)).
|
||||
Files must be explicitly allowed by registering them with `mysql.RegisterLocalFile(filepath)` (recommended) or the allowlist check must be deactivated by using the DSN parameter `allowAllFiles=true` ([*Might be insecure!*](https://dev.mysql.com/doc/refman/8.0/en/load-data.html#load-data-local)).
|
||||
|
||||
To use a `io.Reader` a handler function must be registered with `mysql.RegisterReaderHandler(name, handler)` which returns a `io.Reader` or `io.ReadCloser`. The Reader is available with the filepath `Reader::<name>` then. Choose different names for different handlers and `DeregisterReaderHandler` when you don't need it anymore.
|
||||
|
||||
|
|
@ -496,9 +544,11 @@ However, many want to scan MySQL `DATE` and `DATETIME` values into `time.Time` v
|
|||
### Unicode support
|
||||
Since version 1.5 Go-MySQL-Driver automatically uses the collation ` utf8mb4_general_ci` by default.
|
||||
|
||||
Other collations / charsets can be set using the [`collation`](#collation) DSN parameter.
|
||||
Other charsets / collations can be set using the [`charset`](#charset) or [`collation`](#collation) DSN parameter.
|
||||
|
||||
Version 1.0 of the driver recommended adding `&charset=utf8` (alias for `SET NAMES utf8`) to the DSN to enable proper UTF-8 support. This is not necessary anymore. The [`collation`](#collation) parameter should be preferred to set another collation / charset than the default.
|
||||
- When only the `charset` is specified, the `SET NAMES <charset>` query is sent and the server's default collation is used.
|
||||
- When both the `charset` and `collation` are specified, the `SET NAMES <charset> COLLATE <collation>` query is sent.
|
||||
- When only the `collation` is specified, the collation is specified in the protocol handshake and the `SET NAMES` query is not sent. This can save one roundtrip, but note that the server may ignore the specified collation silently and use the server's default charset/collation instead.
|
||||
|
||||
See http://dev.mysql.com/doc/refman/8.0/en/charset-unicode.html for more details on MySQL's Unicode support.
|
||||
|
||||
|
|
|
|||
63
vendor/github.com/go-sql-driver/mysql/auth.go
generated
vendored
63
vendor/github.com/go-sql-driver/mysql/auth.go
generated
vendored
|
|
@ -13,10 +13,13 @@ import (
|
|||
"crypto/rsa"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"filippo.io/edwards25519"
|
||||
)
|
||||
|
||||
// server pub keys registry
|
||||
|
|
@ -33,7 +36,7 @@ var (
|
|||
// Note: The provided rsa.PublicKey instance is exclusively owned by the driver
|
||||
// after registering it and may not be modified.
|
||||
//
|
||||
// data, err := ioutil.ReadFile("mykey.pem")
|
||||
// data, err := os.ReadFile("mykey.pem")
|
||||
// if err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
|
|
@ -225,6 +228,44 @@ func encryptPassword(password string, seed []byte, pub *rsa.PublicKey) ([]byte,
|
|||
return rsa.EncryptOAEP(sha1, rand.Reader, pub, plain, nil)
|
||||
}
|
||||
|
||||
// authEd25519 does ed25519 authentication used by MariaDB.
|
||||
func authEd25519(scramble []byte, password string) ([]byte, error) {
|
||||
// Derived from https://github.com/MariaDB/server/blob/d8e6bb00888b1f82c031938f4c8ac5d97f6874c3/plugin/auth_ed25519/ref10/sign.c
|
||||
// Code style is from https://cs.opensource.google/go/go/+/refs/tags/go1.21.5:src/crypto/ed25519/ed25519.go;l=207
|
||||
h := sha512.Sum512([]byte(password))
|
||||
|
||||
s, err := edwards25519.NewScalar().SetBytesWithClamping(h[:32])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
A := (&edwards25519.Point{}).ScalarBaseMult(s)
|
||||
|
||||
mh := sha512.New()
|
||||
mh.Write(h[32:])
|
||||
mh.Write(scramble)
|
||||
messageDigest := mh.Sum(nil)
|
||||
r, err := edwards25519.NewScalar().SetUniformBytes(messageDigest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
R := (&edwards25519.Point{}).ScalarBaseMult(r)
|
||||
|
||||
kh := sha512.New()
|
||||
kh.Write(R.Bytes())
|
||||
kh.Write(A.Bytes())
|
||||
kh.Write(scramble)
|
||||
hramDigest := kh.Sum(nil)
|
||||
k, err := edwards25519.NewScalar().SetUniformBytes(hramDigest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
S := k.MultiplyAdd(k, s, r)
|
||||
|
||||
return append(R.Bytes(), S.Bytes()...), nil
|
||||
}
|
||||
|
||||
func (mc *mysqlConn) sendEncryptedPassword(seed []byte, pub *rsa.PublicKey) error {
|
||||
enc, err := encryptPassword(mc.cfg.Passwd, seed, pub)
|
||||
if err != nil {
|
||||
|
|
@ -290,8 +331,14 @@ func (mc *mysqlConn) auth(authData []byte, plugin string) ([]byte, error) {
|
|||
enc, err := encryptPassword(mc.cfg.Passwd, authData, pubKey)
|
||||
return enc, err
|
||||
|
||||
case "client_ed25519":
|
||||
if len(authData) != 32 {
|
||||
return nil, ErrMalformPkt
|
||||
}
|
||||
return authEd25519(authData, mc.cfg.Passwd)
|
||||
|
||||
default:
|
||||
errLog.Print("unknown auth plugin:", plugin)
|
||||
mc.log("unknown auth plugin:", plugin)
|
||||
return nil, ErrUnknownPlugin
|
||||
}
|
||||
}
|
||||
|
|
@ -338,7 +385,7 @@ func (mc *mysqlConn) handleAuthResult(oldAuthData []byte, plugin string) error {
|
|||
|
||||
switch plugin {
|
||||
|
||||
// https://insidemysql.com/preparing-your-community-connector-for-mysql-8-part-2-sha256/
|
||||
// https://dev.mysql.com/blog-archive/preparing-your-community-connector-for-mysql-8-part-2-sha256/
|
||||
case "caching_sha2_password":
|
||||
switch len(authData) {
|
||||
case 0:
|
||||
|
|
@ -346,7 +393,7 @@ func (mc *mysqlConn) handleAuthResult(oldAuthData []byte, plugin string) error {
|
|||
case 1:
|
||||
switch authData[0] {
|
||||
case cachingSha2PasswordFastAuthSuccess:
|
||||
if err = mc.readResultOK(); err == nil {
|
||||
if err = mc.resultUnchanged().readResultOK(); err == nil {
|
||||
return nil // auth successful
|
||||
}
|
||||
|
||||
|
|
@ -376,13 +423,13 @@ func (mc *mysqlConn) handleAuthResult(oldAuthData []byte, plugin string) error {
|
|||
}
|
||||
|
||||
if data[0] != iAuthMoreData {
|
||||
return fmt.Errorf("unexpect resp from server for caching_sha2_password perform full authentication")
|
||||
return fmt.Errorf("unexpected resp from server for caching_sha2_password, perform full authentication")
|
||||
}
|
||||
|
||||
// parse public key
|
||||
block, rest := pem.Decode(data[1:])
|
||||
if block == nil {
|
||||
return fmt.Errorf("No Pem data found, data: %s", rest)
|
||||
return fmt.Errorf("no pem data found, data: %s", rest)
|
||||
}
|
||||
pkix, err := x509.ParsePKIXPublicKey(block.Bytes)
|
||||
if err != nil {
|
||||
|
|
@ -397,7 +444,7 @@ func (mc *mysqlConn) handleAuthResult(oldAuthData []byte, plugin string) error {
|
|||
return err
|
||||
}
|
||||
}
|
||||
return mc.readResultOK()
|
||||
return mc.resultUnchanged().readResultOK()
|
||||
|
||||
default:
|
||||
return ErrMalformPkt
|
||||
|
|
@ -426,7 +473,7 @@ func (mc *mysqlConn) handleAuthResult(oldAuthData []byte, plugin string) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return mc.readResultOK()
|
||||
return mc.resultUnchanged().readResultOK()
|
||||
}
|
||||
|
||||
default:
|
||||
|
|
|
|||
2
vendor/github.com/go-sql-driver/mysql/collations.go
generated
vendored
2
vendor/github.com/go-sql-driver/mysql/collations.go
generated
vendored
|
|
@ -9,7 +9,7 @@
|
|||
package mysql
|
||||
|
||||
const defaultCollation = "utf8mb4_general_ci"
|
||||
const binaryCollation = "binary"
|
||||
const binaryCollationID = 63
|
||||
|
||||
// A list of available collations mapped to the internal ID.
|
||||
// To update this map use the following MySQL query:
|
||||
|
|
|
|||
100
vendor/github.com/go-sql-driver/mysql/connection.go
generated
vendored
100
vendor/github.com/go-sql-driver/mysql/connection.go
generated
vendored
|
|
@ -23,10 +23,10 @@ import (
|
|||
type mysqlConn struct {
|
||||
buf buffer
|
||||
netConn net.Conn
|
||||
rawConn net.Conn // underlying connection when netConn is TLS connection.
|
||||
affectedRows uint64
|
||||
insertId uint64
|
||||
rawConn net.Conn // underlying connection when netConn is TLS connection.
|
||||
result mysqlResult // managed by clearResult() and handleOkPacket().
|
||||
cfg *Config
|
||||
connector *connector
|
||||
maxAllowedPacket int
|
||||
maxWriteSize int
|
||||
writeTimeout time.Duration
|
||||
|
|
@ -34,7 +34,6 @@ type mysqlConn struct {
|
|||
status statusFlag
|
||||
sequence uint8
|
||||
parseTime bool
|
||||
reset bool // set when the Go SQL package calls ResetSession
|
||||
|
||||
// for context support (Go 1.8+)
|
||||
watching bool
|
||||
|
|
@ -45,17 +44,27 @@ type mysqlConn struct {
|
|||
closed atomicBool // set when conn is closed, before closech is closed
|
||||
}
|
||||
|
||||
// Helper function to call per-connection logger.
|
||||
func (mc *mysqlConn) log(v ...any) {
|
||||
mc.cfg.Logger.Print(v...)
|
||||
}
|
||||
|
||||
// Handles parameters set in DSN after the connection is established
|
||||
func (mc *mysqlConn) handleParams() (err error) {
|
||||
var cmdSet strings.Builder
|
||||
|
||||
for param, val := range mc.cfg.Params {
|
||||
switch param {
|
||||
// Charset: character_set_connection, character_set_client, character_set_results
|
||||
case "charset":
|
||||
charsets := strings.Split(val, ",")
|
||||
for i := range charsets {
|
||||
for _, cs := range charsets {
|
||||
// ignore errors here - a charset may not exist
|
||||
err = mc.exec("SET NAMES " + charsets[i])
|
||||
if mc.cfg.Collation != "" {
|
||||
err = mc.exec("SET NAMES " + cs + " COLLATE " + mc.cfg.Collation)
|
||||
} else {
|
||||
err = mc.exec("SET NAMES " + cs)
|
||||
}
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
|
|
@ -68,7 +77,7 @@ func (mc *mysqlConn) handleParams() (err error) {
|
|||
default:
|
||||
if cmdSet.Len() == 0 {
|
||||
// Heuristic: 29 chars for each other key=value to reduce reallocations
|
||||
cmdSet.Grow(4 + len(param) + 1 + len(val) + 30*(len(mc.cfg.Params)-1))
|
||||
cmdSet.Grow(4 + len(param) + 3 + len(val) + 30*(len(mc.cfg.Params)-1))
|
||||
cmdSet.WriteString("SET ")
|
||||
} else {
|
||||
cmdSet.WriteString(", ")
|
||||
|
|
@ -105,7 +114,7 @@ func (mc *mysqlConn) Begin() (driver.Tx, error) {
|
|||
|
||||
func (mc *mysqlConn) begin(readOnly bool) (driver.Tx, error) {
|
||||
if mc.closed.Load() {
|
||||
errLog.Print(ErrInvalidConn)
|
||||
mc.log(ErrInvalidConn)
|
||||
return nil, driver.ErrBadConn
|
||||
}
|
||||
var q string
|
||||
|
|
@ -128,7 +137,7 @@ func (mc *mysqlConn) Close() (err error) {
|
|||
}
|
||||
|
||||
mc.cleanup()
|
||||
|
||||
mc.clearResult()
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -143,12 +152,16 @@ func (mc *mysqlConn) cleanup() {
|
|||
|
||||
// Makes cleanup idempotent
|
||||
close(mc.closech)
|
||||
if mc.netConn == nil {
|
||||
conn := mc.rawConn
|
||||
if conn == nil {
|
||||
return
|
||||
}
|
||||
if err := mc.netConn.Close(); err != nil {
|
||||
errLog.Print(err)
|
||||
if err := conn.Close(); err != nil {
|
||||
mc.log(err)
|
||||
}
|
||||
// This function can be called from multiple goroutines.
|
||||
// So we can not mc.clearResult() here.
|
||||
// Caller should do it if they are in safe goroutine.
|
||||
}
|
||||
|
||||
func (mc *mysqlConn) error() error {
|
||||
|
|
@ -163,14 +176,14 @@ func (mc *mysqlConn) error() error {
|
|||
|
||||
func (mc *mysqlConn) Prepare(query string) (driver.Stmt, error) {
|
||||
if mc.closed.Load() {
|
||||
errLog.Print(ErrInvalidConn)
|
||||
mc.log(ErrInvalidConn)
|
||||
return nil, driver.ErrBadConn
|
||||
}
|
||||
// Send command
|
||||
err := mc.writeCommandPacketStr(comStmtPrepare, query)
|
||||
if err != nil {
|
||||
// STMT_PREPARE is safe to retry. So we can return ErrBadConn here.
|
||||
errLog.Print(err)
|
||||
mc.log(err)
|
||||
return nil, driver.ErrBadConn
|
||||
}
|
||||
|
||||
|
|
@ -204,7 +217,7 @@ func (mc *mysqlConn) interpolateParams(query string, args []driver.Value) (strin
|
|||
buf, err := mc.buf.takeCompleteBuffer()
|
||||
if err != nil {
|
||||
// can not take the buffer. Something must be wrong with the connection
|
||||
errLog.Print(err)
|
||||
mc.log(err)
|
||||
return "", ErrInvalidConn
|
||||
}
|
||||
buf = buf[:0]
|
||||
|
|
@ -246,7 +259,7 @@ func (mc *mysqlConn) interpolateParams(query string, args []driver.Value) (strin
|
|||
buf = append(buf, "'0000-00-00'"...)
|
||||
} else {
|
||||
buf = append(buf, '\'')
|
||||
buf, err = appendDateTime(buf, v.In(mc.cfg.Loc))
|
||||
buf, err = appendDateTime(buf, v.In(mc.cfg.Loc), mc.cfg.timeTruncate)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
|
@ -296,7 +309,7 @@ func (mc *mysqlConn) interpolateParams(query string, args []driver.Value) (strin
|
|||
|
||||
func (mc *mysqlConn) Exec(query string, args []driver.Value) (driver.Result, error) {
|
||||
if mc.closed.Load() {
|
||||
errLog.Print(ErrInvalidConn)
|
||||
mc.log(ErrInvalidConn)
|
||||
return nil, driver.ErrBadConn
|
||||
}
|
||||
if len(args) != 0 {
|
||||
|
|
@ -310,28 +323,25 @@ func (mc *mysqlConn) Exec(query string, args []driver.Value) (driver.Result, err
|
|||
}
|
||||
query = prepared
|
||||
}
|
||||
mc.affectedRows = 0
|
||||
mc.insertId = 0
|
||||
|
||||
err := mc.exec(query)
|
||||
if err == nil {
|
||||
return &mysqlResult{
|
||||
affectedRows: int64(mc.affectedRows),
|
||||
insertId: int64(mc.insertId),
|
||||
}, err
|
||||
copied := mc.result
|
||||
return &copied, err
|
||||
}
|
||||
return nil, mc.markBadConn(err)
|
||||
}
|
||||
|
||||
// Internal function to execute commands
|
||||
func (mc *mysqlConn) exec(query string) error {
|
||||
handleOk := mc.clearResult()
|
||||
// Send command
|
||||
if err := mc.writeCommandPacketStr(comQuery, query); err != nil {
|
||||
return mc.markBadConn(err)
|
||||
}
|
||||
|
||||
// Read Result
|
||||
resLen, err := mc.readResultSetHeaderPacket()
|
||||
resLen, err := handleOk.readResultSetHeaderPacket()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -348,7 +358,7 @@ func (mc *mysqlConn) exec(query string) error {
|
|||
}
|
||||
}
|
||||
|
||||
return mc.discardResults()
|
||||
return handleOk.discardResults()
|
||||
}
|
||||
|
||||
func (mc *mysqlConn) Query(query string, args []driver.Value) (driver.Rows, error) {
|
||||
|
|
@ -356,8 +366,10 @@ func (mc *mysqlConn) Query(query string, args []driver.Value) (driver.Rows, erro
|
|||
}
|
||||
|
||||
func (mc *mysqlConn) query(query string, args []driver.Value) (*textRows, error) {
|
||||
handleOk := mc.clearResult()
|
||||
|
||||
if mc.closed.Load() {
|
||||
errLog.Print(ErrInvalidConn)
|
||||
mc.log(ErrInvalidConn)
|
||||
return nil, driver.ErrBadConn
|
||||
}
|
||||
if len(args) != 0 {
|
||||
|
|
@ -376,7 +388,7 @@ func (mc *mysqlConn) query(query string, args []driver.Value) (*textRows, error)
|
|||
if err == nil {
|
||||
// Read Result
|
||||
var resLen int
|
||||
resLen, err = mc.readResultSetHeaderPacket()
|
||||
resLen, err = handleOk.readResultSetHeaderPacket()
|
||||
if err == nil {
|
||||
rows := new(textRows)
|
||||
rows.mc = mc
|
||||
|
|
@ -404,12 +416,13 @@ func (mc *mysqlConn) query(query string, args []driver.Value) (*textRows, error)
|
|||
// The returned byte slice is only valid until the next read
|
||||
func (mc *mysqlConn) getSystemVar(name string) ([]byte, error) {
|
||||
// Send command
|
||||
handleOk := mc.clearResult()
|
||||
if err := mc.writeCommandPacketStr(comQuery, "SELECT @@"+name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Read Result
|
||||
resLen, err := mc.readResultSetHeaderPacket()
|
||||
resLen, err := handleOk.readResultSetHeaderPacket()
|
||||
if err == nil {
|
||||
rows := new(textRows)
|
||||
rows.mc = mc
|
||||
|
|
@ -451,7 +464,7 @@ func (mc *mysqlConn) finish() {
|
|||
// Ping implements driver.Pinger interface
|
||||
func (mc *mysqlConn) Ping(ctx context.Context) (err error) {
|
||||
if mc.closed.Load() {
|
||||
errLog.Print(ErrInvalidConn)
|
||||
mc.log(ErrInvalidConn)
|
||||
return driver.ErrBadConn
|
||||
}
|
||||
|
||||
|
|
@ -460,11 +473,12 @@ func (mc *mysqlConn) Ping(ctx context.Context) (err error) {
|
|||
}
|
||||
defer mc.finish()
|
||||
|
||||
handleOk := mc.clearResult()
|
||||
if err = mc.writeCommandPacket(comPing); err != nil {
|
||||
return mc.markBadConn(err)
|
||||
}
|
||||
|
||||
return mc.readResultOK()
|
||||
return handleOk.readResultOK()
|
||||
}
|
||||
|
||||
// BeginTx implements driver.ConnBeginTx interface
|
||||
|
|
@ -639,7 +653,31 @@ func (mc *mysqlConn) ResetSession(ctx context.Context) error {
|
|||
if mc.closed.Load() {
|
||||
return driver.ErrBadConn
|
||||
}
|
||||
mc.reset = true
|
||||
|
||||
// Perform a stale connection check. We only perform this check for
|
||||
// the first query on a connection that has been checked out of the
|
||||
// connection pool: a fresh connection from the pool is more likely
|
||||
// to be stale, and it has not performed any previous writes that
|
||||
// could cause data corruption, so it's safe to return ErrBadConn
|
||||
// if the check fails.
|
||||
if mc.cfg.CheckConnLiveness {
|
||||
conn := mc.netConn
|
||||
if mc.rawConn != nil {
|
||||
conn = mc.rawConn
|
||||
}
|
||||
var err error
|
||||
if mc.cfg.ReadTimeout != 0 {
|
||||
err = conn.SetReadDeadline(time.Now().Add(mc.cfg.ReadTimeout))
|
||||
}
|
||||
if err == nil {
|
||||
err = connCheck(conn)
|
||||
}
|
||||
if err != nil {
|
||||
mc.log("closing bad idle connection: ", err)
|
||||
return driver.ErrBadConn
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
67
vendor/github.com/go-sql-driver/mysql/connector.go
generated
vendored
67
vendor/github.com/go-sql-driver/mysql/connector.go
generated
vendored
|
|
@ -12,10 +12,53 @@ import (
|
|||
"context"
|
||||
"database/sql/driver"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type connector struct {
|
||||
cfg *Config // immutable private copy.
|
||||
cfg *Config // immutable private copy.
|
||||
encodedAttributes string // Encoded connection attributes.
|
||||
}
|
||||
|
||||
func encodeConnectionAttributes(cfg *Config) string {
|
||||
connAttrsBuf := make([]byte, 0)
|
||||
|
||||
// default connection attributes
|
||||
connAttrsBuf = appendLengthEncodedString(connAttrsBuf, connAttrClientName)
|
||||
connAttrsBuf = appendLengthEncodedString(connAttrsBuf, connAttrClientNameValue)
|
||||
connAttrsBuf = appendLengthEncodedString(connAttrsBuf, connAttrOS)
|
||||
connAttrsBuf = appendLengthEncodedString(connAttrsBuf, connAttrOSValue)
|
||||
connAttrsBuf = appendLengthEncodedString(connAttrsBuf, connAttrPlatform)
|
||||
connAttrsBuf = appendLengthEncodedString(connAttrsBuf, connAttrPlatformValue)
|
||||
connAttrsBuf = appendLengthEncodedString(connAttrsBuf, connAttrPid)
|
||||
connAttrsBuf = appendLengthEncodedString(connAttrsBuf, strconv.Itoa(os.Getpid()))
|
||||
serverHost, _, _ := net.SplitHostPort(cfg.Addr)
|
||||
if serverHost != "" {
|
||||
connAttrsBuf = appendLengthEncodedString(connAttrsBuf, connAttrServerHost)
|
||||
connAttrsBuf = appendLengthEncodedString(connAttrsBuf, serverHost)
|
||||
}
|
||||
|
||||
// user-defined connection attributes
|
||||
for _, connAttr := range strings.Split(cfg.ConnectionAttributes, ",") {
|
||||
k, v, found := strings.Cut(connAttr, ":")
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
connAttrsBuf = appendLengthEncodedString(connAttrsBuf, k)
|
||||
connAttrsBuf = appendLengthEncodedString(connAttrsBuf, v)
|
||||
}
|
||||
|
||||
return string(connAttrsBuf)
|
||||
}
|
||||
|
||||
func newConnector(cfg *Config) *connector {
|
||||
encodedAttributes := encodeConnectionAttributes(cfg)
|
||||
return &connector{
|
||||
cfg: cfg,
|
||||
encodedAttributes: encodedAttributes,
|
||||
}
|
||||
}
|
||||
|
||||
// Connect implements driver.Connector interface.
|
||||
|
|
@ -23,12 +66,23 @@ type connector struct {
|
|||
func (c *connector) Connect(ctx context.Context) (driver.Conn, error) {
|
||||
var err error
|
||||
|
||||
// Invoke beforeConnect if present, with a copy of the configuration
|
||||
cfg := c.cfg
|
||||
if c.cfg.beforeConnect != nil {
|
||||
cfg = c.cfg.Clone()
|
||||
err = c.cfg.beforeConnect(ctx, cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// New mysqlConn
|
||||
mc := &mysqlConn{
|
||||
maxAllowedPacket: maxPacketSize,
|
||||
maxWriteSize: maxPacketSize - 1,
|
||||
closech: make(chan struct{}),
|
||||
cfg: c.cfg,
|
||||
cfg: cfg,
|
||||
connector: c,
|
||||
}
|
||||
mc.parseTime = mc.cfg.ParseTime
|
||||
|
||||
|
|
@ -48,18 +102,15 @@ func (c *connector) Connect(ctx context.Context) (driver.Conn, error) {
|
|||
nd := net.Dialer{Timeout: mc.cfg.Timeout}
|
||||
mc.netConn, err = nd.DialContext(ctx, mc.cfg.Net, mc.cfg.Addr)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mc.rawConn = mc.netConn
|
||||
|
||||
// Enable TCP Keepalives on TCP connections
|
||||
if tc, ok := mc.netConn.(*net.TCPConn); ok {
|
||||
if err := tc.SetKeepAlive(true); err != nil {
|
||||
// Don't send COM_QUIT before handshake.
|
||||
mc.netConn.Close()
|
||||
mc.netConn = nil
|
||||
return nil, err
|
||||
c.cfg.Logger.Print(err)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -92,7 +143,7 @@ func (c *connector) Connect(ctx context.Context) (driver.Conn, error) {
|
|||
authResp, err := mc.auth(authData, plugin)
|
||||
if err != nil {
|
||||
// try the default auth plugin, if using the requested plugin failed
|
||||
errLog.Print("could not use requested auth plugin '"+plugin+"': ", err.Error())
|
||||
c.cfg.Logger.Print("could not use requested auth plugin '"+plugin+"': ", err.Error())
|
||||
plugin = defaultAuthPlugin
|
||||
authResp, err = mc.auth(authData, plugin)
|
||||
if err != nil {
|
||||
|
|
|
|||
13
vendor/github.com/go-sql-driver/mysql/const.go
generated
vendored
13
vendor/github.com/go-sql-driver/mysql/const.go
generated
vendored
|
|
@ -8,12 +8,25 @@
|
|||
|
||||
package mysql
|
||||
|
||||
import "runtime"
|
||||
|
||||
const (
|
||||
defaultAuthPlugin = "mysql_native_password"
|
||||
defaultMaxAllowedPacket = 64 << 20 // 64 MiB. See https://github.com/go-sql-driver/mysql/issues/1355
|
||||
minProtocolVersion = 10
|
||||
maxPacketSize = 1<<24 - 1
|
||||
timeFormat = "2006-01-02 15:04:05.999999"
|
||||
|
||||
// Connection attributes
|
||||
// See https://dev.mysql.com/doc/refman/8.0/en/performance-schema-connection-attribute-tables.html#performance-schema-connection-attributes-available
|
||||
connAttrClientName = "_client_name"
|
||||
connAttrClientNameValue = "Go-MySQL-Driver"
|
||||
connAttrOS = "_os"
|
||||
connAttrOSValue = runtime.GOOS
|
||||
connAttrPlatform = "_platform"
|
||||
connAttrPlatformValue = runtime.GOARCH
|
||||
connAttrPid = "_pid"
|
||||
connAttrServerHost = "_server_host"
|
||||
)
|
||||
|
||||
// MySQL constants documentation:
|
||||
|
|
|
|||
27
vendor/github.com/go-sql-driver/mysql/driver.go
generated
vendored
27
vendor/github.com/go-sql-driver/mysql/driver.go
generated
vendored
|
|
@ -55,6 +55,15 @@ func RegisterDialContext(net string, dial DialContextFunc) {
|
|||
dials[net] = dial
|
||||
}
|
||||
|
||||
// DeregisterDialContext removes the custom dial function registered with the given net.
|
||||
func DeregisterDialContext(net string) {
|
||||
dialsLock.Lock()
|
||||
defer dialsLock.Unlock()
|
||||
if dials != nil {
|
||||
delete(dials, net)
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterDial registers a custom dial function. It can then be used by the
|
||||
// network address mynet(addr), where mynet is the registered new network.
|
||||
// addr is passed as a parameter to the dial function.
|
||||
|
|
@ -74,14 +83,18 @@ func (d MySQLDriver) Open(dsn string) (driver.Conn, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c := &connector{
|
||||
cfg: cfg,
|
||||
}
|
||||
c := newConnector(cfg)
|
||||
return c.Connect(context.Background())
|
||||
}
|
||||
|
||||
// This variable can be replaced with -ldflags like below:
|
||||
// go build "-ldflags=-X github.com/go-sql-driver/mysql.driverName=custom"
|
||||
var driverName = "mysql"
|
||||
|
||||
func init() {
|
||||
sql.Register("mysql", &MySQLDriver{})
|
||||
if driverName != "" {
|
||||
sql.Register(driverName, &MySQLDriver{})
|
||||
}
|
||||
}
|
||||
|
||||
// NewConnector returns new driver.Connector.
|
||||
|
|
@ -92,7 +105,7 @@ func NewConnector(cfg *Config) (driver.Connector, error) {
|
|||
if err := cfg.normalize(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &connector{cfg: cfg}, nil
|
||||
return newConnector(cfg), nil
|
||||
}
|
||||
|
||||
// OpenConnector implements driver.DriverContext.
|
||||
|
|
@ -101,7 +114,5 @@ func (d MySQLDriver) OpenConnector(dsn string) (driver.Connector, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &connector{
|
||||
cfg: cfg,
|
||||
}, nil
|
||||
return newConnector(cfg), nil
|
||||
}
|
||||
|
|
|
|||
128
vendor/github.com/go-sql-driver/mysql/dsn.go
generated
vendored
128
vendor/github.com/go-sql-driver/mysql/dsn.go
generated
vendored
|
|
@ -10,6 +10,7 @@ package mysql
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/rsa"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
|
|
@ -34,22 +35,27 @@ var (
|
|||
// If a new Config is created instead of being parsed from a DSN string,
|
||||
// the NewConfig function should be used, which sets default values.
|
||||
type Config struct {
|
||||
User string // Username
|
||||
Passwd string // Password (requires User)
|
||||
Net string // Network type
|
||||
Addr string // Network address (requires Net)
|
||||
DBName string // Database name
|
||||
Params map[string]string // Connection parameters
|
||||
Collation string // Connection collation
|
||||
Loc *time.Location // Location for time.Time values
|
||||
MaxAllowedPacket int // Max packet size allowed
|
||||
ServerPubKey string // Server public key name
|
||||
pubKey *rsa.PublicKey // Server public key
|
||||
TLSConfig string // TLS configuration name
|
||||
TLS *tls.Config // TLS configuration, its priority is higher than TLSConfig
|
||||
Timeout time.Duration // Dial timeout
|
||||
ReadTimeout time.Duration // I/O read timeout
|
||||
WriteTimeout time.Duration // I/O write timeout
|
||||
// non boolean fields
|
||||
|
||||
User string // Username
|
||||
Passwd string // Password (requires User)
|
||||
Net string // Network (e.g. "tcp", "tcp6", "unix". default: "tcp")
|
||||
Addr string // Address (default: "127.0.0.1:3306" for "tcp" and "/tmp/mysql.sock" for "unix")
|
||||
DBName string // Database name
|
||||
Params map[string]string // Connection parameters
|
||||
ConnectionAttributes string // Connection Attributes, comma-delimited string of user-defined "key:value" pairs
|
||||
Collation string // Connection collation
|
||||
Loc *time.Location // Location for time.Time values
|
||||
MaxAllowedPacket int // Max packet size allowed
|
||||
ServerPubKey string // Server public key name
|
||||
TLSConfig string // TLS configuration name
|
||||
TLS *tls.Config // TLS configuration, its priority is higher than TLSConfig
|
||||
Timeout time.Duration // Dial timeout
|
||||
ReadTimeout time.Duration // I/O read timeout
|
||||
WriteTimeout time.Duration // I/O write timeout
|
||||
Logger Logger // Logger
|
||||
|
||||
// boolean fields
|
||||
|
||||
AllowAllFiles bool // Allow all files to be used with LOAD DATA LOCAL INFILE
|
||||
AllowCleartextPasswords bool // Allows the cleartext client side plugin
|
||||
|
|
@ -63,17 +69,57 @@ type Config struct {
|
|||
MultiStatements bool // Allow multiple statements in one query
|
||||
ParseTime bool // Parse time values to time.Time
|
||||
RejectReadOnly bool // Reject read-only connections
|
||||
|
||||
// unexported fields. new options should be come here
|
||||
|
||||
beforeConnect func(context.Context, *Config) error // Invoked before a connection is established
|
||||
pubKey *rsa.PublicKey // Server public key
|
||||
timeTruncate time.Duration // Truncate time.Time values to the specified duration
|
||||
}
|
||||
|
||||
// Functional Options Pattern
|
||||
// https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis
|
||||
type Option func(*Config) error
|
||||
|
||||
// NewConfig creates a new Config and sets default values.
|
||||
func NewConfig() *Config {
|
||||
return &Config{
|
||||
Collation: defaultCollation,
|
||||
cfg := &Config{
|
||||
Loc: time.UTC,
|
||||
MaxAllowedPacket: defaultMaxAllowedPacket,
|
||||
Logger: defaultLogger,
|
||||
AllowNativePasswords: true,
|
||||
CheckConnLiveness: true,
|
||||
}
|
||||
|
||||
return cfg
|
||||
}
|
||||
|
||||
// Apply applies the given options to the Config object.
|
||||
func (c *Config) Apply(opts ...Option) error {
|
||||
for _, opt := range opts {
|
||||
err := opt(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TimeTruncate sets the time duration to truncate time.Time values in
|
||||
// query parameters.
|
||||
func TimeTruncate(d time.Duration) Option {
|
||||
return func(cfg *Config) error {
|
||||
cfg.timeTruncate = d
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// BeforeConnect sets the function to be invoked before a connection is established.
|
||||
func BeforeConnect(fn func(context.Context, *Config) error) Option {
|
||||
return func(cfg *Config) error {
|
||||
cfg.beforeConnect = fn
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (cfg *Config) Clone() *Config {
|
||||
|
|
@ -97,7 +143,7 @@ func (cfg *Config) Clone() *Config {
|
|||
}
|
||||
|
||||
func (cfg *Config) normalize() error {
|
||||
if cfg.InterpolateParams && unsafeCollations[cfg.Collation] {
|
||||
if cfg.InterpolateParams && cfg.Collation != "" && unsafeCollations[cfg.Collation] {
|
||||
return errInvalidDSNUnsafeCollation
|
||||
}
|
||||
|
||||
|
|
@ -153,6 +199,10 @@ func (cfg *Config) normalize() error {
|
|||
}
|
||||
}
|
||||
|
||||
if cfg.Logger == nil {
|
||||
cfg.Logger = defaultLogger
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -171,6 +221,8 @@ func writeDSNParam(buf *bytes.Buffer, hasParam *bool, name, value string) {
|
|||
|
||||
// FormatDSN formats the given Config into a DSN string which can be passed to
|
||||
// the driver.
|
||||
//
|
||||
// Note: use [NewConnector] and [database/sql.OpenDB] to open a connection from a [*Config].
|
||||
func (cfg *Config) FormatDSN() string {
|
||||
var buf bytes.Buffer
|
||||
|
||||
|
|
@ -196,7 +248,7 @@ func (cfg *Config) FormatDSN() string {
|
|||
|
||||
// /dbname
|
||||
buf.WriteByte('/')
|
||||
buf.WriteString(cfg.DBName)
|
||||
buf.WriteString(url.PathEscape(cfg.DBName))
|
||||
|
||||
// [?param1=value1&...¶mN=valueN]
|
||||
hasParam := false
|
||||
|
|
@ -230,7 +282,7 @@ func (cfg *Config) FormatDSN() string {
|
|||
writeDSNParam(&buf, &hasParam, "clientFoundRows", "true")
|
||||
}
|
||||
|
||||
if col := cfg.Collation; col != defaultCollation && len(col) > 0 {
|
||||
if col := cfg.Collation; col != "" {
|
||||
writeDSNParam(&buf, &hasParam, "collation", col)
|
||||
}
|
||||
|
||||
|
|
@ -254,6 +306,10 @@ func (cfg *Config) FormatDSN() string {
|
|||
writeDSNParam(&buf, &hasParam, "parseTime", "true")
|
||||
}
|
||||
|
||||
if cfg.timeTruncate > 0 {
|
||||
writeDSNParam(&buf, &hasParam, "timeTruncate", cfg.timeTruncate.String())
|
||||
}
|
||||
|
||||
if cfg.ReadTimeout > 0 {
|
||||
writeDSNParam(&buf, &hasParam, "readTimeout", cfg.ReadTimeout.String())
|
||||
}
|
||||
|
|
@ -358,7 +414,11 @@ func ParseDSN(dsn string) (cfg *Config, err error) {
|
|||
break
|
||||
}
|
||||
}
|
||||
cfg.DBName = dsn[i+1 : j]
|
||||
|
||||
dbname := dsn[i+1 : j]
|
||||
if cfg.DBName, err = url.PathUnescape(dbname); err != nil {
|
||||
return nil, fmt.Errorf("invalid dbname %q: %w", dbname, err)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
|
@ -378,13 +438,13 @@ func ParseDSN(dsn string) (cfg *Config, err error) {
|
|||
// Values must be url.QueryEscape'ed
|
||||
func parseDSNParams(cfg *Config, params string) (err error) {
|
||||
for _, v := range strings.Split(params, "&") {
|
||||
param := strings.SplitN(v, "=", 2)
|
||||
if len(param) != 2 {
|
||||
key, value, found := strings.Cut(v, "=")
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
|
||||
// cfg params
|
||||
switch value := param[1]; param[0] {
|
||||
switch key {
|
||||
// Disable INFILE allowlist / enable all files
|
||||
case "allowAllFiles":
|
||||
var isBool bool
|
||||
|
|
@ -490,6 +550,13 @@ func parseDSNParams(cfg *Config, params string) (err error) {
|
|||
return errors.New("invalid bool value: " + value)
|
||||
}
|
||||
|
||||
// time.Time truncation
|
||||
case "timeTruncate":
|
||||
cfg.timeTruncate, err = time.ParseDuration(value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid timeTruncate value: %v, error: %w", value, err)
|
||||
}
|
||||
|
||||
// I/O read Timeout
|
||||
case "readTimeout":
|
||||
cfg.ReadTimeout, err = time.ParseDuration(value)
|
||||
|
|
@ -554,13 +621,22 @@ func parseDSNParams(cfg *Config, params string) (err error) {
|
|||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Connection attributes
|
||||
case "connectionAttributes":
|
||||
connectionAttributes, err := url.QueryUnescape(value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid connectionAttributes value: %v", err)
|
||||
}
|
||||
cfg.ConnectionAttributes = connectionAttributes
|
||||
|
||||
default:
|
||||
// lazy init
|
||||
if cfg.Params == nil {
|
||||
cfg.Params = make(map[string]string)
|
||||
}
|
||||
|
||||
if cfg.Params[param[0]], err = url.QueryUnescape(value); err != nil {
|
||||
if cfg.Params[key], err = url.QueryUnescape(value); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
|
|||
16
vendor/github.com/go-sql-driver/mysql/errors.go
generated
vendored
16
vendor/github.com/go-sql-driver/mysql/errors.go
generated
vendored
|
|
@ -21,7 +21,7 @@ var (
|
|||
ErrMalformPkt = errors.New("malformed packet")
|
||||
ErrNoTLS = errors.New("TLS requested but server does not support TLS")
|
||||
ErrCleartextPassword = errors.New("this user requires clear text authentication. If you still want to use it, please add 'allowCleartextPasswords=1' to your DSN")
|
||||
ErrNativePassword = errors.New("this user requires mysql native password authentication.")
|
||||
ErrNativePassword = errors.New("this user requires mysql native password authentication")
|
||||
ErrOldPassword = errors.New("this user requires old password authentication. If you still want to use it, please add 'allowOldPasswords=1' to your DSN. See also https://github.com/go-sql-driver/mysql/wiki/old_passwords")
|
||||
ErrUnknownPlugin = errors.New("this authentication plugin is not supported")
|
||||
ErrOldProtocol = errors.New("MySQL server does not support required protocol 41+")
|
||||
|
|
@ -37,20 +37,26 @@ var (
|
|||
errBadConnNoWrite = errors.New("bad connection")
|
||||
)
|
||||
|
||||
var errLog = Logger(log.New(os.Stderr, "[mysql] ", log.Ldate|log.Ltime|log.Lshortfile))
|
||||
var defaultLogger = Logger(log.New(os.Stderr, "[mysql] ", log.Ldate|log.Ltime|log.Lshortfile))
|
||||
|
||||
// Logger is used to log critical error messages.
|
||||
type Logger interface {
|
||||
Print(v ...interface{})
|
||||
Print(v ...any)
|
||||
}
|
||||
|
||||
// SetLogger is used to set the logger for critical errors.
|
||||
// NopLogger is a nop implementation of the Logger interface.
|
||||
type NopLogger struct{}
|
||||
|
||||
// Print implements Logger interface.
|
||||
func (nl *NopLogger) Print(_ ...any) {}
|
||||
|
||||
// SetLogger is used to set the default logger for critical errors.
|
||||
// The initial logger is os.Stderr.
|
||||
func SetLogger(logger Logger) error {
|
||||
if logger == nil {
|
||||
return errors.New("logger is nil")
|
||||
}
|
||||
errLog = logger
|
||||
defaultLogger = logger
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
70
vendor/github.com/go-sql-driver/mysql/fields.go
generated
vendored
70
vendor/github.com/go-sql-driver/mysql/fields.go
generated
vendored
|
|
@ -18,7 +18,7 @@ func (mf *mysqlField) typeDatabaseName() string {
|
|||
case fieldTypeBit:
|
||||
return "BIT"
|
||||
case fieldTypeBLOB:
|
||||
if mf.charSet != collations[binaryCollation] {
|
||||
if mf.charSet != binaryCollationID {
|
||||
return "TEXT"
|
||||
}
|
||||
return "BLOB"
|
||||
|
|
@ -37,6 +37,9 @@ func (mf *mysqlField) typeDatabaseName() string {
|
|||
case fieldTypeGeometry:
|
||||
return "GEOMETRY"
|
||||
case fieldTypeInt24:
|
||||
if mf.flags&flagUnsigned != 0 {
|
||||
return "UNSIGNED MEDIUMINT"
|
||||
}
|
||||
return "MEDIUMINT"
|
||||
case fieldTypeJSON:
|
||||
return "JSON"
|
||||
|
|
@ -46,7 +49,7 @@ func (mf *mysqlField) typeDatabaseName() string {
|
|||
}
|
||||
return "INT"
|
||||
case fieldTypeLongBLOB:
|
||||
if mf.charSet != collations[binaryCollation] {
|
||||
if mf.charSet != binaryCollationID {
|
||||
return "LONGTEXT"
|
||||
}
|
||||
return "LONGBLOB"
|
||||
|
|
@ -56,7 +59,7 @@ func (mf *mysqlField) typeDatabaseName() string {
|
|||
}
|
||||
return "BIGINT"
|
||||
case fieldTypeMediumBLOB:
|
||||
if mf.charSet != collations[binaryCollation] {
|
||||
if mf.charSet != binaryCollationID {
|
||||
return "MEDIUMTEXT"
|
||||
}
|
||||
return "MEDIUMBLOB"
|
||||
|
|
@ -74,7 +77,12 @@ func (mf *mysqlField) typeDatabaseName() string {
|
|||
}
|
||||
return "SMALLINT"
|
||||
case fieldTypeString:
|
||||
if mf.charSet == collations[binaryCollation] {
|
||||
if mf.flags&flagEnum != 0 {
|
||||
return "ENUM"
|
||||
} else if mf.flags&flagSet != 0 {
|
||||
return "SET"
|
||||
}
|
||||
if mf.charSet == binaryCollationID {
|
||||
return "BINARY"
|
||||
}
|
||||
return "CHAR"
|
||||
|
|
@ -88,17 +96,17 @@ func (mf *mysqlField) typeDatabaseName() string {
|
|||
}
|
||||
return "TINYINT"
|
||||
case fieldTypeTinyBLOB:
|
||||
if mf.charSet != collations[binaryCollation] {
|
||||
if mf.charSet != binaryCollationID {
|
||||
return "TINYTEXT"
|
||||
}
|
||||
return "TINYBLOB"
|
||||
case fieldTypeVarChar:
|
||||
if mf.charSet == collations[binaryCollation] {
|
||||
if mf.charSet == binaryCollationID {
|
||||
return "VARBINARY"
|
||||
}
|
||||
return "VARCHAR"
|
||||
case fieldTypeVarString:
|
||||
if mf.charSet == collations[binaryCollation] {
|
||||
if mf.charSet == binaryCollationID {
|
||||
return "VARBINARY"
|
||||
}
|
||||
return "VARCHAR"
|
||||
|
|
@ -110,21 +118,23 @@ func (mf *mysqlField) typeDatabaseName() string {
|
|||
}
|
||||
|
||||
var (
|
||||
scanTypeFloat32 = reflect.TypeOf(float32(0))
|
||||
scanTypeFloat64 = reflect.TypeOf(float64(0))
|
||||
scanTypeInt8 = reflect.TypeOf(int8(0))
|
||||
scanTypeInt16 = reflect.TypeOf(int16(0))
|
||||
scanTypeInt32 = reflect.TypeOf(int32(0))
|
||||
scanTypeInt64 = reflect.TypeOf(int64(0))
|
||||
scanTypeNullFloat = reflect.TypeOf(sql.NullFloat64{})
|
||||
scanTypeNullInt = reflect.TypeOf(sql.NullInt64{})
|
||||
scanTypeNullTime = reflect.TypeOf(sql.NullTime{})
|
||||
scanTypeUint8 = reflect.TypeOf(uint8(0))
|
||||
scanTypeUint16 = reflect.TypeOf(uint16(0))
|
||||
scanTypeUint32 = reflect.TypeOf(uint32(0))
|
||||
scanTypeUint64 = reflect.TypeOf(uint64(0))
|
||||
scanTypeRawBytes = reflect.TypeOf(sql.RawBytes{})
|
||||
scanTypeUnknown = reflect.TypeOf(new(interface{}))
|
||||
scanTypeFloat32 = reflect.TypeOf(float32(0))
|
||||
scanTypeFloat64 = reflect.TypeOf(float64(0))
|
||||
scanTypeInt8 = reflect.TypeOf(int8(0))
|
||||
scanTypeInt16 = reflect.TypeOf(int16(0))
|
||||
scanTypeInt32 = reflect.TypeOf(int32(0))
|
||||
scanTypeInt64 = reflect.TypeOf(int64(0))
|
||||
scanTypeNullFloat = reflect.TypeOf(sql.NullFloat64{})
|
||||
scanTypeNullInt = reflect.TypeOf(sql.NullInt64{})
|
||||
scanTypeNullTime = reflect.TypeOf(sql.NullTime{})
|
||||
scanTypeUint8 = reflect.TypeOf(uint8(0))
|
||||
scanTypeUint16 = reflect.TypeOf(uint16(0))
|
||||
scanTypeUint32 = reflect.TypeOf(uint32(0))
|
||||
scanTypeUint64 = reflect.TypeOf(uint64(0))
|
||||
scanTypeString = reflect.TypeOf("")
|
||||
scanTypeNullString = reflect.TypeOf(sql.NullString{})
|
||||
scanTypeBytes = reflect.TypeOf([]byte{})
|
||||
scanTypeUnknown = reflect.TypeOf(new(any))
|
||||
)
|
||||
|
||||
type mysqlField struct {
|
||||
|
|
@ -187,12 +197,18 @@ func (mf *mysqlField) scanType() reflect.Type {
|
|||
}
|
||||
return scanTypeNullFloat
|
||||
|
||||
case fieldTypeBit, fieldTypeTinyBLOB, fieldTypeMediumBLOB, fieldTypeLongBLOB,
|
||||
fieldTypeBLOB, fieldTypeVarString, fieldTypeString, fieldTypeGeometry:
|
||||
if mf.charSet == binaryCollationID {
|
||||
return scanTypeBytes
|
||||
}
|
||||
fallthrough
|
||||
case fieldTypeDecimal, fieldTypeNewDecimal, fieldTypeVarChar,
|
||||
fieldTypeBit, fieldTypeEnum, fieldTypeSet, fieldTypeTinyBLOB,
|
||||
fieldTypeMediumBLOB, fieldTypeLongBLOB, fieldTypeBLOB,
|
||||
fieldTypeVarString, fieldTypeString, fieldTypeGeometry, fieldTypeJSON,
|
||||
fieldTypeTime:
|
||||
return scanTypeRawBytes
|
||||
fieldTypeEnum, fieldTypeSet, fieldTypeJSON, fieldTypeTime:
|
||||
if mf.flags&flagNotNULL != 0 {
|
||||
return scanTypeString
|
||||
}
|
||||
return scanTypeNullString
|
||||
|
||||
case fieldTypeDate, fieldTypeNewDate,
|
||||
fieldTypeTimestamp, fieldTypeDateTime:
|
||||
|
|
|
|||
25
vendor/github.com/go-sql-driver/mysql/fuzz.go
generated
vendored
25
vendor/github.com/go-sql-driver/mysql/fuzz.go
generated
vendored
|
|
@ -1,25 +0,0 @@
|
|||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package.
|
||||
//
|
||||
// Copyright 2020 The Go-MySQL-Driver Authors. All rights reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
//go:build gofuzz
|
||||
// +build gofuzz
|
||||
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
)
|
||||
|
||||
func Fuzz(data []byte) int {
|
||||
db, err := sql.Open("mysql", string(data))
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
db.Close()
|
||||
return 1
|
||||
}
|
||||
12
vendor/github.com/go-sql-driver/mysql/infile.go
generated
vendored
12
vendor/github.com/go-sql-driver/mysql/infile.go
generated
vendored
|
|
@ -93,7 +93,7 @@ func deferredClose(err *error, closer io.Closer) {
|
|||
|
||||
const defaultPacketSize = 16 * 1024 // 16KB is small enough for disk readahead and large enough for TCP
|
||||
|
||||
func (mc *mysqlConn) handleInFileRequest(name string) (err error) {
|
||||
func (mc *okHandler) handleInFileRequest(name string) (err error) {
|
||||
var rdr io.Reader
|
||||
var data []byte
|
||||
packetSize := defaultPacketSize
|
||||
|
|
@ -116,10 +116,10 @@ func (mc *mysqlConn) handleInFileRequest(name string) (err error) {
|
|||
defer deferredClose(&err, cl)
|
||||
}
|
||||
} else {
|
||||
err = fmt.Errorf("Reader '%s' is <nil>", name)
|
||||
err = fmt.Errorf("reader '%s' is <nil>", name)
|
||||
}
|
||||
} else {
|
||||
err = fmt.Errorf("Reader '%s' is not registered", name)
|
||||
err = fmt.Errorf("reader '%s' is not registered", name)
|
||||
}
|
||||
} else { // File
|
||||
name = strings.Trim(name, `"`)
|
||||
|
|
@ -154,7 +154,7 @@ func (mc *mysqlConn) handleInFileRequest(name string) (err error) {
|
|||
for err == nil {
|
||||
n, err = rdr.Read(data[4:])
|
||||
if n > 0 {
|
||||
if ioErr := mc.writePacket(data[:4+n]); ioErr != nil {
|
||||
if ioErr := mc.conn().writePacket(data[:4+n]); ioErr != nil {
|
||||
return ioErr
|
||||
}
|
||||
}
|
||||
|
|
@ -168,7 +168,7 @@ func (mc *mysqlConn) handleInFileRequest(name string) (err error) {
|
|||
if data == nil {
|
||||
data = make([]byte, 4)
|
||||
}
|
||||
if ioErr := mc.writePacket(data[:4]); ioErr != nil {
|
||||
if ioErr := mc.conn().writePacket(data[:4]); ioErr != nil {
|
||||
return ioErr
|
||||
}
|
||||
|
||||
|
|
@ -177,6 +177,6 @@ func (mc *mysqlConn) handleInFileRequest(name string) (err error) {
|
|||
return mc.readResultOK()
|
||||
}
|
||||
|
||||
mc.readPacket()
|
||||
mc.conn().readPacket()
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
4
vendor/github.com/go-sql-driver/mysql/nulltime.go
generated
vendored
4
vendor/github.com/go-sql-driver/mysql/nulltime.go
generated
vendored
|
|
@ -38,7 +38,7 @@ type NullTime sql.NullTime
|
|||
// Scan implements the Scanner interface.
|
||||
// The value type must be time.Time or string / []byte (formatted time-string),
|
||||
// otherwise Scan fails.
|
||||
func (nt *NullTime) Scan(value interface{}) (err error) {
|
||||
func (nt *NullTime) Scan(value any) (err error) {
|
||||
if value == nil {
|
||||
nt.Time, nt.Valid = time.Time{}, false
|
||||
return
|
||||
|
|
@ -59,7 +59,7 @@ func (nt *NullTime) Scan(value interface{}) (err error) {
|
|||
}
|
||||
|
||||
nt.Valid = false
|
||||
return fmt.Errorf("Can't convert %T to time.Time", value)
|
||||
return fmt.Errorf("can't convert %T to time.Time", value)
|
||||
}
|
||||
|
||||
// Value implements the driver Valuer interface.
|
||||
|
|
|
|||
209
vendor/github.com/go-sql-driver/mysql/packets.go
generated
vendored
209
vendor/github.com/go-sql-driver/mysql/packets.go
generated
vendored
|
|
@ -14,10 +14,10 @@ import (
|
|||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
|
@ -34,7 +34,7 @@ func (mc *mysqlConn) readPacket() ([]byte, error) {
|
|||
if cerr := mc.canceled.Value(); cerr != nil {
|
||||
return nil, cerr
|
||||
}
|
||||
errLog.Print(err)
|
||||
mc.log(err)
|
||||
mc.Close()
|
||||
return nil, ErrInvalidConn
|
||||
}
|
||||
|
|
@ -44,6 +44,7 @@ func (mc *mysqlConn) readPacket() ([]byte, error) {
|
|||
|
||||
// check packet sync [8 bit]
|
||||
if data[3] != mc.sequence {
|
||||
mc.Close()
|
||||
if data[3] > mc.sequence {
|
||||
return nil, ErrPktSyncMul
|
||||
}
|
||||
|
|
@ -56,7 +57,7 @@ func (mc *mysqlConn) readPacket() ([]byte, error) {
|
|||
if pktLen == 0 {
|
||||
// there was no previous packet
|
||||
if prevData == nil {
|
||||
errLog.Print(ErrMalformPkt)
|
||||
mc.log(ErrMalformPkt)
|
||||
mc.Close()
|
||||
return nil, ErrInvalidConn
|
||||
}
|
||||
|
|
@ -70,7 +71,7 @@ func (mc *mysqlConn) readPacket() ([]byte, error) {
|
|||
if cerr := mc.canceled.Value(); cerr != nil {
|
||||
return nil, cerr
|
||||
}
|
||||
errLog.Print(err)
|
||||
mc.log(err)
|
||||
mc.Close()
|
||||
return nil, ErrInvalidConn
|
||||
}
|
||||
|
|
@ -97,34 +98,6 @@ func (mc *mysqlConn) writePacket(data []byte) error {
|
|||
return ErrPktTooLarge
|
||||
}
|
||||
|
||||
// Perform a stale connection check. We only perform this check for
|
||||
// the first query on a connection that has been checked out of the
|
||||
// connection pool: a fresh connection from the pool is more likely
|
||||
// to be stale, and it has not performed any previous writes that
|
||||
// could cause data corruption, so it's safe to return ErrBadConn
|
||||
// if the check fails.
|
||||
if mc.reset {
|
||||
mc.reset = false
|
||||
conn := mc.netConn
|
||||
if mc.rawConn != nil {
|
||||
conn = mc.rawConn
|
||||
}
|
||||
var err error
|
||||
if mc.cfg.CheckConnLiveness {
|
||||
if mc.cfg.ReadTimeout != 0 {
|
||||
err = conn.SetReadDeadline(time.Now().Add(mc.cfg.ReadTimeout))
|
||||
}
|
||||
if err == nil {
|
||||
err = connCheck(conn)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
errLog.Print("closing bad idle connection: ", err)
|
||||
mc.Close()
|
||||
return driver.ErrBadConn
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
var size int
|
||||
if pktLen >= maxPacketSize {
|
||||
|
|
@ -161,7 +134,7 @@ func (mc *mysqlConn) writePacket(data []byte) error {
|
|||
// Handle error
|
||||
if err == nil { // n != len(data)
|
||||
mc.cleanup()
|
||||
errLog.Print(ErrMalformPkt)
|
||||
mc.log(ErrMalformPkt)
|
||||
} else {
|
||||
if cerr := mc.canceled.Value(); cerr != nil {
|
||||
return cerr
|
||||
|
|
@ -171,7 +144,7 @@ func (mc *mysqlConn) writePacket(data []byte) error {
|
|||
return errBadConnNoWrite
|
||||
}
|
||||
mc.cleanup()
|
||||
errLog.Print(err)
|
||||
mc.log(err)
|
||||
}
|
||||
return ErrInvalidConn
|
||||
}
|
||||
|
|
@ -239,7 +212,7 @@ func (mc *mysqlConn) readHandshakePacket() (data []byte, plugin string, err erro
|
|||
// reserved (all [00]) [10 bytes]
|
||||
pos += 1 + 2 + 2 + 1 + 10
|
||||
|
||||
// second part of the password cipher [mininum 13 bytes],
|
||||
// second part of the password cipher [minimum 13 bytes],
|
||||
// where len=MAX(13, length of auth-plugin-data - 8)
|
||||
//
|
||||
// The web documentation is ambiguous about the length. However,
|
||||
|
|
@ -285,6 +258,7 @@ func (mc *mysqlConn) writeHandshakeResponsePacket(authResp []byte, plugin string
|
|||
clientLocalFiles |
|
||||
clientPluginAuth |
|
||||
clientMultiResults |
|
||||
clientConnectAttrs |
|
||||
mc.flags&clientLongFlag
|
||||
|
||||
if mc.cfg.ClientFoundRows {
|
||||
|
|
@ -318,11 +292,17 @@ func (mc *mysqlConn) writeHandshakeResponsePacket(authResp []byte, plugin string
|
|||
pktLen += n + 1
|
||||
}
|
||||
|
||||
// encode length of the connection attributes
|
||||
var connAttrsLEIBuf [9]byte
|
||||
connAttrsLen := len(mc.connector.encodedAttributes)
|
||||
connAttrsLEI := appendLengthEncodedInteger(connAttrsLEIBuf[:0], uint64(connAttrsLen))
|
||||
pktLen += len(connAttrsLEI) + len(mc.connector.encodedAttributes)
|
||||
|
||||
// Calculate packet length and get buffer with that size
|
||||
data, err := mc.buf.takeSmallBuffer(pktLen + 4)
|
||||
data, err := mc.buf.takeBuffer(pktLen + 4)
|
||||
if err != nil {
|
||||
// cannot take the buffer. Something must be wrong with the connection
|
||||
errLog.Print(err)
|
||||
mc.log(err)
|
||||
return errBadConnNoWrite
|
||||
}
|
||||
|
||||
|
|
@ -338,14 +318,18 @@ func (mc *mysqlConn) writeHandshakeResponsePacket(authResp []byte, plugin string
|
|||
data[10] = 0x00
|
||||
data[11] = 0x00
|
||||
|
||||
// Charset [1 byte]
|
||||
// Collation ID [1 byte]
|
||||
cname := mc.cfg.Collation
|
||||
if cname == "" {
|
||||
cname = defaultCollation
|
||||
}
|
||||
var found bool
|
||||
data[12], found = collations[mc.cfg.Collation]
|
||||
data[12], found = collations[cname]
|
||||
if !found {
|
||||
// Note possibility for false negatives:
|
||||
// could be triggered although the collation is valid if the
|
||||
// collations map does not contain entries the server supports.
|
||||
return errors.New("unknown collation")
|
||||
return fmt.Errorf("unknown collation: %q", cname)
|
||||
}
|
||||
|
||||
// Filler [23 bytes] (all 0x00)
|
||||
|
|
@ -367,7 +351,6 @@ func (mc *mysqlConn) writeHandshakeResponsePacket(authResp []byte, plugin string
|
|||
if err := tlsConn.Handshake(); err != nil {
|
||||
return err
|
||||
}
|
||||
mc.rawConn = mc.netConn
|
||||
mc.netConn = tlsConn
|
||||
mc.buf.nc = tlsConn
|
||||
}
|
||||
|
|
@ -394,6 +377,10 @@ func (mc *mysqlConn) writeHandshakeResponsePacket(authResp []byte, plugin string
|
|||
data[pos] = 0x00
|
||||
pos++
|
||||
|
||||
// Connection Attributes
|
||||
pos += copy(data[pos:], connAttrsLEI)
|
||||
pos += copy(data[pos:], []byte(mc.connector.encodedAttributes))
|
||||
|
||||
// Send Auth packet
|
||||
return mc.writePacket(data[:pos])
|
||||
}
|
||||
|
|
@ -404,7 +391,7 @@ func (mc *mysqlConn) writeAuthSwitchPacket(authData []byte) error {
|
|||
data, err := mc.buf.takeSmallBuffer(pktLen)
|
||||
if err != nil {
|
||||
// cannot take the buffer. Something must be wrong with the connection
|
||||
errLog.Print(err)
|
||||
mc.log(err)
|
||||
return errBadConnNoWrite
|
||||
}
|
||||
|
||||
|
|
@ -424,7 +411,7 @@ func (mc *mysqlConn) writeCommandPacket(command byte) error {
|
|||
data, err := mc.buf.takeSmallBuffer(4 + 1)
|
||||
if err != nil {
|
||||
// cannot take the buffer. Something must be wrong with the connection
|
||||
errLog.Print(err)
|
||||
mc.log(err)
|
||||
return errBadConnNoWrite
|
||||
}
|
||||
|
||||
|
|
@ -443,7 +430,7 @@ func (mc *mysqlConn) writeCommandPacketStr(command byte, arg string) error {
|
|||
data, err := mc.buf.takeBuffer(pktLen + 4)
|
||||
if err != nil {
|
||||
// cannot take the buffer. Something must be wrong with the connection
|
||||
errLog.Print(err)
|
||||
mc.log(err)
|
||||
return errBadConnNoWrite
|
||||
}
|
||||
|
||||
|
|
@ -464,7 +451,7 @@ func (mc *mysqlConn) writeCommandPacketUint32(command byte, arg uint32) error {
|
|||
data, err := mc.buf.takeSmallBuffer(4 + 1 + 4)
|
||||
if err != nil {
|
||||
// cannot take the buffer. Something must be wrong with the connection
|
||||
errLog.Print(err)
|
||||
mc.log(err)
|
||||
return errBadConnNoWrite
|
||||
}
|
||||
|
||||
|
|
@ -495,7 +482,9 @@ func (mc *mysqlConn) readAuthResult() ([]byte, string, error) {
|
|||
switch data[0] {
|
||||
|
||||
case iOK:
|
||||
return nil, "", mc.handleOkPacket(data)
|
||||
// resultUnchanged, since auth happens before any queries or
|
||||
// commands have been executed.
|
||||
return nil, "", mc.resultUnchanged().handleOkPacket(data)
|
||||
|
||||
case iAuthMoreData:
|
||||
return data[1:], "", err
|
||||
|
|
@ -518,9 +507,9 @@ func (mc *mysqlConn) readAuthResult() ([]byte, string, error) {
|
|||
}
|
||||
}
|
||||
|
||||
// Returns error if Packet is not an 'Result OK'-Packet
|
||||
func (mc *mysqlConn) readResultOK() error {
|
||||
data, err := mc.readPacket()
|
||||
// Returns error if Packet is not a 'Result OK'-Packet
|
||||
func (mc *okHandler) readResultOK() error {
|
||||
data, err := mc.conn().readPacket()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -528,13 +517,17 @@ func (mc *mysqlConn) readResultOK() error {
|
|||
if data[0] == iOK {
|
||||
return mc.handleOkPacket(data)
|
||||
}
|
||||
return mc.handleErrorPacket(data)
|
||||
return mc.conn().handleErrorPacket(data)
|
||||
}
|
||||
|
||||
// Result Set Header Packet
|
||||
// http://dev.mysql.com/doc/internals/en/com-query-response.html#packet-ProtocolText::Resultset
|
||||
func (mc *mysqlConn) readResultSetHeaderPacket() (int, error) {
|
||||
data, err := mc.readPacket()
|
||||
func (mc *okHandler) readResultSetHeaderPacket() (int, error) {
|
||||
// handleOkPacket replaces both values; other cases leave the values unchanged.
|
||||
mc.result.affectedRows = append(mc.result.affectedRows, 0)
|
||||
mc.result.insertIds = append(mc.result.insertIds, 0)
|
||||
|
||||
data, err := mc.conn().readPacket()
|
||||
if err == nil {
|
||||
switch data[0] {
|
||||
|
||||
|
|
@ -542,19 +535,16 @@ func (mc *mysqlConn) readResultSetHeaderPacket() (int, error) {
|
|||
return 0, mc.handleOkPacket(data)
|
||||
|
||||
case iERR:
|
||||
return 0, mc.handleErrorPacket(data)
|
||||
return 0, mc.conn().handleErrorPacket(data)
|
||||
|
||||
case iLocalInFile:
|
||||
return 0, mc.handleInFileRequest(string(data[1:]))
|
||||
}
|
||||
|
||||
// column count
|
||||
num, _, n := readLengthEncodedInteger(data)
|
||||
if n-len(data) == 0 {
|
||||
return int(num), nil
|
||||
}
|
||||
|
||||
return 0, ErrMalformPkt
|
||||
num, _, _ := readLengthEncodedInteger(data)
|
||||
// ignore remaining data in the packet. see #1478.
|
||||
return int(num), nil
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
|
|
@ -607,18 +597,61 @@ func readStatus(b []byte) statusFlag {
|
|||
return statusFlag(b[0]) | statusFlag(b[1])<<8
|
||||
}
|
||||
|
||||
// Returns an instance of okHandler for codepaths where mysqlConn.result doesn't
|
||||
// need to be cleared first (e.g. during authentication, or while additional
|
||||
// resultsets are being fetched.)
|
||||
func (mc *mysqlConn) resultUnchanged() *okHandler {
|
||||
return (*okHandler)(mc)
|
||||
}
|
||||
|
||||
// okHandler represents the state of the connection when mysqlConn.result has
|
||||
// been prepared for processing of OK packets.
|
||||
//
|
||||
// To correctly populate mysqlConn.result (updated by handleOkPacket()), all
|
||||
// callpaths must either:
|
||||
//
|
||||
// 1. first clear it using clearResult(), or
|
||||
// 2. confirm that they don't need to (by calling resultUnchanged()).
|
||||
//
|
||||
// Both return an instance of type *okHandler.
|
||||
type okHandler mysqlConn
|
||||
|
||||
// Exposes the underlying type's methods.
|
||||
func (mc *okHandler) conn() *mysqlConn {
|
||||
return (*mysqlConn)(mc)
|
||||
}
|
||||
|
||||
// clearResult clears the connection's stored affectedRows and insertIds
|
||||
// fields.
|
||||
//
|
||||
// It returns a handler that can process OK responses.
|
||||
func (mc *mysqlConn) clearResult() *okHandler {
|
||||
mc.result = mysqlResult{}
|
||||
return (*okHandler)(mc)
|
||||
}
|
||||
|
||||
// Ok Packet
|
||||
// http://dev.mysql.com/doc/internals/en/generic-response-packets.html#packet-OK_Packet
|
||||
func (mc *mysqlConn) handleOkPacket(data []byte) error {
|
||||
func (mc *okHandler) handleOkPacket(data []byte) error {
|
||||
var n, m int
|
||||
var affectedRows, insertId uint64
|
||||
|
||||
// 0x00 [1 byte]
|
||||
|
||||
// Affected rows [Length Coded Binary]
|
||||
mc.affectedRows, _, n = readLengthEncodedInteger(data[1:])
|
||||
affectedRows, _, n = readLengthEncodedInteger(data[1:])
|
||||
|
||||
// Insert id [Length Coded Binary]
|
||||
mc.insertId, _, m = readLengthEncodedInteger(data[1+n:])
|
||||
insertId, _, m = readLengthEncodedInteger(data[1+n:])
|
||||
|
||||
// Update for the current statement result (only used by
|
||||
// readResultSetHeaderPacket).
|
||||
if len(mc.result.affectedRows) > 0 {
|
||||
mc.result.affectedRows[len(mc.result.affectedRows)-1] = int64(affectedRows)
|
||||
}
|
||||
if len(mc.result.insertIds) > 0 {
|
||||
mc.result.insertIds[len(mc.result.insertIds)-1] = int64(insertId)
|
||||
}
|
||||
|
||||
// server_status [2 bytes]
|
||||
mc.status = readStatus(data[1+n+m : 1+n+m+2])
|
||||
|
|
@ -769,7 +802,8 @@ func (rows *textRows) readRow(dest []driver.Value) error {
|
|||
|
||||
for i := range dest {
|
||||
// Read bytes and convert to string
|
||||
dest[i], isNull, n, err = readLengthEncodedString(data[pos:])
|
||||
var buf []byte
|
||||
buf, isNull, n, err = readLengthEncodedString(data[pos:])
|
||||
pos += n
|
||||
|
||||
if err != nil {
|
||||
|
|
@ -781,19 +815,40 @@ func (rows *textRows) readRow(dest []driver.Value) error {
|
|||
continue
|
||||
}
|
||||
|
||||
if !mc.parseTime {
|
||||
continue
|
||||
}
|
||||
|
||||
// Parse time field
|
||||
switch rows.rs.columns[i].fieldType {
|
||||
case fieldTypeTimestamp,
|
||||
fieldTypeDateTime,
|
||||
fieldTypeDate,
|
||||
fieldTypeNewDate:
|
||||
if dest[i], err = parseDateTime(dest[i].([]byte), mc.cfg.Loc); err != nil {
|
||||
return err
|
||||
if mc.parseTime {
|
||||
dest[i], err = parseDateTime(buf, mc.cfg.Loc)
|
||||
} else {
|
||||
dest[i] = buf
|
||||
}
|
||||
|
||||
case fieldTypeTiny, fieldTypeShort, fieldTypeInt24, fieldTypeYear, fieldTypeLong:
|
||||
dest[i], err = strconv.ParseInt(string(buf), 10, 64)
|
||||
|
||||
case fieldTypeLongLong:
|
||||
if rows.rs.columns[i].flags&flagUnsigned != 0 {
|
||||
dest[i], err = strconv.ParseUint(string(buf), 10, 64)
|
||||
} else {
|
||||
dest[i], err = strconv.ParseInt(string(buf), 10, 64)
|
||||
}
|
||||
|
||||
case fieldTypeFloat:
|
||||
var d float64
|
||||
d, err = strconv.ParseFloat(string(buf), 32)
|
||||
dest[i] = float32(d)
|
||||
|
||||
case fieldTypeDouble:
|
||||
dest[i], err = strconv.ParseFloat(string(buf), 64)
|
||||
|
||||
default:
|
||||
dest[i] = buf
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -938,7 +993,7 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
|
|||
}
|
||||
if err != nil {
|
||||
// cannot take the buffer. Something must be wrong with the connection
|
||||
errLog.Print(err)
|
||||
mc.log(err)
|
||||
return errBadConnNoWrite
|
||||
}
|
||||
|
||||
|
|
@ -1116,7 +1171,7 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
|
|||
if v.IsZero() {
|
||||
b = append(b, "0000-00-00"...)
|
||||
} else {
|
||||
b, err = appendDateTime(b, v.In(mc.cfg.Loc))
|
||||
b, err = appendDateTime(b, v.In(mc.cfg.Loc), mc.cfg.timeTruncate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -1137,7 +1192,7 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
|
|||
if valuesCap != cap(paramValues) {
|
||||
data = append(data[:pos], paramValues...)
|
||||
if err = mc.buf.store(data); err != nil {
|
||||
errLog.Print(err)
|
||||
mc.log(err)
|
||||
return errBadConnNoWrite
|
||||
}
|
||||
}
|
||||
|
|
@ -1149,7 +1204,9 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
|
|||
return mc.writePacket(data)
|
||||
}
|
||||
|
||||
func (mc *mysqlConn) discardResults() error {
|
||||
// For each remaining resultset in the stream, discards its rows and updates
|
||||
// mc.affectedRows and mc.insertIds.
|
||||
func (mc *okHandler) discardResults() error {
|
||||
for mc.status&statusMoreResultsExists != 0 {
|
||||
resLen, err := mc.readResultSetHeaderPacket()
|
||||
if err != nil {
|
||||
|
|
@ -1157,11 +1214,11 @@ func (mc *mysqlConn) discardResults() error {
|
|||
}
|
||||
if resLen > 0 {
|
||||
// columns
|
||||
if err := mc.readUntilEOF(); err != nil {
|
||||
if err := mc.conn().readUntilEOF(); err != nil {
|
||||
return err
|
||||
}
|
||||
// rows
|
||||
if err := mc.readUntilEOF(); err != nil {
|
||||
if err := mc.conn().readUntilEOF(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
|
|||
36
vendor/github.com/go-sql-driver/mysql/result.go
generated
vendored
36
vendor/github.com/go-sql-driver/mysql/result.go
generated
vendored
|
|
@ -8,15 +8,43 @@
|
|||
|
||||
package mysql
|
||||
|
||||
import "database/sql/driver"
|
||||
|
||||
// Result exposes data not available through *connection.Result.
|
||||
//
|
||||
// This is accessible by executing statements using sql.Conn.Raw() and
|
||||
// downcasting the returned result:
|
||||
//
|
||||
// res, err := rawConn.Exec(...)
|
||||
// res.(mysql.Result).AllRowsAffected()
|
||||
type Result interface {
|
||||
driver.Result
|
||||
// AllRowsAffected returns a slice containing the affected rows for each
|
||||
// executed statement.
|
||||
AllRowsAffected() []int64
|
||||
// AllLastInsertIds returns a slice containing the last inserted ID for each
|
||||
// executed statement.
|
||||
AllLastInsertIds() []int64
|
||||
}
|
||||
|
||||
type mysqlResult struct {
|
||||
affectedRows int64
|
||||
insertId int64
|
||||
// One entry in both slices is created for every executed statement result.
|
||||
affectedRows []int64
|
||||
insertIds []int64
|
||||
}
|
||||
|
||||
func (res *mysqlResult) LastInsertId() (int64, error) {
|
||||
return res.insertId, nil
|
||||
return res.insertIds[len(res.insertIds)-1], nil
|
||||
}
|
||||
|
||||
func (res *mysqlResult) RowsAffected() (int64, error) {
|
||||
return res.affectedRows, nil
|
||||
return res.affectedRows[len(res.affectedRows)-1], nil
|
||||
}
|
||||
|
||||
func (res *mysqlResult) AllLastInsertIds() []int64 {
|
||||
return append([]int64{}, res.insertIds...) // defensive copy
|
||||
}
|
||||
|
||||
func (res *mysqlResult) AllRowsAffected() []int64 {
|
||||
return append([]int64{}, res.affectedRows...) // defensive copy
|
||||
}
|
||||
|
|
|
|||
13
vendor/github.com/go-sql-driver/mysql/rows.go
generated
vendored
13
vendor/github.com/go-sql-driver/mysql/rows.go
generated
vendored
|
|
@ -123,7 +123,8 @@ func (rows *mysqlRows) Close() (err error) {
|
|||
err = mc.readUntilEOF()
|
||||
}
|
||||
if err == nil {
|
||||
if err = mc.discardResults(); err != nil {
|
||||
handleOk := mc.clearResult()
|
||||
if err = handleOk.discardResults(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
@ -160,7 +161,15 @@ func (rows *mysqlRows) nextResultSet() (int, error) {
|
|||
return 0, io.EOF
|
||||
}
|
||||
rows.rs = resultSet{}
|
||||
return rows.mc.readResultSetHeaderPacket()
|
||||
// rows.mc.affectedRows and rows.mc.insertIds accumulate on each call to
|
||||
// nextResultSet.
|
||||
resLen, err := rows.mc.resultUnchanged().readResultSetHeaderPacket()
|
||||
if err != nil {
|
||||
// Clean up about multi-results flag
|
||||
rows.rs.done = true
|
||||
rows.mc.status = rows.mc.status & (^statusMoreResultsExists)
|
||||
}
|
||||
return resLen, err
|
||||
}
|
||||
|
||||
func (rows *mysqlRows) nextNotEmptyResultSet() (int, error) {
|
||||
|
|
|
|||
23
vendor/github.com/go-sql-driver/mysql/statement.go
generated
vendored
23
vendor/github.com/go-sql-driver/mysql/statement.go
generated
vendored
|
|
@ -51,7 +51,7 @@ func (stmt *mysqlStmt) CheckNamedValue(nv *driver.NamedValue) (err error) {
|
|||
|
||||
func (stmt *mysqlStmt) Exec(args []driver.Value) (driver.Result, error) {
|
||||
if stmt.mc.closed.Load() {
|
||||
errLog.Print(ErrInvalidConn)
|
||||
stmt.mc.log(ErrInvalidConn)
|
||||
return nil, driver.ErrBadConn
|
||||
}
|
||||
// Send command
|
||||
|
|
@ -61,12 +61,10 @@ func (stmt *mysqlStmt) Exec(args []driver.Value) (driver.Result, error) {
|
|||
}
|
||||
|
||||
mc := stmt.mc
|
||||
|
||||
mc.affectedRows = 0
|
||||
mc.insertId = 0
|
||||
handleOk := stmt.mc.clearResult()
|
||||
|
||||
// Read Result
|
||||
resLen, err := mc.readResultSetHeaderPacket()
|
||||
resLen, err := handleOk.readResultSetHeaderPacket()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -83,14 +81,12 @@ func (stmt *mysqlStmt) Exec(args []driver.Value) (driver.Result, error) {
|
|||
}
|
||||
}
|
||||
|
||||
if err := mc.discardResults(); err != nil {
|
||||
if err := handleOk.discardResults(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &mysqlResult{
|
||||
affectedRows: int64(mc.affectedRows),
|
||||
insertId: int64(mc.insertId),
|
||||
}, nil
|
||||
copied := mc.result
|
||||
return &copied, nil
|
||||
}
|
||||
|
||||
func (stmt *mysqlStmt) Query(args []driver.Value) (driver.Rows, error) {
|
||||
|
|
@ -99,7 +95,7 @@ func (stmt *mysqlStmt) Query(args []driver.Value) (driver.Rows, error) {
|
|||
|
||||
func (stmt *mysqlStmt) query(args []driver.Value) (*binaryRows, error) {
|
||||
if stmt.mc.closed.Load() {
|
||||
errLog.Print(ErrInvalidConn)
|
||||
stmt.mc.log(ErrInvalidConn)
|
||||
return nil, driver.ErrBadConn
|
||||
}
|
||||
// Send command
|
||||
|
|
@ -111,7 +107,8 @@ func (stmt *mysqlStmt) query(args []driver.Value) (*binaryRows, error) {
|
|||
mc := stmt.mc
|
||||
|
||||
// Read Result
|
||||
resLen, err := mc.readResultSetHeaderPacket()
|
||||
handleOk := stmt.mc.clearResult()
|
||||
resLen, err := handleOk.readResultSetHeaderPacket()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -144,7 +141,7 @@ type converter struct{}
|
|||
// implementation does not. This function should be kept in sync with
|
||||
// database/sql/driver defaultConverter.ConvertValue() except for that
|
||||
// deliberate difference.
|
||||
func (c converter) ConvertValue(v interface{}) (driver.Value, error) {
|
||||
func (c converter) ConvertValue(v any) (driver.Value, error) {
|
||||
if driver.IsValue(v) {
|
||||
return v, nil
|
||||
}
|
||||
|
|
|
|||
13
vendor/github.com/go-sql-driver/mysql/utils.go
generated
vendored
13
vendor/github.com/go-sql-driver/mysql/utils.go
generated
vendored
|
|
@ -36,7 +36,7 @@ var (
|
|||
// registering it.
|
||||
//
|
||||
// rootCertPool := x509.NewCertPool()
|
||||
// pem, err := ioutil.ReadFile("/path/ca-cert.pem")
|
||||
// pem, err := os.ReadFile("/path/ca-cert.pem")
|
||||
// if err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
|
|
@ -265,7 +265,11 @@ func parseBinaryDateTime(num uint64, data []byte, loc *time.Location) (driver.Va
|
|||
return nil, fmt.Errorf("invalid DATETIME packet length %d", num)
|
||||
}
|
||||
|
||||
func appendDateTime(buf []byte, t time.Time) ([]byte, error) {
|
||||
func appendDateTime(buf []byte, t time.Time, timeTruncate time.Duration) ([]byte, error) {
|
||||
if timeTruncate > 0 {
|
||||
t = t.Truncate(timeTruncate)
|
||||
}
|
||||
|
||||
year, month, day := t.Date()
|
||||
hour, min, sec := t.Clock()
|
||||
nsec := t.Nanosecond()
|
||||
|
|
@ -616,6 +620,11 @@ func appendLengthEncodedInteger(b []byte, n uint64) []byte {
|
|||
byte(n>>32), byte(n>>40), byte(n>>48), byte(n>>56))
|
||||
}
|
||||
|
||||
func appendLengthEncodedString(b []byte, s string) []byte {
|
||||
b = appendLengthEncodedInteger(b, uint64(len(s)))
|
||||
return append(b, s...)
|
||||
}
|
||||
|
||||
// reserveBuffer checks cap(buf) and expand buffer to len(buf) + appendSize.
|
||||
// If cap(buf) is not enough, reallocate new buffer.
|
||||
func reserveBuffer(buf []byte, appendSize int) []byte {
|
||||
|
|
|
|||
4
vendor/github.com/golang-jwt/jwt/v5/ecdsa.go
generated
vendored
4
vendor/github.com/golang-jwt/jwt/v5/ecdsa.go
generated
vendored
|
|
@ -62,7 +62,7 @@ func (m *SigningMethodECDSA) Verify(signingString string, sig []byte, key interf
|
|||
case *ecdsa.PublicKey:
|
||||
ecdsaKey = k
|
||||
default:
|
||||
return newError("ECDSA verify expects *ecsda.PublicKey", ErrInvalidKeyType)
|
||||
return newError("ECDSA verify expects *ecdsa.PublicKey", ErrInvalidKeyType)
|
||||
}
|
||||
|
||||
if len(sig) != 2*m.KeySize {
|
||||
|
|
@ -96,7 +96,7 @@ func (m *SigningMethodECDSA) Sign(signingString string, key interface{}) ([]byte
|
|||
case *ecdsa.PrivateKey:
|
||||
ecdsaKey = k
|
||||
default:
|
||||
return nil, newError("ECDSA sign expects *ecsda.PrivateKey", ErrInvalidKeyType)
|
||||
return nil, newError("ECDSA sign expects *ecdsa.PrivateKey", ErrInvalidKeyType)
|
||||
}
|
||||
|
||||
// Create the hasher
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue