k8s.io/kube-openapi@v0.0.0-20240228011516-70dd3763d340/pkg/common/common.go (about)

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