From ce5015491ab8dba53e30b1188c34285d8105db3e Mon Sep 17 00:00:00 2001 From: Mattias-Sehlstedt <60173714+Mattias-Sehlstedt@users.noreply.github.com> Date: Sat, 4 Jul 2026 14:00:15 +0200 Subject: [PATCH] refactor: simplify discriminatorFound logic and move it to the DiscriminatorUtils --- .../openapitools/codegen/DefaultCodegen.java | 120 +------------ .../codegen/utils/DiscriminatorUtils.java | 158 +++++++++++++++--- 2 files changed, 141 insertions(+), 137 deletions(-) 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 006bdc827c08..bb56a83e86c7 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 @@ -3343,110 +3343,6 @@ protected void setAddProps(Schema schema, IJsonSchemaValidationProperties proper } } - /** - * Recursively look in Schema sc for the discriminator discPropName - * and return a CodegenProperty with the dataType and required params set - * the returned CodegenProperty may not be required and it may not be of type string - * - * @param composedSchemaName The name of the sc Schema - * @param sc The Schema that may contain the discriminator - * @param discPropName The String that is the discriminator propertyName in the schema - * @param visitedSchemas A set of visited schema names - */ - private CodegenProperty discriminatorFound(String composedSchemaName, Schema sc, String discPropName, Set visitedSchemas) { - Schema refSchema = ModelUtils.getReferencedSchema(openAPI, sc); - String schemaName = Optional.ofNullable(composedSchemaName) - .or(() -> Optional.ofNullable(refSchema.getName())) - .or(() -> Optional.ofNullable(sc.get$ref()).map(ModelUtils::getSimpleRef)) - .orElseGet(sc::toString); - if (visitedSchemas.contains(schemaName)) { // recursive schema definition found - return null; - } else { - visitedSchemas.add(schemaName); - } - - if (refSchema.getProperties() != null && refSchema.getProperties().get(discPropName) != null) { - Schema discSchema = ModelUtils.getReferencedSchema(openAPI, getDiscriminatorSchema(refSchema, discPropName)); - CodegenProperty cp = new CodegenProperty(); - if (ModelUtils.isStringSchema(discSchema)) { - cp.isString = true; - } - cp.setRequired(false); - if (refSchema.getRequired() != null && refSchema.getRequired().contains(discPropName)) { - cp.setRequired(true); - } - cp.setIsEnum(discSchema.getEnum() != null && !discSchema.getEnum().isEmpty()); - return cp; - } - if (ModelUtils.isComposedSchema(refSchema)) { - Schema composedSchema = refSchema; - if (composedSchema.getAllOf() != null) { - // If our discriminator is in one of the allOf schemas break when we find it - for (Object allOf : composedSchema.getAllOf()) { - Schema allOfSchema = (Schema) allOf; - CodegenProperty cp = discriminatorFound(allOfSchema.getName(), allOfSchema, discPropName, visitedSchemas); - if (cp != null) { - return cp; - } - } - } - if (ModelUtils.hasOneOf(composedSchema)) { - // All oneOf definitions must contain the discriminator - CodegenProperty cp = new CodegenProperty(); - for (Object oneOf : composedSchema.getOneOf()) { - Schema oneOfSchema = (Schema) oneOf; - String modelName = ModelUtils.getSimpleRef((oneOfSchema).get$ref()); - // Must use a copied set as the oneOf schemas can point to the same discriminator. - Set visitedSchemasCopy = new TreeSet<>(visitedSchemas); - CodegenProperty thisCp = discriminatorFound(oneOfSchema.getName(), oneOfSchema, discPropName, visitedSchemasCopy); - if (thisCp == null) { - once(LOGGER).warn( - "'{}' defines discriminator '{}', but the referenced OneOf schema '{}' is missing {}", - composedSchemaName, discPropName, modelName, discPropName); - } - if (cp != null && cp.dataType == null) { - cp = thisCp; - continue; - } - if (cp != thisCp) { - once(LOGGER).warn( - "'{}' defines discriminator '{}', but the OneOf schema '{}' has a different {} definition than the prior OneOf schema's. Make sure the {} type and required values are the same", - composedSchemaName, discPropName, modelName, discPropName, discPropName); - } - } - return cp; - } - if (ModelUtils.hasAnyOf(composedSchema)) { - // All anyOf definitions must contain the discriminator because a min of one must be selected - CodegenProperty cp = new CodegenProperty(); - for (Object anyOf : composedSchema.getAnyOf()) { - Schema anyOfSchema = (Schema) anyOf; - String modelName = ModelUtils.getSimpleRef(anyOfSchema.get$ref()); - // Must use a copied set as the anyOf schemas can point to the same discriminator. - Set visitedSchemasCopy = new TreeSet<>(visitedSchemas); - CodegenProperty thisCp = discriminatorFound(anyOfSchema.getName(), anyOfSchema, discPropName, visitedSchemasCopy); - if (thisCp == null) { - once(LOGGER).warn( - "'{}' defines discriminator '{}', but the referenced AnyOf schema '{}' is missing {}", - composedSchemaName, discPropName, modelName, discPropName); - } - if (cp != null && cp.dataType == null) { - cp = thisCp; - continue; - } - if (cp != thisCp) { - once(LOGGER).warn( - "'{}' defines discriminator '{}', but the AnyOf schema '{}' has a different {} definition than the prior AnyOf schema's. Make sure the {} type and required values are the same", - composedSchemaName, discPropName, modelName, discPropName, discPropName); - } - } - return cp; - - } - } - return null; - } - /** * Recursively look in Schema sc for the discriminator and return it * @@ -3495,7 +3391,7 @@ protected List getOneOfAnyOfDescendants(String composedSchemaName, "Invalid inline schema defined in oneOf/anyOf in '{}'. Per the OpenApi spec, for this case when a composed schema defines a discriminator, the oneOf/anyOf schemas must use $ref. Change this inline definition to a $ref definition", composedSchemaName); } - CodegenProperty df = discriminatorFound(composedSchemaName, sc, discPropName, new TreeSet()); + CodegenProperty df = DiscriminatorUtils.discriminatorFound(openAPI, composedSchemaName, sc, discPropName, new TreeSet()); String modelName = ModelUtils.getSimpleRef(ref); if (df == null || !df.isString || !df.required) { String msgSuffix = ""; @@ -3611,7 +3507,7 @@ protected CodegenDiscriminator createDiscriminator(String schemaName, Schema sch // check to see if the discriminator property is an enum string boolean isEnum = Optional - .ofNullable(discriminatorFound(schemaName, schema, discriminatorPropertyName, new TreeSet<>())) + .ofNullable(DiscriminatorUtils.discriminatorFound(openAPI, schemaName, schema, discriminatorPropertyName, new TreeSet<>())) .map(CodegenProperty::getIsEnum) .orElse(false); discriminator.setIsEnum(isEnum); @@ -3671,18 +3567,6 @@ protected CodegenDiscriminator createDiscriminator(String schemaName, Schema sch return discriminator; } - /** - * Get the Schema for the discriminator type. Requires special handling due to siblings from OAS 3.1. - * An example of a sibling is an enum-ref that has its own description. This will lead to the enum being - * referenced as an allOf that in turn has a ref, rather than a regular ref directly to the enum. - * - * @param schema The input OAS schema. - * @param discriminatorName The name of the discriminator property. - */ - protected Schema getDiscriminatorSchema(Schema schema, String discriminatorName) { - return DiscriminatorUtils.getDiscriminatorSchema(schema, discriminatorName); - } - /** * Get the property type for the discriminator * diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java index 5ed6f5b0761c..43ca035cc17e 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java @@ -3,6 +3,7 @@ import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.media.Discriminator; import io.swagger.v3.oas.models.media.Schema; +import org.openapitools.codegen.CodegenProperty; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -16,6 +17,91 @@ public class DiscriminatorUtils { private static final String CONFLICTING_DISCRIMINATOR_NAMES = "The alternative schemas have conflicting discriminator property names. The schemas must have the same property name, but found {}"; + private static final String DEFINES_DISCRIMINATOR_BUT_REFERENCE_ALTERNATIVE_IS_MISSING = + "'{}' defines discriminator '{}', but the referenced schema '{}' is missing {}"; + private static final String DEFINES_DISCRIMINATOR_BUT_ALTERNATIVE_HAS_OTHER_DEFINITION = + "'{}' defines discriminator '{}', but the schema '{}' has a different {} definition than the prior schema's. Make sure the {} type and required values are the same"; + + /** + * Recursively look in Schema sc for the discriminator discPropName + * and return a CodegenProperty with the dataType and required params set + * the returned CodegenProperty may not be required, and it may not be of type string + * + * @param openAPI The openAPI specification + * @param composedSchemaName The name of the sc Schema + * @param sc The Schema that may contain the discriminator + * @param discPropName The String that is the discriminator propertyName in the schema + * @param visitedSchemas A set of visited schema names + */ + public static CodegenProperty discriminatorFound(OpenAPI openAPI, + String composedSchemaName, + Schema sc, + String discPropName, + Set visitedSchemas) { + Schema refSchema = ModelUtils.getReferencedSchema(openAPI, sc); + String schemaName = Optional.ofNullable(composedSchemaName) + .or(() -> Optional.ofNullable(refSchema.getName())) + .or(() -> Optional.ofNullable(sc.get$ref()).map(ModelUtils::getSimpleRef)) + .orElseGet(sc::toString); + if (visitedSchemas.contains(schemaName)) { // recursive schema definition found + return null; + } else { + visitedSchemas.add(schemaName); + } + + if (refSchema.getProperties() != null && refSchema.getProperties().get(discPropName) != null) { + Schema discSchema = ModelUtils.getReferencedSchema(openAPI, getDiscriminatorSchema(refSchema, discPropName)); + CodegenProperty cp = new CodegenProperty(); + if (ModelUtils.isStringSchema(discSchema)) { + cp.isString = true; + } + cp.setRequired(false); + if (refSchema.getRequired() != null && refSchema.getRequired().contains(discPropName)) { + cp.setRequired(true); + } + cp.setIsEnum(discSchema.getEnum() != null && !discSchema.getEnum().isEmpty()); + return cp; + } + if (ModelUtils.isComposedSchema(refSchema)) { + Schema composedSchema = refSchema; + if (composedSchema.getAllOf() != null) { + // If our discriminator is in one of the allOf schemas break when we find it + for (Object allOf : composedSchema.getAllOf()) { + Schema allOfSchema = (Schema) allOf; + CodegenProperty cp = discriminatorFound(openAPI, allOfSchema.getName(), allOfSchema, discPropName, visitedSchemas); + if (cp != null) { + return cp; + } + } + } + if (ModelUtils.hasOneOf(composedSchema)) { + // All oneOf definitions must contain the discriminator + CodegenProperty cp = new CodegenProperty(); + for (Object oneOf : composedSchema.getOneOf()) { + Schema oneOfSchema = (Schema) oneOf; + CodegenProperty discCP = getDiscriminatorCodegenProperty(openAPI, cp, composedSchemaName, oneOfSchema, discPropName, visitedSchemas); + if (discCP != null) { + cp = discCP; + } + } + return cp; + } + if (ModelUtils.hasAnyOf(composedSchema)) { + // All anyOf definitions must contain the discriminator because a min of one must be selected + CodegenProperty cp = new CodegenProperty(); + for (Object anyOf : composedSchema.getAnyOf()) { + Schema anyOfSchema = (Schema) anyOf; + CodegenProperty discCP = getDiscriminatorCodegenProperty(openAPI, cp, composedSchemaName, anyOfSchema, discPropName, visitedSchemas); + if (discCP != null) { + cp = discCP; + } + } + return cp; + + } + } + return null; + } /** * Gets the simple ref name of the discriminator property type from the schema. @@ -30,25 +116,6 @@ public static Optional getDiscriminatorPropertyType(Schema schema, Strin .map(ModelUtils::getSimpleRef); } - /** - * Get the Schema for the discriminator type. Requires special handling due to siblings from OAS 3.1. - * An example of a sibling is an enum-ref that has its own description. This will lead to the enum being - * referenced as an allOf that in turn has a ref, rather than a regular ref directly to the enum. - * - * @param schema The input OAS schema. - * @param discriminatorName The name of the discriminator property. - */ - public static Schema getDiscriminatorSchema(Schema schema, String discriminatorName) { - if (schema.getProperties() == null) { - return null; - } - Schema discSchema = (Schema) schema.getProperties().get(discriminatorName); - if (ModelUtils.isAllOf(discSchema)) { - discSchema = (Schema) discSchema.getAllOf().get(0); - } - return discSchema; - } - /** * Recursively look in Schema sc for the discriminator and return it * @@ -106,6 +173,25 @@ public static Discriminator recursiveGetDiscriminator( return null; } + /** + * Get the Schema for the discriminator type. Requires special handling due to siblings from OAS 3.1. + * An example of a sibling is an enum-ref that has its own description. This will lead to the enum being + * referenced as an allOf that in turn has a ref, rather than a regular ref directly to the enum. + * + * @param schema The input OAS schema. + * @param discriminatorName The name of the discriminator property. + */ + private static Schema getDiscriminatorSchema(Schema schema, String discriminatorName) { + if (schema.getProperties() == null) { + return null; + } + Schema discSchema = (Schema) schema.getProperties().get(discriminatorName); + if (ModelUtils.isAllOf(discSchema)) { + discSchema = (Schema) discSchema.getAllOf().get(0); + } + return discSchema; + } + /** * Check whether the alternative schemas share a discriminator, and if they do, return it * @@ -151,4 +237,38 @@ private static Discriminator getDiscriminatorFromAlternatives( return null; } + /** + * Recursively look in schema for the discriminator discPropName + * + * @param openAPI The openAPI specification + * @param cp Any previously calculated discriminator codegen property + * @param composedSchemaName The name of the sc Schema + * @param schema The Schema that may contain the discriminator + * @param discPropName The String that is the discriminator propertyName in the schema + * @param visitedSchemas A set of visited schema names + */ + private static CodegenProperty getDiscriminatorCodegenProperty(OpenAPI openAPI, + CodegenProperty cp, + String composedSchemaName, + Schema schema, + String discPropName, + Set visitedSchemas) { + String modelName = ModelUtils.getSimpleRef(schema.get$ref()); + // Must use a copied set as the alternative schemas can point to the same discriminator. + Set visitedSchemasCopy = new TreeSet<>(visitedSchemas); + CodegenProperty thisCp = discriminatorFound(openAPI, schema.getName(), schema, discPropName, visitedSchemasCopy); + if (thisCp == null) { + once(LOGGER).warn(DEFINES_DISCRIMINATOR_BUT_REFERENCE_ALTERNATIVE_IS_MISSING, + composedSchemaName, discPropName, modelName, discPropName); + } + if (cp != null && cp.dataType == null) { + return thisCp; + } + if (cp != thisCp) { + once(LOGGER).warn(DEFINES_DISCRIMINATOR_BUT_ALTERNATIVE_HAS_OTHER_DEFINITION, + composedSchemaName, discPropName, modelName, discPropName, discPropName); + } + return null; + } + }