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 }