common.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. /*
  2. Copyright 2016 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 common
  14. import (
  15. "net/http"
  16. "strings"
  17. "github.com/emicklei/go-restful/v3"
  18. "k8s.io/kube-openapi/pkg/openapiconv"
  19. "k8s.io/kube-openapi/pkg/spec3"
  20. "k8s.io/kube-openapi/pkg/validation/spec"
  21. )
  22. const (
  23. // TODO: Make this configurable.
  24. ExtensionPrefix = "x-kubernetes-"
  25. ExtensionV2Schema = ExtensionPrefix + "v2-schema"
  26. )
  27. // OpenAPIDefinition describes single type. Normally these definitions are auto-generated using gen-openapi.
  28. type OpenAPIDefinition struct {
  29. Schema spec.Schema
  30. Dependencies []string
  31. }
  32. type ReferenceCallback func(path string) spec.Ref
  33. // GetOpenAPIDefinitions is collection of all definitions.
  34. type GetOpenAPIDefinitions func(ReferenceCallback) map[string]OpenAPIDefinition
  35. // OpenAPIDefinitionGetter gets openAPI definitions for a given type. If a type implements this interface,
  36. // the definition returned by it will be used, otherwise the auto-generated definitions will be used. See
  37. // GetOpenAPITypeFormat for more information about trade-offs of using this interface or GetOpenAPITypeFormat method when
  38. // possible.
  39. type OpenAPIDefinitionGetter interface {
  40. OpenAPIDefinition() *OpenAPIDefinition
  41. }
  42. type OpenAPIV3DefinitionGetter interface {
  43. OpenAPIV3Definition() *OpenAPIDefinition
  44. }
  45. type PathHandler interface {
  46. Handle(path string, handler http.Handler)
  47. }
  48. type PathHandlerByGroupVersion interface {
  49. Handle(path string, handler http.Handler)
  50. HandlePrefix(path string, handler http.Handler)
  51. }
  52. // Config is set of configuration for openAPI spec generation.
  53. type Config struct {
  54. // List of supported protocols such as https, http, etc.
  55. ProtocolList []string
  56. // Info is general information about the API.
  57. Info *spec.Info
  58. // DefaultResponse will be used if an operation does not have any responses listed. It
  59. // will show up as ... "responses" : {"default" : $DefaultResponse} in the spec.
  60. DefaultResponse *spec.Response
  61. // ResponseDefinitions will be added to "responses" under the top-level swagger object. This is an object
  62. // that holds responses definitions that can be used across operations. This property does not define
  63. // global responses for all operations. For more info please refer:
  64. // https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#fixed-fields
  65. ResponseDefinitions map[string]spec.Response
  66. // CommonResponses will be added as a response to all operation specs. This is a good place to add common
  67. // responses such as authorization failed.
  68. CommonResponses map[int]spec.Response
  69. // List of webservice's path prefixes to ignore
  70. IgnorePrefixes []string
  71. // OpenAPIDefinitions should provide definition for all models used by routes. Failure to provide this map
  72. // or any of the models will result in spec generation failure.
  73. GetDefinitions GetOpenAPIDefinitions
  74. // Provides the definition for all models used by routes. One of GetDefinitions or Definitions must be defined to generate a spec.
  75. // This takes precedent over the GetDefinitions function
  76. Definitions map[string]OpenAPIDefinition
  77. // GetOperationIDAndTags returns operation id and tags for a restful route. It is an optional function to customize operation IDs.
  78. //
  79. // Deprecated: GetOperationIDAndTagsFromRoute should be used instead. This cannot be specified if using the new Route
  80. // interface set of funcs.
  81. GetOperationIDAndTags func(r *restful.Route) (string, []string, error)
  82. // GetOperationIDAndTagsFromRoute returns operation id and tags for a Route. It is an optional function to customize operation IDs.
  83. GetOperationIDAndTagsFromRoute func(r Route) (string, []string, error)
  84. // GetDefinitionName returns a friendly name for a definition base on the serving path. parameter `name` is the full name of the definition.
  85. // It is an optional function to customize model names.
  86. GetDefinitionName func(name string) (string, spec.Extensions)
  87. // PostProcessSpec runs after the spec is ready to serve. It allows a final modification to the spec before serving.
  88. PostProcessSpec func(*spec.Swagger) (*spec.Swagger, error)
  89. // SecurityDefinitions is list of all security definitions for OpenAPI service. If this is not nil, the user of config
  90. // is responsible to provide DefaultSecurity and (maybe) add unauthorized response to CommonResponses.
  91. SecurityDefinitions *spec.SecurityDefinitions
  92. // DefaultSecurity for all operations. This will pass as spec.SwaggerProps.Security to OpenAPI.
  93. // For most cases, this will be list of acceptable definitions in SecurityDefinitions.
  94. DefaultSecurity []map[string][]string
  95. }
  96. // OpenAPIV3Config is set of configuration for OpenAPI V3 spec generation.
  97. type OpenAPIV3Config struct {
  98. // Info is general information about the API.
  99. Info *spec.Info
  100. // DefaultResponse will be used if an operation does not have any responses listed. It
  101. // will show up as ... "responses" : {"default" : $DefaultResponse} in the spec.
  102. DefaultResponse *spec3.Response
  103. // ResponseDefinitions will be added to responses component. This is an object
  104. // that holds responses that can be used across operations.
  105. ResponseDefinitions map[string]*spec3.Response
  106. // CommonResponses will be added as a response to all operation specs. This is a good place to add common
  107. // responses such as authorization failed.
  108. CommonResponses map[int]*spec3.Response
  109. // List of webservice's path prefixes to ignore
  110. IgnorePrefixes []string
  111. // OpenAPIDefinitions should provide definition for all models used by routes. Failure to provide this map
  112. // or any of the models will result in spec generation failure.
  113. // One of GetDefinitions or Definitions must be defined to generate a spec.
  114. GetDefinitions GetOpenAPIDefinitions
  115. // Provides the definition for all models used by routes. One of GetDefinitions or Definitions must be defined to generate a spec.
  116. // This takes precedent over the GetDefinitions function
  117. Definitions map[string]OpenAPIDefinition
  118. // GetOperationIDAndTags returns operation id and tags for a restful route. It is an optional function to customize operation IDs.
  119. //
  120. // Deprecated: GetOperationIDAndTagsFromRoute should be used instead. This cannot be specified if using the new Route
  121. // interface set of funcs.
  122. GetOperationIDAndTags func(r *restful.Route) (string, []string, error)
  123. // GetOperationIDAndTagsFromRoute returns operation id and tags for a Route. It is an optional function to customize operation IDs.
  124. GetOperationIDAndTagsFromRoute func(r Route) (string, []string, error)
  125. // GetDefinitionName returns a friendly name for a definition base on the serving path. parameter `name` is the full name of the definition.
  126. // It is an optional function to customize model names.
  127. GetDefinitionName func(name string) (string, spec.Extensions)
  128. // SecuritySchemes is list of all security schemes for OpenAPI service.
  129. SecuritySchemes spec3.SecuritySchemes
  130. // DefaultSecurity for all operations.
  131. DefaultSecurity []map[string][]string
  132. }
  133. // ConvertConfigToV3 converts a Config object to an OpenAPIV3Config object
  134. func ConvertConfigToV3(config *Config) *OpenAPIV3Config {
  135. if config == nil {
  136. return nil
  137. }
  138. v3Config := &OpenAPIV3Config{
  139. Info: config.Info,
  140. IgnorePrefixes: config.IgnorePrefixes,
  141. GetDefinitions: config.GetDefinitions,
  142. GetOperationIDAndTags: config.GetOperationIDAndTags,
  143. GetOperationIDAndTagsFromRoute: config.GetOperationIDAndTagsFromRoute,
  144. GetDefinitionName: config.GetDefinitionName,
  145. Definitions: config.Definitions,
  146. SecuritySchemes: make(spec3.SecuritySchemes),
  147. DefaultSecurity: config.DefaultSecurity,
  148. DefaultResponse: openapiconv.ConvertResponse(config.DefaultResponse, []string{"application/json"}),
  149. CommonResponses: make(map[int]*spec3.Response),
  150. ResponseDefinitions: make(map[string]*spec3.Response),
  151. }
  152. if config.SecurityDefinitions != nil {
  153. for s, securityScheme := range *config.SecurityDefinitions {
  154. v3Config.SecuritySchemes[s] = openapiconv.ConvertSecurityScheme(securityScheme)
  155. }
  156. }
  157. for k, commonResponse := range config.CommonResponses {
  158. v3Config.CommonResponses[k] = openapiconv.ConvertResponse(&commonResponse, []string{"application/json"})
  159. }
  160. for k, responseDefinition := range config.ResponseDefinitions {
  161. v3Config.ResponseDefinitions[k] = openapiconv.ConvertResponse(&responseDefinition, []string{"application/json"})
  162. }
  163. return v3Config
  164. }
  165. type typeInfo struct {
  166. name string
  167. format string
  168. zero interface{}
  169. }
  170. var schemaTypeFormatMap = map[string]typeInfo{
  171. "uint": {"integer", "int32", 0.},
  172. "uint8": {"integer", "byte", 0.},
  173. "uint16": {"integer", "int32", 0.},
  174. "uint32": {"integer", "int64", 0.},
  175. "uint64": {"integer", "int64", 0.},
  176. "int": {"integer", "int32", 0.},
  177. "int8": {"integer", "byte", 0.},
  178. "int16": {"integer", "int32", 0.},
  179. "int32": {"integer", "int32", 0.},
  180. "int64": {"integer", "int64", 0.},
  181. "byte": {"integer", "byte", 0},
  182. "float64": {"number", "double", 0.},
  183. "float32": {"number", "float", 0.},
  184. "bool": {"boolean", "", false},
  185. "time.Time": {"string", "date-time", ""},
  186. "string": {"string", "", ""},
  187. "integer": {"integer", "", 0.},
  188. "number": {"number", "", 0.},
  189. "boolean": {"boolean", "", false},
  190. "[]byte": {"string", "byte", ""}, // base64 encoded characters
  191. "interface{}": {"object", "", interface{}(nil)},
  192. }
  193. // This function is a reference for converting go (or any custom type) to a simple open API type,format pair. There are
  194. // two ways to customize spec for a type. If you add it here, a type will be converted to a simple type and the type
  195. // comment (the comment that is added before type definition) will be lost. The spec will still have the property
  196. // comment. The second way is to implement OpenAPIDefinitionGetter interface. That function can customize the spec (so
  197. // the spec does not need to be simple type,format) or can even return a simple type,format (e.g. IntOrString). For simple
  198. // type formats, the benefit of adding OpenAPIDefinitionGetter interface is to keep both type and property documentation.
  199. // Example:
  200. // type Sample struct {
  201. // ...
  202. // // port of the server
  203. // port IntOrString
  204. // ...
  205. // }
  206. // // IntOrString documentation...
  207. // type IntOrString { ... }
  208. //
  209. // Adding IntOrString to this function:
  210. // "port" : {
  211. // format: "string",
  212. // type: "int-or-string",
  213. // Description: "port of the server"
  214. // }
  215. //
  216. // Implement OpenAPIDefinitionGetter for IntOrString:
  217. //
  218. // "port" : {
  219. // $Ref: "#/definitions/IntOrString"
  220. // Description: "port of the server"
  221. // }
  222. // ...
  223. // definitions:
  224. // {
  225. // "IntOrString": {
  226. // format: "string",
  227. // type: "int-or-string",
  228. // Description: "IntOrString documentation..." // new
  229. // }
  230. // }
  231. //
  232. func OpenAPITypeFormat(typeName string) (string, string) {
  233. mapped, ok := schemaTypeFormatMap[typeName]
  234. if !ok {
  235. return "", ""
  236. }
  237. return mapped.name, mapped.format
  238. }
  239. // Returns the zero-value for the given type along with true if the type
  240. // could be found.
  241. func OpenAPIZeroValue(typeName string) (interface{}, bool) {
  242. mapped, ok := schemaTypeFormatMap[typeName]
  243. if !ok {
  244. return nil, false
  245. }
  246. return mapped.zero, true
  247. }
  248. func EscapeJsonPointer(p string) string {
  249. // Escaping reference name using rfc6901
  250. p = strings.Replace(p, "~", "~0", -1)
  251. p = strings.Replace(p, "/", "~1", -1)
  252. return p
  253. }
  254. func EmbedOpenAPIDefinitionIntoV2Extension(main OpenAPIDefinition, embedded OpenAPIDefinition) OpenAPIDefinition {
  255. if main.Schema.Extensions == nil {
  256. main.Schema.Extensions = make(map[string]interface{})
  257. }
  258. main.Schema.Extensions[ExtensionV2Schema] = embedded.Schema
  259. return main
  260. }
  261. // GenerateOpenAPIV3OneOfSchema generate the set of schemas that MUST be assigned to SchemaProps.OneOf
  262. func GenerateOpenAPIV3OneOfSchema(types []string) (oneOf []spec.Schema) {
  263. for _, t := range types {
  264. oneOf = append(oneOf, spec.Schema{SchemaProps: spec.SchemaProps{Type: []string{t}}})
  265. }
  266. return
  267. }