Skip to content

Commit

Permalink
Allow filter instance reuse (#2839)
Browse files Browse the repository at this point in the history
- Add methods to support filter instance re-use.
- Fix CI on macOS 14.

Resolves #2765.
  • Loading branch information
remcolam committed Apr 26, 2024
1 parent 3416927 commit 0166c69
Show file tree
Hide file tree
Showing 16 changed files with 383 additions and 27 deletions.
7 changes: 7 additions & 0 deletions .github/workflows/build.yml
Expand Up @@ -41,6 +41,13 @@ jobs:
- name: Checkout code
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4

- name: Setup .NET SDKs
uses: actions/setup-dotnet@4d6c8fcf3c8f7a60068d26b594648e99df24cee3 # v4.0.0
with:
dotnet-version: |
6.0.x
7.0.x
- name: Setup .NET SDK
uses: actions/setup-dotnet@4d6c8fcf3c8f7a60068d26b594648e99df24cee3 # v4.0.0
id: setup-dotnet
Expand Down
1 change: 1 addition & 0 deletions Directory.Packages.props
Expand Up @@ -22,6 +22,7 @@
<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" />
Expand Down
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));
}
}
}
}
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));
}
}
}
Expand Up @@ -30,5 +30,7 @@ public class FilterDescriptor
public Type Type { get; set; }

public object[] Arguments { get; set; }

public object FilterInstance { get; set; }
}
}
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
{
if (swaggerGenOptions == null) throw new ArgumentNullException(nameof(swaggerGenOptions));
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">The filter instance to use.</param>
public static void AddSchemaFilterInstance<TFilter>(
this SwaggerGenOptions swaggerGenOptions,
TFilter filterInstance)
where TFilter : ISchemaFilter
{
if (swaggerGenOptions == null) throw new ArgumentNullException(nameof(swaggerGenOptions));
if (filterInstance == null) 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
{
if (swaggerGenOptions == null) 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">The filter instance to use.</param>
public static void AddParameterFilterInstance<TFilter>(
this SwaggerGenOptions swaggerGenOptions,
TFilter filterInstance)
where TFilter : IParameterFilter
{
if (swaggerGenOptions == null) throw new ArgumentNullException(nameof(swaggerGenOptions));
if (filterInstance == null) 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
{
if (swaggerGenOptions == null) 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">The filter instance to use.</param>
public static void AddRequestBodyFilterInstance<TFilter>(
this SwaggerGenOptions swaggerGenOptions,
TFilter filterInstance)
where TFilter : IRequestBodyFilter
{
if (swaggerGenOptions == null) throw new ArgumentNullException(nameof(swaggerGenOptions));
if (filterInstance == null) 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
{
if (swaggerGenOptions == null) 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">The filter instance to use.</param>
public static void AddOperationFilterInstance<TFilter>(
this SwaggerGenOptions swaggerGenOptions,
TFilter filterInstance)
where TFilter : IOperationFilter
{
if (swaggerGenOptions == null) throw new ArgumentNullException(nameof(swaggerGenOptions));
if (filterInstance == null) 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
{
if (swaggerGenOptions == null) 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">The filter instance to use.</param>
public static void AddDocumentFilterInstance<TFilter>(
this SwaggerGenOptions swaggerGenOptions,
TFilter filterInstance)
where TFilter : IDocumentFilter
{
if (swaggerGenOptions == null) throw new ArgumentNullException(nameof(swaggerGenOptions));
if (filterInstance == null) throw new ArgumentNullException(nameof(filterInstance));
swaggerGenOptions.DocumentFilterDescriptors.Add(new FilterDescriptor
{
FilterInstance = filterInstance
});
}

/// <summary>
/// Inject human-friendly descriptions for Operations, Parameters and Schemas based on XML Comment files
/// </summary>
Expand Down

This file was deleted.

Expand Up @@ -10,12 +10,17 @@
<TargetFrameworks>netstandard2.0;netcoreapp3.0;net5.0;net6.0;net7.0</TargetFrameworks>
</PropertyGroup>

<ItemGroup>
<InternalsVisibleTo Include="Swashbuckle.AspNetCore.IntegrationTests" Key="00240000048000009400000006020000002400005253413100040000010001000b80d002f0437a94c4f6d77dfc29ce54df1040f66374cb7122ec5ddab3907a6dfe609a07ffd49b9aa6731fe3f8de4da7979ddea73e949c86effc4bec3f469cc36e788cf58c42c4be755efd1afd996ccab9f0db3455b834219f6614d00fe0410cc307c4eced320fd65fcc501f135dc537d7d09dcc1d2a572d24f8459a86469ec5" />
<InternalsVisibleTo Include="Swashbuckle.AspNetCore.SwaggerGen.Test" Key="00240000048000009400000006020000002400005253413100040000010001000b80d002f0437a94c4f6d77dfc29ce54df1040f66374cb7122ec5ddab3907a6dfe609a07ffd49b9aa6731fe3f8de4da7979ddea73e949c86effc4bec3f469cc36e788cf58c42c4be755efd1afd996ccab9f0db3455b834219f6614d00fe0410cc307c4eced320fd65fcc501f135dc537d7d09dcc1d2a572d24f8459a86469ec5" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Swashbuckle.AspNetCore.Swagger\Swashbuckle.AspNetCore.Swagger.csproj" />
</ItemGroup>

<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
<PackageReference Include="Microsoft.AspNetCore.Mvc.ApiExplorer"/>
<PackageReference Include="Microsoft.AspNetCore.Mvc.ApiExplorer" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.DataAnnotations" />
<PackageReference Include="System.Text.Json" />
</ItemGroup>
Expand Down
Expand Up @@ -5,7 +5,7 @@
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\Swashbuckle.AspNetCore.ApiTesting\Swashbuckle.AspNetCore.ApiTesting.csproj" />
<ProjectReference Include="$(MSBuildThisFileDirectory)..\..\src\Swashbuckle.AspNetCore.ApiTesting\Swashbuckle.AspNetCore.ApiTesting.csproj" />
</ItemGroup>

<ItemGroup>
Expand Down
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<AssemblyOriginatorKeyFile>Swashbuckle.AspNetCore.IntegrationTests.snk</AssemblyOriginatorKeyFile>
<AssemblyOriginatorKeyFile>$(MSBuildThisFileDirectory)..\..\src\Swashbuckle.AspNetCore.Swagger\Swashbuckle.AspNetCore.Swagger.snk</AssemblyOriginatorKeyFile>
<NoWarn>$(NoWarn);8002</NoWarn>
<SignAssembly>true</SignAssembly>
<TargetFramework>net6.0</TargetFramework>
Expand Down
Binary file not shown.
Expand Up @@ -619,9 +619,9 @@ public void GenerateSchema_Errors_IfTypesHaveConflictingSchemaIds()
[InlineData(TypeNameHandling.Arrays, TypeNameAssemblyFormatHandling.Full, false,
null)]
[InlineData(TypeNameHandling.Objects, TypeNameAssemblyFormatHandling.Full, true,
"Swashbuckle.AspNetCore.TestSupport.{0}, Swashbuckle.AspNetCore.TestSupport, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")]
"Swashbuckle.AspNetCore.TestSupport.{0}, Swashbuckle.AspNetCore.TestSupport, Version=1.0.0.0, Culture=neutral, PublicKeyToken=62657d7474907593")]
[InlineData(TypeNameHandling.All, TypeNameAssemblyFormatHandling.Full, true,
"Swashbuckle.AspNetCore.TestSupport.{0}, Swashbuckle.AspNetCore.TestSupport, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")]
"Swashbuckle.AspNetCore.TestSupport.{0}, Swashbuckle.AspNetCore.TestSupport, Version=1.0.0.0, Culture=neutral, PublicKeyToken=62657d7474907593")]
[InlineData(TypeNameHandling.Auto, TypeNameAssemblyFormatHandling.Simple, true,
"Swashbuckle.AspNetCore.TestSupport.{0}, Swashbuckle.AspNetCore.TestSupport")]
public void GenerateSchema_HonorsSerializerSetting_TypeNameHandling(
Expand Down

0 comments on commit 0166c69

Please sign in to comment.