Update dependency go modules for k8s v1.27.0-rc.0

This commit is contained in:
Sunny Song
2023-03-28 16:51:42 +00:00
parent d226af52de
commit 236537f90c
600 changed files with 33883 additions and 16186 deletions

264
vendor/k8s.io/kube-openapi/pkg/cached/cache.go generated vendored Normal file
View File

@@ -0,0 +1,264 @@
/*
Copyright 2022 The Kubernetes 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 cache provides a cache mechanism based on etags to lazily
// build, and/or cache results from expensive operation such that those
// operations are not repeated unnecessarily. The operations can be
// created as a tree, and replaced dynamically as needed.
//
// # Dependencies and types of caches
//
// This package uses a source/transform/sink model of caches to build
// the dependency tree, and can be used as follows:
// - [NewSource]: A source cache that recomputes the content every time.
// - [NewStaticSource]: A source cache that always produces the
// same content, it is only called once.
// - [NewTransformer]: A cache that transforms data from one format to
// another. It's only refreshed when the source changes.
// - [NewMerger]: A cache that aggregates multiple caches into one.
// It's only refreshed when the source changes.
// - [Replaceable]: A cache adapter that can be atomically
// replaced with a new one, and saves the previous results in case an
// error pops-up.
//
// # Atomicity
//
// Most of the operations are not atomic/thread-safe, except for
// [Replaceable.Replace] which can be performed while the objects
// are being read.
//
// # Etags
//
// Etags in this library is a cache version identifier. It doesn't
// necessarily strictly match to the semantics of http `etags`, but are
// somewhat inspired from it and function with the same principles.
// Hashing the content is a good way to guarantee that your function is
// never going to be called spuriously. In Kubernetes world, this could
// be a `resourceVersion`, this can be an actual etag, a hash, a UUID
// (if the cache always changes), or even a made-up string when the
// content of the cache never changes.
package cached
import (
"fmt"
"sync/atomic"
)
// Result is the content returned from a call to a cache. It can either
// be created with [NewResultOK] if the call was a success, or
// [NewResultErr] if the call resulted in an error.
type Result[T any] struct {
Data T
Etag string
Err error
}
// NewResultOK creates a new [Result] for a successful operation.
func NewResultOK[T any](data T, etag string) Result[T] {
return Result[T]{
Data: data,
Etag: etag,
}
}
// NewResultErr creates a new [Result] when an error has happened.
func NewResultErr[T any](err error) Result[T] {
return Result[T]{
Err: err,
}
}
// Result can be treated as a [Data] if necessary.
func (r Result[T]) Get() Result[T] {
return r
}
// Data is a cache that performs an action whose result data will be
// cached. It also returns an "etag" identifier to version the cache, so
// that the caller can know if they have the most recent version of the
// cache (and can decide to cache some operation based on that).
//
// The [NewMerger] and [NewTransformer] automatically handle
// that for you by checking if the etag is updated before calling the
// merging or transforming function.
type Data[T any] interface {
// Returns the cached data, as well as an "etag" to identify the
// version of the cache, or an error if something happened.
Get() Result[T]
}
// T is the source type, V is the destination type.
type merger[K comparable, T, V any] struct {
mergeFn func(map[K]Result[T]) Result[V]
caches map[K]Data[T]
cacheResults map[K]Result[T]
result Result[V]
}
// NewMerger creates a new merge cache, a cache that merges the result
// of other caches. The function only gets called if any of the
// dependency has changed.
//
// If any of the dependency returned an error before, or any of the
// dependency returned an error this time, or if the mergeFn failed
// before, then the function is reran.
//
// The caches and results are mapped by K so that associated data can be
// retrieved. The map of dependencies can not be modified after
// creation, and a new merger should be created (and probably replaced
// using a [Replaceable]).
//
// Note that this assumes there is no "partial" merge, the merge
// function will remerge all the dependencies together everytime. Since
// the list of dependencies is constant, there is no way to save some
// partial merge information either.
func NewMerger[K comparable, T, V any](mergeFn func(results map[K]Result[T]) Result[V], caches map[K]Data[T]) Data[V] {
return &merger[K, T, V]{
mergeFn: mergeFn,
caches: caches,
}
}
func (c *merger[K, T, V]) prepareResults() map[K]Result[T] {
cacheResults := make(map[K]Result[T], len(c.caches))
for key, cache := range c.caches {
cacheResults[key] = cache.Get()
}
return cacheResults
}
// Rerun if:
// - The last run resulted in an error
// - Any of the dependency previously returned an error
// - Any of the dependency just returned an error
// - Any of the dependency's etag changed
func (c *merger[K, T, V]) needsRunning(results map[K]Result[T]) bool {
if c.cacheResults == nil {
return true
}
if c.result.Err != nil {
return true
}
if len(results) != len(c.cacheResults) {
panic(fmt.Errorf("invalid number of results: %v (expected %v)", len(results), len(c.cacheResults)))
}
for key, oldResult := range c.cacheResults {
newResult, ok := results[key]
if !ok {
panic(fmt.Errorf("unknown cache entry: %v", key))
}
if newResult.Etag != oldResult.Etag || newResult.Err != nil || oldResult.Err != nil {
return true
}
}
return false
}
func (c *merger[K, T, V]) Get() Result[V] {
cacheResults := c.prepareResults()
if c.needsRunning(cacheResults) {
c.cacheResults = cacheResults
c.result = c.mergeFn(c.cacheResults)
}
return c.result
}
type transformerCacheKeyType struct{}
// NewTransformer creates a new cache that transforms the result of
// another cache. The transformFn will only be called if the source
// cache has updated the output, otherwise, the cached result will be
// returned.
//
// If the dependency returned an error before, or it returns an error
// this time, or if the transformerFn failed before, the function is
// reran.
func NewTransformer[T, V any](transformerFn func(Result[T]) Result[V], source Data[T]) Data[V] {
return NewMerger(func(caches map[transformerCacheKeyType]Result[T]) Result[V] {
cache, ok := caches[transformerCacheKeyType{}]
if len(caches) != 1 || !ok {
panic(fmt.Errorf("invalid cache for transformer cache: %v", caches))
}
return transformerFn(cache)
}, map[transformerCacheKeyType]Data[T]{
{}: source,
})
}
// NewSource creates a new cache that generates some data. This
// will always be called since we don't know the origin of the data and
// if it needs to be updated or not.
func NewSource[T any](sourceFn func() Result[T]) Data[T] {
c := source[T](sourceFn)
return &c
}
type source[T any] func() Result[T]
func (c *source[T]) Get() Result[T] {
return (*c)()
}
// NewStaticSource creates a new cache that always generates the
// same data. This will only be called once (lazily).
func NewStaticSource[T any](staticFn func() Result[T]) Data[T] {
return &static[T]{
fn: staticFn,
}
}
type static[T any] struct {
fn func() Result[T]
result *Result[T]
}
func (c *static[T]) Get() Result[T] {
if c.result == nil {
result := c.fn()
c.result = &result
}
return *c.result
}
// Replaceable is a cache that carries the result even when the
// cache is replaced. The cache can be replaced atomically (without any
// lock held). This is the type that should typically be stored in
// structs.
type Replaceable[T any] struct {
cache atomic.Pointer[Data[T]]
result *Result[T]
}
// Get retrieves the data from the underlying source. [Replaceable]
// implements the [Data] interface itself. This is a pass-through
// that calls the most recent underlying cache. If the cache fails but
// previously had returned a success, that success will be returned
// instead. If the cache fails but we never returned a success, that
// failure is returned.
func (c *Replaceable[T]) Get() Result[T] {
result := (*c.cache.Load()).Get()
if result.Err != nil && c.result != nil && c.result.Err == nil {
return *c.result
}
c.result = &result
return *c.result
}
// Replace changes the cache in a thread-safe way.
func (c *Replaceable[T]) Replace(cache Data[T]) {
c.cache.Swap(&cache)
}

View File

@@ -24,7 +24,6 @@ import (
"net/http"
"net/url"
"path"
"sort"
"strconv"
"strings"
"sync"
@@ -32,11 +31,12 @@ import (
"github.com/golang/protobuf/proto"
openapi_v3 "github.com/google/gnostic/openapiv3"
"github.com/google/uuid"
"github.com/munnerz/goautoneg"
"k8s.io/klog/v2"
"k8s.io/kube-openapi/pkg/cached"
"k8s.io/kube-openapi/pkg/common"
"k8s.io/kube-openapi/pkg/internal/handler"
"k8s.io/kube-openapi/pkg/spec3"
"k8s.io/kube-openapi/pkg/validation/spec"
)
const (
@@ -58,23 +58,63 @@ type OpenAPIV3DiscoveryGroupVersion struct {
ServerRelativeURL string `json:"serverRelativeURL"`
}
func ToV3ProtoBinary(json []byte) ([]byte, error) {
document, err := openapi_v3.ParseDocument(json)
if err != nil {
return nil, err
}
return proto.Marshal(document)
}
type timedSpec struct {
spec []byte
lastModified time.Time
}
// This type is protected by the lock on OpenAPIService.
type openAPIV3Group struct {
specCache cached.Replaceable[*spec3.OpenAPI]
pbCache cached.Data[timedSpec]
jsonCache cached.Data[timedSpec]
}
func newOpenAPIV3Group() *openAPIV3Group {
o := &openAPIV3Group{}
o.jsonCache = cached.NewTransformer[*spec3.OpenAPI](func(result cached.Result[*spec3.OpenAPI]) cached.Result[timedSpec] {
if result.Err != nil {
return cached.NewResultErr[timedSpec](result.Err)
}
json, err := json.Marshal(result.Data)
if err != nil {
return cached.NewResultErr[timedSpec](err)
}
return cached.NewResultOK(timedSpec{spec: json, lastModified: time.Now()}, computeETag(json))
}, &o.specCache)
o.pbCache = cached.NewTransformer(func(result cached.Result[timedSpec]) cached.Result[timedSpec] {
if result.Err != nil {
return cached.NewResultErr[timedSpec](result.Err)
}
proto, err := ToV3ProtoBinary(result.Data.spec)
if err != nil {
return cached.NewResultErr[timedSpec](err)
}
return cached.NewResultOK(timedSpec{spec: proto, lastModified: result.Data.lastModified}, result.Etag)
}, o.jsonCache)
return o
}
func (o *openAPIV3Group) UpdateSpec(openapi cached.Data[*spec3.OpenAPI]) {
o.specCache.Replace(openapi)
}
// OpenAPIService is the service responsible for serving OpenAPI spec. It has
// the ability to safely change the spec while serving it.
type OpenAPIService struct {
// rwMutex protects All members of this service.
rwMutex sync.RWMutex
lastModified time.Time
v3Schema map[string]*OpenAPIV3Group
}
// Mutex protects the schema map.
mutex sync.Mutex
v3Schema map[string]*openAPIV3Group
type OpenAPIV3Group struct {
rwMutex sync.RWMutex
lastModified time.Time
pbCache handler.HandlerCache
jsonCache handler.HandlerCache
etagCache handler.HandlerCache
discoveryCache cached.Replaceable[timedSpec]
}
func computeETag(data []byte) string {
@@ -93,94 +133,90 @@ func constructServerRelativeURL(gvString, etag string) string {
}
// NewOpenAPIService builds an OpenAPIService starting with the given spec.
func NewOpenAPIService(spec *spec.Swagger) (*OpenAPIService, error) {
func NewOpenAPIService() *OpenAPIService {
o := &OpenAPIService{}
o.v3Schema = make(map[string]*OpenAPIV3Group)
return o, nil
o.v3Schema = make(map[string]*openAPIV3Group)
// We're not locked because we haven't shared the structure yet.
o.discoveryCache.Replace(o.buildDiscoveryCacheLocked())
return o
}
func (o *OpenAPIService) getGroupBytes() ([]byte, error) {
o.rwMutex.RLock()
defer o.rwMutex.RUnlock()
keys := make([]string, len(o.v3Schema))
i := 0
for k := range o.v3Schema {
keys[i] = k
i++
func (o *OpenAPIService) buildDiscoveryCacheLocked() cached.Data[timedSpec] {
caches := make(map[string]cached.Data[timedSpec], len(o.v3Schema))
for gvName, group := range o.v3Schema {
caches[gvName] = group.jsonCache
}
sort.Strings(keys)
discovery := &OpenAPIV3Discovery{Paths: make(map[string]OpenAPIV3DiscoveryGroupVersion)}
for gvString, groupVersion := range o.v3Schema {
etagBytes, err := groupVersion.etagCache.Get()
return cached.NewMerger(func(results map[string]cached.Result[timedSpec]) cached.Result[timedSpec] {
discovery := &OpenAPIV3Discovery{Paths: make(map[string]OpenAPIV3DiscoveryGroupVersion)}
for gvName, result := range results {
if result.Err != nil {
return cached.NewResultErr[timedSpec](result.Err)
}
discovery.Paths[gvName] = OpenAPIV3DiscoveryGroupVersion{
ServerRelativeURL: constructServerRelativeURL(gvName, result.Etag),
}
}
j, err := json.Marshal(discovery)
if err != nil {
return nil, err
return cached.NewResultErr[timedSpec](err)
}
discovery.Paths[gvString] = OpenAPIV3DiscoveryGroupVersion{
ServerRelativeURL: constructServerRelativeURL(gvString, string(etagBytes)),
}
}
j, err := json.Marshal(discovery)
if err != nil {
return nil, err
}
return j, nil
return cached.NewResultOK(timedSpec{spec: j, lastModified: time.Now()}, computeETag(j))
}, caches)
}
func (o *OpenAPIService) getSingleGroupBytes(getType string, group string) ([]byte, string, time.Time, error) {
o.rwMutex.RLock()
defer o.rwMutex.RUnlock()
o.mutex.Lock()
defer o.mutex.Unlock()
v, ok := o.v3Schema[group]
if !ok {
return nil, "", time.Now(), fmt.Errorf("Cannot find CRD group %s", group)
}
if getType == subTypeJSON {
specBytes, err := v.jsonCache.Get()
if err != nil {
return nil, "", v.lastModified, err
}
etagBytes, err := v.etagCache.Get()
return specBytes, string(etagBytes), v.lastModified, err
} else if getType == subTypeProtobuf || getType == subTypeProtobufDeprecated {
specPb, err := v.pbCache.Get()
if err != nil {
return nil, "", v.lastModified, err
}
etagBytes, err := v.etagCache.Get()
return specPb, string(etagBytes), v.lastModified, err
result := cached.Result[timedSpec]{}
switch getType {
case subTypeJSON:
result = v.jsonCache.Get()
case subTypeProtobuf, subTypeProtobufDeprecated:
result = v.pbCache.Get()
default:
return nil, "", time.Now(), fmt.Errorf("Invalid accept clause %s", getType)
}
return nil, "", time.Now(), fmt.Errorf("Invalid accept clause %s", getType)
return result.Data.spec, result.Etag, result.Data.lastModified, result.Err
}
func (o *OpenAPIService) UpdateGroupVersion(group string, openapi *spec3.OpenAPI) (err error) {
o.rwMutex.Lock()
defer o.rwMutex.Unlock()
// UpdateGroupVersionLazy adds or updates an existing group with the new cached.
func (o *OpenAPIService) UpdateGroupVersionLazy(group string, openapi cached.Data[*spec3.OpenAPI]) {
o.mutex.Lock()
defer o.mutex.Unlock()
if _, ok := o.v3Schema[group]; !ok {
o.v3Schema[group] = &OpenAPIV3Group{}
o.v3Schema[group] = newOpenAPIV3Group()
// Since there is a new item, we need to re-build the cache map.
o.discoveryCache.Replace(o.buildDiscoveryCacheLocked())
}
return o.v3Schema[group].UpdateSpec(openapi)
o.v3Schema[group].UpdateSpec(openapi)
}
func (o *OpenAPIService) UpdateGroupVersion(group string, openapi *spec3.OpenAPI) {
o.UpdateGroupVersionLazy(group, cached.NewResultOK(openapi, uuid.New().String()))
}
func (o *OpenAPIService) DeleteGroupVersion(group string) {
o.rwMutex.Lock()
defer o.rwMutex.Unlock()
o.mutex.Lock()
defer o.mutex.Unlock()
delete(o.v3Schema, group)
}
func ToV3ProtoBinary(json []byte) ([]byte, error) {
document, err := openapi_v3.ParseDocument(json)
if err != nil {
return nil, err
}
return proto.Marshal(document)
// Rebuild the merge cache map since the items have changed.
o.discoveryCache.Replace(o.buildDiscoveryCacheLocked())
}
func (o *OpenAPIService) HandleDiscovery(w http.ResponseWriter, r *http.Request) {
data, _ := o.getGroupBytes()
w.Header().Set("Etag", strconv.Quote(computeETag(data)))
result := o.discoveryCache.Get()
if result.Err != nil {
klog.Errorf("Error serving discovery: %s", result.Err)
w.WriteHeader(http.StatusInternalServerError)
return
}
w.Header().Set("Etag", strconv.Quote(result.Etag))
w.Header().Set("Content-Type", "application/json")
http.ServeContent(w, r, "/openapi/v3", time.Now(), bytes.NewReader(data))
http.ServeContent(w, r, "/openapi/v3", result.Data.lastModified, bytes.NewReader(result.Data.spec))
}
func (o *OpenAPIService) HandleGroupVersion(w http.ResponseWriter, r *http.Request) {
@@ -256,30 +292,3 @@ func (o *OpenAPIService) RegisterOpenAPIV3VersionedService(servePath string, han
handler.HandlePrefix(servePath+"/", http.HandlerFunc(o.HandleGroupVersion))
return nil
}
func (o *OpenAPIV3Group) UpdateSpec(openapi *spec3.OpenAPI) (err error) {
o.rwMutex.Lock()
defer o.rwMutex.Unlock()
o.jsonCache = o.jsonCache.New(func() ([]byte, error) {
return json.Marshal(openapi)
})
o.pbCache = o.pbCache.New(func() ([]byte, error) {
json, err := o.jsonCache.Get()
if err != nil {
return nil, err
}
return ToV3ProtoBinary(json)
})
// TODO: This forces a json marshal of corresponding group-versions.
// We should look to replace this with a faster hashing mechanism.
o.etagCache = o.etagCache.New(func() ([]byte, error) {
json, err := o.jsonCache.Get()
if err != nil {
return nil, err
}
return []byte(computeETag(json)), nil
})
o.lastModified = time.Now()
return nil
}

View File

@@ -18,7 +18,7 @@ package internal
// Used by tests to selectively disable experimental JSON unmarshaler
var UseOptimizedJSONUnmarshaling bool = true
var UseOptimizedJSONUnmarshalingV3 bool = false
var UseOptimizedJSONUnmarshalingV3 bool = true
// Used by tests to selectively disable experimental JSON marshaler
var UseOptimizedJSONMarshaling bool = true

View File

@@ -1,57 +0,0 @@
/*
Copyright 2021 The Kubernetes 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 handler
import (
"sync"
)
// HandlerCache represents a lazy cache for generating a byte array
// It is used to lazily marshal OpenAPI v2/v3 and lazily generate the ETag
type HandlerCache struct {
BuildCache func() ([]byte, error)
once sync.Once
bytes []byte
err error
}
// Get either returns the cached value or calls BuildCache() once before caching and returning
// its results. If BuildCache returns an error, the last valid value for the cache (from prior
// calls to New()) is used instead if possible.
func (c *HandlerCache) Get() ([]byte, error) {
c.once.Do(func() {
bytes, err := c.BuildCache()
// if there is an error updating the cache, there can be situations where
// c.bytes contains a valid value (carried over from the previous update)
// but c.err is also not nil; the cache user is expected to check for this
c.err = err
if c.err == nil {
// don't override previous spec if we had an error
c.bytes = bytes
}
})
return c.bytes, c.err
}
// New creates a new HandlerCache for situations where a cache refresh is needed.
// This function is not thread-safe and should not be called at the same time as Get().
func (c *HandlerCache) New(cacheBuilder func() ([]byte, error)) HandlerCache {
return HandlerCache{
bytes: c.bytes,
BuildCache: cacheBuilder,
}
}

View File

@@ -18,7 +18,10 @@ package spec3
import (
"encoding/json"
"github.com/go-openapi/swag"
"k8s.io/kube-openapi/pkg/internal"
jsonv2 "k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json"
"k8s.io/kube-openapi/pkg/validation/spec"
)
@@ -41,6 +44,9 @@ func (e *Encoding) MarshalJSON() ([]byte, error) {
}
func (e *Encoding) UnmarshalJSON(data []byte) error {
if internal.UseOptimizedJSONUnmarshalingV3 {
return jsonv2.Unmarshal(data, e)
}
if err := json.Unmarshal(data, &e.EncodingProps); err != nil {
return err
}
@@ -50,6 +56,20 @@ func (e *Encoding) UnmarshalJSON(data []byte) error {
return nil
}
func (e *Encoding) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) error {
var x struct {
spec.Extensions
EncodingProps
}
if err := opts.UnmarshalNext(dec, &x); err != nil {
return err
}
e.Extensions = internal.SanitizeExtensions(x.Extensions)
e.EncodingProps = x.EncodingProps
return nil
}
type EncodingProps struct {
// Content Type for encoding a specific property
ContentType string `json:"contentType,omitempty"`

View File

@@ -20,6 +20,9 @@ import (
"encoding/json"
"github.com/go-openapi/swag"
"k8s.io/kube-openapi/pkg/internal"
jsonv2 "k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json"
"k8s.io/kube-openapi/pkg/validation/spec"
)
@@ -49,6 +52,9 @@ func (e *Example) MarshalJSON() ([]byte, error) {
}
func (e *Example) UnmarshalJSON(data []byte) error {
if internal.UseOptimizedJSONUnmarshalingV3 {
return jsonv2.Unmarshal(data, e)
}
if err := json.Unmarshal(data, &e.Refable); err != nil {
return err
}
@@ -61,6 +67,23 @@ func (e *Example) UnmarshalJSON(data []byte) error {
return nil
}
func (e *Example) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) error {
var x struct {
spec.Extensions
ExampleProps
}
if err := opts.UnmarshalNext(dec, &x); err != nil {
return err
}
if err := internal.JSONRefFromMap(&e.Ref.Ref, x.Extensions); err != nil {
return err
}
e.Extensions = internal.SanitizeExtensions(x.Extensions)
e.ExampleProps = x.ExampleProps
return nil
}
type ExampleProps struct {
// Summary holds a short description of the example
Summary string `json:"summary,omitempty"`

View File

@@ -18,7 +18,10 @@ package spec3
import (
"encoding/json"
"github.com/go-openapi/swag"
"k8s.io/kube-openapi/pkg/internal"
jsonv2 "k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json"
"k8s.io/kube-openapi/pkg/validation/spec"
)
@@ -48,6 +51,9 @@ func (e *ExternalDocumentation) MarshalJSON() ([]byte, error) {
}
func (e *ExternalDocumentation) UnmarshalJSON(data []byte) error {
if internal.UseOptimizedJSONUnmarshalingV3 {
return jsonv2.Unmarshal(data, e)
}
if err := json.Unmarshal(data, &e.ExternalDocumentationProps); err != nil {
return err
}
@@ -56,3 +62,16 @@ func (e *ExternalDocumentation) UnmarshalJSON(data []byte) error {
}
return nil
}
func (e *ExternalDocumentation) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) error {
var x struct {
spec.Extensions
ExternalDocumentationProps
}
if err := opts.UnmarshalNext(dec, &x); err != nil {
return err
}
e.Extensions = internal.SanitizeExtensions(x.Extensions)
e.ExternalDocumentationProps = x.ExternalDocumentationProps
return nil
}

View File

@@ -1,10 +1,11 @@
package spec3
import (
fuzz "github.com/google/gofuzz"
"math/rand"
"strings"
fuzz "github.com/google/gofuzz"
"k8s.io/kube-openapi/pkg/validation/spec"
)
@@ -169,82 +170,85 @@ var OpenAPIV3FuzzFuncs []interface{} = []interface{}{
c.Fuzz(&v.VendorExtensible)
},
func(v *spec.Extensions, c fuzz.Continue) {
*v = spec.Extensions{}
numChildren := c.Intn(5)
if numChildren == 0 {
*v = nil
}
for i := 0; i < numChildren; i++ {
v.Add("x-"+randAlphanumString(), c.RandString()+"x")
if *v == nil {
*v = spec.Extensions{}
}
(*v)["x-"+c.RandString()] = c.RandString()
}
},
func(v *spec.ExternalDocumentation, c fuzz.Continue) {
c.Fuzz(&v.Description)
v.URL = "https://" + randAlphanumString()
},
func(v *spec.SchemaURL, c fuzz.Continue) {
*v = spec.SchemaURL("https://" + randAlphanumString())
},
func(v *spec.SchemaOrBool, c fuzz.Continue) {
*v = spec.SchemaOrBool{}
if c.RandBool() {
v.Allows = c.RandBool()
} else {
v.Schema = &spec.Schema{}
v.Allows = true
c.Fuzz(&v.Schema)
}
},
func(v *spec.SchemaOrArray, c fuzz.Continue) {
*v = spec.SchemaOrArray{}
if c.RandBool() {
schema := spec.Schema{}
c.Fuzz(&schema)
v.Schema = &schema
} else {
v.Schemas = []spec.Schema{}
numChildren := c.Intn(5)
for i := 0; i < numChildren; i++ {
schema := spec.Schema{}
c.Fuzz(&schema)
v.Schemas = append(v.Schemas, schema)
}
}
},
func(v *spec.SchemaOrStringArray, c fuzz.Continue) {
if c.RandBool() {
*v = spec.SchemaOrStringArray{}
if c.RandBool() {
c.Fuzz(&v.Property)
} else {
c.Fuzz(&v.Schema)
}
}
},
func(v *spec.Schema, c fuzz.Continue) {
if c.Intn(refChance) == 0 {
c.Fuzz(&v.Ref)
return
}
c.Fuzz(&v.VendorExtensible)
c.Fuzz(&v.Description)
c.Fuzz(&v.Nullable)
c.Fuzz(&v.Title)
c.Fuzz(&v.Required)
c.Fuzz(&v.ExternalDocs)
n := c.Intn(8)
switch n {
case 0:
// To prevent exponential growth from recursively generating properties, only allow the schema to be an object with low frequency
if c.Intn(5) == 0 {
c.Fuzz(&v.Properties)
c.Fuzz(&v.MinProperties)
c.Fuzz(&v.MaxProperties)
} else {
v.Type = spec.StringOrArray{"integer"}
switch c.Intn(3) {
case 0:
v.Format = "int32"
case 1:
v.Format = "int64"
}
c.Fuzz(&v.MultipleOf)
c.Fuzz(&v.Minimum)
c.Fuzz(&v.Maximum)
c.Fuzz(&v.ExclusiveMaximum)
c.Fuzz(&v.ExclusiveMinimum)
}
case 1:
v.Type = spec.StringOrArray{"number"}
switch c.Intn(3) {
case 0:
v.Format = "float"
case 1:
v.Format = "double"
}
c.Fuzz(&v.MultipleOf)
c.Fuzz(&v.ExclusiveMaximum)
c.Fuzz(&v.ExclusiveMinimum)
c.Fuzz(&v.Minimum)
c.Fuzz(&v.Maximum)
case 2:
v.Type = spec.StringOrArray{"string"}
c.Fuzz(&v.MinLength)
c.Fuzz(&v.MaxLength)
case 3:
v.Type = spec.StringOrArray{"boolean"}
case 4:
v.Type = spec.StringOrArray{"array"}
s := spec.Schema{}
c.Fuzz(&s)
v.Items = &spec.SchemaOrArray{Schema: &s}
case 5:
c.Fuzz(&v.AnyOf)
case 6:
c.Fuzz(&v.AllOf)
case 7:
c.Fuzz(&v.OneOf)
if c.RandBool() {
// file schema
c.Fuzz(&v.Default)
c.Fuzz(&v.Description)
c.Fuzz(&v.Example)
c.Fuzz(&v.ExternalDocs)
c.Fuzz(&v.Format)
c.Fuzz(&v.ReadOnly)
c.Fuzz(&v.Required)
c.Fuzz(&v.Title)
v.Type = spec.StringOrArray{"file"}
} else {
// normal schema
c.Fuzz(&v.SchemaProps)
c.Fuzz(&v.SwaggerSchemaProps)
c.Fuzz(&v.VendorExtensible)
c.Fuzz(&v.ExtraProps)
}
},
}

View File

@@ -20,6 +20,8 @@ import (
"encoding/json"
"github.com/go-openapi/swag"
"k8s.io/kube-openapi/pkg/internal"
jsonv2 "k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json"
"k8s.io/kube-openapi/pkg/validation/spec"
)
@@ -50,6 +52,9 @@ func (h *Header) MarshalJSON() ([]byte, error) {
}
func (h *Header) UnmarshalJSON(data []byte) error {
if internal.UseOptimizedJSONUnmarshalingV3 {
return jsonv2.Unmarshal(data, h)
}
if err := json.Unmarshal(data, &h.Refable); err != nil {
return err
}
@@ -63,6 +68,22 @@ func (h *Header) UnmarshalJSON(data []byte) error {
return nil
}
func (h *Header) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) error {
var x struct {
spec.Extensions
HeaderProps
}
if err := opts.UnmarshalNext(dec, &x); err != nil {
return err
}
if err := internal.JSONRefFromMap(&h.Ref.Ref, x.Extensions); err != nil {
return err
}
h.Extensions = internal.SanitizeExtensions(x.Extensions)
h.HeaderProps = x.HeaderProps
return nil
}
// HeaderProps a struct that describes a header object
type HeaderProps struct {
// Description holds a brief description of the parameter

View File

@@ -18,7 +18,10 @@ package spec3
import (
"encoding/json"
"github.com/go-openapi/swag"
"k8s.io/kube-openapi/pkg/internal"
jsonv2 "k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json"
"k8s.io/kube-openapi/pkg/validation/spec"
)
@@ -44,6 +47,9 @@ func (m *MediaType) MarshalJSON() ([]byte, error) {
}
func (m *MediaType) UnmarshalJSON(data []byte) error {
if internal.UseOptimizedJSONUnmarshalingV3 {
return jsonv2.Unmarshal(data, m)
}
if err := json.Unmarshal(data, &m.MediaTypeProps); err != nil {
return err
}
@@ -53,6 +59,20 @@ func (m *MediaType) UnmarshalJSON(data []byte) error {
return nil
}
func (m *MediaType) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) error {
var x struct {
spec.Extensions
MediaTypeProps
}
if err := opts.UnmarshalNext(dec, &x); err != nil {
return err
}
m.Extensions = internal.SanitizeExtensions(x.Extensions)
m.MediaTypeProps = x.MediaTypeProps
return nil
}
// MediaTypeProps a struct that allows you to specify content format, more at https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#mediaTypeObject
type MediaTypeProps struct {
// Schema holds the schema defining the type used for the media type

View File

@@ -20,6 +20,8 @@ import (
"encoding/json"
"github.com/go-openapi/swag"
"k8s.io/kube-openapi/pkg/internal"
jsonv2 "k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json"
"k8s.io/kube-openapi/pkg/validation/spec"
)
@@ -46,12 +48,28 @@ func (o *Operation) MarshalJSON() ([]byte, error) {
// UnmarshalJSON hydrates this items instance with the data from JSON
func (o *Operation) UnmarshalJSON(data []byte) error {
if internal.UseOptimizedJSONUnmarshalingV3 {
return jsonv2.Unmarshal(data, o)
}
if err := json.Unmarshal(data, &o.OperationProps); err != nil {
return err
}
return json.Unmarshal(data, &o.VendorExtensible)
}
func (o *Operation) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) error {
var x struct {
spec.Extensions
OperationProps
}
if err := opts.UnmarshalNext(dec, &x); err != nil {
return err
}
o.Extensions = internal.SanitizeExtensions(x.Extensions)
o.OperationProps = x.OperationProps
return nil
}
// OperationProps describes a single API operation on a path, more at https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#operationObject
type OperationProps struct {
// Tags holds a list of tags for API documentation control

View File

@@ -20,6 +20,8 @@ import (
"encoding/json"
"github.com/go-openapi/swag"
"k8s.io/kube-openapi/pkg/internal"
jsonv2 "k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json"
"k8s.io/kube-openapi/pkg/validation/spec"
)
@@ -50,6 +52,10 @@ func (p *Parameter) MarshalJSON() ([]byte, error) {
}
func (p *Parameter) UnmarshalJSON(data []byte) error {
if internal.UseOptimizedJSONUnmarshalingV3 {
return jsonv2.Unmarshal(data, p)
}
if err := json.Unmarshal(data, &p.Refable); err != nil {
return err
}
@@ -63,6 +69,22 @@ func (p *Parameter) UnmarshalJSON(data []byte) error {
return nil
}
func (p *Parameter) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) error {
var x struct {
spec.Extensions
ParameterProps
}
if err := opts.UnmarshalNext(dec, &x); err != nil {
return err
}
if err := internal.JSONRefFromMap(&p.Ref.Ref, x.Extensions); err != nil {
return err
}
p.Extensions = internal.SanitizeExtensions(x.Extensions)
p.ParameterProps = x.ParameterProps
return nil
}
// ParameterProps a struct that describes a single operation parameter, more at https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#parameterObject
type ParameterProps struct {
// Name holds the name of the parameter

View File

@@ -18,9 +18,12 @@ package spec3
import (
"encoding/json"
"fmt"
"strings"
"github.com/go-openapi/swag"
"k8s.io/kube-openapi/pkg/internal"
jsonv2 "k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json"
"k8s.io/kube-openapi/pkg/validation/spec"
)
@@ -45,6 +48,9 @@ func (p *Paths) MarshalJSON() ([]byte, error) {
// UnmarshalJSON hydrates this items instance with the data from JSON
func (p *Paths) UnmarshalJSON(data []byte) error {
if internal.UseOptimizedJSONUnmarshalingV3 {
return jsonv2.Unmarshal(data, p)
}
var res map[string]json.RawMessage
if err := json.Unmarshal(data, &res); err != nil {
return err
@@ -74,6 +80,59 @@ func (p *Paths) UnmarshalJSON(data []byte) error {
return nil
}
func (p *Paths) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) error {
tok, err := dec.ReadToken()
if err != nil {
return err
}
switch k := tok.Kind(); k {
case 'n':
*p = Paths{}
return nil
case '{':
for {
tok, err := dec.ReadToken()
if err != nil {
return err
}
if tok.Kind() == '}' {
return nil
}
switch k := tok.String(); {
case internal.IsExtensionKey(k):
var ext any
if err := opts.UnmarshalNext(dec, &ext); err != nil {
return err
}
if p.Extensions == nil {
p.Extensions = make(map[string]any)
}
p.Extensions[k] = ext
case len(k) > 0 && k[0] == '/':
pi := Path{}
if err := opts.UnmarshalNext(dec, &pi); err != nil {
return err
}
if p.Paths == nil {
p.Paths = make(map[string]*Path)
}
p.Paths[k] = &pi
default:
_, err := dec.ReadValue() // skip value
if err != nil {
return err
}
}
}
default:
return fmt.Errorf("unknown JSON kind: %v", k)
}
}
// Path describes the operations available on a single path, more at https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#pathItemObject
//
// Note that this struct is actually a thin wrapper around PathProps to make it referable and extensible
@@ -101,6 +160,9 @@ func (p *Path) MarshalJSON() ([]byte, error) {
}
func (p *Path) UnmarshalJSON(data []byte) error {
if internal.UseOptimizedJSONUnmarshalingV3 {
return jsonv2.Unmarshal(data, p)
}
if err := json.Unmarshal(data, &p.Refable); err != nil {
return err
}
@@ -113,6 +175,24 @@ func (p *Path) UnmarshalJSON(data []byte) error {
return nil
}
func (p *Path) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) error {
var x struct {
spec.Extensions
PathProps
}
if err := opts.UnmarshalNext(dec, &x); err != nil {
return err
}
if err := internal.JSONRefFromMap(&p.Ref.Ref, x.Extensions); err != nil {
return err
}
p.Extensions = internal.SanitizeExtensions(x.Extensions)
p.PathProps = x.PathProps
return nil
}
// PathProps describes the operations available on a single path, more at https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#pathItemObject
type PathProps struct {
// Summary holds a summary for all operations in this path

View File

@@ -20,6 +20,8 @@ import (
"encoding/json"
"github.com/go-openapi/swag"
"k8s.io/kube-openapi/pkg/internal"
jsonv2 "k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json"
"k8s.io/kube-openapi/pkg/validation/spec"
)
@@ -50,6 +52,9 @@ func (r *RequestBody) MarshalJSON() ([]byte, error) {
}
func (r *RequestBody) UnmarshalJSON(data []byte) error {
if internal.UseOptimizedJSONUnmarshalingV3 {
return jsonv2.Unmarshal(data, r)
}
if err := json.Unmarshal(data, &r.Refable); err != nil {
return err
}
@@ -71,3 +76,19 @@ type RequestBodyProps struct {
// Required determines if the request body is required in the request
Required bool `json:"required,omitempty"`
}
func (r *RequestBody) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) error {
var x struct {
spec.Extensions
RequestBodyProps
}
if err := opts.UnmarshalNext(dec, &x); err != nil {
return err
}
if err := internal.JSONRefFromMap(&r.Ref.Ref, x.Extensions); err != nil {
return err
}
r.Extensions = internal.SanitizeExtensions(x.Extensions)
r.RequestBodyProps = x.RequestBodyProps
return nil
}

View File

@@ -18,9 +18,12 @@ package spec3
import (
"encoding/json"
"fmt"
"strconv"
"github.com/go-openapi/swag"
"k8s.io/kube-openapi/pkg/internal"
jsonv2 "k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json"
"k8s.io/kube-openapi/pkg/validation/spec"
)
@@ -46,13 +49,15 @@ func (r *Responses) MarshalJSON() ([]byte, error) {
}
func (r *Responses) UnmarshalJSON(data []byte) error {
if internal.UseOptimizedJSONUnmarshalingV3 {
return jsonv2.Unmarshal(data, r)
}
if err := json.Unmarshal(data, &r.ResponsesProps); err != nil {
return err
}
if err := json.Unmarshal(data, &r.VendorExtensible); err != nil {
return err
}
return nil
}
@@ -78,6 +83,9 @@ func (r ResponsesProps) MarshalJSON() ([]byte, error) {
// UnmarshalJSON unmarshals responses from JSON
func (r *ResponsesProps) UnmarshalJSON(data []byte) error {
if internal.UseOptimizedJSONUnmarshalingV3 {
return jsonv2.Unmarshal(data, r)
}
var res map[string]json.RawMessage
if err := json.Unmarshal(data, &res); err != nil {
return err
@@ -106,6 +114,60 @@ func (r *ResponsesProps) UnmarshalJSON(data []byte) error {
return nil
}
func (r *Responses) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) (err error) {
tok, err := dec.ReadToken()
if err != nil {
return err
}
switch k := tok.Kind(); k {
case 'n':
*r = Responses{}
return nil
case '{':
for {
tok, err := dec.ReadToken()
if err != nil {
return err
}
if tok.Kind() == '}' {
return nil
}
switch k := tok.String(); {
case internal.IsExtensionKey(k):
var ext any
if err := opts.UnmarshalNext(dec, &ext); err != nil {
return err
}
if r.Extensions == nil {
r.Extensions = make(map[string]any)
}
r.Extensions[k] = ext
case k == "default":
resp := Response{}
if err := opts.UnmarshalNext(dec, &resp); err != nil {
return err
}
r.ResponsesProps.Default = &resp
default:
if nk, err := strconv.Atoi(k); err == nil {
resp := Response{}
if err := opts.UnmarshalNext(dec, &resp); err != nil {
return err
}
if r.StatusCodeResponses == nil {
r.StatusCodeResponses = map[int]*Response{}
}
r.StatusCodeResponses[nk] = &resp
}
}
}
default:
return fmt.Errorf("unknown JSON kind: %v", k)
}
}
// Response describes a single response from an API Operation, more at https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#responseObject
//
// Note that this struct is actually a thin wrapper around ResponseProps to make it referable and extensible
@@ -133,6 +195,9 @@ func (r *Response) MarshalJSON() ([]byte, error) {
}
func (r *Response) UnmarshalJSON(data []byte) error {
if internal.UseOptimizedJSONUnmarshalingV3 {
return jsonv2.Unmarshal(data, r)
}
if err := json.Unmarshal(data, &r.Refable); err != nil {
return err
}
@@ -142,7 +207,22 @@ func (r *Response) UnmarshalJSON(data []byte) error {
if err := json.Unmarshal(data, &r.VendorExtensible); err != nil {
return err
}
return nil
}
func (r *Response) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) error {
var x struct {
spec.Extensions
ResponseProps
}
if err := opts.UnmarshalNext(dec, &x); err != nil {
return err
}
if err := internal.JSONRefFromMap(&r.Ref.Ref, x.Extensions); err != nil {
return err
}
r.Extensions = internal.SanitizeExtensions(x.Extensions)
r.ResponseProps = x.ResponseProps
return nil
}
@@ -183,6 +263,9 @@ func (r *Link) MarshalJSON() ([]byte, error) {
}
func (r *Link) UnmarshalJSON(data []byte) error {
if internal.UseOptimizedJSONUnmarshalingV3 {
return jsonv2.Unmarshal(data, r)
}
if err := json.Unmarshal(data, &r.Refable); err != nil {
return err
}
@@ -196,6 +279,22 @@ func (r *Link) UnmarshalJSON(data []byte) error {
return nil
}
func (l *Link) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) error {
var x struct {
spec.Extensions
LinkProps
}
if err := opts.UnmarshalNext(dec, &x); err != nil {
return err
}
if err := internal.JSONRefFromMap(&l.Ref.Ref, x.Extensions); err != nil {
return err
}
l.Extensions = internal.SanitizeExtensions(x.Extensions)
l.LinkProps = x.LinkProps
return nil
}
// LinkProps describes a single response from an API Operation, more at https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#responseObject
type LinkProps struct {
// OperationId is the name of an existing, resolvable OAS operation

View File

@@ -18,7 +18,10 @@ package spec3
import (
"encoding/json"
"github.com/go-openapi/swag"
"k8s.io/kube-openapi/pkg/internal"
jsonv2 "k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json"
"k8s.io/kube-openapi/pkg/validation/spec"
)
@@ -50,6 +53,10 @@ func (s *Server) MarshalJSON() ([]byte, error) {
}
func (s *Server) UnmarshalJSON(data []byte) error {
if internal.UseOptimizedJSONUnmarshalingV3 {
return jsonv2.Unmarshal(data, s)
}
if err := json.Unmarshal(data, &s.ServerProps); err != nil {
return err
}
@@ -59,6 +66,20 @@ func (s *Server) UnmarshalJSON(data []byte) error {
return nil
}
func (s *Server) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) error {
var x struct {
spec.Extensions
ServerProps
}
if err := opts.UnmarshalNext(dec, &x); err != nil {
return err
}
s.Extensions = internal.SanitizeExtensions(x.Extensions)
s.ServerProps = x.ServerProps
return nil
}
type ServerVariable struct {
ServerVariableProps
spec.VendorExtensible
@@ -87,6 +108,9 @@ func (s *ServerVariable) MarshalJSON() ([]byte, error) {
}
func (s *ServerVariable) UnmarshalJSON(data []byte) error {
if internal.UseOptimizedJSONUnmarshalingV3 {
return jsonv2.Unmarshal(data, s)
}
if err := json.Unmarshal(data, &s.ServerVariableProps); err != nil {
return err
}
@@ -95,3 +119,17 @@ func (s *ServerVariable) UnmarshalJSON(data []byte) error {
}
return nil
}
func (s *ServerVariable) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) error {
var x struct {
spec.Extensions
ServerVariableProps
}
if err := opts.UnmarshalNext(dec, &x); err != nil {
return err
}
s.Extensions = internal.SanitizeExtensions(x.Extensions)
s.ServerVariableProps = x.ServerVariableProps
return nil
}

View File

@@ -17,6 +17,10 @@ limitations under the License.
package spec3
import (
"encoding/json"
"k8s.io/kube-openapi/pkg/internal"
jsonv2 "k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json"
"k8s.io/kube-openapi/pkg/validation/spec"
)
@@ -35,3 +39,12 @@ type OpenAPI struct {
// ExternalDocs holds additional external documentation
ExternalDocs *ExternalDocumentation `json:"externalDocs,omitempty"`
}
func (o *OpenAPI) UnmarshalJSON(data []byte) error {
type OpenAPIWithNoFunctions OpenAPI
p := (*OpenAPIWithNoFunctions)(o)
if internal.UseOptimizedJSONUnmarshalingV3 {
return jsonv2.Unmarshal(data, &p)
}
return json.Unmarshal(data, &p)
}

View File

@@ -523,6 +523,7 @@ func (s Schema) MarshalNextJSON(opts jsonv2.MarshalOptions, enc *jsonv2.Encoder)
ArbitraryKeys
SchemaProps schemaPropsOmitZero `json:",inline"`
SwaggerSchemaProps swaggerSchemaPropsOmitZero `json:",inline"`
Schema string `json:"$schema,omitempty"`
Ref string `json:"$ref,omitempty"`
}
x.ArbitraryKeys = make(map[string]any, len(s.Extensions)+len(s.ExtraProps))
@@ -537,6 +538,7 @@ func (s Schema) MarshalNextJSON(opts jsonv2.MarshalOptions, enc *jsonv2.Encoder)
x.SchemaProps = schemaPropsOmitZero(s.SchemaProps)
x.SwaggerSchemaProps = swaggerSchemaPropsOmitZero(s.SwaggerSchemaProps)
x.Ref = s.Ref.String()
x.Schema = string(s.Schema)
return opts.MarshalNext(enc, x)
}
@@ -622,7 +624,7 @@ func (s *Schema) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Dec
}
s.ExtraProps = x.Extensions.sanitizeWithExtra()
s.VendorExtensible.Extensions = x.Extensions
s.Extensions = internal.SanitizeExtensions(x.Extensions)
s.SchemaProps = x.SchemaProps
s.SwaggerSchemaProps = x.SwaggerSchemaProps
return nil

View File

@@ -164,15 +164,15 @@ func (s *SchemaOrBool) UnmarshalJSON(data []byte) error {
}
var nw SchemaOrBool
if len(data) >= 4 {
if data[0] == '{' {
var sch Schema
if err := json.Unmarshal(data, &sch); err != nil {
return err
}
nw.Schema = &sch
if len(data) > 0 && data[0] == '{' {
var sch Schema
if err := json.Unmarshal(data, &sch); err != nil {
return err
}
nw.Allows = !(data[0] == 'f' && data[1] == 'a' && data[2] == 'l' && data[3] == 's' && data[4] == 'e')
nw.Schema = &sch
nw.Allows = true
} else {
json.Unmarshal(data, &nw.Allows)
}
*s = nw
return nil
@@ -385,7 +385,7 @@ func (s SchemaOrArray) MarshalJSON() ([]byte, error) {
if internal.UseOptimizedJSONMarshaling {
return internal.DeterministicMarshal(s)
}
if len(s.Schemas) > 0 {
if s.Schemas != nil {
return json.Marshal(s.Schemas)
}
return json.Marshal(s.Schema)
@@ -393,7 +393,7 @@ func (s SchemaOrArray) MarshalJSON() ([]byte, error) {
// MarshalJSON converts this schema object or array into JSON structure
func (s SchemaOrArray) MarshalNextJSON(opts jsonv2.MarshalOptions, enc *jsonv2.Encoder) error {
if len(s.Schemas) > 0 {
if s.Schemas != nil {
return opts.MarshalNext(enc, s.Schemas)
}
return opts.MarshalNext(enc, s.Schema)