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
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests", "test\Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests.csproj", "{1541DE82-68F3-440A-9ABD-429EC986CB84}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests", "test\Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests.csproj", "{1541DE82-68F3-440A-9ABD-429EC986CB84}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DocFx.XmlComments", "src\DocFx.XmlComments.csproj", "{28570C7C-A9B1-4A6A-95B8-EA2153FCA4A3}"
|
|
||||||
EndProject
|
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
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}.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.ActiveCfg = Release|Any CPU
|
||||||
{1541DE82-68F3-440A-9ABD-429EC986CB84}.Release|Any CPU.Build.0 = 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
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace DocFx.XmlComments;
|
namespace DocFx.XmlComments;
|
|
@ -1,3 +1,5 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
|
@ -16,7 +16,7 @@ internal static class XmlCommentTransformer
|
||||||
private static XslCompiledTransform InitializeTransform()
|
private static XslCompiledTransform InitializeTransform()
|
||||||
{
|
{
|
||||||
var assembly = typeof(XmlCommentTransformer).Assembly;
|
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 stream = assembly.GetManifestResourceStream(xsltFilePath);
|
||||||
using var reader = XmlReader.Create(stream);
|
using var reader = XmlReader.Create(stream);
|
||||||
var xsltSettings = new XsltSettings(true, true);
|
var xsltSettings = new XsltSettings(true, true);
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
using System.Xml.Linq;
|
using System.Xml.Linq;
|
||||||
using System.Xml.XPath;
|
using System.Xml.XPath;
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
using System.Xml.Linq;
|
using System.Xml.Linq;
|
||||||
using System.Xml.XPath;
|
using System.Xml.XPath;
|
|
@ -15,19 +15,24 @@
|
||||||
<NoWarn>RSEXPERIMENTAL002</NoWarn>
|
<NoWarn>RSEXPERIMENTAL002</NoWarn>
|
||||||
<LangVersion>preview</LangVersion>
|
<LangVersion>preview</LangVersion>
|
||||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||||
|
<PackageTags>source generator, openapi, xml</PackageTags>
|
||||||
|
<Description>Source generator to provide XML doc support for Microsoft.AspNetCore.OpenApi</Description>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" PrivateAssets="All" IsImplicitlyDefined="true" Version="4.11.0-2.final" />
|
<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="Microsoft.CodeAnalysis.Common" PrivateAssets="All" IsImplicitlyDefined="true" Version="4.11.0-2.final" />
|
||||||
</ItemGroup>
|
<PackageReference Include="System.Text.Json" Version="8.0.4" />
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\src\DocFx.XmlComments.csproj" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include=".\README.md" Pack="true" PackagePath="\"/>
|
<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>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -182,7 +182,7 @@ namespace Microsoft.AspNetCore.OpenApi.Generated
|
||||||
{
|
{
|
||||||
public static IOpenApiAny ToOpenApiAny(string? example, Type type)
|
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();
|
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 => """
|
AddOpenApiOverloadVariant.AddOpenApiDocumentNameConfigureOptions => """
|
||||||
public static IServiceCollection AddOpenApi(this IServiceCollection services, string documentName, Action<OpenApiOptions> configureOptions)
|
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 invocationExpression = (InvocationExpressionSyntax)context.Node;
|
||||||
var interceptableLocation = context.SemanticModel.GetInterceptableLocation(invocationExpression, cancellationToken);
|
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,
|
return new(AddOpenApiOverloadVariant.AddOpenApi, invocationExpression, interceptableLocation);
|
||||||
1 => AddOpenApiOverloadVariant.AddOpenApiDocumentName,
|
}
|
||||||
2 => AddOpenApiOverloadVariant.AddOpenApiDocumentNameConfigureOptions,
|
else if (argumentsCount == 2)
|
||||||
_ => throw new InvalidOperationException("Invalid number of arguments for supported `AddOpenApi` overload."),
|
{
|
||||||
}, invocationExpression, interceptableLocation);
|
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)
|
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();
|
return new OpenApiNull();
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,12 +14,11 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<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>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="../gen/Microsoft.AspNetCore.OpenApi.SourceGenerators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
|
<ProjectReference Include="../gen/Microsoft.AspNetCore.OpenApi.SourceGenerators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="true" />
|
||||||
<ProjectReference Include="../src/DocFx.XmlComments.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="true" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<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>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\gen\Microsoft.AspNetCore.OpenApi.SourceGenerators.csproj" />
|
<ProjectReference Include="..\gen\Microsoft.AspNetCore.OpenApi.SourceGenerators.csproj" />
|
||||||
<ProjectReference Include="..\src\DocFx.XmlComments.csproj" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<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());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
Загрузка…
Ссылка в новой задаче