I have this controller
public class Person
{
public int Id { get; set; }
public string Position { get; set; }
}
[ApiController]
[Route("[controller]")]
public class MyCtrl : ControllerBase
{
[HttpGet("{id}")]
public ActionResult<Person> Get(int id)
{
// load person with ID == id from db
return new ActionResult<Person>(person);
}
}
Now, the Position
is just a string, but based on configuration in the database, it can only be a set of finite values. I would really like if I could mimic that Position
was an enum in the Swagger interface (I use NSwag).
I would like if the API looked like it had been generated from this (where PositionType is generated from the configuration in the DB):
public enum PositionType
{
Student,
Teacher
}
public class Person
{
public int Id { get; set; }
public PositionType Position { get; set; }
}
I ended up doing this.
[AttributeUsage(AttributeTargets.Property)]
public class PositionEnum : Attribute
{
}
public class Person
{
public int Id { get; set; }
[PositionEnum]
public string Position { get; set; }
}
public class PositionEnumProcessor : ISchemaProcessor
{
public void Process(SchemaProcessorContext context)
{
var propertyInfos =
context.ContextualType.Properties
.Where(x => Attribute.IsDefined(x.PropertyInfo, typeof(PositionEnum)))
.ToList();
if (!propertyInfos.Any())
{
return;
}
var jsonSchema = context.Generator.Generate(typeof(PositionType), context.Resolver);
var containerSchema = JsonSchema.FromType<object>();
containerSchema.Type = JsonObjectType.None;
containerSchema.Reference = jsonSchema;
foreach (var propertyInfo in propertyInfos)
{
var camelCasePropertyName = System.Text.Json.JsonNamingPolicy.CamelCase.ConvertName(propertyInfo.Name);
if (context.Schema.Properties.TryGetValue(camelCasePropertyName, out var schemaProperty))
{
schemaProperty.Type = JsonObjectType.None;
schemaProperty.OneOf.Add(containerSchema);
}
}
}
}
Then in builder.Services.AddOpenApiDocument()
I have added this:
...
openApiConfiguration.SchemaSettings.SchemaProcessors.Add(new PositionEnumProcessor());
...
If anyone has a more elegant solution, please let me know 😊