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 }