From 9537a7fb46b29f649b802d7a470ef52c62cd631b Mon Sep 17 00:00:00 2001 From: moznion Date: Tue, 25 Feb 2025 20:58:27 +0900 Subject: [PATCH] [typescript] Prevent generating invalid enum code due to empty variable names (#20699) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [typescript] Prevent generating invalid enum code due to empty variable names 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 * Remove unnecessary imports Signed-off-by: moznion * Use new sanitizer for TypeScript symbol which takes wider variety characters for enum var name Signed-off-by: moznion --------- Signed-off-by: moznion --- .../languages/TypeScriptClientCodegen.java | 20 ++++++++++-- .../TypeScriptClientCodegenTest.java | 31 +++++++++++++++++++ ...ypescript_enum_var_name_all_sanitized.yaml | 19 ++++++++++++ 3 files changed, 68 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 f35c40b7677..d0eb4289781 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,25 @@ public class TypeScriptClientCodegen extends AbstractTypeScriptClientCodegen imp 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 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) { 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 574aa043446..d80a4de9e9e 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 @@ -5,6 +5,7 @@ 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.TypeScriptClientCodegen; import org.openapitools.codegen.model.ModelMap; import org.openapitools.codegen.model.ModelsMap; @@ -12,6 +13,9 @@ import org.openapitools.codegen.utils.ModelUtils; import org.testng.Assert; import org.testng.annotations.Test; +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.Collections; import java.util.List; import java.util.Map; @@ -179,4 +183,31 @@ public class TypeScriptClientCodegenTest { 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" + + " _こんにちは = 'こんにちは',\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 new file mode 100644 index 00000000000..dfb23296e39 --- /dev/null +++ b/modules/openapi-generator/src/test/resources/bugs/typescript_enum_var_name_all_sanitized.yaml @@ -0,0 +1,19 @@ +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: + - 'こんにちは' + - '你好' + - '안녕하세요' + - '!@#%' + - '^&*🍣'