Skip to content

Commit

Permalink
Add OpenAPIConv
Browse files Browse the repository at this point in the history
  • Loading branch information
Jefftree committed Mar 24, 2022
1 parent 29d7264 commit a5c6ebb
Show file tree
Hide file tree
Showing 15 changed files with 657 additions and 22 deletions.
66 changes: 49 additions & 17 deletions pkg/builder3/openapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const (
)

type openAPI struct {
config *common.Config
config *common.OpenAPIV3Config
spec *spec3.OpenAPI
definitions map[string]common.OpenAPIDefinition
}
Expand Down Expand Up @@ -83,6 +83,15 @@ func (o *openAPI) buildOperations(route common.Route, inPathCommonParamsMap map[
},
},
}
for k, v := range route.Metadata() {
if strings.HasPrefix(k, common.ExtensionPrefix) {
if ret.Extensions == nil {
ret.Extensions = spec.Extensions{}
}
ret.Extensions.Add(k, v)
}
}

var err error
if ret.OperationId, ret.Tags, err = o.config.GetOperationIDAndTagsFromRoute(route); err != nil {
return ret, err
Expand All @@ -104,9 +113,17 @@ func (o *openAPI) buildOperations(route common.Route, inPathCommonParamsMap map[
}
}

// TODO: Default response if needed. Common Response config
for code, resp := range o.config.CommonResponses {
if _, exists := ret.Responses.StatusCodeResponses[code]; !exists {
ret.Responses.StatusCodeResponses[code] = resp
}
}

// If there is still no response, use default response provided.
if len(ret.Responses.StatusCodeResponses) == 0 {
ret.Responses.Default = o.config.DefaultResponse
}

ret.Parameters = make([]*spec3.Parameter, 0)
params := route.Parameters()
for _, param := range params {
_, isCommon := inPathCommonParamsMap[mapKeyFromParam(param)]
Expand All @@ -119,7 +136,7 @@ func (o *openAPI) buildOperations(route common.Route, inPathCommonParamsMap map[
}
}

body, err := o.buildRequestBody(params, route.RequestPayloadSample())
body, err := o.buildRequestBody(params, route.Consumes(), route.RequestPayloadSample())
if err != nil {
return nil, err
}
Expand All @@ -130,7 +147,7 @@ func (o *openAPI) buildOperations(route common.Route, inPathCommonParamsMap map[
return ret, nil
}

func (o *openAPI) buildRequestBody(parameters []common.Parameter, bodySample interface{}) (*spec3.RequestBody, error) {
func (o *openAPI) buildRequestBody(parameters []common.Parameter, consumes []string, bodySample interface{}) (*spec3.RequestBody, error) {
for _, param := range parameters {
if param.Kind() == common.BodyParameterKind && bodySample != nil {
schema, err := o.toSchema(util.GetCanonicalTypeName(bodySample))
Expand All @@ -139,15 +156,16 @@ func (o *openAPI) buildRequestBody(parameters []common.Parameter, bodySample int
}
r := &spec3.RequestBody{
RequestBodyProps: spec3.RequestBodyProps{
Content: map[string]*spec3.MediaType{
"application/json": &spec3.MediaType{
MediaTypeProps: spec3.MediaTypeProps{
Schema: schema,
},
},
},
Content: map[string]*spec3.MediaType{},
},
}
for _, consume := range consumes {
r.Content[consume] = &spec3.MediaType{
MediaTypeProps: spec3.MediaTypeProps{
Schema: schema,
},
}
}
return r, nil
}
}
Expand All @@ -156,18 +174,33 @@ func (o *openAPI) buildRequestBody(parameters []common.Parameter, bodySample int

func newOpenAPI(config *common.Config) openAPI {
o := openAPI{
config: config,
config: common.ConvertConfigToV3(config),
spec: &spec3.OpenAPI{
Version: "3.0.0",
Info: config.Info,
Paths: &spec3.Paths{
Paths: map[string]*spec3.Path{},
},
Components: &spec3.Components{
Schemas: map[string]*spec.Schema{},
Schemas: map[string]*spec.Schema{},
},
},
}
if len(o.config.ResponseDefinitions) > 0 {
o.spec.Components.Responses = make(map[string]*spec3.Response)

}
for k, response := range o.config.ResponseDefinitions {
o.spec.Components.Responses[k] = response
}

if len(o.config.SecuritySchemes) > 0 {
o.spec.Components.SecuritySchemes = make(spec3.SecuritySchemes)

}
for k, securityScheme := range o.config.SecuritySchemes {
o.spec.Components.SecuritySchemes[k] = securityScheme
}

if o.config.GetOperationIDAndTagsFromRoute == nil {
// Map the deprecated handler to the common interface, if provided.
Expand Down Expand Up @@ -235,9 +268,7 @@ func (o *openAPI) buildOpenAPISpec(webServices []common.RouteContainer) error {
}

pathItem = &spec3.Path{
PathProps: spec3.PathProps{
Parameters: make([]*spec3.Parameter, 0),
},
PathProps: spec3.PathProps{},
}

// add web services's parameters as well as any parameters appears in all ops, as common parameters
Expand All @@ -249,6 +280,7 @@ func (o *openAPI) buildOpenAPISpec(webServices []common.RouteContainer) error {

for _, route := range routes {
op, _ := o.buildOperations(route, inPathCommonParamsMap)
sortParameters(op.Parameters)

switch strings.ToUpper(route.Method()) {
case "GET":
Expand Down
82 changes: 82 additions & 0 deletions pkg/common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import (

"github.com/emicklei/go-restful"

"k8s.io/kube-openapi/pkg/openapiconv"
"k8s.io/kube-openapi/pkg/spec3"
"k8s.io/kube-openapi/pkg/validation/spec"
)

Expand Down Expand Up @@ -117,6 +119,86 @@ type Config struct {
DefaultSecurity []map[string][]string
}

// OpenAPIV3Config is set of configuration for OpenAPI V3 spec generation.
type OpenAPIV3Config struct {
// Info is general information about the API.
Info *spec.Info

// DefaultResponse will be used if an operation does not have any responses listed. It
// will show up as ... "responses" : {"default" : $DefaultResponse} in the spec.
DefaultResponse *spec3.Response

// ResponseDefinitions will be added to responses component. This is an object
// that holds responses that can be used across operations.
ResponseDefinitions map[string]*spec3.Response

// CommonResponses will be added as a response to all operation specs. This is a good place to add common
// responses such as authorization failed.
CommonResponses map[int]*spec3.Response

// List of webservice's path prefixes to ignore
IgnorePrefixes []string

// OpenAPIDefinitions should provide definition for all models used by routes. Failure to provide this map
// or any of the models will result in spec generation failure.
GetDefinitions GetOpenAPIDefinitions

// GetOperationIDAndTags returns operation id and tags for a restful route. It is an optional function to customize operation IDs.
//
// Deprecated: GetOperationIDAndTagsFromRoute should be used instead. This cannot be specified if using the new Route
// interface set of funcs.
GetOperationIDAndTags func(r *restful.Route) (string, []string, error)

// GetOperationIDAndTagsFromRoute returns operation id and tags for a Route. It is an optional function to customize operation IDs.
GetOperationIDAndTagsFromRoute func(r Route) (string, []string, error)

// GetDefinitionName returns a friendly name for a definition base on the serving path. parameter `name` is the full name of the definition.
// It is an optional function to customize model names.
GetDefinitionName func(name string) (string, spec.Extensions)

// SecuritySchemes is list of all security schemes for OpenAPI service.
SecuritySchemes spec3.SecuritySchemes

// DefaultSecurity for all operations.
DefaultSecurity []map[string][]string
}

// ConvertConfigToV3 converts a Config object to an OpenAPIV3Config object
func ConvertConfigToV3(config *Config) *OpenAPIV3Config {
if config == nil {
return nil
}

v3Config := &OpenAPIV3Config{
Info: config.Info,
IgnorePrefixes: config.IgnorePrefixes,
GetDefinitions: config.GetDefinitions,
GetOperationIDAndTags: config.GetOperationIDAndTags,
GetOperationIDAndTagsFromRoute: config.GetOperationIDAndTagsFromRoute,
GetDefinitionName: config.GetDefinitionName,
SecuritySchemes: make(spec3.SecuritySchemes),
DefaultSecurity: config.DefaultSecurity,
DefaultResponse: openapiconv.ConvertResponse(config.DefaultResponse, []string{"application/json"}),

CommonResponses: make(map[int]*spec3.Response),
ResponseDefinitions: make(map[string]*spec3.Response),
}

if config.SecurityDefinitions != nil {
for s, securityScheme := range *config.SecurityDefinitions {
v3Config.SecuritySchemes[s] = openapiconv.ConvertSecurityScheme(securityScheme)
}
}
for k, commonResponse := range config.CommonResponses {
v3Config.CommonResponses[k] = openapiconv.ConvertResponse(&commonResponse, []string{"application/json"})
}

for k, responseDefinition := range config.ResponseDefinitions {
v3Config.ResponseDefinitions[k] = openapiconv.ConvertResponse(&responseDefinition, []string{"application/json"})
}
return v3Config
}

type typeInfo struct {
name string
format string
Expand Down
Loading

0 comments on commit a5c6ebb

Please sign in to comment.