Skip to content

Instantly share code, notes, and snippets.

@spaasis
Last active March 25, 2025 03:06
Show Gist options
  • Save spaasis/f558337fa1ccd1db200b6035cd83d9e0 to your computer and use it in GitHub Desktop.
Save spaasis/f558337fa1ccd1db200b6035cd83d9e0 to your computer and use it in GitHub Desktop.
Swashbuckle IDocumentFilter for hiding paths based on IFeatureManagement and FeatureGateAttribute
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.FeatureManagement;
using Microsoft.FeatureManagement.Mvc;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
/// <summary>Checks the current Feature Management configuration and removes all paths with disabled features</summary>
public class SwaggerFeatureGateFilter : IDocumentFilter {
private readonly IFeatureManager _featureManager;
public SwaggerFeatureGateFilter(IFeatureManager featureManager) {
_featureManager = featureManager;
}
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context) {
//base implementation from https://github.com/domaindrivendev/Swashbuckle.WebApi/issues/153#issuecomment-635400815
foreach (var apiDescription in context.ApiDescriptions) {
var actionDescriptor = (ControllerActionDescriptor)apiDescription.ActionDescriptor;
var attrForController = actionDescriptor.ControllerTypeInfo.GetCustomAttributes(typeof(FeatureGateAttribute), true).Select(a => (FeatureGateAttribute)a).ToList();
var attrForEndpoint = actionDescriptor.MethodInfo.GetCustomAttributes(typeof(FeatureGateAttribute), true).Select(a => (FeatureGateAttribute)a).ToList();
if (!attrForController.Any() && !attrForEndpoint.Any()) {
continue;
}
var allControllerAttributesEnabled = attrForController.Select(attr =>
attr.RequirementType == RequirementType.All
? attr.Features.All<string>(feature => _featureManager.IsEnabledAsync(feature).Result)
: attr.Features.Any<string>(feature => _featureManager.IsEnabledAsync(feature).Result))
.All(flag => flag);
var allEndpointAttributesEnabled = attrForEndpoint.Select(attr =>
attr.RequirementType == RequirementType.All
? attr.Features.All<string>(feature => _featureManager.IsEnabledAsync(feature).Result)
: attr.Features.Any<string>(feature => _featureManager.IsEnabledAsync(feature).Result))
.All(flag => flag);
if (allControllerAttributesEnabled && allEndpointAttributesEnabled) {
continue;
}
var key = "/" + apiDescription.RelativePath!.TrimEnd('/');
var operation = (OperationType)Enum.Parse(typeof(OperationType), apiDescription.HttpMethod!, true);
swaggerDoc.Paths[key].Operations.Remove(operation);
// drop the entire route if there are no operations left
if (!swaggerDoc.Paths[key].Operations.Any()) {
swaggerDoc.Paths.Remove(key);
}
}
}
}
@Lavshyak
Copy link

Lavshyak commented Jun 4, 2024

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment