Fix up packge for OpenAPI XML support (#611)
This commit is contained in:
Родитель
ab68617aa6
Коммит
776c606e78
|
@ -9,8 +9,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample", "sample\Sample.csp
|
|||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests", "test\Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests.csproj", "{1541DE82-68F3-440A-9ABD-429EC986CB84}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DocFx.XmlComments", "src\DocFx.XmlComments.csproj", "{28570C7C-A9B1-4A6A-95B8-EA2153FCA4A3}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -29,10 +27,6 @@ Global
|
|||
{1541DE82-68F3-440A-9ABD-429EC986CB84}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1541DE82-68F3-440A-9ABD-429EC986CB84}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1541DE82-68F3-440A-9ABD-429EC986CB84}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{28570C7C-A9B1-4A6A-95B8-EA2153FCA4A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{28570C7C-A9B1-4A6A-95B8-EA2153FCA4A3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{28570C7C-A9B1-4A6A-95B8-EA2153FCA4A3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{28570C7C-A9B1-4A6A-95B8-EA2153FCA4A3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace DocFx.XmlComments;
|
|
@ -1,3 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
|
@ -16,7 +16,7 @@ internal static class XmlCommentTransformer
|
|||
private static XslCompiledTransform InitializeTransform()
|
||||
{
|
||||
var assembly = typeof(XmlCommentTransformer).Assembly;
|
||||
var xsltFilePath = $"{assembly.GetName().Name}.Resources.XmlCommentTransform.xsl";
|
||||
var xsltFilePath = $"{assembly.GetName().Name}.DocFx.XmlComments.Resources.XmlCommentTransform.xsl";
|
||||
using var stream = assembly.GetManifestResourceStream(xsltFilePath);
|
||||
using var reader = XmlReader.Create(stream);
|
||||
var xsltSettings = new XsltSettings(true, true);
|
|
@ -1,3 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
using System.Xml.XPath;
|
|
@ -1,3 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
using System.Xml.XPath;
|
|
@ -15,19 +15,24 @@
|
|||
<NoWarn>RSEXPERIMENTAL002</NoWarn>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
<PackageTags>source generator, openapi, xml</PackageTags>
|
||||
<Description>Source generator to provide XML doc support for Microsoft.AspNetCore.OpenApi</Description>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" PrivateAssets="All" IsImplicitlyDefined="true" Version="4.11.0-2.final" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.Common" PrivateAssets="All" IsImplicitlyDefined="true" Version="4.11.0-2.final" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\src\DocFx.XmlComments.csproj" />
|
||||
<PackageReference Include="System.Text.Json" Version="8.0.4" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include=".\README.md" Pack="true" PackagePath="\"/>
|
||||
<None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
|
||||
<None Include=".\build\Microsoft.AspNetCore.OpenApi.SourceGenerators.targets" Pack="true" PackagePath="build" Visible="false" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="DocFx.XmlComments\Resources\**" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -182,7 +182,7 @@ namespace Microsoft.AspNetCore.OpenApi.Generated
|
|||
{
|
||||
public static IOpenApiAny ToOpenApiAny(string? example, Type type)
|
||||
{
|
||||
if (example is null || type is null)
|
||||
if (string.IsNullOrEmpty(example) || type is null)
|
||||
{
|
||||
return new OpenApiNull();
|
||||
}
|
||||
|
@ -230,6 +230,17 @@ namespace Microsoft.AspNetCore.OpenApi.Generated
|
|||
});
|
||||
}
|
||||
""",
|
||||
AddOpenApiOverloadVariant.AddOpenApiConfigureOptions => """
|
||||
public static IServiceCollection AddOpenApi(this IServiceCollection services, Action<OpenApiOptions> configureOptions)
|
||||
{
|
||||
return services.AddOpenApi("v1", options =>
|
||||
{
|
||||
configureOptions(options);
|
||||
options.AddSchemaTransformer(new XmlCommentSchemaTransformer());
|
||||
options.AddOperationTransformer(new XmlCommentOperationTransformer());
|
||||
});
|
||||
}
|
||||
""",
|
||||
AddOpenApiOverloadVariant.AddOpenApiDocumentNameConfigureOptions => """
|
||||
public static IServiceCollection AddOpenApi(this IServiceCollection services, string documentName, Action<OpenApiOptions> configureOptions)
|
||||
{
|
||||
|
|
|
@ -87,12 +87,28 @@ public sealed partial class XmlCommentGenerator
|
|||
{
|
||||
var invocationExpression = (InvocationExpressionSyntax)context.Node;
|
||||
var interceptableLocation = context.SemanticModel.GetInterceptableLocation(invocationExpression, cancellationToken);
|
||||
return new(invocationExpression.ArgumentList.Arguments.Count switch
|
||||
var argumentsCount = invocationExpression.ArgumentList.Arguments.Count;
|
||||
if (argumentsCount == 0)
|
||||
{
|
||||
0 => AddOpenApiOverloadVariant.AddOpenApi,
|
||||
1 => AddOpenApiOverloadVariant.AddOpenApiDocumentName,
|
||||
2 => AddOpenApiOverloadVariant.AddOpenApiDocumentNameConfigureOptions,
|
||||
_ => throw new InvalidOperationException("Invalid number of arguments for supported `AddOpenApi` overload."),
|
||||
}, invocationExpression, interceptableLocation);
|
||||
return new(AddOpenApiOverloadVariant.AddOpenApi, invocationExpression, interceptableLocation);
|
||||
}
|
||||
else if (argumentsCount == 2)
|
||||
{
|
||||
return new(AddOpenApiOverloadVariant.AddOpenApiDocumentNameConfigureOptions, invocationExpression, interceptableLocation);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We need to disambiguate between the two overloads that take a string and a delegate
|
||||
// AddOpenApi("v1") vs. AddOpenApi(options => { })
|
||||
var argument = invocationExpression.ArgumentList.Arguments[0];
|
||||
if (argument.Expression is LiteralExpressionSyntax)
|
||||
{
|
||||
return new(AddOpenApiOverloadVariant.AddOpenApiDocumentName, invocationExpression, interceptableLocation);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new(AddOpenApiOverloadVariant.AddOpenApiConfigureOptions, invocationExpression, interceptableLocation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<InterceptorsNamespaces>$(InterceptorsNamespaces);Microsoft.AspNetCore.OpenApi.Generated</InterceptorsNamespaces>
|
||||
<InterceptorsPreviewNamespaces>
|
||||
$(InterceptorsPreviewNamespaces);Microsoft.AspNetCore.OpenApi.Generated</InterceptorsPreviewNamespaces>
|
||||
</PropertyGroup>
|
||||
</Project>
|
|
@ -167,7 +167,7 @@ namespace Microsoft.AspNetCore.OpenApi.Generated
|
|||
{
|
||||
public static IOpenApiAny ToOpenApiAny(string? example, Type type)
|
||||
{
|
||||
if (example is null || type is null)
|
||||
if (string.IsNullOrEmpty(example) || type is null)
|
||||
{
|
||||
return new OpenApiNull();
|
||||
}
|
||||
|
|
|
@ -14,12 +14,11 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.0-preview.rc.1.*" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.0-preview.rc.2.*" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../gen/Microsoft.AspNetCore.OpenApi.SourceGenerators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
|
||||
<ProjectReference Include="../src/DocFx.XmlComments.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="true" />
|
||||
<ProjectReference Include="../gen/Microsoft.AspNetCore.OpenApi.SourceGenerators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="true" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>preview</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="../gen/Helpers/Polyfills.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" PrivateAssets="All" IsImplicitlyDefined="true" Version="4.11.0-2.final" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.Common" PrivateAssets="All" IsImplicitlyDefined="true" Version="4.11.0-2.final" />
|
||||
<PackageReference Include="System.Text.Json" Version="8.0.4" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Resources\**" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,105 @@
|
|||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.OpenApi.Models;
|
||||
|
||||
namespace Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests;
|
||||
|
||||
public partial class AddOpenApiTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task CanInterceptAddOpenApiWithNoParameters()
|
||||
{
|
||||
var source = """
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
var builder = WebApplication.CreateBuilder();
|
||||
|
||||
builder.Services.AddOpenApi();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
app.MapPost("", () => "Hello world!");
|
||||
|
||||
app.Run();
|
||||
""";
|
||||
|
||||
var generator = new XmlCommentGenerator();
|
||||
await SnapshotTestHelper.Verify(source, generator, out var compilation);
|
||||
Assert.Empty(compilation.GetDiagnostics().Where(d => d.Severity > DiagnosticSeverity.Warning));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CanInterceptAddOpenApiWithNameParameter()
|
||||
{
|
||||
var source = """
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
var builder = WebApplication.CreateBuilder();
|
||||
|
||||
builder.Services.AddOpenApi("v2");
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
app.MapPost("", () => "Hello world!");
|
||||
|
||||
app.Run();
|
||||
""";
|
||||
|
||||
var generator = new XmlCommentGenerator();
|
||||
await SnapshotTestHelper.Verify(source, generator, out var compilation);
|
||||
Assert.Empty(compilation.GetDiagnostics().Where(d => d.Severity > DiagnosticSeverity.Warning));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CanInterceptAddOpenApiWithConfigureOptionsParameter()
|
||||
{
|
||||
var source = """
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
var builder = WebApplication.CreateBuilder();
|
||||
|
||||
builder.Services.AddOpenApi(options =>
|
||||
{
|
||||
options.OpenApiVersion = Microsoft.OpenApi.OpenApiSpecVersion.OpenApi2_0;
|
||||
});
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
app.MapPost("", () => "Hello world!");
|
||||
|
||||
app.Run();
|
||||
""";
|
||||
|
||||
var generator = new XmlCommentGenerator();
|
||||
await SnapshotTestHelper.Verify(source, generator, out var compilation);
|
||||
Assert.Empty(compilation.GetDiagnostics().Where(d => d.Severity > DiagnosticSeverity.Warning));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CanInterceptAddOpenApiWithNameAndConfigureOptionsParameter()
|
||||
{
|
||||
var source = """
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
var builder = WebApplication.CreateBuilder();
|
||||
|
||||
builder.Services.AddOpenApi("v2", options =>
|
||||
{
|
||||
options.OpenApiVersion = Microsoft.OpenApi.OpenApiSpecVersion.OpenApi2_0;
|
||||
});
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
app.MapPost("", () => "Hello world!");
|
||||
|
||||
app.Run();
|
||||
""";
|
||||
|
||||
var generator = new XmlCommentGenerator();
|
||||
await SnapshotTestHelper.Verify(source, generator, out var compilation);
|
||||
Assert.Empty(compilation.GetDiagnostics().Where(d => d.Severity > DiagnosticSeverity.Warning));
|
||||
}
|
||||
}
|
|
@ -28,7 +28,6 @@
|
|||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\gen\Microsoft.AspNetCore.OpenApi.SourceGenerators.csproj" />
|
||||
<ProjectReference Include="..\src\DocFx.XmlComments.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -0,0 +1,199 @@
|
|||
//HintName: OpenApiXmlCommentSupport.generated.cs
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
#nullable enable
|
||||
|
||||
namespace System.Runtime.CompilerServices
|
||||
{
|
||||
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.OpenApi.SourceGenerators, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")]
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
|
||||
file sealed class InterceptsLocationAttribute : System.Attribute
|
||||
{
|
||||
public InterceptsLocationAttribute(int version, string data)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace Microsoft.AspNetCore.OpenApi.Generated
|
||||
{
|
||||
using DocFx.XmlComments;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.OpenApi;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Microsoft.OpenApi.Any;
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.OpenApi.SourceGenerators, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")]
|
||||
file static class XmlCommentCache
|
||||
{
|
||||
private static Dictionary<(Type?, string?), string>? _cache;
|
||||
public static Dictionary<(Type?, string?), string> Cache
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_cache is null)
|
||||
{
|
||||
_cache = GenerateCacheEntries();
|
||||
}
|
||||
return _cache;
|
||||
}
|
||||
}
|
||||
|
||||
private static Dictionary<(Type?, string?), string> GenerateCacheEntries()
|
||||
{
|
||||
var _cache = new Dictionary<(Type?, string?), string>();
|
||||
return _cache;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.OpenApi.SourceGenerators, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")]
|
||||
file class XmlCommentOperationTransformer : IOpenApiOperationTransformer
|
||||
{
|
||||
public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransformerContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
var methodInfo = context.Description.ActionDescriptor is ControllerActionDescriptor controllerActionDescriptor
|
||||
? controllerActionDescriptor.MethodInfo
|
||||
: context.Description.ActionDescriptor.EndpointMetadata.OfType<MethodInfo>().SingleOrDefault();
|
||||
|
||||
if (methodInfo is null)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
if (XmlCommentCache.Cache.TryGetValue((methodInfo.DeclaringType, methodInfo.Name), out var methodCommentString))
|
||||
{
|
||||
System.Diagnostics.Debugger.Break();
|
||||
var methodComment = JsonSerializer.Deserialize<XmlComment>(methodCommentString);
|
||||
if (methodComment is null)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
operation.Summary = methodComment.Summary;
|
||||
operation.Description = methodComment.Description;
|
||||
foreach (var parameterComment in methodComment.Parameters)
|
||||
{
|
||||
var parameterInfo = methodInfo.GetParameters().SingleOrDefault(info => info.Name == parameterComment.Name);
|
||||
var operationParameter = operation.Parameters?.SingleOrDefault(parameter => parameter.Name == parameterComment.Name);
|
||||
if (operationParameter is not null)
|
||||
{
|
||||
operationParameter.Description = parameterComment.Description;
|
||||
if (parameterInfo is not null)
|
||||
{
|
||||
operationParameter.Example = OpenApiExamplesHelper.ToOpenApiAny(parameterComment.Example, parameterInfo.ParameterType);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var requestBody = operation.RequestBody;
|
||||
if (requestBody is not null)
|
||||
{
|
||||
requestBody.Description = parameterComment.Description;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (methodComment.Responses is { Count: > 0} && operation.Responses is { Count: > 0 })
|
||||
{
|
||||
foreach (var response in operation.Responses)
|
||||
{
|
||||
var responseComment = methodComment.Responses.SingleOrDefault(xmlResponse => xmlResponse.Code == response.Key);
|
||||
if (responseComment is not null)
|
||||
{
|
||||
response.Value.Description = responseComment.Description;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.OpenApi.SourceGenerators, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")]
|
||||
file class XmlCommentSchemaTransformer : IOpenApiSchemaTransformer
|
||||
{
|
||||
public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
if (context.JsonPropertyInfo is { AttributeProvider: PropertyInfo propertyInfo })
|
||||
{
|
||||
if (XmlCommentCache.Cache.TryGetValue((propertyInfo.DeclaringType, propertyInfo.Name), out var propertyCommentString))
|
||||
{
|
||||
var propertyComment = JsonSerializer.Deserialize<XmlComment>(propertyCommentString);
|
||||
if (propertyComment is not null)
|
||||
{
|
||||
schema.Description = propertyComment.Returns ?? propertyComment.Summary;
|
||||
if (propertyComment.Examples is { Count: > 0 })
|
||||
{
|
||||
schema.Example = OpenApiExamplesHelper.ToOpenApiAny(propertyComment.Examples.FirstOrDefault(), propertyInfo.PropertyType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (XmlCommentCache.Cache.TryGetValue((context.JsonTypeInfo.Type, null), out var typeCommentString))
|
||||
{
|
||||
var typeComment = JsonSerializer.Deserialize<XmlComment>(typeCommentString);
|
||||
if (typeComment is not null)
|
||||
{
|
||||
schema.Description = typeComment.Summary;
|
||||
if (typeComment.Examples is { Count: > 0 })
|
||||
{
|
||||
schema.Example = OpenApiExamplesHelper.ToOpenApiAny(typeComment.Examples.FirstOrDefault(), context.JsonTypeInfo.Type);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.OpenApi.SourceGenerators, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")]
|
||||
file static class OpenApiExamplesHelper
|
||||
{
|
||||
public static IOpenApiAny ToOpenApiAny(string? example, Type type)
|
||||
{
|
||||
if (example is null || type is null)
|
||||
{
|
||||
return new OpenApiNull();
|
||||
}
|
||||
return Type.GetTypeCode(type) switch
|
||||
{
|
||||
TypeCode.String => new OpenApiString(example),
|
||||
TypeCode.Boolean => new OpenApiBoolean(bool.Parse(example)),
|
||||
TypeCode.Int32 => new OpenApiInteger(int.Parse(example)),
|
||||
TypeCode.Int64 => new OpenApiLong(long.Parse(example)),
|
||||
TypeCode.Double => new OpenApiDouble(double.Parse(example)),
|
||||
TypeCode.Single => new OpenApiFloat(float.Parse(example)),
|
||||
TypeCode.DateTime => new OpenApiDateTime(DateTime.Parse(example)),
|
||||
_ => new OpenApiNull()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.OpenApi.SourceGenerators, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")]
|
||||
file static class GeneratedServiceCollectionExtensions
|
||||
{
|
||||
[global::System.Runtime.CompilerServices.InterceptsLocationAttribute(1, "WEYG7v0MUfj4KhOfDMNMrZUAAABQcm9ncmFtLmNz")]
|
||||
public static IServiceCollection AddOpenApi(this IServiceCollection services, string documentName)
|
||||
{
|
||||
return services.AddOpenApi(documentName, options =>
|
||||
{
|
||||
options.AddSchemaTransformer(new XmlCommentSchemaTransformer());
|
||||
options.AddOperationTransformer(new XmlCommentOperationTransformer());
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,201 @@
|
|||
//HintName: OpenApiXmlCommentSupport.generated.cs
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
#nullable enable
|
||||
|
||||
namespace System.Runtime.CompilerServices
|
||||
{
|
||||
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.OpenApi.SourceGenerators, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")]
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
|
||||
file sealed class InterceptsLocationAttribute : System.Attribute
|
||||
{
|
||||
public InterceptsLocationAttribute(int version, string data)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace Microsoft.AspNetCore.OpenApi.Generated
|
||||
{
|
||||
using DocFx.XmlComments;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.OpenApi;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Microsoft.OpenApi.Any;
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.OpenApi.SourceGenerators, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")]
|
||||
file static class XmlCommentCache
|
||||
{
|
||||
private static Dictionary<(Type?, string?), string>? _cache;
|
||||
public static Dictionary<(Type?, string?), string> Cache
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_cache is null)
|
||||
{
|
||||
_cache = GenerateCacheEntries();
|
||||
}
|
||||
return _cache;
|
||||
}
|
||||
}
|
||||
|
||||
private static Dictionary<(Type?, string?), string> GenerateCacheEntries()
|
||||
{
|
||||
var _cache = new Dictionary<(Type?, string?), string>();
|
||||
return _cache;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.OpenApi.SourceGenerators, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")]
|
||||
file class XmlCommentOperationTransformer : IOpenApiOperationTransformer
|
||||
{
|
||||
public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransformerContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
var methodInfo = context.Description.ActionDescriptor is ControllerActionDescriptor controllerActionDescriptor
|
||||
? controllerActionDescriptor.MethodInfo
|
||||
: context.Description.ActionDescriptor.EndpointMetadata.OfType<MethodInfo>().SingleOrDefault();
|
||||
|
||||
if (methodInfo is null)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
if (XmlCommentCache.Cache.TryGetValue((methodInfo.DeclaringType, methodInfo.Name), out var methodCommentString))
|
||||
{
|
||||
System.Diagnostics.Debugger.Break();
|
||||
var methodComment = JsonSerializer.Deserialize<XmlComment>(methodCommentString);
|
||||
if (methodComment is null)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
operation.Summary = methodComment.Summary;
|
||||
operation.Description = methodComment.Description;
|
||||
foreach (var parameterComment in methodComment.Parameters)
|
||||
{
|
||||
var parameterInfo = methodInfo.GetParameters().SingleOrDefault(info => info.Name == parameterComment.Name);
|
||||
var operationParameter = operation.Parameters?.SingleOrDefault(parameter => parameter.Name == parameterComment.Name);
|
||||
if (operationParameter is not null)
|
||||
{
|
||||
operationParameter.Description = parameterComment.Description;
|
||||
if (parameterInfo is not null)
|
||||
{
|
||||
operationParameter.Example = OpenApiExamplesHelper.ToOpenApiAny(parameterComment.Example, parameterInfo.ParameterType);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var requestBody = operation.RequestBody;
|
||||
if (requestBody is not null)
|
||||
{
|
||||
requestBody.Description = parameterComment.Description;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (methodComment.Responses is { Count: > 0} && operation.Responses is { Count: > 0 })
|
||||
{
|
||||
foreach (var response in operation.Responses)
|
||||
{
|
||||
var responseComment = methodComment.Responses.SingleOrDefault(xmlResponse => xmlResponse.Code == response.Key);
|
||||
if (responseComment is not null)
|
||||
{
|
||||
response.Value.Description = responseComment.Description;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.OpenApi.SourceGenerators, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")]
|
||||
file class XmlCommentSchemaTransformer : IOpenApiSchemaTransformer
|
||||
{
|
||||
public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
if (context.JsonPropertyInfo is { AttributeProvider: PropertyInfo propertyInfo })
|
||||
{
|
||||
if (XmlCommentCache.Cache.TryGetValue((propertyInfo.DeclaringType, propertyInfo.Name), out var propertyCommentString))
|
||||
{
|
||||
var propertyComment = JsonSerializer.Deserialize<XmlComment>(propertyCommentString);
|
||||
if (propertyComment is not null)
|
||||
{
|
||||
schema.Description = propertyComment.Returns ?? propertyComment.Summary;
|
||||
if (propertyComment.Examples is { Count: > 0 })
|
||||
{
|
||||
schema.Example = OpenApiExamplesHelper.ToOpenApiAny(propertyComment.Examples.FirstOrDefault(), propertyInfo.PropertyType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (XmlCommentCache.Cache.TryGetValue((context.JsonTypeInfo.Type, null), out var typeCommentString))
|
||||
{
|
||||
var typeComment = JsonSerializer.Deserialize<XmlComment>(typeCommentString);
|
||||
if (typeComment is not null)
|
||||
{
|
||||
schema.Description = typeComment.Summary;
|
||||
if (typeComment.Examples is { Count: > 0 })
|
||||
{
|
||||
schema.Example = OpenApiExamplesHelper.ToOpenApiAny(typeComment.Examples.FirstOrDefault(), context.JsonTypeInfo.Type);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.OpenApi.SourceGenerators, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")]
|
||||
file static class OpenApiExamplesHelper
|
||||
{
|
||||
public static IOpenApiAny ToOpenApiAny(string? example, Type type)
|
||||
{
|
||||
if (example is null || type is null)
|
||||
{
|
||||
return new OpenApiNull();
|
||||
}
|
||||
return Type.GetTypeCode(type) switch
|
||||
{
|
||||
TypeCode.String => new OpenApiString(example),
|
||||
TypeCode.Boolean => new OpenApiBoolean(bool.Parse(example)),
|
||||
TypeCode.Int32 => new OpenApiInteger(int.Parse(example)),
|
||||
TypeCode.Int64 => new OpenApiLong(long.Parse(example)),
|
||||
TypeCode.Double => new OpenApiDouble(double.Parse(example)),
|
||||
TypeCode.Single => new OpenApiFloat(float.Parse(example)),
|
||||
TypeCode.DateTime => new OpenApiDateTime(DateTime.Parse(example)),
|
||||
_ => new OpenApiNull()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.OpenApi.SourceGenerators, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")]
|
||||
file static class GeneratedServiceCollectionExtensions
|
||||
{
|
||||
[global::System.Runtime.CompilerServices.InterceptsLocationAttribute(1, "o5sPKTobw7zSg5h72CUcUpUAAABQcm9ncmFtLmNz")]
|
||||
public static IServiceCollection AddOpenApi(this IServiceCollection services, string documentName, Action<OpenApiOptions> configureOptions)
|
||||
{
|
||||
// This overload is not intercepted.
|
||||
return OpenApiServiceCollectionExtensions.AddOpenApi(services, documentName, options =>
|
||||
{
|
||||
configureOptions(options);
|
||||
options.AddSchemaTransformer(new XmlCommentTransformer());
|
||||
options.AddOperationTransformer(new XmlCommentOperationTransformer());
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,199 @@
|
|||
//HintName: OpenApiXmlCommentSupport.generated.cs
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
#nullable enable
|
||||
|
||||
namespace System.Runtime.CompilerServices
|
||||
{
|
||||
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.OpenApi.SourceGenerators, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")]
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
|
||||
file sealed class InterceptsLocationAttribute : System.Attribute
|
||||
{
|
||||
public InterceptsLocationAttribute(int version, string data)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace Microsoft.AspNetCore.OpenApi.Generated
|
||||
{
|
||||
using DocFx.XmlComments;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.OpenApi;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Microsoft.OpenApi.Any;
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.OpenApi.SourceGenerators, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")]
|
||||
file static class XmlCommentCache
|
||||
{
|
||||
private static Dictionary<(Type?, string?), string>? _cache;
|
||||
public static Dictionary<(Type?, string?), string> Cache
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_cache is null)
|
||||
{
|
||||
_cache = GenerateCacheEntries();
|
||||
}
|
||||
return _cache;
|
||||
}
|
||||
}
|
||||
|
||||
private static Dictionary<(Type?, string?), string> GenerateCacheEntries()
|
||||
{
|
||||
var _cache = new Dictionary<(Type?, string?), string>();
|
||||
return _cache;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.OpenApi.SourceGenerators, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")]
|
||||
file class XmlCommentOperationTransformer : IOpenApiOperationTransformer
|
||||
{
|
||||
public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransformerContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
var methodInfo = context.Description.ActionDescriptor is ControllerActionDescriptor controllerActionDescriptor
|
||||
? controllerActionDescriptor.MethodInfo
|
||||
: context.Description.ActionDescriptor.EndpointMetadata.OfType<MethodInfo>().SingleOrDefault();
|
||||
|
||||
if (methodInfo is null)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
if (XmlCommentCache.Cache.TryGetValue((methodInfo.DeclaringType, methodInfo.Name), out var methodCommentString))
|
||||
{
|
||||
System.Diagnostics.Debugger.Break();
|
||||
var methodComment = JsonSerializer.Deserialize<XmlComment>(methodCommentString);
|
||||
if (methodComment is null)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
operation.Summary = methodComment.Summary;
|
||||
operation.Description = methodComment.Description;
|
||||
foreach (var parameterComment in methodComment.Parameters)
|
||||
{
|
||||
var parameterInfo = methodInfo.GetParameters().SingleOrDefault(info => info.Name == parameterComment.Name);
|
||||
var operationParameter = operation.Parameters?.SingleOrDefault(parameter => parameter.Name == parameterComment.Name);
|
||||
if (operationParameter is not null)
|
||||
{
|
||||
operationParameter.Description = parameterComment.Description;
|
||||
if (parameterInfo is not null)
|
||||
{
|
||||
operationParameter.Example = OpenApiExamplesHelper.ToOpenApiAny(parameterComment.Example, parameterInfo.ParameterType);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var requestBody = operation.RequestBody;
|
||||
if (requestBody is not null)
|
||||
{
|
||||
requestBody.Description = parameterComment.Description;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (methodComment.Responses is { Count: > 0} && operation.Responses is { Count: > 0 })
|
||||
{
|
||||
foreach (var response in operation.Responses)
|
||||
{
|
||||
var responseComment = methodComment.Responses.SingleOrDefault(xmlResponse => xmlResponse.Code == response.Key);
|
||||
if (responseComment is not null)
|
||||
{
|
||||
response.Value.Description = responseComment.Description;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.OpenApi.SourceGenerators, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")]
|
||||
file class XmlCommentSchemaTransformer : IOpenApiSchemaTransformer
|
||||
{
|
||||
public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
if (context.JsonPropertyInfo is { AttributeProvider: PropertyInfo propertyInfo })
|
||||
{
|
||||
if (XmlCommentCache.Cache.TryGetValue((propertyInfo.DeclaringType, propertyInfo.Name), out var propertyCommentString))
|
||||
{
|
||||
var propertyComment = JsonSerializer.Deserialize<XmlComment>(propertyCommentString);
|
||||
if (propertyComment is not null)
|
||||
{
|
||||
schema.Description = propertyComment.Returns ?? propertyComment.Summary;
|
||||
if (propertyComment.Examples is { Count: > 0 })
|
||||
{
|
||||
schema.Example = OpenApiExamplesHelper.ToOpenApiAny(propertyComment.Examples.FirstOrDefault(), propertyInfo.PropertyType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (XmlCommentCache.Cache.TryGetValue((context.JsonTypeInfo.Type, null), out var typeCommentString))
|
||||
{
|
||||
var typeComment = JsonSerializer.Deserialize<XmlComment>(typeCommentString);
|
||||
if (typeComment is not null)
|
||||
{
|
||||
schema.Description = typeComment.Summary;
|
||||
if (typeComment.Examples is { Count: > 0 })
|
||||
{
|
||||
schema.Example = OpenApiExamplesHelper.ToOpenApiAny(typeComment.Examples.FirstOrDefault(), context.JsonTypeInfo.Type);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.OpenApi.SourceGenerators, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")]
|
||||
file static class OpenApiExamplesHelper
|
||||
{
|
||||
public static IOpenApiAny ToOpenApiAny(string? example, Type type)
|
||||
{
|
||||
if (example is null || type is null)
|
||||
{
|
||||
return new OpenApiNull();
|
||||
}
|
||||
return Type.GetTypeCode(type) switch
|
||||
{
|
||||
TypeCode.String => new OpenApiString(example),
|
||||
TypeCode.Boolean => new OpenApiBoolean(bool.Parse(example)),
|
||||
TypeCode.Int32 => new OpenApiInteger(int.Parse(example)),
|
||||
TypeCode.Int64 => new OpenApiLong(long.Parse(example)),
|
||||
TypeCode.Double => new OpenApiDouble(double.Parse(example)),
|
||||
TypeCode.Single => new OpenApiFloat(float.Parse(example)),
|
||||
TypeCode.DateTime => new OpenApiDateTime(DateTime.Parse(example)),
|
||||
_ => new OpenApiNull()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.OpenApi.SourceGenerators, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")]
|
||||
file static class GeneratedServiceCollectionExtensions
|
||||
{
|
||||
[global::System.Runtime.CompilerServices.InterceptsLocationAttribute(1, "Oxa8kEsc+P8JWEeyXj4l05UAAABQcm9ncmFtLmNz")]
|
||||
public static IServiceCollection AddOpenApi(this IServiceCollection services, string documentName)
|
||||
{
|
||||
return services.AddOpenApi(documentName, options =>
|
||||
{
|
||||
options.AddSchemaTransformer(new XmlCommentSchemaTransformer());
|
||||
options.AddOperationTransformer(new XmlCommentOperationTransformer());
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,199 @@
|
|||
//HintName: OpenApiXmlCommentSupport.generated.cs
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
#nullable enable
|
||||
|
||||
namespace System.Runtime.CompilerServices
|
||||
{
|
||||
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.OpenApi.SourceGenerators, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")]
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
|
||||
file sealed class InterceptsLocationAttribute : System.Attribute
|
||||
{
|
||||
public InterceptsLocationAttribute(int version, string data)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace Microsoft.AspNetCore.OpenApi.Generated
|
||||
{
|
||||
using DocFx.XmlComments;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.OpenApi;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Microsoft.OpenApi.Any;
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.OpenApi.SourceGenerators, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")]
|
||||
file static class XmlCommentCache
|
||||
{
|
||||
private static Dictionary<(Type?, string?), string>? _cache;
|
||||
public static Dictionary<(Type?, string?), string> Cache
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_cache is null)
|
||||
{
|
||||
_cache = GenerateCacheEntries();
|
||||
}
|
||||
return _cache;
|
||||
}
|
||||
}
|
||||
|
||||
private static Dictionary<(Type?, string?), string> GenerateCacheEntries()
|
||||
{
|
||||
var _cache = new Dictionary<(Type?, string?), string>();
|
||||
return _cache;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.OpenApi.SourceGenerators, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")]
|
||||
file class XmlCommentOperationTransformer : IOpenApiOperationTransformer
|
||||
{
|
||||
public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransformerContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
var methodInfo = context.Description.ActionDescriptor is ControllerActionDescriptor controllerActionDescriptor
|
||||
? controllerActionDescriptor.MethodInfo
|
||||
: context.Description.ActionDescriptor.EndpointMetadata.OfType<MethodInfo>().SingleOrDefault();
|
||||
|
||||
if (methodInfo is null)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
if (XmlCommentCache.Cache.TryGetValue((methodInfo.DeclaringType, methodInfo.Name), out var methodCommentString))
|
||||
{
|
||||
System.Diagnostics.Debugger.Break();
|
||||
var methodComment = JsonSerializer.Deserialize<XmlComment>(methodCommentString);
|
||||
if (methodComment is null)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
operation.Summary = methodComment.Summary;
|
||||
operation.Description = methodComment.Description;
|
||||
foreach (var parameterComment in methodComment.Parameters)
|
||||
{
|
||||
var parameterInfo = methodInfo.GetParameters().SingleOrDefault(info => info.Name == parameterComment.Name);
|
||||
var operationParameter = operation.Parameters?.SingleOrDefault(parameter => parameter.Name == parameterComment.Name);
|
||||
if (operationParameter is not null)
|
||||
{
|
||||
operationParameter.Description = parameterComment.Description;
|
||||
if (parameterInfo is not null)
|
||||
{
|
||||
operationParameter.Example = OpenApiExamplesHelper.ToOpenApiAny(parameterComment.Example, parameterInfo.ParameterType);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var requestBody = operation.RequestBody;
|
||||
if (requestBody is not null)
|
||||
{
|
||||
requestBody.Description = parameterComment.Description;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (methodComment.Responses is { Count: > 0} && operation.Responses is { Count: > 0 })
|
||||
{
|
||||
foreach (var response in operation.Responses)
|
||||
{
|
||||
var responseComment = methodComment.Responses.SingleOrDefault(xmlResponse => xmlResponse.Code == response.Key);
|
||||
if (responseComment is not null)
|
||||
{
|
||||
response.Value.Description = responseComment.Description;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.OpenApi.SourceGenerators, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")]
|
||||
file class XmlCommentSchemaTransformer : IOpenApiSchemaTransformer
|
||||
{
|
||||
public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
if (context.JsonPropertyInfo is { AttributeProvider: PropertyInfo propertyInfo })
|
||||
{
|
||||
if (XmlCommentCache.Cache.TryGetValue((propertyInfo.DeclaringType, propertyInfo.Name), out var propertyCommentString))
|
||||
{
|
||||
var propertyComment = JsonSerializer.Deserialize<XmlComment>(propertyCommentString);
|
||||
if (propertyComment is not null)
|
||||
{
|
||||
schema.Description = propertyComment.Returns ?? propertyComment.Summary;
|
||||
if (propertyComment.Examples is { Count: > 0 })
|
||||
{
|
||||
schema.Example = OpenApiExamplesHelper.ToOpenApiAny(propertyComment.Examples.FirstOrDefault(), propertyInfo.PropertyType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (XmlCommentCache.Cache.TryGetValue((context.JsonTypeInfo.Type, null), out var typeCommentString))
|
||||
{
|
||||
var typeComment = JsonSerializer.Deserialize<XmlComment>(typeCommentString);
|
||||
if (typeComment is not null)
|
||||
{
|
||||
schema.Description = typeComment.Summary;
|
||||
if (typeComment.Examples is { Count: > 0 })
|
||||
{
|
||||
schema.Example = OpenApiExamplesHelper.ToOpenApiAny(typeComment.Examples.FirstOrDefault(), context.JsonTypeInfo.Type);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.OpenApi.SourceGenerators, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")]
|
||||
file static class OpenApiExamplesHelper
|
||||
{
|
||||
public static IOpenApiAny ToOpenApiAny(string? example, Type type)
|
||||
{
|
||||
if (example is null || type is null)
|
||||
{
|
||||
return new OpenApiNull();
|
||||
}
|
||||
return Type.GetTypeCode(type) switch
|
||||
{
|
||||
TypeCode.String => new OpenApiString(example),
|
||||
TypeCode.Boolean => new OpenApiBoolean(bool.Parse(example)),
|
||||
TypeCode.Int32 => new OpenApiInteger(int.Parse(example)),
|
||||
TypeCode.Int64 => new OpenApiLong(long.Parse(example)),
|
||||
TypeCode.Double => new OpenApiDouble(double.Parse(example)),
|
||||
TypeCode.Single => new OpenApiFloat(float.Parse(example)),
|
||||
TypeCode.DateTime => new OpenApiDateTime(DateTime.Parse(example)),
|
||||
_ => new OpenApiNull()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.OpenApi.SourceGenerators, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")]
|
||||
file static class GeneratedServiceCollectionExtensions
|
||||
{
|
||||
[global::System.Runtime.CompilerServices.InterceptsLocationAttribute(1, "TLaexAhvQY+MEDWz7mc7e5UAAABQcm9ncmFtLmNz")]
|
||||
public static IServiceCollection AddOpenApi(this IServiceCollection services)
|
||||
{
|
||||
return services.AddOpenApi("v1", options =>
|
||||
{
|
||||
options.AddSchemaTransformer(new XmlCommentSchemaTransformer());
|
||||
options.AddOperationTransformer(new XmlCommentOperationTransformer());
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче