From f6ac603e157b7e4d5c9376f762c3103f6e58560d Mon Sep 17 00:00:00 2001 From: Martin Visser Date: Mon, 7 Aug 2023 03:32:00 +0200 Subject: [PATCH] [kotlin] Add fix for wrongly encoded backticks for reserved words when generating kotlin-spring server code (#14027) --- .../kotlin-spring/bodyParams.mustache | 2 +- .../kotlin-spring/returnValue.mustache | 2 +- .../resources/kotlin-spring/service.mustache | 4 +- .../kotlin/KotlinReservedWordsTest.java | 40 +++++++++++++++++++ .../resources/3_0/kotlin/reserved_words.yaml | 18 ++++++++- ...14026_kotlin_backticks_reserved_words.yaml | 25 ++++++++++++ 6 files changed, 86 insertions(+), 5 deletions(-) create mode 100644 modules/openapi-generator/src/test/resources/bugs/issue_14026_kotlin_backticks_reserved_words.yaml 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 321b15e5154..911bea74a1b 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{{/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/returnValue.mustache b/modules/openapi-generator/src/main/resources/kotlin-spring/returnValue.mustache index 98d9e9f83b6..8c7c1aa4f5c 100644 --- a/modules/openapi-generator/src/main/resources/kotlin-spring/returnValue.mustache +++ b/modules/openapi-generator/src/main/resources/kotlin-spring/returnValue.mustache @@ -1 +1 @@ -{{#serviceInterface}}ResponseEntity(service.{{operationId}}({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}), {{#responses}}{{#-first}}HttpStatus.valueOf({{code}}){{/-first}}{{/responses}}){{/serviceInterface}}{{^serviceInterface}}ResponseEntity(HttpStatus.NOT_IMPLEMENTED){{/serviceInterface}} \ No newline at end of file +{{#serviceInterface}}ResponseEntity(service.{{operationId}}({{#allParams}}{{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}}), {{#responses}}{{#-first}}HttpStatus.valueOf({{code}}){{/-first}}{{/responses}}){{/serviceInterface}}{{^serviceInterface}}ResponseEntity(HttpStatus.NOT_IMPLEMENTED){{/serviceInterface}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/kotlin-spring/service.mustache b/modules/openapi-generator/src/main/resources/kotlin-spring/service.mustache index 875cdfba2dd..1f2a0d02474 100644 --- a/modules/openapi-generator/src/main/resources/kotlin-spring/service.mustache +++ b/modules/openapi-generator/src/main/resources/kotlin-spring/service.mustache @@ -17,7 +17,7 @@ interface {{classname}}Service { {{/notes}} * {{#allParams}} - * @param {{paramName}} {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}} + * @param {{{paramName}}} {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}} {{/allParams}} * @return {{#responses}}{{message}} (status code {{code}}){{^-last}} * or {{/-last}}{{/responses}} @@ -30,7 +30,7 @@ interface {{classname}}Service { {{/externalDocs}} * @see {{classname}}#{{operationId}} */ - {{#reactive}}{{^isArray}}suspend {{/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}}{{/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}} {{/operation}} } {{/operations}} diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/KotlinReservedWordsTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/KotlinReservedWordsTest.java index 0d12b103ea4..1eda19d8d77 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/KotlinReservedWordsTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/KotlinReservedWordsTest.java @@ -6,6 +6,7 @@ import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.parameters.Parameter; import org.openapitools.codegen.*; import org.openapitools.codegen.languages.KotlinClientCodegen; +import org.openapitools.codegen.languages.KotlinSpringServerCodegen; import org.openapitools.codegen.utils.StringUtils; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -26,6 +27,7 @@ public class KotlinReservedWordsTest { @DataProvider(name = "reservedWords") static Object[][] reservedWords() { return new Object[][]{ + {"annotation"}, {"as"}, {"break"}, {"class"}, @@ -162,4 +164,42 @@ public class KotlinReservedWordsTest { "`" ); } + + @Test + public void reservedWordsInGeneratedServerCode() throws Exception { + String baseApiPackage = "/org/openapitools/api/"; + File output = Files.createTempDirectory("test").toFile().getCanonicalFile(); //may be move to /build + OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/bugs/issue_14026_kotlin_backticks_reserved_words.yaml"); + + KotlinSpringServerCodegen codegen = new KotlinSpringServerCodegen(); + codegen.setOutputDir(output.getAbsolutePath()); + codegen.setServiceInterface(true); + + ClientOptInput input = new ClientOptInput(); + input.openAPI(openAPI); + input.config(codegen); + + DefaultGenerator generator = new DefaultGenerator(); + generator.opts(input).generate(); + + File resultSourcePath = new File(output, "src/main/kotlin"); + + assertFileContains(Paths.get(resultSourcePath.getAbsolutePath() + baseApiPackage + "AnnotationsApiController.kt"), + "fun annotationsPost(@Parameter(description = \"\", required = true) @Valid @RequestBody `annotation`: Annotation", + "return ResponseEntity(service.annotationsPost(`annotation`), HttpStatus.valueOf(200))" + ); + + assertFileNotContains(Paths.get(resultSourcePath.getAbsolutePath() + baseApiPackage + "AnnotationsApiController.kt"), + "`" + ); + + assertFileContains(Paths.get(resultSourcePath.getAbsolutePath() + baseApiPackage + "AnnotationsApiService.kt"), + "* @param `annotation` (required)", + "fun annotationsPost(`annotation`: Annotation): Unit" + ); + + assertFileNotContains(Paths.get(resultSourcePath.getAbsolutePath() + baseApiPackage + "AnnotationsApiService.kt"), + "`" + ); + } } diff --git a/modules/openapi-generator/src/test/resources/3_0/kotlin/reserved_words.yaml b/modules/openapi-generator/src/test/resources/3_0/kotlin/reserved_words.yaml index 540607bb157..38356d3aacd 100644 --- a/modules/openapi-generator/src/test/resources/3_0/kotlin/reserved_words.yaml +++ b/modules/openapi-generator/src/test/resources/3_0/kotlin/reserved_words.yaml @@ -8,6 +8,10 @@ paths: summary: Ping description: Pingy Ping parameters: + - name: annotation + in: header + schema: + type: string - name: as in: header schema: @@ -146,6 +150,8 @@ components: description: OK, Good type: object properties: + annotation: + type: string as: type: string break: @@ -217,6 +223,8 @@ components: description: Reference links type: object properties: + annotation: + $ref: '#/components/schemas/annotation' as: $ref: '#/components/schemas/as' break: @@ -281,6 +289,14 @@ components: $ref: '#/components/schemas/external' internal: $ref: '#/components/schemas/internal' + annotation: + title: Testing reserved word 'annotation' + type: object + properties: + id: + type: integer + format: int64 + as: title: Testing reserved word 'as' type: object @@ -535,4 +551,4 @@ components: properties: id: type: integer - format: int64 \ No newline at end of file + format: int64 diff --git a/modules/openapi-generator/src/test/resources/bugs/issue_14026_kotlin_backticks_reserved_words.yaml b/modules/openapi-generator/src/test/resources/bugs/issue_14026_kotlin_backticks_reserved_words.yaml new file mode 100644 index 00000000000..aa8b3d043a7 --- /dev/null +++ b/modules/openapi-generator/src/test/resources/bugs/issue_14026_kotlin_backticks_reserved_words.yaml @@ -0,0 +1,25 @@ +openapi: 3.0.7 +info: + title: title + version: '0.0.1' +paths: + /annotations: + post: + summary: annotate + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Annotation' + responses: + '200': + description: OK +components: + schemas: + Annotation: + type: object + properties: + id: + type: string + format: uuid