From 69a4b1bb4fe0db437da6c848309a5883f4e04134 Mon Sep 17 00:00:00 2001 From: moznion Date: Thu, 20 Feb 2025 21:12:12 +0900 Subject: [PATCH 1/3] [typescript] Prevent generating invalid enum code due to empty variable names MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After sanitizing all characters (e.g. multibyte characters), the enum variable name may become an empty string. Since an empty string would cause a syntax error, this patch pads the pseudo variable name (`STRING`) to avoid that issue. For example, given the following OpenAPI definition: ```yaml openapi: "3.0.0" info: title: Sample project version: '1.0' description: 'Sample API Check "API Key" ' license: name: Apache 2.0 url: 'https://www.apache.org/licenses/LICENSE-2.0' paths: {} components: schemas: Greeting: type: string enum: - 'こんにちは' - '你好' - '안녕하세요' ``` The current logic generates the following code for Greeting: ```typescript export enum Greeting { = 'こんにちは', 2 = '你好', 3 = '안녕하세요' } ``` This code is invalid. With this patch, the generated code becomes: ```typescript export enum Greeting { STRING = 'こんにちは', STRING2 = '你好', STRING3 = '안녕하세요' } ``` Signed-off-by: moznion --- .../languages/TypeScriptClientCodegen.java | 10 ++++-- .../TypeScriptClientCodegenTest.java | 35 +++++++++++++++++++ ...ypescript_enum_var_name_all_sanitized.yaml | 17 +++++++++ 3 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 modules/openapi-generator/src/test/resources/bugs/typescript_enum_var_name_all_sanitized.yaml diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptClientCodegen.java index f35c40b7677e..503ea2bfb459 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptClientCodegen.java @@ -311,9 +311,15 @@ public String toEnumVarName(String name, String datatype) { if (enumName.matches("\\d.*")) { // starts with number return "_" + enumName; - } else { - return enumName; } + + if (enumName.isEmpty()) { + // After sanitizing *all* characters (e.g. multibyte characters), the var name becomes an empty string. + // An empty string would cause a syntax error, so this pads the pseudo var name to avoid that. + return "STRING"; + } + + return enumName; } private String getNameWithEnumPropertyNaming(String name) { diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/TypeScriptClientCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/TypeScriptClientCodegenTest.java index 574aa0434467..809365699321 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/TypeScriptClientCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/TypeScriptClientCodegenTest.java @@ -1,18 +1,28 @@ package org.openapitools.codegen.typescript; +import com.github.jknack.handlebars.internal.lang3.StringUtils; import com.google.common.collect.Sets; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.PathItem; import io.swagger.v3.oas.models.media.*; import org.openapitools.codegen.*; +import org.openapitools.codegen.config.CodegenConfigurator; +import org.openapitools.codegen.languages.TypeScriptAxiosClientCodegen; import org.openapitools.codegen.languages.TypeScriptClientCodegen; import org.openapitools.codegen.model.ModelMap; import org.openapitools.codegen.model.ModelsMap; import org.openapitools.codegen.utils.ModelUtils; import org.testng.Assert; import org.testng.annotations.Test; +import org.testng.log4testng.Logger; +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -179,4 +189,29 @@ public void testAdditionalPropertiesPutForConfigValues() throws Exception { Assert.assertEquals(codegen.getLicenseName(), licenseName); } + + @Test + public void testForAllSanitizedEnum() throws Exception { + final File output = Files.createTempDirectory("typescriptnodeclient_").toFile(); + output.deleteOnExit(); + + final CodegenConfigurator configurator = new CodegenConfigurator() + .setGeneratorName("typescript") + .setInputSpec("src/test/resources/bugs/typescript_enum_var_name_all_sanitized.yaml") + .setOutputDir(output.getAbsolutePath().replace("\\", "/")); + + final ClientOptInput clientOptInput = configurator.toClientOptInput(); + final DefaultGenerator generator = new DefaultGenerator(); + final List files = generator.opts(clientOptInput).generate(); + files.forEach(File::deleteOnExit); + + TestUtils.assertFileContains( + Paths.get(output + "/models/Greeting.ts"), + "export enum Greeting {\n" + + " STRING = 'こんにちは',\n" + + " STRING2 = '你好',\n" + + " STRING3 = '안녕하세요'\n" + + "}" + ); + } } diff --git a/modules/openapi-generator/src/test/resources/bugs/typescript_enum_var_name_all_sanitized.yaml b/modules/openapi-generator/src/test/resources/bugs/typescript_enum_var_name_all_sanitized.yaml new file mode 100644 index 000000000000..e86beb7a0d6d --- /dev/null +++ b/modules/openapi-generator/src/test/resources/bugs/typescript_enum_var_name_all_sanitized.yaml @@ -0,0 +1,17 @@ +openapi: "3.0.0" +info: + title: Sample project + version: '1.0' + description: 'Sample API Check "API Key" ' + license: + name: Apache 2.0 + url: 'https://www.apache.org/licenses/LICENSE-2.0' +paths: {} +components: + schemas: + Greeting: + type: string + enum: + - 'こんにちは' + - '你好' + - '안녕하세요' From 1b8747e15a0ebfdc8494715f565a27c854ba20d7 Mon Sep 17 00:00:00 2001 From: moznion Date: Thu, 20 Feb 2025 21:46:32 +0900 Subject: [PATCH 2/3] Remove unnecessary imports Signed-off-by: moznion --- .../codegen/typescript/TypeScriptClientCodegenTest.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/TypeScriptClientCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/TypeScriptClientCodegenTest.java index 809365699321..eb7916b2be85 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/TypeScriptClientCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/TypeScriptClientCodegenTest.java @@ -1,28 +1,22 @@ package org.openapitools.codegen.typescript; -import com.github.jknack.handlebars.internal.lang3.StringUtils; import com.google.common.collect.Sets; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.PathItem; import io.swagger.v3.oas.models.media.*; import org.openapitools.codegen.*; import org.openapitools.codegen.config.CodegenConfigurator; -import org.openapitools.codegen.languages.TypeScriptAxiosClientCodegen; import org.openapitools.codegen.languages.TypeScriptClientCodegen; import org.openapitools.codegen.model.ModelMap; import org.openapitools.codegen.model.ModelsMap; import org.openapitools.codegen.utils.ModelUtils; import org.testng.Assert; import org.testng.annotations.Test; -import org.testng.log4testng.Logger; import java.io.File; -import java.nio.charset.StandardCharsets; import java.nio.file.Files; -import java.nio.file.Path; import java.nio.file.Paths; import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; From edc3b9d7c735e9ced68e21493998bf95efb8e2bf Mon Sep 17 00:00:00 2001 From: moznion Date: Fri, 21 Feb 2025 15:20:06 +0900 Subject: [PATCH 3/3] Use new sanitizer for TypeScript symbol which takes wider variety characters for enum var name Signed-off-by: moznion --- .../codegen/languages/TypeScriptClientCodegen.java | 14 ++++++++++++-- .../typescript/TypeScriptClientCodegenTest.java | 8 +++++--- .../typescript_enum_var_name_all_sanitized.yaml | 2 ++ 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptClientCodegen.java index 503ea2bfb459..d0eb42897814 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptClientCodegen.java @@ -315,13 +315,23 @@ public String toEnumVarName(String name, String datatype) { if (enumName.isEmpty()) { // After sanitizing *all* characters (e.g. multibyte characters), the var name becomes an empty string. - // An empty string would cause a syntax error, so this pads the pseudo var name to avoid that. - return "STRING"; + // An empty string would cause a syntax error, so this code attempts to re-sanitize the name using another sanitizer that allows a wider variety of characters. + // For backward compatibility, this additional sanitization is only applied if the original sanitized name is empty. + final String sanitized = sanitizeNameForTypeScriptSymbol(name); + if (sanitized.isEmpty()) { + // After re-sanitizing, this pads a pseudo var name ("STRING") if still the name is empty. + return "STRING"; + } + return "_" + sanitized; } return enumName; } + private String sanitizeNameForTypeScriptSymbol(String name) { + return sanitizeName(name, "[^\\p{L}\\p{Nd}\\$_]"); + } + private String getNameWithEnumPropertyNaming(String name) { switch (getEnumPropertyNaming()) { case original: diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/TypeScriptClientCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/TypeScriptClientCodegenTest.java index eb7916b2be85..d80a4de9e9e8 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/TypeScriptClientCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/TypeScriptClientCodegenTest.java @@ -202,9 +202,11 @@ public void testForAllSanitizedEnum() throws Exception { TestUtils.assertFileContains( Paths.get(output + "/models/Greeting.ts"), "export enum Greeting {\n" + - " STRING = 'こんにちは',\n" + - " STRING2 = '你好',\n" + - " STRING3 = '안녕하세요'\n" + + " _こんにちは = 'こんにちは',\n" + + " _你好 = '你好',\n" + + " _안녕하세요 = '안녕하세요',\n" + + " STRING = '!@#%',\n" + + " STRING2 = '^&*\uD83C\uDF63'", "}" ); } diff --git a/modules/openapi-generator/src/test/resources/bugs/typescript_enum_var_name_all_sanitized.yaml b/modules/openapi-generator/src/test/resources/bugs/typescript_enum_var_name_all_sanitized.yaml index e86beb7a0d6d..dfb23296e39a 100644 --- a/modules/openapi-generator/src/test/resources/bugs/typescript_enum_var_name_all_sanitized.yaml +++ b/modules/openapi-generator/src/test/resources/bugs/typescript_enum_var_name_all_sanitized.yaml @@ -15,3 +15,5 @@ components: - 'こんにちは' - '你好' - '안녕하세요' + - '!@#%' + - '^&*🍣'