Skip to content

Commit a7d57ca

Browse files
authored
[typescript-angular] Fix inner enum reference in multi-map property type (#22748)
* [typescript-angular] Fix inner enum reference in multi-map property type Fixes #22747 * [typescript] Fix inner enum reference in multi-map property type Fixes #20877, #22747 * test for the double-prefixed enum names This proves that following comment is not relevant: #22748 (comment) * Implement review comment about the trailing `>` This implements comment #22748 (comment) * fix @type for generic interfaces in TypeScript clients Type annotation in a comment should match the actual field type.
1 parent 8d88b82 commit a7d57ca

41 files changed

Lines changed: 386 additions & 95 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractTypeScriptClientCodegen.java

Lines changed: 131 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -17,39 +17,65 @@
1717

1818
package org.openapitools.codegen.languages;
1919

20-
import io.swagger.v3.oas.models.OpenAPI;
21-
import io.swagger.v3.oas.models.media.Schema;
22-
import io.swagger.v3.oas.models.parameters.Parameter;
23-
import lombok.Getter;
24-
import lombok.Setter;
20+
import static org.openapitools.codegen.languages.AbstractTypeScriptClientCodegen.ParameterExpander.ParamStyle.form;
21+
import static org.openapitools.codegen.languages.AbstractTypeScriptClientCodegen.ParameterExpander.ParamStyle.simple;
22+
import static org.openapitools.codegen.utils.CamelizeOption.LOWERCASE_FIRST_LETTER;
23+
import static org.openapitools.codegen.utils.StringUtils.camelize;
24+
import static org.openapitools.codegen.utils.StringUtils.underscore;
25+
26+
import java.io.File;
27+
import java.text.SimpleDateFormat;
28+
import java.util.ArrayList;
29+
import java.util.Arrays;
30+
import java.util.Collections;
31+
import java.util.Date;
32+
import java.util.EnumSet;
33+
import java.util.HashMap;
34+
import java.util.HashSet;
35+
import java.util.List;
36+
import java.util.Locale;
37+
import java.util.Map;
38+
import java.util.Set;
39+
import java.util.TreeSet;
40+
import java.util.function.BiPredicate;
41+
import java.util.function.Function;
42+
import java.util.regex.Pattern;
43+
import java.util.stream.Collectors;
44+
import java.util.stream.Stream;
45+
2546
import org.apache.commons.io.FilenameUtils;
2647
import org.apache.commons.lang3.StringUtils;
2748
import org.checkerframework.checker.nullness.qual.Nullable;
28-
import org.openapitools.codegen.*;
49+
import org.openapitools.codegen.CliOption;
50+
import org.openapitools.codegen.CodegenConfig;
51+
import org.openapitools.codegen.CodegenConstants;
2952
import org.openapitools.codegen.CodegenConstants.ENUM_PROPERTY_NAMING_TYPE;
3053
import org.openapitools.codegen.CodegenConstants.MODEL_PROPERTY_NAMING_TYPE;
3154
import org.openapitools.codegen.CodegenConstants.PARAM_NAMING_TYPE;
32-
import org.openapitools.codegen.meta.features.*;
55+
import org.openapitools.codegen.CodegenModel;
56+
import org.openapitools.codegen.CodegenOperation;
57+
import org.openapitools.codegen.CodegenParameter;
58+
import org.openapitools.codegen.CodegenProperty;
59+
import org.openapitools.codegen.CodegenType;
60+
import org.openapitools.codegen.DefaultCodegen;
61+
import org.openapitools.codegen.GeneratorLanguage;
62+
import org.openapitools.codegen.meta.features.ClientModificationFeature;
63+
import org.openapitools.codegen.meta.features.DocumentationFeature;
64+
import org.openapitools.codegen.meta.features.GlobalFeature;
65+
import org.openapitools.codegen.meta.features.SchemaSupportFeature;
66+
import org.openapitools.codegen.meta.features.SecurityFeature;
67+
import org.openapitools.codegen.meta.features.WireFormatFeature;
3368
import org.openapitools.codegen.model.ModelMap;
3469
import org.openapitools.codegen.model.ModelsMap;
3570
import org.openapitools.codegen.utils.ModelUtils;
3671
import org.slf4j.Logger;
3772
import org.slf4j.LoggerFactory;
3873

39-
import java.io.File;
40-
import java.text.SimpleDateFormat;
41-
import java.util.*;
42-
import java.util.function.BiPredicate;
43-
import java.util.function.Function;
44-
import java.util.regex.Pattern;
45-
import java.util.stream.Collectors;
46-
import java.util.stream.Stream;
47-
48-
import static org.openapitools.codegen.languages.AbstractTypeScriptClientCodegen.ParameterExpander.ParamStyle.form;
49-
import static org.openapitools.codegen.languages.AbstractTypeScriptClientCodegen.ParameterExpander.ParamStyle.simple;
50-
import static org.openapitools.codegen.utils.CamelizeOption.LOWERCASE_FIRST_LETTER;
51-
import static org.openapitools.codegen.utils.StringUtils.camelize;
52-
import static org.openapitools.codegen.utils.StringUtils.underscore;
74+
import io.swagger.v3.oas.models.OpenAPI;
75+
import io.swagger.v3.oas.models.media.Schema;
76+
import io.swagger.v3.oas.models.parameters.Parameter;
77+
import lombok.Getter;
78+
import lombok.Setter;
5379

5480
public abstract class AbstractTypeScriptClientCodegen extends DefaultCodegen implements CodegenConfig {
5581

@@ -1023,22 +1049,104 @@ protected void addImport(CodegenModel m, String type) {
10231049
}
10241050
}
10251051

1052+
/**
1053+
* Override to fix the inner enum naming issue for maps/arrays of enums.
1054+
* <p>
1055+
* The parent implementation uses toEnumName(baseItem) which produces a generic name
1056+
* like "InnerEnum" based on the inner item's name. This override first calculates
1057+
* the correct property.enumName, then uses it in the datatypeWithEnum replacement.
1058+
* </p>
1059+
*
1060+
* @param property Codegen property
1061+
*/
1062+
@Override
1063+
protected void updateDataTypeWithEnumForArray(CodegenProperty property) {
1064+
CodegenProperty baseItem = property.items;
1065+
while (baseItem != null && (Boolean.TRUE.equals(baseItem.isMap)
1066+
|| Boolean.TRUE.equals(baseItem.isArray))) {
1067+
baseItem = baseItem.items;
1068+
}
1069+
1070+
if (baseItem != null) {
1071+
// First, set the property's enumName using the property itself (not the inner item)
1072+
// This ensures the correct enum name (e.g., "OptionsEnum") is used
1073+
// instead of the generic inner item name (e.g., "InnerEnum")
1074+
property.enumName = toEnumName(property);
1075+
1076+
// Now use property.enumName for datatypeWithEnum
1077+
property.datatypeWithEnum = property.datatypeWithEnum.replace(baseItem.baseType, property.enumName);
1078+
1079+
// set default value for variable with inner enum
1080+
if (property.defaultValue != null) {
1081+
property.defaultValue = property.defaultValue.replace(baseItem.baseType, property.enumName);
1082+
}
1083+
1084+
updateCodegenPropertyEnum(property);
1085+
}
1086+
}
1087+
1088+
/**
1089+
* Override to fix the inner enum naming issue for map properties.
1090+
* <p>
1091+
* The parent implementation uses {@code toEnumName(baseItem)} which produces "InnerEnum"
1092+
* for properties whose inner item has a generic name like "inner". We instead
1093+
* calculate the enumName from the property itself first, then use it in the replacement.
1094+
* </p>
1095+
*
1096+
* @param property Codegen property
1097+
*/
1098+
@Override
1099+
protected void updateDataTypeWithEnumForMap(CodegenProperty property) {
1100+
CodegenProperty baseItem = property.items;
1101+
while (baseItem != null && (Boolean.TRUE.equals(baseItem.isMap)
1102+
|| Boolean.TRUE.equals(baseItem.isArray))) {
1103+
baseItem = baseItem.items;
1104+
}
1105+
1106+
if (baseItem != null) {
1107+
// First, set the property's enumName using the property itself (not the inner item)
1108+
property.enumName = toEnumName(property);
1109+
1110+
// Replace only the LAST occurrence of baseType with enumName.
1111+
// In map types, the value type appears last (after the key type),
1112+
// so this approach is template-agnostic.
1113+
String datatypeWithEnum = property.datatypeWithEnum;
1114+
int lastIndex = datatypeWithEnum.lastIndexOf(baseItem.baseType);
1115+
if (lastIndex >= 0) {
1116+
property.datatypeWithEnum = datatypeWithEnum.substring(0, lastIndex)
1117+
+ property.enumName
1118+
+ datatypeWithEnum.substring(lastIndex + baseItem.baseType.length());
1119+
}
1120+
LOGGER.info("Updated datatypeWithEnum for map property '{}': {}", property.name, property.datatypeWithEnum);
1121+
1122+
// set default value for variable with inner enum
1123+
if (property.defaultValue != null) {
1124+
property.defaultValue = property.defaultValue.replace(baseItem.baseType, property.enumName);
1125+
}
1126+
1127+
updateCodegenPropertyEnum(property);
1128+
}
1129+
}
1130+
10261131
@Override
10271132
public ModelsMap postProcessModels(ModelsMap objs) {
10281133
// process enum in models
10291134
List<ModelMap> models = postProcessModelsEnum(objs).getModels();
10301135
for (ModelMap mo : models) {
10311136
CodegenModel cm = mo.getModel();
10321137
cm.imports = new TreeSet<>(cm.imports);
1138+
10331139
// name enum with model name, e.g. StatusEnum => Pet.StatusEnum
1140+
// This applies to both direct enum properties (isEnum) and properties containing
1141+
// inner enums (isInnerEnum) like maps or arrays of enums.
10341142
for (CodegenProperty var : cm.vars) {
1035-
if (Boolean.TRUE.equals(var.isEnum)) {
1143+
if (Boolean.TRUE.equals(var.isEnum) || Boolean.TRUE.equals(var.isInnerEnum)) {
10361144
var.datatypeWithEnum = var.datatypeWithEnum.replace(var.enumName, cm.classname + classEnumSeparator + var.enumName);
10371145
}
10381146
}
10391147
if (cm.parent != null) {
10401148
for (CodegenProperty var : cm.allVars) {
1041-
if (Boolean.TRUE.equals(var.isEnum)) {
1149+
if (Boolean.TRUE.equals(var.isEnum) || Boolean.TRUE.equals(var.isInnerEnum)) {
10421150
var.datatypeWithEnum = var.datatypeWithEnum
10431151
.replace(var.enumName, cm.classname + classEnumSeparator + var.enumName);
10441152
}

modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptFetchClientCodegen.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,7 @@ protected void addAdditionPropertiesToCodeGenModel(CodegenModel codegenModel, Sc
383383

384384
@Override
385385
public ModelsMap postProcessModels(ModelsMap objs) {
386+
// postProcessModelsEnum applies inner enum fixes via the parent class override
386387
List<ModelMap> models = postProcessModelsEnum(objs).getModels();
387388

388389
// process enum and custom properties in models
@@ -800,7 +801,8 @@ private ExtendedCodegenModel processCodeGenModel(ExtendedCodegenModel cm) {
800801
for (CodegenProperty cpVar : cm.allVars) {
801802
ExtendedCodegenProperty var = (ExtendedCodegenProperty) cpVar;
802803

803-
if (Boolean.TRUE.equals(var.isEnum)) {
804+
// Handle both direct enum properties and inner enums (maps/arrays of enums)
805+
if (Boolean.TRUE.equals(var.isEnum) || Boolean.TRUE.equals(var.isInnerEnum)) {
804806
var.datatypeWithEnum = var.datatypeWithEnum
805807
.replace(var.enumName, cm.classname + var.enumName);
806808
}
@@ -845,7 +847,9 @@ private ExtendedCodegenModel processCodeGenModel(ExtendedCodegenModel cm) {
845847

846848
private boolean processCodegenProperty(ExtendedCodegenProperty var, String parentClassName, Object xEntityId) {
847849
// name enum with model name, e.g. StatusEnum => PetStatusEnum
848-
if (Boolean.TRUE.equals(var.isEnum)) {
850+
// This applies to both direct enum properties (isEnum) and properties containing
851+
// inner enums (isInnerEnum) like maps or arrays of enums.
852+
if (Boolean.TRUE.equals(var.isEnum) || Boolean.TRUE.equals(var.isInnerEnum)) {
849853
// behaviour for enum names is specific for Typescript Fetch, not using namespaces
850854
var.datatypeWithEnum = var.datatypeWithEnum.replace(var.enumName, parentClassName + var.enumName);
851855

@@ -1525,11 +1529,11 @@ public class ExtendedCodegenModel extends CodegenModel {
15251529
public Set<CodegenProperty> oneOfPrimitives = new HashSet<>();
15261530
@Getter @Setter
15271531
public CodegenDiscriminator.MappedModel selfReferencingDiscriminatorMapping;
1528-
1532+
15291533
public boolean isEntity; // Is a model containing an "id" property marked as isUniqueId
15301534
public String returnPassthrough;
15311535
public boolean hasReturnPassthroughVoid;
1532-
1536+
15331537
public boolean hasSelfReferencingDiscriminatorMapping(){
15341538
return selfReferencingDiscriminatorMapping != null;
15351539
}

modules/openapi-generator/src/main/resources/typescript-fetch/modelGenericInterfaces.mustache

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@ export interface {{classname}} {{#parent}}extends {{{.}}} {{/parent}}{
1010
{{#vars}}
1111
/**
1212
* {{#lambda.indented_star_4}}{{{unescapedDescription}}}{{/lambda.indented_star_4}}
13-
* @type {{=<% %>=}}{<%&datatype%>}<%={{ }}=%>
13+
* @type {{=<% %>=}}{<%&datatypeWithEnum%>}<%={{ }}=%>
1414
* @memberof {{classname}}
1515
{{#deprecated}}
1616
* @deprecated
1717
{{/deprecated}}
1818
*/
19-
{{#isReadOnly}}readonly {{/isReadOnly}}{{name}}{{^required}}?{{/required}}: {{#isEnum}}{{{datatypeWithEnum}}}{{#isNullable}} | null{{/isNullable}}{{/isEnum}}{{^isEnum}}{{{datatype}}}{{#isNullable}} | null{{/isNullable}}{{/isEnum}};
19+
{{#isReadOnly}}readonly {{/isReadOnly}}{{name}}{{^required}}?{{/required}}: {{{datatypeWithEnum}}}{{#isNullable}} | null{{/isNullable}};
2020
{{/vars}}
2121
}{{#hasEnums}}
2222

0 commit comments

Comments
 (0)