Skip to content
Open
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
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{{^isFile}}{{{dataType}}}{{^required}}{{^defaultValue}}?{{/defaultValue}}{{/required}}{{/isFile}}{{#isFile}}{{#isArray}}Array<{{/isArray}}org.springframework.web.multipart.MultipartFile{{#isArray}}>{{/isArray}}{{^isArray}}{{^required}}?{{/required}}{{/isArray}}{{/isFile}}
{{^isFile}}{{{dataType}}}{{^required}}{{^defaultValue}}?{{/defaultValue}}{{/required}}{{/isFile}}{{#isFile}}{{#isArray}}Array<{{/isArray}}org.springframework.web.multipart.MultipartFile{{#isArray}}>{{/isArray}}{{#isNullable}}?{{/isNullable}}{{/isFile}}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about optional schema in which isNullable is set to false? should it also generate the ? at the end to make it nullable in kotlin?

Copy link
Author

@jreynard-code jreynard-code Oct 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean exactly?

Something equivalent to Optional<T> in Java?

Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,44 @@ public void delegateReactiveWithTags() throws Exception {
"ApiUtil");
}


@Test
public void testNullableMultipartFile() throws IOException {
File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
output.deleteOnExit();
String outputPath = output.getAbsolutePath().replace('\\', '/');

OpenAPI openAPI = new OpenAPIParser()
.readLocation("src/test/resources/3_0/kotlin/feat-multipartfile_nullable.yaml", null, new ParseOptions()).getOpenAPI();

KotlinSpringServerCodegen codegen = new KotlinSpringServerCodegen();
codegen.setOutputDir(output.getAbsolutePath());
codegen.additionalProperties().put(CXFServerFeatures.LOAD_TEST_DATA_FROM_FILE, "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/kotlin/org/openapitools/api/NullableMultipartfileApiController.kt"),
"file: org.springframework.web.multipart.MultipartFile?)");
assertFileContains(Paths.get(outputPath + "/src/main/kotlin/org/openapitools/api/NullableMultipartfileArrayApiController.kt"),
"files: Array<org.springframework.web.multipart.MultipartFile>?)");
assertFileContains(Paths.get(outputPath + "/src/main/kotlin/org/openapitools/api/NonNullableMultipartfileApiController.kt"),
"file: org.springframework.web.multipart.MultipartFile)");
assertFileContains(Paths.get(outputPath + "/src/main/kotlin/org/openapitools/api/NonNullableMultipartfileArrayApiController.kt"),
"files: Array<org.springframework.web.multipart.MultipartFile>)");
}

@Test
public void arrayItemsCanBeNullable() throws IOException {
File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
openapi: 3.0.0
servers:
- url: 'https://example.org/v1'
info:
description: >-
Example created for nullable multipartfile issue
version: 1.0.0
title: OpenAPI Stuff API created to reproduce issue
license:
name: Apache-2.0
url: 'https://www.apache.org/licenses/LICENSE-2.0.html'
tags:
- name: multipartfile
description: All about the nullable multipartfile
security:
- bearerAuth: []
paths:
/nullable-multipartfile:
post:
tags:
- multipartfile
summary: simple nullable multipartfile
operationId: testNullableMultipartfile
requestBody:
content:
multipart/form-data:
schema:
type: object
properties:
file:
type: string
description: File to upload
format: binary
nullable: true
jsonPayload:
type: object
description: simple json payload
properties:
name:
type: string
required:
- jsonPayload
responses:
'200':
description: successful operation
content:
application/json:
schema:
type: string
'400':
description: Invalid status value

/nullable-multipartfile-array:
post:
tags:
- multipartfile
summary: simple nullable multipartfile
operationId: testNullableMultipartfile
requestBody:
content:
multipart/form-data:
schema:
type: object
properties:
files:
type: array
items:
type: string
description: File to upload
format: binary
nullable: true
jsonPayload:
type: object
description: simple json payload
properties:
name:
type: string
required:
- jsonPayload
responses:
'200':
description: successful operation
content:
application/json:
schema:
type: string
'400':
description: Invalid status value
/non-nullable-multipartfile:
post:
tags:
- multipartfile
summary: simple non nullable multipartfile
operationId: testNonNullableMultipartfile
requestBody:
content:
multipart/form-data:
schema:
type: object
properties:
file:
type: string
description: File to upload
format: binary
nullable: false
jsonPayload:
type: object
description: simple json payload
properties:
name:
type: string
required:
- jsonPayload
responses:
'200':
description: successful operation
content:
application/json:
schema:
type: string
'400':
description: Invalid status value

/non-nullable-multipartfile-array:
post:
tags:
- multipartfile
summary: simple non nullable multipartfile
operationId: testNonNullableMultipartfile
requestBody:
content:
multipart/form-data:
schema:
type: object
properties:
files:
type: array
items:
type: string
description: File to upload
format: binary
nullable: false
jsonPayload:
type: object
description: simple json payload
properties:
name:
type: string
required:
- jsonPayload
responses:
'200':
description: successful operation
content:
application/json:
schema:
type: string
'400':
description: Invalid status value
externalDocs:
description: Find out more about Swagger
url: 'http://swagger.io'
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
schemas:
Stuff:
type: object
properties:
id:
type: integer
format: int64
name:
type: string
tag:
type: string
required:
- name

Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ interface PetApi {
produces = ["application/json"],
consumes = ["multipart/form-data"]
)
fun uploadFile( @PathVariable("petId") petId: kotlin.Long, @Valid @RequestParam(value = "additionalMetadata", required = false) additionalMetadata: kotlin.String? , @Valid @RequestPart("file", required = false) file: org.springframework.web.multipart.MultipartFile?): ResponseEntity<ModelApiResponse> {
fun uploadFile( @PathVariable("petId") petId: kotlin.Long, @Valid @RequestParam(value = "additionalMetadata", required = false) additionalMetadata: kotlin.String? , @Valid @RequestPart("file", required = false) file: org.springframework.web.multipart.MultipartFile): ResponseEntity<ModelApiResponse> {
return ResponseEntity(HttpStatus.NOT_IMPLEMENTED)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ class PetApiController() {
produces = ["application/json"],
consumes = ["multipart/form-data"]
)
fun uploadFile(@Parameter(description = "ID of pet to update", required = true) @PathVariable("petId") petId: kotlin.Long,@Parameter(description = "Additional data to pass to server") @Valid @RequestParam(value = "additionalMetadata", required = false) additionalMetadata: kotlin.String? ,@Parameter(description = "file to upload") @Valid @RequestPart("file", required = false) file: org.springframework.web.multipart.MultipartFile?): ResponseEntity<ModelApiResponse> {
fun uploadFile(@Parameter(description = "ID of pet to update", required = true) @PathVariable("petId") petId: kotlin.Long,@Parameter(description = "Additional data to pass to server") @Valid @RequestParam(value = "additionalMetadata", required = false) additionalMetadata: kotlin.String? ,@Parameter(description = "file to upload") @Valid @RequestPart("file", required = false) file: org.springframework.web.multipart.MultipartFile): ResponseEntity<ModelApiResponse> {
return ResponseEntity(HttpStatus.NOT_IMPLEMENTED)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ class PetApiController(@Autowired(required = true) val service: PetApiService) {
produces = ["application/json"],
consumes = ["multipart/form-data"]
)
fun uploadFile( @PathVariable("petId") petId: kotlin.Long, @Valid @RequestParam(value = "additionalMetadata", required = false) additionalMetadata: kotlin.String? , @Valid @RequestPart("file", required = false) file: org.springframework.web.multipart.MultipartFile?): ResponseEntity<ModelApiResponse> {
fun uploadFile( @PathVariable("petId") petId: kotlin.Long, @Valid @RequestParam(value = "additionalMetadata", required = false) additionalMetadata: kotlin.String? , @Valid @RequestPart("file", required = false) file: org.springframework.web.multipart.MultipartFile): ResponseEntity<ModelApiResponse> {
return ResponseEntity(service.uploadFile(petId, additionalMetadata, file), HttpStatus.valueOf(200))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -99,5 +99,5 @@ interface PetApiService {
* @return successful operation (status code 200)
* @see PetApi#uploadFile
*/
fun uploadFile(petId: kotlin.Long, additionalMetadata: kotlin.String?, file: org.springframework.web.multipart.MultipartFile?): ModelApiResponse
fun uploadFile(petId: kotlin.Long, additionalMetadata: kotlin.String?, file: org.springframework.web.multipart.MultipartFile): ModelApiResponse
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class PetApiServiceImpl : PetApiService {
TODO("Implement me")
}

override fun uploadFile(petId: kotlin.Long, additionalMetadata: kotlin.String?, file: org.springframework.web.multipart.MultipartFile?): ModelApiResponse {
override fun uploadFile(petId: kotlin.Long, additionalMetadata: kotlin.String?, file: org.springframework.web.multipart.MultipartFile): ModelApiResponse {
TODO("Implement me")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ class PetApiController(@Autowired(required = true) val service: PetApiService) {
produces = ["application/json"],
consumes = ["multipart/form-data"]
)
fun uploadFile( @PathVariable("petId") petId: kotlin.Long, @Valid @RequestParam(value = "additionalMetadata", required = false) additionalMetadata: kotlin.String? , @Valid @RequestPart("file", required = false) file: org.springframework.web.multipart.MultipartFile?): ResponseEntity<ModelApiResponse> {
fun uploadFile( @PathVariable("petId") petId: kotlin.Long, @Valid @RequestParam(value = "additionalMetadata", required = false) additionalMetadata: kotlin.String? , @Valid @RequestPart("file", required = false) file: org.springframework.web.multipart.MultipartFile): ResponseEntity<ModelApiResponse> {
return ResponseEntity(service.uploadFile(petId, additionalMetadata, file), HttpStatus.valueOf(200))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -99,5 +99,5 @@ interface PetApiService {
* @return successful operation (status code 200)
* @see PetApi#uploadFile
*/
fun uploadFile(petId: kotlin.Long, additionalMetadata: kotlin.String?, file: org.springframework.web.multipart.MultipartFile?): ModelApiResponse
fun uploadFile(petId: kotlin.Long, additionalMetadata: kotlin.String?, file: org.springframework.web.multipart.MultipartFile): ModelApiResponse
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class PetApiServiceImpl : PetApiService {
TODO("Implement me")
}

override fun uploadFile(petId: kotlin.Long, additionalMetadata: kotlin.String?, file: org.springframework.web.multipart.MultipartFile?): ModelApiResponse {
override fun uploadFile(petId: kotlin.Long, additionalMetadata: kotlin.String?, file: org.springframework.web.multipart.MultipartFile): ModelApiResponse {
TODO("Implement me")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ interface PetApi {
produces = ["application/json"],
consumes = ["multipart/form-data"]
)
fun uploadFile(@Parameter(description = "ID of pet to update", required = true) @PathVariable("petId") petId: kotlin.Long,@Parameter(description = "Additional data to pass to server") @Valid @RequestParam(value = "additionalMetadata", required = false) additionalMetadata: kotlin.String? ,@Parameter(description = "file to upload") @Valid @RequestPart("file", required = false) file: org.springframework.web.multipart.MultipartFile?): ResponseEntity<ModelApiResponse> {
fun uploadFile(@Parameter(description = "ID of pet to update", required = true) @PathVariable("petId") petId: kotlin.Long,@Parameter(description = "Additional data to pass to server") @Valid @RequestParam(value = "additionalMetadata", required = false) additionalMetadata: kotlin.String? ,@Parameter(description = "file to upload") @Valid @RequestPart("file", required = false) file: org.springframework.web.multipart.MultipartFile): ResponseEntity<ModelApiResponse> {
return getDelegate().uploadFile(petId, additionalMetadata, file)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,6 @@ interface PetApiDelegate {
*/
fun uploadFile(petId: kotlin.Long,
additionalMetadata: kotlin.String?,
file: org.springframework.web.multipart.MultipartFile?): ResponseEntity<ModelApiResponse>
file: org.springframework.web.multipart.MultipartFile): ResponseEntity<ModelApiResponse>

}
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ interface PetApi {
produces = ["application/json"],
consumes = ["multipart/form-data"]
)
fun uploadFile(@Parameter(description = "ID of pet to update", required = true) @PathVariable("petId") petId: kotlin.Long,@Parameter(description = "Additional data to pass to server") @Valid @RequestParam(value = "additionalMetadata", required = false) additionalMetadata: kotlin.String? ,@Parameter(description = "file to upload") @Valid @RequestPart("file", required = false) file: org.springframework.web.multipart.MultipartFile?): ResponseEntity<ModelApiResponse> {
fun uploadFile(@Parameter(description = "ID of pet to update", required = true) @PathVariable("petId") petId: kotlin.Long,@Parameter(description = "Additional data to pass to server") @Valid @RequestParam(value = "additionalMetadata", required = false) additionalMetadata: kotlin.String? ,@Parameter(description = "file to upload") @Valid @RequestPart("file", required = false) file: org.springframework.web.multipart.MultipartFile): ResponseEntity<ModelApiResponse> {
return getDelegate().uploadFile(petId, additionalMetadata, file)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ interface PetApiDelegate {
*/
fun uploadFile(petId: kotlin.Long,
additionalMetadata: kotlin.String?,
file: org.springframework.web.multipart.MultipartFile?): ResponseEntity<ModelApiResponse> {
file: org.springframework.web.multipart.MultipartFile): ResponseEntity<ModelApiResponse> {
getRequest().ifPresent { request ->
for (mediaType in MediaType.parseMediaTypes(request.getHeader("Accept"))) {
if (mediaType.isCompatibleWith(MediaType.valueOf("application/json"))) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ class PetApiController(@Autowired(required = true) val service: PetApiService) {
produces = ["application/json"],
consumes = ["multipart/form-data"]
)
fun uploadFile(@Parameter(description = "ID of pet to update", required = true) @PathVariable("petId") petId: kotlin.Long,@Parameter(description = "Additional data to pass to server") @Valid @RequestParam(value = "additionalMetadata", required = false) additionalMetadata: kotlin.String? ,@Parameter(description = "file to upload") @Valid @RequestPart("file", required = false) file: org.springframework.web.multipart.MultipartFile?): ResponseEntity<ModelApiResponse> {
fun uploadFile(@Parameter(description = "ID of pet to update", required = true) @PathVariable("petId") petId: kotlin.Long,@Parameter(description = "Additional data to pass to server") @Valid @RequestParam(value = "additionalMetadata", required = false) additionalMetadata: kotlin.String? ,@Parameter(description = "file to upload") @Valid @RequestPart("file", required = false) file: org.springframework.web.multipart.MultipartFile): ResponseEntity<ModelApiResponse> {
return ResponseEntity(service.uploadFile(petId, additionalMetadata, file), HttpStatus.valueOf(200))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -90,5 +90,5 @@ interface PetApiService {
* @return successful operation (status code 200)
* @see PetApi#uploadFile
*/
fun uploadFile(petId: kotlin.Long, additionalMetadata: kotlin.String?, file: org.springframework.web.multipart.MultipartFile?): ModelApiResponse
fun uploadFile(petId: kotlin.Long, additionalMetadata: kotlin.String?, file: org.springframework.web.multipart.MultipartFile): ModelApiResponse
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class PetApiServiceImpl : PetApiService {
TODO("Implement me")
}

override fun uploadFile(petId: kotlin.Long, additionalMetadata: kotlin.String?, file: org.springframework.web.multipart.MultipartFile?): ModelApiResponse {
override fun uploadFile(petId: kotlin.Long, additionalMetadata: kotlin.String?, file: org.springframework.web.multipart.MultipartFile): ModelApiResponse {
TODO("Implement me")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ class PetApiController(@Autowired(required = true) val service: PetApiService) {
produces = ["application/json"],
consumes = ["multipart/form-data"]
)
suspend fun uploadFile(@Parameter(description = "ID of pet to update", required = true) @PathVariable("petId") petId: kotlin.Long,@Parameter(description = "Additional data to pass to server") @Valid @RequestParam(value = "additionalMetadata", required = false) additionalMetadata: kotlin.String? ,@Parameter(description = "file to upload") @Valid @RequestPart("file", required = false) file: org.springframework.web.multipart.MultipartFile?): ResponseEntity<ModelApiResponse> {
suspend fun uploadFile(@Parameter(description = "ID of pet to update", required = true) @PathVariable("petId") petId: kotlin.Long,@Parameter(description = "Additional data to pass to server") @Valid @RequestParam(value = "additionalMetadata", required = false) additionalMetadata: kotlin.String? ,@Parameter(description = "file to upload") @Valid @RequestPart("file", required = false) file: org.springframework.web.multipart.MultipartFile): ResponseEntity<ModelApiResponse> {
return ResponseEntity(service.uploadFile(petId, additionalMetadata, file), HttpStatus.valueOf(200))
}
}
Loading