Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#2765 Allow Filter instance reuse #2839

Merged
4 changes: 3 additions & 1 deletion Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="6.0.28" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Versioning" Version="5.1.0" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer" Version="5.1.0" />
<PackageVersion Include="Microsoft.AspNetCore.OpenApi" Version="8.0.4" />
<PackageVersion Include="Microsoft.AspNetCore.Razor.Design" Version="2.1.2" />
<PackageVersion Include="Microsoft.AspNetCore.Routing" Version="2.1.0" />
<PackageVersion Include="Microsoft.AspNetCore.StaticFiles" Version="2.1.0" />
Expand All @@ -22,11 +23,12 @@
<PackageVersion Include="Microsoft.OpenApi.Readers" Version="1.6.14" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
<PackageVersion Include="NSubstitute" Version="5.1.0" />
<PackageVersion Include="Nswag.MSbuild" Version="13.17.0" />
<PackageVersion Include="ReportGenerator" Version="5.2.2" />
<PackageVersion Include="System.Text.Json" Version="4.6.0" />
<PackageVersion Include="xunit" Version="2.7.0" />
<PackageVersion Include="xunit.core" Version="2.7.0" />
<PackageVersion Include="xunit.runner.visualstudio" Version="2.5.7" />
</ItemGroup>
</Project>
</Project>
remcolam marked this conversation as resolved.
Show resolved Hide resolved
1 change: 0 additions & 1 deletion Swashbuckle.AspNetCore.sln
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31717.71
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public void Configure(SchemaGeneratorOptions options)

// Create and add any filters that were specified through the FilterDescriptor lists
_swaggerGenOptions.SchemaFilterDescriptors.ForEach(
filterDescriptor => options.SchemaFilters.Add(CreateFilter<ISchemaFilter>(filterDescriptor)));
filterDescriptor => options.SchemaFilters.Add(GetOrCreateFilter<ISchemaFilter>(filterDescriptor)));
}

private void DeepCopy(SchemaGeneratorOptions source, SchemaGeneratorOptions target)
Expand All @@ -44,10 +44,10 @@ private void DeepCopy(SchemaGeneratorOptions source, SchemaGeneratorOptions targ
target.SchemaFilters = new List<ISchemaFilter>(source.SchemaFilters);
}

private TFilter CreateFilter<TFilter>(FilterDescriptor filterDescriptor)
private TFilter GetOrCreateFilter<TFilter>(FilterDescriptor filterDescriptor)
{
return (TFilter)ActivatorUtilities
.CreateInstance(_serviceProvider, filterDescriptor.Type, filterDescriptor.Arguments);
return (TFilter)(filterDescriptor.FilterInstance
?? ActivatorUtilities .CreateInstance(_serviceProvider, filterDescriptor.Type, filterDescriptor.Arguments));
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,16 @@ public void Configure(SwaggerGeneratorOptions options)
// Create and add any filters that were specified through the FilterDescriptor lists ...

_swaggerGenOptions.ParameterFilterDescriptors.ForEach(
filterDescriptor => options.ParameterFilters.Add(CreateFilter<IParameterFilter>(filterDescriptor)));
filterDescriptor => options.ParameterFilters.Add(GetOrCreateFilter<IParameterFilter>(filterDescriptor)));

_swaggerGenOptions.RequestBodyFilterDescriptors.ForEach(
filterDescriptor => options.RequestBodyFilters.Add(CreateFilter<IRequestBodyFilter>(filterDescriptor)));
filterDescriptor => options.RequestBodyFilters.Add(GetOrCreateFilter<IRequestBodyFilter>(filterDescriptor)));

_swaggerGenOptions.OperationFilterDescriptors.ForEach(
filterDescriptor => options.OperationFilters.Add(CreateFilter<IOperationFilter>(filterDescriptor)));
filterDescriptor => options.OperationFilters.Add(GetOrCreateFilter<IOperationFilter>(filterDescriptor)));

_swaggerGenOptions.DocumentFilterDescriptors.ForEach(
filterDescriptor => options.DocumentFilters.Add(CreateFilter<IDocumentFilter>(filterDescriptor)));
filterDescriptor => options.DocumentFilters.Add(GetOrCreateFilter<IDocumentFilter>(filterDescriptor)));

if (!options.SwaggerDocs.Any())
{
Expand Down Expand Up @@ -74,10 +74,10 @@ public void DeepCopy(SwaggerGeneratorOptions source, SwaggerGeneratorOptions tar
target.SecuritySchemesSelector = source.SecuritySchemesSelector;
}

private TFilter CreateFilter<TFilter>(FilterDescriptor filterDescriptor)
private TFilter GetOrCreateFilter<TFilter>(FilterDescriptor filterDescriptor)
{
return (TFilter)ActivatorUtilities
.CreateInstance(_serviceProvider, filterDescriptor.Type, filterDescriptor.Arguments);
return (TFilter)(filterDescriptor.FilterInstance
?? ActivatorUtilities.CreateInstance(_serviceProvider, filterDescriptor.Type, filterDescriptor.Arguments));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,7 @@ public class FilterDescriptor
public Type Type { get; set; }

public object[] Arguments { get; set; }

public object FilterInstance { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -328,93 +328,193 @@ public static void SupportNonNullableReferenceTypes(this SwaggerGenOptions swagg
/// <summary>
/// Extend the Swagger Generator with "filters" that can modify Schemas after they're initially generated
/// </summary>
/// <typeparam name="TFilter">A type that derives from ISchemaFilter</typeparam>
/// <typeparam name="TFilter">A type that derives from <see cref="ISchemaFilter"/></typeparam>
/// <param name="swaggerGenOptions"></param>
/// <param name="arguments">Optionally inject parameters through filter constructors</param>
public static void SchemaFilter<TFilter>(
this SwaggerGenOptions swaggerGenOptions,
params object[] arguments)
where TFilter : ISchemaFilter
{
swaggerGenOptions = swaggerGenOptions ?? throw new ArgumentNullException(nameof(swaggerGenOptions));
martincostello marked this conversation as resolved.
Show resolved Hide resolved
swaggerGenOptions.SchemaFilterDescriptors.Add(new FilterDescriptor
{
Type = typeof(TFilter),
Arguments = arguments
});
}

/// <summary>
/// Extend the Swagger Generator with "filters" that can modify Schemas after they're initially generated
/// </summary>
/// <typeparam name="TFilter">A type that derives from <see cref="ISchemaFilter"/></typeparam>
/// <param name="swaggerGenOptions"></param>
/// <param name="filterInstance">An instance of the filter, to enable reuse.</param>
martincostello marked this conversation as resolved.
Show resolved Hide resolved
public static void AddSchemaFilterInstance<TFilter>(
this SwaggerGenOptions swaggerGenOptions,
TFilter filterInstance)
where TFilter : ISchemaFilter
{
swaggerGenOptions = swaggerGenOptions ?? throw new ArgumentNullException(nameof(swaggerGenOptions));
filterInstance = filterInstance ?? throw new ArgumentNullException(nameof(filterInstance));
swaggerGenOptions.SchemaFilterDescriptors.Add(new FilterDescriptor
{
FilterInstance = filterInstance
});
}

/// <summary>
/// Extend the Swagger Generator with "filters" that can modify Parameters after they're initially generated
/// </summary>
/// <typeparam name="TFilter">A type that derives from IParameterFilter</typeparam>
/// <typeparam name="TFilter">A type that derives from <see cref="IParameterFilter"/></typeparam>
/// <param name="swaggerGenOptions"></param>
/// <param name="arguments">Optionally inject parameters through filter constructors</param>
public static void ParameterFilter<TFilter>(
this SwaggerGenOptions swaggerGenOptions,
params object[] arguments)
where TFilter : IParameterFilter
{
swaggerGenOptions = swaggerGenOptions ?? throw new ArgumentNullException(nameof(swaggerGenOptions));
swaggerGenOptions.ParameterFilterDescriptors.Add(new FilterDescriptor
{
Type = typeof(TFilter),
Arguments = arguments
});
}

/// <summary>
/// Extend the Swagger Generator with "filters" that can modify Parameters after they're initially generated
/// </summary>
/// <typeparam name="TFilter">A type that derives from <see cref="IParameterFilter"/></typeparam>
/// <param name="swaggerGenOptions"></param>
/// <param name="filterInstance">An instance of the filter, to enable reuse.</param>
public static void AddParameterFilterInstance<TFilter>(
this SwaggerGenOptions swaggerGenOptions,
TFilter filterInstance)
where TFilter : IParameterFilter
{
swaggerGenOptions = swaggerGenOptions ?? throw new ArgumentNullException(nameof(swaggerGenOptions));
filterInstance = filterInstance ?? throw new ArgumentNullException(nameof(filterInstance));
swaggerGenOptions.ParameterFilterDescriptors.Add(new FilterDescriptor
{
FilterInstance = filterInstance
});
}

/// <summary>
/// Extend the Swagger Generator with "filters" that can modify RequestBodys after they're initially generated
/// </summary>
/// <typeparam name="TFilter">A type that derives from IRequestBodyFilter</typeparam>
/// <typeparam name="TFilter">A type that derives from <see cref="IRequestBodyFilter"/></typeparam>
/// <param name="swaggerGenOptions"></param>
/// <param name="arguments">Optionally inject parameters through filter constructors</param>
public static void RequestBodyFilter<TFilter>(
this SwaggerGenOptions swaggerGenOptions,
params object[] arguments)
where TFilter : IRequestBodyFilter
{
swaggerGenOptions = swaggerGenOptions ?? throw new ArgumentNullException(nameof(swaggerGenOptions));
swaggerGenOptions.RequestBodyFilterDescriptors.Add(new FilterDescriptor
{
Type = typeof(TFilter),
Arguments = arguments
});
}

/// <summary>
/// Extend the Swagger Generator with "filters" that can modify RequestBodys after they're initially generated
/// </summary>
/// <typeparam name="TFilter">A type that derives from <see cref="IRequestBodyFilter"/></typeparam>
/// <param name="swaggerGenOptions"></param>
/// <param name="filterInstance">An instance of the filter, to enable reuse.</param>
public static void AddRequestBodyFilterInstance<TFilter>(
this SwaggerGenOptions swaggerGenOptions,
TFilter filterInstance)
where TFilter : IRequestBodyFilter
{
swaggerGenOptions = swaggerGenOptions ?? throw new ArgumentNullException(nameof(swaggerGenOptions));
filterInstance = filterInstance ?? throw new ArgumentNullException(nameof(filterInstance));
swaggerGenOptions.RequestBodyFilterDescriptors.Add(new FilterDescriptor
{
FilterInstance = filterInstance
});
}

/// <summary>
/// Extend the Swagger Generator with "filters" that can modify Operations after they're initially generated
/// </summary>
/// <typeparam name="TFilter">A type that derives from IOperationFilter</typeparam>
/// <typeparam name="TFilter">A type that derives from <see cref="IOperationFilter"/></typeparam>
/// <param name="swaggerGenOptions"></param>
/// <param name="arguments">Optionally inject parameters through filter constructors</param>
public static void OperationFilter<TFilter>(
this SwaggerGenOptions swaggerGenOptions,
params object[] arguments)
where TFilter : IOperationFilter
{
swaggerGenOptions = swaggerGenOptions ?? throw new ArgumentNullException(nameof(swaggerGenOptions));
swaggerGenOptions.OperationFilterDescriptors.Add(new FilterDescriptor
{
Type = typeof(TFilter),
Arguments = arguments
});
}

/// <summary>
/// Extend the Swagger Generator with "filters" that can modify Operations after they're initially generated
/// </summary>
/// <typeparam name="TFilter">A type that derives from <see cref="IOperationFilter"/></typeparam>
/// <param name="swaggerGenOptions"></param>
/// <param name="filterInstance">An instance of the filter, to enable reuse.</param>
public static void AddOperationFilterInstance<TFilter>(
this SwaggerGenOptions swaggerGenOptions,
TFilter filterInstance)
martincostello marked this conversation as resolved.
Show resolved Hide resolved
where TFilter : IOperationFilter
{
swaggerGenOptions = swaggerGenOptions ?? throw new ArgumentNullException(nameof(swaggerGenOptions));
filterInstance = filterInstance ?? throw new ArgumentNullException(nameof(filterInstance));
swaggerGenOptions.OperationFilterDescriptors.Add(new FilterDescriptor
{
FilterInstance = filterInstance
});
}

/// <summary>
/// Extend the Swagger Generator with "filters" that can modify SwaggerDocuments after they're initially generated
/// </summary>
/// <typeparam name="TFilter">A type that derives from IDocumentFilter</typeparam>
/// <typeparam name="TFilter">A type that derives from <see cref="IDocumentFilter"/></typeparam>
/// <param name="swaggerGenOptions"></param>
/// <param name="arguments">Optionally inject parameters through filter constructors</param>
public static void DocumentFilter<TFilter>(
this SwaggerGenOptions swaggerGenOptions,
params object[] arguments)
where TFilter : IDocumentFilter
{
swaggerGenOptions = swaggerGenOptions ?? throw new ArgumentNullException(nameof(swaggerGenOptions));
swaggerGenOptions.DocumentFilterDescriptors.Add(new FilterDescriptor
{
Type = typeof(TFilter),
Arguments = arguments
});
}

/// <summary>
/// Extend the Swagger Generator with "filters" that can modify SwaggerDocuments after they're initially generated
/// </summary>
/// <typeparam name="TFilter">A type that derives from <see cref="IDocumentFilter"/></typeparam>
/// <param name="swaggerGenOptions"></param>
/// <param name="filterInstance">An instance of the filter, to enable reuse.</param>
public static void AddDocumentFilterInstance<TFilter>(
this SwaggerGenOptions swaggerGenOptions,
TFilter filterInstance)
where TFilter : IDocumentFilter
{
swaggerGenOptions = swaggerGenOptions ?? throw new ArgumentNullException(nameof(swaggerGenOptions));
filterInstance = filterInstance ?? throw new ArgumentNullException(nameof(filterInstance));
swaggerGenOptions.DocumentFilterDescriptors.Add(new FilterDescriptor
martincostello marked this conversation as resolved.
Show resolved Hide resolved
{
FilterInstance = filterInstance
});
}

/// <summary>
/// Inject human-friendly descriptions for Operations, Parameters and Schemas based on XML Comment files
/// </summary>
Expand Down
martincostello marked this conversation as resolved.
Show resolved Hide resolved
martincostello marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("Swashbuckle.AspNetCore.IntegrationTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100795482b2b019ad690196ad34ca1d13abb1c3ed0e24fdaea1ca82ca11462a2d883e149864773aada8b40f41c1b1ea7824f23fb569cfc20e9fd03763896a2905fa23e1a62a940d40d14e3107e8cf90f6d705848e39fe25121a7f8c1cd5dcd6701aea982d13c4a42b3a0766dfc27fc02fa40194ed0a7841a8768d33f7aaabde87a7")]
[assembly: InternalsVisibleTo("Swashbuckle.AspNetCore.SwaggerGen.Test, PublicKey=002400000480000094000000060200000024000052534131000400000100010055d6060294c6e19033fac1eb2d1a17f29d7ddd3a09b31f460302a2a7a9955cd1afaa6bd579125ee2ccce330e4dc84f238ed10d372a5c7db774ab2bf2dd77bf9fc0abb3fc84a32f0aacf4cf166c5af981e80419f83c0e4bb9868c3b9dd3a3c7e0a022b7da66ebfed28c073e99e3847491a84678ac55bcff5a2f573838518f4fef")]
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
using System.Reflection;
using System.Linq;
using System.Reflection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting.Internal;
using Microsoft.Extensions.Options;
using NSubstitute;
using Xunit;

namespace Swashbuckle.AspNetCore.SwaggerGen.Test;
Expand All @@ -15,4 +21,27 @@ public static void DeepCopy_Copies_All_Properties()
// to SwaggerGeneratorOptions and ConfigureSwaggerGeneratorOptions.DeepCopy() needs to be updated
Assert.Equal(18, publicProperties.Length);
}

[Fact]
public static void AddingDocumentFilterInstance_WhenConfiguringOption_SameInstanceIsAdded()
{
var webhostingEnvironment = Substitute.For<IWebHostEnvironment>();
webhostingEnvironment.ApplicationName.Returns("Swashbuckle.AspNetCore.SwaggerGen.Test");
remcolam marked this conversation as resolved.
Show resolved Hide resolved

var testDocumentFilter = new TestDocumentFilter();

var options = new SwaggerGenOptions();
options.AddDocumentFilterInstance(testDocumentFilter);

var configureSwaggerGeneratorOptions = new ConfigureSwaggerGeneratorOptions(
Options.Create(options),
null,
webhostingEnvironment);
var swaggerGeneratorOptions = new SwaggerGeneratorOptions();

configureSwaggerGeneratorOptions.Configure(swaggerGeneratorOptions);

Assert.Single(swaggerGeneratorOptions.DocumentFilters);
Assert.Same(testDocumentFilter, swaggerGeneratorOptions.DocumentFilters.First());
martincostello marked this conversation as resolved.
Show resolved Hide resolved
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>1701;1702;1591</NoWarn>
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
<SignAssembly>True</SignAssembly>
<AssemblyOriginatorKeyFile>Swashbuckle.AspNetCore.SwaggerGen.Test.snk</AssemblyOriginatorKeyFile>
martincostello marked this conversation as resolved.
Show resolved Hide resolved
</PropertyGroup>

<ItemGroup>
Expand All @@ -14,6 +16,7 @@

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="NSubstitute" />
<PackageReference Include="xunit" />
<PackageReference Include="xunit.runner.visualstudio" />
</ItemGroup>
Expand Down
martincostello marked this conversation as resolved.
Show resolved Hide resolved
martincostello marked this conversation as resolved.
Show resolved Hide resolved
Binary file not shown.