Skip to content

Commit c680601

Browse files
committed
Add OpenAPIConv
1 parent 29d7264 commit c680601

File tree

15 files changed

+656
-21
lines changed

15 files changed

+656
-21
lines changed

pkg/builder3/openapi.go

Lines changed: 48 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ const (
3636
)
3737

3838
type openAPI struct {
39-
config *common.Config
39+
config *common.OpenAPIV3Config
4040
spec *spec3.OpenAPI
4141
definitions map[string]common.OpenAPIDefinition
4242
}
@@ -83,6 +83,15 @@ func (o *openAPI) buildOperations(route common.Route, inPathCommonParamsMap map[
8383
},
8484
},
8585
}
86+
for k, v := range route.Metadata() {
87+
if strings.HasPrefix(k, common.ExtensionPrefix) {
88+
if ret.Extensions == nil {
89+
ret.Extensions = spec.Extensions{}
90+
}
91+
ret.Extensions.Add(k, v)
92+
}
93+
}
94+
8695
var err error
8796
if ret.OperationId, ret.Tags, err = o.config.GetOperationIDAndTagsFromRoute(route); err != nil {
8897
return ret, err
@@ -104,9 +113,17 @@ func (o *openAPI) buildOperations(route common.Route, inPathCommonParamsMap map[
104113
}
105114
}
106115

107-
// TODO: Default response if needed. Common Response config
116+
for code, resp := range o.config.CommonResponses {
117+
if _, exists := ret.Responses.StatusCodeResponses[code]; !exists {
118+
ret.Responses.StatusCodeResponses[code] = resp
119+
}
120+
}
121+
122+
// If there is still no response, use default response provided.
123+
if len(ret.Responses.StatusCodeResponses) == 0 {
124+
ret.Responses.Default = o.config.DefaultResponse
125+
}
108126

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

122-
body, err := o.buildRequestBody(params, route.RequestPayloadSample())
139+
body, err := o.buildRequestBody(params, route.Consumes(), route.RequestPayloadSample())
123140
if err != nil {
124141
return nil, err
125142
}
@@ -130,7 +147,7 @@ func (o *openAPI) buildOperations(route common.Route, inPathCommonParamsMap map[
130147
return ret, nil
131148
}
132149

133-
func (o *openAPI) buildRequestBody(parameters []common.Parameter, bodySample interface{}) (*spec3.RequestBody, error) {
150+
func (o *openAPI) buildRequestBody(parameters []common.Parameter, consumes []string, bodySample interface{}) (*spec3.RequestBody, error) {
134151
for _, param := range parameters {
135152
if param.Kind() == common.BodyParameterKind && bodySample != nil {
136153
schema, err := o.toSchema(util.GetCanonicalTypeName(bodySample))
@@ -139,15 +156,16 @@ func (o *openAPI) buildRequestBody(parameters []common.Parameter, bodySample int
139156
}
140157
r := &spec3.RequestBody{
141158
RequestBodyProps: spec3.RequestBodyProps{
142-
Content: map[string]*spec3.MediaType{
143-
"application/json": &spec3.MediaType{
144-
MediaTypeProps: spec3.MediaTypeProps{
145-
Schema: schema,
146-
},
147-
},
148-
},
159+
Content: map[string]*spec3.MediaType{},
149160
},
150161
}
162+
for _, consume := range consumes {
163+
r.Content[consume] = &spec3.MediaType{
164+
MediaTypeProps: spec3.MediaTypeProps{
165+
Schema: schema,
166+
},
167+
}
168+
}
151169
return r, nil
152170
}
153171
}
@@ -156,7 +174,7 @@ func (o *openAPI) buildRequestBody(parameters []common.Parameter, bodySample int
156174

157175
func newOpenAPI(config *common.Config) openAPI {
158176
o := openAPI{
159-
config: config,
177+
config: common.ConvertConfigToV3(config),
160178
spec: &spec3.OpenAPI{
161179
Version: "3.0.0",
162180
Info: config.Info,
@@ -168,6 +186,21 @@ func newOpenAPI(config *common.Config) openAPI {
168186
},
169187
},
170188
}
189+
if len(o.config.ResponseDefinitions) > 0 {
190+
o.spec.Components.Responses = make(map[string]*spec3.Response)
191+
192+
}
193+
for k, response := range o.config.ResponseDefinitions {
194+
o.spec.Components.Responses[k] = response
195+
}
196+
197+
if len(o.config.SecuritySchemes) > 0 {
198+
o.spec.Components.SecuritySchemes = make(spec3.SecuritySchemes)
199+
200+
}
201+
for k, securityScheme := range o.config.SecuritySchemes {
202+
o.spec.Components.SecuritySchemes[k] = securityScheme
203+
}
171204

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

237270
pathItem = &spec3.Path{
238-
PathProps: spec3.PathProps{
239-
Parameters: make([]*spec3.Parameter, 0),
240-
},
271+
PathProps: spec3.PathProps{},
241272
}
242273

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

250281
for _, route := range routes {
251282
op, _ := o.buildOperations(route, inPathCommonParamsMap)
283+
sortParameters(op.Parameters)
252284

253285
switch strings.ToUpper(route.Method()) {
254286
case "GET":

pkg/common/common.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ import (
2222

2323
"github.com/emicklei/go-restful"
2424

25+
"k8s.io/kube-openapi/pkg/openapiconv"
26+
"k8s.io/kube-openapi/pkg/spec3"
2527
"k8s.io/kube-openapi/pkg/validation/spec"
2628
)
2729

@@ -117,6 +119,86 @@ type Config struct {
117119
DefaultSecurity []map[string][]string
118120
}
119121

122+
// OpenAPIV3Config is set of configuration for OpenAPI V3 spec generation.
123+
type OpenAPIV3Config struct {
124+
// Info is general information about the API.
125+
Info *spec.Info
126+
127+
// DefaultResponse will be used if an operation does not have any responses listed. It
128+
// will show up as ... "responses" : {"default" : $DefaultResponse} in the spec.
129+
DefaultResponse *spec3.Response
130+
131+
// ResponseDefinitions will be added to responses component. This is an object
132+
// that holds responses that can be used across operations.
133+
ResponseDefinitions map[string]*spec3.Response
134+
135+
// CommonResponses will be added as a response to all operation specs. This is a good place to add common
136+
// responses such as authorization failed.
137+
CommonResponses map[int]*spec3.Response
138+
139+
// List of webservice's path prefixes to ignore
140+
IgnorePrefixes []string
141+
142+
// OpenAPIDefinitions should provide definition for all models used by routes. Failure to provide this map
143+
// or any of the models will result in spec generation failure.
144+
GetDefinitions GetOpenAPIDefinitions
145+
146+
// GetOperationIDAndTags returns operation id and tags for a restful route. It is an optional function to customize operation IDs.
147+
//
148+
// Deprecated: GetOperationIDAndTagsFromRoute should be used instead. This cannot be specified if using the new Route
149+
// interface set of funcs.
150+
GetOperationIDAndTags func(r *restful.Route) (string, []string, error)
151+
152+
// GetOperationIDAndTagsFromRoute returns operation id and tags for a Route. It is an optional function to customize operation IDs.
153+
GetOperationIDAndTagsFromRoute func(r Route) (string, []string, error)
154+
155+
// GetDefinitionName returns a friendly name for a definition base on the serving path. parameter `name` is the full name of the definition.
156+
// It is an optional function to customize model names.
157+
GetDefinitionName func(name string) (string, spec.Extensions)
158+
159+
// SecuritySchemes is list of all security schemes for OpenAPI service.
160+
SecuritySchemes spec3.SecuritySchemes
161+
162+
// DefaultSecurity for all operations.
163+
DefaultSecurity []map[string][]string
164+
}
165+
166+
// ConvertConfigToV3 converts a Config object to an OpenAPIV3Config object
167+
func ConvertConfigToV3(config *Config) *OpenAPIV3Config {
168+
if config == nil {
169+
return nil
170+
}
171+
172+
v3Config := &OpenAPIV3Config{
173+
Info: config.Info,
174+
IgnorePrefixes: config.IgnorePrefixes,
175+
GetDefinitions: config.GetDefinitions,
176+
GetOperationIDAndTags: config.GetOperationIDAndTags,
177+
GetOperationIDAndTagsFromRoute: config.GetOperationIDAndTagsFromRoute,
178+
GetDefinitionName: config.GetDefinitionName,
179+
SecuritySchemes: make(spec3.SecuritySchemes),
180+
DefaultSecurity: config.DefaultSecurity,
181+
DefaultResponse: openapiconv.ConvertResponse(config.DefaultResponse, []string{"application/json"}),
182+
183+
CommonResponses: make(map[int]*spec3.Response),
184+
ResponseDefinitions: make(map[string]*spec3.Response),
185+
}
186+
187+
if config.SecurityDefinitions != nil {
188+
for s, securityScheme := range *config.SecurityDefinitions {
189+
v3Config.SecuritySchemes[s] = openapiconv.ConvertSecurityScheme(securityScheme)
190+
}
191+
}
192+
for k, commonResponse := range config.CommonResponses {
193+
v3Config.CommonResponses[k] = openapiconv.ConvertResponse(&commonResponse, []string{"application/json"})
194+
}
195+
196+
for k, responseDefinition := range config.ResponseDefinitions {
197+
v3Config.ResponseDefinitions[k] = openapiconv.ConvertResponse(&responseDefinition, []string{"application/json"})
198+
}
199+
return v3Config
200+
}
201+
120202
type typeInfo struct {
121203
name string
122204
format string

0 commit comments

Comments
 (0)