1
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
32
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
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 }
323
View as plain text