diff --git a/README.md b/README.md index 097b041..04fb318 100644 --- a/README.md +++ b/README.md @@ -304,7 +304,7 @@ public void ConfigureServices(IServiceCollection services) options.ChangeAllResponsesByHttpStatusCode( httpStatusCode: HttpStatusCode.BadRequest, responseDescription: "400 status code description", - responseExampleOption: ResponseExampleOptions.Clear, // claer response examples + responseExampleOption: ResponseExampleOptions.Clear, // clear response examples responseExample: new ComplicatedClass()); // some class for response examples // change responses for specific HTTP status code ("201" (StatusCodes.Status201Created)) diff --git a/Unchase.Swashbuckle.AspNetCore.Extensions.sln b/Unchase.Swashbuckle.AspNetCore.Extensions.sln index 31b2d6b..248aa31 100644 --- a/Unchase.Swashbuckle.AspNetCore.Extensions.sln +++ b/Unchase.Swashbuckle.AspNetCore.Extensions.sln @@ -36,7 +36,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebApi3.1-Swashbuckle", "te EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebApi5-Swashbuckle", "test\WebApi5-Swashbuckle\WebApi5-Swashbuckle.csproj", "{C9251364-9941-4776-A9A3-841173DB58C4}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApi7-Swashbuckle", "test\WebApi7-Swashbuckle\WebApi7-Swashbuckle.csproj", "{4C6ADEFF-E66A-4E7D-B8B7-24FCDB3F6A89}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebApi8-Swashbuckle", "test\WebApi8-Swashbuckle\WebApi8-Swashbuckle.csproj", "{BD6CDEB9-F093-4BD3-AA28-2B74B8A985B9}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebApi7-Swashbuckle", "test\WebApi7-Swashbuckle\WebApi7-Swashbuckle.csproj", "{7868CB09-04B0-418F-A26E-316B4F73F865}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -60,10 +62,14 @@ Global {C9251364-9941-4776-A9A3-841173DB58C4}.Debug|Any CPU.Build.0 = Debug|Any CPU {C9251364-9941-4776-A9A3-841173DB58C4}.Release|Any CPU.ActiveCfg = Release|Any CPU {C9251364-9941-4776-A9A3-841173DB58C4}.Release|Any CPU.Build.0 = Release|Any CPU - {4C6ADEFF-E66A-4E7D-B8B7-24FCDB3F6A89}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4C6ADEFF-E66A-4E7D-B8B7-24FCDB3F6A89}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4C6ADEFF-E66A-4E7D-B8B7-24FCDB3F6A89}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4C6ADEFF-E66A-4E7D-B8B7-24FCDB3F6A89}.Release|Any CPU.Build.0 = Release|Any CPU + {BD6CDEB9-F093-4BD3-AA28-2B74B8A985B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BD6CDEB9-F093-4BD3-AA28-2B74B8A985B9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BD6CDEB9-F093-4BD3-AA28-2B74B8A985B9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BD6CDEB9-F093-4BD3-AA28-2B74B8A985B9}.Release|Any CPU.Build.0 = Release|Any CPU + {7868CB09-04B0-418F-A26E-316B4F73F865}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7868CB09-04B0-418F-A26E-316B4F73F865}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7868CB09-04B0-418F-A26E-316B4F73F865}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7868CB09-04B0-418F-A26E-316B4F73F865}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -73,7 +79,8 @@ Global {ADFE3232-3F94-4582-BE4E-CD93603EAE18} = {41C53B82-ED58-4927-B597-CCCAEC8E3BF8} {1AD5C40F-8AF7-4DAA-9C72-40D1269304C0} = {41C53B82-ED58-4927-B597-CCCAEC8E3BF8} {C9251364-9941-4776-A9A3-841173DB58C4} = {41C53B82-ED58-4927-B597-CCCAEC8E3BF8} - {4C6ADEFF-E66A-4E7D-B8B7-24FCDB3F6A89} = {41C53B82-ED58-4927-B597-CCCAEC8E3BF8} + {BD6CDEB9-F093-4BD3-AA28-2B74B8A985B9} = {41C53B82-ED58-4927-B597-CCCAEC8E3BF8} + {7868CB09-04B0-418F-A26E-316B4F73F865} = {41C53B82-ED58-4927-B597-CCCAEC8E3BF8} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {4830AE48-C5E4-460E-B23E-B2D7D0578670} diff --git a/Unchase.Swashbuckle.AspNetCore.Extensions.sln.DotSettings b/Unchase.Swashbuckle.AspNetCore.Extensions.sln.DotSettings new file mode 100644 index 0000000..34c0a9a --- /dev/null +++ b/Unchase.Swashbuckle.AspNetCore.Extensions.sln.DotSettings @@ -0,0 +1,4 @@ + + True + True + True \ No newline at end of file diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Extensions/EnumTypeExtensions.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Extensions/EnumTypeExtensions.cs index fb7bcdb..bd5c8e7 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Extensions/EnumTypeExtensions.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Extensions/EnumTypeExtensions.cs @@ -1,15 +1,14 @@ -using System; -using System.Text; +using Microsoft.OpenApi.Any; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; +using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Reflection; +using System.Text; using System.Xml.XPath; -using Microsoft.OpenApi.Any; -using Microsoft.OpenApi.Models; -using Swashbuckle.AspNetCore.SwaggerGen; - namespace Unchase.Swashbuckle.AspNetCore.Extensions.Extensions { /// @@ -41,8 +40,7 @@ private static string GetDescriptionFromEnumOption( Type enumOptionType, object enumOption) { - return enumOptionType - .GetFieldAttributeDescription(enumOption, 0); + return GetFieldAttributeDescription(enumOptionType, enumOption, 0); } private static string GetFieldAttributeDescription( @@ -85,6 +83,7 @@ private static string GetFieldAttributeDescription( case DescriptionSources.XmlComments: var memberInfo = enumType.GetMembers().FirstOrDefault(m => m.Name.Equals(enumValue.ToString(), StringComparison.InvariantCultureIgnoreCase)); + // ReSharper disable once PossibleMultipleEnumeration enumDescription = TryGetMemberComments(memberInfo, xmlNavigators, includeRemarks); break; case DescriptionSources.DescriptionAttributesThenXmlComments: @@ -93,12 +92,14 @@ private static string GetFieldAttributeDescription( { var memberInfo2 = enumType.GetMembers().FirstOrDefault(m => m.Name.Equals(enumValue.ToString(), StringComparison.InvariantCultureIgnoreCase)); + // ReSharper disable once PossibleMultipleEnumeration enumDescription = TryGetMemberComments(memberInfo2, xmlNavigators, includeRemarks); } break; } } + // ReSharper disable once EmptyGeneralCatchClause catch { } @@ -140,6 +141,7 @@ private static string TryGetMemberComments( commentsBuilder.Append(XmlCommentsTextHelper.Humanize(xpathSummaryNavigator.InnerXml)); if (includeRemarks) { + // ReSharper disable once ConstantConditionalAccessQualifier var xpathRemarksNavigator = xpathMemberNavigator?.SelectSingleNode("remarks"); if (xpathRemarksNavigator != null && !string.IsNullOrWhiteSpace(xpathRemarksNavigator.InnerXml)) { @@ -158,6 +160,7 @@ private static string GetNodeNameForMember( MemberInfo memberInfo) { var stringBuilder = new StringBuilder((memberInfo.MemberType & MemberTypes.Field) != 0 ? "F:" : "P:"); + // ReSharper disable once RedundantArgumentDefaultValue stringBuilder.Append(QualifiedNameFor(memberInfo.DeclaringType, false)); stringBuilder.Append("." + memberInfo.Name); return stringBuilder.ToString(); @@ -187,8 +190,7 @@ private static string QualifiedNameFor( { 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)); + var values = type.GetGenericArguments().Select(t => !t.IsGenericParameter ? QualifiedNameFor(t, true) : string.Format("`{0}", t.GenericParameterPosition)); stringBuilder.Append("{" + string.Join(",", values) + "}"); } else diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Extensions/OpenApiDocumentExtensions.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Extensions/OpenApiDocumentExtensions.cs index 74e91c9..341ad0c 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Extensions/OpenApiDocumentExtensions.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Extensions/OpenApiDocumentExtensions.cs @@ -1,10 +1,9 @@ -using System; +using Microsoft.AspNetCore.Mvc; +using Microsoft.OpenApi.Models; +using System; using System.Collections.Generic; using System.Linq; using System.Reflection; - -using Microsoft.AspNetCore.Mvc; -using Microsoft.OpenApi.Models; using Unchase.Swashbuckle.AspNetCore.Extensions.Factories; using Unchase.Swashbuckle.AspNetCore.Extensions.Filters; diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Extensions/SwaggerGenOptionsExtensions.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Extensions/SwaggerGenOptionsExtensions.cs index 07ef308..a4517b7 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Extensions/SwaggerGenOptionsExtensions.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Extensions/SwaggerGenOptionsExtensions.cs @@ -1,11 +1,9 @@ -using System; +using Swashbuckle.AspNetCore.SwaggerGen; +using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Xml.XPath; - -using Microsoft.Extensions.DependencyInjection; -using Swashbuckle.AspNetCore.SwaggerGen; using Unchase.Swashbuckle.AspNetCore.Extensions.Filters; using Unchase.Swashbuckle.AspNetCore.Extensions.Options; @@ -37,7 +35,7 @@ public static SwaggerGenOptions ChangeAllResponsesByHttpStatusCode( ResponseExampleOptions responseExampleOption = ResponseExampleOptions.None, T responseExample = default) where T : class { - swaggerGenOptions.DocumentFilter>(httpStatusCode, responseDescription, responseExampleOption, responseExample); + Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.DocumentFilter>(swaggerGenOptions, httpStatusCode, responseDescription, responseExampleOption, responseExample); return swaggerGenOptions; } @@ -60,7 +58,7 @@ public static SwaggerGenOptions ChangeAllResponsesByHttpStatusCode( ResponseExampleOptions responseExampleOption = ResponseExampleOptions.None, T responseExample = default) where T : class { - return swaggerGenOptions.ChangeAllResponsesByHttpStatusCode((int)httpStatusCode, responseDescription, responseExampleOption, responseExample); + return ChangeAllResponsesByHttpStatusCode(swaggerGenOptions, (int)httpStatusCode, responseDescription, responseExampleOption, responseExample); } /// @@ -75,9 +73,9 @@ public static SwaggerGenOptions AddEnumsWithValuesFixFilters( // local function void EmptyAction(FixEnumsOptions x) { } - swaggerGenOptions.SchemaFilter(configureOptions ?? EmptyAction); - swaggerGenOptions.ParameterFilter(configureOptions ?? EmptyAction); - swaggerGenOptions.DocumentFilter(); + Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.SchemaFilter(swaggerGenOptions, configureOptions ?? EmptyAction); + Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.ParameterFilter(swaggerGenOptions, configureOptions ?? EmptyAction); + Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.DocumentFilter(swaggerGenOptions); return swaggerGenOptions; } @@ -97,18 +95,18 @@ public static SwaggerGenOptions IncludeXmlCommentsWithRemarks( bool includeControllerXmlComments = false, params Type[] excludedTypes) { - swaggerGenOptions.IncludeXmlComments(xmlDocFactory, includeControllerXmlComments); + Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.IncludeXmlComments(swaggerGenOptions, xmlDocFactory, includeControllerXmlComments); var distinctExcludedTypes = excludedTypes?.Distinct().ToArray() ?? new Type[] { }; var xmlDoc = xmlDocFactory(); - swaggerGenOptions.ParameterFilter(xmlDoc, distinctExcludedTypes); - swaggerGenOptions.RequestBodyFilter(xmlDoc, distinctExcludedTypes); - swaggerGenOptions.SchemaFilter(xmlDoc, distinctExcludedTypes); + Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.ParameterFilter(swaggerGenOptions, xmlDoc, distinctExcludedTypes); + Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.RequestBodyFilter(swaggerGenOptions, xmlDoc, distinctExcludedTypes); + Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.SchemaFilter(swaggerGenOptions, xmlDoc, distinctExcludedTypes); if (includeControllerXmlComments) { - swaggerGenOptions.DocumentFilter(xmlDoc, distinctExcludedTypes); + Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.DocumentFilter(swaggerGenOptions, xmlDoc, distinctExcludedTypes); } return swaggerGenOptions; @@ -130,7 +128,7 @@ public static SwaggerGenOptions IncludeXmlCommentsWithRemarks( bool includeControllerXmlComments = false, params Type[] excludedTypes) { - return swaggerGenOptions.IncludeXmlCommentsWithRemarks(() => new XPathDocument(filePath), includeControllerXmlComments, excludedTypes); + return IncludeXmlCommentsWithRemarks(swaggerGenOptions, () => new XPathDocument(filePath), includeControllerXmlComments, excludedTypes); } /// @@ -149,7 +147,7 @@ public static SwaggerGenOptions IncludeXmlCommentsWithRemarks( bool includeControllerXmlComments = false, Func excludedTypesFunc = default) { - return swaggerGenOptions.IncludeXmlCommentsWithRemarks(xmlDocFactory, includeControllerXmlComments, excludedTypesFunc?.Invoke()); + return IncludeXmlCommentsWithRemarks(swaggerGenOptions, xmlDocFactory, includeControllerXmlComments, excludedTypesFunc?.Invoke()); } /// @@ -168,7 +166,7 @@ public static SwaggerGenOptions IncludeXmlCommentsWithRemarks( bool includeControllerXmlComments = false, Func excludedTypesFunc = default) { - return swaggerGenOptions.IncludeXmlCommentsWithRemarks(() => new XPathDocument(filePath), includeControllerXmlComments, excludedTypesFunc?.Invoke()); + return IncludeXmlCommentsWithRemarks(swaggerGenOptions, () => new XPathDocument(filePath), includeControllerXmlComments, excludedTypesFunc?.Invoke()); } /// @@ -184,41 +182,35 @@ public static SwaggerGenOptions IncludeXmlCommentsFromInheritDocs( bool includeRemarks = false, params Type[] excludedTypes) { - var documents = swaggerGenOptions.SchemaFilterDescriptors.Where(x => x.Type == typeof(XmlCommentsSchemaFilter)) - .Select(x => x.Arguments.Single()) - .Cast() - .ToList(); + var documents = swaggerGenOptions.SchemaFilterDescriptors.Where(x => x.Type == typeof(XmlCommentsSchemaFilter)).Select(x => x.Arguments.Single()).Cast().ToList(); - var inheritedDocs = documents.SelectMany( - doc => + var inheritedDocs = documents.SelectMany(doc => + { + var inheritedElements = new List<(string Name, string Cref, string Path)>(); + foreach (XPathNavigator member in doc.CreateNavigator().Select("doc/members/member/inheritdoc")) + { + string cref = member.GetAttribute("cref", string.Empty); + string path = member.GetAttribute("path", string.Empty); + member.MoveToParent(); + string parentCref = member.GetAttribute("cref", string.Empty); + string parentPath = member.GetAttribute("path", string.Empty); + if (!string.IsNullOrWhiteSpace(parentCref)) { - var inheritedElements = new List<(string Name, string Cref, string Path)>(); - foreach (XPathNavigator member in doc.CreateNavigator().Select("doc/members/member/inheritdoc")) - { - string cref = member.GetAttribute("cref", string.Empty); - string path = member.GetAttribute("path", string.Empty); - member.MoveToParent(); - string parentCref = member.GetAttribute("cref", string.Empty); - string parentPath = member.GetAttribute("path", string.Empty); - if (!string.IsNullOrWhiteSpace(parentCref)) - { - cref = parentCref; - path = parentPath; - } - - inheritedElements.Add((member.GetAttribute("name", string.Empty), cref, path)); - } - - return inheritedElements; - }) - .GroupBy(x => x.Name) - .ToDictionary(x => x.Key, x => (x.First().Cref, x.First().Path)); + cref = parentCref; + path = parentPath; + } + + inheritedElements.Add((member.GetAttribute("name", string.Empty), cref, path)); + } + + return inheritedElements; + }).GroupBy(x => x.Name).ToDictionary(x => x.Key, x => (x.First().Cref, x.First().Path)); var distinctExcludedTypes = excludedTypes?.Distinct().ToArray() ?? new Type[] { }; - swaggerGenOptions.ParameterFilter(documents, inheritedDocs, includeRemarks, distinctExcludedTypes); - swaggerGenOptions.RequestBodyFilter(documents, inheritedDocs, includeRemarks, distinctExcludedTypes); - swaggerGenOptions.SchemaFilter(documents, inheritedDocs, includeRemarks, distinctExcludedTypes); - swaggerGenOptions.OperationFilter(documents, inheritedDocs, includeRemarks, distinctExcludedTypes); + Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.ParameterFilter(swaggerGenOptions, documents, inheritedDocs, includeRemarks, distinctExcludedTypes); + Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.RequestBodyFilter(swaggerGenOptions, documents, inheritedDocs, includeRemarks, distinctExcludedTypes); + Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.SchemaFilter(swaggerGenOptions, documents, inheritedDocs, includeRemarks, distinctExcludedTypes); + Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.OperationFilter(swaggerGenOptions, documents, inheritedDocs, includeRemarks, distinctExcludedTypes); return swaggerGenOptions; } diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Extensions/XmlCommentsExtensions.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Extensions/XmlCommentsExtensions.cs index 6d35504..59a5ec1 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Extensions/XmlCommentsExtensions.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Extensions/XmlCommentsExtensions.cs @@ -1,13 +1,12 @@ -using System; +using Microsoft.OpenApi.Any; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; +using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Xml.XPath; -using Microsoft.OpenApi.Any; -using Microsoft.OpenApi.Models; -using Swashbuckle.AspNetCore.SwaggerGen; - namespace Unchase.Swashbuckle.AspNetCore.Extensions.Extensions { internal static class XmlCommentsExtensions @@ -114,15 +113,11 @@ private static MemberInfo[] GetTargets(MemberInfo memberInfo, string cref) } // Find all matching members in all interfaces and the base class. - var targets = type.GetInterfaces() - .Append(type.BaseType) - .SelectMany( - x => x.FindMembers( - memberInfo.MemberType, - BindingFlags.Instance | BindingFlags.Public, - (info, _) => info.Name == memberInfo.Name, - null)) - .ToList(); + var targets = type.GetInterfaces().Append(type.BaseType).SelectMany(x => x.FindMembers( + memberInfo.MemberType, + BindingFlags.Instance | BindingFlags.Public, + (info, _) => info.Name == memberInfo.Name, + null)).ToList(); // Try to find the target, if one is declared. if (!string.IsNullOrWhiteSpace(cref)) @@ -168,7 +163,7 @@ internal static void ApplyPropertyComments( XPathNavigator targetXmlNode; if (string.IsNullOrWhiteSpace(cref)) { - var target = memberInfo.GetTargetRecursive(inheritedDocs, cref); + var target = GetTargetRecursive(memberInfo, inheritedDocs, cref); if (target == null) { return; @@ -204,10 +199,54 @@ internal static void ApplyPropertyComments( var exampleNode = targetXmlNode.SelectSingleNode(ExampleTag); if (exampleNode != null) { - schema.Example = new OpenApiString(XmlCommentsTextHelper.Humanize(exampleNode.InnerXml)); + schema.Example = GetExampleValue(memberInfo, exampleNode); } } + private static IOpenApiAny GetExampleValue(MemberInfo memberInfo, XPathNavigator exampleNode) + { + var type = GetUnderlyingType(memberInfo); + var exampleValue = exampleNode.InnerXml; + + return string.IsNullOrEmpty(exampleValue) + ? new OpenApiNull() + : GetExampleValueAsIOpenApiAny(type, XmlCommentsTextHelper.Humanize(exampleValue)); + } + + private static IOpenApiAny GetExampleValueAsIOpenApiAny(Type type, string exampleString) + { + return type == typeof(string) + ? new OpenApiString(exampleString) + : OpenApiAnyFactory.CreateFromJson(exampleString); + } + + private static Type GetUnderlyingType(this MemberInfo member) + { + switch (member.MemberType) + { + case MemberTypes.Event: + return ((EventInfo)member).EventHandlerType; + case MemberTypes.Field: + return ((FieldInfo)member).FieldType; + case MemberTypes.Method: + return ((MethodInfo)member).ReturnType; + case MemberTypes.Property: + var propType = ((PropertyInfo)member).PropertyType; + + if (!propType.Name.ToLower().Contains("nullable")) + { + return propType; + } + + return propType.GenericTypeArguments?.Length > 0 ? propType.GenericTypeArguments[0] : propType; + + default: + throw new ArgumentException + ( + "Input MemberInfo must be if type EventInfo, FieldInfo, MethodInfo, or PropertyInfo" + ); + } + } internal static XPathNavigator GetMemberXmlNode(string memberName, List documents) { string path = $"/doc/members/member[@name='{memberName}']"; diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Factories/ApiDescriptionFactory.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Factories/ApiDescriptionFactory.cs index dd2b47e..0036c6f 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Factories/ApiDescriptionFactory.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Factories/ApiDescriptionFactory.cs @@ -1,13 +1,12 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; - -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Abstractions; using Microsoft.AspNetCore.Mvc.ApiExplorer; using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.AspNetCore.Mvc.Routing; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; namespace Unchase.Swashbuckle.AspNetCore.Extensions.Factories { @@ -70,6 +69,7 @@ internal static ApiDescription Create( actionDescriptor.AttributeRouteInfo.Template.Replace($"[{routeValue.Key}]", routeValue.Value); } + // ReSharper disable once ConstantConditionalAccessQualifier httpMethod = httpMethod ?? methodInfo?.GetCustomAttributes(true).OfType().FirstOrDefault()?.HttpMethods?.FirstOrDefault(); var apiDescription = new ApiDescription @@ -85,9 +85,7 @@ internal static ApiDescription Create( foreach (var parameter in parameterDescriptions) { // If the provided action has a matching parameter - use it to assign ParameterDescriptor & ModelMetadata - var controllerParameterDescriptor = actionDescriptor.Parameters - .OfType() - .FirstOrDefault(parameterDescriptor => parameterDescriptor.Name == parameter.Name); + var controllerParameterDescriptor = actionDescriptor.Parameters.OfType().FirstOrDefault(parameterDescriptor => parameterDescriptor.Name == parameter.Name); if (controllerParameterDescriptor != null) { @@ -242,9 +240,7 @@ private static ActionDescriptor CreateActionDescriptor(MethodInfo methodInfo) ? new AttributeRouteInfo { Template = httpMethodAttribute.Template, Name = httpMethodAttribute.Name } : null; - var parameterDescriptors = methodInfo.GetParameters() - .Select(CreateParameterDescriptor) - .ToList(); + var parameterDescriptors = methodInfo.GetParameters().Select(CreateParameterDescriptor).ToList(); var routeValues = new Dictionary { diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Factories/ModelMetadataFactory.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Factories/ModelMetadataFactory.cs index 924a8fc..6c7e40a 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Factories/ModelMetadataFactory.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Factories/ModelMetadataFactory.cs @@ -1,8 +1,7 @@ -using System; +using Microsoft.AspNetCore.Mvc.ModelBinding; +using System; using System.Reflection; -using Microsoft.AspNetCore.Mvc.ModelBinding; - namespace Unchase.Swashbuckle.AspNetCore.Extensions.Factories { /// diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/AppendActionCountToTagSummaryDocumentFilter.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/AppendActionCountToTagSummaryDocumentFilter.cs index af6c39d..ceb2995 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/AppendActionCountToTagSummaryDocumentFilter.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/AppendActionCountToTagSummaryDocumentFilter.cs @@ -1,10 +1,9 @@ -using System; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; +using System; using System.Collections.Generic; using System.Linq; -using Microsoft.OpenApi.Models; -using Swashbuckle.AspNetCore.SwaggerGen; - namespace Unchase.Swashbuckle.AspNetCore.Extensions.Filters { /// diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/ChangeResponseByHttpStatusCodeDocumentFilter.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/ChangeResponseByHttpStatusCodeDocumentFilter.cs index 8b52395..2854b77 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/ChangeResponseByHttpStatusCodeDocumentFilter.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/ChangeResponseByHttpStatusCodeDocumentFilter.cs @@ -1,8 +1,7 @@ -using System.Linq; - -using Microsoft.OpenApi.Any; +using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Models; using Swashbuckle.AspNetCore.SwaggerGen; +using System.Linq; namespace Unchase.Swashbuckle.AspNetCore.Extensions.Filters { @@ -83,7 +82,6 @@ public void Apply( { if (context.SchemaRepository.Schemas.ContainsKey(typeof(T).Name)) { - var schema = context.SchemaRepository.Schemas[typeof(T).Name]; foreach (var response in swaggerDoc.Paths.SelectMany(p => p.Value.Operations.SelectMany(o => o.Value.Responses))) { if (response.Key == _httpStatusCode.ToString()) diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/DisplayEnumsWithValuesDocumentFilter.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/DisplayEnumsWithValuesDocumentFilter.cs index 4d38405..488ab26 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/DisplayEnumsWithValuesDocumentFilter.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/DisplayEnumsWithValuesDocumentFilter.cs @@ -1,9 +1,8 @@ -using System; -using System.Linq; - -using Microsoft.Extensions.Options; +using Microsoft.Extensions.Options; using Microsoft.OpenApi.Models; using Swashbuckle.AspNetCore.SwaggerGen; +using System; +using System.Linq; using Unchase.Swashbuckle.AspNetCore.Extensions.Extensions; using Unchase.Swashbuckle.AspNetCore.Extensions.Options; @@ -135,9 +134,11 @@ public void Apply( { if (requestBodyContent.Value.Schema?.Reference?.Id != null) { + // ReSharper disable once AssignNullToNotNullAttribute var schema = context.SchemaRepository.Schemas[requestBodyContent.Value.Schema?.Reference?.Id]; if (schema != null) { + // ReSharper disable once PossibleNullReferenceException requestBodyContent.Value.Schema.Description = schema.Description; requestBodyContent.Value.Schema.Extensions = schema.Extensions; } diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/HidePathsAndDefinitionsByRolesDocumentFilter.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/HidePathsAndDefinitionsByRolesDocumentFilter.cs index 174a47a..6fec7cf 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/HidePathsAndDefinitionsByRolesDocumentFilter.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/HidePathsAndDefinitionsByRolesDocumentFilter.cs @@ -1,12 +1,11 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; - -using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.OpenApi.Models; using Swashbuckle.AspNetCore.SwaggerGen; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; namespace Unchase.Swashbuckle.AspNetCore.Extensions.Filters { @@ -47,11 +46,13 @@ private static List GetRequiredDefinitions( if (reference?.Id != null) { + // ReSharper disable once ConstantConditionalAccessQualifier if (!result.Contains(reference?.Id)) { result.Add(reference.Id); } + // ReSharper disable once ConstantConditionalAccessQualifier var responseSchema = schemas[reference?.Id]; if (responseSchema != null) { @@ -359,7 +360,7 @@ public void Apply( var paths = new Dictionary<(MethodInfo, Type), string>(); foreach (var actionDescriptor in apiDescriptions.Select(ad => ad.ActionDescriptor)) { - var t = ((ControllerActionDescriptor) actionDescriptor).ControllerTypeInfo.AsType(); + var t = ((ControllerActionDescriptor)actionDescriptor).ControllerTypeInfo.AsType(); paths.Add((((ControllerActionDescriptor)actionDescriptor).MethodInfo, t), actionDescriptor.AttributeRouteInfo.Template); } diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/InheritDocOperationFilter.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/InheritDocOperationFilter.cs index 7144917..508cf96 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/InheritDocOperationFilter.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/InheritDocOperationFilter.cs @@ -1,10 +1,9 @@ -using System; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; +using System; using System.Collections.Generic; using System.Linq; using System.Xml.XPath; - -using Microsoft.OpenApi.Models; -using Swashbuckle.AspNetCore.SwaggerGen; using Unchase.Swashbuckle.AspNetCore.Extensions.Extensions; namespace Unchase.Swashbuckle.AspNetCore.Extensions.Filters @@ -16,7 +15,6 @@ internal class InheritDocOperationFilter : private const string SummaryTag = "summary"; private const string RemarksTag = "remarks"; - private const string ExampleTag = "example"; private const string ParamTag = "param"; private const string ParamXPath = "/doc/members/member/param[@name='{0}']"; private readonly bool _includeRemarks; diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/InheritDocParameterFilter.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/InheritDocParameterFilter.cs index 315bd69..2018289 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/InheritDocParameterFilter.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/InheritDocParameterFilter.cs @@ -1,11 +1,10 @@ -using System; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; +using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Xml.XPath; - -using Microsoft.OpenApi.Models; -using Swashbuckle.AspNetCore.SwaggerGen; using Unchase.Swashbuckle.AspNetCore.Extensions.Extensions; namespace Unchase.Swashbuckle.AspNetCore.Extensions.Filters @@ -21,7 +20,6 @@ internal class InheritDocParameterFilter : private const string SummaryTag = "summary"; private const string RemarksTag = "remarks"; - private const string ExampleTag = "example"; private readonly bool _includeRemarks; private readonly List _documents; private readonly Dictionary _inheritedDocs; diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/InheritDocRequestBodyFilter.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/InheritDocRequestBodyFilter.cs index 458b5cc..d85b5f1 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/InheritDocRequestBodyFilter.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/InheritDocRequestBodyFilter.cs @@ -1,10 +1,9 @@ -using System; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; +using System; using System.Collections.Generic; using System.Linq; using System.Xml.XPath; - -using Microsoft.OpenApi.Models; -using Swashbuckle.AspNetCore.SwaggerGen; using Unchase.Swashbuckle.AspNetCore.Extensions.Extensions; namespace Unchase.Swashbuckle.AspNetCore.Extensions.Filters @@ -20,7 +19,6 @@ internal class InheritDocRequestBodyFilter : private const string SummaryTag = "summary"; private const string RemarksTag = "remarks"; - private const string ExampleTag = "example"; private readonly bool _includeRemarks; private readonly List _documents; private readonly Dictionary _inheritedDocs; diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/InheritDocSchemaFilter.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/InheritDocSchemaFilter.cs index 35f0634..05e89d8 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/InheritDocSchemaFilter.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/InheritDocSchemaFilter.cs @@ -1,10 +1,9 @@ -using System; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; +using System; using System.Collections.Generic; using System.Linq; using System.Xml.XPath; - -using Microsoft.OpenApi.Models; -using Swashbuckle.AspNetCore.SwaggerGen; using Unchase.Swashbuckle.AspNetCore.Extensions.Extensions; namespace Unchase.Swashbuckle.AspNetCore.Extensions.Filters @@ -20,7 +19,6 @@ internal class InheritDocSchemaFilter : private const string SummaryTag = "summary"; private const string RemarksTag = "remarks"; - private const string ExampleTag = "example"; private readonly bool _includeRemarks; private readonly List _documents; private readonly Dictionary _inheritedDocs; diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/TagOrderByNameDocumentFilter.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/TagOrderByNameDocumentFilter.cs index 6993ee8..20d74da 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/TagOrderByNameDocumentFilter.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/TagOrderByNameDocumentFilter.cs @@ -1,7 +1,6 @@ -using System.Linq; - -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Models; using Swashbuckle.AspNetCore.SwaggerGen; +using System.Linq; namespace Unchase.Swashbuckle.AspNetCore.Extensions.Filters { diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XEnumNamesParameterFilter.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XEnumNamesParameterFilter.cs index 866f06d..a6d0c06 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XEnumNamesParameterFilter.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XEnumNamesParameterFilter.cs @@ -1,13 +1,12 @@ -using System; +using Microsoft.Extensions.Options; +using Microsoft.OpenApi.Any; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; +using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Xml.XPath; - -using Microsoft.Extensions.Options; -using Microsoft.OpenApi.Any; -using Microsoft.OpenApi.Models; -using Swashbuckle.AspNetCore.SwaggerGen; using Unchase.Swashbuckle.AspNetCore.Extensions.Extensions; using Unchase.Swashbuckle.AspNetCore.Extensions.Options; @@ -50,6 +49,7 @@ public XEnumNamesParameterFilter( _applyFiler = options.Value?.ApplyParameterFilter ?? false; _xEnumNamesAlias = options.Value?.XEnumNamesAlias; _xEnumDescriptionsAlias = options.Value?.XEnumDescriptionsAlias; + // ReSharper disable once PossibleNullReferenceException _includeDescriptionFromAttribute = options.Value.IncludeDescriptions; _newLine = options.Value.NewLine; foreach (var filePath in options.Value?.IncludedXmlCommentsPaths ?? new HashSet()) @@ -91,11 +91,7 @@ public void Apply( if (typeInfo.IsEnum) { var names = Enum - .GetNames(typeInfo) - .Select(name => (Enum.Parse(typeInfo, name), new OpenApiString(name))) - .GroupBy(x => x.Item1) - .Select(x => x.LastOrDefault().Item2) - .ToList(); + .GetNames(typeInfo).Select(name => (Enum.Parse(typeInfo, name), new OpenApiString(name))).GroupBy(x => x.Item1).Select(x => x.LastOrDefault().Item2).ToList(); enumsArray.AddRange(names); if (!parameter.Extensions.ContainsKey(_xEnumNamesAlias) && enumsArray.Any()) { @@ -105,9 +101,7 @@ public void Apply( if (_includeXEnumDescriptions) { enumsDescriptionsArray.AddRange(EnumTypeExtensions - .GetEnumValuesDescription(typeInfo, _descriptionSources, _xmlNavigators, _includeXEnumRemarks) - .GroupBy(x => x.EnumValue) - .Select(x => x.LastOrDefault().EnumDescription)); + .GetEnumValuesDescription(typeInfo, _descriptionSources, _xmlNavigators, _includeXEnumRemarks).GroupBy(x => x.EnumValue).Select(x => x.LastOrDefault().EnumDescription)); if (!parameter.Extensions.ContainsKey(_xEnumDescriptionsAlias) && enumsDescriptionsArray.Any()) { parameter.Extensions.Add(_xEnumDescriptionsAlias, enumsDescriptionsArray); @@ -121,11 +115,7 @@ public void Apply( if (genericArgumentType.IsEnum) { var names = Enum - .GetNames(genericArgumentType) - .Select(name => (Enum.Parse(genericArgumentType, name), new OpenApiString(name))) - .GroupBy(x => x.Item1) - .Select(x => x.LastOrDefault().Item2) - .ToList(); + .GetNames(genericArgumentType).Select(name => (Enum.Parse(genericArgumentType, name), new OpenApiString(name))).GroupBy(x => x.Item1).Select(x => x.LastOrDefault().Item2).ToList(); enumsArray.AddRange(names); if (!parameter.Extensions.ContainsKey(_xEnumNamesAlias) && enumsArray.Any()) { @@ -135,9 +125,7 @@ public void Apply( if (_includeXEnumDescriptions) { enumsDescriptionsArray.AddRange(EnumTypeExtensions - .GetEnumValuesDescription(genericArgumentType, _descriptionSources, _xmlNavigators, _includeXEnumRemarks) - .GroupBy(x => x.EnumValue) - .Select(x => x.LastOrDefault().EnumDescription)); + .GetEnumValuesDescription(genericArgumentType, _descriptionSources, _xmlNavigators, _includeXEnumRemarks).GroupBy(x => x.EnumValue).Select(x => x.LastOrDefault().EnumDescription)); if (!parameter.Extensions.ContainsKey(_xEnumDescriptionsAlias) && enumsDescriptionsArray.Any()) { parameter.Extensions.Add(_xEnumDescriptionsAlias, enumsDescriptionsArray); diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XEnumNamesSchemaFilter.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XEnumNamesSchemaFilter.cs index bab25a2..6691a8b 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XEnumNamesSchemaFilter.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XEnumNamesSchemaFilter.cs @@ -1,14 +1,13 @@ -using System; +using Microsoft.Extensions.Options; +using Microsoft.OpenApi.Any; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; +using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Xml.XPath; - -using Microsoft.Extensions.Options; -using Microsoft.OpenApi.Any; -using Microsoft.OpenApi.Models; -using Swashbuckle.AspNetCore.SwaggerGen; using Unchase.Swashbuckle.AspNetCore.Extensions.Extensions; using Unchase.Swashbuckle.AspNetCore.Extensions.Options; @@ -84,11 +83,7 @@ public void Apply( if (typeInfo.IsEnum) { var names = Enum - .GetNames(context.Type) - .Select(name => (Enum.Parse(context.Type, name), new OpenApiString(name))) - .GroupBy(x => x.Item1) - .Select(x => x.LastOrDefault().Item2) - .ToList(); + .GetNames(context.Type).Select(name => (Enum.Parse(context.Type, name), new OpenApiString(name))).GroupBy(x => x.Item1).Select(x => x.LastOrDefault().Item2).ToList(); enumsArray.AddRange(names); if (!schema.Extensions.ContainsKey(_xEnumNamesAlias) && enumsArray.Any()) { @@ -98,9 +93,7 @@ public void Apply( if (_includeXEnumDescriptions) { enumsDescriptionsArray.AddRange(EnumTypeExtensions - .GetEnumValuesDescription(context.Type, _descriptionSources, _xmlNavigators, _includeXEnumRemarks) - .GroupBy(x => x.EnumValue) - .Select(x => x.LastOrDefault().EnumDescription)); + .GetEnumValuesDescription(context.Type, _descriptionSources, _xmlNavigators, _includeXEnumRemarks).GroupBy(x => x.EnumValue).Select(x => x.LastOrDefault().EnumDescription)); if (!schema.Extensions.ContainsKey(_xEnumDescriptionsAlias) && enumsDescriptionsArray.Any()) { schema.Extensions.Add(_xEnumDescriptionsAlias, enumsDescriptionsArray); @@ -126,11 +119,7 @@ public void Apply( if (propertySchema != null) { var names = Enum - .GetNames(genericArgumentType) - .Select(name => (Enum.Parse(genericArgumentType, name), new OpenApiString(name))) - .GroupBy(x => x.Item1) - .Select(x => x.LastOrDefault().Item2) - .ToList(); + .GetNames(genericArgumentType).Select(name => (Enum.Parse(genericArgumentType, name), new OpenApiString(name))).GroupBy(x => x.Item1).Select(x => x.LastOrDefault().Item2).ToList(); enumsArray.AddRange(names); if (!schemaPropertyValue.Extensions.ContainsKey(_xEnumNamesAlias) && enumsArray.Any()) { @@ -140,9 +129,7 @@ public void Apply( if (_includeXEnumDescriptions) { enumsDescriptionsArray.AddRange(EnumTypeExtensions - .GetEnumValuesDescription(genericArgumentType, _descriptionSources, _xmlNavigators, _includeXEnumRemarks) - .GroupBy(x => x.EnumValue) - .Select(x => x.LastOrDefault().EnumDescription)); + .GetEnumValuesDescription(genericArgumentType, _descriptionSources, _xmlNavigators, _includeXEnumRemarks).GroupBy(x => x.EnumValue).Select(x => x.LastOrDefault().EnumDescription)); if (!schemaPropertyValue.Extensions.ContainsKey(_xEnumDescriptionsAlias) && enumsDescriptionsArray.Any()) { schemaPropertyValue.Extensions.Add(_xEnumDescriptionsAlias, enumsDescriptionsArray); diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XmlCommentsWithRemarksDocumentFilter.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XmlCommentsWithRemarksDocumentFilter.cs index 7d56363..b53ce38 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XmlCommentsWithRemarksDocumentFilter.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XmlCommentsWithRemarksDocumentFilter.cs @@ -1,12 +1,11 @@ -using System; +using Microsoft.AspNetCore.Mvc.Controllers; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; +using System; using System.Collections.Generic; using System.Linq; using System.Xml.XPath; -using Microsoft.AspNetCore.Mvc.Controllers; -using Microsoft.OpenApi.Models; -using Swashbuckle.AspNetCore.SwaggerGen; - namespace Unchase.Swashbuckle.AspNetCore.Extensions.Filters { /// @@ -54,11 +53,7 @@ public void Apply( DocumentFilterContext context) { // Collect (unique) controller names and types in a dictionary - var controllerNamesAndTypes = context.ApiDescriptions - .Select(apiDesc => apiDesc.ActionDescriptor as ControllerActionDescriptor) - .SkipWhile(actionDesc => actionDesc == null) - .GroupBy(actionDesc => actionDesc.ControllerName) - .Select(group => new KeyValuePair(group.Key, group.First().ControllerTypeInfo.AsType())); + var controllerNamesAndTypes = context.ApiDescriptions.Select(apiDesc => apiDesc.ActionDescriptor as ControllerActionDescriptor).SkipWhile(actionDesc => actionDesc == null).GroupBy(actionDesc => actionDesc.ControllerName).Select(group => new KeyValuePair(group.Key, group.First().ControllerTypeInfo.AsType())); foreach (var nameAndType in controllerNamesAndTypes) { diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XmlCommentsWithRemarksParameterFilter.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XmlCommentsWithRemarksParameterFilter.cs index edd032c..e458903 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XmlCommentsWithRemarksParameterFilter.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XmlCommentsWithRemarksParameterFilter.cs @@ -1,11 +1,10 @@ -using System; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; +using System; using System.Linq; using System.Reflection; using System.Xml.XPath; -using Microsoft.OpenApi.Models; -using Swashbuckle.AspNetCore.SwaggerGen; - namespace Unchase.Swashbuckle.AspNetCore.Extensions.Filters { /// diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XmlCommentsWithRemarksRequestBodyFilter.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XmlCommentsWithRemarksRequestBodyFilter.cs index 51e1dc2..5996457 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XmlCommentsWithRemarksRequestBodyFilter.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XmlCommentsWithRemarksRequestBodyFilter.cs @@ -1,11 +1,10 @@ -using System; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; +using System; using System.Linq; using System.Reflection; using System.Xml.XPath; -using Microsoft.OpenApi.Models; -using Swashbuckle.AspNetCore.SwaggerGen; - namespace Unchase.Swashbuckle.AspNetCore.Extensions.Filters { /// @@ -62,6 +61,7 @@ public void Apply( if (propertyInfo != null) { ApplyPropertyTags(requestBody, propertyInfo); + // ReSharper disable once RedundantJumpStatement return; } } diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XmlCommentsWithRemarksSchemaFilter.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XmlCommentsWithRemarksSchemaFilter.cs index e382835..c01c830 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XmlCommentsWithRemarksSchemaFilter.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Filters/XmlCommentsWithRemarksSchemaFilter.cs @@ -1,11 +1,10 @@ -using System; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; +using System; using System.Linq; using System.Reflection; using System.Xml.XPath; -using Microsoft.OpenApi.Models; -using Swashbuckle.AspNetCore.SwaggerGen; - namespace Unchase.Swashbuckle.AspNetCore.Extensions.Filters { /// 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 d6c678d..dfca255 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Unchase.Swashbuckle.AspNetCore.Extensions.csproj +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Unchase.Swashbuckle.AspNetCore.Extensions.csproj @@ -13,13 +13,15 @@ Swagger;Swashbuckle;Filters;NSwag 7.3 - https://github.com/unchase/Unchase.Swashbuckle.AspNetCore.Extensions/blob/master/assets/icon.png?raw=true + icon.png 2.7.1 2.7.1.0 2.7.1.0 - True Unchase.Swashbuckle.AspNetCore.Extensions.xml Unchase.Swashbuckle.AspNetCore.Extensions + readme.md + False + False @@ -27,7 +29,18 @@ - + + True + \ + + + True + \ + + + + + diff --git a/test/Unchase.Swashbuckle.AspNetCore.Extensions.Tests/Models/FreeText.cs b/test/Unchase.Swashbuckle.AspNetCore.Extensions.Tests/Models/FreeText.cs new file mode 100644 index 0000000..158bde2 --- /dev/null +++ b/test/Unchase.Swashbuckle.AspNetCore.Extensions.Tests/Models/FreeText.cs @@ -0,0 +1,14 @@ +// ReSharper disable ClassNeverInstantiated.Global +namespace Unchase.Swashbuckle.AspNetCore.Extensions.Tests.Models +{ + /// + /// Some free text information. + /// + public class FreeText + { + /// + /// The Text of FreeText + /// + public string Text { get; set; } + } +} diff --git a/test/Unchase.Swashbuckle.AspNetCore.Extensions.Tests/Models/IInheritDocClass.cs b/test/Unchase.Swashbuckle.AspNetCore.Extensions.Tests/Models/IInheritDocClass.cs index fb5765f..b423ba3 100644 --- a/test/Unchase.Swashbuckle.AspNetCore.Extensions.Tests/Models/IInheritDocClass.cs +++ b/test/Unchase.Swashbuckle.AspNetCore.Extensions.Tests/Models/IInheritDocClass.cs @@ -1,4 +1,9 @@ -namespace Unchase.Swashbuckle.AspNetCore.Extensions.Tests.Models +// ReSharper disable CommentTypo +// ReSharper disable IdentifierTypo + +using System.Collections.Generic; + +namespace Unchase.Swashbuckle.AspNetCore.Extensions.Tests.Models { /// /// InheritDocClass - inheritdoc @@ -14,6 +19,80 @@ public interface IInheritDocClass : IInheritDocCommon /// /// Name remarks - inheritdoc /// + /// Donald Duck string Name { get; set; } + + /// + /// Age - inheritdoc + /// + /// + /// Age remarks - inheritdoc + /// + /// 75 + int? Age { get; set; } + + /// + /// Weight - inheritdoc + /// + /// + /// Weight remarks - inheritdoc + /// + /// 0.174 + float? Weight { get; set; } + + /// + /// NumberOfFeet - inheritdoc + /// + /// + /// NumberOfFeet remarks - inheritdoc + /// + /// 2 + int? NumberOfFeet { get; set; } + + /// + /// AByte - inheritdoc + /// + /// + /// AByte remarks - inheritdoc + /// + /// 1 + byte? AByte { get; set; } + + /// + /// AShort - inheritdoc + /// + /// + /// AShort remarks - inheritdoc + /// + /// 1234 + short? AShort { get; set; } + + /// + /// ALong - inheritdoc + /// + /// + /// ALong remarks - inheritdoc + /// + /// 1234 + long? ALong { get; set; } + + /// + /// AnArray - inheritdoc + /// + /// + /// AnArray remarks - inheritdoc + /// + /// [ "One", "Two", "Three" ] + string[] AnArray { get; set; } + + /// + /// SomeFreeTexts - inheritdoc + /// + /// + /// SomeFreeTexts remarks - inheritdoc + /// + /// [{"text": "KAROSSERI OFÖRÄNDRAT, FLAK TÄCK MED KÅPA SOM HAR ALLA LUCKOR FASTLÅSTA.B"}, {"text": "AKLÄM FASTLÅST. INGEN MÖJLIGHET TILL LAST FINNS.BAKDÖRRAR FASTSVETSADE"}] + IEnumerable SomeFreeTexts { get; set; } + } } diff --git a/test/Unchase.Swashbuckle.AspNetCore.Extensions.Tests/Models/InheritDocClass.cs b/test/Unchase.Swashbuckle.AspNetCore.Extensions.Tests/Models/InheritDocClass.cs index e55ebb7..2e439a6 100644 --- a/test/Unchase.Swashbuckle.AspNetCore.Extensions.Tests/Models/InheritDocClass.cs +++ b/test/Unchase.Swashbuckle.AspNetCore.Extensions.Tests/Models/InheritDocClass.cs @@ -1,4 +1,6 @@ -namespace Unchase.Swashbuckle.AspNetCore.Extensions.Tests.Models +using System.Collections.Generic; + +namespace Unchase.Swashbuckle.AspNetCore.Extensions.Tests.Models { /// public class InheritDocClass : IInheritDocClass @@ -6,6 +8,30 @@ public class InheritDocClass : IInheritDocClass /// public string Name { get; set; } + /// + public int? Age { get; set; } + + /// + public float? Weight { get; set; } + + /// + public int? NumberOfFeet { get; set; } + + /// + public byte? AByte { get; set; } + + /// + public short? AShort { get; set; } + + /// + public long? ALong { get; set; } + + /// + public string[] AnArray { get; set; } + + /// + public IEnumerable SomeFreeTexts { get; set; } + /// public string Common { get; set; } diff --git a/test/Unchase.Swashbuckle.AspNetCore.Extensions.Tests/Unchase.Swashbuckle.AspNetCore.Extensions.Tests.csproj b/test/Unchase.Swashbuckle.AspNetCore.Extensions.Tests/Unchase.Swashbuckle.AspNetCore.Extensions.Tests.csproj index 2072f02..7478b4e 100644 --- a/test/Unchase.Swashbuckle.AspNetCore.Extensions.Tests/Unchase.Swashbuckle.AspNetCore.Extensions.Tests.csproj +++ b/test/Unchase.Swashbuckle.AspNetCore.Extensions.Tests/Unchase.Swashbuckle.AspNetCore.Extensions.Tests.csproj @@ -1,8 +1,7 @@  - netcoreapp2.2 - + netcoreapp2.2.8 false diff --git a/test/Unchase.Swashbuckle.AspNetCore.Extensions.Tests/Unchase.Swashbuckle.AspNetCore.Extensions.Tests.xml b/test/Unchase.Swashbuckle.AspNetCore.Extensions.Tests/Unchase.Swashbuckle.AspNetCore.Extensions.Tests.xml index ef35883..68204ba 100644 --- a/test/Unchase.Swashbuckle.AspNetCore.Extensions.Tests/Unchase.Swashbuckle.AspNetCore.Extensions.Tests.xml +++ b/test/Unchase.Swashbuckle.AspNetCore.Extensions.Tests/Unchase.Swashbuckle.AspNetCore.Extensions.Tests.xml @@ -395,6 +395,16 @@ This is second + + + Some free text information. + + + + + The Text of FreeText + + InheritDocClass - inheritdoc @@ -410,6 +420,79 @@ Name remarks - inheritdoc + Donald Duck + + + + Age - inheritdoc + + + Age remarks - inheritdoc + + 75 + + + + Weight - inheritdoc + + + Weight remarks - inheritdoc + + 0.174 + + + + NumberOfFeet - inheritdoc + + + NumberOfFeet remarks - inheritdoc + + 2 + + + + AByte - inheritdoc + + + AByte remarks - inheritdoc + + 1 + + + + AShort - inheritdoc + + + AShort remarks - inheritdoc + + 1234 + + + + ALong - inheritdoc + + + ALong remarks - inheritdoc + + 1234 + + + + AnArray - inheritdoc + + + AnArray remarks - inheritdoc + + [ "One", "Two", "Three" ] + + + + SomeFreeTexts - inheritdoc + + + SomeFreeTexts remarks - inheritdoc + + [{"text": "KAROSSERI OFÖRÄNDRAT, FLAK TÄCK MED KÅPA SOM HAR ALLA LUCKOR FASTLÅSTA.B"}, {"text": "AKLÄM FASTLÅST. INGEN MÖJLIGHET TILL LAST FINNS.BAKDÖRRAR FASTSVETSADE"}] @@ -438,6 +521,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/WebApi3.1-Swashbuckle/Program.cs b/test/WebApi3.1-Swashbuckle/Program.cs index ca876e4..db0b512 100644 --- a/test/WebApi3.1-Swashbuckle/Program.cs +++ b/test/WebApi3.1-Swashbuckle/Program.cs @@ -12,8 +12,7 @@ public static void Main(string[] args) } public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => + Host.CreateDefaultBuilder(args).ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); }); diff --git a/test/WebApi3.1-Swashbuckle/Startup.cs b/test/WebApi3.1-Swashbuckle/Startup.cs index 932a19b..96ef2df 100644 --- a/test/WebApi3.1-Swashbuckle/Startup.cs +++ b/test/WebApi3.1-Swashbuckle/Startup.cs @@ -23,7 +23,7 @@ public class Startup public void ConfigureServices(IServiceCollection services) { services.AddControllers(); - //.AddJsonOptions(options => options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter())); + //.AddJsonOptions(options => options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter())); // Register the Swagger generator services.AddSwaggerGen(options => @@ -138,15 +138,13 @@ public void ConfigureServices(IServiceCollection services) // responseExample: new TodoItem { Tag = Tag.Workout, Id = 111, IsComplete = false, Name = "test" }); // some class for response examples // change responses for specific HTTP status code ("400" (HttpStatusCode.BadRequest)) - options.ChangeAllResponsesByHttpStatusCode( - httpStatusCode: HttpStatusCode.BadRequest, + options.ChangeAllResponsesByHttpStatusCode(httpStatusCode: HttpStatusCode.BadRequest, responseDescription: "400 status code description", - responseExampleOption: ResponseExampleOptions.Clear, // claer response examples + responseExampleOption: ResponseExampleOptions.Clear, // clear response examples responseExample: new ComplicatedClass()); // some class for response examples // change responses for specific HTTP status code ("201" (StatusCodes.Status201Created)) - options.ChangeAllResponsesByHttpStatusCode( - httpStatusCode: StatusCodes.Status201Created, + options.ChangeAllResponsesByHttpStatusCode(httpStatusCode: StatusCodes.Status201Created, responseDescription: "201 status code description", responseExampleOption: ResponseExampleOptions.None, // do nothing with response examples responseExample: new ComplicatedClass()); // some class for response examples @@ -169,14 +167,14 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) c.PreSerializeFilters.Add((openApiDoc, httpRequest) => { // remove Paths and Components from OpenApi documentation for specific controller action without accepted roles - openApiDoc.RemovePathsAndComponentsWithoutAcceptedRolesFor(controller => nameof(controller.HidedAction), new List {"AcceptedRole"}); + openApiDoc.RemovePathsAndComponentsWithoutAcceptedRolesFor(controller => nameof(controller.HidedAction), new List { "AcceptedRole" }); // or //openApiDoc.RemovePathsAndComponentsWithoutAcceptedRolesFor(nameof(HidedController.HidedAction), new List { "AcceptedRole" }); // remove Paths and Components from OpenApi documentation for all controller actions without accepted roles - openApiDoc.RemovePathsAndComponentsWithoutAcceptedRolesForController(new List {"AcceptedRole"}); + openApiDoc.RemovePathsAndComponentsWithoutAcceptedRolesForController(new List { "AcceptedRole" }); // or you can get accepted roles by httpRequest like this: //openApiDoc.RemovePathsAndComponentsWithoutAcceptedRolesForController(GetAcceptedRolesByRemoteIp(httpRequest.HttpContext.Connection.RemoteIpAddress)); diff --git a/test/WebApi5-Swashbuckle/Program.cs b/test/WebApi5-Swashbuckle/Program.cs index a224011..5bf028f 100644 --- a/test/WebApi5-Swashbuckle/Program.cs +++ b/test/WebApi5-Swashbuckle/Program.cs @@ -12,8 +12,7 @@ public static void Main(string[] args) } public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => + Host.CreateDefaultBuilder(args).ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); }); diff --git a/test/WebApi5-Swashbuckle/Startup.cs b/test/WebApi5-Swashbuckle/Startup.cs index 4d61226..b613425 100644 --- a/test/WebApi5-Swashbuckle/Startup.cs +++ b/test/WebApi5-Swashbuckle/Startup.cs @@ -1,20 +1,23 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using Microsoft.OpenApi.Models; using System; using System.Collections.Generic; using System.IO; using System.Net; -using Microsoft.AspNetCore.Http; -using Microsoft.OpenApi.Models; using Unchase.Swashbuckle.AspNetCore.Extensions.Extensions; using Unchase.Swashbuckle.AspNetCore.Extensions.Filters; using Unchase.Swashbuckle.AspNetCore.Extensions.Options; using Unchase.Swashbuckle.AspNetCore.Extensions.Tests.Controllers; using Unchase.Swashbuckle.AspNetCore.Extensions.Tests.Models; +// ReSharper disable ArrangeNamespaceBody +#pragma warning disable IDE0161 + namespace WebApi5_Swashbuckle { #pragma warning disable CS1591 @@ -25,6 +28,7 @@ public Startup(IConfiguration configuration) Configuration = configuration; } + // ReSharper disable once UnusedAutoPropertyAccessor.Global public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. @@ -142,15 +146,13 @@ public void ConfigureServices(IServiceCollection services) // responseExample: new TodoItem { Tag = Tag.Workout, Id = 111, IsComplete = false, Name = "test" }); // some class for response examples // change responses for specific HTTP status code ("400" (HttpStatusCode.BadRequest)) - options.ChangeAllResponsesByHttpStatusCode( - httpStatusCode: HttpStatusCode.BadRequest, + options.ChangeAllResponsesByHttpStatusCode(httpStatusCode: HttpStatusCode.BadRequest, responseDescription: "400 status code description", - responseExampleOption: ResponseExampleOptions.Clear, // claer response examples + responseExampleOption: ResponseExampleOptions.Clear, // clear response examples responseExample: new ComplicatedClass()); // some class for response examples // change responses for specific HTTP status code ("201" (StatusCodes.Status201Created)) - options.ChangeAllResponsesByHttpStatusCode( - httpStatusCode: StatusCodes.Status201Created, + options.ChangeAllResponsesByHttpStatusCode(httpStatusCode: StatusCodes.Status201Created, responseDescription: "201 status code description", responseExampleOption: ResponseExampleOptions.None, // do nothing with response examples responseExample: new ComplicatedClass()); // some class for response examples diff --git a/test/WebApi7-Swashbuckle/GlobalSuppressions.cs b/test/WebApi7-Swashbuckle/GlobalSuppressions.cs new file mode 100644 index 0000000..44b8725 --- /dev/null +++ b/test/WebApi7-Swashbuckle/GlobalSuppressions.cs @@ -0,0 +1,8 @@ +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage("Usage", "ASP0014:Suggest using top level route registrations", Justification = "")] diff --git a/test/WebApi7-Swashbuckle/Program.cs b/test/WebApi7-Swashbuckle/Program.cs index 163d659..b4ebf80 100644 --- a/test/WebApi7-Swashbuckle/Program.cs +++ b/test/WebApi7-Swashbuckle/Program.cs @@ -1,6 +1,5 @@ -using System.Net; - using Microsoft.OpenApi.Models; +using System.Net; using Unchase.Swashbuckle.AspNetCore.Extensions.Extensions; using Unchase.Swashbuckle.AspNetCore.Extensions.Filters; using Unchase.Swashbuckle.AspNetCore.Extensions.Options; @@ -123,15 +122,13 @@ // responseExample: new TodoItem { Tag = Tag.Workout, Id = 111, IsComplete = false, Name = "test" }); // some class for response examples // change responses for specific HTTP status code ("400" (HttpStatusCode.BadRequest)) - options.ChangeAllResponsesByHttpStatusCode( - httpStatusCode: HttpStatusCode.BadRequest, + options.ChangeAllResponsesByHttpStatusCode(httpStatusCode: HttpStatusCode.BadRequest, responseDescription: "400 status code description", - responseExampleOption: ResponseExampleOptions.Clear, // claer response examples + responseExampleOption: ResponseExampleOptions.Clear, // clear response examples responseExample: new ComplicatedClass()); // some class for response examples // change responses for specific HTTP status code ("201" (StatusCodes.Status201Created)) - options.ChangeAllResponsesByHttpStatusCode( - httpStatusCode: StatusCodes.Status201Created, + options.ChangeAllResponsesByHttpStatusCode(httpStatusCode: StatusCodes.Status201Created, responseDescription: "201 status code description", responseExampleOption: ResponseExampleOptions.None, // do nothing with response examples responseExample: new ComplicatedClass()); // some class for response examples @@ -150,7 +147,7 @@ // enable middleware to serve generated Swagger as a JSON endpoint. app.UseSwagger(c => { - c.PreSerializeFilters.Add((openApiDoc, httpRequest) => + c.PreSerializeFilters.Add((openApiDoc, _) => { // remove Paths and Components from OpenApi documentation for specific controller action without accepted roles openApiDoc.RemovePathsAndComponentsWithoutAcceptedRolesFor(controller => nameof(controller.HidedAction), new List { "AcceptedRole" }); diff --git a/test/WebApi8-Swashbuckle/Controllers/RecordsController.cs b/test/WebApi8-Swashbuckle/Controllers/RecordsController.cs new file mode 100644 index 0000000..db30dfa --- /dev/null +++ b/test/WebApi8-Swashbuckle/Controllers/RecordsController.cs @@ -0,0 +1,32 @@ +using Microsoft.AspNetCore.Mvc; +using Swashbuckle.AspNetCore.Annotations; +using WebApi8_Swashbuckle.Models; + +namespace WebApi8_Swashbuckle.Controllers; + +/// +/// Records controller +/// +[Produces("application/json")] +[Route("api/[controller]")] +[ApiController] +[SwaggerTag("Records controller")] +[ProducesResponseType(StatusCodes.Status200OK)] +[ProducesResponseType(StatusCodes.Status201Created)] +[ProducesResponseType(StatusCodes.Status400BadRequest)] +[ProducesResponseType(420)] +public class RecordsController : ControllerBase +{ + /// + /// Get record response action + /// + /// + /// Get record response action remarks + /// + [HttpGet("get-record-response")] + [ProducesResponseType(StatusCodes.Status200OK)] + public ActionResult GetRecordResponseAction() + { + return Ok(new RecordResponse(5, "name1")); + } +} \ No newline at end of file diff --git a/test/WebApi8-Swashbuckle/Models/RecordResponse.cs b/test/WebApi8-Swashbuckle/Models/RecordResponse.cs new file mode 100644 index 0000000..4af75fc --- /dev/null +++ b/test/WebApi8-Swashbuckle/Models/RecordResponse.cs @@ -0,0 +1,11 @@ +namespace WebApi8_Swashbuckle.Models; + +/// +/// Record response. +/// +/// +/// Just record response. +/// +/// The code. +/// The name. +public record RecordResponse(int Code, string Name); \ No newline at end of file diff --git a/test/WebApi8-Swashbuckle/Program.cs b/test/WebApi8-Swashbuckle/Program.cs new file mode 100644 index 0000000..a467d44 --- /dev/null +++ b/test/WebApi8-Swashbuckle/Program.cs @@ -0,0 +1,180 @@ +using Microsoft.OpenApi.Models; +using System.Net; +using Unchase.Swashbuckle.AspNetCore.Extensions.Extensions; +using Unchase.Swashbuckle.AspNetCore.Extensions.Filters; +using Unchase.Swashbuckle.AspNetCore.Extensions.Options; +using Unchase.Swashbuckle.AspNetCore.Extensions.Tests.Controllers; +using Unchase.Swashbuckle.AspNetCore.Extensions.Tests.Models; + +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. +builder.Services.AddControllers(); + +// Register the Swagger generator +builder.Services.AddSwaggerGen(options => +{ + options.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" }); + + // use it if you want to hide Paths and Definitions from OpenApi documentation correctly + options.UseAllOfToExtendReferenceSchemas(); + + // if you want to add xml comments from summary and remarks into the swagger documentation, first of all add: + // you can exclude remarks for concrete types + var xmlFilePath = Path.Combine(AppContext.BaseDirectory, "Unchase.Swashbuckle.AspNetCore.Extensions.Tests.xml"); + options.IncludeXmlCommentsWithRemarks(filePath: xmlFilePath, includeControllerXmlComments: false, + typeof(ComplicatedClass), + typeof(InnerEnum)); + + var xmlFilePath1 = xmlFilePath; + xmlFilePath = Path.Combine(AppContext.BaseDirectory, "WebApi8-Swashbuckle.xml"); + options.IncludeXmlCommentsWithRemarks(filePath: xmlFilePath, includeControllerXmlComments: false, + typeof(ComplicatedClass), + typeof(InnerEnum)); + + // if you want to add xml comments from inheritdocs (from summary and remarks) into the swagger documentation, add: + // you can exclude remarks for concrete types + options.IncludeXmlCommentsFromInheritDocs(includeRemarks: true); + + // options.IncludeXmlCommentsWithRemarks(filePath: xmlFilePath, includeControllerXmlComments: false, () => new[] { typeof(InnerEnum) }); + + // or add without remarks + //options.IncludeXmlComments(xmlFilePath); + + #region AddEnumsWithValuesFixFilters + + // Add filters to fix enums + // use by default: + options.AddEnumsWithValuesFixFilters(); + + // or configured: + options.AddEnumsWithValuesFixFilters(o => + { + // add schema filter to fix enums (add 'x-enumNames' for NSwag or its alias from XEnumNamesAlias) in schema + o.ApplySchemaFilter = true; + + // alias for replacing 'x-enumNames' in swagger document + o.XEnumNamesAlias = "x-enum-varnames"; + + // alias for replacing 'x-enumDescriptions' in swagger document + o.XEnumDescriptionsAlias = "x-enum-descriptions"; + + // add parameter filter to fix enums (add 'x-enumNames' for NSwag or its alias from XEnumNamesAlias) in schema parameters + o.ApplyParameterFilter = true; + + // add document filter to fix enums displaying in swagger document + o.ApplyDocumentFilter = true; + + // 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; + + // new line for enum values descriptions + // o.NewLine = Environment.NewLine; + o.NewLine = "\n"; + + // get descriptions from xml-file comments on the specified path + // should use "options.IncludeXmlComments(xmlFilePath);" before + o.IncludeXmlCommentsFrom(xmlFilePath); + o.IncludeXmlCommentsFrom(xmlFilePath1); + // the same for another xml-files... + }); + + #endregion + + #region HidePathsAndDefinitionsByRolesDocumentFilter + + // remove Paths and Components from OpenApi documentation without accepted roles + options.DocumentFilter(new List { "AcceptedRole" }); + + #endregion + + #region AppendActionCountToTagSummaryDocumentFilter + + // enable openApi Annotations + options.EnableAnnotations(); + + // add action count (with message template) into the SwaggerTag's descriptions + // you can use it after "HidePathsAndDefinitionsByRolesDocumentFilter" + options.DocumentFilter("(the count of actions: {0})"); + + #endregion + + #region TagOrderByNameDocumentFilter + + // order tags by name + options.DocumentFilter(); + + #endregion + + #region ChangeAllResponsesByHttpStatusCode + + // change responses for specific HTTP status code ("200") + //options.ChangeAllResponsesByHttpStatusCode( + // httpStatusCode: 200, + // responseDescription: "200 status code description", + // responseExampleOption: ResponseExampleOptions.AddNew, // add new response examples + // responseExample: new TodoItem { Tag = Tag.Workout, Id = 111, IsComplete = false, Name = "test" }); // some class for response examples + + // change responses for specific HTTP status code ("400" (HttpStatusCode.BadRequest)) + options.ChangeAllResponsesByHttpStatusCode(httpStatusCode: HttpStatusCode.BadRequest, + responseDescription: "400 status code description", + responseExampleOption: ResponseExampleOptions.Clear, // clear response examples + responseExample: new ComplicatedClass()); // some class for response examples + + // change responses for specific HTTP status code ("201" (StatusCodes.Status201Created)) + options.ChangeAllResponsesByHttpStatusCode(httpStatusCode: StatusCodes.Status201Created, + responseDescription: "201 status code description", + responseExampleOption: ResponseExampleOptions.None, // do nothing with response examples + responseExample: new ComplicatedClass()); // some class for response examples + + #endregion +}); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (!app.Environment.IsDevelopment()) +{ + app.UseDeveloperExceptionPage(); +} + +// enable middleware to serve generated Swagger as a JSON endpoint. +app.UseSwagger(c => +{ + c.PreSerializeFilters.Add((openApiDoc, _) => + { + // remove Paths and Components from OpenApi documentation for specific controller action without accepted roles + openApiDoc.RemovePathsAndComponentsWithoutAcceptedRolesFor(controller => nameof(controller.HidedAction), new List { "AcceptedRole" }); + + // or + //openApiDoc.RemovePathsAndComponentsWithoutAcceptedRolesFor(nameof(HidedController.HidedAction), new List { "AcceptedRole" }); + + + // remove Paths and Components from OpenApi documentation for all controller actions without accepted roles + openApiDoc.RemovePathsAndComponentsWithoutAcceptedRolesForController(new List { "AcceptedRole" }); + + // or you can get accepted roles by httpRequest like this: + //openApiDoc.RemovePathsAndComponentsWithoutAcceptedRolesForController(GetAcceptedRolesByRemoteIp(httpRequest.HttpContext.Connection.RemoteIpAddress)); + }); +}); + +// Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.), +// specifying the Swagger JSON endpoint. +app.UseSwaggerUI(c => +{ + c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1"); +}); + +app.UseRouting(); + +#pragma warning disable ASP0014 +app.UseEndpoints(endpoints => endpoints.MapControllers()); +#pragma warning restore ASP0014 + +app.Run(); \ No newline at end of file diff --git a/test/WebApi8-Swashbuckle/Properties/launchSettings.json b/test/WebApi8-Swashbuckle/Properties/launchSettings.json new file mode 100644 index 0000000..6b9c4b4 --- /dev/null +++ b/test/WebApi8-Swashbuckle/Properties/launchSettings.json @@ -0,0 +1,30 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:16979", + "sslPort": 44314 + } + }, + "profiles": { + "WebApi8_Swashbuckle": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/test/WebApi8-Swashbuckle/WebApi8-Swashbuckle.csproj b/test/WebApi8-Swashbuckle/WebApi8-Swashbuckle.csproj new file mode 100644 index 0000000..72334ce --- /dev/null +++ b/test/WebApi8-Swashbuckle/WebApi8-Swashbuckle.csproj @@ -0,0 +1,15 @@ + + + + net8.0 + enable + enable + WebApi8_Swashbuckle + True + + + + + + + diff --git a/test/WebApi8-Swashbuckle/appsettings.Development.json b/test/WebApi8-Swashbuckle/appsettings.Development.json new file mode 100644 index 0000000..770d3e9 --- /dev/null +++ b/test/WebApi8-Swashbuckle/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "DetailedErrors": true, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/test/WebApi8-Swashbuckle/appsettings.json b/test/WebApi8-Swashbuckle/appsettings.json new file mode 100644 index 0000000..10f68b8 --- /dev/null +++ b/test/WebApi8-Swashbuckle/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +}