-
-
Notifications
You must be signed in to change notification settings - Fork 7.6k
refactor: introduce DiscriminatorUtils to separate handling of discriminator discoverability #24143
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Mattias-Sehlstedt
wants to merge
1
commit into
OpenAPITools:master
Choose a base branch
from
Mattias-Sehlstedt:discriminator-utils
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+162
−104
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
154 changes: 154 additions & 0 deletions
154
...es/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,154 @@ | ||
| package org.openapitools.codegen.utils; | ||
|
|
||
| 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.slf4j.Logger; | ||
| import org.slf4j.LoggerFactory; | ||
|
|
||
| import java.util.*; | ||
|
|
||
| import static org.openapitools.codegen.utils.OnceLogger.once; | ||
|
|
||
| public class DiscriminatorUtils { | ||
|
|
||
| private static final Logger LOGGER = LoggerFactory.getLogger(DiscriminatorUtils.class); | ||
|
|
||
| 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 {}"; | ||
|
|
||
| /** | ||
| * Gets the simple ref name of the discriminator property type from the schema. | ||
| * | ||
| * @param schema The input OAS schema. | ||
| * @param discriminatorPropertyName The name of the discriminator property. | ||
| * @return referenced type name, or an empty optional if unavailable | ||
| */ | ||
| public static Optional<String> getDiscriminatorPropertyType(Schema schema, String discriminatorPropertyName) { | ||
| return Optional.ofNullable(getDiscriminatorSchema(schema, discriminatorPropertyName)) | ||
| .map(Schema::get$ref) | ||
| .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 | ||
| * | ||
| * @param openAPI the openAPI specification | ||
| * @param legacyDiscriminatorBehavior whether legacy discriminator behavior is enabled | ||
| * @param sc The Schema that may contain the discriminator | ||
| * @param visitedSchemas an array list of visited schemas | ||
| */ | ||
| public static Discriminator recursiveGetDiscriminator( | ||
| OpenAPI openAPI, | ||
| boolean legacyDiscriminatorBehavior, | ||
| Schema sc, | ||
| ArrayList<Schema> visitedSchemas) { | ||
| Schema refSchema = ModelUtils.getReferencedSchema(openAPI, sc); | ||
| Discriminator foundDisc = refSchema.getDiscriminator(); | ||
| if (foundDisc != null) { | ||
| return foundDisc; | ||
| } | ||
|
|
||
| if (legacyDiscriminatorBehavior) { | ||
| return null; | ||
| } | ||
|
|
||
| for (Schema s : visitedSchemas) { | ||
| if (s == refSchema) { | ||
| return null; | ||
| } | ||
| } | ||
| visitedSchemas.add(refSchema); | ||
|
|
||
| Discriminator disc = new Discriminator(); | ||
| 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()) { | ||
| foundDisc = recursiveGetDiscriminator(openAPI, legacyDiscriminatorBehavior, (Schema) allOf, visitedSchemas); | ||
| if (foundDisc != null) { | ||
| disc.setPropertyName(foundDisc.getPropertyName()); | ||
| disc.setMapping(foundDisc.getMapping()); | ||
| return disc; | ||
| } | ||
| } | ||
| } | ||
| if (ModelUtils.hasOneOf(composedSchema)) { | ||
| foundDisc = getDiscriminatorFromAlternatives(openAPI, legacyDiscriminatorBehavior, composedSchema.getOneOf(), visitedSchemas); | ||
| if (foundDisc != null) { | ||
| return foundDisc; | ||
| } | ||
| } | ||
| if (ModelUtils.hasAnyOf(composedSchema)) { | ||
| return getDiscriminatorFromAlternatives(openAPI, legacyDiscriminatorBehavior, composedSchema.getAnyOf(), visitedSchemas); | ||
| } | ||
| } | ||
| return null; | ||
| } | ||
|
|
||
| /** | ||
| * Check whether the alternative schemas share a discriminator, and if they do, return it | ||
| * | ||
| * @param openAPI the openAPI specification | ||
| * @param legacyDiscriminatorBehavior whether legacy discriminator behavior is enabled | ||
| * @param alternativeSchemas list of schemas that should be checked for a shared discriminator | ||
| * @param visitedSchemas an array list of visited schemas | ||
| * @return the discriminator if the alternatives correctly shares one, otherwise null | ||
| */ | ||
| private static Discriminator getDiscriminatorFromAlternatives( | ||
| OpenAPI openAPI, | ||
| boolean legacyDiscriminatorBehavior, | ||
| List<Schema> alternativeSchemas, | ||
| ArrayList<Schema> visitedSchemas) { | ||
| Discriminator discriminator = new Discriminator(); | ||
| Discriminator foundDisc = null; | ||
| Integer hasDiscriminatorCnt = 0; | ||
| Integer hasNullTypeCnt = 0; | ||
| Set<String> discriminatorsPropNames = new HashSet<>(); | ||
| for (Object alternative : alternativeSchemas) { | ||
| if (ModelUtils.isNullType((Schema) alternative)) { | ||
| // The null type does not have a discriminator. Skip. | ||
| hasNullTypeCnt++; | ||
| continue; | ||
| } | ||
| foundDisc = recursiveGetDiscriminator(openAPI, legacyDiscriminatorBehavior, (Schema) alternative, visitedSchemas); | ||
| if (foundDisc != null) { | ||
| discriminatorsPropNames.add(foundDisc.getPropertyName()); | ||
| hasDiscriminatorCnt++; | ||
| } | ||
| } | ||
| if (discriminatorsPropNames.size() > 1) { | ||
| once(LOGGER).warn(CONFLICTING_DISCRIMINATOR_NAMES, String.join(", ", discriminatorsPropNames)); | ||
| } | ||
| boolean allAlternativesHaveADiscriminator = hasDiscriminatorCnt + hasNullTypeCnt == alternativeSchemas.size(); | ||
| if (foundDisc != null && allAlternativesHaveADiscriminator && discriminatorsPropNames.size() == 1) { | ||
| discriminator.setPropertyName(foundDisc.getPropertyName()); | ||
| discriminator.setMapping(foundDisc.getMapping()); | ||
|
Mattias-Sehlstedt marked this conversation as resolved.
|
||
| return discriminator; | ||
| } | ||
| // If the scenario when composite schema has two children and one of them is the 'null' type, | ||
| // there is no need for a discriminator. | ||
| return null; | ||
| } | ||
|
|
||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.