Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -375,12 +375,21 @@ private OpenApiParameter GenerateParameter(
apiParameter.RouteInfo)
: new OpenApiSchema { Type = "string" };

var description = schema.Description;
if (string.IsNullOrEmpty(description)
&& !string.IsNullOrEmpty(schema?.Reference?.Id)
&& schemaRepository.Schemas.TryGetValue(schema.Reference.Id, out var openApiSchema))
{
description = openApiSchema.Description;
}

var parameter = new OpenApiParameter
{
Name = name,
In = location,
Required = isRequired,
Schema = schema
Schema = schema,
Description = description
};

var filterContext = new ParameterFilterContext(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ internal class FakeICompositeMetadataDetailsProvider : ICompositeMetadataDetails
{
public void CreateBindingMetadata(BindingMetadataProviderContext context)
{
throw new NotImplementedException();
context.BindingMetadata.IsBindingAllowed = true;
}

public void CreateDisplayMetadata(DisplayMetadataProviderContext context)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using System;
using System.Linq;
using System.Text;
using Microsoft.OpenApi.Models;

namespace Swashbuckle.AspNetCore.SwaggerGen.Test.Fixtures
{
internal class TestEnumSchemaFilter : ISchemaFilter
{
public void Apply(OpenApiSchema schema, SchemaFilterContext context)
{
bool isEnumArgument = (context.Type?.GenericTypeArguments?.Length ?? 0) == 1 && context.Type.GenericTypeArguments.All(b => b.IsEnum);
var isEnumArray = context.Type.IsArray && context.Type.GetElementType().IsEnum;
if (context.Type.IsEnum || isEnumArgument || isEnumArray)
{
var enumType = (context.Type.IsEnum, isEnumArgument, isEnumArray) switch
{
(true, _, _) => context.Type,
(_, true, _) => context.Type.GenericTypeArguments.First(),
_ => context.Type.GetElementType()
};
StringBuilder stringBuilder = new("<p>Members:</p><ul>");
foreach (var enumValue in Enum.GetValues(enumType))
{
if (enumValue is Enum value)
{
stringBuilder.Append($"<li>{value} - {value:d}</li>");
}
}
schema.Description = stringBuilder.Append("</ul>").ToString();
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1723,15 +1723,68 @@ public void GetSwagger_Works_As_Expected_When_FromForm_Attribute_With_SwaggerIgn
Assert.Equal(new[] { nameof(SwaggerIngoreAnnotatedType.NotIgnoredString) }, mediaType.Encoding.Keys);
}

[Fact]
public void GetSwagger_Copies_Description_From_GeneratedSchema()
{
var propertyEnum = typeof(TypeWithDefaultAttributeOnEnum).GetProperty(nameof(TypeWithDefaultAttributeOnEnum.EnumWithDefault));
var modelMetadataForEnum = new DefaultModelMetadata(
new DefaultModelMetadataProvider(new FakeICompositeMetadataDetailsProvider()),
new FakeICompositeMetadataDetailsProvider(),
new DefaultMetadataDetails(ModelMetadataIdentity.ForProperty(propertyEnum, typeof(IntEnum), typeof(TypeWithDefaultAttributeOnEnum)), ModelAttributes.GetAttributesForProperty(typeof(TypeWithDefaultAttributeOnEnum), propertyEnum)));

var propertyEnumArray = typeof(TypeWithDefaultAttributeOnEnum).GetProperty(nameof(TypeWithDefaultAttributeOnEnum.EnumArrayWithDefault));
var modelMetadataForEnumArray = new DefaultModelMetadata(
new DefaultModelMetadataProvider(new FakeICompositeMetadataDetailsProvider()),
new FakeICompositeMetadataDetailsProvider(),
new DefaultMetadataDetails(ModelMetadataIdentity.ForProperty(propertyEnumArray, typeof(IntEnum[]), typeof(TypeWithDefaultAttributeOnEnum)), ModelAttributes.GetAttributesForProperty(typeof(TypeWithDefaultAttributeOnEnum), propertyEnumArray)));
var subject = Subject(
apiDescriptions:
[
ApiDescriptionFactory.Create<FakeController>(
c => nameof(c.ActionHavingFromFormAttributeWithSwaggerIgnore),
groupName: "v1",
httpMethod: "POST",
relativePath: "resource",
parameterDescriptions: new[]
{
new ApiParameterDescription
{
Name = nameof(TypeWithDefaultAttributeOnEnum.EnumWithDefault),
Source = BindingSource.Query,
Type = typeof(IntEnum),
ModelMetadata = modelMetadataForEnum
},
new ApiParameterDescription
{
Name = nameof(TypeWithDefaultAttributeOnEnum.EnumArrayWithDefault),
Source = BindingSource.Query,
Type = typeof(IntEnum[]),
ModelMetadata = modelMetadataForEnumArray
}
})
],
schemaFilters: [new TestEnumSchemaFilter()]
);
var document = subject.GetSwagger("v1");

var operation = document.Paths["/resource"].Operations[OperationType.Post];
Assert.NotEmpty(operation.Parameters);
Assert.Equal(nameof(TypeWithDefaultAttributeOnEnum.EnumWithDefault), operation.Parameters[0].Name);
Assert.Equal(document.Components.Schemas[nameof(IntEnum)].Description, operation.Parameters[0].Description);
Assert.Equal(nameof(TypeWithDefaultAttributeOnEnum.EnumArrayWithDefault), operation.Parameters[1].Name);
Assert.Equal(document.Components.Schemas[nameof(IntEnum)].Description, operation.Parameters[1].Description);
}

private static SwaggerGenerator Subject(
IEnumerable<ApiDescription> apiDescriptions,
SwaggerGeneratorOptions options = null,
IEnumerable<AuthenticationScheme> authenticationSchemes = null)
IEnumerable<AuthenticationScheme> authenticationSchemes = null,
List<ISchemaFilter> schemaFilters = null)
{
return new SwaggerGenerator(
options ?? DefaultOptions,
new FakeApiDescriptionGroupCollectionProvider(apiDescriptions),
new SchemaGenerator(new SchemaGeneratorOptions(), new JsonSerializerDataContractResolver(new JsonSerializerOptions())),
new SchemaGenerator(new SchemaGeneratorOptions() { SchemaFilters = schemaFilters ?? [] }, new JsonSerializerDataContractResolver(new JsonSerializerOptions())),
new FakeAuthenticationSchemeProvider(authenticationSchemes ?? Enumerable.Empty<AuthenticationScheme>())
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
{
Info: {
Title: Test API,
Version: V1
},
Paths: {
/resource: {
Operations: {
Post: {
Tags: [
{
Name: Fake,
UnresolvedReference: false
}
],
Parameters: [
{
UnresolvedReference: false,
Name: EnumWithDefault,
In: Query,
Description: <p>Members:</p><ul><li>Value2 - 2</li><li>Value4 - 4</li><li>Value8 - 8</li></ul>,
Required: false,
Deprecated: false,
AllowEmptyValue: false,
Style: Form,
Explode: true,
AllowReserved: false,
Schema: {
ReadOnly: false,
WriteOnly: false,
AdditionalPropertiesAllowed: true,
Nullable: false,
Deprecated: false,
UnresolvedReference: false,
Reference: {
IsFragrament: false,
Type: Schema,
Id: IntEnum,
IsExternal: false,
IsLocal: true,
ReferenceV3: #/components/schemas/IntEnum,
ReferenceV2: #/definitions/IntEnum
}
}
},
{
UnresolvedReference: false,
Name: EnumArrayWithDefault,
In: Query,
Description: <p>Members:</p><ul><li>Value2 - 2</li><li>Value4 - 4</li><li>Value8 - 8</li></ul>,
Required: false,
Deprecated: false,
AllowEmptyValue: false,
Style: Form,
Explode: true,
AllowReserved: false,
Schema: {
Type: array,
Description: <p>Members:</p><ul><li>Value2 - 2</li><li>Value4 - 4</li><li>Value8 - 8</li></ul>,
Default: [
{
Value: 4
}
],
ReadOnly: false,
WriteOnly: false,
Items: {
ReadOnly: false,
WriteOnly: false,
AdditionalPropertiesAllowed: true,
Nullable: false,
Deprecated: false,
UnresolvedReference: false,
Reference: {
IsFragrament: false,
Type: Schema,
Id: IntEnum,
IsExternal: false,
IsLocal: true,
ReferenceV3: #/components/schemas/IntEnum,
ReferenceV2: #/definitions/IntEnum
}
},
AdditionalPropertiesAllowed: true,
Nullable: false,
Deprecated: false,
UnresolvedReference: false
}
}
],
Responses: {
200: {
Description: OK,
UnresolvedReference: false
}
},
Deprecated: false
}
},
UnresolvedReference: false
}
},
Components: {
Schemas: {
IntEnum: {
Type: integer,
Format: int32,
Description: <p>Members:</p><ul><li>Value2 - 2</li><li>Value4 - 4</li><li>Value8 - 8</li></ul>,
ReadOnly: false,
WriteOnly: false,
AdditionalPropertiesAllowed: true,
Enum: [
{
Value: 2
},
{
Value: 4
},
{
Value: 8
}
],
Nullable: false,
Deprecated: false,
UnresolvedReference: false
}
}
},
HashCode: 29A4A89ADE3B75E921A9EF9CE77F3E1517293B167A0B1513F55C57F3E84194193E8B24F875170DE0524C73CF5AB9DDCDF6556F1BDC2E6EA1F8171340F4C0F0B1
}
Original file line number Diff line number Diff line change
Expand Up @@ -1102,15 +1102,63 @@ public Task ActionHavingFromFormAttributeWithSwaggerIgnore()
return Verifier.Verify(document);
}

[Fact]
public Task GetSwagger_Copies_Description_From_GeneratedSchema()
{
var propertyEnum = typeof(TypeWithDefaultAttributeOnEnum).GetProperty(nameof(TypeWithDefaultAttributeOnEnum.EnumWithDefault));
var modelMetadataForEnum = new DefaultModelMetadata(
new DefaultModelMetadataProvider(new FakeICompositeMetadataDetailsProvider()),
new FakeICompositeMetadataDetailsProvider(),
new DefaultMetadataDetails(ModelMetadataIdentity.ForProperty(propertyEnum, typeof(IntEnum), typeof(TypeWithDefaultAttributeOnEnum)), ModelAttributes.GetAttributesForProperty(typeof(TypeWithDefaultAttributeOnEnum), propertyEnum)));

var propertyEnumArray = typeof(TypeWithDefaultAttributeOnEnum).GetProperty(nameof(TypeWithDefaultAttributeOnEnum.EnumArrayWithDefault));
var modelMetadataForEnumArray = new DefaultModelMetadata(
new DefaultModelMetadataProvider(new FakeICompositeMetadataDetailsProvider()),
new FakeICompositeMetadataDetailsProvider(),
new DefaultMetadataDetails(ModelMetadataIdentity.ForProperty(propertyEnumArray, typeof(IntEnum[]), typeof(TypeWithDefaultAttributeOnEnum)), ModelAttributes.GetAttributesForProperty(typeof(TypeWithDefaultAttributeOnEnum), propertyEnumArray)));
var subject = Subject(
apiDescriptions:
[
ApiDescriptionFactory.Create<FakeController>(
c => nameof(c.ActionHavingFromFormAttributeWithSwaggerIgnore),
groupName: "v1",
httpMethod: "POST",
relativePath: "resource",
parameterDescriptions: new[]
{
new ApiParameterDescription
{
Name = nameof(TypeWithDefaultAttributeOnEnum.EnumWithDefault),
Source = BindingSource.Query,
Type = typeof(IntEnum),
ModelMetadata = modelMetadataForEnum
},
new ApiParameterDescription
{
Name = nameof(TypeWithDefaultAttributeOnEnum.EnumArrayWithDefault),
Source = BindingSource.Query,
Type = typeof(IntEnum[]),
ModelMetadata = modelMetadataForEnumArray
}
})
],
schemaFilters: [new TestEnumSchemaFilter()]
);
var document = subject.GetSwagger("v1");

return Verifier.Verify(document);
}

private static SwaggerGenerator Subject(
IEnumerable<ApiDescription> apiDescriptions,
SwaggerGeneratorOptions options = null,
IEnumerable<AuthenticationScheme> authenticationSchemes = null)
IEnumerable<ApiDescription> apiDescriptions,
SwaggerGeneratorOptions options = null,
IEnumerable<AuthenticationScheme> authenticationSchemes = null,
List<ISchemaFilter> schemaFilters = null)
{
return new SwaggerGenerator(
options ?? DefaultOptions,
new FakeApiDescriptionGroupCollectionProvider(apiDescriptions),
new SchemaGenerator(new SchemaGeneratorOptions(), new JsonSerializerDataContractResolver(new JsonSerializerOptions())),
new SchemaGenerator(new SchemaGeneratorOptions() { SchemaFilters = schemaFilters ?? [] }, new JsonSerializerDataContractResolver(new JsonSerializerOptions())),
new FakeAuthenticationSchemeProvider(authenticationSchemes ?? Enumerable.Empty<AuthenticationScheme>())
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,7 @@ public class TypeWithDefaultAttributeOnEnum
{
[DefaultValue(IntEnum.Value4)]
public IntEnum EnumWithDefault { get; set; }
[DefaultValue(new IntEnum[] { IntEnum.Value4 })]
public IntEnum[] EnumArrayWithDefault { get; set; }
}
}