-
Notifications
You must be signed in to change notification settings - Fork 10.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Microsoft.AspNetCore.OpenApi does not generate a schema for an enum that is a key in a dictionary #60163
Comments
Can also confirm. This happens to our project outside of a dictionary (inside records). |
@PatrezDev Thank you for filing this issue. I think there is no schema generated for the enum because it would not be referenced anywhere. Unlike NSwag, the ASP.NET OpenAPI generation does not produce an In .NET 10 you should be able to write a transformer to do this. Unfortunately because it involves adding a schema this is not possible with .NET 9. Actually I just tried this with .NET 10 preview 1 (will be released next week) and found it more difficult that I hoped. This generated some good ideas for making this easier and we'll try to get some of these done in upcoming preview releases. I'm going to mark this as a feature request so we can link it to the features we deliver in .NET 10. |
.net 9: Using the schema transformer: services.AddOpenApi(static options => internal sealed class GenericSchemaTransformer : IOpenApiSchemaTransformer
{
public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext context, CancellationToken cancellationToken)
{
OpenApiSchema? newSchema = null;
if (context.JsonTypeInfo.Type.IsEnum)
{
newSchema = EnumOpenApiSchemaGenerator.GetBaseJsonSchema(context.JsonTypeInfo.Type);
}
if (newSchema is null)
{
return Task.CompletedTask;
}
// copy properties from new schema to existing schema
schema.AdditionalProperties = newSchema.AdditionalProperties;
schema.AdditionalPropertiesAllowed = newSchema.AdditionalPropertiesAllowed;
schema.AllOf = newSchema.AllOf;
schema.Annotations = newSchema.Annotations;
schema.AnyOf = newSchema.AnyOf;
schema.Default = newSchema.Default;
schema.Deprecated = newSchema.Deprecated;
schema.Description = newSchema.Description;
schema.Discriminator = newSchema.Discriminator;
schema.Enum = newSchema.Enum;
schema.Example = newSchema.Example;
schema.ExclusiveMaximum = newSchema.ExclusiveMaximum;
schema.ExclusiveMinimum = newSchema.ExclusiveMinimum;
schema.Extensions = newSchema.Extensions;
schema.ExternalDocs = newSchema.ExternalDocs;
schema.Format = newSchema.Format;
schema.Items = newSchema.Items;
schema.Maximum = newSchema.Maximum;
schema.MaxItems = newSchema.MaxItems;
schema.MaxLength = newSchema.MaxLength;
schema.MaxProperties = newSchema.MaxProperties;
schema.Minimum = newSchema.Minimum;
schema.MinItems = newSchema.MinItems;
schema.MinLength = newSchema.MinLength;
schema.MinProperties = newSchema.MinProperties;
schema.MultipleOf = newSchema.MultipleOf;
schema.Not = newSchema.Not;
schema.Nullable = newSchema.Nullable;
schema.OneOf = newSchema.OneOf;
schema.Pattern = newSchema.Pattern;
schema.Properties = newSchema.Properties
.OrderBy(static it => it.Value.Title)
.ToDictionary(static it => it.Key, static it => it.Value);
schema.ReadOnly = newSchema.ReadOnly;
schema.Reference = newSchema.Reference;
schema.Required = newSchema.Required.Select(OpenApiSchemaExtensions.ToCamelCase).ToHashSet();
schema.Title = newSchema.Title;
schema.Type = newSchema.Type;
schema.UniqueItems = newSchema.UniqueItems;
schema.UnresolvedReference = newSchema.UnresolvedReference;
schema.WriteOnly = newSchema.WriteOnly;
schema.Xml = newSchema.Xml;
return Task.CompletedTask;
}
}
public static class EnumOpenApiSchemaGenerator
{
public static OpenApiSchema GetBaseJsonSchema<TEnum>(TEnum? defaultValue = null)
where TEnum : struct, Enum
{
return GetBaseJsonSchema(typeof(TEnum).Name, Enum.GetNames<TEnum>(), defaultValue?.ToString());
}
public static OpenApiSchema GetBaseJsonSchema(Type enumType, string? defaultValue = null)
{
if (!enumType.IsEnum)
{
throw new ArgumentException("Type must be an enum.", nameof(enumType));
}
return GetBaseJsonSchema(enumType.Name, Enum.GetNames(enumType), defaultValue);
}
private static OpenApiSchema GetBaseJsonSchema(
string title,
string[] names,
string? defaultValue = null)
{
var openApiSchema = new OpenApiSchema()
{
Title = title,
AdditionalPropertiesAllowed = false,
Type = SchemaType.String.ToStringFast(true),
Enum = [.. names.Select(name => new OpenApiString(name))],
Annotations = new Dictionary<string, object>
{
["x-schema-id"] = title,
},
};
if (defaultValue is not null)
openApiSchema.Default = new OpenApiString(defaultValue);
return openApiSchema;
}
}
public enum SchemaType
{
String,
Integer,
Number,
Boolean,
Object,
Array,
}
// Test Endpoints
public static void MapCultureEndpoints(this WebApplication application)
{
var router = application
.MapGroup("/api/cultures")
.WithTags("cultures");
router.MapGet("/schema-type", () => SchemaTypeExtensions.GetNames())
.WithName("cultures-schema-type")
.WithDisplayName("schema-type")
.Produces<SchemaType>();
router.MapGet("/schema-type-45", () => SchemaTypeExtensions.GetNames())
.WithName("cultures-schema-typev2")
.WithDisplayName("schema-typev2")
.Produces<SchemaType>();
} open-api.json {
"openapi": "3.0.1",
"info": {
"title": "Portal API",
"version": "v1"
},
"paths": {
"/api/cultures/schema-type": {
"get": {
"tags": [
"cultures"
],
"operationId": "cultures-schema-type",
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SchemaType"
}
}
}
}
}
}
},
"/api/cultures/schema-type-45": {
"get": {
"tags": [
"cultures"
],
"operationId": "cultures-schema-typev2",
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SchemaType"
}
}
}
}
}
}
}
},
"components": {
"schemas": {
"SchemaType": {
"title": "SchemaType",
"enum": [
"String",
"Integer",
"Number",
"Boolean",
"Object",
"Array"
],
"type": "string",
"additionalProperties": false
}
}
},
"tags": [
{
"name": "cultures"
}
]
} |
Is there an existing issue for this?
Describe the bug
WebAPI .NET 9
Nuget: Microsoft.AspNetCore.OpenApi 9.0.1
Hello, when I use attribute:
[ProducesResponseType<Dictionary<AuthErrorType, ErrorDetail>>(403)]
the enum values are not generated in the OpenApi.json under the components/schemas section.
The enum is not generated even when using minimal API
app.MapGet("/minimal-api-approach", () => { return "Hello World!"; }).Produces<Dictionary<AuthErrorType, ErrorDetail>>();
`public class WeatherForecastController : ControllerBase
{
public WeatherForecastController() {
Expected Behavior
The expected result is a generated enum values in the components/schemas section.
With NSwag the result is below:
`
"components": {
}`
Steps To Reproduce
https://github.com/PatrezDev/openapispec-net-nswag
Exceptions (if any)
No response
.NET Version
.NET 9
Anything else?
The text was updated successfully, but these errors were encountered: