k8s.io/kube-openapi@v0.0.0-20240228011516-70dd3763d340/pkg/openapiconv/convert.go (about) 1 /* 2 Copyright 2022 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 openapiconv 18 19 import ( 20 "strings" 21 22 klog "k8s.io/klog/v2" 23 builderutil "k8s.io/kube-openapi/pkg/builder3/util" 24 "k8s.io/kube-openapi/pkg/spec3" 25 "k8s.io/kube-openapi/pkg/validation/spec" 26 ) 27 28 var OpenAPIV2DefPrefix = "#/definitions/" 29 var OpenAPIV3DefPrefix = "#/components/schemas/" 30 31 // ConvertV2ToV3 converts an OpenAPI V2 object into V3. 32 // Certain references may be shared between the V2 and V3 objects in the conversion. 33 func ConvertV2ToV3(v2Spec *spec.Swagger) *spec3.OpenAPI { 34 v3Spec := &spec3.OpenAPI{ 35 Version: "3.0.0", 36 Info: v2Spec.Info, 37 ExternalDocs: ConvertExternalDocumentation(v2Spec.ExternalDocs), 38 Paths: ConvertPaths(v2Spec.Paths), 39 Components: ConvertComponents(v2Spec.SecurityDefinitions, v2Spec.Definitions, v2Spec.Responses, v2Spec.Produces), 40 } 41 42 return v3Spec 43 } 44 45 func ConvertExternalDocumentation(v2ED *spec.ExternalDocumentation) *spec3.ExternalDocumentation { 46 if v2ED == nil { 47 return nil 48 } 49 return &spec3.ExternalDocumentation{ 50 ExternalDocumentationProps: spec3.ExternalDocumentationProps{ 51 Description: v2ED.Description, 52 URL: v2ED.URL, 53 }, 54 } 55 } 56 57 func ConvertComponents(v2SecurityDefinitions spec.SecurityDefinitions, v2Definitions spec.Definitions, v2Responses map[string]spec.Response, produces []string) *spec3.Components { 58 components := &spec3.Components{} 59 60 if v2Definitions != nil { 61 components.Schemas = make(map[string]*spec.Schema) 62 } 63 for s, schema := range v2Definitions { 64 components.Schemas[s] = ConvertSchema(&schema) 65 } 66 if v2SecurityDefinitions != nil { 67 components.SecuritySchemes = make(spec3.SecuritySchemes) 68 } 69 for s, securityScheme := range v2SecurityDefinitions { 70 components.SecuritySchemes[s] = ConvertSecurityScheme(securityScheme) 71 } 72 if v2Responses != nil { 73 components.Responses = make(map[string]*spec3.Response) 74 } 75 for r, response := range v2Responses { 76 components.Responses[r] = ConvertResponse(&response, produces) 77 } 78 79 return components 80 } 81 82 func ConvertSchema(v2Schema *spec.Schema) *spec.Schema { 83 if v2Schema == nil { 84 return nil 85 } 86 v3Schema := spec.Schema{ 87 VendorExtensible: v2Schema.VendorExtensible, 88 SchemaProps: v2Schema.SchemaProps, 89 SwaggerSchemaProps: v2Schema.SwaggerSchemaProps, 90 ExtraProps: v2Schema.ExtraProps, 91 } 92 93 if refString := v2Schema.Ref.String(); refString != "" { 94 if idx := strings.Index(refString, OpenAPIV2DefPrefix); idx != -1 { 95 v3Schema.Ref = spec.MustCreateRef(OpenAPIV3DefPrefix + refString[idx+len(OpenAPIV2DefPrefix):]) 96 } else { 97 klog.Errorf("Error: Swagger V2 Ref %s does not contain #/definitions\n", refString) 98 } 99 } 100 101 if v2Schema.Properties != nil { 102 v3Schema.Properties = make(map[string]spec.Schema) 103 for key, property := range v2Schema.Properties { 104 v3Schema.Properties[key] = *ConvertSchema(&property) 105 } 106 } 107 if v2Schema.Items != nil { 108 v3Schema.Items = &spec.SchemaOrArray{ 109 Schema: ConvertSchema(v2Schema.Items.Schema), 110 Schemas: ConvertSchemaList(v2Schema.Items.Schemas), 111 } 112 } 113 114 if v2Schema.AdditionalProperties != nil { 115 v3Schema.AdditionalProperties = &spec.SchemaOrBool{ 116 Schema: ConvertSchema(v2Schema.AdditionalProperties.Schema), 117 Allows: v2Schema.AdditionalProperties.Allows, 118 } 119 } 120 if v2Schema.AdditionalItems != nil { 121 v3Schema.AdditionalItems = &spec.SchemaOrBool{ 122 Schema: ConvertSchema(v2Schema.AdditionalItems.Schema), 123 Allows: v2Schema.AdditionalItems.Allows, 124 } 125 } 126 127 return builderutil.WrapRefs(&v3Schema) 128 } 129 130 func ConvertSchemaList(v2SchemaList []spec.Schema) []spec.Schema { 131 if v2SchemaList == nil { 132 return nil 133 } 134 v3SchemaList := []spec.Schema{} 135 for _, s := range v2SchemaList { 136 v3SchemaList = append(v3SchemaList, *ConvertSchema(&s)) 137 } 138 return v3SchemaList 139 } 140 141 func ConvertSecurityScheme(v2securityScheme *spec.SecurityScheme) *spec3.SecurityScheme { 142 if v2securityScheme == nil { 143 return nil 144 } 145 securityScheme := &spec3.SecurityScheme{ 146 VendorExtensible: v2securityScheme.VendorExtensible, 147 SecuritySchemeProps: spec3.SecuritySchemeProps{ 148 Description: v2securityScheme.Description, 149 Type: v2securityScheme.Type, 150 Name: v2securityScheme.Name, 151 In: v2securityScheme.In, 152 }, 153 } 154 155 if v2securityScheme.Flow != "" { 156 securityScheme.Flows = make(map[string]*spec3.OAuthFlow) 157 securityScheme.Flows[v2securityScheme.Flow] = &spec3.OAuthFlow{ 158 OAuthFlowProps: spec3.OAuthFlowProps{ 159 AuthorizationUrl: v2securityScheme.AuthorizationURL, 160 TokenUrl: v2securityScheme.TokenURL, 161 Scopes: v2securityScheme.Scopes, 162 }, 163 } 164 } 165 return securityScheme 166 } 167 168 func ConvertPaths(v2Paths *spec.Paths) *spec3.Paths { 169 if v2Paths == nil { 170 return nil 171 } 172 paths := &spec3.Paths{ 173 VendorExtensible: v2Paths.VendorExtensible, 174 } 175 176 if v2Paths.Paths != nil { 177 paths.Paths = make(map[string]*spec3.Path) 178 } 179 for k, v := range v2Paths.Paths { 180 paths.Paths[k] = ConvertPathItem(v) 181 } 182 return paths 183 } 184 185 func ConvertPathItem(v2pathItem spec.PathItem) *spec3.Path { 186 path := &spec3.Path{ 187 Refable: v2pathItem.Refable, 188 PathProps: spec3.PathProps{ 189 Get: ConvertOperation(v2pathItem.Get), 190 Put: ConvertOperation(v2pathItem.Put), 191 Post: ConvertOperation(v2pathItem.Post), 192 Delete: ConvertOperation(v2pathItem.Delete), 193 Options: ConvertOperation(v2pathItem.Options), 194 Head: ConvertOperation(v2pathItem.Head), 195 Patch: ConvertOperation(v2pathItem.Patch), 196 }, 197 VendorExtensible: v2pathItem.VendorExtensible, 198 } 199 for _, param := range v2pathItem.Parameters { 200 path.Parameters = append(path.Parameters, ConvertParameter(param)) 201 } 202 return path 203 } 204 205 func ConvertOperation(v2Operation *spec.Operation) *spec3.Operation { 206 if v2Operation == nil { 207 return nil 208 } 209 operation := &spec3.Operation{ 210 VendorExtensible: v2Operation.VendorExtensible, 211 OperationProps: spec3.OperationProps{ 212 Description: v2Operation.Description, 213 ExternalDocs: ConvertExternalDocumentation(v2Operation.OperationProps.ExternalDocs), 214 Tags: v2Operation.Tags, 215 Summary: v2Operation.Summary, 216 Deprecated: v2Operation.Deprecated, 217 OperationId: v2Operation.ID, 218 }, 219 } 220 221 for _, param := range v2Operation.Parameters { 222 if param.ParamProps.Name == "body" && param.ParamProps.Schema != nil { 223 operation.OperationProps.RequestBody = &spec3.RequestBody{ 224 RequestBodyProps: spec3.RequestBodyProps{}, 225 } 226 if v2Operation.Consumes != nil { 227 operation.RequestBody.Content = make(map[string]*spec3.MediaType) 228 } 229 for _, consumer := range v2Operation.Consumes { 230 operation.RequestBody.Content[consumer] = &spec3.MediaType{ 231 MediaTypeProps: spec3.MediaTypeProps{ 232 Schema: ConvertSchema(param.ParamProps.Schema), 233 }, 234 } 235 } 236 } else { 237 operation.Parameters = append(operation.Parameters, ConvertParameter(param)) 238 } 239 } 240 241 operation.Responses = &spec3.Responses{ResponsesProps: spec3.ResponsesProps{ 242 Default: ConvertResponse(v2Operation.Responses.Default, v2Operation.Produces), 243 }, 244 VendorExtensible: v2Operation.Responses.VendorExtensible, 245 } 246 247 if v2Operation.Responses.StatusCodeResponses != nil { 248 operation.Responses.StatusCodeResponses = make(map[int]*spec3.Response) 249 } 250 for k, v := range v2Operation.Responses.StatusCodeResponses { 251 operation.Responses.StatusCodeResponses[k] = ConvertResponse(&v, v2Operation.Produces) 252 } 253 return operation 254 } 255 256 func ConvertResponse(v2Response *spec.Response, produces []string) *spec3.Response { 257 if v2Response == nil { 258 return nil 259 } 260 response := &spec3.Response{ 261 Refable: ConvertRefableResponse(v2Response.Refable), 262 VendorExtensible: v2Response.VendorExtensible, 263 ResponseProps: spec3.ResponseProps{ 264 Description: v2Response.Description, 265 }, 266 } 267 268 if v2Response.Schema != nil { 269 if produces != nil { 270 response.Content = make(map[string]*spec3.MediaType) 271 } 272 for _, producer := range produces { 273 response.ResponseProps.Content[producer] = &spec3.MediaType{ 274 MediaTypeProps: spec3.MediaTypeProps{ 275 Schema: ConvertSchema(v2Response.Schema), 276 }, 277 } 278 } 279 } 280 return response 281 } 282 283 func ConvertParameter(v2Param spec.Parameter) *spec3.Parameter { 284 param := &spec3.Parameter{ 285 Refable: ConvertRefableParameter(v2Param.Refable), 286 VendorExtensible: v2Param.VendorExtensible, 287 ParameterProps: spec3.ParameterProps{ 288 Name: v2Param.Name, 289 Description: v2Param.Description, 290 In: v2Param.In, 291 Required: v2Param.Required, 292 Schema: ConvertSchema(v2Param.Schema), 293 AllowEmptyValue: v2Param.AllowEmptyValue, 294 }, 295 } 296 // Convert SimpleSchema into Schema 297 if param.Schema == nil { 298 param.Schema = &spec.Schema{ 299 SchemaProps: spec.SchemaProps{ 300 Type: []string{v2Param.Type}, 301 Format: v2Param.Format, 302 UniqueItems: v2Param.UniqueItems, 303 }, 304 } 305 } 306 307 return param 308 } 309 310 func ConvertRefableParameter(refable spec.Refable) spec.Refable { 311 if refable.Ref.String() != "" { 312 return spec.Refable{Ref: spec.MustCreateRef(strings.Replace(refable.Ref.String(), "#/parameters/", "#/components/parameters/", 1))} 313 } 314 return refable 315 } 316 317 func ConvertRefableResponse(refable spec.Refable) spec.Refable { 318 if refable.Ref.String() != "" { 319 return spec.Refable{Ref: spec.MustCreateRef(strings.Replace(refable.Ref.String(), "#/responses/", "#/components/responses/", 1))} 320 } 321 return refable 322 }