diff --git a/modules/openapi-generator-cli/src/main/java/org/openapitools/codegen/cmd/Generate.java b/modules/openapi-generator-cli/src/main/java/org/openapitools/codegen/cmd/Generate.java index 317666b7a07c..a1099caa85cc 100644 --- a/modules/openapi-generator-cli/src/main/java/org/openapitools/codegen/cmd/Generate.java +++ b/modules/openapi-generator-cli/src/main/java/org/openapitools/codegen/cmd/Generate.java @@ -181,6 +181,15 @@ public class Generate extends OpenApiGeneratorCommand { + " You can also have multiple occurrences of this option.") private List schemaMappings = new ArrayList<>(); + @Option( + name = {"--forced-generate-schemas"}, + title = "forced generate schemas", + description = "comma-separated list of schema names that must be generated even when listed " + + "in schemaMappings or importMappings. Example: MyEnum,OtherSchema." + + " Use the wildcard '*' to force-generate all mapped schemas at once." + + " You can also have multiple occurrences of this option.") + private List forcedGenerateSchemas = new ArrayList<>(); + @Option( name = {"--inline-schema-name-mappings"}, title = "inline schema name mappings", @@ -508,6 +517,7 @@ public void execute() { applyInstantiationTypesKvpList(instantiationTypes, configurator); applyImportMappingsKvpList(importMappings, configurator); applySchemaMappingsKvpList(schemaMappings, configurator); + applyForcedGenerateSchemasKvpList(forcedGenerateSchemas, configurator); applyInlineSchemaNameMappingsKvpList(inlineSchemaNameMappings, configurator); applyInlineSchemaOptionsKvpList(inlineSchemaOptions, configurator); applyNameMappingsKvpList(nameMappings, configurator); diff --git a/modules/openapi-generator-core/src/main/java/org/openapitools/codegen/config/GeneratorSettings.java b/modules/openapi-generator-core/src/main/java/org/openapitools/codegen/config/GeneratorSettings.java index 30106d484765..52b03ff06e36 100644 --- a/modules/openapi-generator-core/src/main/java/org/openapitools/codegen/config/GeneratorSettings.java +++ b/modules/openapi-generator-core/src/main/java/org/openapitools/codegen/config/GeneratorSettings.java @@ -51,6 +51,7 @@ public final class GeneratorSettings implements Serializable { private final Map additionalProperties; private final Map importMappings; private final Map schemaMappings; + private final Set forcedGenerateSchemas; private final Map inlineSchemaNameMappings; private final Map inlineSchemaOptions; private final Map nameMappings; @@ -253,6 +254,16 @@ public Map getSchemaMappings() { return schemaMappings; } + /** + * Gets the set of schema names that must be generated even when listed in schemaMappings or importMappings. + * Use {@code "*"} as a wildcard to force-generate all mapped schemas at once. + * + * @return the forced generate schemas + */ + public Set getForcedGenerateSchemas() { + return forcedGenerateSchemas; + } + /** * Gets inline schema name mappings between an inline schema name and the new name. * @@ -450,6 +461,7 @@ private GeneratorSettings(Builder builder) { typeMappings = Collections.unmodifiableMap(builder.typeMappings); importMappings = Collections.unmodifiableMap(builder.importMappings); schemaMappings = Collections.unmodifiableMap(builder.schemaMappings); + forcedGenerateSchemas = Collections.unmodifiableSet(builder.forcedGenerateSchemas); inlineSchemaNameMappings = Collections.unmodifiableMap(builder.inlineSchemaNameMappings); inlineSchemaOptions = Collections.unmodifiableMap(builder.inlineSchemaOptions); nameMappings = Collections.unmodifiableMap(builder.nameMappings); @@ -530,6 +542,7 @@ public GeneratorSettings() { additionalProperties = Collections.unmodifiableMap(new HashMap<>(0)); importMappings = Collections.unmodifiableMap(new HashMap<>(0)); schemaMappings = Collections.unmodifiableMap(new HashMap<>(0)); + forcedGenerateSchemas = Collections.unmodifiableSet(new HashSet<>(0)); inlineSchemaNameMappings = Collections.unmodifiableMap(new HashMap<>(0)); inlineSchemaOptions = Collections.unmodifiableMap(new HashMap<>(0)); nameMappings = Collections.unmodifiableMap(new HashMap<>(0)); @@ -593,6 +606,9 @@ public static Builder newBuilder(GeneratorSettings copy) { if (copy.getSchemaMappings() != null) { builder.schemaMappings.putAll(copy.getSchemaMappings()); } + if (copy.getForcedGenerateSchemas() != null) { + builder.forcedGenerateSchemas.addAll(copy.getForcedGenerateSchemas()); + } if (copy.getInlineSchemaNameMappings() != null) { builder.inlineSchemaNameMappings.putAll(copy.getInlineSchemaNameMappings()); } @@ -660,6 +676,7 @@ public static final class Builder { private Map additionalProperties; private Map importMappings; private Map schemaMappings; + private Set forcedGenerateSchemas; private Map inlineSchemaNameMappings; private Map inlineSchemaOptions; private Map nameMappings; @@ -687,6 +704,7 @@ public Builder() { additionalProperties = new HashMap<>(); importMappings = new HashMap<>(); schemaMappings = new HashMap<>(); + forcedGenerateSchemas = new HashSet<>(); inlineSchemaNameMappings = new HashMap<>(); inlineSchemaOptions = new HashMap<>(); nameMappings = new HashMap<>(); @@ -938,6 +956,35 @@ public Builder withSchemaMapping(String key, String value) { return this; } + /** + * Sets the {@code forcedGenerateSchemas} (schemas to generate even when listed in schemaMappings or importMappings). + * Use {@code "*"} as a wildcard to force-generate all mapped schemas at once. + * and returns a reference to this Builder so that the methods can be chained together. + * + * @param schemas the {@code forcedGenerateSchemas} to set + * @return a reference to this Builder + */ + public Builder withForcedGenerateSchemas(Set schemas) { + this.forcedGenerateSchemas = schemas; + return this; + } + + /** + * Adds a single schema name to {@code forcedGenerateSchemas} (schemas to generate even when listed in schemaMappings or importMappings). + * Use {@code "*"} as a wildcard to force-generate all mapped schemas at once. + * Returns a reference to this Builder so that the methods can be chained together. + * + * @param schema the schema name to add + * @return a reference to this Builder + */ + public Builder withForcedGenerateSchema(String schema) { + if (this.forcedGenerateSchemas == null) { + this.forcedGenerateSchemas = new HashSet<>(); + } + this.forcedGenerateSchemas.add(schema); + return this; + } + /** * Sets the {@code importMappings} and returns a reference to this Builder so that the methods can be chained together. * @@ -1350,6 +1397,7 @@ public String toString() { ", typeMappings=" + typeMappings + ", additionalProperties=" + additionalProperties + ", importMappings=" + importMappings + + ", forcedGenerateSchemas=" + forcedGenerateSchemas + ", languageSpecificPrimitives=" + languageSpecificPrimitives + ", openapiGeneratorIgnoreList=" + openapiGeneratorIgnoreList + ", reservedWordsMappings=" + reservedWordsMappings + @@ -1383,6 +1431,7 @@ public boolean equals(Object o) { Objects.equals(getAdditionalProperties(), that.getAdditionalProperties()) && Objects.equals(getImportMappings(), that.getImportMappings()) && Objects.equals(getSchemaMappings(), that.getSchemaMappings()) && + Objects.equals(getForcedGenerateSchemas(), that.getForcedGenerateSchemas()) && Objects.equals(getInlineSchemaNameMappings(), that.getInlineSchemaNameMappings()) && Objects.equals(getInlineSchemaOptions(), that.getInlineSchemaOptions()) && Objects.equals(getNameMappings(), that.getNameMappings()) && @@ -1421,6 +1470,7 @@ public int hashCode() { getAdditionalProperties(), getImportMappings(), getSchemaMappings(), + getForcedGenerateSchemas(), getInlineSchemaNameMappings(), getInlineSchemaOptions(), getNameMappings(), diff --git a/modules/openapi-generator-gradle-plugin/README.adoc b/modules/openapi-generator-gradle-plugin/README.adoc index b747a7c63581..6f62b97cc924 100644 --- a/modules/openapi-generator-gradle-plugin/README.adoc +++ b/modules/openapi-generator-gradle-plugin/README.adoc @@ -245,6 +245,11 @@ apply plugin: 'org.openapi.generator' |None |specifies mappings between the schema and the new name in the format of schema_a=Cat,schema_b=Bird. https://openapi-generator.tech/docs/customization/#schema-mapping +|forcedGenerateSchemas +|List / Provider +|None +|Forces generation of the named schemas even when they are listed in `schemaMappings` or `importMappings` (which would normally suppress their generation). The primary use case is producing a *reference copy* of a hand-written class — for example, a custom enum that replaces a generated one — so you can assert in a test that the hand-written version has not diverged from what the generator would produce. List of schema names, e.g. `["MyEnum", "OtherSchema"]`. Use `["*"]` as a wildcard to force-generate *all* schemas that would otherwise be suppressed by a mapping. + |nameMappings |Map / Provider |None diff --git a/modules/openapi-generator-gradle-plugin/src/main/kotlin/org/openapitools/generator/gradle/plugin/OpenApiGeneratorPlugin.kt b/modules/openapi-generator-gradle-plugin/src/main/kotlin/org/openapitools/generator/gradle/plugin/OpenApiGeneratorPlugin.kt index 901383ac6378..b4dab703b787 100644 --- a/modules/openapi-generator-gradle-plugin/src/main/kotlin/org/openapitools/generator/gradle/plugin/OpenApiGeneratorPlugin.kt +++ b/modules/openapi-generator-gradle-plugin/src/main/kotlin/org/openapitools/generator/gradle/plugin/OpenApiGeneratorPlugin.kt @@ -122,6 +122,7 @@ class OpenApiGeneratorPlugin : Plugin { openapiGeneratorIgnoreList.set(generate.openapiGeneratorIgnoreList) importMappings.set(generate.importMappings) schemaMappings.set(generate.schemaMappings) + forcedGenerateSchemas.set(generate.forcedGenerateSchemas) inlineSchemaNameMappings.set(generate.inlineSchemaNameMappings) inlineSchemaOptions.set(generate.inlineSchemaOptions) nameMappings.set(generate.nameMappings) diff --git a/modules/openapi-generator-gradle-plugin/src/main/kotlin/org/openapitools/generator/gradle/plugin/extensions/OpenApiGeneratorGenerateExtension.kt b/modules/openapi-generator-gradle-plugin/src/main/kotlin/org/openapitools/generator/gradle/plugin/extensions/OpenApiGeneratorGenerateExtension.kt index 556d6acb72a6..2ce926fcdc20 100644 --- a/modules/openapi-generator-gradle-plugin/src/main/kotlin/org/openapitools/generator/gradle/plugin/extensions/OpenApiGeneratorGenerateExtension.kt +++ b/modules/openapi-generator-gradle-plugin/src/main/kotlin/org/openapitools/generator/gradle/plugin/extensions/OpenApiGeneratorGenerateExtension.kt @@ -183,6 +183,11 @@ open class OpenApiGeneratorGenerateExtension(private val project: Project) { */ val schemaMappings = project.objects.mapProperty() + /** + * Specifies schema names that must be generated even when listed in schemaMappings or importMappings + */ + val forcedGenerateSchemas = project.objects.listProperty() + /** * Specifies mappings between an inline schema name and the new name */ diff --git a/modules/openapi-generator-gradle-plugin/src/main/kotlin/org/openapitools/generator/gradle/plugin/tasks/GenerateTask.kt b/modules/openapi-generator-gradle-plugin/src/main/kotlin/org/openapitools/generator/gradle/plugin/tasks/GenerateTask.kt index fa8be13d5cdd..bde4d9d492a5 100644 --- a/modules/openapi-generator-gradle-plugin/src/main/kotlin/org/openapitools/generator/gradle/plugin/tasks/GenerateTask.kt +++ b/modules/openapi-generator-gradle-plugin/src/main/kotlin/org/openapitools/generator/gradle/plugin/tasks/GenerateTask.kt @@ -86,6 +86,7 @@ interface OpenApiWorkParameters : WorkParameters { val instantiationTypes: MapProperty val importMappings: MapProperty val schemaMappings: MapProperty + val forcedGenerateSchemas: ListProperty val inlineSchemaNameMappings: MapProperty val inlineSchemaOptions: MapProperty val nameMappings: MapProperty @@ -204,6 +205,7 @@ abstract class OpenApiWorkAction : WorkAction { params.instantiationTypes.orNull?.forEach { (k, v) -> configurator.addInstantiationType(k, v) } params.importMappings.orNull?.forEach { (k, v) -> configurator.addImportMapping(k, v) } params.schemaMappings.orNull?.forEach { (k, v) -> configurator.addSchemaMapping(k, v) } + params.forcedGenerateSchemas.orNull?.forEach { configurator.addForcedGenerateSchema(it) } params.inlineSchemaNameMappings.orNull?.forEach { (k, v) -> configurator.addInlineSchemaNameMapping(k, v) } params.inlineSchemaOptions.orNull?.forEach { (k, v) -> configurator.addInlineSchemaOption(k, v) } params.nameMappings.orNull?.forEach { (k, v) -> configurator.addNameMapping(k, v) } @@ -549,6 +551,13 @@ abstract class GenerateTask : DefaultTask() { @get:Input abstract val schemaMappings: MapProperty + /** + * Specifies schema names that must be generated even when listed in schemaMappings or importMappings. + */ + @get:Optional + @get:Input + abstract val forcedGenerateSchemas: ListProperty + /** * Specifies mappings between the inline scheme name and the new name */ @@ -964,6 +973,7 @@ abstract class GenerateTask : DefaultTask() { parameters.instantiationTypes.set(instantiationTypes) parameters.importMappings.set(importMappings) parameters.schemaMappings.set(schemaMappings) + parameters.forcedGenerateSchemas.set(forcedGenerateSchemas) parameters.inlineSchemaNameMappings.set(inlineSchemaNameMappings) parameters.inlineSchemaOptions.set(inlineSchemaOptions) parameters.nameMappings.set(nameMappings) diff --git a/modules/openapi-generator-maven-plugin/src/main/java/org/openapitools/codegen/plugin/CodeGenMojo.java b/modules/openapi-generator-maven-plugin/src/main/java/org/openapitools/codegen/plugin/CodeGenMojo.java index 443f5b52b482..228297370bc7 100644 --- a/modules/openapi-generator-maven-plugin/src/main/java/org/openapitools/codegen/plugin/CodeGenMojo.java +++ b/modules/openapi-generator-maven-plugin/src/main/java/org/openapitools/codegen/plugin/CodeGenMojo.java @@ -351,6 +351,13 @@ public class CodeGenMojo extends AbstractMojo { @Parameter(name = "schemaMappings", property = "openapi.generator.maven.plugin.schemaMappings") private List schemaMappings; + /** + * A list of schema names that must be generated even when listed in schemaMappings or importMappings. + * Use {@code *} as a wildcard to force-generate all mapped schemas at once. + */ + @Parameter(name = "forcedGenerateSchemas", property = "openapi.generator.maven.plugin.forcedGenerateSchemas") + private List forcedGenerateSchemas; + /** * A map of inline scheme names and the new names */ @@ -824,6 +831,13 @@ public void execute() throws MojoExecutionException { configurator); } + // Retained for backwards-compatibility with configOptions -> forced-generate-schemas + if (forcedGenerateSchemas == null && configOptions.containsKey("forced-generate-schemas")) { + applyForcedGenerateSchemasKvpList( + Arrays.asList(configOptions.get("forced-generate-schemas").toString().split(",")), + configurator); + } + // Retained for backwards-compatibility with configOptions -> inline-schema-name-mappings if (inlineSchemaNameMappings == null && configOptions.containsKey("inline-schema-name-mappings")) { applyInlineSchemaNameMappingsKvp(configOptions.get("inline-schema-name-mappings").toString(), @@ -891,6 +905,11 @@ public void execute() throws MojoExecutionException { applySchemaMappingsKvpList(schemaMappings, configurator); } + // Apply Forced Generate Schemas + if (forcedGenerateSchemas != null && (configOptions == null || !configOptions.containsKey("forced-generate-schemas"))) { + applyForcedGenerateSchemasKvpList(forcedGenerateSchemas, configurator); + } + // Apply Inline Schema Name Mappings if (inlineSchemaNameMappings != null && (configOptions == null || !configOptions.containsKey("inline-schema-name-mappings"))) { applyInlineSchemaNameMappingsKvpList(inlineSchemaNameMappings, configurator); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConfig.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConfig.java index 27cd5b4ab0bd..bc775c432d7f 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConfig.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConfig.java @@ -146,6 +146,15 @@ public interface CodegenConfig { Map schemaMapping(); + /** + * Returns the set of schema names that must be generated even when they appear in + * schemaMappings or importMappings (which would normally suppress their generation). + *

+ * Use {@link CodegenConstants#FORCE_GENERATE_ALL_SCHEMAS} ({@code "*"}) as a wildcard + * to force-generate all mapped schemas at once. + */ + Set forcedGenerateSchemas(); + Map inlineSchemaNameMapping(); Map inlineSchemaOption(); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConstants.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConstants.java index 65511f68f671..05e8d60fac88 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConstants.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConstants.java @@ -36,6 +36,12 @@ public class CodegenConstants { public static final String SKIP_FORM_MODEL = "skipFormModel"; /* /end System Properties */ + /** + * Wildcard token for {@code forcedGenerateSchemas}: when this value is present in the set, + * all schemas are generated even if they appear in schemaMappings or importMappings. + */ + public static final String FORCE_GENERATE_ALL_SCHEMAS = "*"; + public static final String API_NAME = "apiName"; public static final String API_PACKAGE = "apiPackage"; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index d000cde76814..05378832726d 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -179,6 +179,9 @@ public class DefaultCodegen implements CodegenConfig { protected Map importMapping = new HashMap<>(); // a map to store the mapping between a schema and the new one protected Map schemaMapping = new HashMap<>(); + // a set of schema names that must be generated even when listed in schemaMappings or importMappings. + // Use CodegenConstants.FORCE_GENERATE_ALL_SCHEMAS ("*") to force-generate all mapped schemas. + protected Set forcedGenerateSchemas = new HashSet<>(); // a map to store the mapping between inline schema and the name provided by the user protected Map inlineSchemaNameMapping = new HashMap<>(); // a map to store the inline schema naming conventions @@ -1302,6 +1305,11 @@ public Map schemaMapping() { return schemaMapping; } + @Override + public Set forcedGenerateSchemas() { + return forcedGenerateSchemas; + } + @Override public Map inlineSchemaNameMapping() { return inlineSchemaNameMapping; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java index 60b17e8e47d5..941a3cf4d0e3 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java @@ -404,6 +404,17 @@ private void generateModelTests(List files, Map models, St } } + /** + * Returns {@code true} if the named schema should be generated even when it appears in + * schemaMappings or importMappings. This is the case when the schema name is explicitly + * listed in {@code forcedGenerateSchemas} or when the wildcard + * {@link CodegenConstants#FORCE_GENERATE_ALL_SCHEMAS} ({@code "*"}) is present. + */ + private boolean isNotForcedGenerate(String schemaName) { + return !config.forcedGenerateSchemas().contains(CodegenConstants.FORCE_GENERATE_ALL_SCHEMAS) + && !config.forcedGenerateSchemas().contains(schemaName); + } + private void generateModelDocumentation(List files, Map models, String modelName) throws IOException { for (String templateName : config.modelDocTemplateFiles().keySet()) { String docExtension = config.getDocExtension(); @@ -467,8 +478,8 @@ void generateModels(List files, List allModels, List unu for (String name : modelKeys) { processedModels.add(name); try { - //don't generate models that have an import mapping - if (config.schemaMapping().containsKey(name)) { + //don't generate models that have an import mapping or are in the list of schemas to always generate + if (config.schemaMapping().containsKey(name) && isNotForcedGenerate(name)) { LOGGER.info("Model {} not generated due to schema mapping", name); continue; } @@ -549,8 +560,8 @@ void generateModels(List files, List allModels, List unu ModelsMap models = allProcessedModels.get(modelName); models.put("modelPackage", config.modelPackage()); try { - //don't generate models that have a schema mapping - if (config.schemaMapping().containsKey(modelName)) { + //don't generate models that have a schema mapping or are in the list of schemas to always generate + if (config.schemaMapping().containsKey(modelName) && isNotForcedGenerate(modelName)) { continue; } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/config/CodegenConfigurator.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/config/CodegenConfigurator.java index a3597fa37f2e..57053a30791b 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/config/CodegenConfigurator.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/config/CodegenConfigurator.java @@ -69,6 +69,7 @@ public class CodegenConfigurator { private Map additionalProperties = new HashMap<>(); private Map importMappings = new HashMap<>(); private Map schemaMappings = new HashMap<>(); + private Set forcedGenerateSchemas = new HashSet<>(); private Map inlineSchemaNameMappings = new HashMap<>(); private Map inlineSchemaOptions = new HashMap<>(); private Map nameMappings = new HashMap<>(); @@ -124,6 +125,9 @@ public static CodegenConfigurator fromFile(String configFile, Module... modules) if (generatorSettings.getSchemaMappings() != null) { configurator.schemaMappings.putAll(generatorSettings.getSchemaMappings()); } + if (generatorSettings.getForcedGenerateSchemas() != null) { + configurator.forcedGenerateSchemas.addAll(generatorSettings.getForcedGenerateSchemas()); + } if (generatorSettings.getInlineSchemaNameMappings() != null) { configurator.inlineSchemaNameMappings.putAll(generatorSettings.getInlineSchemaNameMappings()); } @@ -226,6 +230,29 @@ public CodegenConfigurator addSchemaMapping(String key, String value) { return this; } + /** + * Adds a single schema name to {@code forcedGenerateSchemas}. + * Schemas in this set are generated even when they appear in schemaMappings or importMappings. + * Use {@code "*"} ({@link CodegenConstants#FORCE_GENERATE_ALL_SCHEMAS}) to force-generate + * all mapped schemas at once. + */ + public CodegenConfigurator addForcedGenerateSchema(String schema) { + this.forcedGenerateSchemas.add(schema); + generatorSettingsBuilder.withForcedGenerateSchema(schema); + return this; + } + + /** + * Replaces the entire {@code forcedGenerateSchemas} set. + * Use {@code "*"} ({@link CodegenConstants#FORCE_GENERATE_ALL_SCHEMAS}) as a wildcard + * to force-generate all mapped schemas at once. + */ + public CodegenConfigurator setForcedGenerateSchemas(Set schemas) { + this.forcedGenerateSchemas = schemas; + generatorSettingsBuilder.withForcedGenerateSchemas(schemas); + return this; + } + public CodegenConfigurator addInlineSchemaNameMapping(String key, String value) { this.inlineSchemaNameMappings.put(key, value); generatorSettingsBuilder.withInlineSchemaNameMapping(key, value); @@ -773,6 +800,7 @@ public ClientOptInput toClientOptInput() { config.typeMapping().putAll(generatorSettings.getTypeMappings()); config.importMapping().putAll(generatorSettings.getImportMappings()); config.schemaMapping().putAll(generatorSettings.getSchemaMappings()); + config.forcedGenerateSchemas().addAll(generatorSettings.getForcedGenerateSchemas()); config.inlineSchemaNameMapping().putAll(generatorSettings.getInlineSchemaNameMappings()); config.inlineSchemaOption().putAll(generatorSettings.getInlineSchemaOptions()); config.nameMapping().putAll(generatorSettings.getNameMappings()); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/config/CodegenConfiguratorUtils.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/config/CodegenConfiguratorUtils.java index 42f423984060..3a0391da3adc 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/config/CodegenConfiguratorUtils.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/config/CodegenConfiguratorUtils.java @@ -94,6 +94,12 @@ public static void applySchemaMappingsKvp(String schemaMappings, CodegenConfigur } } + public static void applyForcedGenerateSchemasKvpList(List schemas, CodegenConfigurator configurator) { + for (String schema : schemas) { + configurator.addForcedGenerateSchema(schema.trim()); + } + } + public static void applyInlineSchemaNameMappingsKvpList(List inlineSchemaNameMappings, CodegenConfigurator configurator) { for (String propString : inlineSchemaNameMappings) { applyInlineSchemaNameMappingsKvp(propString, configurator); diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/DefaultGeneratorTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/DefaultGeneratorTest.java index 1ba9a2c7c5c6..9f0e14c1a2b1 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/DefaultGeneratorTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/DefaultGeneratorTest.java @@ -344,6 +344,130 @@ public void supportCustomTemplateEngine() throws IOException { } } + /** + * Verifies that a schema listed in schemaMappings is skipped by default, but is generated + * when it also appears in forcedGenerateSchemas. + * + * When a schema is in schemaMappings, the generator renames the model using the mapped value. + * For example, mapping "Category" -> "ExternalCategory" means the generated file is + * ExternalCategory.java. Part 2 verifies that this file IS written when forcedGenerateSchemas + * contains "Category", whereas Part 1 verifies that NO such file exists without it. + */ + @Test + public void forcedGenerateSchemaOverridesSchemaMappingSkip() throws IOException { + // Using a simple (non-FQN) mapped name so the generated filename is predictable. + final String mappedModelRelPath = "src/main/java/org/openapitools/client/model/ExternalCategory.java"; + final String originalModelRelPath = "src/main/java/org/openapitools/client/model/Category.java"; + + // --- Part 1: schemaMapping alone must suppress generation of Category entirely --- + Path target1 = Files.createTempDirectory("test-forced-gen-skip"); + try { + final CodegenConfigurator configurator = new CodegenConfigurator() + .setGeneratorName("java") + .setInputSpec("src/test/resources/3_0/petstore.yaml") + .setOutputDir(target1.toAbsolutePath().toString()) + .addSchemaMapping("Category", "ExternalCategory"); + + final ClientOptInput clientOptInput = configurator.toClientOptInput(); + DefaultGenerator generator = new DefaultGenerator(false); + generator.setGeneratorPropertyDefault(CodegenConstants.MODELS, "true"); + generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_TESTS, "false"); + generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_DOCS, "false"); + generator.setGeneratorPropertyDefault(CodegenConstants.APIS, "false"); + generator.setGeneratorPropertyDefault(CodegenConstants.SUPPORTING_FILES, "false"); + + List files = generator.opts(clientOptInput).generate(); + + Assert.assertFalse( + files.stream().anyMatch(f -> f.getPath().replace('\\', '/').endsWith(originalModelRelPath)), + "Category.java must NOT be generated when it is in schemaMappings"); + Assert.assertFalse( + files.stream().anyMatch(f -> f.getPath().replace('\\', '/').endsWith(mappedModelRelPath)), + "ExternalCategory.java must NOT be generated when Category is in schemaMappings"); + } finally { + target1.toFile().deleteOnExit(); + } + + // --- Part 2: forcedGenerateSchemas must force generation despite schemaMapping --- + // The Java generator resolves the model name through schemaMapping (Category -> ExternalCategory), + // so the output file is ExternalCategory.java, not Category.java. + Path target2 = Files.createTempDirectory("test-forced-gen-force"); + try { + final CodegenConfigurator configurator = new CodegenConfigurator() + .setGeneratorName("java") + .setInputSpec("src/test/resources/3_0/petstore.yaml") + .setOutputDir(target2.toAbsolutePath().toString()) + .addSchemaMapping("Category", "ExternalCategory") + .addForcedGenerateSchema("Category"); + + final ClientOptInput clientOptInput = configurator.toClientOptInput(); + + Assert.assertTrue( + clientOptInput.getConfig().forcedGenerateSchemas().contains("Category"), + "forcedGenerateSchemas must be wired to the config by toClientOptInput()"); + + DefaultGenerator generator = new DefaultGenerator(false); + generator.setGeneratorPropertyDefault(CodegenConstants.MODELS, "true"); + generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_TESTS, "false"); + generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_DOCS, "false"); + generator.setGeneratorPropertyDefault(CodegenConstants.APIS, "false"); + generator.setGeneratorPropertyDefault(CodegenConstants.SUPPORTING_FILES, "false"); + + List files = generator.opts(clientOptInput).generate(); + + Assert.assertTrue( + files.stream().anyMatch(f -> f.getPath().replace('\\', '/').endsWith(mappedModelRelPath)), + "ExternalCategory.java MUST be generated when Category is in both schemaMappings and forcedGenerateSchemas"); + Assert.assertTrue( + new File(target2.toFile(), mappedModelRelPath).exists(), + "ExternalCategory.java MUST exist on disk when forcedGenerateSchemas overrides schemaMappings"); + } finally { + target2.toFile().deleteOnExit(); + } + + // --- Part 3: wildcard "*" must force-generate ALL schemas suppressed by schemaMappings --- + // Two schemas are mapped (Category->ExternalCategory, Tag->ExternalTag). + // Adding only "*" (FORCE_GENERATE_ALL_SCHEMAS) to forcedGenerateSchemas must cause both to be generated. + Path target3 = Files.createTempDirectory("test-forced-gen-wildcard"); + try { + final CodegenConfigurator configurator = new CodegenConfigurator() + .setGeneratorName("java") + .setInputSpec("src/test/resources/3_0/petstore.yaml") + .setOutputDir(target3.toAbsolutePath().toString()) + .addSchemaMapping("Category", "ExternalCategory") + .addSchemaMapping("Tag", "ExternalTag") + .addForcedGenerateSchema(CodegenConstants.FORCE_GENERATE_ALL_SCHEMAS); + + final ClientOptInput clientOptInput = configurator.toClientOptInput(); + DefaultGenerator generator = new DefaultGenerator(false); + generator.setGeneratorPropertyDefault(CodegenConstants.MODELS, "true"); + generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_TESTS, "false"); + generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_DOCS, "false"); + generator.setGeneratorPropertyDefault(CodegenConstants.APIS, "false"); + generator.setGeneratorPropertyDefault(CodegenConstants.SUPPORTING_FILES, "false"); + + List files = generator.opts(clientOptInput).generate(); + + final String externalCategoryRelPath = "src/main/java/org/openapitools/client/model/ExternalCategory.java"; + final String externalTagRelPath = "src/main/java/org/openapitools/client/model/ExternalTag.java"; + + Assert.assertTrue( + files.stream().anyMatch(f -> f.getPath().replace('\\', '/').endsWith(externalCategoryRelPath)), + "ExternalCategory.java MUST be generated when wildcard \"*\" is in forcedGenerateSchemas"); + Assert.assertTrue( + files.stream().anyMatch(f -> f.getPath().replace('\\', '/').endsWith(externalTagRelPath)), + "ExternalTag.java MUST be generated when wildcard \"*\" is in forcedGenerateSchemas"); + Assert.assertTrue( + new File(target3.toFile(), externalCategoryRelPath).exists(), + "ExternalCategory.java MUST exist on disk when wildcard \"*\" is used"); + Assert.assertTrue( + new File(target3.toFile(), externalTagRelPath).exists(), + "ExternalTag.java MUST exist on disk when wildcard \"*\" is used"); + } finally { + target3.toFile().deleteOnExit(); + } + } + @Test public void testNonStrictProcessPaths() throws Exception { OpenAPI openAPI = TestUtils.createOpenAPI();