From ced31f49b3c358a0a31a42a942eb74a1b0b647e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Baran?= <86005806+lukbaran@users.noreply.github.com> Date: Wed, 8 Nov 2023 07:33:39 +0100 Subject: [PATCH] add kotlin-spring beanValidation for RequestBody parameters fix #16738 (#16859) * fix missing bean validation annotations on RequestBody * add new line to files * remove new line from files * reformat file --- .../beanValidationBodyParams.mustache | 1 + ...h.mustache => beanValidationCore.mustache} | 0 .../beanValidationPathParams.mustache | 2 +- .../beanValidationQueryParams.mustache | 2 +- .../kotlin-spring/bodyParams.mustache | 2 +- .../kotlin-spring/headerParams.mustache | 2 +- .../spring/KotlinSpringServerCodegenTest.java | 213 ++++++++++-------- 7 files changed, 127 insertions(+), 95 deletions(-) create mode 100644 modules/openapi-generator/src/main/resources/kotlin-spring/beanValidationBodyParams.mustache rename modules/openapi-generator/src/main/resources/kotlin-spring/{beanValidationPath.mustache => beanValidationCore.mustache} (100%) diff --git a/modules/openapi-generator/src/main/resources/kotlin-spring/beanValidationBodyParams.mustache b/modules/openapi-generator/src/main/resources/kotlin-spring/beanValidationBodyParams.mustache new file mode 100644 index 00000000000..71bc546835e --- /dev/null +++ b/modules/openapi-generator/src/main/resources/kotlin-spring/beanValidationBodyParams.mustache @@ -0,0 +1 @@ +{{! RequestBody required param is responsible for optional and nullability }}{{>beanValidationCore}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/kotlin-spring/beanValidationPath.mustache b/modules/openapi-generator/src/main/resources/kotlin-spring/beanValidationCore.mustache similarity index 100% rename from modules/openapi-generator/src/main/resources/kotlin-spring/beanValidationPath.mustache rename to modules/openapi-generator/src/main/resources/kotlin-spring/beanValidationCore.mustache diff --git a/modules/openapi-generator/src/main/resources/kotlin-spring/beanValidationPathParams.mustache b/modules/openapi-generator/src/main/resources/kotlin-spring/beanValidationPathParams.mustache index 3c57e76be1a..051bd53c0a5 100644 --- a/modules/openapi-generator/src/main/resources/kotlin-spring/beanValidationPathParams.mustache +++ b/modules/openapi-generator/src/main/resources/kotlin-spring/beanValidationPathParams.mustache @@ -1 +1 @@ -{{! PathParam is always required, no @NotNull necessary }}{{>beanValidationPath}} \ No newline at end of file +{{! PathParam is always required, no @NotNull necessary }}{{>beanValidationCore}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/kotlin-spring/beanValidationQueryParams.mustache b/modules/openapi-generator/src/main/resources/kotlin-spring/beanValidationQueryParams.mustache index cc53bc96232..9cca8cb8874 100644 --- a/modules/openapi-generator/src/main/resources/kotlin-spring/beanValidationQueryParams.mustache +++ b/modules/openapi-generator/src/main/resources/kotlin-spring/beanValidationQueryParams.mustache @@ -1 +1 @@ -{{#required}}@NotNull {{/required}}{{>beanValidationPath}} \ No newline at end of file +{{#required}}@NotNull {{/required}}{{>beanValidationCore}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/kotlin-spring/bodyParams.mustache b/modules/openapi-generator/src/main/resources/kotlin-spring/bodyParams.mustache index 911bea74a1b..4ca6b20ca93 100644 --- a/modules/openapi-generator/src/main/resources/kotlin-spring/bodyParams.mustache +++ b/modules/openapi-generator/src/main/resources/kotlin-spring/bodyParams.mustache @@ -1 +1 @@ -{{#isBodyParam}}{{#swagger2AnnotationLibrary}}@Parameter(description = "{{{description}}}"{{#required}}, required = true{{/required}}{{^isContainer}}{{#allowableValues}}{{#defaultValue}}, schema = Schema(allowableValues = ["{{{allowableValues}}}"], defaultValue = {{^isString}}"{{/isString}}{{#isString}}{{#isEnum}}"{{/isEnum}}{{/isString}}{{{defaultValue}}}{{^isString}}"{{/isString}}{{#isString}}{{#isEnum}}"{{/isEnum}}{{/isString}}){{/defaultValue}}{{/allowableValues}}{{^allowableValues}}{{#defaultValue}}, schema = Schema(defaultValue = {{^isString}}"{{/isString}}{{#isString}}{{#isEnum}}"{{/isEnum}}{{/isString}}{{{defaultValue}}}{{^isString}}"{{/isString}}{{#isString}}{{#isEnum}}"{{/isEnum}}{{/isString}}){{/defaultValue}}{{/allowableValues}}{{#allowableValues}}{{^defaultValue}}, schema = Schema(allowableValues = ["{{{allowableValues}}}"]){{/defaultValue}}{{/allowableValues}}{{/isContainer}}){{/swagger2AnnotationLibrary}}{{#swagger1AnnotationLibrary}}@ApiParam(value = "{{{description}}}"{{#required}}, required = true{{/required}}{{^isContainer}}{{#allowableValues}}, allowableValues = "{{{.}}}"{{/allowableValues}}{{/isContainer}}{{#defaultValue}}, defaultValue = "{{{.}}}"{{/defaultValue}}){{/swagger1AnnotationLibrary}} {{#useBeanValidation}}@Valid{{/useBeanValidation}} @RequestBody{{^required}}(required = false){{/required}} {{{paramName}}}: {{^reactive}}{{>optionalDataType}}{{/reactive}}{{#reactive}}{{^isArray}}{{>optionalDataType}}{{/isArray}}{{#isArray}}Flow<{{{baseType}}}>{{/isArray}}{{/reactive}}{{/isBodyParam}} \ No newline at end of file +{{#isBodyParam}}{{#swagger2AnnotationLibrary}}@Parameter(description = "{{{description}}}"{{#required}}, required = true{{/required}}{{^isContainer}}{{#allowableValues}}{{#defaultValue}}, schema = Schema(allowableValues = ["{{{allowableValues}}}"], defaultValue = {{^isString}}"{{/isString}}{{#isString}}{{#isEnum}}"{{/isEnum}}{{/isString}}{{{defaultValue}}}{{^isString}}"{{/isString}}{{#isString}}{{#isEnum}}"{{/isEnum}}{{/isString}}){{/defaultValue}}{{/allowableValues}}{{^allowableValues}}{{#defaultValue}}, schema = Schema(defaultValue = {{^isString}}"{{/isString}}{{#isString}}{{#isEnum}}"{{/isEnum}}{{/isString}}{{{defaultValue}}}{{^isString}}"{{/isString}}{{#isString}}{{#isEnum}}"{{/isEnum}}{{/isString}}){{/defaultValue}}{{/allowableValues}}{{#allowableValues}}{{^defaultValue}}, schema = Schema(allowableValues = ["{{{allowableValues}}}"]){{/defaultValue}}{{/allowableValues}}{{/isContainer}}){{/swagger2AnnotationLibrary}}{{#swagger1AnnotationLibrary}}@ApiParam(value = "{{{description}}}"{{#required}}, required = true{{/required}}{{^isContainer}}{{#allowableValues}}, allowableValues = "{{{.}}}"{{/allowableValues}}{{/isContainer}}{{#defaultValue}}, defaultValue = "{{{.}}}"{{/defaultValue}}){{/swagger1AnnotationLibrary}}{{#useBeanValidation}} @Valid{{>beanValidationBodyParams}}{{/useBeanValidation}} @RequestBody{{^required}}(required = false){{/required}} {{{paramName}}}: {{^reactive}}{{>optionalDataType}}{{/reactive}}{{#reactive}}{{^isArray}}{{>optionalDataType}}{{/isArray}}{{#isArray}}Flow<{{{baseType}}}>{{/isArray}}{{/reactive}}{{/isBodyParam}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/kotlin-spring/headerParams.mustache b/modules/openapi-generator/src/main/resources/kotlin-spring/headerParams.mustache index 9bc3f6002e9..53c8e620d2a 100644 --- a/modules/openapi-generator/src/main/resources/kotlin-spring/headerParams.mustache +++ b/modules/openapi-generator/src/main/resources/kotlin-spring/headerParams.mustache @@ -1 +1 @@ -{{#isHeaderParam}}{{#useBeanValidation}}{{>beanValidationPath}}{{/useBeanValidation}}{{#swagger2AnnotationLibrary}}@Parameter(description = "{{{description}}}", `in` = ParameterIn.HEADER{{#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}} @RequestHeader(value = "{{baseName}}", required = {{#required}}true{{/required}}{{^required}}false{{/required}}) {{paramName}}: {{>optionalDataType}}{{/isHeaderParam}} \ No newline at end of file +{{#isHeaderParam}}{{#useBeanValidation}}{{>beanValidationCore}}{{/useBeanValidation}}{{#swagger2AnnotationLibrary}}@Parameter(description = "{{{description}}}", `in` = ParameterIn.HEADER{{#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}} @RequestHeader(value = "{{baseName}}", required = {{#required}}true{{/required}}{{^required}}false{{/required}}) {{paramName}}: {{>optionalDataType}}{{/isHeaderParam}} \ No newline at end of file diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/spring/KotlinSpringServerCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/spring/KotlinSpringServerCodegenTest.java index 9f8a027907a..35a92c66c5f 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/spring/KotlinSpringServerCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/spring/KotlinSpringServerCodegenTest.java @@ -25,6 +25,9 @@ import java.nio.file.Files; import java.nio.file.Paths; import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; import static org.openapitools.codegen.TestUtils.assertFileContains; import static org.openapitools.codegen.TestUtils.assertFileNotContains; @@ -87,14 +90,14 @@ public class KotlinSpringServerCodegenTest { codegen.setLibrary("spring-cloud"); new DefaultGenerator().opts(new ClientOptInput() - .openAPI(TestUtils.parseSpec("src/test/resources/3_0/kotlin/feat13488_use_kotlinSpring_with_springCloud.yaml")) - .config(codegen)) - .generate(); + .openAPI(TestUtils.parseSpec("src/test/resources/3_0/kotlin/feat13488_use_kotlinSpring_with_springCloud.yaml")) + .config(codegen)) + .generate(); // Check that the @RequestMapping annotation is not generated in the Api file assertFileNotContains( - Paths.get(output + "/src/main/kotlin/org/openapitools/api/TestV1Api.kt"), - "@RequestMapping(\"\\${api.base-path" + Paths.get(output + "/src/main/kotlin/org/openapitools/api/TestV1Api.kt"), + "@RequestMapping(\"\\${api.base-path" ); } @@ -214,18 +217,18 @@ public class KotlinSpringServerCodegenTest { codegen.additionalProperties().put(KotlinSpringServerCodegen.USE_TAGS, true); List files = new DefaultGenerator() - .opts( - new ClientOptInput() - .openAPI(TestUtils.parseSpec("src/test/resources/3_0/kotlin/issue5497-use-tags-kotlin.yaml")) - .config(codegen) - ) - .generate(); + .opts( + new ClientOptInput() + .openAPI(TestUtils.parseSpec("src/test/resources/3_0/kotlin/issue5497-use-tags-kotlin.yaml")) + .config(codegen) + ) + .generate(); Helpers.assertContainsAllOf(files, - new File(output, "src/main/kotlin/org/openapitools/api/TestV1ApiController.kt"), - new File(output, "src/main/kotlin/org/openapitools/api/TestV1ApiDelegate.kt"), - new File(output, "src/main/kotlin/org/openapitools/api/TestV2ApiController.kt"), - new File(output, "src/main/kotlin/org/openapitools/api/TestV2ApiDelegate.kt") + new File(output, "src/main/kotlin/org/openapitools/api/TestV1ApiController.kt"), + new File(output, "src/main/kotlin/org/openapitools/api/TestV1ApiDelegate.kt"), + new File(output, "src/main/kotlin/org/openapitools/api/TestV2ApiController.kt"), + new File(output, "src/main/kotlin/org/openapitools/api/TestV2ApiDelegate.kt") ); } @@ -239,51 +242,51 @@ public class KotlinSpringServerCodegenTest { codegen.additionalProperties().put(KotlinSpringServerCodegen.USE_TAGS, true); List files = new DefaultGenerator() - .opts( - new ClientOptInput() - .openAPI(TestUtils.parseSpec("src/test/resources/3_0/kotlin/issue7325-use-delegate-reactive-tags-kotlin.yaml")) - .config(codegen) - ) - .generate(); + .opts( + new ClientOptInput() + .openAPI(TestUtils.parseSpec("src/test/resources/3_0/kotlin/issue7325-use-delegate-reactive-tags-kotlin.yaml")) + .config(codegen) + ) + .generate(); Helpers.assertContainsAllOf(files, - new File(output, "src/main/kotlin/org/openapitools/api/TestV1Api.kt"), - new File(output, "src/main/kotlin/org/openapitools/api/TestV1ApiController.kt"), - new File(output, "src/main/kotlin/org/openapitools/api/TestV1ApiDelegate.kt"), - new File(output, "src/main/kotlin/org/openapitools/api/TestV2Api.kt"), - new File(output, "src/main/kotlin/org/openapitools/api/TestV2ApiController.kt"), - new File(output, "src/main/kotlin/org/openapitools/api/TestV2ApiDelegate.kt"), - new File(output, "src/main/kotlin/org/openapitools/api/TestV3Api.kt"), - new File(output, "src/main/kotlin/org/openapitools/api/TestV3ApiController.kt"), - new File(output, "src/main/kotlin/org/openapitools/api/TestV3ApiDelegate.kt") + new File(output, "src/main/kotlin/org/openapitools/api/TestV1Api.kt"), + new File(output, "src/main/kotlin/org/openapitools/api/TestV1ApiController.kt"), + new File(output, "src/main/kotlin/org/openapitools/api/TestV1ApiDelegate.kt"), + new File(output, "src/main/kotlin/org/openapitools/api/TestV2Api.kt"), + new File(output, "src/main/kotlin/org/openapitools/api/TestV2ApiController.kt"), + new File(output, "src/main/kotlin/org/openapitools/api/TestV2ApiDelegate.kt"), + new File(output, "src/main/kotlin/org/openapitools/api/TestV3Api.kt"), + new File(output, "src/main/kotlin/org/openapitools/api/TestV3ApiController.kt"), + new File(output, "src/main/kotlin/org/openapitools/api/TestV3ApiDelegate.kt") ); assertFileContains(Paths.get(output + "/src/main/kotlin/org/openapitools/api/TestV1Api.kt"), - "suspend fun"); + "suspend fun"); assertFileNotContains(Paths.get(output + "/src/main/kotlin/org/openapitools/api/TestV1Api.kt"), - "exchange"); + "exchange"); assertFileContains(Paths.get(output + "/src/main/kotlin/org/openapitools/api/TestV1ApiDelegate.kt"), - "suspend fun"); + "suspend fun"); assertFileNotContains(Paths.get(output + "/src/main/kotlin/org/openapitools/api/TestV1ApiDelegate.kt"), - "ApiUtil"); + "ApiUtil"); assertFileContains(Paths.get(output + "/src/main/kotlin/org/openapitools/api/TestV2Api.kt"), - "import kotlinx.coroutines.flow.Flow", "ResponseEntity>"); + "import kotlinx.coroutines.flow.Flow", "ResponseEntity>"); assertFileNotContains(Paths.get(output + "/src/main/kotlin/org/openapitools/api/TestV2Api.kt"), - "exchange"); + "exchange"); assertFileContains(Paths.get(output + "/src/main/kotlin/org/openapitools/api/TestV2ApiDelegate.kt"), - "import kotlinx.coroutines.flow.Flow", "ResponseEntity>"); + "import kotlinx.coroutines.flow.Flow", "ResponseEntity>"); assertFileNotContains(Paths.get(output + "/src/main/kotlin/org/openapitools/api/TestV2ApiDelegate.kt"), - "suspend fun", "ApiUtil"); + "suspend fun", "ApiUtil"); assertFileContains(Paths.get(output + "/src/main/kotlin/org/openapitools/api/TestV3Api.kt"), - "import kotlinx.coroutines.flow.Flow", "requestBody: Flow"); + "import kotlinx.coroutines.flow.Flow", "requestBody: Flow"); assertFileNotContains(Paths.get(output + "/src/main/kotlin/org/openapitools/api/TestV3Api.kt"), - "exchange"); + "exchange"); assertFileContains(Paths.get(output + "/src/main/kotlin/org/openapitools/api/TestV3ApiDelegate.kt"), - "import kotlinx.coroutines.flow.Flow", "suspend fun", "requestBody: Flow"); + "import kotlinx.coroutines.flow.Flow", "suspend fun", "requestBody: Flow"); assertFileNotContains(Paths.get(output + "/src/main/kotlin/org/openapitools/api/TestV3ApiDelegate.kt"), - "ApiUtil"); + "ApiUtil"); } @Test @@ -293,7 +296,7 @@ public class KotlinSpringServerCodegenTest { String outputPath = output.getAbsolutePath().replace('\\', '/'); OpenAPI openAPI = new OpenAPIParser() - .readLocation("src/test/resources/3_0/objectQueryParam.yaml", null, new ParseOptions()).getOpenAPI(); + .readLocation("src/test/resources/3_0/objectQueryParam.yaml", null, new ParseOptions()).getOpenAPI(); KotlinSpringServerCodegen codegen = new KotlinSpringServerCodegen(); codegen.setOutputDir(output.getAbsolutePath()); @@ -323,7 +326,7 @@ public class KotlinSpringServerCodegenTest { String outputPath = output.getAbsolutePath().replace('\\', '/'); OpenAPI openAPI = new OpenAPIParser() - .readLocation("src/test/resources/3_0/issue_3248.yaml", null, new ParseOptions()).getOpenAPI(); + .readLocation("src/test/resources/3_0/issue_3248.yaml", null, new ParseOptions()).getOpenAPI(); KotlinSpringServerCodegen codegen = new KotlinSpringServerCodegen(); codegen.setOutputDir(output.getAbsolutePath()); @@ -360,7 +363,7 @@ public class KotlinSpringServerCodegenTest { String outputPath = output.getAbsolutePath().replace('\\', '/'); OpenAPI openAPI = new OpenAPIParser() - .readLocation("src/test/resources/3_0/issue_2053.yaml", null, new ParseOptions()).getOpenAPI(); + .readLocation("src/test/resources/3_0/issue_2053.yaml", null, new ParseOptions()).getOpenAPI(); KotlinSpringServerCodegen codegen = new KotlinSpringServerCodegen(); codegen.setOutputDir(output.getAbsolutePath()); @@ -380,12 +383,12 @@ public class KotlinSpringServerCodegenTest { generator.opts(input).generate(); assertFileContains( - Paths.get(outputPath + "/src/main/kotlin/org/openapitools/api/ElephantsApiController.kt"), - "@org.springframework.format.annotation.DateTimeFormat(iso = org.springframework.format.annotation.DateTimeFormat.ISO.DATE)" + Paths.get(outputPath + "/src/main/kotlin/org/openapitools/api/ElephantsApiController.kt"), + "@org.springframework.format.annotation.DateTimeFormat(iso = org.springframework.format.annotation.DateTimeFormat.ISO.DATE)" ); assertFileContains( - Paths.get(outputPath + "/src/main/kotlin/org/openapitools/api/ZebrasApiController.kt"), - "@org.springframework.format.annotation.DateTimeFormat(iso = org.springframework.format.annotation.DateTimeFormat.ISO.DATE_TIME)" + Paths.get(outputPath + "/src/main/kotlin/org/openapitools/api/ZebrasApiController.kt"), + "@org.springframework.format.annotation.DateTimeFormat(iso = org.springframework.format.annotation.DateTimeFormat.ISO.DATE_TIME)" ); } @@ -400,13 +403,13 @@ public class KotlinSpringServerCodegenTest { codegen.additionalProperties().put(KotlinSpringServerCodegen.BEAN_QUALIFIERS, true); new DefaultGenerator().opts(new ClientOptInput() - .openAPI(TestUtils.parseSpec("src/test/resources/3_0/kotlin/bean-qualifiers.yaml")) - .config(codegen)) - .generate(); + .openAPI(TestUtils.parseSpec("src/test/resources/3_0/kotlin/bean-qualifiers.yaml")) + .config(codegen)) + .generate(); assertFileContains( - Paths.get(outputPath + "/src/main/kotlin/org/openapitools/api/PingApiController.kt"), - "@RestController(\"org.openapitools.api.PingApiController\")" + Paths.get(outputPath + "/src/main/kotlin/org/openapitools/api/PingApiController.kt"), + "@RestController(\"org.openapitools.api.PingApiController\")" ); } @@ -422,13 +425,13 @@ public class KotlinSpringServerCodegenTest { codegen.additionalProperties().put(KotlinSpringServerCodegen.SKIP_DEFAULT_INTERFACE, true); new DefaultGenerator().opts(new ClientOptInput() - .openAPI(TestUtils.parseSpec("src/test/resources/3_0/kotlin/skip-default-interface.yaml")) - .config(codegen)) - .generate(); + .openAPI(TestUtils.parseSpec("src/test/resources/3_0/kotlin/skip-default-interface.yaml")) + .config(codegen)) + .generate(); assertFileNotContains( - Paths.get(outputPath + "/src/main/kotlin/org/openapitools/api/PingApi.kt"), - "return " + Paths.get(outputPath + "/src/main/kotlin/org/openapitools/api/PingApi.kt"), + "return " ); } @@ -443,21 +446,21 @@ public class KotlinSpringServerCodegenTest { codegen.additionalProperties().put(KotlinSpringServerCodegen.USE_SPRING_BOOT3, true); new DefaultGenerator().opts(new ClientOptInput() - .openAPI(TestUtils.parseSpec("src/test/resources/3_0/kotlin/feat13578_use_springboot3_jakarta_extension.yaml")) - .config(codegen)) - .generate(); + .openAPI(TestUtils.parseSpec("src/test/resources/3_0/kotlin/feat13578_use_springboot3_jakarta_extension.yaml")) + .config(codegen)) + .generate(); assertFileContains( - Paths.get(outputPath + "/src/main/kotlin/org/openapitools/api/ApiUtil.kt"), - "import jakarta.servlet.http.HttpServletResponse" + Paths.get(outputPath + "/src/main/kotlin/org/openapitools/api/ApiUtil.kt"), + "import jakarta.servlet.http.HttpServletResponse" ); assertFileContains( - Paths.get(outputPath + "/src/main/kotlin/org/openapitools/api/Exceptions.kt"), - "import jakarta.validation.ConstraintViolationException" + Paths.get(outputPath + "/src/main/kotlin/org/openapitools/api/Exceptions.kt"), + "import jakarta.validation.ConstraintViolationException" ); assertFileContains( - Paths.get(outputPath + "/src/main/kotlin/org/openapitools/api/PingApiController.kt"), - "import jakarta.validation.Valid" + Paths.get(outputPath + "/src/main/kotlin/org/openapitools/api/PingApiController.kt"), + "import jakarta.validation.Valid" ); } @@ -515,25 +518,25 @@ public class KotlinSpringServerCodegenTest { codegen.setOutputDir(output.getAbsolutePath()); new DefaultGenerator().opts(new ClientOptInput() - .openAPI(TestUtils.parseSpec("src/test/resources/3_0/kotlin/issue3596-use-correct-get-annotation-target.yaml")) - .config(codegen)) - .generate(); + .openAPI(TestUtils.parseSpec("src/test/resources/3_0/kotlin/issue3596-use-correct-get-annotation-target.yaml")) + .config(codegen)) + .generate(); assertFileNotContains( - Paths.get(outputPath + "/src/main/kotlin/org/openapitools/model/Animal.kt"), - "@Schema(example = \"null\", description = \"\")" + Paths.get(outputPath + "/src/main/kotlin/org/openapitools/model/Animal.kt"), + "@Schema(example = \"null\", description = \"\")" ); assertFileContains( - Paths.get(outputPath + "/src/main/kotlin/org/openapitools/model/Animal.kt"), - "@get:Schema(example = \"null\", description = \"\")" + Paths.get(outputPath + "/src/main/kotlin/org/openapitools/model/Animal.kt"), + "@get:Schema(example = \"null\", description = \"\")" ); assertFileNotContains( - Paths.get(outputPath + "/src/main/kotlin/org/openapitools/model/Animal.kt"), - "@Schema(example = \"null\", requiredMode = Schema.RequiredMode.REQUIRED, description = \"\")" + Paths.get(outputPath + "/src/main/kotlin/org/openapitools/model/Animal.kt"), + "@Schema(example = \"null\", requiredMode = Schema.RequiredMode.REQUIRED, description = \"\")" ); assertFileContains( - Paths.get(outputPath + "/src/main/kotlin/org/openapitools/model/Animal.kt"), - "@get:Schema(example = \"null\", requiredMode = Schema.RequiredMode.REQUIRED, description = \"\")" + Paths.get(outputPath + "/src/main/kotlin/org/openapitools/model/Animal.kt"), + "@get:Schema(example = \"null\", requiredMode = Schema.RequiredMode.REQUIRED, description = \"\")" ); } @@ -549,25 +552,53 @@ public class KotlinSpringServerCodegenTest { codegen.additionalProperties().put(DOCUMENTATION_PROVIDER, DocumentationProvider.SPRINGFOX.toCliOptValue()); new DefaultGenerator().opts(new ClientOptInput() - .openAPI(TestUtils.parseSpec("src/test/resources/3_0/kotlin/issue3596-use-correct-get-annotation-target.yaml")) - .config(codegen)) - .generate(); + .openAPI(TestUtils.parseSpec("src/test/resources/3_0/kotlin/issue3596-use-correct-get-annotation-target.yaml")) + .config(codegen)) + .generate(); assertFileNotContains( - Paths.get(outputPath + "/src/main/kotlin/org/openapitools/model/Animal.kt"), - "@ApiModelProperty(example = \"null\", value = \"\")" + Paths.get(outputPath + "/src/main/kotlin/org/openapitools/model/Animal.kt"), + "@ApiModelProperty(example = \"null\", value = \"\")" ); assertFileContains( - Paths.get(outputPath + "/src/main/kotlin/org/openapitools/model/Animal.kt"), - "@get:ApiModelProperty(example = \"null\", value = \"\")" + Paths.get(outputPath + "/src/main/kotlin/org/openapitools/model/Animal.kt"), + "@get:ApiModelProperty(example = \"null\", value = \"\")" ); assertFileNotContains( - Paths.get(outputPath + "/src/main/kotlin/org/openapitools/model/Animal.kt"), - "@ApiModelProperty(example = \"null\", required = true, value = \"\")" + Paths.get(outputPath + "/src/main/kotlin/org/openapitools/model/Animal.kt"), + "@ApiModelProperty(example = \"null\", required = true, value = \"\")" ); assertFileContains( - Paths.get(outputPath + "/src/main/kotlin/org/openapitools/model/Animal.kt"), - "@get:ApiModelProperty(example = \"null\", required = true, value = \"\")" + Paths.get(outputPath + "/src/main/kotlin/org/openapitools/model/Animal.kt"), + "@get:ApiModelProperty(example = \"null\", required = true, value = \"\")" ); } -} + + @Test + public void useBeanValidationGenerateAnnotationsForRequestBody() throws IOException { + File output = Files.createTempDirectory("test").toFile().getCanonicalFile(); + output.deleteOnExit(); + + OpenAPI openAPI = new OpenAPIParser() + .readLocation("src/test/resources/bugs/issue_13932.yml", null, new ParseOptions()).getOpenAPI(); + KotlinSpringServerCodegen codegen = new KotlinSpringServerCodegen(); + codegen.setOutputDir(output.getAbsolutePath()); + codegen.additionalProperties().put(KotlinSpringServerCodegen.INTERFACE_ONLY, "true"); + codegen.additionalProperties().put(KotlinSpringServerCodegen.USE_BEANVALIDATION, "true"); + codegen.additionalProperties().put(CodegenConstants.MODEL_PACKAGE, "xyz.model"); + codegen.additionalProperties().put(CodegenConstants.API_PACKAGE, "xyz.controller"); + + ClientOptInput input = new ClientOptInput() + .openAPI(openAPI) + .config(codegen); + + DefaultGenerator generator = new DefaultGenerator(); + Map files = generator.opts(input).generate().stream() + .collect(Collectors.toMap(File::getName, Function.identity())); + + assertFileContains( + Paths.get(files.get("AddApi.kt").getAbsolutePath()), + "@Min(2)" + ); + } +} \ No newline at end of file