Skip to content

Commit

Permalink
fix: fix schema id conflict
Browse files Browse the repository at this point in the history
  • Loading branch information
jxnkwlp committed Sep 14, 2023
1 parent 3af0fb6 commit 93f1f57
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 68 deletions.
59 changes: 23 additions & 36 deletions src/SwaggerExtensions/SwaggerExtensions/Extensions.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Collections.Concurrent;
using System.Text;

namespace Swashbuckle.AspNetCore.SwaggerGen
{
internal static class Extensions
{
private static readonly ConcurrentDictionary<Type, string> TypeSimpleNameCache = new ConcurrentDictionary<Type, string>();

public static string RemovePostFix(this string value, string postfix)
{
if (string.IsNullOrWhiteSpace(value))
Expand All @@ -28,55 +30,40 @@ public static string RemovePreFix(this string value, string prefix)
return value;
}

public static string ToSimpleTypeString(this Type type, bool trimArgCount = true)
public static string ToSimpleTypeName(this Type type)
{
if (type.IsGenericType)
{
var genericArgs = type.GetGenericArguments().ToList();

return HandleTypeArguments(type, trimArgCount, genericArgs);
}

return type.Name;
return TypeSimpleNameCache.GetOrAdd(type, (t) => BuildSimpleTypeName(t));
}

private static string HandleTypeArguments(Type t, bool trimArgCount, List<Type> availableArguments)
private static string BuildSimpleTypeName(Type type)
{
if (t.IsGenericType)
if (type.IsGenericType)
{
string value = t.Name;
if (trimArgCount && value.IndexOf("`") > -1)
{
value = value.Substring(0, value.IndexOf("`"));
}
StringBuilder sb = new StringBuilder();

if (t.DeclaringType != null)
string name = type.Name;
if (name.IndexOf("`") > -1)
{
// This is a nested type, build the nesting type first
value = HandleTypeArguments(t.DeclaringType, trimArgCount, availableArguments) + "+" + value;
name = name.Substring(0, name.IndexOf("`"));
}

// Build the type arguments (if any)
string argString = "";
var thisTypeArgs = t.GetGenericArguments();
for (int i = 0; i < thisTypeArgs.Length && availableArguments.Count > 0; i++)
{
if (i != 0) argString += ", ";
sb.Append(name);

argString += ToSimpleTypeString(availableArguments[0], trimArgCount);
availableArguments.RemoveAt(0);
}

// If there are type arguments, add them with < >
if (argString.Length > 0)
var argumenTypes = type.GetGenericArguments();
if (argumenTypes.Length > 0)
{
value += "<" + argString + ">";
sb.Append("<");
foreach (var item in argumenTypes)
{
sb.Append(BuildSimpleTypeName(item));
}
sb.Append(">");
}

return value;
return sb.ToString();
}

return t.Name;
return type.Name;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public void Apply(OpenApiSchema schema, SchemaFilterContext context)
}
);

schema.Description = string.Join("<br/> ", names.Select(x => $"{Convert.ToInt32(Enum.Parse(type, x))}:{x}"));
schema.Description += string.Join("<br/> ", names.Select(x => $"{Convert.ToInt32(Enum.Parse(type, x))}:{x}"));
}
}
}
Expand Down
11 changes: 11 additions & 0 deletions src/SwaggerExtensions/SwaggerExtensions/SwaggerExtensionOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System;

namespace Swashbuckle.AspNetCore.SwaggerGen
{
public class SwaggerExtensionOptions
{
public bool RemoveDtoFix { get; set; } = true;
public bool AllowDuplicateOperationId { get; set; }
public Func<Type, string, string> ConflictingSchemaIdResolver { get; set; }
}
}
90 changes: 61 additions & 29 deletions src/SwaggerExtensions/SwaggerExtensions/SwaggerExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,66 +1,98 @@
using System.Collections.Generic;
using System;
using System.Collections.Concurrent;
using System.Linq;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.Extensions.DependencyInjection;

namespace Swashbuckle.AspNetCore.SwaggerGen
{
public static class SwaggerGenOptionsExtensions
{
private static readonly Dictionary<string, int> OperationIds = new Dictionary<string, int>();
private static readonly Dictionary<ApiDescription, string> OperationIdCache = new Dictionary<ApiDescription, string>();
private static readonly ConcurrentDictionary<string, int> OperationIdConflicts = new ConcurrentDictionary<string, int>();
private static readonly ConcurrentDictionary<ApiDescription, string> OperationIdCache = new ConcurrentDictionary<ApiDescription, string>();

private static readonly ConcurrentDictionary<string, int> SchemaIdConflicts = new ConcurrentDictionary<string, int>();
private static readonly ConcurrentDictionary<Type, string> SchemaIdCache = new ConcurrentDictionary<Type, string>();

[Obsolete("Use 'ApplyExtensions' ", error: true)]
public static void GenerateSchemaIdAndOperationId(this SwaggerGenOptions options, bool removeDtoFix = true, bool allowDuplicateOperationId = false)
{
//
options.SchemaFilter<SwaggerEnumDescriptions>();
throw new NotImplementedException();
}

public static void ApplyExtensions(this SwaggerGenOptions swaggerGenOptions, SwaggerExtensionOptions options = null)
{
options = options ?? new SwaggerExtensionOptions();
//
swaggerGenOptions.SchemaFilter<SwaggerEnumDescriptions>();
//
options.CustomSchemaIds(type =>
swaggerGenOptions.CustomSchemaIds(type => GenerateSchemaId(type, options));
//
swaggerGenOptions.CustomOperationIds(e => GenerateOperationIds(e, options));
}

private static string GenerateSchemaId(Type type, SwaggerExtensionOptions options)
{
return SchemaIdCache.GetOrAdd(type, (typeItem) =>
{
var typeString = type.ToSimpleTypeString(true);
var typeString = typeItem.ToSimpleTypeName();

if (typeString.Contains("<"))
{
var wrapType = typeString.Substring(0, typeString.IndexOf("<"));
var argType = typeString.Substring(typeString.IndexOf("<") + 1, typeString.Length - wrapType.Length - 2);
var typeNames = typeString.Split('<', '>').ToList();
typeNames.Reverse();

typeString = argType.Replace(", ", null) + wrapType;
typeString = string.Concat(typeNames);
}

if (removeDtoFix)
return typeString.Replace("Dto", null);
if (options.RemoveDtoFix)
typeString = typeString.Replace("Dto", null);

if (options.ConflictingSchemaIdResolver == null)
{
options.ConflictingSchemaIdResolver = (_, s) => HandleConflictingTypeSchemaId(s);
}

return typeString;
return options.ConflictingSchemaIdResolver(typeItem, typeString);
});
//
options.CustomOperationIds(e =>
}

private static string HandleConflictingTypeSchemaId(string schemaId)
{
if (SchemaIdConflicts.ContainsKey(schemaId))
{
string action = e.ActionDescriptor.RouteValues["action"];
string controller = e.ActionDescriptor.RouteValues["controller"];
string method = e.HttpMethod;
SchemaIdConflicts[schemaId]++;
return schemaId + SchemaIdConflicts[schemaId];
}

if (OperationIdCache.ContainsKey(e))
{
return OperationIdCache[e];
}
SchemaIdConflicts[schemaId] = 0;
return schemaId;
}

private static string GenerateOperationIds(ApiDescription apiDescription, SwaggerExtensionOptions options)
{
return OperationIdCache.GetOrAdd(apiDescription, (apiItem) =>
{
string action = apiItem.ActionDescriptor.RouteValues["action"];
string controller = apiItem.ActionDescriptor.RouteValues["controller"];
string method = apiItem.HttpMethod;

var operationId = GenerateApiOperationId(method, controller, action);

if (!allowDuplicateOperationId)
if (!options.AllowDuplicateOperationId)
{
if (OperationIds.ContainsKey(operationId))
if (OperationIdConflicts.ContainsKey(operationId))
{
OperationIds[operationId]++;
OperationIdConflicts[operationId]++;

operationId = operationId + "_" + OperationIds[operationId];
operationId += OperationIdConflicts[operationId];
}
else
{
OperationIds[operationId] = 0;
OperationIdConflicts[operationId] = 0;
}
}

OperationIdCache[e] = operationId;

return operationId;
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@
<Authors>Passingwind</Authors>
<PackageProjectUrl>https://github.com/jxnkwlp/Passingwind.CommonLibs</PackageProjectUrl>
<RepositoryUrl>https://github.com/jxnkwlp/Passingwind.CommonLibs</RepositoryUrl>
<RepositoryType>github</RepositoryType>
<PackageVersion>0.3.0</PackageVersion>
<RepositoryType>git</RepositoryType>
<GenerateDocumentationFile>False</GenerateDocumentationFile>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<IncludeSymbols>True</IncludeSymbols>
<PackageVersion>0.4.0</PackageVersion>
</PropertyGroup>

<ItemGroup>
Expand Down

0 comments on commit 93f1f57

Please sign in to comment.