k8s.io/kube-openapi@v0.0.0-20240228011516-70dd3763d340/pkg/validation/spec/swagger.go (about)

     1  // Copyright 2015 go-swagger maintainers
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //    http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package spec
    16  
    17  import (
    18  	"encoding/json"
    19  	"fmt"
    20  
    21  	"github.com/go-openapi/swag"
    22  	"k8s.io/kube-openapi/pkg/internal"
    23  	jsonv2 "k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json"
    24  )
    25  
    26  // Swagger this is the root document object for the API specification.
    27  // It combines what previously was the Resource Listing and API Declaration (version 1.2 and earlier)
    28  // together into one document.
    29  //
    30  // For more information: http://goo.gl/8us55a#swagger-object-
    31  type Swagger struct {
    32  	VendorExtensible
    33  	SwaggerProps
    34  }
    35  
    36  // MarshalJSON marshals this swagger structure to json
    37  func (s Swagger) MarshalJSON() ([]byte, error) {
    38  	if internal.UseOptimizedJSONMarshaling {
    39  		return internal.DeterministicMarshal(s)
    40  	}
    41  	b1, err := json.Marshal(s.SwaggerProps)
    42  	if err != nil {
    43  		return nil, err
    44  	}
    45  	b2, err := json.Marshal(s.VendorExtensible)
    46  	if err != nil {
    47  		return nil, err
    48  	}
    49  	return swag.ConcatJSON(b1, b2), nil
    50  }
    51  
    52  // MarshalJSON marshals this swagger structure to json
    53  func (s Swagger) MarshalNextJSON(opts jsonv2.MarshalOptions, enc *jsonv2.Encoder) error {
    54  	var x struct {
    55  		Extensions
    56  		SwaggerProps
    57  	}
    58  	x.Extensions = internal.SanitizeExtensions(s.Extensions)
    59  	x.SwaggerProps = s.SwaggerProps
    60  	return opts.MarshalNext(enc, x)
    61  }
    62  
    63  // UnmarshalJSON unmarshals a swagger spec from json
    64  func (s *Swagger) UnmarshalJSON(data []byte) error {
    65  	if internal.UseOptimizedJSONUnmarshaling {
    66  		return jsonv2.Unmarshal(data, s)
    67  	}
    68  	var sw Swagger
    69  	if err := json.Unmarshal(data, &sw.SwaggerProps); err != nil {
    70  		return err
    71  	}
    72  	if err := json.Unmarshal(data, &sw.VendorExtensible); err != nil {
    73  		return err
    74  	}
    75  	*s = sw
    76  	return nil
    77  }
    78  
    79  func (s *Swagger) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) error {
    80  	// Note: If you're willing to make breaking changes, it is possible to
    81  	// optimize this and other usages of this pattern:
    82  	// https://github.com/kubernetes/kube-openapi/pull/319#discussion_r983165948
    83  	var x struct {
    84  		Extensions
    85  		SwaggerProps
    86  	}
    87  
    88  	if err := opts.UnmarshalNext(dec, &x); err != nil {
    89  		return err
    90  	}
    91  	s.Extensions = internal.SanitizeExtensions(x.Extensions)
    92  	s.SwaggerProps = x.SwaggerProps
    93  	return nil
    94  }
    95  
    96  // SwaggerProps captures the top-level properties of an Api specification
    97  //
    98  // NOTE: validation rules
    99  // - the scheme, when present must be from [http, https, ws, wss]
   100  // - BasePath must start with a leading "/"
   101  // - Paths is required
   102  type SwaggerProps struct {
   103  	ID                  string                 `json:"id,omitempty"`
   104  	Consumes            []string               `json:"consumes,omitempty"`
   105  	Produces            []string               `json:"produces,omitempty"`
   106  	Schemes             []string               `json:"schemes,omitempty"`
   107  	Swagger             string                 `json:"swagger,omitempty"`
   108  	Info                *Info                  `json:"info,omitempty"`
   109  	Host                string                 `json:"host,omitempty"`
   110  	BasePath            string                 `json:"basePath,omitempty"`
   111  	Paths               *Paths                 `json:"paths"`
   112  	Definitions         Definitions            `json:"definitions,omitempty"`
   113  	Parameters          map[string]Parameter   `json:"parameters,omitempty"`
   114  	Responses           map[string]Response    `json:"responses,omitempty"`
   115  	SecurityDefinitions SecurityDefinitions    `json:"securityDefinitions,omitempty"`
   116  	Security            []map[string][]string  `json:"security,omitempty"`
   117  	Tags                []Tag                  `json:"tags,omitempty"`
   118  	ExternalDocs        *ExternalDocumentation `json:"externalDocs,omitempty"`
   119  }
   120  
   121  // Dependencies represent a dependencies property
   122  type Dependencies map[string]SchemaOrStringArray
   123  
   124  // SchemaOrBool represents a schema or boolean value, is biased towards true for the boolean property
   125  type SchemaOrBool struct {
   126  	Allows bool
   127  	Schema *Schema
   128  }
   129  
   130  var jsTrue = []byte("true")
   131  var jsFalse = []byte("false")
   132  
   133  // MarshalJSON convert this object to JSON
   134  func (s SchemaOrBool) MarshalJSON() ([]byte, error) {
   135  	if internal.UseOptimizedJSONMarshaling {
   136  		return internal.DeterministicMarshal(s)
   137  	}
   138  	if s.Schema != nil {
   139  		return json.Marshal(s.Schema)
   140  	}
   141  
   142  	if s.Schema == nil && !s.Allows {
   143  		return jsFalse, nil
   144  	}
   145  	return jsTrue, nil
   146  }
   147  
   148  // MarshalJSON convert this object to JSON
   149  func (s SchemaOrBool) MarshalNextJSON(opts jsonv2.MarshalOptions, enc *jsonv2.Encoder) error {
   150  	if s.Schema != nil {
   151  		return opts.MarshalNext(enc, s.Schema)
   152  	}
   153  
   154  	if s.Schema == nil && !s.Allows {
   155  		return enc.WriteToken(jsonv2.False)
   156  	}
   157  	return enc.WriteToken(jsonv2.True)
   158  }
   159  
   160  // UnmarshalJSON converts this bool or schema object from a JSON structure
   161  func (s *SchemaOrBool) UnmarshalJSON(data []byte) error {
   162  	if internal.UseOptimizedJSONUnmarshaling {
   163  		return jsonv2.Unmarshal(data, s)
   164  	}
   165  
   166  	var nw SchemaOrBool
   167  	if len(data) > 0 && data[0] == '{' {
   168  		var sch Schema
   169  		if err := json.Unmarshal(data, &sch); err != nil {
   170  			return err
   171  		}
   172  		nw.Schema = &sch
   173  		nw.Allows = true
   174  	} else {
   175  		json.Unmarshal(data, &nw.Allows)
   176  	}
   177  	*s = nw
   178  	return nil
   179  }
   180  
   181  func (s *SchemaOrBool) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) error {
   182  	switch k := dec.PeekKind(); k {
   183  	case '{':
   184  		err := opts.UnmarshalNext(dec, &s.Schema)
   185  		if err != nil {
   186  			return err
   187  		}
   188  		s.Allows = true
   189  		return nil
   190  	case 't', 'f':
   191  		err := opts.UnmarshalNext(dec, &s.Allows)
   192  		if err != nil {
   193  			return err
   194  		}
   195  		return nil
   196  	default:
   197  		return fmt.Errorf("expected object or bool, not '%v'", k.String())
   198  	}
   199  }
   200  
   201  // SchemaOrStringArray represents a schema or a string array
   202  type SchemaOrStringArray struct {
   203  	Schema   *Schema
   204  	Property []string
   205  }
   206  
   207  // MarshalJSON converts this schema object or array into JSON structure
   208  func (s SchemaOrStringArray) MarshalJSON() ([]byte, error) {
   209  	if internal.UseOptimizedJSONMarshaling {
   210  		return internal.DeterministicMarshal(s)
   211  	}
   212  	if len(s.Property) > 0 {
   213  		return json.Marshal(s.Property)
   214  	}
   215  	if s.Schema != nil {
   216  		return json.Marshal(s.Schema)
   217  	}
   218  	return []byte("null"), nil
   219  }
   220  
   221  // MarshalJSON converts this schema object or array into JSON structure
   222  func (s SchemaOrStringArray) MarshalNextJSON(opts jsonv2.MarshalOptions, enc *jsonv2.Encoder) error {
   223  	if len(s.Property) > 0 {
   224  		return opts.MarshalNext(enc, s.Property)
   225  	}
   226  	if s.Schema != nil {
   227  		return opts.MarshalNext(enc, s.Schema)
   228  	}
   229  	return enc.WriteToken(jsonv2.Null)
   230  }
   231  
   232  // UnmarshalJSON converts this schema object or array from a JSON structure
   233  func (s *SchemaOrStringArray) UnmarshalJSON(data []byte) error {
   234  	if internal.UseOptimizedJSONUnmarshaling {
   235  		return jsonv2.Unmarshal(data, s)
   236  	}
   237  
   238  	var first byte
   239  	if len(data) > 1 {
   240  		first = data[0]
   241  	}
   242  	var nw SchemaOrStringArray
   243  	if first == '{' {
   244  		var sch Schema
   245  		if err := json.Unmarshal(data, &sch); err != nil {
   246  			return err
   247  		}
   248  		nw.Schema = &sch
   249  	}
   250  	if first == '[' {
   251  		if err := json.Unmarshal(data, &nw.Property); err != nil {
   252  			return err
   253  		}
   254  	}
   255  	*s = nw
   256  	return nil
   257  }
   258  
   259  func (s *SchemaOrStringArray) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) error {
   260  	switch dec.PeekKind() {
   261  	case '{':
   262  		return opts.UnmarshalNext(dec, &s.Schema)
   263  	case '[':
   264  		return opts.UnmarshalNext(dec, &s.Property)
   265  	default:
   266  		_, err := dec.ReadValue()
   267  		return err
   268  	}
   269  }
   270  
   271  // Definitions contains the models explicitly defined in this spec
   272  // An object to hold data types that can be consumed and produced by operations.
   273  // These data types can be primitives, arrays or models.
   274  //
   275  // For more information: http://goo.gl/8us55a#definitionsObject
   276  type Definitions map[string]Schema
   277  
   278  // SecurityDefinitions a declaration of the security schemes available to be used in the specification.
   279  // This does not enforce the security schemes on the operations and only serves to provide
   280  // the relevant details for each scheme.
   281  //
   282  // For more information: http://goo.gl/8us55a#securityDefinitionsObject
   283  type SecurityDefinitions map[string]*SecurityScheme
   284  
   285  // StringOrArray represents a value that can either be a string
   286  // or an array of strings. Mainly here for serialization purposes
   287  type StringOrArray []string
   288  
   289  // Contains returns true when the value is contained in the slice
   290  func (s StringOrArray) Contains(value string) bool {
   291  	for _, str := range s {
   292  		if str == value {
   293  			return true
   294  		}
   295  	}
   296  	return false
   297  }
   298  
   299  // UnmarshalJSON unmarshals this string or array object from a JSON array or JSON string
   300  func (s *StringOrArray) UnmarshalJSON(data []byte) error {
   301  	if internal.UseOptimizedJSONUnmarshaling {
   302  		return jsonv2.Unmarshal(data, s)
   303  	}
   304  
   305  	var first byte
   306  	if len(data) > 1 {
   307  		first = data[0]
   308  	}
   309  
   310  	if first == '[' {
   311  		var parsed []string
   312  		if err := json.Unmarshal(data, &parsed); err != nil {
   313  			return err
   314  		}
   315  		*s = StringOrArray(parsed)
   316  		return nil
   317  	}
   318  
   319  	var single interface{}
   320  	if err := json.Unmarshal(data, &single); err != nil {
   321  		return err
   322  	}
   323  	if single == nil {
   324  		return nil
   325  	}
   326  	switch v := single.(type) {
   327  	case string:
   328  		*s = StringOrArray([]string{v})
   329  		return nil
   330  	default:
   331  		return fmt.Errorf("only string or array is allowed, not %T", single)
   332  	}
   333  }
   334  
   335  func (s *StringOrArray) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) error {
   336  	switch k := dec.PeekKind(); k {
   337  	case '[':
   338  		*s = StringOrArray{}
   339  		return opts.UnmarshalNext(dec, (*[]string)(s))
   340  	case '"':
   341  		*s = StringOrArray{""}
   342  		return opts.UnmarshalNext(dec, &(*s)[0])
   343  	case 'n':
   344  		// Throw out null token
   345  		_, _ = dec.ReadToken()
   346  		return nil
   347  	default:
   348  		return fmt.Errorf("expected string or array, not '%v'", k.String())
   349  	}
   350  }
   351  
   352  // MarshalJSON converts this string or array to a JSON array or JSON string
   353  func (s StringOrArray) MarshalJSON() ([]byte, error) {
   354  	if len(s) == 1 {
   355  		return json.Marshal([]string(s)[0])
   356  	}
   357  	return json.Marshal([]string(s))
   358  }
   359  
   360  // SchemaOrArray represents a value that can either be a Schema
   361  // or an array of Schema. Mainly here for serialization purposes
   362  type SchemaOrArray struct {
   363  	Schema  *Schema
   364  	Schemas []Schema
   365  }
   366  
   367  // Len returns the number of schemas in this property
   368  func (s SchemaOrArray) Len() int {
   369  	if s.Schema != nil {
   370  		return 1
   371  	}
   372  	return len(s.Schemas)
   373  }
   374  
   375  // ContainsType returns true when one of the schemas is of the specified type
   376  func (s *SchemaOrArray) ContainsType(name string) bool {
   377  	if s.Schema != nil {
   378  		return s.Schema.Type != nil && s.Schema.Type.Contains(name)
   379  	}
   380  	return false
   381  }
   382  
   383  // MarshalJSON converts this schema object or array into JSON structure
   384  func (s SchemaOrArray) MarshalJSON() ([]byte, error) {
   385  	if internal.UseOptimizedJSONMarshaling {
   386  		return internal.DeterministicMarshal(s)
   387  	}
   388  	if s.Schemas != nil {
   389  		return json.Marshal(s.Schemas)
   390  	}
   391  	return json.Marshal(s.Schema)
   392  }
   393  
   394  // MarshalJSON converts this schema object or array into JSON structure
   395  func (s SchemaOrArray) MarshalNextJSON(opts jsonv2.MarshalOptions, enc *jsonv2.Encoder) error {
   396  	if s.Schemas != nil {
   397  		return opts.MarshalNext(enc, s.Schemas)
   398  	}
   399  	return opts.MarshalNext(enc, s.Schema)
   400  }
   401  
   402  // UnmarshalJSON converts this schema object or array from a JSON structure
   403  func (s *SchemaOrArray) UnmarshalJSON(data []byte) error {
   404  	if internal.UseOptimizedJSONUnmarshaling {
   405  		return jsonv2.Unmarshal(data, s)
   406  	}
   407  
   408  	var nw SchemaOrArray
   409  	var first byte
   410  	if len(data) > 1 {
   411  		first = data[0]
   412  	}
   413  	if first == '{' {
   414  		var sch Schema
   415  		if err := json.Unmarshal(data, &sch); err != nil {
   416  			return err
   417  		}
   418  		nw.Schema = &sch
   419  	}
   420  	if first == '[' {
   421  		if err := json.Unmarshal(data, &nw.Schemas); err != nil {
   422  			return err
   423  		}
   424  	}
   425  	*s = nw
   426  	return nil
   427  }
   428  
   429  func (s *SchemaOrArray) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) error {
   430  	switch dec.PeekKind() {
   431  	case '{':
   432  		return opts.UnmarshalNext(dec, &s.Schema)
   433  	case '[':
   434  		return opts.UnmarshalNext(dec, &s.Schemas)
   435  	default:
   436  		_, err := dec.ReadValue()
   437  		return err
   438  	}
   439  }