Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/generators/go.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|enumUnknownDefaultCase|If the server adds new enum cases, that are unknown by an old spec/client, the client will fail to parse the network response. With this option enabled, each enum will have a new case, 'unknown_default_open_api', so that when the server sends an enum case that is not known by the client/spec, they can safely fallback to this case.|<dl><dt>**false**</dt><dd>No changes to the enums are made, this is the default option.</dd><dt>**true**</dt><dd>With this option enabled, each enum will have a new case, 'unknown_default_open_api', so that when the enum case sent by the server is not known by the client/spec, can safely be decoded to this case.</dd></dl>|false|
|generateInterfaces|Generate interfaces for api classes| |false|
|generateMarshalJSON|Generate MarshalJSON method| |true|
|generateTypeAliasesForDedupedSchemas|Generate type aliases for deduplicated inline model schemas. When the InlineModelResolver deduplicates inline schemas with identical structures, this option creates type aliases using the original inline model names, preserving naming context from the OpenAPI specification.| |true|
|generateUnmarshalJSON|Generate UnmarshalJSON method| |true|
|hideGenerationTimestamp|Hides the generation timestamp when files are generated.| |true|
|isGoSubmodule|whether the generated Go module is a submodule| |false|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,9 @@ public static enum ENUM_PROPERTY_NAMING_TYPE {camelCase, PascalCase, snake_case,

public static final String GENERATE_UNMARSHAL_JSON = "generateUnmarshalJSON";
public static final String GENERATE_UNMARSHAL_JSON_DESC = "Generate UnmarshalJSON method";

public static final String GENERATE_TYPE_ALIASES_FOR_DEDUPED_SCHEMAS = "generateTypeAliasesForDedupedSchemas";
public static final String GENERATE_TYPE_ALIASES_FOR_DEDUPED_SCHEMAS_DESC = "Generate type aliases for deduplicated inline model schemas (Go only)";
public static final String MAX_ATTEMPTS_FOR_RETRY = "maxAttemptsForRetry";

public static final String WAIT_TIME_OF_THREAD = "waitTimeMillis";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,12 @@ public class CodegenProperty implements Cloneable, IJsonSchemaValidationProperti
public boolean isDiscriminator;
public boolean isNew; // true when this property overrides an inherited property
public Boolean isOverridden; // true if the property is a parent property (not defined in child/current schema)
/**
* The type alias name to use when this property references a deduplicated inline model.
* When non-null, code generators may emit a type alias declaration.
*/
@Getter @Setter
public String dataTypeAlias;
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
@Getter @Setter
public List<String> _enum;
@Getter @Setter
Expand Down Expand Up @@ -980,6 +986,7 @@ public String toString() {
sb.append(", setter='").append(setter).append('\'');
sb.append(", description='").append(description).append('\'');
sb.append(", dataType='").append(dataType).append('\'');
sb.append(", dataTypeAlias='").append(dataTypeAlias).append('\'');
sb.append(", datatypeWithEnum='").append(datatypeWithEnum).append('\'');
sb.append(", dataFormat='").append(dataFormat).append('\'');
sb.append(", name='").append(name).append('\'');
Expand Down Expand Up @@ -1167,6 +1174,7 @@ public boolean equals(Object o) {
Objects.equals(setter, that.setter) &&
Objects.equals(description, that.description) &&
Objects.equals(dataType, that.dataType) &&
Objects.equals(dataTypeAlias, that.dataTypeAlias) &&
Objects.equals(datatypeWithEnum, that.datatypeWithEnum) &&
Objects.equals(dataFormat, that.dataFormat) &&
Objects.equals(name, that.name) &&
Expand Down Expand Up @@ -1211,7 +1219,7 @@ public boolean equals(Object o) {
public int hashCode() {

return Objects.hash(openApiType, baseName, complexType, getter, setter, description,
dataType, datatypeWithEnum, dataFormat, name, min, max, defaultValue,
dataType, dataTypeAlias, datatypeWithEnum, dataFormat, name, min, max, defaultValue,
defaultValueWithParam, baseType, containerType, containerTypeMapped, title, unescapedDescription,
maxLength, minLength, pattern, example, jsonSchema, minimum, maximum,
exclusiveMinimum, exclusiveMaximum, required, deprecated,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4252,6 +4252,15 @@ public CodegenProperty fromProperty(String name, Schema p, boolean required, boo
String type = getSchemaType(p);
setNonArrayMapProperty(property, type);
property.isModel = (ModelUtils.isComposedSchema(referencedSchema) || ModelUtils.isObjectSchema(referencedSchema)) && ModelUtils.isModel(referencedSchema);

// Check if this property is reusing a model type that was generated/deduplicated by InlineModelResolver
// InlineModelResolver marks deduplicated schemas with x-alias-name vendor extension
if (p.get$ref() != null && original != null && original.getExtensions() != null) {
String dedupedName = (String) original.getExtensions().get("x-alias-name");
if (dedupedName != null && ModelUtils.isModel(referencedSchema)) {
property.dataTypeAlias = toModelName(dedupedName);
}
}
}

// restore original schema with default value, nullable, readonly etc
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -667,6 +667,7 @@ private void flattenComposedChildren(String key, List<Schema> children, boolean
listIterator.set(schema);
} else {
Schema schema = new Schema().$ref(existing);
schema.addExtension("x-alias-name", applyInlineSchemaNameMapping(innerModelName));
schema.setRequired(component.getRequired());
listIterator.set(schema);
}
Expand Down Expand Up @@ -826,6 +827,7 @@ private void flattenProperties(OpenAPI openAPI, Map<String, Schema> properties,
String existing = matchGenerated(model);
if (existing != null) {
Schema schema = new Schema().$ref(existing);
schema.addExtension("x-alias-name", applyInlineSchemaNameMapping(modelName));
schema.setRequired(op.getRequired());
propsToUpdate.put(key, schema);
} else {
Expand All @@ -846,6 +848,7 @@ private void flattenProperties(OpenAPI openAPI, Map<String, Schema> properties,
String existing = matchGenerated(innerModel);
if (existing != null) {
Schema schema = new Schema().$ref(existing);
schema.addExtension("x-alias-name", applyInlineSchemaNameMapping(modelName));
schema.setRequired(op.getRequired());
property.setItems(schema);
} else {
Expand Down Expand Up @@ -876,6 +879,7 @@ private void flattenProperties(OpenAPI openAPI, Map<String, Schema> properties,
String existing = matchGenerated(innerModel);
if (existing != null) {
Schema schema = new Schema().$ref(existing);
schema.addExtension("x-alias-name", applyInlineSchemaNameMapping(modelName));
schema.setRequired(op.getRequired());
property.setAdditionalProperties(schema);
} else {
Expand Down Expand Up @@ -985,6 +989,19 @@ private Schema modelFromProperty(OpenAPI openAPI, Schema object, String path) {
return model;
}

/**
* Apply inlineSchemaNameMapping if configured.
*
* @param name the inline schema name to map
* @return the mapped name if mapping exists, otherwise the original name
*/
private String applyInlineSchemaNameMapping(String name) {
if (inlineSchemaNameMapping.containsKey(name)) {
return inlineSchemaNameMapping.get(name);
}
return name;
}

/**
* Move schema to components (if new) and return $ref to schema or
* existing schema.
Expand All @@ -998,6 +1015,7 @@ private Schema makeSchemaInComponents(String name, Schema schema) {
Schema refSchema;
if (existing != null) {
refSchema = new Schema().$ref(existing);
refSchema.addExtension("x-alias-name", applyInlineSchemaNameMapping(name));
} else {
if (resolveInlineEnums && schema.getEnum() != null && schema.getEnum().size() > 0) {
LOGGER.warn("Model " + name + " promoted to its own schema due to resolveInlineEnums=true");
Expand Down Expand Up @@ -1035,6 +1053,10 @@ private void copyVendorExtensions(Schema source, Schema target) {
return;
}
for (String extName : vendorExtensions.keySet()) {
// Don't overwrite x-alias-name that was set during deduplication
if ("x-alias-name".equals(extName) && target.getExtensions() != null && target.getExtensions().containsKey("x-alias-name")) {
continue;
}
target.addExtension(extName, vendorExtensions.get(extName));
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ public abstract class AbstractGoCodegen extends DefaultCodegen implements Codege
@Setter
protected boolean generateUnmarshalJSON = true;
@Setter
protected boolean generateTypeAliasesForDedupedSchemas = true;
@Setter
protected boolean useDefaultValuesForRequiredVars = false;

@Setter
Expand Down Expand Up @@ -736,6 +738,21 @@ public void postProcessModelProperty(CodegenModel model, CodegenProperty propert
property.vendorExtensions.put("x-golang-is-container", true);
}
}

private void swapDataTypeAndAlias(CodegenProperty property, Map<String, String> typeAliasesMap) {
if (property.dataTypeAlias != null) {
String dedupedType = property.dataType;
String aliasName = property.dataTypeAlias;
// Swap: field uses the alias, alias definition points to deduplicated type
property.dataType = aliasName;
property.dataTypeAlias = dedupedType;

// Collect type alias (after swap, dataType is alias name, dataTypeAlias is target)
if (!property.isContainer && !typeAliasesMap.containsKey(property.dataType)) {
typeAliasesMap.put(property.dataType, property.dataTypeAlias);
}
}
}

@Override
public ModelsMap postProcessModels(ModelsMap objs) {
Expand Down Expand Up @@ -775,7 +792,27 @@ public ModelsMap postProcessModels(ModelsMap objs) {
codegenProperties.addAll(inheritedProperties);
}

// Collect reused model properties for type alias generation
Map<String, String> typeAliasesMap = new LinkedHashMap<>();

for (CodegenProperty cp : codegenProperties) {
// Swap dataType and dataTypeAlias so fields use the alias name (only if feature is enabled)
if (generateTypeAliasesForDedupedSchemas) {
swapDataTypeAndAlias(cp, typeAliasesMap);

// Also swap for array items and update the array's dataType
if (cp.items != null && cp.items.dataTypeAlias != null) {
String oldItemsDataType = cp.items.dataType;
swapDataTypeAndAlias(cp.items, typeAliasesMap);
String newItemsDataType = cp.items.dataType;

// Update the array's dataType to use the new items dataType
if (cp.dataType != null && cp.dataType.contains(oldItemsDataType)) {
cp.dataType = cp.dataType.replace(oldItemsDataType, newItemsDataType);
}
}
}

if (!addedTimeImport && ("time.Time".equals(cp.dataType) || (cp.items != null && "time.Time".equals(cp.items.complexType)))) {
imports.add(createMapping("import", "time"));
addedTimeImport = true;
Expand Down Expand Up @@ -855,6 +892,23 @@ public ModelsMap postProcessModels(ModelsMap objs) {
if (generateUnmarshalJSON) {
model.vendorExtensions.putIfAbsent("x-go-generate-unmarshal-json", true);
}

// Convert type aliases map to list for template usage (only if feature is enabled)
if (generateTypeAliasesForDedupedSchemas && !typeAliasesMap.isEmpty()) {
List<Map<String, String>> typeAliases = new ArrayList<>();
for (Map.Entry<String, String> entry : typeAliasesMap.entrySet()) {
if (!entry.getKey().equals(entry.getValue())) {
Map<String, String> aliasMap = new HashMap<>();
aliasMap.put("aliasName", entry.getKey());
aliasMap.put("originalType", entry.getValue());
typeAliases.add(aliasMap);
}
}
if (!typeAliases.isEmpty()) {
model.vendorExtensions.put("x-go-type-aliases", typeAliases);
model.vendorExtensions.put("x-go-has-type-aliases", true);
}
}
}

// recursively add import for mapping one type to multiple imports
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ public GoClientCodegen() {
cliOptions.add(CliOption.newBoolean(WITH_GO_MOD, "Generate go.mod and go.sum", true));
cliOptions.add(CliOption.newBoolean(CodegenConstants.GENERATE_MARSHAL_JSON, CodegenConstants.GENERATE_MARSHAL_JSON_DESC, true));
cliOptions.add(CliOption.newBoolean(CodegenConstants.GENERATE_UNMARSHAL_JSON, CodegenConstants.GENERATE_UNMARSHAL_JSON_DESC, true));
cliOptions.add(CliOption.newBoolean(CodegenConstants.GENERATE_TYPE_ALIASES_FOR_DEDUPED_SCHEMAS, CodegenConstants.GENERATE_TYPE_ALIASES_FOR_DEDUPED_SCHEMAS_DESC, true));

CliOption enumUnknownDefaultCaseOpt = CliOption.newBoolean(
CodegenConstants.ENUM_UNKNOWN_DEFAULT_CASE,
Expand Down Expand Up @@ -312,6 +313,10 @@ public void processOpts() {
setGenerateUnmarshalJSON(Boolean.parseBoolean(additionalProperties.get(CodegenConstants.GENERATE_UNMARSHAL_JSON).toString()));
}

if (additionalProperties.containsKey(CodegenConstants.GENERATE_TYPE_ALIASES_FOR_DEDUPED_SCHEMAS)) {
setGenerateTypeAliasesForDedupedSchemas(Boolean.parseBoolean(additionalProperties.get(CodegenConstants.GENERATE_TYPE_ALIASES_FOR_DEDUPED_SCHEMAS).toString()));
}


// add lambda for mustache templates to handle oneOf/anyOf naming
// e.g. []string => ArrayOfString
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
{{#vendorExtensions.x-go-has-type-aliases}}
// Type aliases for deduplicated inline model schemas
{{#vendorExtensions.x-go-type-aliases}}
type {{aliasName}} = {{originalType}}
{{/vendorExtensions.x-go-type-aliases}}

{{/vendorExtensions.x-go-has-type-aliases}}
// checks if the {{classname}} type satisfies the MappedNullable interface at compile time
var _ MappedNullable = &{{classname}}{}

Expand Down
Loading