k8s.io/kube-openapi@v0.0.0-20240228011516-70dd3763d340/pkg/spec3/path.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  	"strings"
    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  // Paths describes the available paths and operations for the API, more at https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#pathsObject
    31  type Paths struct {
    32  	Paths map[string]*Path
    33  	spec.VendorExtensible
    34  }
    35  
    36  // MarshalJSON is a custom marshal function that knows how to encode Paths as JSON
    37  func (p *Paths) MarshalJSON() ([]byte, error) {
    38  	if internal.UseOptimizedJSONMarshalingV3 {
    39  		return internal.DeterministicMarshal(p)
    40  	}
    41  	b1, err := json.Marshal(p.VendorExtensible)
    42  	if err != nil {
    43  		return nil, err
    44  	}
    45  
    46  	pths := make(map[string]*Path)
    47  	for k, v := range p.Paths {
    48  		if strings.HasPrefix(k, "/") {
    49  			pths[k] = v
    50  		}
    51  	}
    52  	b2, err := json.Marshal(pths)
    53  	if err != nil {
    54  		return nil, err
    55  	}
    56  	concated := swag.ConcatJSON(b1, b2)
    57  	return concated, nil
    58  }
    59  
    60  func (p *Paths) MarshalNextJSON(opts jsonv2.MarshalOptions, enc *jsonv2.Encoder) error {
    61  	m := make(map[string]any, len(p.Extensions)+len(p.Paths))
    62  	for k, v := range p.Extensions {
    63  		if internal.IsExtensionKey(k) {
    64  			m[k] = v
    65  		}
    66  	}
    67  	for k, v := range p.Paths {
    68  		if strings.HasPrefix(k, "/") {
    69  			m[k] = v
    70  		}
    71  	}
    72  	return opts.MarshalNext(enc, m)
    73  }
    74  
    75  // UnmarshalJSON hydrates this items instance with the data from JSON
    76  func (p *Paths) UnmarshalJSON(data []byte) error {
    77  	if internal.UseOptimizedJSONUnmarshalingV3 {
    78  		return jsonv2.Unmarshal(data, p)
    79  	}
    80  	var res map[string]json.RawMessage
    81  	if err := json.Unmarshal(data, &res); err != nil {
    82  		return err
    83  	}
    84  	for k, v := range res {
    85  		if strings.HasPrefix(strings.ToLower(k), "x-") {
    86  			if p.Extensions == nil {
    87  				p.Extensions = make(map[string]interface{})
    88  			}
    89  			var d interface{}
    90  			if err := json.Unmarshal(v, &d); err != nil {
    91  				return err
    92  			}
    93  			p.Extensions[k] = d
    94  		}
    95  		if strings.HasPrefix(k, "/") {
    96  			if p.Paths == nil {
    97  				p.Paths = make(map[string]*Path)
    98  			}
    99  			var pi *Path
   100  			if err := json.Unmarshal(v, &pi); err != nil {
   101  				return err
   102  			}
   103  			p.Paths[k] = pi
   104  		}
   105  	}
   106  	return nil
   107  }
   108  
   109  func (p *Paths) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) error {
   110  	tok, err := dec.ReadToken()
   111  	if err != nil {
   112  		return err
   113  	}
   114  	switch k := tok.Kind(); k {
   115  	case 'n':
   116  		*p = Paths{}
   117  		return nil
   118  	case '{':
   119  		for {
   120  			tok, err := dec.ReadToken()
   121  			if err != nil {
   122  				return err
   123  			}
   124  
   125  			if tok.Kind() == '}' {
   126  				return nil
   127  			}
   128  
   129  			switch k := tok.String(); {
   130  			case internal.IsExtensionKey(k):
   131  				var ext any
   132  				if err := opts.UnmarshalNext(dec, &ext); err != nil {
   133  					return err
   134  				}
   135  
   136  				if p.Extensions == nil {
   137  					p.Extensions = make(map[string]any)
   138  				}
   139  				p.Extensions[k] = ext
   140  			case len(k) > 0 && k[0] == '/':
   141  				pi := Path{}
   142  				if err := opts.UnmarshalNext(dec, &pi); err != nil {
   143  					return err
   144  				}
   145  
   146  				if p.Paths == nil {
   147  					p.Paths = make(map[string]*Path)
   148  				}
   149  				p.Paths[k] = &pi
   150  			default:
   151  				_, err := dec.ReadValue() // skip value
   152  				if err != nil {
   153  					return err
   154  				}
   155  			}
   156  		}
   157  	default:
   158  		return fmt.Errorf("unknown JSON kind: %v", k)
   159  	}
   160  }
   161  
   162  // 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
   163  //
   164  // Note that this struct is actually a thin wrapper around PathProps to make it referable and extensible
   165  type Path struct {
   166  	spec.Refable
   167  	PathProps
   168  	spec.VendorExtensible
   169  }
   170  
   171  // MarshalJSON is a custom marshal function that knows how to encode Path as JSON
   172  func (p *Path) MarshalJSON() ([]byte, error) {
   173  	if internal.UseOptimizedJSONMarshalingV3 {
   174  		return internal.DeterministicMarshal(p)
   175  	}
   176  	b1, err := json.Marshal(p.Refable)
   177  	if err != nil {
   178  		return nil, err
   179  	}
   180  	b2, err := json.Marshal(p.PathProps)
   181  	if err != nil {
   182  		return nil, err
   183  	}
   184  	b3, err := json.Marshal(p.VendorExtensible)
   185  	if err != nil {
   186  		return nil, err
   187  	}
   188  	return swag.ConcatJSON(b1, b2, b3), nil
   189  }
   190  
   191  func (p *Path) MarshalNextJSON(opts jsonv2.MarshalOptions, enc *jsonv2.Encoder) error {
   192  	var x struct {
   193  		Ref string `json:"$ref,omitempty"`
   194  		spec.Extensions
   195  		PathProps
   196  	}
   197  	x.Ref = p.Refable.Ref.String()
   198  	x.Extensions = internal.SanitizeExtensions(p.Extensions)
   199  	x.PathProps = p.PathProps
   200  	return opts.MarshalNext(enc, x)
   201  }
   202  
   203  func (p *Path) UnmarshalJSON(data []byte) error {
   204  	if internal.UseOptimizedJSONUnmarshalingV3 {
   205  		return jsonv2.Unmarshal(data, p)
   206  	}
   207  	if err := json.Unmarshal(data, &p.Refable); err != nil {
   208  		return err
   209  	}
   210  	if err := json.Unmarshal(data, &p.PathProps); err != nil {
   211  		return err
   212  	}
   213  	if err := json.Unmarshal(data, &p.VendorExtensible); err != nil {
   214  		return err
   215  	}
   216  	return nil
   217  }
   218  
   219  func (p *Path) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) error {
   220  	var x struct {
   221  		spec.Extensions
   222  		PathProps
   223  	}
   224  
   225  	if err := opts.UnmarshalNext(dec, &x); err != nil {
   226  		return err
   227  	}
   228  	if err := internal.JSONRefFromMap(&p.Ref.Ref, x.Extensions); err != nil {
   229  		return err
   230  	}
   231  	p.Extensions = internal.SanitizeExtensions(x.Extensions)
   232  	p.PathProps = x.PathProps
   233  
   234  	return nil
   235  }
   236  
   237  // 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
   238  type PathProps struct {
   239  	// Summary holds a summary for all operations in this path
   240  	Summary string `json:"summary,omitempty"`
   241  	// Description holds a description for all operations in this path
   242  	Description string `json:"description,omitempty"`
   243  	// Get defines GET operation
   244  	Get *Operation `json:"get,omitempty"`
   245  	// Put defines PUT operation
   246  	Put *Operation `json:"put,omitempty"`
   247  	// Post defines POST operation
   248  	Post *Operation `json:"post,omitempty"`
   249  	// Delete defines DELETE operation
   250  	Delete *Operation `json:"delete,omitempty"`
   251  	// Options defines OPTIONS operation
   252  	Options *Operation `json:"options,omitempty"`
   253  	// Head defines HEAD operation
   254  	Head *Operation `json:"head,omitempty"`
   255  	// Patch defines PATCH operation
   256  	Patch *Operation `json:"patch,omitempty"`
   257  	// Trace defines TRACE operation
   258  	Trace *Operation `json:"trace,omitempty"`
   259  	// Servers is an alternative server array to service all operations in this path
   260  	Servers []*Server `json:"servers,omitempty"`
   261  	// Parameters a list of parameters that are applicable for this operation
   262  	Parameters []*Parameter `json:"parameters,omitempty"`
   263  }