From c6688caae33ae8222f8c04683d9ec4940b22c5fb Mon Sep 17 00:00:00 2001 From: Pierre Millot Date: Mon, 29 Jul 2024 17:37:00 +0200 Subject: [PATCH] fix duplicate models --- .../algolia/codegen/AlgoliaJavaGenerator.java | 2 +- .../codegen/AlgoliaSwiftGenerator.java | 4 +- .../codegen/utils/GenericPropagator.java | 8 +- .../codegen/utils/OrphanDestroyer.java | 133 ++++++++++++++++++ .../algoliasearch/builds/models.mustache | 8 +- 5 files changed, 145 insertions(+), 10 deletions(-) create mode 100644 generators/src/main/java/com/algolia/codegen/utils/OrphanDestroyer.java diff --git a/generators/src/main/java/com/algolia/codegen/AlgoliaJavaGenerator.java b/generators/src/main/java/com/algolia/codegen/AlgoliaJavaGenerator.java index becdb89dd50..7ba337cb671 100644 --- a/generators/src/main/java/com/algolia/codegen/AlgoliaJavaGenerator.java +++ b/generators/src/main/java/com/algolia/codegen/AlgoliaJavaGenerator.java @@ -2,7 +2,6 @@ import com.algolia.codegen.exceptions.*; import com.algolia.codegen.utils.*; -import com.algolia.codegen.utils.OneOf; import com.google.common.collect.ImmutableMap.Builder; import com.samskivert.mustache.Mustache; import com.samskivert.mustache.Mustache.Lambda; @@ -98,6 +97,7 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List modelsMap, b } } + public static void propagateGenericsToModels(Map modelsMap) { + propagateGenericsToModels(modelsMap, false); + } + /** Mark operations with a generic return type with x-is-generic */ public static void propagateGenericsToOperations(OperationsMap operations, List allModels) { Map models = convertToMap(allModels); @@ -207,8 +211,4 @@ public static void propagateGenericsToOperations(OperationsMap operations, List< } } } - - public static void propagateGenericsToModels(Map modelsMap) { - propagateGenericsToModels(modelsMap, false); - } } diff --git a/generators/src/main/java/com/algolia/codegen/utils/OrphanDestroyer.java b/generators/src/main/java/com/algolia/codegen/utils/OrphanDestroyer.java new file mode 100644 index 00000000000..84b7cb9aedb --- /dev/null +++ b/generators/src/main/java/com/algolia/codegen/utils/OrphanDestroyer.java @@ -0,0 +1,133 @@ +package com.algolia.codegen.utils; + +import java.io.File; +import java.util.*; +import org.openapitools.codegen.*; +import org.openapitools.codegen.model.ModelMap; +import org.openapitools.codegen.model.OperationsMap; + +public class OrphanDestroyer { + + private static Set primitiveModels = new HashSet<>(Arrays.asList("object", "array", "string", "boolean", "integer")); + private static Set helperModels = new HashSet<>( + Arrays.asList("apiKeyOperation", "securedApiKeyRestrictions", "replaceAllObjectsResponse") + ); + + private Map models; + private HashSet visitedModels; + + private OrphanDestroyer(Map models) { + this.visitedModels = new HashSet<>(); + this.models = models; + } + + private CodegenModel getModel(String name) { + // openapi generator returns some weird error when looking for primitive type, so we filter them + // by hand + if (primitiveModels.contains(name)) { + return null; + } + + return models.get(name); + } + + private CodegenModel propertyToModel(CodegenProperty prop) { + return prop == null ? null : getModel(prop.openApiType); + } + + private void exploreProperties(CodegenModel model, List properties) { + for (CodegenProperty property : properties) { + CodegenModel propModel = propertyToModel(property); + if (propModel != null && !visitedModels.contains(propModel.name)) { + System.out.println("Visiting property: " + propModel.name + " from " + model.name); + visitModelRecursive(propModel); + visitedModels.add(propModel.name); + } + CodegenModel itemsModel = propertyToModel(property.items); + if (itemsModel != null && !visitedModels.contains(itemsModel.name)) { + System.out.println("Visiting item: " + itemsModel.name + " from " + model.name); + visitedModels.add(itemsModel.name); + visitModelRecursive(itemsModel); + } + } + } + + private void visitModelRecursive(CodegenModel model) { + exploreProperties(model, model.getVars()); + if (model.getComposedSchemas() != null && model.getComposedSchemas().getOneOf() != null) { + exploreProperties(model, model.getComposedSchemas().getOneOf()); + } + } + + private static Map convertToMap(List models) { + Map modelsMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + for (ModelMap modelMap : models) { + CodegenModel model = modelMap.getModel(); + modelsMap.put(model.name, model); + } + return modelsMap; + } + + private void exploreGraph(OperationsMap operations) { + for (CodegenModel model : models.values()) { + visitModelRecursive(model); + } + + for (CodegenOperation ope : operations.getOperations().getOperation()) { + if (ope.returnType != null) { + String modelName = ope.returnType; + if (modelName.startsWith("List<")) { + modelName = modelName.substring(5, modelName.length() - 1); + } else if (modelName.startsWith("Map<")) { + modelName = modelName.substring(4, modelName.length() - 1).split(",")[1].trim(); + } + + System.out.println("Visiting return type: " + modelName); + CodegenModel returnType = getModel(modelName); + if (returnType != null) { + visitedModels.add(returnType.name); + } + } + for (CodegenParameter param : ope.allParams) { + CodegenModel paramType = getModel(param.dataType); + if (paramType != null) { + visitedModels.add(paramType.name); + } + CodegenModel itemsModel = propertyToModel(param.items); + if (itemsModel != null) { + visitedModels.add(itemsModel.name); + } + } + } + } + + /** remove all the unused models, most likely the sub models of allOf */ + public static void removeOrphans(CodegenConfig config, OperationsMap operations, List allModels) { + // visit all the models that are accessible from: + // - the properties of a model (needs recursive search) + // - the return type of an operation + // - the parameters of an operation + + OrphanDestroyer orphanDestroyer = new OrphanDestroyer(convertToMap(allModels)); + + orphanDestroyer.exploreGraph(operations); + + List toRemove = new ArrayList<>(); + for (String modelName : orphanDestroyer.models.keySet()) { + if (!helperModels.contains(modelName) && !orphanDestroyer.visitedModels.contains(modelName)) { + toRemove.add(modelName); + } + } + + String templateName = config.modelTemplateFiles().keySet().iterator().next(); + + for (String modelName : toRemove) { + String filename = config.modelFilename(templateName, modelName); + File file = new File(filename); + if (file.exists()) { + file.delete(); + System.out.println("Removed orphan model: " + modelName); + } + } + } +} diff --git a/templates/javascript/clients/algoliasearch/builds/models.mustache b/templates/javascript/clients/algoliasearch/builds/models.mustache index 49a23b9af20..378d4b09956 100644 --- a/templates/javascript/clients/algoliasearch/builds/models.mustache +++ b/templates/javascript/clients/algoliasearch/builds/models.mustache @@ -10,7 +10,6 @@ import { AlternativesAsExact, Anchoring, AroundPrecision, - AroundPrecisionFromValueInner, AroundRadius, AroundRadiusAll, AutomaticFacetFilter, @@ -55,11 +54,12 @@ import { PromoteObjectID, PromoteObjectIDs, QueryType, + Range, RankingInfo, ReRankingApplyFilter, Redirect, RedirectRuleIndexMetadata, - RedirectRuleIndexMetadataData, + RedirectRuleIndexData, RedirectURL, RemoveStopWords, RemoveWordsIfNoResults, @@ -97,7 +97,6 @@ export { AlternativesAsExact, Anchoring, AroundPrecision, - AroundPrecisionFromValueInner, AroundRadius, AroundRadiusAll, AutomaticFacetFilter, @@ -141,11 +140,12 @@ export { PromoteObjectID, PromoteObjectIDs, QueryType, + Range, RankingInfo, ReRankingApplyFilter, Redirect, RedirectRuleIndexMetadata, - RedirectRuleIndexMetadataData, + RedirectRuleIndexData, RedirectURL, RemoveStopWords, RemoveWordsIfNoResults,