11/*
2- * Copyright 2019 the original authors
2+ * Copyright 2019-2020 the original authors
33 *
44 * Licensed under the Apache License, Version 2.0 (the "License");
55 * you may not use this file except in compliance with the License.
@@ -21,10 +21,13 @@ import com.github.hauner.openapi.spring.converter.schema.RefResolver
2121import com.github.hauner.openapi.spring.converter.schema.ResponseSchemaInfo
2222import com.github.hauner.openapi.spring.converter.schema.SchemaInfo
2323import com.github.hauner.openapi.spring.model.Api
24+ import com.github.hauner.openapi.spring.model.DataTypes
2425import com.github.hauner.openapi.spring.model.Endpoint
25- import com.github.hauner.openapi.spring.model.RequestBody
26+ import com.github.hauner.openapi.spring.model.RequestBody as ModelRequestBody
27+ import com.github.hauner.openapi.spring.model.datatypes.ObjectDataType
2628import com.github.hauner.openapi.spring.model.parameters.CookieParameter
2729import com.github.hauner.openapi.spring.model.parameters.HeaderParameter
30+ import com.github.hauner.openapi.spring.model.parameters.MultipartParameter
2831import com.github.hauner.openapi.spring.model.parameters.Parameter as ModelParameter
2932import com.github.hauner.openapi.spring.model.parameters.PathParameter
3033import com.github.hauner.openapi.spring.model.parameters.QueryParameter
@@ -36,7 +39,9 @@ import io.swagger.v3.oas.models.OpenAPI
3639import io.swagger.v3.oas.models.PathItem
3740import io.swagger.v3.oas.models.media.MediaType
3841import io.swagger.v3.oas.models.parameters.Parameter
42+ import io.swagger.v3.oas.models.parameters.RequestBody
3943import io.swagger.v3.oas.models.responses.ApiResponse
44+ import io.swagger.v3.oas.models.responses.ApiResponses
4045
4146/**
4247 * Converts the open api model to a new model that is better suited for generating source files
@@ -46,6 +51,7 @@ import io.swagger.v3.oas.models.responses.ApiResponse
4651 */
4752@Slf4j
4853class ApiConverter {
54+ public static final String MULTIPART = " multipart/form-data"
4955
5056 private DataTypeConverter dataTypeConverter
5157 private ApiOptions options
@@ -76,7 +82,6 @@ class ApiConverter {
7682 }
7783
7884 private Map<String , PathItem > addEndpointsToInterfaces (OpenAPI api , Api target ) {
79- def resolver = new RefResolver (api. components)
8085
8186 api. paths. each { Map.Entry <String , PathItem > pathEntry ->
8287 String path = pathEntry. key
@@ -89,47 +94,11 @@ class ApiConverter {
8994 Endpoint ep = new Endpoint (path : path, method : httpOperation. httpMethod)
9095
9196 try {
92- httpOperation. parameters. each { Parameter parameter ->
93- ep. parameters. addAll (createParameter(path, parameter, target, resolver))
94- }
95-
96- if (httpOperation. requestBody != null ) {
97- def required = httpOperation. requestBody. required != null ?: false
98- httpOperation. requestBody. content. each { Map.Entry <String , MediaType > requestBodyEntry ->
99- def contentType = requestBodyEntry. key
100- def requestBody = requestBodyEntry. value
101-
102- def info = new SchemaInfo (path, requestBody. schema, getInlineTypeName (path))
103- info. resolver = resolver
104-
105- DataType dataType = dataTypeConverter. convert (info, target. models)
106-
107- def body = new RequestBody (
108- contentType : contentType,
109- requestBodyType : dataType,
110- required : required)
111-
112- ep. requestBodies. add (body)
113- }
114- }
115-
116- httpOperation. responses. each { Map.Entry <String , ApiResponse > responseEntry ->
117- def httpStatus = responseEntry. key
118- def httpResponse = responseEntry. value
119-
120- if (! httpResponse. content) {
121- ep. responses. add (createEmptyResponse ())
122- } else {
123- List<Response > responses = createResponses (
124- path,
125- httpResponse,
126- getInlineResponseName (path, httpStatus),
127- target,
128- resolver)
129-
130- ep. responses. addAll (responses)
131- }
132- }
97+ def resolver = new RefResolver (api. components)
98+
99+ collectParameters (httpOperation. parameters, ep, target. models, resolver)
100+ collectRequestBody (httpOperation. requestBody, ep, target. models, resolver)
101+ collectResponses (httpOperation. responses, ep, target. models, resolver)
133102
134103 itf. endpoints. add (ep)
135104
@@ -140,11 +109,59 @@ class ApiConverter {
140109 }
141110 }
142111
143- private ModelParameter createParameter (String path , Parameter parameter , Api target , resolver ) {
112+ private void collectParameters (List<Parameter > parameters , Endpoint ep , DataTypes dataTypes , RefResolver resolver ) {
113+ parameters. each { Parameter parameter ->
114+ ep. parameters. add (createParameter (ep. path, parameter, dataTypes, resolver))
115+ }
116+ }
117+
118+ private void collectRequestBody (RequestBody requestBody , Endpoint ep , DataTypes dataTypes , RefResolver resolver ) {
119+ if (requestBody == null ) {
120+ return
121+ }
122+
123+ def required = requestBody. required != null ?: false
124+
125+ requestBody. content. each { Map.Entry <String , MediaType > requestBodyEntry ->
126+ def contentType = requestBodyEntry. key
127+ def reqBody = requestBodyEntry. value
128+
129+ def info = new SchemaInfo (ep. path, reqBody. schema, getInlineTypeName (ep. path))
130+ info. resolver = resolver
131+
132+ if (contentType == MULTIPART ) {
133+ ep. parameters. addAll (createMultipartParameter (info, required))
134+ } else {
135+ ep. requestBodies. add (createRequestBody (contentType, info, required, dataTypes))
136+ }
137+ }
138+ }
139+
140+ private collectResponses (ApiResponses responses , Endpoint ep , DataTypes dataTypes , RefResolver resolver ) {
141+ responses. each { Map.Entry <String , ApiResponse > responseEntry ->
142+ def httpStatus = responseEntry. key
143+ def httpResponse = responseEntry. value
144+
145+ if (! httpResponse. content) {
146+ ep. responses. add (createEmptyResponse ())
147+ } else {
148+ List<Response > results = createResponses (
149+ ep. path,
150+ httpResponse,
151+ getInlineResponseName (ep. path, httpStatus),
152+ dataTypes,
153+ resolver)
154+
155+ ep. responses. addAll (results)
156+ }
157+ }
158+ }
159+
160+ private ModelParameter createParameter (String path , Parameter parameter , DataTypes dataTypes , resolver ) {
144161 def info = new ParameterSchemaInfo (path, parameter. schema, parameter. name)
145162 info. resolver = resolver
146163
147- DataType dataType = dataTypeConverter. convert (info, target . models )
164+ DataType dataType = dataTypeConverter. convert (info, dataTypes )
148165
149166 switch (parameter. in ) {
150167 case ' query' :
@@ -161,19 +178,27 @@ class ApiConverter {
161178 }
162179 }
163180
164- private String getInlineTypeName (String path ) {
165- Identifier . toClass (path) + ' RequestBody'
166- }
181+ private ModelRequestBody createRequestBody (String contentType , SchemaInfo info , boolean required , DataTypes dataTypes ) {
182+ DataType dataType = dataTypeConverter. convert (info, dataTypes)
167183
168- private String getInlineResponseName (String path , String httpStatus ) {
169- Identifier . toClass (path) + ' Response' + httpStatus
184+ new ModelRequestBody (
185+ contentType : contentType,
186+ requestBodyType : dataType,
187+ required : required)
170188 }
171189
172- private Response createEmptyResponse () {
173- new Response (responseType : dataTypeConverter. none ())
190+ private Collection<ModelParameter > createMultipartParameter (SchemaInfo info , boolean required ) {
191+ DataType dataType = dataTypeConverter. convert (info, new DataTypes ())
192+ if (! (dataType instanceof ObjectDataType )) {
193+ throw new MultipartResponseBodyException (info. path)
194+ }
195+
196+ dataType. getObjectProperties (). collect {
197+ new MultipartParameter (name : it. key, required : required, dataType : it. value)
198+ }
174199 }
175200
176- private List<Response > createResponses (String path , ApiResponse apiResponse , String inlineName , Api target , RefResolver resolver ) {
201+ private List<Response > createResponses (String path , ApiResponse apiResponse , String inlineName , DataTypes dataTypes , RefResolver resolver ) {
177202 def responses = []
178203
179204 apiResponse. content. each { Map.Entry <String , MediaType > contentEntry ->
@@ -188,9 +213,7 @@ class ApiConverter {
188213 inlineName)
189214 info. resolver = resolver
190215
191- DataType dataType = dataTypeConverter. convert (
192- info,
193- target. models)
216+ DataType dataType = dataTypeConverter. convert (info, dataTypes)
194217
195218 def response = new Response (
196219 contentType : contentType,
@@ -202,6 +225,18 @@ class ApiConverter {
202225 responses
203226 }
204227
228+ private String getInlineTypeName (String path ) {
229+ Identifier . toClass (path) + ' RequestBody'
230+ }
231+
232+ private String getInlineResponseName (String path , String httpStatus ) {
233+ Identifier . toClass (path) + ' Response' + httpStatus
234+ }
235+
236+ private Response createEmptyResponse () {
237+ new Response (responseType : dataTypeConverter. none ())
238+ }
239+
205240 private void collectInterfaces (OpenAPI api , Api target ) {
206241 target. interfaces = new InterfaceCollector (options)
207242 .collect (api. paths)
0 commit comments