Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion multiapi-engine/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

<groupId>com.sngular</groupId>
<artifactId>multiapi-engine</artifactId>
<version>6.6.2</version>
<version>6.6.3</version>
<packaging>jar</packaging>

<properties>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<#ftl output_format="plainText">
<#macro formDataParts request><#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}) <#if part.dataType.baseType == "multipartfile">FilePart<#elseif part.dataType.baseType == "array" && part.dataType.innerType.baseType == "multipartfile">Flux<FilePart><#else>${part.dataType}</#if> ${part.baseName}<#if part?has_next>, </#if></#list></#macro>
package <#if packageApi??>${packageApi}<#elseif package??>${package}</#if>;

import java.util.List;
Expand All @@ -21,18 +22,41 @@ import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import reactor.core.publisher.Flux;
import springfox.documentation.annotations.ApiIgnore;
<#assign needsFilePart=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 needsFilePart=true>
</#if>
</#list>
</#if>
</#list>
</#if>
</#list>
</#list>
</#list>
<#if needsFilePart>
import org.springframework.http.codec.multipart.FilePart;
</#if>

<#assign imports=[]>
<#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))>
<#if (!checkBasicTypes?seq_contains(content.importName))>
<#assign imports = imports+[content.importName]>
</#if>
</#if>
</#list>
</#if>
</#list>
<#list operation.responseObjects as response>
<#list response.contentObjects as content>
Expand Down Expand Up @@ -70,7 +94,7 @@ public interface ${className?cap_first}Api {
* @param <#list path.parameterObjects as parameter> ${parameter.name} ${parameter.description} ${parameter.required?c}</#list>
</#if>
<#if operation.requestObjects?has_content>
*<#list operation.requestObjects as request> @param <#list request.contentObjects as content>${content.dataType?api.getVariableNameString()}<#if content?has_next>, </#if></#list>${request.description! ""}<#if request.required == true> (required)</#if></#list>
*<#list operation.requestObjects as request> @param <#if request.isFormData><#list request.contentObjects[0].schemaObject.fieldObjectList as part>${part.baseName}<#if part?has_next>, </#if></#list><#else><#list request.contentObjects as content>${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>
Expand All @@ -88,22 +112,23 @@ 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>
)
<#if operation.securities?has_content>
default Mono<ResponseEntity<@compress single_line=true><<#if operation.responseObjects[0].contentObjects[0]??><#if operation.responseObjects[0].contentObjects[0].dataType.baseType == "array" || operation.responseObjects[0].contentObjects[0].dataType.baseType == "map">Flux<${operation.responseObjects[0].contentObjects[0].dataType.innerType}><#else>${operation.responseObjects[0].contentObjects[0].dataType}></#if><#else>Void</#if>></@compress> ${operation.operationId}(<@compress single_line=true>
<#if operation.parameterObjects?has_content><#list operation.parameterObjects as parameter> @Parameter(name = "${parameter.name}", description = "${parameter.description}", 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}", description = "${parameter.description}", 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 @RequestBody
<#if operation.requestObjects?has_content><#list operation.requestObjects as request><#if request.isFormData><@formDataParts request/><#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
<#if request.contentObjects[0].dataType.baseType == "array" || request.contentObjects[0].dataType.baseType == "map"> Flux<${request.contentObjects[0].dataType.innerType}>
<#else> Mono<${request.contentObjects[0].dataType}> </#if> ${request.contentObjects[0].dataType?api.getVariableNameString()}<#if request?has_next>, </#if></#list></#if></@compress><#if operation.parameterObjects?has_content || operation.requestObjects?has_content>, </#if><#if path.parameterObjects?has_content || path.requestObjects?has_content>, </#if>@ApiIgnore final ServerWebExchange exchange) {
<#else> Mono<${request.contentObjects[0].dataType}> </#if> ${request.contentObjects[0].dataType?api.getVariableNameString()}<#if request?has_next>, </#if></#if></#list></#if></@compress><#if operation.parameterObjects?has_content || operation.requestObjects?has_content>, </#if><#if path.parameterObjects?has_content || path.requestObjects?has_content>, </#if>@ApiIgnore final ServerWebExchange exchange) {
return Mono.just(new ResponseEntity(HttpStatus.NOT_IMPLEMENTED));
}
<#else>
default ResponseEntity<@compress single_line=true><<#if operation.responseObjects[0].contentObjects[0]??><#if operation.responseObjects[0].contentObjects[0].dataType.baseType == "array" || operation.responseObjects[0].contentObjects[0].dataType.baseType == "map">Flux<${operation.responseObjects[0].contentObjects[0].dataType.innerType}><#else>Mono<${operation.responseObjects[0].contentObjects[0].dataType}></#if><#else>Void</#if>></@compress> ${operation.operationId}(<@compress single_line=true>
<#if operation.parameterObjects?has_content><#list operation.parameterObjects as parameter> @Parameter(name = "${parameter.name}", description = "${parameter.description}", 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}", description = "${parameter.description}", 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 @RequestBody
<#if operation.requestObjects?has_content><#list operation.requestObjects as request><#if request.isFormData><@formDataParts request/><#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
<#if request.contentObjects[0].dataType.baseType == "array" || request.contentObjects[0].dataType.baseType == "map"> Flux<${request.contentObjects[0].dataType.innerType}>
<#else> Mono<${request.contentObjects[0].dataType}> </#if> ${request.contentObjects[0].dataType?api.getVariableNameString()}<#if request?has_next>, </#if></#list></#if></@compress><#if operation.parameterObjects?has_content || operation.requestObjects?has_content>, </#if><#if path.parameterObjects?has_content || path.requestObjects?has_content>, </#if>@ApiIgnore final ServerWebExchange exchange) {
<#else> Mono<${request.contentObjects[0].dataType}> </#if> ${request.contentObjects[0].dataType?api.getVariableNameString()}<#if request?has_next>, </#if></#if></#list></#if></@compress><#if operation.parameterObjects?has_content || operation.requestObjects?has_content>, </#if><#if path.parameterObjects?has_content || path.requestObjects?has_content>, </#if>@ApiIgnore final ServerWebExchange exchange) {
return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
}
</#if>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@ public final class OpenApiGeneratorFixtures {
.modelPackage("com.sngular.multifileplugin.reactivegeneration.model").modelNamePrefix("Api")
.modelNameSuffix("DTO").useLombokModelAnnotation(false).isReactive(true).build());

static final List<SpecFile> TEST_REACTIVE_FORM_DATA_MULTIPART = List
.of(SpecFile.builder().filePath("openapigenerator/testReactiveFormDataMultipart/api-test.yml")
.apiPackage("com.sngular.multifileplugin.testreactiveformdatamultipart")
.useLombokModelAnnotation(false).isReactive(true).build());

static final List<SpecFile> TEST_API_TAGS_GENERATION = List
.of(SpecFile.builder().filePath("openapigenerator/testApiTagsGeneration/api-tags-test.yml")
.apiPackage("com.sngular.multifileplugin.tagsgeneration")
Expand Down Expand Up @@ -602,6 +607,22 @@ static Function<Path, Boolean> validateApiReactiveGeneration(int springBootVersi

}

static Function<Path, Boolean> validateReactiveFormDataMultipart() {

final String DEFAULT_TARGET_API = "generated/com/sngular/multifileplugin/testreactiveformdatamultipart";

final String DEFAULT_MODEL_API = "generated/com/sngular/multifileplugin/testreactiveformdatamultipart/model";

final String COMMON_PATH = "openapigenerator/testReactiveFormDataMultipart/";

final String ASSETS_PATH = COMMON_PATH + "assets/";

final List<String> expectedTestApiFile = List.of(ASSETS_PATH + "UploadApi.java");

return path -> commonTest(path, expectedTestApiFile, Collections.emptyList(), DEFAULT_TARGET_API,
DEFAULT_MODEL_API, Collections.emptyList(), null);
}

static Function<Path, Boolean> validateTagsGeneration() {

final String DEFAULT_TARGET_API = "generated/com/sngular/multifileplugin/tagsgeneration";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ static Stream<Arguments> fileSpecToProcess() {
OpenApiGeneratorFixtures.validatePathWithSpecialCharGeneration()),
Arguments.of("testApiReactiveGeneration", OpenApiGeneratorFixtures.TEST_API_REACTIVE_GENERATION,
OpenApiGeneratorFixtures.validateApiReactiveGeneration(SPRING_BOOT_VERSION)),
Arguments.of("testReactiveFormDataMultipart", OpenApiGeneratorFixtures.TEST_REACTIVE_FORM_DATA_MULTIPART,
OpenApiGeneratorFixtures.validateReactiveFormDataMultipart()),
Arguments.of("testApiTagsGeneration", OpenApiGeneratorFixtures.TEST_API_TAGS_GENERATION,
OpenApiGeneratorFixtures.validateTagsGeneration()),
Arguments.of("testMultipleRefGeneration", OpenApiGeneratorFixtures.TEST_MULTIPLE_REF_GENERATION,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
openapi: 3.0.2
info:
title: Testing reactive multipart file upload
version: 1.0.0
servers:
- url: http://localhost/v1
paths:
/upload:
post:
tags:
- test
operationId: uploadMultipart
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
properties:
someString:
type: string
someFile:
type: string
format: binary
someFiles:
type: array
items:
type: string
format: binary
responses:
'200':
description:
OK
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.sngular.multifileplugin.testreactiveformdatamultipart;

import java.util.List;
import java.util.Map;
import java.nio.charset.StandardCharsets;
import javax.validation.Valid;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import org.springframework.http.MediaType;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import reactor.core.publisher.Flux;
import springfox.documentation.annotations.ApiIgnore;
import org.springframework.http.codec.multipart.FilePart;


public interface UploadApi {

/**
* POST /upload
* @param someFile, someFiles, someString (required)
* @return OK; (status code 200)
* @throws WebClientResponseException if an error occurs while attempting to invoke the API
*/
@Operation(
operationId = "uploadMultipart",
tags = {"test"},
responses = {
@ApiResponse(responseCode = "200", description = "OK")
}
)
@RequestMapping(
method = RequestMethod.POST,
value = "/upload",
produces = {"application/json"},
consumes = {MediaType.MULTIPART_FORM_DATA_VALUE}
)
default ResponseEntity<Void> uploadMultipart(@Parameter(name = "someFile", required = false, schema = @Schema(description = "")) @RequestPart(value = "someFile", required = false) FilePart someFile, @Parameter(name = "someFiles", required = false, schema = @Schema(description = "")) @RequestPart(value = "someFiles", required = false) Flux<FilePart> someFiles, @Parameter(name = "someString", required = false, schema = @Schema(description = "")) @RequestPart(value = "someString", required = false) String someString, @ApiIgnore final ServerWebExchange exchange) {
return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
}

}
6 changes: 3 additions & 3 deletions scs-multiapi-gradle-plugin/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ repositories {
}

group = 'com.sngular'
version = '6.6.2'
version = '6.6.3'

def SCSMultiApiPluginGroupId = group
def SCSMultiApiPluginVersion = version
Expand All @@ -31,7 +31,7 @@ dependencies {
shadow localGroovy()
shadow gradleApi()

implementation 'com.sngular:multiapi-engine:6.6.2'
implementation 'com.sngular:multiapi-engine:6.6.3'
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'
Expand Down Expand Up @@ -100,7 +100,7 @@ testing {

integrationTest(JvmTestSuite) {
dependencies {
implementation 'com.sngular:scs-multiapi-gradle-plugin:6.6.2'
implementation 'com.sngular:scs-multiapi-gradle-plugin:6.6.3'
implementation 'org.assertj:assertj-core:3.24.2'
}

Expand Down
4 changes: 2 additions & 2 deletions scs-multiapi-maven-plugin/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

<groupId>com.sngular</groupId>
<artifactId>scs-multiapi-maven-plugin</artifactId>
<version>6.6.2</version>
<version>6.6.3</version>
<packaging>maven-plugin</packaging>

<name>AsyncApi - OpenApi Code Generator Maven Plugin</name>
Expand Down Expand Up @@ -271,7 +271,7 @@
<dependency>
<groupId>com.sngular</groupId>
<artifactId>multiapi-engine</artifactId>
<version>6.6.2</version>
<version>6.6.3</version>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
Expand Down
Loading