Skip to content

Commit feea044

Browse files
Add operation and schema transformer interfaces
- Add `IOpenApiOperationTransformer`. - Add `IOpenApiSchemaTransformer`. - Rename `Use*Transfomer()` methods to `Add*Transformer()`. Resolves #56022.
1 parent 28481ab commit feea044

22 files changed

+1055
-114
lines changed

AspNetCore.sln

+2-2
Original file line numberDiff line numberDiff line change
@@ -1788,7 +1788,7 @@ 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}"
1791+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample", "src\OpenApi\sample\Sample.csproj", "{6DEC24A8-A166-432F-8E3B-58FFCDA92F52}"
17921792
EndProject
17931793
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Hybrid", "Hybrid", "{2D64CA23-6E81-488E-A7D3-9BDF87240098}"
17941794
EndProject
@@ -1802,7 +1802,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Cachin
18021802
EndProject
18031803
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "perf", "perf", "{9DC6B242-457B-4767-A84B-C3D23B76C642}"
18041804
EndProject
1805-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.OpenApi.Microbenchmarks", "src\OpenApi\perf\Microbenchmarks\Microsoft.AspNetCore.OpenApi.Microbenchmarks.csproj", "{D53F0EF7-0CDC-49B4-AA2D-229901B0A734}"
1805+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.OpenApi.Microbenchmarks", "src\OpenApi\perf\Microbenchmarks\Microsoft.AspNetCore.OpenApi.Microbenchmarks.csproj", "{D53F0EF7-0CDC-49B4-AA2D-229901B0A734}"
18061806
EndProject
18071807
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "KeyManagementSimulator", "src\DataProtection\samples\KeyManagementSimulator\KeyManagementSimulator.csproj", "{5B5F86CC-3598-463C-9F9B-F78FBB6642F4}"
18081808
EndProject

src/OpenApi/OpenApi.slnf

+6-2
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,19 @@
22
"solution": {
33
"path": "..\\..\\AspNetCore.sln",
44
"projects": [
5+
"src\\Extensions\\Features\\src\\Microsoft.Extensions.Features.csproj",
56
"src\\Hosting\\Abstractions\\src\\Microsoft.AspNetCore.Hosting.Abstractions.csproj",
67
"src\\Hosting\\Hosting\\src\\Microsoft.AspNetCore.Hosting.csproj",
8+
"src\\Http\\Headers\\src\\Microsoft.Net.Http.Headers.csproj",
79
"src\\Http\\Http.Abstractions\\src\\Microsoft.AspNetCore.Http.Abstractions.csproj",
810
"src\\Http\\Http\\src\\Microsoft.AspNetCore.Http.csproj",
11+
"src\\Http\\Http.Features\\src\\Microsoft.AspNetCore.Http.Features.csproj",
912
"src\\Http\\Routing\\src\\Microsoft.AspNetCore.Routing.csproj",
13+
"src\\OpenApi\\perf\\Microbenchmarks\\Microsoft.AspNetCore.OpenApi.Microbenchmarks.csproj",
14+
"src\\OpenApi\\sample\\Sample.csproj",
1015
"src\\OpenApi\\src\\Microsoft.AspNetCore.OpenApi.csproj",
1116
"src\\OpenApi\\test\\Microsoft.AspNetCore.OpenApi.Tests.csproj",
12-
"src\\OpenApi\\sample\\Sample.csproj",
13-
"src\\OpenApi\\perf\\Microbenchmarks\\Microsoft.AspNetCore.OpenApi.Microbenchmarks.csproj"
17+
"src\\Servers\\Connections.Abstractions\\src\\Microsoft.AspNetCore.Connections.Abstractions.csproj"
1418
]
1519
}
1620
}

src/OpenApi/perf/Microbenchmarks/TransformersBenchmark.cs

+67-8
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,27 @@ public class TransformersBenchmark : OpenApiDocumentServiceTestBase
2121
public int TransformerCount { get; set; }
2222

2323
private readonly IEndpointRouteBuilder _builder = CreateBuilder();
24-
private readonly OpenApiOptions _options = new OpenApiOptions();
24+
private readonly OpenApiOptions _options = new();
2525
private OpenApiDocumentService _documentService;
2626

27+
[GlobalSetup(Target = nameof(ActivatedOperationTransformer))]
28+
public void ActivatedOperationTransformer_Setup()
29+
{
30+
_builder.MapGet("/", () => { });
31+
for (var i = 0; i <= TransformerCount; i++)
32+
{
33+
_options.AddOperationTransformer<OperationTransformer>();
34+
}
35+
_documentService = CreateDocumentService(_builder, _options);
36+
}
37+
2738
[GlobalSetup(Target = nameof(OperationTransformerAsDelegate))]
2839
public void OperationTransformerAsDelegate_Setup()
2940
{
3041
_builder.MapGet("/", () => { });
3142
for (var i = 0; i <= TransformerCount; i++)
3243
{
33-
_options.UseOperationTransformer((operation, context, token) =>
44+
_options.AddOperationTransformer((operation, context, token) =>
3445
{
3546
operation.Description = "New Description";
3647
return Task.CompletedTask;
@@ -45,7 +56,7 @@ public void ActivatedDocumentTransformer_Setup()
4556
_builder.MapGet("/", () => { });
4657
for (var i = 0; i <= TransformerCount; i++)
4758
{
48-
_options.UseTransformer<ActivatedTransformer>();
59+
_options.AddDocumentTransformer<DocumentTransformer>();
4960
}
5061
_documentService = CreateDocumentService(_builder, _options);
5162
}
@@ -56,7 +67,7 @@ public void DocumentTransformerAsDelegate_Delegate()
5667
_builder.MapGet("/", () => { });
5768
for (var i = 0; i <= TransformerCount; i++)
5869
{
59-
_options.UseTransformer((document, context, token) =>
70+
_options.AddDocumentTransformer((document, context, token) =>
6071
{
6172
document.Info.Description = "New Description";
6273
return Task.CompletedTask;
@@ -65,13 +76,24 @@ public void DocumentTransformerAsDelegate_Delegate()
6576
_documentService = CreateDocumentService(_builder, _options);
6677
}
6778

68-
[GlobalSetup(Target = nameof(SchemaTransformer))]
79+
[GlobalSetup(Target = nameof(ActivatedSchemaTransformer))]
80+
public void ActivatedSchemaTransformer_Setup()
81+
{
82+
_builder.MapGet("/", () => { });
83+
for (var i = 0; i <= TransformerCount; i++)
84+
{
85+
_options.AddSchemaTransformer<SchemaTransformer>();
86+
}
87+
_documentService = CreateDocumentService(_builder, _options);
88+
}
89+
90+
[GlobalSetup(Target = nameof(SchemaTransformerAsDelegate))]
6991
public void SchemaTransformer_Setup()
7092
{
7193
_builder.MapPost("/", (Todo todo) => todo);
7294
for (var i = 0; i <= TransformerCount; i++)
7395
{
74-
_options.UseSchemaTransformer((schema, context, token) =>
96+
_options.AddSchemaTransformer((schema, context, token) =>
7597
{
7698
if (context.Type == typeof(Todo) && context.ParameterDescription != null)
7799
{
@@ -87,6 +109,12 @@ public void SchemaTransformer_Setup()
87109
_documentService = CreateDocumentService(_builder, _options);
88110
}
89111

112+
[Benchmark]
113+
public async Task ActivatedOperationTransformer()
114+
{
115+
await _documentService.GetOpenApiDocumentAsync();
116+
}
117+
90118
[Benchmark]
91119
public async Task OperationTransformerAsDelegate()
92120
{
@@ -106,17 +134,48 @@ public async Task DocumentTransformerAsDelegate()
106134
}
107135

108136
[Benchmark]
109-
public async Task SchemaTransformer()
137+
public async Task ActivatedSchemaTransformer()
138+
{
139+
await _documentService.GetOpenApiDocumentAsync();
140+
}
141+
142+
[Benchmark]
143+
public async Task SchemaTransformerAsDelegate()
110144
{
111145
await _documentService.GetOpenApiDocumentAsync();
112146
}
113147

114-
private class ActivatedTransformer : IOpenApiDocumentTransformer
148+
private class DocumentTransformer : IOpenApiDocumentTransformer
115149
{
116150
public Task TransformAsync(OpenApiDocument document, OpenApiDocumentTransformerContext context, CancellationToken cancellationToken)
117151
{
118152
document.Info.Description = "Info Description";
119153
return Task.CompletedTask;
120154
}
121155
}
156+
157+
private class OperationTransformer : IOpenApiOperationTransformer
158+
{
159+
public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransformerContext context, CancellationToken cancellationToken)
160+
{
161+
operation.Description = "Operation Description";
162+
return Task.CompletedTask;
163+
}
164+
}
165+
166+
private class SchemaTransformer : IOpenApiSchemaTransformer
167+
{
168+
public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext context, CancellationToken cancellationToken)
169+
{
170+
if (context.Type == typeof(Todo) && context.ParameterDescription != null)
171+
{
172+
schema.Extensions["x-my-extension"] = new OpenApiString(context.ParameterDescription.Name);
173+
}
174+
else
175+
{
176+
schema.Extensions["x-my-extension"] = new OpenApiString("response");
177+
}
178+
return Task.CompletedTask;
179+
}
180+
}
122181
}

src/OpenApi/sample/Program.cs

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
using System.Collections.Frozen;
54
using System.Collections.Immutable;
65
using System.ComponentModel;
76
using Microsoft.AspNetCore.Http.HttpResults;
@@ -19,11 +18,11 @@
1918
builder.Services.AddOpenApi("v1", options =>
2019
{
2120
options.AddHeader("X-Version", "1.0");
22-
options.UseTransformer<BearerSecuritySchemeTransformer>();
21+
options.AddDocumentTransformer<BearerSecuritySchemeTransformer>();
2322
});
2423
builder.Services.AddOpenApi("v2", options => {
25-
options.UseTransformer(new AddContactTransformer());
26-
options.UseTransformer((document, context, token) => {
24+
options.AddDocumentTransformer(new AddContactTransformer());
25+
options.AddDocumentTransformer((document, context, token) => {
2726
document.Info.License = new OpenApiLicense { Name = "MIT" };
2827
return Task.CompletedTask;
2928
});

src/OpenApi/sample/Transformers/OperationTransformers.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,17 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using Microsoft.AspNetCore.OpenApi;
5-
using Microsoft.OpenApi.Models;
65
using Microsoft.OpenApi.Any;
76
using Microsoft.OpenApi.Extensions;
7+
using Microsoft.OpenApi.Models;
88

99
namespace Sample.Transformers;
1010

1111
public static class OperationTransformers
1212
{
1313
public static OpenApiOptions AddHeader(this OpenApiOptions options, string headerName, string defaultValue)
1414
{
15-
return options.UseOperationTransformer((operation, context, cancellationToken) =>
15+
return options.AddOperationTransformer((operation, context, cancellationToken) =>
1616
{
1717
var schema = OpenApiTypeMapper.MapTypeToOpenApiPrimitiveType(typeof(string));
1818
schema.Default = new OpenApiString(defaultValue);

src/OpenApi/src/PublicAPI.Unshipped.txt

+13-5
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,28 @@
11
#nullable enable
22
Microsoft.AspNetCore.Builder.OpenApiEndpointRouteBuilderExtensions
33
Microsoft.AspNetCore.OpenApi.IOpenApiDocumentTransformer.TransformAsync(Microsoft.OpenApi.Models.OpenApiDocument! document, Microsoft.AspNetCore.OpenApi.OpenApiDocumentTransformerContext! context, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task!
4+
Microsoft.AspNetCore.OpenApi.IOpenApiOperationTransformer
5+
Microsoft.AspNetCore.OpenApi.IOpenApiOperationTransformer.TransformAsync(Microsoft.OpenApi.Models.OpenApiOperation! operation, Microsoft.AspNetCore.OpenApi.OpenApiOperationTransformerContext! context, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task!
6+
Microsoft.AspNetCore.OpenApi.IOpenApiSchemaTransformer
7+
Microsoft.AspNetCore.OpenApi.IOpenApiSchemaTransformer.TransformAsync(Microsoft.OpenApi.Models.OpenApiSchema! schema, Microsoft.AspNetCore.OpenApi.OpenApiSchemaTransformerContext! context, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task!
48
Microsoft.AspNetCore.OpenApi.OpenApiOperationTransformerContext.Description.get -> Microsoft.AspNetCore.Mvc.ApiExplorer.ApiDescription!
59
Microsoft.AspNetCore.OpenApi.OpenApiOperationTransformerContext.Description.init -> void
610
Microsoft.AspNetCore.OpenApi.OpenApiOptions
11+
Microsoft.AspNetCore.OpenApi.OpenApiOptions.AddOperationTransformer(Microsoft.AspNetCore.OpenApi.IOpenApiOperationTransformer! transformer) -> Microsoft.AspNetCore.OpenApi.OpenApiOptions!
12+
Microsoft.AspNetCore.OpenApi.OpenApiOptions.AddOperationTransformer<TTransformerType>() -> Microsoft.AspNetCore.OpenApi.OpenApiOptions!
13+
Microsoft.AspNetCore.OpenApi.OpenApiOptions.AddSchemaTransformer(Microsoft.AspNetCore.OpenApi.IOpenApiSchemaTransformer! transformer) -> Microsoft.AspNetCore.OpenApi.OpenApiOptions!
14+
Microsoft.AspNetCore.OpenApi.OpenApiOptions.AddSchemaTransformer<TTransformerType>() -> Microsoft.AspNetCore.OpenApi.OpenApiOptions!
715
Microsoft.AspNetCore.OpenApi.OpenApiOptions.DocumentName.get -> string!
816
Microsoft.AspNetCore.OpenApi.OpenApiOptions.OpenApiOptions() -> void
917
Microsoft.AspNetCore.OpenApi.OpenApiOptions.OpenApiVersion.get -> Microsoft.OpenApi.OpenApiSpecVersion
1018
Microsoft.AspNetCore.OpenApi.OpenApiOptions.OpenApiVersion.set -> void
1119
Microsoft.AspNetCore.OpenApi.OpenApiOptions.ShouldInclude.get -> System.Func<Microsoft.AspNetCore.Mvc.ApiExplorer.ApiDescription!, bool>!
1220
Microsoft.AspNetCore.OpenApi.OpenApiOptions.ShouldInclude.set -> void
13-
Microsoft.AspNetCore.OpenApi.OpenApiOptions.UseOperationTransformer(System.Func<Microsoft.OpenApi.Models.OpenApiOperation!, Microsoft.AspNetCore.OpenApi.OpenApiOperationTransformerContext!, System.Threading.CancellationToken, System.Threading.Tasks.Task!>! transformer) -> Microsoft.AspNetCore.OpenApi.OpenApiOptions!
14-
Microsoft.AspNetCore.OpenApi.OpenApiOptions.UseSchemaTransformer(System.Func<Microsoft.OpenApi.Models.OpenApiSchema!, Microsoft.AspNetCore.OpenApi.OpenApiSchemaTransformerContext!, System.Threading.CancellationToken, System.Threading.Tasks.Task!>! transformer) -> Microsoft.AspNetCore.OpenApi.OpenApiOptions!
15-
Microsoft.AspNetCore.OpenApi.OpenApiOptions.UseTransformer(Microsoft.AspNetCore.OpenApi.IOpenApiDocumentTransformer! transformer) -> Microsoft.AspNetCore.OpenApi.OpenApiOptions!
16-
Microsoft.AspNetCore.OpenApi.OpenApiOptions.UseTransformer(System.Func<Microsoft.OpenApi.Models.OpenApiDocument!, Microsoft.AspNetCore.OpenApi.OpenApiDocumentTransformerContext!, System.Threading.CancellationToken, System.Threading.Tasks.Task!>! transformer) -> Microsoft.AspNetCore.OpenApi.OpenApiOptions!
17-
Microsoft.AspNetCore.OpenApi.OpenApiOptions.UseTransformer<TTransformerType>() -> Microsoft.AspNetCore.OpenApi.OpenApiOptions!
21+
Microsoft.AspNetCore.OpenApi.OpenApiOptions.AddOperationTransformer(System.Func<Microsoft.OpenApi.Models.OpenApiOperation!, Microsoft.AspNetCore.OpenApi.OpenApiOperationTransformerContext!, System.Threading.CancellationToken, System.Threading.Tasks.Task!>! transformer) -> Microsoft.AspNetCore.OpenApi.OpenApiOptions!
22+
Microsoft.AspNetCore.OpenApi.OpenApiOptions.AddSchemaTransformer(System.Func<Microsoft.OpenApi.Models.OpenApiSchema!, Microsoft.AspNetCore.OpenApi.OpenApiSchemaTransformerContext!, System.Threading.CancellationToken, System.Threading.Tasks.Task!>! transformer) -> Microsoft.AspNetCore.OpenApi.OpenApiOptions!
23+
Microsoft.AspNetCore.OpenApi.OpenApiOptions.AddDocumentTransformer(Microsoft.AspNetCore.OpenApi.IOpenApiDocumentTransformer! transformer) -> Microsoft.AspNetCore.OpenApi.OpenApiOptions!
24+
Microsoft.AspNetCore.OpenApi.OpenApiOptions.AddDocumentTransformer(System.Func<Microsoft.OpenApi.Models.OpenApiDocument!, Microsoft.AspNetCore.OpenApi.OpenApiDocumentTransformerContext!, System.Threading.CancellationToken, System.Threading.Tasks.Task!>! transformer) -> Microsoft.AspNetCore.OpenApi.OpenApiOptions!
25+
Microsoft.AspNetCore.OpenApi.OpenApiOptions.AddDocumentTransformer<TTransformerType>() -> Microsoft.AspNetCore.OpenApi.OpenApiOptions!
1826
Microsoft.AspNetCore.OpenApi.OpenApiSchemaTransformerContext
1927
Microsoft.AspNetCore.OpenApi.OpenApiSchemaTransformerContext.ApplicationServices.get -> System.IServiceProvider!
2028
Microsoft.AspNetCore.OpenApi.OpenApiSchemaTransformerContext.ApplicationServices.init -> void

0 commit comments

Comments
 (0)