Skip to content
Closed
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
Expand Up @@ -33,7 +33,7 @@ interface {{classname}}Delegate {
/**
* @see {{classname}}#{{operationId}}
*/
{{#reactive}}{{^isArray}}suspend {{/isArray}}{{#isArray}}{{^useFlowForArrayReturnType}}suspend {{/useFlowForArrayReturnType}}{{/isArray}}{{/reactive}}fun {{operationId}}({{#allParams}}{{{paramName}}}: {{^reactive}}{{>optionalDataType}}{{/reactive}}{{#reactive}}{{^isArray}}{{>optionalDataType}}{{/isArray}}{{#isArray}}{{#isBodyParam}}Flow<{{{baseType}}}>{{/isBodyParam}}{{^isBodyParam}}{{>optionalDataType}}{{/isBodyParam}}{{/isArray}}{{/reactive}}{{^-last}},
{{#reactive}}{{^isArray}}suspend {{/isArray}}{{#isArray}}{{^useFlowForArrayReturnType}}suspend {{/useFlowForArrayReturnType}}{{/isArray}}{{/reactive}}fun {{operationId}}({{#allParams}}{{{paramName}}}: {{#isFile}}{{>fileParamType}}{{/isFile}}{{^isFile}}{{#reactive}}{{^isArray}}{{>optionalDataType}}{{/isArray}}{{#isArray}}{{#isBodyParam}}Flow<{{{baseType}}}>{{/isBodyParam}}{{^isBodyParam}}{{>optionalDataType}}{{/isBodyParam}}{{/isArray}}{{/reactive}}{{^reactive}}{{>optionalDataType}}{{/reactive}}{{/isFile}}{{^-last}},
{{/-last}}{{/allParams}}): {{#responseWrapper}}{{.}}<{{/responseWrapper}}ResponseEntity<{{>returnTypes}}>{{#responseWrapper}}>{{/responseWrapper}}{{^skipDefaultDelegateInterface}} {
{{>methodBody}}{{! prevent indent}}
}{{/skipDefaultDelegateInterface}}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{{#isFile}}{{#reactive}}{{#isArray}}Flux<{{/isArray}}org.springframework.http.codec.multipart.Part{{#isArray}}>{{/isArray}}{{^required}}?{{/required}}{{/reactive}}{{^reactive}}{{#isArray}}Array<{{/isArray}}org.springframework.web.multipart.MultipartFile{{#isArray}}>{{/isArray}}{{^required}}?{{/required}}{{/reactive}}{{/isFile}}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{{#isFormParam}}{{^isFile}}{{#swagger2AnnotationLibrary}}@Parameter(description = "{{{description}}}"{{#required}}, required = true{{/required}}{{#allowableValues}}{{#defaultValue}}, schema = Schema(allowableValues = [{{#values}}"{{{.}}}"{{^-last}}, {{/-last}}{{/values}}]{{^isContainer}}, defaultValue = {{^isString}}"{{/isString}}{{#isString}}{{#isEnum}}"{{/isEnum}}{{/isString}}{{{defaultValue}}}{{^isString}}"{{/isString}}{{#isString}}{{#isEnum}}"{{/isEnum}}{{/isString}}{{/isContainer}}){{/defaultValue}}{{/allowableValues}}{{#allowableValues}}{{^defaultValue}}, schema = Schema(allowableValues = [{{#values}}"{{{.}}}"{{^-last}}, {{/-last}}{{/values}}]){{/defaultValue}}{{/allowableValues}}{{^allowableValues}}{{#defaultValue}}{{^isContainer}}, schema = Schema(defaultValue = {{^isString}}"{{/isString}}{{#isString}}{{#isEnum}}"{{/isEnum}}{{/isString}}{{{defaultValue}}}{{^isString}}"{{/isString}}{{#isString}}{{#isEnum}}"{{/isEnum}}{{/isString}}){{/isContainer}}{{/defaultValue}}{{/allowableValues}}){{/swagger2AnnotationLibrary}}{{#swagger1AnnotationLibrary}}@ApiParam(value = "{{{description}}}"{{#required}}, required = true{{/required}}{{#allowableValues}}, allowableValues = "{{#values}}{{{.}}}{{^-last}}, {{/-last}}{{/values}}"{{/allowableValues}}{{#defaultValue}}, defaultValue = "{{{.}}}"{{/defaultValue}}){{/swagger1AnnotationLibrary}} {{#useBeanValidation}}@Valid{{/useBeanValidation}} {{#isModel}}@RequestPart{{/isModel}}{{^isModel}}@RequestParam{{/isModel}}(value = "{{baseName}}"{{#required}}, required = true{{/required}}{{^required}}, required = false{{/required}}) {{{paramName}}}: {{>optionalDataType}} {{/isFile}}{{#isFile}}{{#swagger2AnnotationLibrary}}@Parameter(description = "{{{description}}}"){{/swagger2AnnotationLibrary}}{{#swagger1AnnotationLibrary}}@ApiParam(value = "file detail"){{/swagger1AnnotationLibrary}} {{#useBeanValidation}}@Valid{{/useBeanValidation}} @RequestPart("{{baseName}}"{{#required}}, required = true{{/required}}{{^required}}, required = false{{/required}}) {{{paramName}}}: {{>optionalDataType}}{{/isFile}}{{/isFormParam}}
{{#isFormParam}}{{^isFile}}{{#swagger2AnnotationLibrary}}@Parameter(description = "{{{description}}}"{{#required}}, required = true{{/required}}{{#allowableValues}}{{#defaultValue}}, schema = Schema(allowableValues = [{{#values}}"{{{.}}}"{{^-last}}, {{/-last}}{{/values}}]{{^isContainer}}, defaultValue = {{^isString}}"{{/isString}}{{#isString}}{{#isEnum}}"{{/isEnum}}{{/isString}}{{{defaultValue}}}{{^isString}}"{{/isString}}{{#isString}}{{#isEnum}}"{{/isEnum}}{{/isString}}{{/isContainer}}){{/defaultValue}}{{/allowableValues}}{{#allowableValues}}{{^defaultValue}}, schema = Schema(allowableValues = [{{#values}}"{{{.}}}"{{^-last}}, {{/-last}}{{/values}}]){{/defaultValue}}{{/allowableValues}}{{^allowableValues}}{{#defaultValue}}{{^isContainer}}, schema = Schema(defaultValue = {{^isString}}"{{/isString}}{{#isString}}{{#isEnum}}"{{/isEnum}}{{/isString}}{{{defaultValue}}}{{^isString}}"{{/isString}}{{#isString}}{{#isEnum}}"{{/isEnum}}{{/isString}}){{/isContainer}}{{/defaultValue}}{{/allowableValues}}){{/swagger2AnnotationLibrary}}{{#swagger1AnnotationLibrary}}@ApiParam(value = "{{{description}}}"{{#required}}, required = true{{/required}}{{#allowableValues}}, allowableValues = "{{#values}}{{{.}}}{{^-last}}, {{/-last}}{{/values}}"{{/allowableValues}}{{#defaultValue}}, defaultValue = "{{{.}}}"{{/defaultValue}}){{/swagger1AnnotationLibrary}} {{#useBeanValidation}}@Valid{{/useBeanValidation}} {{#isModel}}@RequestPart{{/isModel}}{{^isModel}}@RequestParam{{/isModel}}(value = "{{baseName}}"{{#required}}, required = true{{/required}}{{^required}}, required = false{{/required}}) {{{paramName}}}: {{>optionalDataType}} {{/isFile}}{{#isFile}}{{#swagger2AnnotationLibrary}}@Parameter(description = "{{{description}}}"){{/swagger2AnnotationLibrary}}{{#swagger1AnnotationLibrary}}@ApiParam(value = "file detail"){{/swagger1AnnotationLibrary}} {{#useBeanValidation}}@Valid{{/useBeanValidation}} @RequestPart("{{baseName}}"{{#required}}, required = true{{/required}}{{^required}}, required = false{{/required}}) {{{paramName}}}: {{>fileParamType}}{{/isFile}}{{/isFormParam}}
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}}
{{{dataType}}}{{^required}}{{^defaultValue}}?{{/defaultValue}}{{/required}}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ interface {{classname}}Service {
{{/externalDocs}}
* @see {{classname}}#{{operationId}}
*/
{{#reactive}}{{^isArray}}suspend {{/isArray}}{{#isArray}}{{^useFlowForArrayReturnType}}suspend {{/useFlowForArrayReturnType}}{{/isArray}}{{/reactive}}fun {{operationId}}({{#allParams}}{{{paramName}}}: {{^isBodyParam}}{{>optionalDataType}}{{/isBodyParam}}{{#isBodyParam}}{{^reactive}}{{>optionalDataType}}{{/reactive}}{{#reactive}}{{^isArray}}{{>optionalDataType}}{{/isArray}}{{#isArray}}Flow<{{{baseType}}}>{{/isArray}}{{/reactive}}{{/isBodyParam}}{{^-last}}, {{/-last}}{{/allParams}}): {{>returnTypes}}
{{#reactive}}{{^isArray}}suspend {{/isArray}}{{#isArray}}{{^useFlowForArrayReturnType}}suspend {{/useFlowForArrayReturnType}}{{/isArray}}{{/reactive}}fun {{operationId}}({{#allParams}}{{{paramName}}}: {{^isBodyParam}}{{#isFile}}{{>fileParamType}}{{/isFile}}{{^isFile}}{{>optionalDataType}}{{/isFile}}{{/isBodyParam}}{{#isBodyParam}}{{^reactive}}{{>optionalDataType}}{{/reactive}}{{#reactive}}{{^isArray}}{{>optionalDataType}}{{/isArray}}{{#isArray}}Flow<{{{baseType}}}>{{/isArray}}{{/reactive}}{{/isBodyParam}}{{^-last}}, {{/-last}}{{/allParams}}): {{>returnTypes}}
{{/operation}}
}
{{/operations}}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import org.springframework.stereotype.Service
class {{classname}}ServiceImpl : {{classname}}Service {
{{#operation}}

override {{#reactive}}{{^isArray}}suspend {{/isArray}}{{#isArray}}{{^useFlowForArrayReturnType}}suspend {{/useFlowForArrayReturnType}}{{/isArray}}{{/reactive}}fun {{operationId}}({{#allParams}}{{{paramName}}}: {{^isBodyParam}}{{>optionalDataType}}{{/isBodyParam}}{{#isBodyParam}}{{^reactive}}{{>optionalDataType}}{{/reactive}}{{#reactive}}{{^isArray}}{{>optionalDataType}}{{/isArray}}{{#isArray}}Flow<{{{baseType}}}>{{/isArray}}{{/reactive}}{{/isBodyParam}}{{^-last}}, {{/-last}}{{/allParams}}): {{>returnTypes}} {
override {{#reactive}}{{^isArray}}suspend {{/isArray}}{{#isArray}}{{^useFlowForArrayReturnType}}suspend {{/useFlowForArrayReturnType}}{{/isArray}}{{/reactive}}fun {{operationId}}({{#allParams}}{{{paramName}}}: {{^isBodyParam}}{{#isFile}}{{>fileParamType}}{{/isFile}}{{^isFile}}{{>optionalDataType}}{{/isFile}}{{/isBodyParam}}{{#isBodyParam}}{{^reactive}}{{>optionalDataType}}{{/reactive}}{{#reactive}}{{^isArray}}{{>optionalDataType}}{{/isArray}}{{#isArray}}Flow<{{{baseType}}}>{{/isArray}}{{/reactive}}{{/isBodyParam}}{{^-last}}, {{/-last}}{{/allParams}}): {{>returnTypes}} {
TODO("Implement me")
}
{{/operation}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.servers.Server;
import io.swagger.v3.parser.core.models.ParseOptions;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.stream.Stream;
import org.apache.commons.io.FileUtils;
import org.assertj.core.api.Assertions;
import org.jetbrains.annotations.NotNull;
Expand Down Expand Up @@ -38,7 +41,6 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.openapitools.codegen.TestUtils.assertFileContains;
import static org.openapitools.codegen.TestUtils.assertFileNotContains;
import static org.openapitools.codegen.languages.SpringCodegen.SPRING_BOOT;
import static org.openapitools.codegen.languages.features.DocumentationProviderFeatures.ANNOTATION_LIBRARY;
import static org.openapitools.codegen.languages.features.DocumentationProviderFeatures.DOCUMENTATION_PROVIDER;

Expand Down Expand Up @@ -789,42 +791,138 @@ public void contractWithResolvedInnerEnumContainsEnumConverter() throws IOExcept
}

@Test
public void givenMultipartFormArray_whenGenerateDelegateAndService_thenParameterIsCreatedAsListOfMultipartFile() throws IOException {
File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
output.deleteOnExit();
String outputPath = output.getAbsolutePath().replace('\\', '/');
public void givenNonRequiredMultipartFileArray_whenGenerateDelegateAndService_thenParameterIsCreatedAsNullableListOfMultipartFile() throws IOException {
Map<String, Object> additionalProperties = new HashMap<>();
additionalProperties.put(KotlinSpringServerCodegen.DELEGATE_PATTERN, true);
additionalProperties.put(KotlinSpringServerCodegen.SERVICE_IMPLEMENTATION, true);

Map<String, String> generatorPropertyDefaults = new HashMap<>();
generatorPropertyDefaults.put(CodegenConstants.MODELS, "false");
generatorPropertyDefaults.put(CodegenConstants.MODEL_TESTS, "false");
generatorPropertyDefaults.put(CodegenConstants.MODEL_DOCS, "false");
generatorPropertyDefaults.put(CodegenConstants.APIS, "true");
generatorPropertyDefaults.put(CodegenConstants.SUPPORTING_FILES, "false");

final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/kotlin/petstore-with-tags.yaml");
final KotlinSpringServerCodegen codegen = new KotlinSpringServerCodegen();
codegen.setOpenAPI(openAPI);
codegen.setOutputDir(output.getAbsolutePath());
codegen.setDelegatePattern(true);
codegen.setServiceInterface(true);
Map<String, File> files = generateFromContract(
"src/test/resources/3_0/kotlin/petstore-with-tags.yaml",
additionalProperties,
generatorPropertyDefaults
);

ClientOptInput input = new ClientOptInput();
input.openAPI(openAPI);
input.config(codegen);
validateMultipartFiles(
files,
"Pet",
"additionalMetadata: kotlin.String?",
"images: Array<org.springframework.web.multipart.MultipartFile>?)"
);
}

DefaultGenerator generator = new DefaultGenerator();
@Test
public void givenMultipartBinaryArray_whenGenerateDelegateAndService_correctMultipartFileIsCreated() throws IOException {
Map<String, Object> additionalProperties = new HashMap<>();
additionalProperties.put(KotlinSpringServerCodegen.DELEGATE_PATTERN, true);
additionalProperties.put(KotlinSpringServerCodegen.SERVICE_IMPLEMENTATION, true);

Map<String, String> generatorPropertyDefaults = new HashMap<>();
generatorPropertyDefaults.put(CodegenConstants.MODELS, "false");
generatorPropertyDefaults.put(CodegenConstants.MODEL_TESTS, "false");
generatorPropertyDefaults.put(CodegenConstants.MODEL_DOCS, "false");
generatorPropertyDefaults.put(CodegenConstants.APIS, "true");
generatorPropertyDefaults.put(CodegenConstants.SUPPORTING_FILES, "false");

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");
Map<String, File> files = generateFromContract(
"src/test/resources/3_0/form-multipart-binary-array.yaml",
additionalProperties,
generatorPropertyDefaults
);

generator.opts(input).generate();
validateMultipartFiles(
files,
"MultipartArray",
"files: Array<org.springframework.web.multipart.MultipartFile>?)"
);

Path delegateFile = Paths.get(outputPath + "/src/main/kotlin/org/openapitools/api/PetApiDelegate.kt");
assertFileContains(delegateFile, "additionalMetadata: kotlin.String?");
assertFileContains(delegateFile, "images: Array<org.springframework.web.multipart.MultipartFile>");
validateMultipartFiles(
files,
"MultipartMixed",
"file: org.springframework.web.multipart.MultipartFile,",
"marker: MultipartMixedRequestMarker?",
"statusArray: kotlin.collections.List<MultipartMixedStatus>?"
);

Path controllerFile = Paths.get(outputPath + "/src/main/kotlin/org/openapitools/api/PetApi.kt");
assertFileContains(controllerFile, "images: Array<org.springframework.web.multipart.MultipartFile>");
validateMultipartFiles(
files,
"MultipartSingle",
"file: org.springframework.web.multipart.MultipartFile?"
);
}

@Test
public void givenMultipartBinaryArray_whenGenerateReactiveDelegateAndService_correctPartIsCreated() throws IOException {
Map<String, Object> additionalProperties = new HashMap<>();
additionalProperties.put(KotlinSpringServerCodegen.DELEGATE_PATTERN, true);
additionalProperties.put(KotlinSpringServerCodegen.SERVICE_IMPLEMENTATION, true);
additionalProperties.put(KotlinSpringServerCodegen.REACTIVE, true);

Map<String, String> generatorPropertyDefaults = new HashMap<>();
generatorPropertyDefaults.put(CodegenConstants.MODELS, "false");
generatorPropertyDefaults.put(CodegenConstants.MODEL_TESTS, "false");
generatorPropertyDefaults.put(CodegenConstants.MODEL_DOCS, "false");
generatorPropertyDefaults.put(CodegenConstants.APIS, "true");
generatorPropertyDefaults.put(CodegenConstants.SUPPORTING_FILES, "false");

Path serviceFile = Paths.get(outputPath + "/src/main/kotlin/org/openapitools/api/PetApiService.kt");
assertFileContains(serviceFile, "images: Array<org.springframework.web.multipart.MultipartFile>");
Map<String, File> files = generateFromContract(
"src/test/resources/3_0/form-multipart-binary-array.yaml",
additionalProperties,
generatorPropertyDefaults
);

validateMultipartFiles(
files,
"MultipartArray",
"files: Flux<org.springframework.http.codec.multipart.Part>?)"
);

validateMultipartFiles(
files,
"MultipartMixed",
"file: org.springframework.http.codec.multipart.Part,",
"marker: MultipartMixedRequestMarker?",
"statusArray: kotlin.collections.List<MultipartMixedStatus>?"
);

validateMultipartFiles(
files,
"MultipartSingle",
"file: org.springframework.http.codec.multipart.Part?"
);
}

/**
* Utility function to help validate that all delegate, service & api code generated for
* schemas with multipart-form-data have the same lines.
*/
private void validateMultipartFiles(
Map<String, File> files,
String filePrefix,
String... lines
) {
Stream.of(
filePrefix + "ApiDelegate.kt",
filePrefix + "ApiService.kt",
filePrefix + "ApiServiceImpl.kt",
filePrefix + "Api.kt"
)
.map(files::get)
.map(Objects::requireNonNull)
.map(File::toPath)
.forEach(path -> {
try {
TestUtils.assertFileContains(path, lines);
} catch (AssertionError e) {
throw new AssertionError(path.toString() + " does not contain a line", e);
}
});
}

@Test
Expand Down Expand Up @@ -887,7 +985,7 @@ public void givenMultipartForm_whenGenerateReactiveServer_thenParameterAreCreate
assertFileContains(outputFilepath,
"@Parameter(description = \"Additional data to pass to server\") @Valid @RequestParam(value = \"additionalMetadata\", required = false) additionalMetadata: kotlin.String?");
assertFileContains(outputFilepath,
"@Parameter(description = \"image to upload\") @Valid @RequestPart(\"image\", required = false) image: org.springframework.web.multipart.MultipartFile");
"@Parameter(description = \"image to upload\") @Valid @RequestPart(\"image\", required = false) image: org.springframework.web.multipart.MultipartFile?)");

}

Expand Down Expand Up @@ -1269,7 +1367,7 @@ private Map<String, File> generateFromContract(
.setAdditionalProperties(additionalProperties)
.setValidateSpec(false)
.setInputSpec(url)
.setLibrary(SPRING_BOOT)
.setLibrary(KotlinSpringServerCodegen.SPRING_BOOT)
.setOutputDir(output.getAbsolutePath());

consumer.accept(configurator);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -267,16 +267,15 @@ paths:
content:
multipart/form-data:
schema:
type: object
properties:
additionalMetadata:
type: string
description: Additional data to pass to server
required: false
type: string
description: Additional data to pass to server
image:
type: string
description: image to upload
format: binary
required: true
type: string
description: image to upload
format: binary
responses:
200:
description: successful operation
Expand Down Expand Up @@ -306,18 +305,17 @@ paths:
content:
multipart/form-data:
schema:
type: object
properties:
additionalMetadata:
type: string
description: Additional data to pass to server
required: false
images:
type: array
items:
type: string
description: image to upload
format: binary
required: true
responses:
200:
description: successful operation
Expand Down
Loading
Loading