I have two applications A and B. On A I use swagger for describing an API. On A I have also the definitions of classes with some enums properties. I want to generate a client API on B. For this I use Autorest. Everything works fine except one thing - enums. For some reason the enums are not generated properly and a type of properties (which were originally enum) are type of string or int (depends on the use of DescribeAllEnumsAsStrings(). I used it in example below so it is string in this case).
Enum definition generated JSON by swagger:
"Car": {
"properties": {
"color": {
"enum": [
"Red",
"Blue",
"Green"
],
"type": "string"
}
},
"type": "object"
}
Swagger registraion in Startup.cs
// Register the Swagger generator, defining one or more Swagger documents
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Info { Title = "Car API", Version = "v1" });
c.AddSecurityDefinition("Bearer", new ApiKeyScheme { In = "header", Description = "description", Name = "Authorization", Type = "apiKey" });
c.DescribeAllEnumsAsStrings();
});
Autorest command:
autorest --input-file=http://localhost:55448/swagger/v1/swagger.json --csharp --namespace=Online.Integrations.Api.Service.Car --output-folder=.
Generated class (string instead of enum):
public partial class Car
{
/// <summary>
/// Initializes a new instance of the Car class.
/// </summary>
public Car()
{
CustomInit();
}
/// <summary>
/// Initializes a new instance of the Car class.
/// </summary>
/// <param name="application">Possible values include: 'Any', 'Web'
/// ...
/// </param>
public Car(string color = default(string))
{
Color = color;
}
/// <summary>
/// An initialization method that performs custom operations like setting defaults
/// </summary>
partial void CustomInit();
/// <summary>
/// </summary>
[JsonProperty(PropertyName = "color")]
public string Color { get; set; }
}
I found this: https://github.com/Azure/autorest/tree/master/docs/extensions#x-ms-enum
there is some x-ms-enum extension but i have no idea how use it. I tryed edit JSON definition manually (what i did in example below) but it was also unsuccessful. Generated class contains object color = default(object) instead of string as in first example, not enum.
"color": {
"type": "string",
"enum": [
"Red",
"Blue",
"Green"
],
"x-ms-enum": {
"name": "Color",
"modelAsString": false
},
I'll be glad for any help. Thanks!
Edit: 1) Full Swagger definition (result type : Sting):
In swagger def. is "type": "string" right after enum definition
{"swagger":"2.0","info":{"version":"v1","title":"Audit Overseer API"},"basePath":"/","paths":{"/api/Audit":{"get":{"tags":["Audit"],"operationId":"ApiAuditGet","consumes":[],"produces":["text/plain","application/json","text/json"],"parameters":[{"name":"Id","in":"query","required":false,"type":"string"},{"name":"Application","in":"query","required":false,"type":"string","enum":["Any","Alfa","Beta"]},{"name":"DatabaseName","in":"query","required":false,"type":"string"}],"responses":{"200":{"description":"Success","schema":{"type":"array","items":{"$ref":"#/definitions/Transaction"}}}}}}},"definitions":{"Transaction":{"type":"object","properties":{"callId":{"type":"string"},"actionName":{"type":"string"},"application":{"enum":["Any","Alfa","Beta"],"type":"string"},"httpStatusCode":{"type":"string"},"dateTime":{"format":"date-time","type":"string"}}}},"securityDefinitions":{"Bearer":{"name":"Authorization","in":"header","type":"apiKey","description":"Please insert JWT with Bearer into field"}}}
autorest execution:
result:
2) Full Swagger definition (result type : Object):
In swagger def. my experiment with "x-ms-enum" right after enum definition
{"basePath":"/","definitions":{"Transaction":{"properties":{"actionName":{"type":"string"},"application":{"enum":["Any","Alfa","Beta"],"x-ms-enum":{"modelAsString":false,"name":"Application"}},"callId":{"type":"string"},"dateTime":{"format":"date-time","type":"string"},"httpStatusCode":{"type":"string"}},"type":"object"}},"info":{"title":"Audit Overseer API","version":"v1"},"paths":{"/api/Audit":{"get":{"consumes":[],"operationId":"ApiAuditGet","parameters":[{"in":"query","name":"Id","required":false,"type":"string"},{"enum":["Any","Alfa","Beta"],"in":"query","name":"Application","required":false,"type":"string"},{"in":"query","name":"DatabaseName","required":false,"type":"string"}],"produces":["text/plain","application/json","text/json"],"responses":{"200":{"description":"Success","schema":{"items":{"$ref":"#/definitions/Transaction"},"type":"array"}}},"tags":["Audit"]}}},"securityDefinitions":{"Bearer":{"description":"Please insert JWT with Bearer into field","in":"header","name":"Authorization","type":"apiKey"}},"swagger":"2.0"}
...or I can keep it empty. Result is same
{"basePath":"/","definitions":{"Transaction":{"properties":{"actionName":{"type":"string"},"application":{"enum":["Any","Alfa","Beta"]},"callId":{"type":"string"},"dateTime":{"format":"date-time","type":"string"},"httpStatusCode":{"type":"string"}},"type":"object"}},"info":{"title":"Audit Overseer API","version":"v1"},"paths":{"/api/Audit":{"get":{"consumes":[],"operationId":"ApiAuditGet","parameters":[{"in":"query","name":"Id","required":false,"type":"string"},{"enum":["Any","Alfa","Beta"],"in":"query","name":"Application","required":false,"type":"string"},{"in":"query","name":"DatabaseName","required":false,"type":"string"}],"produces":["text/plain","application/json","text/json"],"responses":{"200":{"description":"Success","schema":{"items":{"$ref":"#/definitions/Transaction"},"type":"array"}}},"tags":["Audit"]}}},"securityDefinitions":{"Bearer":{"description":"Please insert JWT with Bearer into field","in":"header","name":"Authorization","type":"apiKey"}},"swagger":"2.0"}
(now lets execute same autorest command as before)
and result is:
Future proofing solution, further to @richard-mcdaniel solution, once the outstanding enum values issue on the autorest repo get's fixed, here is the values of enums section to the EnumFilter:ISchemaFilter
Swagger registration in Startup.cs
// Register the Swagger generator, defining one or more Swagger documents
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Info { Title = "Car API", Version = "v1" });
c.AddSecurityDefinition("Bearer", new ApiKeyScheme { In = "header", Description = "description", Name = "Authorization", Type = "apiKey" });
c.ApplyFiltersToAllSchemas();//ignore deprecation warning
c.SchemaFilter(() => new EnumTypeSchemaFilter(false));
c.DescribeAllEnumsAsStrings();
});
Then define an EnumFilter
class with the following code:
using Swashbuckle.Swagger;
using System;
namespace Swashbuckle.AutoRestExtensions
{
public class EnumTypeSchemaFilter : ISchemaFilter
{
public EnumTypeSchemaFilter()
{
}
public EnumTypeSchemaFilter(bool modelAsString)
{
_modelAsString = modelAsString;
}
public void Apply(Schema schema, SchemaRegistry schemaRegistry, Type type)
{
type = Nullable.GetUnderlyingType(type) ?? type;
System.Diagnostics.Debug.WriteLine(type.FullName);
if (type.IsEnum)
{
// Add enum type information once
if (schema.vendorExtensions.ContainsKey("x-ms-enum")) return;
if (_modelAsString)
{
schema.vendorExtensions.Add("x-ms-enum", new
{
name = type.Name,
modelAsString = _modelAsString
});
}
else
{
var valuesList = new System.Collections.Generic.List<object>();
foreach (var fieldInfo in type.GetFields())
{
if (fieldInfo.FieldType.IsEnum)
{
var fName = fieldInfo.Name;
var fValue = (int)fieldInfo.GetRawConstantValue();
valuesList.Add(new { value = fValue, description = fName, name = fName });
}
}
schema.vendorExtensions.Add("x-ms-enum", new
{
name = type.Name,
modelAsString = _modelAsString,
values = valuesList
//Values:
/*
accountType:
type: string
enum:
- Standard_LRS
- Standard_ZRS
- Standard_GRS
- Standard_RAGRS
- Premium_LRS
x-ms-enum:
name: AccountType
modelAsString: false
values:
- value: Standard_LRS
description: Locally redundant storage.
name: StandardLocalRedundancy
- value: Standard_ZRS
description: Zone-redundant storage.
- value: Standard_GRS
name: StandardGeoRedundancy
- value: Standard_RAGRS
- value: Premium_LRS
*/
});
}
}
}
private readonly bool _modelAsString;
}
}
It seems like you could get SwaggerGen
to automatically add the x-ms-enum
extension by using the SchemaFilter<TFilter>
option method.
Swagger registration in Startup.cs
// Register the Swagger generator, defining one or more Swagger documents
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Info { Title = "Car API", Version = "v1" });
c.AddSecurityDefinition("Bearer", new ApiKeyScheme { In = "header", Description = "description", Name = "Authorization", Type = "apiKey" });
c.ApplyFiltersToAllSchemas();//ignore deprecation warning
c.SchemaFilter<EnumFilter>();
c.DescribeAllEnumsAsStrings();
});
Then define an EnumFilter
class with the following code
public class EnumFilter : ISchemaFilter
{
public void Apply(Schema model, SchemaFilterContext context)
{
if (model == null)
throw new ArgumentNullException("model");
if (context == null)
throw new ArgumentNullException("context");
if (context.SystemType.IsEnum)
model.Extensions.Add("x-ms-enum", new
{
name = context.SystemType.Name,
modelAsString = false
});
}
}
Looked hours to find a solution for nullable enumerations!
public class EnumFilter : ISchemaFilter
{
public void Apply(Schema model, SchemaRegistry schemaRegistry, Type type)
{
if (model == null)
{
throw new ArgumentNullException("model");
}
if (schemaRegistry == null)
{
throw new ArgumentNullException("schemaRegistry");
}
if (IsEnum(type, out var enumName))
{
model.vendorExtensions.Add("x-ms-enum", new
{
name = enumName ?? type.Name,
modelAsString = false
});
}
}
public static bool IsEnum(Type t, out string enumName)
{
if (t.IsEnum)
{
enumName = t.Name;
return true;
}
Type u = Nullable.GetUnderlyingType(t);
enumName = u?.Name;
return (u != null) && u.IsEnum;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With