diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenDiscriminator.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenDiscriminator.java
index d995668041a5..753c63e3fc8e 100644
--- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenDiscriminator.java
+++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenDiscriminator.java
@@ -88,6 +88,10 @@ public MappedModel(String mappingName, String modelName, boolean explicitMapping
this.explicitMapping = explicitMapping;
}
+ public boolean isExplicitMapping() {
+ return explicitMapping;
+ }
+
public MappedModel(String mappingName, String modelName) {
this(mappingName, modelName, false);
}
diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ProtobufSchemaCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ProtobufSchemaCodegen.java
index 4793dae5f9f9..9e1b641b6e9d 100644
--- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ProtobufSchemaCodegen.java
+++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ProtobufSchemaCodegen.java
@@ -24,6 +24,7 @@
import lombok.Setter;
import org.apache.commons.lang3.StringUtils;
import org.openapitools.codegen.*;
+import org.openapitools.codegen.CodegenDiscriminator.MappedModel;
import org.openapitools.codegen.exceptions.ProtoBufIndexComputationException;
import org.openapitools.codegen.meta.GeneratorMetadata;
import org.openapitools.codegen.meta.Stability;
@@ -76,6 +77,24 @@ public class ProtobufSchemaCodegen extends DefaultCodegen implements CodegenConf
public static final String SUPPORT_MULTIPLE_RESPONSES = "supportMultipleResponses";
+ public static final String EXTRACT_ENUMS_TO_SEPARATE_FILES = "extractEnumsToSeparateFiles";
+
+ /**
+ * The inner enum name used when wrapping extracted enums in message containers.
+ * This prevents enum value name collisions in the Protocol Buffers global namespace.
+ */
+ public static final String ENUM_WRAPPER_INNER_NAME = "Enum";
+
+ /**
+ * Vendor extension key indicating that an enum property has been extracted to a separate file.
+ */
+ private static final String VENDOR_EXT_ENUM_EXTRACTED = "x-protobuf-enum-extracted-to-file";
+
+ /**
+ * Vendor extension key for the wrapper message name of an extracted enum.
+ */
+ private static final String VENDOR_EXT_ENUM_WRAPPER_MESSAGE = "x-protobuf-enum-wrapper-message";
+
private final Logger LOGGER = LoggerFactory.getLogger(ProtobufSchemaCodegen.class);
@Setter protected String packageName = "openapitools";
@@ -100,6 +119,8 @@ public class ProtobufSchemaCodegen extends DefaultCodegen implements CodegenConf
private boolean supportMultipleResponses = true;
+ private boolean extractEnumsToSeparateFiles = false;
+
@Override
public CodegenType getTag() {
return CodegenType.SCHEMA;
@@ -139,7 +160,7 @@ public ProtobufSchemaCodegen() {
apiTemplateFiles.put("api.mustache", ".proto");
embeddedTemplateDir = templateDir = "protobuf-schema";
hideGenerationTimestamp = Boolean.TRUE;
- modelPackage = "models";
+ super.setModelPackage("models");
apiPackage = "services";
defaultIncludes = new HashSet<>(
@@ -212,6 +233,7 @@ public ProtobufSchemaCodegen() {
addSwitch(WRAP_COMPLEX_TYPE, "Generate Additional message for complex type", wrapComplexType);
addSwitch(USE_SIMPLIFIED_ENUM_NAMES, "Use a simple name for enums", useSimplifiedEnumNames);
addSwitch(SUPPORT_MULTIPLE_RESPONSES, "Support multiple responses", supportMultipleResponses);
+ addSwitch(EXTRACT_ENUMS_TO_SEPARATE_FILES, "Extract enums to separate protobuf files and import them in models", extractEnumsToSeparateFiles);
addOption(AGGREGATE_MODELS_NAME, "Aggregated model filename. If set, all generated models will be combined into this single file.", null);
addOption(CUSTOM_OPTIONS_API, "Custom options for the api files.", null);
addOption(CUSTOM_OPTIONS_MODEL, "Custom options for the model files.", null);
@@ -279,6 +301,12 @@ public void processOpts() {
additionalProperties.put(this.SUPPORT_MULTIPLE_RESPONSES, this.supportMultipleResponses);
}
+ if (additionalProperties.containsKey(EXTRACT_ENUMS_TO_SEPARATE_FILES)) {
+ this.extractEnumsToSeparateFiles = convertPropertyToBooleanAndWriteBack(EXTRACT_ENUMS_TO_SEPARATE_FILES);
+ } else {
+ additionalProperties.put(EXTRACT_ENUMS_TO_SEPARATE_FILES, this.extractEnumsToSeparateFiles);
+ }
+
supportingFiles.add(new SupportingFile("README.mustache", "", "README.md"));
}
@@ -628,7 +656,37 @@ public String getNameFromDataType(CodegenProperty property) {
}
}
-
+ /**
+ * Post-processes CodegenModel objects to apply protobuf-specific transformations.
+ *
+ *
This method performs several critical operations:
+ *
+ * - Processes enum definitions and optionally extracts them to separate files
+ * - Sets vendor extensions for protobuf field types and data types
+ * - Assigns field numbers based on configuration
+ * - Handles oneOf/anyOf composition schemas
+ *
+ *
+ * Enum Extraction Behavior: When {@code extractEnumsToSeparateFiles} is enabled,
+ * inline enum properties are extracted to separate .proto files wrapped in message containers.
+ * This prevents enum value name collisions in the protobuf global namespace. The resulting
+ * format is:
+ *
+ * message EnumName {
+ * enum Enum {
+ * VALUE1 = 0;
+ * VALUE2 = 1;
+ * }
+ * }
+ *
+ *
+ * References to these enums use the qualified name {@code EnumName.Enum}.
+ *
+ * @param objs the models map containing all CodegenModel objects
+ * @return the modified models map with protobuf-specific transformations applied
+ * @see #extractEnumsToSeparateFiles(ModelsMap)
+ * @see #extractEnums(ModelsMap)
+ */
@Override
public ModelsMap postProcessModels(ModelsMap objs) {
objs = postProcessModelsEnum(objs);
@@ -652,52 +710,118 @@ public ModelsMap postProcessModels(ModelsMap objs) {
cm.vars = processOneOfAnyOfItems(cm.getComposedSchemas().getAnyOf());
}
int index = 1;
- for (CodegenProperty var : cm.vars) {
+ for (CodegenProperty property : cm.vars) {
// add x-protobuf-type: repeated if it's an array
- if (Boolean.TRUE.equals(var.isArray)) {
- var.vendorExtensions.put("x-protobuf-type", "repeated");
- } else if (Boolean.TRUE.equals(var.isNullable && var.isPrimitiveType)) {
- var.vendorExtensions.put("x-protobuf-type", "optional");
+ if (Boolean.TRUE.equals(property.isArray)) {
+ property.vendorExtensions.put("x-protobuf-type", "repeated");
+ } else if (Boolean.TRUE.equals(property.isNullable && property.isPrimitiveType)) {
+ property.vendorExtensions.put("x-protobuf-type", "optional");
}
// add x-protobuf-data-type
// ref: https://developers.google.com/protocol-buffers/docs/proto3
- if (!var.vendorExtensions.containsKey("x-protobuf-data-type")) {
- if (var.isArray) {
- var.vendorExtensions.put("x-protobuf-data-type", var.items.dataType);
+ if (!property.vendorExtensions.containsKey("x-protobuf-data-type")) {
+ if (property.isArray) {
+ property.vendorExtensions.put("x-protobuf-data-type", property.items.dataType);
} else {
- var.vendorExtensions.put("x-protobuf-data-type", var.dataType);
+ property.vendorExtensions.put("x-protobuf-data-type", property.dataType);
}
}
- if (var.isEnum) {
- addUnspecifiedToAllowableValues(var.allowableValues);
- addEnumValuesPrefix(var.allowableValues, var.getEnumName());
-
- if (var.allowableValues.containsKey("enumVars")) {
- List