[Java][Spring] Fix RequestPart/RequestParam handling on multipart request in reactive stack (#16535)

* Fix requestpart in reactive

* Update samples
This commit is contained in:
Jorge Rodríguez Martín 2023-10-01 17:23:24 +02:00 committed by GitHub
parent 2eca00f87d
commit ee5d9f6ad3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 121 additions and 61 deletions

View File

@ -1 +1 @@
{{#isFormParam}}{{^isFile}}{{>paramDoc}}{{#useBeanValidation}} @Valid{{/useBeanValidation}} {{#isModel}}@RequestPart{{/isModel}}{{^isModel}}{{#isArray}}@RequestPart{{/isArray}}{{^isArray}}@RequestParam{{/isArray}}{{/isModel}}(value = "{{baseName}}"{{#required}}, required = true{{/required}}{{^required}}, required = false{{/required}}){{>dateTimeParam}} {{{dataType}}} {{paramName}}{{/isFile}}{{#isFile}}{{>paramDoc}} @RequestPart(value = "{{baseName}}"{{#required}}, required = true{{/required}}{{^required}}, required = false{{/required}}) {{#isArray}}List<{{/isArray}}{{#reactive}}Flux<Part>{{/reactive}}{{^reactive}}MultipartFile{{/reactive}}{{#isArray}}>{{/isArray}} {{paramName}}{{/isFile}}{{/isFormParam}}
{{#isFormParam}}{{^isFile}}{{>paramDoc}}{{#useBeanValidation}} @Valid{{/useBeanValidation}} {{#isModel}}@RequestPart{{/isModel}}{{^isModel}}{{#isArray}}@RequestPart{{/isArray}}{{^isArray}}{{#reactive}}@RequestPart{{/reactive}}{{^reactive}}@RequestParam{{/reactive}}{{/isArray}}{{/isModel}}(value = "{{baseName}}"{{#required}}, required = true{{/required}}{{^required}}, required = false{{/required}}){{>dateTimeParam}} {{{dataType}}} {{paramName}}{{/isFile}}{{#isFile}}{{>paramDoc}} @RequestPart(value = "{{baseName}}"{{#required}}, required = true{{/required}}{{^required}}, required = false{{/required}}) {{#isArray}}List<{{/isArray}}{{#reactive}}Flux<Part>{{/reactive}}{{^reactive}}MultipartFile{{/reactive}}{{#isArray}}>{{/isArray}} {{paramName}}{{/isFile}}{{/isFormParam}}

View File

@ -3175,4 +3175,64 @@ public class SpringCodegenTest {
;
}
@Test
public void givenMultipartForm_whenGenerateReactiveServer_thenParameterAreCreatedAsRequestPart() throws IOException {
File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
output.deleteOnExit();
String outputPath = output.getAbsolutePath().replace('\\', '/');
final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/spring/petstore-with-tags.yaml");
final SpringCodegen codegen = new SpringCodegen();
codegen.setOpenAPI(openAPI);
codegen.setOutputDir(output.getAbsolutePath());
codegen.additionalProperties().put(SpringCodegen.REACTIVE, "true");
ClientOptInput input = new ClientOptInput();
input.openAPI(openAPI);
input.config(codegen);
DefaultGenerator generator = new DefaultGenerator();
generator.setGeneratorPropertyDefault(CodegenConstants.MODELS, "false");
generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_TESTS, "false");
generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_DOCS, "false");
generator.setGeneratorPropertyDefault(CodegenConstants.APIS, "true");
generator.setGeneratorPropertyDefault(CodegenConstants.SUPPORTING_FILES, "false");
generator.opts(input).generate();
assertFileContains(Paths.get(outputPath + "/src/main/java/org/openapitools/api/PetApi.java"),
"@Valid @RequestPart(value = \"additionalMetadata\", required = false) String additionalMetadata");
}
@Test
public void givenMultipartForm_whenGenerateBlockedServer_thenParameterAreCreatedAsRequestPart() throws IOException {
File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
output.deleteOnExit();
String outputPath = output.getAbsolutePath().replace('\\', '/');
final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/spring/petstore-with-tags.yaml");
final SpringCodegen codegen = new SpringCodegen();
codegen.setOpenAPI(openAPI);
codegen.setOutputDir(output.getAbsolutePath());
ClientOptInput input = new ClientOptInput();
input.openAPI(openAPI);
input.config(codegen);
DefaultGenerator generator = new DefaultGenerator();
generator.setGeneratorPropertyDefault(CodegenConstants.MODELS, "false");
generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_TESTS, "false");
generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_DOCS, "false");
generator.setGeneratorPropertyDefault(CodegenConstants.APIS, "true");
generator.setGeneratorPropertyDefault(CodegenConstants.SUPPORTING_FILES, "false");
generator.opts(input).generate();
assertFileContains(Paths.get(outputPath + "/src/main/java/org/openapitools/api/PetApi.java"),
"@Valid @RequestParam(value = \"additionalMetadata\", required = false) String additionalMetadata");
}
}

View File

@ -207,20 +207,20 @@ public interface FakeApi {
contentType = "application/x-www-form-urlencoded"
)
Mono<ResponseEntity<Void>> testEndpointParameters(
@RequestParam(value = "number", required = true) BigDecimal number,
@RequestParam(value = "double", required = true) Double _double,
@RequestParam(value = "pattern_without_delimiter", required = true) String patternWithoutDelimiter,
@RequestParam(value = "byte", required = true) byte[] _byte,
@RequestParam(value = "integer", required = false) Integer integer,
@RequestParam(value = "int32", required = false) Integer int32,
@RequestParam(value = "int64", required = false) Long int64,
@RequestParam(value = "float", required = false) Float _float,
@RequestParam(value = "string", required = false) String string,
@RequestPart(value = "number", required = true) BigDecimal number,
@RequestPart(value = "double", required = true) Double _double,
@RequestPart(value = "pattern_without_delimiter", required = true) String patternWithoutDelimiter,
@RequestPart(value = "byte", required = true) byte[] _byte,
@RequestPart(value = "integer", required = false) Integer integer,
@RequestPart(value = "int32", required = false) Integer int32,
@RequestPart(value = "int64", required = false) Long int64,
@RequestPart(value = "float", required = false) Float _float,
@RequestPart(value = "string", required = false) String string,
@RequestPart(value = "binary", required = false) Flux<Part> binary,
@RequestParam(value = "date", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate date,
@RequestParam(value = "dateTime", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) OffsetDateTime dateTime,
@RequestParam(value = "password", required = false) String password,
@RequestParam(value = "callback", required = false) String paramCallback
@RequestPart(value = "date", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate date,
@RequestPart(value = "dateTime", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) OffsetDateTime dateTime,
@RequestPart(value = "password", required = false) String password,
@RequestPart(value = "callback", required = false) String paramCallback
);
@ -253,7 +253,7 @@ public interface FakeApi {
@RequestParam(value = "enum_query_integer", required = false) Integer enumQueryInteger,
@RequestParam(value = "enum_query_double", required = false) Double enumQueryDouble,
@RequestPart(value = "enum_form_string_array", required = false) List<String> enumFormStringArray,
@RequestParam(value = "enum_form_string", required = false) String enumFormString
@RequestPart(value = "enum_form_string", required = false) String enumFormString
);
@ -317,8 +317,8 @@ public interface FakeApi {
contentType = "application/x-www-form-urlencoded"
)
Mono<ResponseEntity<Void>> testJsonFormData(
@RequestParam(value = "param", required = true) String param,
@RequestParam(value = "param2", required = true) String param2
@RequestPart(value = "param", required = true) String param,
@RequestPart(value = "param2", required = true) String param2
);

View File

@ -177,8 +177,8 @@ public interface PetApi {
)
Mono<ResponseEntity<Void>> updatePetWithForm(
@PathVariable("petId") Long petId,
@RequestParam(value = "name", required = false) String name,
@RequestParam(value = "status", required = false) String status
@RequestPart(value = "name", required = false) String name,
@RequestPart(value = "status", required = false) String status
);
@ -199,7 +199,7 @@ public interface PetApi {
)
Mono<ResponseEntity<ModelApiResponse>> uploadFile(
@PathVariable("petId") Long petId,
@RequestParam(value = "additionalMetadata", required = false) String additionalMetadata,
@RequestPart(value = "additionalMetadata", required = false) String additionalMetadata,
@RequestPart(value = "file", required = false) Flux<Part> file
);
@ -222,7 +222,7 @@ public interface PetApi {
Mono<ResponseEntity<ModelApiResponse>> uploadFileWithRequiredFile(
@PathVariable("petId") Long petId,
@RequestPart(value = "requiredFile", required = true) Flux<Part> requiredFile,
@RequestParam(value = "additionalMetadata", required = false) String additionalMetadata
@RequestPart(value = "additionalMetadata", required = false) String additionalMetadata
);
}

View File

@ -376,20 +376,20 @@ public interface FakeApi {
@ResponseStatus(HttpStatus.BAD_REQUEST)
default Mono<Void> testEndpointParameters(
@ApiParam(value = "None", required = true) @Valid @RequestParam(value = "number", required = true) BigDecimal number,
@ApiParam(value = "None", required = true) @Valid @RequestParam(value = "double", required = true) Double _double,
@ApiParam(value = "None", required = true) @Valid @RequestParam(value = "pattern_without_delimiter", required = true) String patternWithoutDelimiter,
@ApiParam(value = "None", required = true) @Valid @RequestParam(value = "byte", required = true) byte[] _byte,
@ApiParam(value = "None") @Valid @RequestParam(value = "integer", required = false) Integer integer,
@ApiParam(value = "None") @Valid @RequestParam(value = "int32", required = false) Integer int32,
@ApiParam(value = "None") @Valid @RequestParam(value = "int64", required = false) Long int64,
@ApiParam(value = "None") @Valid @RequestParam(value = "float", required = false) Float _float,
@ApiParam(value = "None") @Valid @RequestParam(value = "string", required = false) String string,
@ApiParam(value = "None", required = true) @Valid @RequestPart(value = "number", required = true) BigDecimal number,
@ApiParam(value = "None", required = true) @Valid @RequestPart(value = "double", required = true) Double _double,
@ApiParam(value = "None", required = true) @Valid @RequestPart(value = "pattern_without_delimiter", required = true) String patternWithoutDelimiter,
@ApiParam(value = "None", required = true) @Valid @RequestPart(value = "byte", required = true) byte[] _byte,
@ApiParam(value = "None") @Valid @RequestPart(value = "integer", required = false) Integer integer,
@ApiParam(value = "None") @Valid @RequestPart(value = "int32", required = false) Integer int32,
@ApiParam(value = "None") @Valid @RequestPart(value = "int64", required = false) Long int64,
@ApiParam(value = "None") @Valid @RequestPart(value = "float", required = false) Float _float,
@ApiParam(value = "None") @Valid @RequestPart(value = "string", required = false) String string,
@ApiParam(value = "None") @RequestPart(value = "binary", required = false) Flux<Part> binary,
@ApiParam(value = "None") @Valid @RequestParam(value = "date", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate date,
@ApiParam(value = "None") @Valid @RequestParam(value = "dateTime", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) OffsetDateTime dateTime,
@ApiParam(value = "None") @Valid @RequestParam(value = "password", required = false) String password,
@ApiParam(value = "None") @Valid @RequestParam(value = "callback", required = false) String paramCallback,
@ApiParam(value = "None") @Valid @RequestPart(value = "date", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate date,
@ApiParam(value = "None") @Valid @RequestPart(value = "dateTime", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) OffsetDateTime dateTime,
@ApiParam(value = "None") @Valid @RequestPart(value = "password", required = false) String password,
@ApiParam(value = "None") @Valid @RequestPart(value = "callback", required = false) String paramCallback,
@ApiIgnore final ServerWebExchange exchange
) {
return getDelegate().testEndpointParameters(number, _double, patternWithoutDelimiter, _byte, integer, int32, int64, _float, string, binary, date, dateTime, password, paramCallback, exchange);
@ -436,7 +436,7 @@ public interface FakeApi {
@ApiParam(value = "Query parameter enum test (double)", allowableValues = "1, -2") @Valid @RequestParam(value = "enum_query_integer", required = false) Integer enumQueryInteger,
@ApiParam(value = "Query parameter enum test (double)", allowableValues = "1.1, -1.2") @Valid @RequestParam(value = "enum_query_double", required = false) Double enumQueryDouble,
@ApiParam(value = "Form parameter enum test (string array)", allowableValues = ">, $", defaultValue = "$") @Valid @RequestPart(value = "enum_form_string_array", required = false) List<String> enumFormStringArray,
@ApiParam(value = "Form parameter enum test (string)", allowableValues = "_abc, -efg, (xyz)", defaultValue = "-efg") @Valid @RequestParam(value = "enum_form_string", required = false) String enumFormString,
@ApiParam(value = "Form parameter enum test (string)", allowableValues = "_abc, -efg, (xyz)", defaultValue = "-efg") @Valid @RequestPart(value = "enum_form_string", required = false) String enumFormString,
@ApiIgnore final ServerWebExchange exchange
) {
return getDelegate().testEnumParameters(enumHeaderStringArray, enumHeaderString, enumQueryStringArray, enumQueryString, enumQueryInteger, enumQueryDouble, enumFormStringArray, enumFormString, exchange);
@ -539,8 +539,8 @@ public interface FakeApi {
@ResponseStatus(HttpStatus.OK)
default Mono<Void> testJsonFormData(
@ApiParam(value = "field1", required = true) @Valid @RequestParam(value = "param", required = true) String param,
@ApiParam(value = "field2", required = true) @Valid @RequestParam(value = "param2", required = true) String param2,
@ApiParam(value = "field1", required = true) @Valid @RequestPart(value = "param", required = true) String param,
@ApiParam(value = "field2", required = true) @Valid @RequestPart(value = "param2", required = true) String param2,
@ApiIgnore final ServerWebExchange exchange
) {
return getDelegate().testJsonFormData(param, param2, exchange);
@ -680,7 +680,7 @@ public interface FakeApi {
default Mono<ModelApiResponse> uploadFileWithRequiredFile(
@ApiParam(value = "ID of pet to update", required = true) @PathVariable("petId") Long petId,
@ApiParam(value = "file to upload", required = true) @RequestPart(value = "requiredFile", required = true) Flux<Part> requiredFile,
@ApiParam(value = "Additional data to pass to server") @Valid @RequestParam(value = "additionalMetadata", required = false) String additionalMetadata,
@ApiParam(value = "Additional data to pass to server") @Valid @RequestPart(value = "additionalMetadata", required = false) String additionalMetadata,
@ApiIgnore final ServerWebExchange exchange
) {
return getDelegate().uploadFileWithRequiredFile(petId, requiredFile, additionalMetadata, exchange);

View File

@ -312,8 +312,8 @@ public interface PetApi {
default Mono<Void> updatePetWithForm(
@ApiParam(value = "ID of pet that needs to be updated", required = true) @PathVariable("petId") Long petId,
@ApiParam(value = "Updated name of the pet") @Valid @RequestParam(value = "name", required = false) String name,
@ApiParam(value = "Updated status of the pet") @Valid @RequestParam(value = "status", required = false) String status,
@ApiParam(value = "Updated name of the pet") @Valid @RequestPart(value = "name", required = false) String name,
@ApiParam(value = "Updated status of the pet") @Valid @RequestPart(value = "status", required = false) String status,
@ApiIgnore final ServerWebExchange exchange
) {
return getDelegate().updatePetWithForm(petId, name, status, exchange);
@ -355,7 +355,7 @@ public interface PetApi {
default Mono<ModelApiResponse> uploadFile(
@ApiParam(value = "ID of pet to update", required = true) @PathVariable("petId") Long petId,
@ApiParam(value = "Additional data to pass to server") @Valid @RequestParam(value = "additionalMetadata", required = false) String additionalMetadata,
@ApiParam(value = "Additional data to pass to server") @Valid @RequestPart(value = "additionalMetadata", required = false) String additionalMetadata,
@ApiParam(value = "file to upload") @RequestPart(value = "file", required = false) Flux<Part> file,
@ApiIgnore final ServerWebExchange exchange
) {

View File

@ -366,20 +366,20 @@ public interface FakeApi {
)
default Mono<ResponseEntity<Void>> testEndpointParameters(
@ApiParam(value = "None", required = true) @Valid @RequestParam(value = "number", required = true) BigDecimal number,
@ApiParam(value = "None", required = true) @Valid @RequestParam(value = "double", required = true) Double _double,
@ApiParam(value = "None", required = true) @Valid @RequestParam(value = "pattern_without_delimiter", required = true) String patternWithoutDelimiter,
@ApiParam(value = "None", required = true) @Valid @RequestParam(value = "byte", required = true) byte[] _byte,
@ApiParam(value = "None") @Valid @RequestParam(value = "integer", required = false) Integer integer,
@ApiParam(value = "None") @Valid @RequestParam(value = "int32", required = false) Integer int32,
@ApiParam(value = "None") @Valid @RequestParam(value = "int64", required = false) Long int64,
@ApiParam(value = "None") @Valid @RequestParam(value = "float", required = false) Float _float,
@ApiParam(value = "None") @Valid @RequestParam(value = "string", required = false) String string,
@ApiParam(value = "None", required = true) @Valid @RequestPart(value = "number", required = true) BigDecimal number,
@ApiParam(value = "None", required = true) @Valid @RequestPart(value = "double", required = true) Double _double,
@ApiParam(value = "None", required = true) @Valid @RequestPart(value = "pattern_without_delimiter", required = true) String patternWithoutDelimiter,
@ApiParam(value = "None", required = true) @Valid @RequestPart(value = "byte", required = true) byte[] _byte,
@ApiParam(value = "None") @Valid @RequestPart(value = "integer", required = false) Integer integer,
@ApiParam(value = "None") @Valid @RequestPart(value = "int32", required = false) Integer int32,
@ApiParam(value = "None") @Valid @RequestPart(value = "int64", required = false) Long int64,
@ApiParam(value = "None") @Valid @RequestPart(value = "float", required = false) Float _float,
@ApiParam(value = "None") @Valid @RequestPart(value = "string", required = false) String string,
@ApiParam(value = "None") @RequestPart(value = "binary", required = false) Flux<Part> binary,
@ApiParam(value = "None") @Valid @RequestParam(value = "date", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate date,
@ApiParam(value = "None") @Valid @RequestParam(value = "dateTime", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) OffsetDateTime dateTime,
@ApiParam(value = "None") @Valid @RequestParam(value = "password", required = false) String password,
@ApiParam(value = "None") @Valid @RequestParam(value = "callback", required = false) String paramCallback,
@ApiParam(value = "None") @Valid @RequestPart(value = "date", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate date,
@ApiParam(value = "None") @Valid @RequestPart(value = "dateTime", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) OffsetDateTime dateTime,
@ApiParam(value = "None") @Valid @RequestPart(value = "password", required = false) String password,
@ApiParam(value = "None") @Valid @RequestPart(value = "callback", required = false) String paramCallback,
@ApiIgnore final ServerWebExchange exchange
) {
return getDelegate().testEndpointParameters(number, _double, patternWithoutDelimiter, _byte, integer, int32, int64, _float, string, binary, date, dateTime, password, paramCallback, exchange);
@ -425,7 +425,7 @@ public interface FakeApi {
@ApiParam(value = "Query parameter enum test (double)", allowableValues = "1, -2") @Valid @RequestParam(value = "enum_query_integer", required = false) Integer enumQueryInteger,
@ApiParam(value = "Query parameter enum test (double)", allowableValues = "1.1, -1.2") @Valid @RequestParam(value = "enum_query_double", required = false) Double enumQueryDouble,
@ApiParam(value = "Form parameter enum test (string array)", allowableValues = ">, $", defaultValue = "$") @Valid @RequestPart(value = "enum_form_string_array", required = false) List<String> enumFormStringArray,
@ApiParam(value = "Form parameter enum test (string)", allowableValues = "_abc, -efg, (xyz)", defaultValue = "-efg") @Valid @RequestParam(value = "enum_form_string", required = false) String enumFormString,
@ApiParam(value = "Form parameter enum test (string)", allowableValues = "_abc, -efg, (xyz)", defaultValue = "-efg") @Valid @RequestPart(value = "enum_form_string", required = false) String enumFormString,
@ApiIgnore final ServerWebExchange exchange
) {
return getDelegate().testEnumParameters(enumHeaderStringArray, enumHeaderString, enumQueryStringArray, enumQueryString, enumQueryInteger, enumQueryDouble, enumFormStringArray, enumFormString, exchange);
@ -525,8 +525,8 @@ public interface FakeApi {
)
default Mono<ResponseEntity<Void>> testJsonFormData(
@ApiParam(value = "field1", required = true) @Valid @RequestParam(value = "param", required = true) String param,
@ApiParam(value = "field2", required = true) @Valid @RequestParam(value = "param2", required = true) String param2,
@ApiParam(value = "field1", required = true) @Valid @RequestPart(value = "param", required = true) String param,
@ApiParam(value = "field2", required = true) @Valid @RequestPart(value = "param2", required = true) String param2,
@ApiIgnore final ServerWebExchange exchange
) {
return getDelegate().testJsonFormData(param, param2, exchange);
@ -662,7 +662,7 @@ public interface FakeApi {
default Mono<ResponseEntity<ModelApiResponse>> uploadFileWithRequiredFile(
@ApiParam(value = "ID of pet to update", required = true) @PathVariable("petId") Long petId,
@ApiParam(value = "file to upload", required = true) @RequestPart(value = "requiredFile", required = true) Flux<Part> requiredFile,
@ApiParam(value = "Additional data to pass to server") @Valid @RequestParam(value = "additionalMetadata", required = false) String additionalMetadata,
@ApiParam(value = "Additional data to pass to server") @Valid @RequestPart(value = "additionalMetadata", required = false) String additionalMetadata,
@ApiIgnore final ServerWebExchange exchange
) {
return getDelegate().uploadFileWithRequiredFile(petId, requiredFile, additionalMetadata, exchange);

View File

@ -305,8 +305,8 @@ public interface PetApi {
default Mono<ResponseEntity<Void>> updatePetWithForm(
@ApiParam(value = "ID of pet that needs to be updated", required = true) @PathVariable("petId") Long petId,
@ApiParam(value = "Updated name of the pet") @Valid @RequestParam(value = "name", required = false) String name,
@ApiParam(value = "Updated status of the pet") @Valid @RequestParam(value = "status", required = false) String status,
@ApiParam(value = "Updated name of the pet") @Valid @RequestPart(value = "name", required = false) String name,
@ApiParam(value = "Updated status of the pet") @Valid @RequestPart(value = "status", required = false) String status,
@ApiIgnore final ServerWebExchange exchange
) {
return getDelegate().updatePetWithForm(petId, name, status, exchange);
@ -347,7 +347,7 @@ public interface PetApi {
default Mono<ResponseEntity<ModelApiResponse>> uploadFile(
@ApiParam(value = "ID of pet to update", required = true) @PathVariable("petId") Long petId,
@ApiParam(value = "Additional data to pass to server") @Valid @RequestParam(value = "additionalMetadata", required = false) String additionalMetadata,
@ApiParam(value = "Additional data to pass to server") @Valid @RequestPart(value = "additionalMetadata", required = false) String additionalMetadata,
@ApiParam(value = "file to upload") @RequestPart(value = "file", required = false) Flux<Part> file,
@ApiIgnore final ServerWebExchange exchange
) {