Skip to content

Commit 534946e

Browse files
authored
Merge feature/openapi to main (#55182)
Co-authored-by: Martin Costello <[email protected]> Co-authored-by: Rick Anderson <[email protected]> This PR adds support for OpenAPI document generation, sans schema generation to Microsoft.AspNetCore.OpenApi. Relevant changes are available in individual PRs: - Add entry-point APIs for OpenAPI support (#54789) - Support resolving OpenApiPaths entries from document (#54847) - Support generating OpenAPI operation and associated fields (#54903) - Add APIs for OpenAPI document transformers (#54935) - Add support for generating OpenAPI parameters (#55041) - Add support for generating OpenAPI responses (#55020) - Add support for generating OpenAPI request bodies (#55040)
1 parent afbcecb commit 534946e

File tree

57 files changed

+3901
-5
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+3901
-5
lines changed

AspNetCore.sln

+41
Original file line numberDiff line numberDiff line change
@@ -1788,12 +1788,18 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NotReferencedInWasmCodePack
17881788
EndProject
17891789
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Components.WasmRemoteAuthentication", "src\Components\test\testassets\Components.WasmRemoteAuthentication\Components.WasmRemoteAuthentication.csproj", "{8A021D6D-7935-4AB3-BB47-38D4FF9B0D13}"
17901790
EndProject
1791+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample", "src\OpenApi\sample\Sample.csproj", "{6DEC24A8-A166-432F-8E3B-58FFCDA92F52}"
1792+
EndProject
17911793
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Hybrid", "Hybrid", "{2D64CA23-6E81-488E-A7D3-9BDF87240098}"
17921794
EndProject
17931795
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Caching.Hybrid", "src\Caching\Hybrid\src\Microsoft.Extensions.Caching.Hybrid.csproj", "{2B60E6D3-9E7C-427A-AD4E-BBE9A6D935B9}"
17941796
EndProject
17951797
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Caching.Hybrid.Tests", "src\Caching\Hybrid\test\Microsoft.Extensions.Caching.Hybrid.Tests.csproj", "{CF63C942-895A-4F6B-888A-7653D7C4991A}"
17961798
EndProject
1799+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "perf", "perf", "{9DC6B242-457B-4767-A84B-C3D23B76C642}"
1800+
EndProject
1801+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.OpenApi.Microbenchmarks", "src\OpenApi\perf\Microbenchmarks\Microsoft.AspNetCore.OpenApi.Microbenchmarks.csproj", "{D53F0EF7-0CDC-49B4-AA2D-229901B0A734}"
1802+
EndProject
17971803
Global
17981804
GlobalSection(SolutionConfigurationPlatforms) = preSolution
17991805
Debug|Any CPU = Debug|Any CPU
@@ -10795,6 +10801,22 @@ Global
1079510801
{8A021D6D-7935-4AB3-BB47-38D4FF9B0D13}.Release|x64.Build.0 = Release|Any CPU
1079610802
{8A021D6D-7935-4AB3-BB47-38D4FF9B0D13}.Release|x86.ActiveCfg = Release|Any CPU
1079710803
{8A021D6D-7935-4AB3-BB47-38D4FF9B0D13}.Release|x86.Build.0 = Release|Any CPU
10804+
{6DEC24A8-A166-432F-8E3B-58FFCDA92F52}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
10805+
{6DEC24A8-A166-432F-8E3B-58FFCDA92F52}.Debug|Any CPU.Build.0 = Debug|Any CPU
10806+
{6DEC24A8-A166-432F-8E3B-58FFCDA92F52}.Debug|arm64.ActiveCfg = Debug|Any CPU
10807+
{6DEC24A8-A166-432F-8E3B-58FFCDA92F52}.Debug|arm64.Build.0 = Debug|Any CPU
10808+
{6DEC24A8-A166-432F-8E3B-58FFCDA92F52}.Debug|x64.ActiveCfg = Debug|Any CPU
10809+
{6DEC24A8-A166-432F-8E3B-58FFCDA92F52}.Debug|x64.Build.0 = Debug|Any CPU
10810+
{6DEC24A8-A166-432F-8E3B-58FFCDA92F52}.Debug|x86.ActiveCfg = Debug|Any CPU
10811+
{6DEC24A8-A166-432F-8E3B-58FFCDA92F52}.Debug|x86.Build.0 = Debug|Any CPU
10812+
{6DEC24A8-A166-432F-8E3B-58FFCDA92F52}.Release|Any CPU.ActiveCfg = Release|Any CPU
10813+
{6DEC24A8-A166-432F-8E3B-58FFCDA92F52}.Release|Any CPU.Build.0 = Release|Any CPU
10814+
{6DEC24A8-A166-432F-8E3B-58FFCDA92F52}.Release|arm64.ActiveCfg = Release|Any CPU
10815+
{6DEC24A8-A166-432F-8E3B-58FFCDA92F52}.Release|arm64.Build.0 = Release|Any CPU
10816+
{6DEC24A8-A166-432F-8E3B-58FFCDA92F52}.Release|x64.ActiveCfg = Release|Any CPU
10817+
{6DEC24A8-A166-432F-8E3B-58FFCDA92F52}.Release|x64.Build.0 = Release|Any CPU
10818+
{6DEC24A8-A166-432F-8E3B-58FFCDA92F52}.Release|x86.ActiveCfg = Release|Any CPU
10819+
{6DEC24A8-A166-432F-8E3B-58FFCDA92F52}.Release|x86.Build.0 = Release|Any CPU
1079810820
{2B60E6D3-9E7C-427A-AD4E-BBE9A6D935B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
1079910821
{2B60E6D3-9E7C-427A-AD4E-BBE9A6D935B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
1080010822
{2B60E6D3-9E7C-427A-AD4E-BBE9A6D935B9}.Debug|arm64.ActiveCfg = Debug|Any CPU
@@ -10827,6 +10849,22 @@ Global
1082710849
{CF63C942-895A-4F6B-888A-7653D7C4991A}.Release|x64.Build.0 = Release|Any CPU
1082810850
{CF63C942-895A-4F6B-888A-7653D7C4991A}.Release|x86.ActiveCfg = Release|Any CPU
1082910851
{CF63C942-895A-4F6B-888A-7653D7C4991A}.Release|x86.Build.0 = Release|Any CPU
10852+
{D53F0EF7-0CDC-49B4-AA2D-229901B0A734}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
10853+
{D53F0EF7-0CDC-49B4-AA2D-229901B0A734}.Debug|Any CPU.Build.0 = Debug|Any CPU
10854+
{D53F0EF7-0CDC-49B4-AA2D-229901B0A734}.Debug|arm64.ActiveCfg = Debug|Any CPU
10855+
{D53F0EF7-0CDC-49B4-AA2D-229901B0A734}.Debug|arm64.Build.0 = Debug|Any CPU
10856+
{D53F0EF7-0CDC-49B4-AA2D-229901B0A734}.Debug|x64.ActiveCfg = Debug|Any CPU
10857+
{D53F0EF7-0CDC-49B4-AA2D-229901B0A734}.Debug|x64.Build.0 = Debug|Any CPU
10858+
{D53F0EF7-0CDC-49B4-AA2D-229901B0A734}.Debug|x86.ActiveCfg = Debug|Any CPU
10859+
{D53F0EF7-0CDC-49B4-AA2D-229901B0A734}.Debug|x86.Build.0 = Debug|Any CPU
10860+
{D53F0EF7-0CDC-49B4-AA2D-229901B0A734}.Release|Any CPU.ActiveCfg = Release|Any CPU
10861+
{D53F0EF7-0CDC-49B4-AA2D-229901B0A734}.Release|Any CPU.Build.0 = Release|Any CPU
10862+
{D53F0EF7-0CDC-49B4-AA2D-229901B0A734}.Release|arm64.ActiveCfg = Release|Any CPU
10863+
{D53F0EF7-0CDC-49B4-AA2D-229901B0A734}.Release|arm64.Build.0 = Release|Any CPU
10864+
{D53F0EF7-0CDC-49B4-AA2D-229901B0A734}.Release|x64.ActiveCfg = Release|Any CPU
10865+
{D53F0EF7-0CDC-49B4-AA2D-229901B0A734}.Release|x64.Build.0 = Release|Any CPU
10866+
{D53F0EF7-0CDC-49B4-AA2D-229901B0A734}.Release|x86.ActiveCfg = Release|Any CPU
10867+
{D53F0EF7-0CDC-49B4-AA2D-229901B0A734}.Release|x86.Build.0 = Release|Any CPU
1083010868
EndGlobalSection
1083110869
GlobalSection(SolutionProperties) = preSolution
1083210870
HideSolutionNode = FALSE
@@ -11710,9 +11748,12 @@ Global
1171011748
{15D08EA7-8C63-45FB-8B4D-C5F8E43B433E} = {05A169C7-4F20-4516-B10A-B13C5649D346}
1171111749
{433F91E4-E39D-4EB0-B798-2998B3969A2C} = {6126DCE4-9692-4EE2-B240-C65743572995}
1171211750
{8A021D6D-7935-4AB3-BB47-38D4FF9B0D13} = {6126DCE4-9692-4EE2-B240-C65743572995}
11751+
{6DEC24A8-A166-432F-8E3B-58FFCDA92F52} = {2299CCD8-8F9C-4F2B-A633-9BF4DA81022B}
1171311752
{2D64CA23-6E81-488E-A7D3-9BDF87240098} = {0F39820F-F4A5-41C6-9809-D79B68F032EF}
1171411753
{2B60E6D3-9E7C-427A-AD4E-BBE9A6D935B9} = {2D64CA23-6E81-488E-A7D3-9BDF87240098}
1171511754
{CF63C942-895A-4F6B-888A-7653D7C4991A} = {2D64CA23-6E81-488E-A7D3-9BDF87240098}
11755+
{9DC6B242-457B-4767-A84B-C3D23B76C642} = {2299CCD8-8F9C-4F2B-A633-9BF4DA81022B}
11756+
{D53F0EF7-0CDC-49B4-AA2D-229901B0A734} = {9DC6B242-457B-4767-A84B-C3D23B76C642}
1171611757
EndGlobalSection
1171711758
GlobalSection(ExtensibilityGlobals) = postSolution
1171811759
SolutionGuid = {3E8720B3-DBDD-498C-B383-2CC32A054E8F}

eng/Dependencies.props

+1
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ and are generated based on the last package release.
6969
<LatestPackageReference Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.XUnit" />
7070
<LatestPackageReference Include="Microsoft.CodeAnalysis.CSharp.CodeFix.Testing.XUnit" />
7171
<LatestPackageReference Include="Microsoft.OpenApi" />
72+
<LatestPackageReference Include="Microsoft.OpenApi.Readers" />
7273
<LatestPackageReference Include="System.Buffers" />
7374
<LatestPackageReference Include="System.CodeDom" />
7475
<LatestPackageReference Include="System.CommandLine.Experimental" />

eng/Versions.props

+1
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,7 @@
335335
<MicrosoftDataSqlClientVersion>4.0.5</MicrosoftDataSqlClientVersion>
336336
<MicrosoftAspNetCoreAppVersion>6.0.0-preview.3.21167.1</MicrosoftAspNetCoreAppVersion>
337337
<MicrosoftOpenApiVersion>1.6.13</MicrosoftOpenApiVersion>
338+
<MicrosoftOpenApiReadersVersion>1.6.13</MicrosoftOpenApiReadersVersion>
338339
<!-- dotnet tool versions (see also auto-updated DotnetEfVersion property). -->
339340
<DotnetDumpVersion>6.0.322601</DotnetDumpVersion>
340341
<DotnetServeVersion>1.10.93</DotnetServeVersion>

src/Http/Http.Extensions/test/RequestDelegateGenerator/SharedTypes.cs

-2
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@
1212
using Microsoft.Extensions.DependencyInjection;
1313
using Microsoft.AspNetCore.Mvc;
1414
using Microsoft.AspNetCore.Routing;
15-
using System.Diagnostics.CodeAnalysis;
16-
using Microsoft.Diagnostics.Runtime.Interop;
1715

1816
namespace Microsoft.AspNetCore.Http.Generators.Tests;
1917

src/Mvc/Mvc.ApiExplorer/src/Microsoft.AspNetCore.Mvc.ApiExplorer.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,6 @@
2020

2121
<ItemGroup>
2222
<InternalsVisibleTo Include="Microsoft.AspNetCore.Mvc.ApiExplorer.Test" />
23+
<InternalsVisibleTo Include="Microsoft.AspNetCore.OpenApi.Tests" />
2324
</ItemGroup>
2425
</Project>

src/OpenApi/OpenApi.slnf

+4-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
"src\\Http\\Http\\src\\Microsoft.AspNetCore.Http.csproj",
99
"src\\Http\\Routing\\src\\Microsoft.AspNetCore.Routing.csproj",
1010
"src\\OpenApi\\src\\Microsoft.AspNetCore.OpenApi.csproj",
11-
"src\\OpenApi\\test\\Microsoft.AspNetCore.OpenApi.Tests.csproj"
11+
"src\\OpenApi\\test\\Microsoft.AspNetCore.OpenApi.Tests.csproj",
12+
"src\\OpenApi\\sample\\Sample.csproj",
13+
"src\\OpenApi\\perf\\Microbenchmarks\\Microsoft.AspNetCore.OpenApi.Microbenchmarks.csproj"
1214
]
1315
}
14-
}
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
[assembly: BenchmarkDotNet.Attributes.AspNetCoreBenchmark]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using BenchmarkDotNet.Attributes;
5+
using Microsoft.AspNetCore.Builder;
6+
using Microsoft.AspNetCore.Http;
7+
using Microsoft.AspNetCore.Routing;
8+
9+
namespace Microsoft.AspNetCore.OpenApi.Microbenchmarks;
10+
11+
/// <summary>
12+
/// The following benchmarks are used to assess the performance of the
13+
/// core OpenAPI document generation logic. The parameter under test here
14+
/// is the number of endpoints/operations that are defined in the application.
15+
/// </summary>
16+
[MemoryDiagnoser]
17+
public class GenerationBenchmarks : OpenApiDocumentServiceTestBase
18+
{
19+
[Params(10, 100, 1000)]
20+
public int EndpointCount { get; set; }
21+
22+
private readonly IEndpointRouteBuilder _builder = CreateBuilder();
23+
private readonly OpenApiOptions _options = new OpenApiOptions();
24+
private OpenApiDocumentService _documentService;
25+
26+
[GlobalSetup(Target = nameof(GenerateDocument))]
27+
public void OperationTransformerAsDelegate_Setup()
28+
{
29+
_builder.MapGet("/", () => { });
30+
for (var i = 0; i <= EndpointCount; i++)
31+
{
32+
_builder.MapGet($"/{i}", (int i) => new Todo(1, "Write benchmarks", false, DateTime.Now));
33+
_builder.MapPost($"/{i}", (Todo todo) => Results.Ok());
34+
_builder.MapDelete($"/{i}", (string id) => Results.NoContent());
35+
}
36+
_documentService = CreateDocumentService(_builder, _options);
37+
}
38+
39+
[Benchmark]
40+
public async Task GenerateDocument()
41+
{
42+
await _documentService.GetOpenApiDocumentAsync();
43+
}
44+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
5+
<OutputType>Exe</OutputType>
6+
<PreserveCompilationContext>true</PreserveCompilationContext>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<Reference Include="BenchmarkDotNet" />
11+
<Reference Include="Microsoft.AspNetCore" />
12+
<Reference Include="Microsoft.AspNetCore.OpenApi" />
13+
</ItemGroup>
14+
15+
<ItemGroup>
16+
<ProjectReference Include="..\..\test\Microsoft.AspNetCore.OpenApi.Tests.csproj" />
17+
</ItemGroup>
18+
19+
<ItemGroup>
20+
<Compile Include="$(SharedSourceRoot)BenchmarkRunner\*.cs" />
21+
</ItemGroup>
22+
23+
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using BenchmarkDotNet.Attributes;
5+
using Microsoft.AspNetCore.Builder;
6+
using Microsoft.AspNetCore.Routing;
7+
using Microsoft.OpenApi.Models;
8+
9+
namespace Microsoft.AspNetCore.OpenApi.Microbenchmarks;
10+
11+
/// <summary>
12+
/// The following benchmarks are used to assess the memory and performance
13+
/// impact of different types of transformers. In particular, we want to
14+
/// measure the impact of (a) context-object creation and caching and (b)
15+
/// enumerator usage when processing operations in a given document.
16+
/// </summary>
17+
public class TransformersBenchmark : OpenApiDocumentServiceTestBase
18+
{
19+
[Params(10, 100, 1000)]
20+
public int TransformerCount { get; set; }
21+
22+
private readonly IEndpointRouteBuilder _builder = CreateBuilder();
23+
private readonly OpenApiOptions _options = new OpenApiOptions();
24+
private OpenApiDocumentService _documentService;
25+
26+
[GlobalSetup(Target = nameof(OperationTransformerAsDelegate))]
27+
public void OperationTransformerAsDelegate_Setup()
28+
{
29+
_builder.MapGet("/", () => { });
30+
for (var i = 0; i <= TransformerCount; i++)
31+
{
32+
_options.UseOperationTransformer((operation, context, token) =>
33+
{
34+
operation.Description = "New Description";
35+
return Task.CompletedTask;
36+
});
37+
}
38+
_documentService = CreateDocumentService(_builder, _options);
39+
}
40+
41+
[GlobalSetup(Target = nameof(ActivatedDocumentTransformer))]
42+
public void ActivatedDocumentTransformer_Setup()
43+
{
44+
_builder.MapGet("/", () => { });
45+
for (var i = 0; i <= TransformerCount; i++)
46+
{
47+
_options.UseTransformer<ActivatedTransformer>();
48+
}
49+
_documentService = CreateDocumentService(_builder, _options);
50+
}
51+
52+
[GlobalSetup(Target = nameof(DocumentTransformerAsDelegate))]
53+
public void DocumentTransformerAsDelegate_Delegate()
54+
{
55+
_builder.MapGet("/", () => { });
56+
for (var i = 0; i <= TransformerCount; i++)
57+
{
58+
_options.UseTransformer((document, context, token) =>
59+
{
60+
document.Info.Description = "New Description";
61+
return Task.CompletedTask;
62+
});
63+
}
64+
_documentService = CreateDocumentService(_builder, _options);
65+
}
66+
67+
[Benchmark]
68+
public async Task OperationTransformerAsDelegate()
69+
{
70+
await _documentService.GetOpenApiDocumentAsync();
71+
}
72+
73+
[Benchmark]
74+
public async Task ActivatedDocumentTransformer()
75+
{
76+
await _documentService.GetOpenApiDocumentAsync();
77+
}
78+
79+
[Benchmark]
80+
public async Task DocumentTransformerAsDelegate()
81+
{
82+
await _documentService.GetOpenApiDocumentAsync();
83+
}
84+
85+
private class ActivatedTransformer : IOpenApiDocumentTransformer
86+
{
87+
public Task TransformAsync(OpenApiDocument document, OpenApiDocumentTransformerContext context, CancellationToken cancellationToken)
88+
{
89+
document.Info.Description = "Info Description";
90+
return Task.CompletedTask;
91+
}
92+
}
93+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
internal static class OpenApiEndpointRouteBuilderExtensions
5+
{
6+
/// <summary>
7+
/// Helper method to render Swagger UI view for testing.
8+
/// </summary>
9+
public static IEndpointConventionBuilder MapSwaggerUi(this IEndpointRouteBuilder endpoints)
10+
{
11+
return endpoints.MapGet("/swagger/{documentName}", (string documentName) => Results.Content($$"""
12+
<html>
13+
<head>
14+
<meta charset="UTF-8">
15+
<title>OpenAPI -- {{documentName}}</title>
16+
<link rel="stylesheet" type="text/css" href="https://unpkg.com/swagger-ui-dist/swagger-ui.css">
17+
</head>
18+
<body>
19+
<div id="swagger-ui"></div>
20+
21+
<script src="https://unpkg.com/swagger-ui-dist/swagger-ui-standalone-preset.js"></script>
22+
<script src="https://unpkg.com/swagger-ui-dist/swagger-ui-bundle.js"></script>
23+
24+
<script>
25+
window.onload = function() {
26+
const ui = SwaggerUIBundle({
27+
url: "/openapi/{{documentName}}.json",
28+
dom_id: '#swagger-ui',
29+
deepLinking: true,
30+
presets: [
31+
SwaggerUIBundle.presets.apis,
32+
SwaggerUIStandalonePreset
33+
],
34+
plugins: [
35+
SwaggerUIBundle.plugins.DownloadUrl
36+
],
37+
layout: "StandaloneLayout",
38+
})
39+
window.ui = ui
40+
}
41+
</script>
42+
</body>
43+
</html>
44+
""", "text/html")).ExcludeFromDescription();
45+
}
46+
}

0 commit comments

Comments
 (0)