k8s.io/kube-openapi@v0.0.0-20240228011516-70dd3763d340/pkg/spec3/response.go (about)

     1  /*
     2  Copyright 2021 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package spec3
    18  
    19  import (
    20  	"encoding/json"
    21  	"fmt"
    22  	"strconv"
    23  
    24  	"github.com/go-openapi/swag"
    25  	"k8s.io/kube-openapi/pkg/internal"
    26  	jsonv2 "k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json"
    27  	"k8s.io/kube-openapi/pkg/validation/spec"
    28  )
    29  
    30  // Responses holds the list of possible responses as they are returned from executing this operation
    31  //
    32  // Note that this struct is actually a thin wrapper around ResponsesProps to make it referable and extensible
    33  type Responses struct {
    34  	ResponsesProps
    35  	spec.VendorExtensible
    36  }
    37  
    38  // MarshalJSON is a custom marshal function that knows how to encode Responses as JSON
    39  func (r *Responses) MarshalJSON() ([]byte, error) {
    40  	if internal.UseOptimizedJSONMarshalingV3 {
    41  		return internal.DeterministicMarshal(r)
    42  	}
    43  	b1, err := json.Marshal(r.ResponsesProps)
    44  	if err != nil {
    45  		return nil, err
    46  	}
    47  	b2, err := json.Marshal(r.VendorExtensible)
    48  	if err != nil {
    49  		return nil, err
    50  	}
    51  	return swag.ConcatJSON(b1, b2), nil
    52  }
    53  
    54  func (r Responses) MarshalNextJSON(opts jsonv2.MarshalOptions, enc *jsonv2.Encoder) error {
    55  	type ArbitraryKeys map[string]interface{}
    56  	var x struct {
    57  		ArbitraryKeys
    58  		Default *Response `json:"default,omitzero"`
    59  	}
    60  	x.ArbitraryKeys = make(map[string]any, len(r.Extensions)+len(r.StatusCodeResponses))
    61  	for k, v := range r.Extensions {
    62  		if internal.IsExtensionKey(k) {
    63  			x.ArbitraryKeys[k] = v
    64  		}
    65  	}
    66  	for k, v := range r.StatusCodeResponses {
    67  		x.ArbitraryKeys[strconv.Itoa(k)] = v
    68  	}
    69  	x.Default = r.Default
    70  	return opts.MarshalNext(enc, x)
    71  }
    72  
    73  func (r *Responses) UnmarshalJSON(data []byte) error {
    74  	if internal.UseOptimizedJSONUnmarshalingV3 {
    75  		return jsonv2.Unmarshal(data, r)
    76  	}
    77  	if err := json.Unmarshal(data, &r.ResponsesProps); err != nil {
    78  		return err
    79  	}
    80  	if err := json.Unmarshal(data, &r.VendorExtensible); err != nil {
    81  		return err
    82  	}
    83  	return nil
    84  }
    85  
    86  // ResponsesProps holds the list of possible responses as they are returned from executing this operation
    87  type ResponsesProps struct {
    88  	// Default holds the documentation of responses other than the ones declared for specific HTTP response codes. Use this field to cover undeclared responses
    89  	Default *Response `json:"-"`
    90  	// StatusCodeResponses holds a map of any HTTP status code to the response definition
    91  	StatusCodeResponses map[int]*Response `json:"-"`
    92  }
    93  
    94  // MarshalJSON is a custom marshal function that knows how to encode ResponsesProps as JSON
    95  func (r ResponsesProps) MarshalJSON() ([]byte, error) {
    96  	toser := map[string]*Response{}
    97  	if r.Default != nil {
    98  		toser["default"] = r.Default
    99  	}
   100  	for k, v := range r.StatusCodeResponses {
   101  		toser[strconv.Itoa(k)] = v
   102  	}
   103  	return json.Marshal(toser)
   104  }
   105  
   106  // UnmarshalJSON unmarshals responses from JSON
   107  func (r *ResponsesProps) UnmarshalJSON(data []byte) error {
   108  	if internal.UseOptimizedJSONUnmarshalingV3 {
   109  		return jsonv2.Unmarshal(data, r)
   110  	}
   111  	var res map[string]json.RawMessage
   112  	if err := json.Unmarshal(data, &res); err != nil {
   113  		return err
   114  	}
   115  	if v, ok := res["default"]; ok {
   116  		value := Response{}
   117  		if err := json.Unmarshal(v, &value); err != nil {
   118  			return err
   119  		}
   120  		r.Default = &value
   121  		delete(res, "default")
   122  	}
   123  	for k, v := range res {
   124  		// Take all integral keys
   125  		if nk, err := strconv.Atoi(k); err == nil {
   126  			if r.StatusCodeResponses == nil {
   127  				r.StatusCodeResponses = map[int]*Response{}
   128  			}
   129  			value := Response{}
   130  			if err := json.Unmarshal(v, &value); err != nil {
   131  				return err
   132  			}
   133  			r.StatusCodeResponses[nk] = &value
   134  		}
   135  	}
   136  	return nil
   137  }
   138  
   139  func (r *Responses) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) (err error) {
   140  	tok, err := dec.ReadToken()
   141  	if err != nil {
   142  		return err
   143  	}
   144  	switch k := tok.Kind(); k {
   145  	case 'n':
   146  		*r = Responses{}
   147  		return nil
   148  	case '{':
   149  		for {
   150  			tok, err := dec.ReadToken()
   151  			if err != nil {
   152  				return err
   153  			}
   154  			if tok.Kind() == '}' {
   155  				return nil
   156  			}
   157  			switch k := tok.String(); {
   158  			case internal.IsExtensionKey(k):
   159  				var ext any
   160  				if err := opts.UnmarshalNext(dec, &ext); err != nil {
   161  					return err
   162  				}
   163  
   164  				if r.Extensions == nil {
   165  					r.Extensions = make(map[string]any)
   166  				}
   167  				r.Extensions[k] = ext
   168  			case k == "default":
   169  				resp := Response{}
   170  				if err := opts.UnmarshalNext(dec, &resp); err != nil {
   171  					return err
   172  				}
   173  				r.ResponsesProps.Default = &resp
   174  			default:
   175  				if nk, err := strconv.Atoi(k); err == nil {
   176  					resp := Response{}
   177  					if err := opts.UnmarshalNext(dec, &resp); err != nil {
   178  						return err
   179  					}
   180  
   181  					if r.StatusCodeResponses == nil {
   182  						r.StatusCodeResponses = map[int]*Response{}
   183  					}
   184  					r.StatusCodeResponses[nk] = &resp
   185  				}
   186  			}
   187  		}
   188  	default:
   189  		return fmt.Errorf("unknown JSON kind: %v", k)
   190  	}
   191  }
   192  
   193  // 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
   194  //
   195  // Note that this struct is actually a thin wrapper around ResponseProps to make it referable and extensible
   196  type Response struct {
   197  	spec.Refable
   198  	ResponseProps
   199  	spec.VendorExtensible
   200  }
   201  
   202  // MarshalJSON is a custom marshal function that knows how to encode Response as JSON
   203  func (r *Response) MarshalJSON() ([]byte, error) {
   204  	if internal.UseOptimizedJSONMarshalingV3 {
   205  		return internal.DeterministicMarshal(r)
   206  	}
   207  	b1, err := json.Marshal(r.Refable)
   208  	if err != nil {
   209  		return nil, err
   210  	}
   211  	b2, err := json.Marshal(r.ResponseProps)
   212  	if err != nil {
   213  		return nil, err
   214  	}
   215  	b3, err := json.Marshal(r.VendorExtensible)
   216  	if err != nil {
   217  		return nil, err
   218  	}
   219  	return swag.ConcatJSON(b1, b2, b3), nil
   220  }
   221  
   222  func (r Response) MarshalNextJSON(opts jsonv2.MarshalOptions, enc *jsonv2.Encoder) error {
   223  	var x struct {
   224  		Ref string `json:"$ref,omitempty"`
   225  		spec.Extensions
   226  		ResponseProps `json:",inline"`
   227  	}
   228  	x.Ref = r.Refable.Ref.String()
   229  	x.Extensions = internal.SanitizeExtensions(r.Extensions)
   230  	x.ResponseProps = r.ResponseProps
   231  	return opts.MarshalNext(enc, x)
   232  }
   233  
   234  func (r *Response) UnmarshalJSON(data []byte) error {
   235  	if internal.UseOptimizedJSONUnmarshalingV3 {
   236  		return jsonv2.Unmarshal(data, r)
   237  	}
   238  	if err := json.Unmarshal(data, &r.Refable); err != nil {
   239  		return err
   240  	}
   241  	if err := json.Unmarshal(data, &r.ResponseProps); err != nil {
   242  		return err
   243  	}
   244  	if err := json.Unmarshal(data, &r.VendorExtensible); err != nil {
   245  		return err
   246  	}
   247  	return nil
   248  }
   249  
   250  func (r *Response) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) error {
   251  	var x struct {
   252  		spec.Extensions
   253  		ResponseProps
   254  	}
   255  	if err := opts.UnmarshalNext(dec, &x); err != nil {
   256  		return err
   257  	}
   258  	if err := internal.JSONRefFromMap(&r.Ref.Ref, x.Extensions); err != nil {
   259  		return err
   260  	}
   261  	r.Extensions = internal.SanitizeExtensions(x.Extensions)
   262  	r.ResponseProps = x.ResponseProps
   263  	return nil
   264  }
   265  
   266  // ResponseProps describes a single response from an API Operation, more at https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#responseObject
   267  type ResponseProps struct {
   268  	// Description holds a short description of the response
   269  	Description string `json:"description,omitempty"`
   270  	// Headers holds a maps of a headers name to its definition
   271  	Headers map[string]*Header `json:"headers,omitempty"`
   272  	// Content holds a map containing descriptions of potential response payloads
   273  	Content map[string]*MediaType `json:"content,omitempty"`
   274  	// Links is a map of operations links that can be followed from the response
   275  	Links map[string]*Link `json:"links,omitempty"`
   276  }
   277  
   278  // Link represents a possible design-time link for a response, more at https://swagger.io/specification/#link-object
   279  type Link struct {
   280  	spec.Refable
   281  	LinkProps
   282  	spec.VendorExtensible
   283  }
   284  
   285  // MarshalJSON is a custom marshal function that knows how to encode Link as JSON
   286  func (r *Link) MarshalJSON() ([]byte, error) {
   287  	if internal.UseOptimizedJSONMarshalingV3 {
   288  		return internal.DeterministicMarshal(r)
   289  	}
   290  	b1, err := json.Marshal(r.Refable)
   291  	if err != nil {
   292  		return nil, err
   293  	}
   294  	b2, err := json.Marshal(r.LinkProps)
   295  	if err != nil {
   296  		return nil, err
   297  	}
   298  	b3, err := json.Marshal(r.VendorExtensible)
   299  	if err != nil {
   300  		return nil, err
   301  	}
   302  	return swag.ConcatJSON(b1, b2, b3), nil
   303  }
   304  
   305  func (r *Link) MarshalNextJSON(opts jsonv2.MarshalOptions, enc *jsonv2.Encoder) error {
   306  	var x struct {
   307  		Ref string `json:"$ref,omitempty"`
   308  		spec.Extensions
   309  		LinkProps `json:",inline"`
   310  	}
   311  	x.Ref = r.Refable.Ref.String()
   312  	x.Extensions = internal.SanitizeExtensions(r.Extensions)
   313  	x.LinkProps = r.LinkProps
   314  	return opts.MarshalNext(enc, x)
   315  }
   316  
   317  func (r *Link) UnmarshalJSON(data []byte) error {
   318  	if internal.UseOptimizedJSONUnmarshalingV3 {
   319  		return jsonv2.Unmarshal(data, r)
   320  	}
   321  	if err := json.Unmarshal(data, &r.Refable); err != nil {
   322  		return err
   323  	}
   324  	if err := json.Unmarshal(data, &r.LinkProps); err != nil {
   325  		return err
   326  	}
   327  	if err := json.Unmarshal(data, &r.VendorExtensible); err != nil {
   328  		return err
   329  	}
   330  
   331  	return nil
   332  }
   333  
   334  func (l *Link) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) error {
   335  	var x struct {
   336  		spec.Extensions
   337  		LinkProps
   338  	}
   339  	if err := opts.UnmarshalNext(dec, &x); err != nil {
   340  		return err
   341  	}
   342  	if err := internal.JSONRefFromMap(&l.Ref.Ref, x.Extensions); err != nil {
   343  		return err
   344  	}
   345  	l.Extensions = internal.SanitizeExtensions(x.Extensions)
   346  	l.LinkProps = x.LinkProps
   347  	return nil
   348  }
   349  
   350  // 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
   351  type LinkProps struct {
   352  	// OperationId is the name of an existing, resolvable OAS operation
   353  	OperationId string `json:"operationId,omitempty"`
   354  	// Parameters is a map representing parameters to pass to an operation as specified with operationId or identified via operationRef
   355  	Parameters map[string]interface{} `json:"parameters,omitempty"`
   356  	// Description holds a description of the link
   357  	Description string `json:"description,omitempty"`
   358  	// RequestBody is a literal value or expresion to use as a request body when calling the target operation
   359  	RequestBody interface{} `json:"requestBody,omitempty"`
   360  	// Server holds a server object used by the target operation
   361  	Server *Server `json:"server,omitempty"`
   362  }