Search code examples
c#enumsswaggerswashbuckle.aspnetcore

How to get enum documentation in Swagger


I am working with a Swagger API documentation and want to have my description of the enums in the documentation. I am using a custom filter to extract information from classes and enums, using annotations. e.g.:

    using System.ComponentModel;
    ...
    [DisplayName("Upload class")]            // Works fine
    public class Upload
    {
        [DisplayName("Primary Key")]          // Works fine
        public int UploadMetaId { get; set; }
        [Display(Name = "document name")]     // Works fine too
        public string Title { get; set; }
    }

But I have problems with my Enums:

[DisplayName("Document types")]    // Illegal, gives error CS0592
// or
[Display(Name = "Document types")]  // Illegal too, gives also CS0592
public enum UploadType
{
    [Display(Name = "Årsopgørelse")] // Works fine
    PartnerAarsopgoerelse = 1,
    [Display(Name = "Ægtefælles årsopgørelse")]
    AegtefaelleAarsopgoerelse = 2
}

The error CS0592 says it's not valid on this declaration type.

So what can I use instead?

UPDATE

I am using the following NuGet packages:

  • Microsoft.OpenApi (1.6.15)
  • SwashBucle.AspNetCore(6.6.2)

Solution

  • Thanks to @ralf and the article in his comment, I created a solution with my own IDocumentFilter.

    I hope I can save someone for a lot of searches by using the code below.

    I program.cs I pick up the XML documentation from the project and pass relevant 'summary' to the IDocumentFilter:

     private static void ConfigureServices(IServiceCollection services)
     {
         ...
         services.AddSwaggerGen(c =>
         {
             ...
             Dictionary<string, string> Dict = new Dictionary<string, string>();
             var dir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
             foreach (var filePath in System.IO.Directory.GetFiles(dir, "*.xml"))
             {
                 c.IncludeXmlComments(filePath);
                 GetXMLDoc(filePath, Dict);          
             }
             c.DocumentFilter<SwaggerEnumDocumentFilter>(Dict);               
         });
     }
    
    private static void GetXMLDoc(string path, Dictionary<string,string> Dict)
    {
        var _xmlComments = new XmlDocument();
        var reader = XmlReader.Create(path);
        _xmlComments.Load(reader);
        var xpath = $"//member[@name[starts-with(.,'F')] or @name[starts-with(.,'T')]]";
        var nodes = _xmlComments.DocumentElement.SelectNodes(xpath);
        foreach(XmlNode node in nodes)
        {
            var attribute = node.Attributes["name"];
            if (attribute != null)
            {
                string summary = node.FirstChild.InnerText.Trim(' ','\n');
                Dict.Add(attribute.Value, summary);
            }
        }
    }
    

    In the filter I pick up the XML documentation from the passed dictionary:

    public class SwaggerEnumDocumentFilter : IDocumentFilter
    {
    
        Dictionary<string, string> EnumDescription = new Dictionary<string, string>();
    
        public SwaggerEnumDocumentFilter(Dictionary<string, string> enumDescription)
        {
            EnumDescription = enumDescription;
        }
    
        public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
        {
            // add enum descriptions Schemas section in Swagger
            foreach (var property in swaggerDoc.Components.Schemas)
            {
                var propertyEnums = property.Value.Enum;
                if (propertyEnums is { Count: > 0 })
                {
                    property.Value.Description += DescribeEnum(property.Key, false);
                }
            }
    
            if (swaggerDoc.Paths.Count <= 0)
            {
                return;
            }
    
            //add enum descriptions to input parameters
            foreach (var pathItem in swaggerDoc.Paths.Values)
            {
                foreach (var operation in pathItem.Operations)
                {
                    if (operation.Value.Parameters != null)
                    {
                        foreach (var param in operation.Value.Parameters)
                        {
                            if (param.Schema.Reference != null)
                            {
                                param.Description += DescribeEnum(param.Schema.Reference.Id, true);
                            }
                        }
                    }
                }
            }
        }
    
        private Type GetEnumTypeByName(string enumTypeName)
        {
            if (string.IsNullOrEmpty(enumTypeName))
            {
                return null;
            }
    
            try
            {
                return AppDomain.CurrentDomain
                                .GetAssemblies()
                                .SelectMany(x => x.GetTypes())
                                .Single(x => x.FullName != null && x.Name == enumTypeName);
            }
            catch (InvalidOperationException e)
            {
                throw new Exception($"SwaggerDoc: Can not find a unique Enum for specified typeName '{enumTypeName}'. Please provide a more unique enum name.");
            }
        }
    
        private string DescribeEnum(string propertyTypeName, bool inApiList)
        {
            var enumType = GetEnumTypeByName(propertyTypeName);
    
            if (enumType == null)
            {
                return null;
            }
    
            var values = Enum.GetValues(enumType);
            if (values == null)
            {
                return null;
            }
    
            string result = "<ul>";
    
            if (inApiList)
            {
                var key = $"T:{enumType.FullName}";
                var summary = EnumDescription.ContainsKey(key) ? EnumDescription[key] : "";
                result = $"<p>{summary}</p><ul>";
            }
    
            foreach (var value in values)
            {
                var key = $"F:{enumType.FullName}.{value}";
                var summary = EnumDescription.ContainsKey(key) ? EnumDescription[key] : "";
                string n = EnumUtils.GetDisplayName((System.Enum)value);
                result += $"<li>{(int)value} - {n} : {summary} </li>";
            }
            result += "</ul>";
            return result;
        }
    
    }