Swashbuckle.WebApi icon indicating copy to clipboard operation
Swashbuckle.WebApi copied to clipboard

x-enumNames

Open RouR opened this issue 5 years ago • 3 comments

VERSION:

Swashbuckle.AspNetCore -Version 3.0.0

STEPS TO REPRODUCE:

 public enum Currency
    {
        Unknown = 0,
        Usd,
        Euro,
    }

EXPECTED RESULT:

"currency": {
  "format": "int32",          
  "enum": [
	0,
	1,
	2
  ],
  "type": "integer",
  "x-enumNames": [
	"Unknown",            
	"Usd",
	"Euro"
  ]
}

ACTUAL RESULT:

"currency": {
  "format": "int32",          
  "enum": [
	0,
	1,
	2
  ],
  "type": "integer"
}

ADDITIONAL DETAILS

Please add x-enumNames attribute It is very useful for NSwag integration and auto-generating client code (see https://github.com/RSuter/NSwag/issues/1234 and https://stackoverflow.com/questions/36452468/swagger-ui-web-api-documentation-present-enums-as-strings)

It can be like

public class SwaggerAddEnumDescriptions : IDocumentFilter
    {
        public void Apply(SwaggerDocument swaggerDoc, DocumentFilterContext context)
        {
            // add enum descriptions to result models
            foreach (var schemaDictionaryItem in swaggerDoc.Definitions)
            {
                var schema = schemaDictionaryItem.Value;
                foreach (var propertyDictionaryItem in schema.Properties)
                {
                    var property = propertyDictionaryItem.Value;
                    var propertyEnums = property.Enum;
                    if (propertyEnums != null && propertyEnums.Count > 0)
                    {
                        property.Description += DescribeEnum(propertyEnums);
                        property.Extensions.Add("x-enumNames", GetStringMapping(propertyEnums));
                    }
                }
            }

            if (swaggerDoc.Paths.Count <= 0) return;

            // add enum descriptions to input parameters
            foreach (var pathItem in swaggerDoc.Paths.Values)
            {
                DescribeEnumParameters(pathItem.Parameters);

                // head, patch, options, delete left out
                var possibleParameterisedOperations = new List<Operation> {pathItem.Get, pathItem.Post, pathItem.Put};
                possibleParameterisedOperations.FindAll(x => x != null)
                    .ForEach(x => DescribeEnumParameters(x.Parameters));
            }
        }

        private string[] GetStringMapping(IList<object> enums)
        {
            var enumDescriptions = new List<string>();
            Type type = null;
            foreach (var enumOption in enums)
            {
                if (type == null) type = enumOption.GetType();
                enumDescriptions.Add(Enum.GetName(type, enumOption));
            }

            return enumDescriptions.ToArray();
        }

        private static void DescribeEnumParameters(IList<IParameter> parameters)
        {
            if (parameters == null) return;

            foreach (var param in parameters)
            {
                if (param.Extensions.ContainsKey("enum") && param.Extensions["enum"] is IList<object> paramEnums &&
                    paramEnums.Count > 0)
                {
                    param.Description += DescribeEnum(paramEnums);
                }
            }
        }

        private static string DescribeEnum(IEnumerable<object> enums)
        {
            var enumDescriptions = new List<string>();
            Type type = null;
            foreach (var enumOption in enums)
            {
                if (type == null) type = enumOption.GetType();
                enumDescriptions.Add(
                    $"{Convert.ChangeType(enumOption, type.GetEnumUnderlyingType())} = {Enum.GetName(type, enumOption)}");
            }

            return $"{Environment.NewLine}{string.Join(Environment.NewLine, enumDescriptions)}";
        }
    }

and usage

options.DocumentFilter<SwaggerAddEnumDescriptions>();
//options.DescribeAllEnumsAsStrings();

RouR avatar Dec 23 '18 18:12 RouR

Currently you can do it as easy as:

public class EnumSchemaFilter : ISchemaFilter
{
    public void Apply(OpenApiSchema schema, SchemaFilterContext context)
    {
        if (context.Type.IsEnum)
        {
            var array = new OpenApiArray();
            array.AddRange(Enum.GetNames(context.Type).Select(n => new OpenApiString(n)));
            schema.Extensions.Add("x-enumNames", array);
        }
    }
}

And when configuring:

options.SchemaFilter<EnumSchemaFilter>();

sherlock1982 avatar Jul 07 '20 22:07 sherlock1982

When we are using NSwag, I guess we are all using the excellent implementation of EnumSchemaFilter described by @sherlock1982 above.

On our side, we need to keep the enum: [10, 20, 30] as integer, it's important for typescript NSwag generation the enumerables were correctly formatted (something like Wire = 10, Bluetooth = 20, Wifi = 30).

swagger.json image

NSwag generation image

But on the UI interface, the generation does not display the x-enumNames, so we only see AccessoryConnectionEnum: [ 10, 20, 30 ] image

It's a lack of information, our partners asked us the meaning of these values, we need to maintain a documentation for enums, it makes no sense because the enums values are listed inside the swagger.json, but not displayed on the UI side...

Could it be part of the roadmap ?

I have seen the same discussion on other stack with swagger implementation. https://github.com/swagger-api/swagger-ui/pull/6948#issuecomment-780935833

StephanArnas avatar Mar 07 '23 12:03 StephanArnas

I agree with that, would be nice to have additional description for those x-enumNames

jakubgerlee avatar May 17 '23 08:05 jakubgerlee