Skip to content

Commit b7ad201

Browse files
committed
Handle disposable transformers
1 parent 73fc68a commit b7ad201

File tree

3 files changed

+152
-4
lines changed

3 files changed

+152
-4
lines changed

src/OpenApi/src/Services/OpenApiDocumentService.cs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,25 @@ private async Task ApplyTransformersAsync(OpenApiDocument document, Cancellation
7070
for (var i = 0; i < _options.DocumentTransformers.Count; i++)
7171
{
7272
var transformer = _options.DocumentTransformers[i];
73-
await transformer.TransformAsync(document, documentTransformerContext, cancellationToken);
73+
try
74+
{
75+
await transformer.TransformAsync(document, documentTransformerContext, cancellationToken);
76+
}
77+
finally
78+
{
79+
if (transformer is IDisposable disposable)
80+
{
81+
disposable.Dispose();
82+
}
83+
if (transformer is IAsyncDisposable asyncDisposable)
84+
{
85+
await asyncDisposable.DisposeAsync();
86+
}
87+
if (transformer is TypeBasedOpenApiDocumentTransformer typedTransformer)
88+
{
89+
await typedTransformer.DisposeAsync();
90+
}
91+
}
7492
}
7593
}
7694

src/OpenApi/src/Transformers/TypeBasedOpenApiDocumentTransformer.cs

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,25 @@ namespace Microsoft.AspNetCore.OpenApi;
1010
internal sealed class TypeBasedOpenApiDocumentTransformer(Type transformerType) : IOpenApiDocumentTransformer
1111
{
1212
private readonly ObjectFactory _transformerFactory = ActivatorUtilities.CreateFactory(transformerType, []);
13+
private IOpenApiDocumentTransformer? _transformer;
14+
15+
public ValueTask DisposeAsync()
16+
{
17+
if (_transformer is IAsyncDisposable asyncDisposable)
18+
{
19+
return asyncDisposable.DisposeAsync();
20+
}
21+
if (_transformer is IDisposable disposable)
22+
{
23+
disposable.Dispose();
24+
}
25+
return ValueTask.CompletedTask;
26+
}
27+
1328
public Task TransformAsync(OpenApiDocument document, OpenApiDocumentTransformerContext context, CancellationToken cancellationToken)
1429
{
15-
var transformer = _transformerFactory.Invoke(context.ApplicationServices, []) as IOpenApiDocumentTransformer;
16-
Debug.Assert(transformer != null, $"The type {transformerType} does not implement {nameof(IOpenApiDocumentTransformer)}.");
17-
return transformer.TransformAsync(document, context, cancellationToken);
30+
_transformer = _transformerFactory.Invoke(context.ApplicationServices, []) as IOpenApiDocumentTransformer;
31+
Debug.Assert(_transformer != null, $"The type {transformerType} does not implement {nameof(IOpenApiDocumentTransformer)}.");
32+
return _transformer.TransformAsync(document, context, cancellationToken);
1833
}
1934
}

src/OpenApi/test/Transformers/DocumentTransformerTests.cs

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,84 @@ await VerifyOpenApiDocument(builder, options, document =>
122122
});
123123
}
124124

125+
[Fact]
126+
public async Task DocumentTransformer_SupportsDisposableInstanceTransformer()
127+
{
128+
var builder = CreateBuilder();
129+
130+
builder.MapGet("/todo", () => { });
131+
builder.MapGet("/user", () => { });
132+
133+
var options = new OpenApiOptions();
134+
var transformer = new DisposableTransformer();
135+
options.UseTransformer(transformer);
136+
137+
Assert.False(transformer.Disposed);
138+
await VerifyOpenApiDocument(builder, options, document =>
139+
{
140+
Assert.Equal("Info Description", document.Info.Description);
141+
});
142+
Assert.True(transformer.Disposed);
143+
}
144+
145+
[Fact]
146+
public async Task DocumentTransformer_SupportsAsyncDisposableInstanceTransformer()
147+
{
148+
var builder = CreateBuilder();
149+
150+
builder.MapGet("/todo", () => { });
151+
builder.MapGet("/user", () => { });
152+
153+
var options = new OpenApiOptions();
154+
var transformer = new AsyncDisposableTransformer();
155+
options.UseTransformer(transformer);
156+
157+
Assert.False(transformer.Disposed);
158+
await VerifyOpenApiDocument(builder, options, document =>
159+
{
160+
Assert.Equal("Info Description", document.Info.Description);
161+
});
162+
Assert.True(transformer.Disposed);
163+
}
164+
165+
[Fact]
166+
public async Task DocumentTransformer_SupportsDisposableActivatedTransformer()
167+
{
168+
var builder = CreateBuilder();
169+
170+
builder.MapGet("/todo", () => { });
171+
builder.MapGet("/user", () => { });
172+
173+
var options = new OpenApiOptions();
174+
options.UseTransformer<DisposableTransformer>();
175+
176+
DisposableTransformer.DisposeCount = 0;
177+
await VerifyOpenApiDocument(builder, options, document =>
178+
{
179+
Assert.Equal("Info Description", document.Info.Description);
180+
});
181+
Assert.Equal(1, DisposableTransformer.DisposeCount);
182+
}
183+
184+
[Fact]
185+
public async Task DocumentTransformer_SupportsAsyncDisposableActivatedTransformer()
186+
{
187+
var builder = CreateBuilder();
188+
189+
builder.MapGet("/todo", () => { });
190+
builder.MapGet("/user", () => { });
191+
192+
var options = new OpenApiOptions();
193+
options.UseTransformer<AsyncDisposableTransformer>();
194+
195+
AsyncDisposableTransformer.DisposeCount = 0;
196+
await VerifyOpenApiDocument(builder, options, document =>
197+
{
198+
Assert.Equal("Info Description", document.Info.Description);
199+
});
200+
Assert.Equal(1, AsyncDisposableTransformer.DisposeCount);
201+
}
202+
125203
private class ActivatedTransformer : IOpenApiDocumentTransformer
126204
{
127205
public Task TransformAsync(OpenApiDocument document, OpenApiDocumentTransformerContext context, CancellationToken cancellationToken)
@@ -131,6 +209,43 @@ public Task TransformAsync(OpenApiDocument document, OpenApiDocumentTransformerC
131209
}
132210
}
133211

212+
private class DisposableTransformer : IOpenApiDocumentTransformer, IDisposable
213+
{
214+
internal bool Disposed = false;
215+
internal static int DisposeCount = 0;
216+
217+
public void Dispose()
218+
{
219+
Disposed = true;
220+
DisposeCount += 1;
221+
}
222+
223+
public Task TransformAsync(OpenApiDocument document, OpenApiDocumentTransformerContext context, CancellationToken cancellationToken)
224+
{
225+
document.Info.Description = "Info Description";
226+
return Task.CompletedTask;
227+
}
228+
}
229+
230+
private class AsyncDisposableTransformer : IOpenApiDocumentTransformer, IAsyncDisposable
231+
{
232+
internal bool Disposed = false;
233+
internal static int DisposeCount = 0;
234+
235+
public ValueTask DisposeAsync()
236+
{
237+
Disposed = true;
238+
DisposeCount += 1;
239+
return ValueTask.CompletedTask;
240+
}
241+
242+
public Task TransformAsync(OpenApiDocument document, OpenApiDocumentTransformerContext context, CancellationToken cancellationToken)
243+
{
244+
document.Info.Description = "Info Description";
245+
return Task.CompletedTask;
246+
}
247+
}
248+
134249
private class ActivatedTransformerWithDependency(Dependency dependency) : IOpenApiDocumentTransformer
135250
{
136251
public Task TransformAsync(OpenApiDocument document, OpenApiDocumentTransformerContext context, CancellationToken cancellationToken)

0 commit comments

Comments
 (0)