From 970a850e7ef3536d71f1ea56f21138e386bd6a57 Mon Sep 17 00:00:00 2001 From: Chebotov Nikolay Date: Thu, 8 Oct 2020 00:16:18 +0300 Subject: [PATCH] Add "IncludeXEnumRemarks" option to include remarks for descriptions from xml-comments. --- CHANGELOG.md | 5 ++ README.md | 3 + .../Extensions/EnumTypeExtensions.cs | 65 ++++++++++++++++--- .../Filters/XEnumNamesParameterFilter.cs | 6 +- .../Filters/XEnumNamesSchemaFilter.cs | 6 +- .../Options/FixEnumsOptions.cs | 5 ++ ...e.Swashbuckle.AspNetCore.Extensions.csproj | 6 +- ...hase.Swashbuckle.AspNetCore.Extensions.xml | 5 ++ .../Models/ComplicatedClass.cs | 11 ++++ test/WebApi3.1-Swashbuckle/Startup.cs | 5 +- 10 files changed, 99 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ca0d8c..3f67818 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,11 @@ These are the changes to each version that has been released on the [nuget](https://www.nuget.org/packages/Unchase.Swashbuckle.AspNetCore.Extensions/). +## v2.3.13 `2020-10-07` + +- [x] Add `IncludeXEnumRemarks` option to include remarks for descriptions from xml-comments +- [x] Fix small bugs + ## v2.3.12 `(2020-09-21)` - [x] Fix [issue #11](https://github.com/unchase/Unchase.Swashbuckle.AspNetCore.Extensions/issues/11) diff --git a/README.md b/README.md index 3dfca54..065fcf8 100644 --- a/README.md +++ b/README.md @@ -129,6 +129,9 @@ public void ConfigureServices(IServiceCollection services) // add descriptions from DescriptionAttribute or xml-comments to fix enums (add 'x-enumDescriptions' for schema extensions) for applied filters o.IncludeDescriptions = true; + // add remarks for descriptions from xml-comments + o.IncludeXEnumRemarks = true; + // get descriptions from DescriptionAttribute then from xml-comments o.DescriptionSource = DescriptionSources.DescriptionAttributesThenXmlComments; diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Extensions/EnumTypeExtensions.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Extensions/EnumTypeExtensions.cs index ab1a4c2..64c59a2 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Extensions/EnumTypeExtensions.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Extensions/EnumTypeExtensions.cs @@ -50,7 +50,7 @@ private static string GetFieldAttributeDescription(this Type enumType, object en return string.Empty; } - internal static List GetEnumValuesDescription(Type enumType, DescriptionSources descriptionSource, IEnumerable xmlNavigators = null) + internal static List GetEnumValuesDescription(Type enumType, DescriptionSources descriptionSource, IEnumerable xmlNavigators, bool includeRemarks = false) { var enumsDescriptions = new List(); foreach (var enumValue in Enum.GetValues(enumType)) @@ -66,7 +66,7 @@ internal static List GetEnumValuesDescription(Type enumType, Desc case DescriptionSources.XmlComments: var memberInfo = enumType.GetMembers().FirstOrDefault(m => m.Name.Equals(enumValue.ToString(), StringComparison.InvariantCultureIgnoreCase)); - enumDescription = TryGetMemberComments(memberInfo, xmlNavigators); + enumDescription = TryGetMemberComments(memberInfo, xmlNavigators, includeRemarks); break; case DescriptionSources.DescriptionAttributesThenXmlComments: enumDescription = GetDescriptionFromEnumOption(enumType, enumValue); @@ -74,7 +74,7 @@ internal static List GetEnumValuesDescription(Type enumType, Desc { var memberInfo2 = enumType.GetMembers().FirstOrDefault(m => m.Name.Equals(enumValue.ToString(), StringComparison.InvariantCultureIgnoreCase)); - enumDescription = TryGetMemberComments(memberInfo2, xmlNavigators); + enumDescription = TryGetMemberComments(memberInfo2, xmlNavigators, includeRemarks); } break; } @@ -85,32 +85,77 @@ internal static List GetEnumValuesDescription(Type enumType, Desc } finally { - enumsDescriptions.Add(new OpenApiString(enumDescription)); + if (!string.IsNullOrWhiteSpace(enumDescription)) + enumsDescriptions.Add(new OpenApiString(enumDescription)); } } return enumsDescriptions; } - private static string TryGetMemberComments(MemberInfo memberInfo, IEnumerable xmlNavigators) + private static string TryGetMemberComments(MemberInfo memberInfo, IEnumerable xmlNavigators, bool includeRemarks = false) { + var commentsBuilder = new StringBuilder(); if (xmlNavigators == null) return string.Empty; foreach (var xmlNavigator in xmlNavigators) { - var nodeNameForMember = XmlCommentsNodeNameHelper.GetNodeNameForMember(memberInfo); - var xpathNavigator1 = xmlNavigator.SelectSingleNode( + var nodeNameForMember = GetNodeNameForMember(memberInfo); + var xpathMemberNavigator = xmlNavigator.SelectSingleNode( $"/doc/members/member[@name='{nodeNameForMember}']"); - var xpathNavigator2 = xpathNavigator1?.SelectSingleNode("summary"); - if (xpathNavigator2 != null) + var xpathSummaryNavigator = xpathMemberNavigator?.SelectSingleNode("summary"); + if (xpathSummaryNavigator != null) { - return XmlCommentsTextHelper.Humanize(xpathNavigator2.InnerXml); + commentsBuilder.Append(XmlCommentsTextHelper.Humanize(xpathSummaryNavigator.InnerXml)); + if (includeRemarks) + { + var xpathRemarksNavigator = xpathMemberNavigator?.SelectSingleNode("remarks"); + if (xpathRemarksNavigator != null && !string.IsNullOrWhiteSpace(xpathRemarksNavigator.InnerXml)) + { + commentsBuilder.Append($" ({XmlCommentsTextHelper.Humanize(xpathRemarksNavigator.InnerXml)})"); + } + } + + return commentsBuilder.ToString(); } } return string.Empty; } + private static string GetNodeNameForMember(MemberInfo memberInfo) + { + var stringBuilder = new StringBuilder((memberInfo.MemberType & MemberTypes.Field) != 0 ? "F:" : "P:"); + stringBuilder.Append(QualifiedNameFor(memberInfo.DeclaringType, false)); + stringBuilder.Append("." + memberInfo.Name); + return stringBuilder.ToString(); + } + + private static string QualifiedNameFor(Type type, bool expandGenericArgs = false) + { + if (type.IsArray) + return QualifiedNameFor(type.GetElementType(), expandGenericArgs) + "[]"; + + var stringBuilder = new StringBuilder(); + if (!string.IsNullOrEmpty(type.Namespace)) + stringBuilder.Append(type.Namespace + "."); + + if (type.IsNested) + stringBuilder.Append(type.DeclaringType?.Name + "."); + + if (type.IsConstructedGenericType & expandGenericArgs) + { + var str = type.Name.Split('`').First(); + stringBuilder.Append(str); + var values = type.GetGenericArguments().Select(t => !t.IsGenericParameter ? QualifiedNameFor(t, true) : string.Format("`{0}", t.GenericParameterPosition)); + stringBuilder.Append("{" + string.Join(",", values) + "}"); + } + else + stringBuilder.Append(type.Name); + + return stringBuilder.ToString(); + } + internal static string AddEnumValuesDescription(this OpenApiSchema schema, bool includeDescriptionFromAttribute = false) { if (schema.Enum == null || schema.Enum.Count == 0) diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XEnumNamesParameterFilter.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XEnumNamesParameterFilter.cs index 065005b..58161f7 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XEnumNamesParameterFilter.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XEnumNamesParameterFilter.cs @@ -17,6 +17,7 @@ internal class XEnumNamesParameterFilter : IParameterFilter #region Fields private readonly bool _includeXEnumDescriptions; + private readonly bool _includeXEnumRemarks; private readonly DescriptionSources _descriptionSources; private readonly bool _applyFiler; private readonly HashSet _xmlNavigators = new HashSet(); @@ -36,6 +37,7 @@ public XEnumNamesParameterFilter(IOptions options, Action _xmlNavigators = new HashSet(); @@ -37,6 +38,7 @@ public XEnumNamesSchemaFilter(IOptions options, Action public bool IncludeDescriptions { get; set; } = false; + /// + /// Include remarks for descriptions from xml comments. Default value is false. + /// + public bool IncludeXEnumRemarks { get; set; } = false; + /// /// Source to get descriptions. Default value is . /// diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Unchase.Swashbuckle.AspNetCore.Extensions.csproj b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Unchase.Swashbuckle.AspNetCore.Extensions.csproj index ddd6069..5a54f8a 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Unchase.Swashbuckle.AspNetCore.Extensions.csproj +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Unchase.Swashbuckle.AspNetCore.Extensions.csproj @@ -14,9 +14,9 @@ 7.3 https://github.com/unchase/Unchase.Swashbuckle.AspNetCore.Extensions/blob/master/assets/icon.png?raw=true - 2.3.12 - 2.3.12.0 - 2.3.12.0 + 2.3.13 + 2.3.13.0 + 2.3.13.0 false Unchase.Swashbuckle.AspNetCore.Extensions.xml diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Unchase.Swashbuckle.AspNetCore.Extensions.xml b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Unchase.Swashbuckle.AspNetCore.Extensions.xml index f7f42bb..29eaafe 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Unchase.Swashbuckle.AspNetCore.Extensions.xml +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Unchase.Swashbuckle.AspNetCore.Extensions.xml @@ -310,6 +310,11 @@ Include descriptions from or xml comments. Default value is false. + + + Include remarks for descriptions from xml comments. Default value is false. + + Source to get descriptions. Default value is . diff --git a/test/WebApi3.1-Swashbuckle/Models/ComplicatedClass.cs b/test/WebApi3.1-Swashbuckle/Models/ComplicatedClass.cs index ad9683a..09c3308 100644 --- a/test/WebApi3.1-Swashbuckle/Models/ComplicatedClass.cs +++ b/test/WebApi3.1-Swashbuckle/Models/ComplicatedClass.cs @@ -11,11 +11,22 @@ public class ComplicatedClass /// /// Tag. /// + /// + /// Tag remarks. + /// public Tag Tag { get; set; } /// /// Inner class. /// + /// + /// + /// InnerClass remarks. + /// + /// + /// With para. + /// + /// public InnerClass InnerClass { get; set; } } diff --git a/test/WebApi3.1-Swashbuckle/Startup.cs b/test/WebApi3.1-Swashbuckle/Startup.cs index 74a5a7a..fbf8776 100644 --- a/test/WebApi3.1-Swashbuckle/Startup.cs +++ b/test/WebApi3.1-Swashbuckle/Startup.cs @@ -42,7 +42,7 @@ public void ConfigureServices(IServiceCollection services) // Add filters to fix enums // use by default: - //options.AddEnumsWithValuesFixFilters(); + options.AddEnumsWithValuesFixFilters(); // or configured: options.AddEnumsWithValuesFixFilters(services, o => @@ -59,6 +59,9 @@ public void ConfigureServices(IServiceCollection services) // add descriptions from DescriptionAttribute or xml-comments to fix enums (add 'x-enumDescriptions' for schema extensions) for applied filters o.IncludeDescriptions = true; + // add remarks for descriptions from xml-comments + o.IncludeXEnumRemarks = true; + // get descriptions from DescriptionAttribute then from xml-comments o.DescriptionSource = DescriptionSources.DescriptionAttributesThenXmlComments;