diff --git a/Directory.Build.props b/Directory.Build.props index 737b43008b..364ca8cf91 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -49,7 +49,7 @@ snupkg true true - 6.7.4 + 6.8.0 false diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/DependencyInjection/ConfigureSwaggerGeneratorOptions.cs b/src/Swashbuckle.AspNetCore.SwaggerGen/DependencyInjection/ConfigureSwaggerGeneratorOptions.cs index 29d43ca555..a8366c9cad 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/DependencyInjection/ConfigureSwaggerGeneratorOptions.cs +++ b/src/Swashbuckle.AspNetCore.SwaggerGen/DependencyInjection/ConfigureSwaggerGeneratorOptions.cs @@ -34,17 +34,57 @@ public void Configure(SwaggerGeneratorOptions options) // Create and add any filters that were specified through the FilterDescriptor lists ... - _swaggerGenOptions.ParameterFilterDescriptors.ForEach( - filterDescriptor => options.ParameterFilters.Add(GetOrCreateFilter(filterDescriptor))); + foreach (var filterDescriptor in _swaggerGenOptions.ParameterFilterDescriptors) + { + if (filterDescriptor.Type.IsAssignableTo(typeof(IParameterFilter))) + { + options.ParameterFilters.Add(GetOrCreateFilter(filterDescriptor)); + } + + if (filterDescriptor.Type.IsAssignableTo(typeof(IParameterAsyncFilter))) + { + options.ParameterAsyncFilters.Add(GetOrCreateFilter(filterDescriptor)); + } + } + + foreach (var filterDescriptor in _swaggerGenOptions.RequestBodyFilterDescriptors) + { + if (filterDescriptor.Type.IsAssignableTo(typeof(IRequestBodyFilter))) + { + options.RequestBodyFilters.Add(GetOrCreateFilter(filterDescriptor)); + } + + if (filterDescriptor.Type.IsAssignableTo(typeof(IRequestBodyAsyncFilter))) + { + options.RequestBodyAsyncFilters.Add(GetOrCreateFilter(filterDescriptor)); + } + } + + foreach (var filterDescriptor in _swaggerGenOptions.OperationFilterDescriptors) + { + if (filterDescriptor.Type.IsAssignableTo(typeof(IOperationFilter))) + { + options.OperationFilters.Add(GetOrCreateFilter(filterDescriptor)); + } - _swaggerGenOptions.RequestBodyFilterDescriptors.ForEach( - filterDescriptor => options.RequestBodyFilters.Add(GetOrCreateFilter(filterDescriptor))); + if (filterDescriptor.Type.IsAssignableTo(typeof(IOperationAsyncFilter))) + { + options.OperationAsyncFilters.Add(GetOrCreateFilter(filterDescriptor)); + } + } - _swaggerGenOptions.OperationFilterDescriptors.ForEach( - filterDescriptor => options.OperationFilters.Add(GetOrCreateFilter(filterDescriptor))); + foreach (var filterDescriptor in _swaggerGenOptions.DocumentFilterDescriptors) + { + if (filterDescriptor.Type.IsAssignableTo(typeof(IDocumentFilter))) + { + options.DocumentFilters.Add(GetOrCreateFilter(filterDescriptor)); + } - _swaggerGenOptions.DocumentFilterDescriptors.ForEach( - filterDescriptor => options.DocumentFilters.Add(GetOrCreateFilter(filterDescriptor))); + if (filterDescriptor.Type.IsAssignableTo(typeof(IDocumentAsyncFilter))) + { + options.DocumentAsyncFilters.Add(GetOrCreateFilter(filterDescriptor)); + } + } if (!options.SwaggerDocs.Any()) { diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/DependencyInjection/SwaggerGenOptionsExtensions.cs b/src/Swashbuckle.AspNetCore.SwaggerGen/DependencyInjection/SwaggerGenOptionsExtensions.cs index 0d2be04390..37144900a5 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/DependencyInjection/SwaggerGenOptionsExtensions.cs +++ b/src/Swashbuckle.AspNetCore.SwaggerGen/DependencyInjection/SwaggerGenOptionsExtensions.cs @@ -370,6 +370,7 @@ public static void AddSchemaFilterInstance( if (filterInstance == null) throw new ArgumentNullException(nameof(filterInstance)); swaggerGenOptions.SchemaFilterDescriptors.Add(new FilterDescriptor { + Type = typeof(TFilter), FilterInstance = filterInstance }); } @@ -393,6 +394,25 @@ public static void ParameterFilter( }); } + /// + /// Extend the Swagger Generator with "filters" that can modify Parameters asynchronously after they're initially generated + /// + /// A type that derives from + /// + /// Optionally inject parameters through filter constructors + public static void ParameterAsyncFilter( + this SwaggerGenOptions swaggerGenOptions, + params object[] arguments) + where TFilter : IParameterAsyncFilter + { + if (swaggerGenOptions == null) throw new ArgumentNullException(nameof(swaggerGenOptions)); + swaggerGenOptions.ParameterFilterDescriptors.Add(new FilterDescriptor + { + Type = typeof(TFilter), + Arguments = arguments + }); + } + /// /// Extend the Swagger Generator with "filters" that can modify Parameters after they're initially generated /// @@ -408,6 +428,27 @@ public static void AddParameterFilterInstance( if (filterInstance == null) throw new ArgumentNullException(nameof(filterInstance)); swaggerGenOptions.ParameterFilterDescriptors.Add(new FilterDescriptor { + Type = typeof(TFilter), + FilterInstance = filterInstance + }); + } + + /// + /// Extend the Swagger Generator with "filters" that can modify Parameters asynchronously after they're initially generated + /// + /// A type that derives from + /// + /// The filter instance to use. + public static void AddParameterAsyncFilterInstance( + this SwaggerGenOptions swaggerGenOptions, + TFilter filterInstance) + where TFilter : IParameterAsyncFilter + { + if (swaggerGenOptions == null) throw new ArgumentNullException(nameof(swaggerGenOptions)); + if (filterInstance == null) throw new ArgumentNullException(nameof(filterInstance)); + swaggerGenOptions.ParameterFilterDescriptors.Add(new FilterDescriptor + { + Type = typeof(TFilter), FilterInstance = filterInstance }); } @@ -431,6 +472,25 @@ public static void RequestBodyFilter( }); } + /// + /// Extend the Swagger Generator with "filters" that can modify RequestBodys asynchronously after they're initially generated + /// + /// A type that derives from + /// + /// Optionally inject parameters through filter constructors + public static void RequestBodyAsyncFilter( + this SwaggerGenOptions swaggerGenOptions, + params object[] arguments) + where TFilter : IRequestBodyAsyncFilter + { + if (swaggerGenOptions == null) throw new ArgumentNullException(nameof(swaggerGenOptions)); + swaggerGenOptions.RequestBodyFilterDescriptors.Add(new FilterDescriptor + { + Type = typeof(TFilter), + Arguments = arguments + }); + } + /// /// Extend the Swagger Generator with "filters" that can modify RequestBodys after they're initially generated /// @@ -446,6 +506,27 @@ public static void AddRequestBodyFilterInstance( if (filterInstance == null) throw new ArgumentNullException(nameof(filterInstance)); swaggerGenOptions.RequestBodyFilterDescriptors.Add(new FilterDescriptor { + Type = typeof(TFilter), + FilterInstance = filterInstance + }); + } + + /// + /// Extend the Swagger Generator with "filters" that can modify RequestBodys asynchronously after they're initially generated + /// + /// A type that derives from + /// + /// The filter instance to use. + public static void AddRequestBodyAsyncFilterInstance( + this SwaggerGenOptions swaggerGenOptions, + TFilter filterInstance) + where TFilter : IRequestBodyAsyncFilter + { + if (swaggerGenOptions == null) throw new ArgumentNullException(nameof(swaggerGenOptions)); + if (filterInstance == null) throw new ArgumentNullException(nameof(filterInstance)); + swaggerGenOptions.RequestBodyFilterDescriptors.Add(new FilterDescriptor + { + Type = typeof(TFilter), FilterInstance = filterInstance }); } @@ -469,6 +550,25 @@ public static void OperationFilter( }); } + /// + /// Extend the Swagger Generator with "filters" that can modify Operations asynchronously after they're initially generated + /// + /// A type that derives from + /// + /// Optionally inject parameters through filter constructors + public static void OperationAsyncFilter( + this SwaggerGenOptions swaggerGenOptions, + params object[] arguments) + where TFilter : IOperationAsyncFilter + { + if (swaggerGenOptions == null) throw new ArgumentNullException(nameof(swaggerGenOptions)); + swaggerGenOptions.OperationFilterDescriptors.Add(new FilterDescriptor + { + Type = typeof(TFilter), + Arguments = arguments + }); + } + /// /// Extend the Swagger Generator with "filters" that can modify Operations after they're initially generated /// @@ -484,6 +584,27 @@ public static void AddOperationFilterInstance( if (filterInstance == null) throw new ArgumentNullException(nameof(filterInstance)); swaggerGenOptions.OperationFilterDescriptors.Add(new FilterDescriptor { + Type = typeof(TFilter), + FilterInstance = filterInstance + }); + } + + /// + /// Extend the Swagger Generator with "filters" that can modify Operations asynchronously after they're initially generated + /// + /// A type that derives from + /// + /// The filter instance to use + public static void AddOperationAsyncFilterInstance( + this SwaggerGenOptions swaggerGenOptions, + TFilter filterInstance) + where TFilter : IOperationAsyncFilter + { + if (swaggerGenOptions == null) throw new ArgumentNullException(nameof(swaggerGenOptions)); + if (filterInstance == null) throw new ArgumentNullException(nameof(filterInstance)); + swaggerGenOptions.OperationFilterDescriptors.Add(new FilterDescriptor + { + Type = typeof(TFilter), FilterInstance = filterInstance }); } @@ -507,6 +628,26 @@ public static void DocumentFilter( }); } + /// + /// Extend the Swagger Generator with "filters" that can modify SwaggerDocuments asynchronously after they're initially generated + /// + /// A type that derives from + /// + /// Optionally inject parameters through filter constructors + /// + public static void DocumentAsyncFilter( + this SwaggerGenOptions swaggerGenOptions, + params object[] arguments) + where TFilter : IDocumentAsyncFilter + { + if (swaggerGenOptions == null) throw new ArgumentNullException(nameof(swaggerGenOptions)); + swaggerGenOptions.DocumentFilterDescriptors.Add(new FilterDescriptor + { + Type = typeof(TFilter), + Arguments = arguments + }); + } + /// /// Extend the Swagger Generator with "filters" that can modify SwaggerDocuments after they're initially generated /// @@ -522,6 +663,28 @@ public static void AddDocumentFilterInstance( if (filterInstance == null) throw new ArgumentNullException(nameof(filterInstance)); swaggerGenOptions.DocumentFilterDescriptors.Add(new FilterDescriptor { + Type = typeof(TFilter), + FilterInstance = filterInstance + }); + } + + /// + /// Extend the Swagger Generator with "filters" that can modify SwaggerDocuments asynchronously after they're initially generated + /// + /// A type that derives from + /// + /// The filter instance to use. + /// + public static void AddDocumentAsyncFilterInstance( + this SwaggerGenOptions swaggerGenOptions, + TFilter filterInstance) + where TFilter : IDocumentAsyncFilter + { + if (swaggerGenOptions == null) throw new ArgumentNullException(nameof(swaggerGenOptions)); + if (filterInstance == null) throw new ArgumentNullException(nameof(filterInstance)); + swaggerGenOptions.DocumentFilterDescriptors.Add(new FilterDescriptor + { + Type = typeof(TFilter), FilterInstance = filterInstance }); } diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/PublicAPI/PublicAPI.Unshipped.txt b/src/Swashbuckle.AspNetCore.SwaggerGen/PublicAPI/PublicAPI.Unshipped.txt index e69de29bb2..2736a7c349 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/PublicAPI/PublicAPI.Unshipped.txt +++ b/src/Swashbuckle.AspNetCore.SwaggerGen/PublicAPI/PublicAPI.Unshipped.txt @@ -0,0 +1,8 @@ +static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.AddDocumentAsyncFilterInstance(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, TFilter filterInstance) -> void +static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.AddOperationAsyncFilterInstance(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, TFilter filterInstance) -> void +static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.AddParameterAsyncFilterInstance(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, TFilter filterInstance) -> void +static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.AddRequestBodyAsyncFilterInstance(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, TFilter filterInstance) -> void +static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.DocumentAsyncFilter(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, params object[] arguments) -> void +static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.OperationAsyncFilter(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, params object[] arguments) -> void +static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.ParameterAsyncFilter(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, params object[] arguments) -> void +static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.RequestBodyAsyncFilter(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, params object[] arguments) -> void diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/ConfigureSwaggerGeneratorOptionsTests.cs b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/ConfigureSwaggerGeneratorOptionsTests.cs index b16c02e894..9145625c2a 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/ConfigureSwaggerGeneratorOptionsTests.cs +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/ConfigureSwaggerGeneratorOptionsTests.cs @@ -216,4 +216,200 @@ public static void AddingDocumentFilterTypes_WhenConfiguringOptions_DifferentIns Assert.Equal(2, swaggerGeneratorOptions.DocumentFilters.Count); Assert.NotSame(swaggerGeneratorOptions.DocumentFilters.First(), swaggerGeneratorOptions.DocumentFilters.Last()); } + + [Fact] + public static void AddingParameterAsyncFilterInstances_WhenConfiguringOptions_SameInstanceIsAdded() + { + var webhostingEnvironment = Substitute.For(); + webhostingEnvironment.ApplicationName.Returns("Swashbuckle.AspNetCore.SwaggerGen.Test"); + + var testParameterFilter = new TestParameterFilter(); + + var options = new SwaggerGenOptions(); + options.AddParameterAsyncFilterInstance(testParameterFilter); + options.AddParameterAsyncFilterInstance(testParameterFilter); + + var configureSwaggerGeneratorOptions = new ConfigureSwaggerGeneratorOptions( + Options.Create(options), + null, + webhostingEnvironment); + var swaggerGeneratorOptions = new SwaggerGeneratorOptions(); + + configureSwaggerGeneratorOptions.Configure(swaggerGeneratorOptions); + + Assert.Equal(2, swaggerGeneratorOptions.ParameterAsyncFilters.Count); + Assert.Same(testParameterFilter, swaggerGeneratorOptions.ParameterAsyncFilters.First()); + Assert.Same(testParameterFilter, swaggerGeneratorOptions.ParameterAsyncFilters.Last()); + } + + [Fact] + public static void AddingParameterAsyncFilterTypes_WhenConfiguringOptions_DifferentInstancesAreAdded() + { + var webhostingEnvironment = Substitute.For(); + webhostingEnvironment.ApplicationName.Returns("Swashbuckle.AspNetCore.SwaggerGen.Test"); + + var options = new SwaggerGenOptions(); + options.ParameterAsyncFilter(); + options.ParameterAsyncFilter(); + + using var serviceProvider = new ServiceCollection().BuildServiceProvider(); + + var configureSwaggerGeneratorOptions = new ConfigureSwaggerGeneratorOptions( + Options.Create(options), + serviceProvider, + webhostingEnvironment); + var swaggerGeneratorOptions = new SwaggerGeneratorOptions(); + + configureSwaggerGeneratorOptions.Configure(swaggerGeneratorOptions); + + Assert.Equal(2, swaggerGeneratorOptions.ParameterAsyncFilters.Count); + Assert.NotSame(swaggerGeneratorOptions.ParameterAsyncFilters.First(), swaggerGeneratorOptions.ParameterAsyncFilters.Last()); + } + + [Fact] + public static void AddingRequestBodyAsyncFilterInstances_WhenConfiguringOptions_SameInstanceIsAdded() + { + var webhostingEnvironment = Substitute.For(); + webhostingEnvironment.ApplicationName.Returns("Swashbuckle.AspNetCore.SwaggerGen.Test"); + + var testRequestBodyFilter = new TestRequestBodyFilter(); + + var options = new SwaggerGenOptions(); + options.AddRequestBodyAsyncFilterInstance(testRequestBodyFilter); + options.AddRequestBodyAsyncFilterInstance(testRequestBodyFilter); + + var configureSwaggerGeneratorOptions = new ConfigureSwaggerGeneratorOptions( + Options.Create(options), + null, + webhostingEnvironment); + var swaggerGeneratorOptions = new SwaggerGeneratorOptions(); + + configureSwaggerGeneratorOptions.Configure(swaggerGeneratorOptions); + + Assert.Equal(2, swaggerGeneratorOptions.RequestBodyAsyncFilters.Count); + Assert.Same(testRequestBodyFilter, swaggerGeneratorOptions.RequestBodyAsyncFilters.First()); + Assert.Same(testRequestBodyFilter, swaggerGeneratorOptions.RequestBodyAsyncFilters.Last()); + } + + [Fact] + public static void AddingRequestBodyAsyncFilterTypes_WhenConfiguringOptions_DifferentInstancesAreAdded() + { + var webhostingEnvironment = Substitute.For(); + webhostingEnvironment.ApplicationName.Returns("Swashbuckle.AspNetCore.SwaggerGen.Test"); + + var options = new SwaggerGenOptions(); + options.RequestBodyAsyncFilter(); + options.RequestBodyAsyncFilter(); + + using var serviceProvider = new ServiceCollection().BuildServiceProvider(); + + var configureSwaggerGeneratorOptions = new ConfigureSwaggerGeneratorOptions( + Options.Create(options), + serviceProvider, + webhostingEnvironment); + var swaggerGeneratorOptions = new SwaggerGeneratorOptions(); + + configureSwaggerGeneratorOptions.Configure(swaggerGeneratorOptions); + + Assert.Equal(2, swaggerGeneratorOptions.RequestBodyAsyncFilters.Count); + Assert.NotSame(swaggerGeneratorOptions.RequestBodyAsyncFilters.First(), swaggerGeneratorOptions.RequestBodyAsyncFilters.Last()); + } + + [Fact] + public static void AddingOperationAsyncFilterInstances_WhenConfiguringOptions_SameInstanceIsAdded() + { + var webhostingEnvironment = Substitute.For(); + webhostingEnvironment.ApplicationName.Returns("Swashbuckle.AspNetCore.SwaggerGen.Test"); + + var testOperationFilter = new TestOperationFilter(); + + var options = new SwaggerGenOptions(); + options.AddOperationAsyncFilterInstance(testOperationFilter); + options.AddOperationAsyncFilterInstance(testOperationFilter); + + var configureSwaggerGeneratorOptions = new ConfigureSwaggerGeneratorOptions( + Options.Create(options), + null, + webhostingEnvironment); + var swaggerGeneratorOptions = new SwaggerGeneratorOptions(); + + configureSwaggerGeneratorOptions.Configure(swaggerGeneratorOptions); + + Assert.Equal(2, swaggerGeneratorOptions.OperationAsyncFilters.Count); + Assert.Same(testOperationFilter, swaggerGeneratorOptions.OperationAsyncFilters.First()); + Assert.Same(testOperationFilter, swaggerGeneratorOptions.OperationAsyncFilters.Last()); + } + + [Fact] + public static void AddingOperationAsyncFilterTypes_WhenConfiguringOptions_DifferentInstancesAreAdded() + { + var webhostingEnvironment = Substitute.For(); + webhostingEnvironment.ApplicationName.Returns("Swashbuckle.AspNetCore.SwaggerGen.Test"); + + var options = new SwaggerGenOptions(); + options.OperationAsyncFilter(); + options.OperationAsyncFilter(); + + using var serviceProvider = new ServiceCollection().BuildServiceProvider(); + + var configureSwaggerGeneratorOptions = new ConfigureSwaggerGeneratorOptions( + Options.Create(options), + serviceProvider, + webhostingEnvironment); + var swaggerGeneratorOptions = new SwaggerGeneratorOptions(); + + configureSwaggerGeneratorOptions.Configure(swaggerGeneratorOptions); + + Assert.Equal(2, swaggerGeneratorOptions.OperationAsyncFilters.Count); + Assert.NotSame(swaggerGeneratorOptions.OperationAsyncFilters.First(), swaggerGeneratorOptions.OperationAsyncFilters.Last()); + } + + [Fact] + public static void AddingDocumentAsyncFilterInstances_WhenConfiguringOptions_SameInstanceIsAdded() + { + var webhostingEnvironment = Substitute.For(); + webhostingEnvironment.ApplicationName.Returns("Swashbuckle.AspNetCore.SwaggerGen.Test"); + + var testDocumentFilter = new TestDocumentFilter(); + + var options = new SwaggerGenOptions(); + options.AddDocumentAsyncFilterInstance(testDocumentFilter); + options.AddDocumentAsyncFilterInstance(testDocumentFilter); + + var configureSwaggerGeneratorOptions = new ConfigureSwaggerGeneratorOptions( + Options.Create(options), + null, + webhostingEnvironment); + var swaggerGeneratorOptions = new SwaggerGeneratorOptions(); + + configureSwaggerGeneratorOptions.Configure(swaggerGeneratorOptions); + + Assert.Equal(2, swaggerGeneratorOptions.DocumentAsyncFilters.Count); + Assert.Same(testDocumentFilter, swaggerGeneratorOptions.DocumentAsyncFilters.First()); + Assert.Same(testDocumentFilter, swaggerGeneratorOptions.DocumentAsyncFilters.Last()); + } + + [Fact] + public static void AddingDocumentAsyncFilterTypes_WhenConfiguringOptions_DifferentInstancesAreAdded() + { + var webhostingEnvironment = Substitute.For(); + webhostingEnvironment.ApplicationName.Returns("Swashbuckle.AspNetCore.SwaggerGen.Test"); + + var options = new SwaggerGenOptions(); + options.DocumentAsyncFilter(); + options.DocumentAsyncFilter(); + + using var serviceProvider = new ServiceCollection().BuildServiceProvider(); + + var configureSwaggerGeneratorOptions = new ConfigureSwaggerGeneratorOptions( + Options.Create(options), + serviceProvider, + webhostingEnvironment); + var swaggerGeneratorOptions = new SwaggerGeneratorOptions(); + + configureSwaggerGeneratorOptions.Configure(swaggerGeneratorOptions); + + Assert.Equal(2, swaggerGeneratorOptions.DocumentAsyncFilters.Count); + Assert.NotSame(swaggerGeneratorOptions.DocumentAsyncFilters.First(), swaggerGeneratorOptions.DocumentAsyncFilters.Last()); + } }