diff --git a/multiapi-engine/pom.xml b/multiapi-engine/pom.xml
index 1f447e23..fb183eb1 100644
--- a/multiapi-engine/pom.xml
+++ b/multiapi-engine/pom.xml
@@ -4,7 +4,7 @@
com.sngular
multiapi-engine
- 6.6.1
+ 6.6.2
jar
diff --git a/multiapi-engine/src/main/java/com/sngular/api/generator/plugin/openapi/utils/OpenApiUtil.java b/multiapi-engine/src/main/java/com/sngular/api/generator/plugin/openapi/utils/OpenApiUtil.java
index 5dbc47fe..6807f25f 100644
--- a/multiapi-engine/src/main/java/com/sngular/api/generator/plugin/openapi/utils/OpenApiUtil.java
+++ b/multiapi-engine/src/main/java/com/sngular/api/generator/plugin/openapi/utils/OpenApiUtil.java
@@ -207,6 +207,11 @@ private static void processParameters(final HashMap basicJsonN
private static void processRequestBody(final HashMap basicJsonNodeMap, final JsonNode operation, SpecFile specFile) {
if (ApiTool.hasNode(operation, "requestBody") && !operation.at("/requestBody/content").isMissingNode()) {
final var content = operation.at("/requestBody/content");
+ if (content.has("multipart/form-data")) {
+ // multipart parts are exposed individually as @RequestPart parameters,
+ // so no wrapper model is generated for them.
+ return;
+ }
final var schema = content.findValue("schema");
if (!ApiTool.hasRef(schema)) {
basicJsonNodeMap.put(StringCaseUtils.titleToSnakeCase(MapperUtil.getPojoName("InlineObject" + StringUtils.capitalize(getOperationId(operation)), specFile)), schema);
diff --git a/multiapi-engine/src/main/resources/templates/openapi/template.ftlh b/multiapi-engine/src/main/resources/templates/openapi/template.ftlh
index f98549b4..cca53a5d 100644
--- a/multiapi-engine/src/main/resources/templates/openapi/template.ftlh
+++ b/multiapi-engine/src/main/resources/templates/openapi/template.ftlh
@@ -3,9 +3,28 @@ package <#if packageApi??>${packageApi}<#elseif package??> ${package}#if>;
<#assign imports=[]>
<#assign model_imports=[]>
+<#assign needsMultipartFile=false>
<#list pathObjects as path>
<#list path.operationObjects as operation>
<#list operation.requestObjects as request>
+ <#if request.isFormData>
+ <#list request.contentObjects as content>
+ <#if content.schemaObject??>
+ <#list content.schemaObject.fieldObjectList as field>
+ <#if "${field.dataType}"?contains("MultipartFile")>
+ <#assign needsMultipartFile=true>
+ #if>
+ #list>
+ #if>
+ #list>
+ #if>
+ #list>
+ #list>
+#list>
+<#list pathObjects as path>
+ <#list path.operationObjects as operation>
+ <#list operation.requestObjects as request>
+ <#if !request.isFormData>
<#list request.contentObjects as content>
<#if content.importName?? && (!imports?seq_contains(content.importName)) && (!model_imports?seq_contains(content.importName))>
<#if (!checkBasicTypes?seq_contains(content.importName))>
@@ -17,6 +36,7 @@ package <#if packageApi??>${packageApi}<#elseif package??> ${package}#if>;
#if>
#if>
#list>
+ #if>
#list>
<#list operation.responseObjects as response>
<#list response.contentObjects as content>
@@ -62,6 +82,9 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.NativeWebRequest;
+<#if needsMultipartFile>
+import org.springframework.web.multipart.MultipartFile;
+#if>
<#list model_imports as import>
import <#if packageModel??>${packageModel}<#elseif package??>${package}#if>.${import};
@@ -80,7 +103,7 @@ public interface ${className?cap_first}Api {
*<#list path.parameterObjects as parameter> @param ${parameter.name} <#if parameter.description?has_content>${parameter.description} #if>${parameter.required?c}#list>
#if>
<#if operation.requestObjects?has_content>
- *<#list operation.requestObjects as request><#list request.contentObjects as content> @param ${content.dataType?api.getVariableNameString()}<#if content?has_next>, #if>#list>${request.description! ""}<#if request.required == true> (required)#if>#list>
+ *<#list operation.requestObjects as request><#if request.isFormData><#list request.contentObjects[0].schemaObject.fieldObjectList as part> @param ${part.baseName}#list><#else><#list request.contentObjects as content> @param ${content.dataType?api.getVariableNameString()}<#if content?has_next>, #if>#list>#if>${request.description! ""}<#if request.required == true> (required)#if>#list>
#if>
* @return<#list operation.responseObjects as response><#if response.responseName != "default"> ${response.description}; (status code ${response.responseName})#if>#list>#if>
*/
@@ -97,15 +120,16 @@ public interface ${className?cap_first}Api {
@RequestMapping(
method = RequestMethod.${operation.operationType},
value = "${path.pathName}",
- produces = {"application/json"}
+ produces = {"application/json"}<#if operation.requestObjects?has_content && operation.requestObjects[0].isFormData>,
+ consumes = {MediaType.MULTIPART_FORM_DATA_VALUE}#if>
)
default ResponseEntity<@compress single_line=true><#if operation.responseObjects[0].contentObjects[0]??>
<${operation.responseObjects[0].contentObjects[0].dataType}<#else>>@compress> ${operation.operationId}(<@compress single_line=true>
<#if operation.parameterObjects?has_content><#list operation.parameterObjects as parameter> @Parameter(name = "${parameter.name}", <#if parameter.description?has_content>description = "${parameter.description}", #if>required = ${parameter.required?c}, schema = @Schema(description = "")) <#if parameter.in == "path"> @PathVariable("${parameter.name}") <#elseif parameter.in == "query"> @RequestParam(required = ${parameter.required?c}) #if> ${parameter.dataType} ${parameter.name} <#if parameter?has_next || operation.requestObjects?has_content>, #if>#list>#if>
<#if path.parameterObjects?has_content><#list path.parameterObjects as parameter> @Parameter(name = "${parameter.name}", <#if parameter.description?has_content>description = "${parameter.description}", #if>required = ${parameter.required?c}, schema = @Schema(description = "")) <#if parameter.in == "path"> @PathVariable("${parameter.name}") <#elseif parameter.in == "query"> @RequestParam(required = ${parameter.required?c}) #if> ${parameter.dataType} ${parameter.name} <#if parameter?has_next || operation.requestObjects?has_content>, #if>#list>#if>
- <#if operation.requestObjects?has_content><#list operation.requestObjects as request> @Parameter(name = "${request.contentObjects[0].dataType?api.getVariableNameString()}", description = "${request.description! ""}", required = ${request.required?c}, schema = @Schema(description = "${request.contentObjects[0].description! ""}")) @Valid <#if request.isFormData == false>@RequestBody#if>
- ${request.contentObjects[0].dataType} ${request.contentObjects[0].dataType?api.getVariableNameString()} <#if request?has_next>, #if>#list>#if>@compress>) {
+ <#if operation.requestObjects?has_content><#list operation.requestObjects as request><#if request.isFormData><#list request.contentObjects[0].schemaObject.fieldObjectList as part> @Parameter(name = "${part.baseName}", required = ${part.required?c}, schema = @Schema(description = "")) @RequestPart(value = "${part.baseName}", required = ${part.required?c}) ${part.dataType} ${part.baseName} <#if part?has_next || request?has_next>, #if>#list><#else> @Parameter(name = "${request.contentObjects[0].dataType?api.getVariableNameString()}", description = "${request.description! ""}", required = ${request.required?c}, schema = @Schema(description = "${request.contentObjects[0].description! ""}")) @Valid @RequestBody
+ ${request.contentObjects[0].dataType} ${request.contentObjects[0].dataType?api.getVariableNameString()} <#if request?has_next>, #if>#if>#list>#if>@compress>) {
return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
}
#list>
diff --git a/multiapi-engine/src/test/resources/openapigenerator/testFormDataMultipartGeneration/api-test.yml b/multiapi-engine/src/test/resources/openapigenerator/testFormDataMultipartGeneration/api-test.yml
index 7ab7b27f..c2181132 100644
--- a/multiapi-engine/src/test/resources/openapigenerator/testFormDataMultipartGeneration/api-test.yml
+++ b/multiapi-engine/src/test/resources/openapigenerator/testFormDataMultipartGeneration/api-test.yml
@@ -22,6 +22,11 @@ paths:
someFile:
type: string
format: binary
+ someFiles:
+ type: array
+ items:
+ type: string
+ format: binary
responses:
'200':
diff --git a/multiapi-engine/src/test/resources/openapigenerator/testFormDataMultipartGeneration/assets/TestApi.java b/multiapi-engine/src/test/resources/openapigenerator/testFormDataMultipartGeneration/assets/TestApi.java
index f55eba8d..8af926c8 100644
--- a/multiapi-engine/src/test/resources/openapigenerator/testFormDataMultipartGeneration/assets/TestApi.java
+++ b/multiapi-engine/src/test/resources/openapigenerator/testFormDataMultipartGeneration/assets/TestApi.java
@@ -15,14 +15,14 @@
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.NativeWebRequest;
+import org.springframework.web.multipart.MultipartFile;
-import com.sngular.multifileplugin.testformdatamultipartgeneration.InlineObjectTestMultipart;
public interface TestApi {
/**
* GET /test
- * @param inlineObjectTestMultipart (required)
+ * @param someFile @param someFiles @param someString (required)
* @return OK; (status code 200)
*/
@@ -36,10 +36,11 @@ public interface TestApi {
@RequestMapping(
method = RequestMethod.GET,
value = "/test",
- produces = {"application/json"}
+ produces = {"application/json"},
+ consumes = {MediaType.MULTIPART_FORM_DATA_VALUE}
)
- default ResponseEntity testMultipart(@Parameter(name = "inlineObjectTestMultipart", description = "", required = true, schema = @Schema(description = "")) @Valid InlineObjectTestMultipart inlineObjectTestMultipart) {
+ default ResponseEntity testMultipart(@Parameter(name = "someFile", required = false, schema = @Schema(description = "")) @RequestPart(value = "someFile", required = false) MultipartFile someFile , @Parameter(name = "someFiles", required = false, schema = @Schema(description = "")) @RequestPart(value = "someFiles", required = false) List someFiles , @Parameter(name = "someString", required = false, schema = @Schema(description = "")) @RequestPart(value = "someString", required = false) String someString) {
return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
}
diff --git a/scs-multiapi-gradle-plugin/build.gradle b/scs-multiapi-gradle-plugin/build.gradle
index 3d8ecccf..a28a2352 100644
--- a/scs-multiapi-gradle-plugin/build.gradle
+++ b/scs-multiapi-gradle-plugin/build.gradle
@@ -21,7 +21,7 @@ repositories {
}
group = 'com.sngular'
-version = '6.6.1'
+version = '6.6.2'
def SCSMultiApiPluginGroupId = group
def SCSMultiApiPluginVersion = version
@@ -31,7 +31,7 @@ dependencies {
shadow localGroovy()
shadow gradleApi()
- implementation 'com.sngular:multiapi-engine:6.6.1'
+ implementation 'com.sngular:multiapi-engine:6.6.2'
testImplementation 'org.assertj:assertj-core:3.24.2'
testImplementation 'com.puppycrawl.tools:checkstyle:10.12.3'
testImplementation 'org.junit.platform:junit-platform-launcher:1.9.2'
@@ -100,7 +100,7 @@ testing {
integrationTest(JvmTestSuite) {
dependencies {
- implementation 'com.sngular:scs-multiapi-gradle-plugin:6.6.1'
+ implementation 'com.sngular:scs-multiapi-gradle-plugin:6.6.2'
implementation 'org.assertj:assertj-core:3.24.2'
}
diff --git a/scs-multiapi-maven-plugin/pom.xml b/scs-multiapi-maven-plugin/pom.xml
index 13c08fe2..c26471b9 100644
--- a/scs-multiapi-maven-plugin/pom.xml
+++ b/scs-multiapi-maven-plugin/pom.xml
@@ -4,7 +4,7 @@
com.sngular
scs-multiapi-maven-plugin
- 6.6.1
+ 6.6.2
maven-plugin
AsyncApi - OpenApi Code Generator Maven Plugin
@@ -271,7 +271,7 @@
com.sngular
multiapi-engine
- 6.6.1
+ 6.6.2
org.apache.maven