convert.go 9.6 KB

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