From 5491fa9da2353e77663a7e67a7ea0e39c19cfdbe Mon Sep 17 00:00:00 2001 From: kirides <13602143+kirides@users.noreply.github.com> Date: Fri, 17 Jan 2025 19:57:25 +0100 Subject: [PATCH] - #557 - discard unused union types/inheritance types via config --- src/Refitter.Core/RefitGenerator.cs | 10 ++-- src/Refitter.Core/SchemaCleaner.cs | 51 +++++++++++++++---- .../Settings/RefitGeneratorSettings.cs | 9 +++- src/Refitter/GenerateCommand.cs | 1 + src/Refitter/Settings.cs | 5 ++ 5 files changed, 63 insertions(+), 13 deletions(-) diff --git a/src/Refitter.Core/RefitGenerator.cs b/src/Refitter.Core/RefitGenerator.cs index e7d78dc8..037335a2 100644 --- a/src/Refitter.Core/RefitGenerator.cs +++ b/src/Refitter.Core/RefitGenerator.cs @@ -20,7 +20,7 @@ public static async Task CreateAsync(RefitGeneratorSettings sett ProcessTagFilters(openApiDocument, settings.IncludeTags); ProcessPathFilters(openApiDocument, settings.IncludePathMatches); - ProcessContractFilter(openApiDocument, settings.TrimUnusedSchema, settings.KeepSchemaPatterns); + ProcessContractFilter(openApiDocument, settings.TrimUnusedSchema, settings.KeepSchemaPatterns, settings.IncludeInheritanceHierarchy); return new RefitGenerator(settings, openApiDocument); } @@ -55,13 +55,17 @@ private static OpenApiDocument SanitizePath( return openApiDocument; } - private static void ProcessContractFilter(OpenApiDocument openApiDocument, bool removeUnusedSchema, string[] includeSchemaMatches) + private static void ProcessContractFilter(OpenApiDocument openApiDocument, bool removeUnusedSchema, string[] includeSchemaMatches, + bool includeInheritanceHierarchy) { if (!removeUnusedSchema) { return; } - var cleaner = new SchemaCleaner(openApiDocument, includeSchemaMatches); + var cleaner = new SchemaCleaner(openApiDocument, includeSchemaMatches) + { + IncludeInheritanceHierarchy = includeInheritanceHierarchy + }; cleaner.RemoveUnreferencedSchema(); } diff --git a/src/Refitter.Core/SchemaCleaner.cs b/src/Refitter.Core/SchemaCleaner.cs index 6f134c50..27a80690 100644 --- a/src/Refitter.Core/SchemaCleaner.cs +++ b/src/Refitter.Core/SchemaCleaner.cs @@ -9,6 +9,8 @@ public class SchemaCleaner private readonly OpenApiDocument document; private readonly string[] keepSchemaPatterns; + public bool IncludeInheritanceHierarchy { get; set; } + public SchemaCleaner(OpenApiDocument document, string[] keepSchemaPatterns) { this.document = document; @@ -17,8 +19,7 @@ public SchemaCleaner(OpenApiDocument document, string[] keepSchemaPatterns) public void RemoveUnreferencedSchema() { - var usage = FindUsedSchema(document); - + var (usedJsonSchema, usage) = FindUsedJsonSchema(document); var unused = document.Components.Schemas.Where(s => !usage.Contains(s.Key)) .ToArray(); @@ -26,9 +27,31 @@ public void RemoveUnreferencedSchema() { document.Components.Schemas.Remove(unusedSchema); } + + if (!IncludeInheritanceHierarchy) + { + foreach (var schema in usedJsonSchema) + { + // Fix any "abstract/sum" types so that the unused-types get removed + if (schema.DiscriminatorObject != null) + { + var mappings = schema.DiscriminatorObject.Mapping; + var keepMappings = mappings.Where(x => + usage.Contains(x.Value.ActualSchema.Id ?? x.Value.Id ?? "_________SOME NON EXISTING VALUE") + ) + .ToArray(); + + schema.DiscriminatorObject.Mapping.Clear(); + foreach (var kvp in keepMappings) + { + schema.DiscriminatorObject.Mapping[kvp.Key] = kvp.Value; + } + } + } + } } - private HashSet FindUsedSchema(OpenApiDocument doc) + private (IReadOnlyCollection, HashSet) FindUsedJsonSchema(OpenApiDocument doc) { var toProcess = new Stack(); var schemaIdLookup = document.Components.Schemas @@ -82,7 +105,7 @@ private HashSet FindUsedSchema(OpenApiDocument doc) } } - return seenIds; + return (seen, seenIds); } private IEnumerable GetSchemaForPath(OpenApiPathItem pathItem) @@ -144,10 +167,10 @@ private IEnumerable EnumerateSchema(JsonSchema? schema) .Where(x => x != null) .Select(x => x!); - static IEnumerable EnumerateInternal(JsonSchema schema) + IEnumerable EnumerateInternal(JsonSchema schema) { schema = schema.ActualSchema; - + yield return schema.AdditionalItemsSchema; yield return schema.AdditionalPropertiesSchema; if (schema.AllInheritedSchemas != null) @@ -157,12 +180,12 @@ private IEnumerable EnumerateSchema(JsonSchema? schema) yield return s; } } - + if (schema.Item != null) { yield return schema.Item; } - + if (schema.Items != null) { foreach (JsonSchema s in schema.Items) @@ -179,6 +202,16 @@ private IEnumerable EnumerateSchema(JsonSchema? schema) yield return subSchema; } + if (schema.DiscriminatorObject != null && IncludeInheritanceHierarchy) + { + // abstract type + // if we let these out, we get a bunch of "AnonymousN"-classes + foreach (var subSchema in schema.DiscriminatorObject.Mapping) + { + yield return subSchema.Value; + } + } + foreach (var subSchema in schema.AnyOf) { yield return subSchema; @@ -200,4 +233,4 @@ private IEnumerable EnumerateSchema(JsonSchema? schema) } } } -} \ No newline at end of file +} diff --git a/src/Refitter.Core/Settings/RefitGeneratorSettings.cs b/src/Refitter.Core/Settings/RefitGeneratorSettings.cs index 91a1daed..321cea6c 100644 --- a/src/Refitter.Core/Settings/RefitGeneratorSettings.cs +++ b/src/Refitter.Core/Settings/RefitGeneratorSettings.cs @@ -138,7 +138,7 @@ public class RefitGeneratorSettings public bool GenerateDeprecatedOperations { get; set; } = true; /// - /// Generate operation names using pattern. + /// Generate operation names using pattern. /// When using , this is name of the Execute() method in the interface. /// public string? OperationNameTemplate { get; set; } @@ -187,6 +187,13 @@ public class RefitGeneratorSettings /// public string[] KeepSchemaPatterns { get; set; } = Array.Empty(); + /// + /// Set to true to keep all possible type-instances of inheritance/union types. + /// If this is false only directly referenced types will be kept. + /// This works in conjunction with . + /// + public bool IncludeInheritanceHierarchy { get; set; } + /// /// The NSwag IOperationNameGenerator implementation to use /// diff --git a/src/Refitter/GenerateCommand.cs b/src/Refitter/GenerateCommand.cs index 1c5a922d..129155ee 100644 --- a/src/Refitter/GenerateCommand.cs +++ b/src/Refitter/GenerateCommand.cs @@ -134,6 +134,7 @@ private static RefitGeneratorSettings CreateRefitGeneratorSettings(Settings sett OptionalParameters = settings.OptionalNullableParameters, TrimUnusedSchema = settings.TrimUnusedSchema, KeepSchemaPatterns = settings.KeepSchemaPatterns ?? Array.Empty(), + IncludeInheritanceHierarchy = settings.IncludeInheritanceHierarchy, OperationNameGenerator = settings.OperationNameGenerator, GenerateDefaultAdditionalProperties = !settings.SkipDefaultAdditionalProperties, ImmutableRecords = settings.ImmutableRecords, diff --git a/src/Refitter/Settings.cs b/src/Refitter/Settings.cs index 14b0a4dd..3092c0cb 100644 --- a/src/Refitter/Settings.cs +++ b/src/Refitter/Settings.cs @@ -158,6 +158,11 @@ Generate multiple files instead of a single large file. [DefaultValue(new string[0])] public string[]? KeepSchemaPatterns { get; set; } + [Description("Keep all possible inherited types/union types even if they are not directly used.")] + [CommandOption("--include-inheritance-hierarchy")] + [DefaultValue(false)] + public bool IncludeInheritanceHierarchy { get; set; } + [Description("Don't show donation banner")] [CommandOption("--no-banner")] [DefaultValue(false)]