Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

## Breaking Changes

* Since [v3.0.0](https://github.com/unchase/Unchase.Swashbuckle.AspNetCore.Extensions/releases/tag/v3.0.0) supports [Swashbuckle.AspNetCore 10.0.0](https://www.nuget.org/packages/Swashbuckle.AspNetCore/) with **breaking changes**.
*
* Since [v2.0.0](https://github.com/unchase/Unchase.Swashbuckle.AspNetCore.Extensions/releases/tag/v2.0.0) supports [Swashbuckle.AspNetCore 5.0.0](https://www.nuget.org/packages/Swashbuckle.AspNetCore/) with **breaking changes**.

For old versions see [README_OLD.md](README_OLD.md).
Expand All @@ -20,7 +22,8 @@ For old versions see [README_OLD.md](README_OLD.md).

|Swashbuckle Version|ASP.NET Core|Swagger / OpenAPI Spec.|This extension Version|
|:------------------|:-----------|:----------------------|:---------------------|
|[master](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/tree/master/README.md)|>= 2.0.0|2.0, 3.0|[master](https://github.com/unchase/Unchase.Swashbuckle.AspNetCore.Extensions/tree/master/README.md)|
|[master](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/tree/master/README.md)|>= 8.0.0|2.0, 3.0, 3.1|[master](https://github.com/unchase/Unchase.Swashbuckle.AspNetCore.Extensions/tree/master/README.md)|
|[10.0.0](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/tree/10.0.0/README.md)|>= 8.0.0|2.0, 3.0, 3.1|[v3.0.0](https://github.com/unchase/Unchase.Swashbuckle.AspNetCore.Extensions/tree/master/README.md)|
|[6.1.5](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/tree/v6.1.5)|>= 2.0.0|2.0, 3.0|[v2.6.1](https://github.com/unchase/Unchase.Swashbuckle.AspNetCore.Extensions/tree/v2.6.1/README.md)|
|[6.1.0](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/tree/v6.1.0)|>= 2.0.0|2.0, 3.0|[v2.6.0](https://github.com/unchase/Unchase.Swashbuckle.AspNetCore.Extensions/tree/v2.6.0/README.md)|
|[6.0.0](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/tree/v6.0.0)|>= 2.0.0|2.0, 3.0|[v2.5.1](https://github.com/unchase/Unchase.Swashbuckle.AspNetCore.Extensions/tree/v2.5.1/README.md)|
Expand Down
20 changes: 3 additions & 17 deletions Unchase.Swashbuckle.AspNetCore.Extensions.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.4.33122.133
# Visual Studio Version 18
VisualStudioVersion = 18.1.11312.151
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{7C4518F9-6506-49BE-B39D-1BD1BE121E4B}"
EndProject
Expand Down Expand Up @@ -32,11 +32,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Unchase.Swashbuckle.AspNetC
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Unchase.Swashbuckle.AspNetCore.Extensions.Tests", "test\Unchase.Swashbuckle.AspNetCore.Extensions.Tests\Unchase.Swashbuckle.AspNetCore.Extensions.Tests.csproj", "{ADFE3232-3F94-4582-BE4E-CD93603EAE18}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebApi3.1-Swashbuckle", "test\WebApi3.1-Swashbuckle\WebApi3.1-Swashbuckle.csproj", "{1AD5C40F-8AF7-4DAA-9C72-40D1269304C0}"
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("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApi8-Swashbuckle", "test\WebApi8-Swashbuckle\WebApi8-Swashbuckle.csproj", "{4C6ADEFF-E66A-4E7D-B8B7-24FCDB3F6A89}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand All @@ -52,14 +48,6 @@ Global
{ADFE3232-3F94-4582-BE4E-CD93603EAE18}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ADFE3232-3F94-4582-BE4E-CD93603EAE18}.Release|Any CPU.ActiveCfg = Release|Any CPU
{ADFE3232-3F94-4582-BE4E-CD93603EAE18}.Release|Any CPU.Build.0 = Release|Any CPU
{1AD5C40F-8AF7-4DAA-9C72-40D1269304C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1AD5C40F-8AF7-4DAA-9C72-40D1269304C0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1AD5C40F-8AF7-4DAA-9C72-40D1269304C0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1AD5C40F-8AF7-4DAA-9C72-40D1269304C0}.Release|Any CPU.Build.0 = Release|Any CPU
{C9251364-9941-4776-A9A3-841173DB58C4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{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
Expand All @@ -71,8 +59,6 @@ Global
GlobalSection(NestedProjects) = preSolution
{63011319-4764-44DE-B8C2-CDABAD5FD8BE} = {7C4518F9-6506-49BE-B39D-1BD1BE121E4B}
{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}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@
using System.Reflection;
using System.Xml.XPath;

using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi;
using Swashbuckle.AspNetCore.SwaggerGen;
using System.Text.Json.Nodes;
using System.Text.Json;

namespace Unchase.Swashbuckle.AspNetCore.Extensions.Extensions
{
Expand Down Expand Up @@ -55,7 +56,13 @@ private static string GetFieldAttributeDescription(
return string.Empty;
}

var memInfo = enumType.GetMember(enumField.ToString());
var enumFieldString = enumField.ToString();
if (string.IsNullOrWhiteSpace(enumFieldString))
{
return string.Empty;
}

var memInfo = enumType.GetMember(enumFieldString);
var attributes = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), true);
if (attributes.Length > 0)
{
Expand All @@ -65,13 +72,13 @@ private static string GetFieldAttributeDescription(
return string.Empty;
}

internal static List<(object EnumValue, OpenApiString EnumDescription)> GetEnumValuesDescription(
internal static List<(object EnumValue, JsonNode EnumDescription)> GetEnumValuesDescription(
Type enumType,
DescriptionSources descriptionSource,
IEnumerable<XPathNavigator> xmlNavigators,
bool includeRemarks = false)
{
var enumsDescriptions = new List<(object, OpenApiString)>();
var enumsDescriptions = new List<(object, JsonNode)>();
foreach (var enumValue in Enum.GetValues(enumType))
{
var enumDescription = string.Empty;
Expand Down Expand Up @@ -106,11 +113,11 @@ private static string GetFieldAttributeDescription(
{
if (!string.IsNullOrWhiteSpace(enumDescription))
{
enumsDescriptions.Add((enumValue, new OpenApiString(enumDescription)));
enumsDescriptions.Add((enumValue, enumDescription));
}
else
{
enumsDescriptions.Add((enumValue, new OpenApiString(string.Empty)));
enumsDescriptions.Add((enumValue, string.Empty));
}
}
}
Expand All @@ -119,16 +126,17 @@ private static string GetFieldAttributeDescription(
}

private static string TryGetMemberComments(
MemberInfo memberInfo,
MemberInfo? memberInfo,
IEnumerable<XPathNavigator> xmlNavigators,
bool includeRemarks = false)
{
var commentsBuilder = new StringBuilder();
if (xmlNavigators == null)
if (memberInfo == null || xmlNavigators == null)
{
return string.Empty;
}

var commentsBuilder = new StringBuilder();

foreach (var xmlNavigator in xmlNavigators)
{
var nodeNameForMember = GetNodeNameForMember(memberInfo);
Expand Down Expand Up @@ -158,7 +166,7 @@ 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(QualifiedNameFor(memberInfo.DeclaringType!, false));
stringBuilder.Append("." + memberInfo.Name);
return stringBuilder.ToString();
}
Expand All @@ -169,7 +177,7 @@ private static string QualifiedNameFor(
{
if (type.IsArray)
{
return QualifiedNameFor(type.GetElementType(), expandGenericArgs) + "[]";
return QualifiedNameFor(type.GetElementType()!, expandGenericArgs) + "[]";
}

var stringBuilder = new StringBuilder();
Expand Down Expand Up @@ -199,36 +207,36 @@ private static string QualifiedNameFor(
return stringBuilder.ToString();
}

internal static string AddEnumValuesDescription(
this OpenApiSchema schema,
internal static string? AddEnumValuesDescription(
this IOpenApiSchema schema,
string xEnumNamesAlias,
string xEnumDescriptionsAlias,
bool includeDescriptionFromAttribute = false,
string newLine = "\n")
{
if (schema.Enum == null || schema.Enum.Count == 0)
if (schema.Extensions == null || schema.Enum == null || schema.Enum.Count == 0)
{
return null;
}

if (!schema.Extensions.ContainsKey(xEnumNamesAlias) ||
((OpenApiArray)schema.Extensions[xEnumNamesAlias]).Count != schema.Enum.Count)
((JsonArray)((JsonNodeExtension)schema.Extensions[xEnumNamesAlias]).Node).Count != schema.Enum.Count)
{
return null;
}

var sb = new StringBuilder();
for (var i = 0; i < schema.Enum.Count; i++)
{
if (schema.Enum[i] is OpenApiInteger schemaEnumInt)
if (schema.Enum[i] is JsonValue schemaEnumInt && schemaEnumInt.GetValueKind() is JsonValueKind.Number)
{
var value = schemaEnumInt.Value;
var name = ((OpenApiString)((OpenApiArray)schema.Extensions[xEnumNamesAlias])[i]).Value;
var value = schemaEnumInt.GetValue<long>();
var name = ((JsonArray)((JsonNodeExtension)schema.Extensions[xEnumNamesAlias]).Node)[i]!.GetValue<string>();
sb.Append($"{newLine}{newLine}{value} = {name}");
}
else if (schema.Enum[i] is OpenApiString schemaEnumString)
else if (schema.Enum[i] is JsonValue schemaEnumString && schemaEnumString.GetValueKind() is JsonValueKind.String)
{
var value = schemaEnumString.Value;
var value = schemaEnumString.GetValue<string>();
sb.Append($"{newLine}{newLine}{value}");
}

Expand All @@ -240,10 +248,10 @@ internal static string AddEnumValuesDescription(
continue;
}

var xEnumDescriptions = (OpenApiArray)schema.Extensions[xEnumDescriptionsAlias];
var xEnumDescriptions = (JsonArray)((JsonNodeExtension)schema.Extensions[xEnumDescriptionsAlias]).Node;
if (xEnumDescriptions?.Count == schema.Enum.Count)
{
var description = ((OpenApiString)((OpenApiArray)schema.Extensions[xEnumDescriptionsAlias])[i]).Value;
var description = xEnumDescriptions[i]!.GetValue<string>();
if (!string.IsNullOrWhiteSpace(description))
{
sb.Append($" ({description})");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
using System.Reflection;

using Microsoft.AspNetCore.Mvc;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi;
using Unchase.Swashbuckle.AspNetCore.Extensions.Factories;
using Unchase.Swashbuckle.AspNetCore.Extensions.Filters;

Expand Down Expand Up @@ -32,8 +32,8 @@ public static OpenApiDocument RemovePathsAndComponentsWithoutAcceptedRolesFor<TC
IReadOnlyList<string> acceptedRoles)
where TController : class, new()
{
var actionDescriptor = ApiDescriptionFactory.Create(actionNameSelector, typeof(TController).GetCustomAttribute<RouteAttribute>().Template)?.ActionDescriptor;
if (actionDescriptor != null)
var actionDescriptor = ApiDescriptionFactory.Create(actionNameSelector, typeof(TController).GetCustomAttribute<RouteAttribute>()?.Template)?.ActionDescriptor;
if (actionDescriptor?.AttributeRouteInfo?.Template != null && openApiDoc.Components?.Schemas != null)
{
var paths = new Dictionary<(MethodInfo, Type), string>
{
Expand All @@ -60,8 +60,8 @@ public static OpenApiDocument RemovePathsAndComponentsWithoutAcceptedRolesFor<TC
string actionName,
IReadOnlyList<string> acceptedRoles) where TController : class
{
var actionDescriptor = ApiDescriptionFactory.Create(typeof(TController), actionName, typeof(TController).GetCustomAttribute<RouteAttribute>().Template)?.ActionDescriptor;
if (actionDescriptor != null)
var actionDescriptor = ApiDescriptionFactory.Create(typeof(TController), actionName, typeof(TController).GetCustomAttribute<RouteAttribute>()?.Template)?.ActionDescriptor;
if (actionDescriptor?.AttributeRouteInfo?.Template != null && openApiDoc.Components?.Schemas != null)
{
var paths = new Dictionary<(MethodInfo, Type), string>
{
Expand All @@ -86,11 +86,13 @@ public static OpenApiDocument RemovePathsAndComponentsWithoutAcceptedRolesForCon
this OpenApiDocument openApiDoc,
IReadOnlyList<string> acceptedRoles) where TController : class
{
if (openApiDoc.Components?.Schemas == null) return openApiDoc;

var paths = new Dictionary<(MethodInfo, Type), string>();
foreach (var methodInfo in typeof(TController).GetMethods().Where(m => !m.IsSpecialName))
{
var actionDescriptor = ApiDescriptionFactory.Create<TController>(methodInfo, typeof(TController).GetCustomAttribute<RouteAttribute>().Template)?.ActionDescriptor;
if (actionDescriptor != null)
var actionDescriptor = ApiDescriptionFactory.Create<TController>(methodInfo, typeof(TController).GetCustomAttribute<RouteAttribute>()?.Template)?.ActionDescriptor;
if (actionDescriptor?.AttributeRouteInfo?.Template != null)
{
paths.Add((((Microsoft.AspNetCore.Mvc.Controllers.ControllerActionDescriptor)actionDescriptor).MethodInfo, typeof(TController)), actionDescriptor.AttributeRouteInfo.Template);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ public static class SwaggerGenOptionsExtensions
#region Fields

private static readonly FieldInfo _xmlDocMembers =
typeof(XmlCommentsSchemaFilter).GetField("_xmlDocMembers", BindingFlags.Instance | BindingFlags.NonPublic);
typeof(XmlCommentsSchemaFilter).GetField("_xmlDocMembers", BindingFlags.Instance | BindingFlags.NonPublic)
?? throw new InvalidOperationException($"Get field info \"_xmlDocMembers\" of type {typeof(XmlCommentsSchemaFilter).FullName} failed.");

#endregion

Expand All @@ -41,9 +42,9 @@ public static class SwaggerGenOptionsExtensions
public static SwaggerGenOptions ChangeAllResponsesByHttpStatusCode<T>(
this SwaggerGenOptions swaggerGenOptions,
int httpStatusCode,
string responseDescription = null,
string? responseDescription = null,
ResponseExampleOptions responseExampleOption = ResponseExampleOptions.None,
T responseExample = default) where T : class
T? responseExample = default) where T : class
{
swaggerGenOptions.DocumentFilter<ChangeResponseByHttpStatusCodeDocumentFilter<T>>(httpStatusCode, responseDescription, responseExampleOption, responseExample);
return swaggerGenOptions;
Expand All @@ -64,9 +65,9 @@ public static SwaggerGenOptions ChangeAllResponsesByHttpStatusCode<T>(
public static SwaggerGenOptions ChangeAllResponsesByHttpStatusCode<T>(
this SwaggerGenOptions swaggerGenOptions,
HttpStatusCode httpStatusCode,
string responseDescription = null,
string? responseDescription = null,
ResponseExampleOptions responseExampleOption = ResponseExampleOptions.None,
T responseExample = default) where T : class
T? responseExample = default) where T : class
{
return swaggerGenOptions.ChangeAllResponsesByHttpStatusCode((int)httpStatusCode, responseDescription, responseExampleOption, responseExample);
}
Expand All @@ -78,7 +79,7 @@ public static SwaggerGenOptions ChangeAllResponsesByHttpStatusCode<T>(
/// <param name="configureOptions">An <see cref="Action{FixEnumsOptions}"/> to configure options for filters.</param>
public static SwaggerGenOptions AddEnumsWithValuesFixFilters(
this SwaggerGenOptions swaggerGenOptions,
Action<FixEnumsOptions> configureOptions = null)
Action<FixEnumsOptions>? configureOptions = null)
{
// local function
void EmptyAction(FixEnumsOptions x) { }
Expand Down Expand Up @@ -155,9 +156,9 @@ public static SwaggerGenOptions IncludeXmlCommentsWithRemarks(
this SwaggerGenOptions swaggerGenOptions,
Func<XPathDocument> xmlDocFactory,
bool includeControllerXmlComments = false,
Func<Type[]> excludedTypesFunc = default)
Func<Type[]>? excludedTypesFunc = default)
{
return swaggerGenOptions.IncludeXmlCommentsWithRemarks(xmlDocFactory, includeControllerXmlComments, excludedTypesFunc?.Invoke());
return swaggerGenOptions.IncludeXmlCommentsWithRemarks(xmlDocFactory, includeControllerXmlComments, excludedTypesFunc?.Invoke() ?? []);
}

/// <summary>
Expand All @@ -174,9 +175,9 @@ public static SwaggerGenOptions IncludeXmlCommentsWithRemarks(
this SwaggerGenOptions swaggerGenOptions,
string filePath,
bool includeControllerXmlComments = false,
Func<Type[]> excludedTypesFunc = default)
Func<Type[]>? excludedTypesFunc = default)
{
return swaggerGenOptions.IncludeXmlCommentsWithRemarks(() => new XPathDocument(filePath), includeControllerXmlComments, excludedTypesFunc?.Invoke());
return swaggerGenOptions.IncludeXmlCommentsWithRemarks(() => new XPathDocument(filePath), includeControllerXmlComments, excludedTypesFunc?.Invoke() ?? []);
}

/// <summary>
Expand Down
Loading