Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,29 @@ public class XmlCommentsSchemaFilter(IReadOnlyDictionary<string, XPathNavigator>

public void Apply(IOpenApiSchema schema, SchemaFilterContext context)
{
ApplyTypeTags(schema, context.Type);
ApplyTypeTags(schema, context);

if (context.MemberInfo != null)
{
ApplyMemberTags(schema, context);
}
}

private void ApplyTypeTags(IOpenApiSchema schema, Type type)
private void ApplyTypeTags(IOpenApiSchema schema, SchemaFilterContext context)
{
var typeMemberName = XmlCommentsNodeNameHelper.GetMemberNameForType(type);
var typeMemberName = XmlCommentsNodeNameHelper.GetMemberNameForType(context.Type);

if (!_xmlDocMembers.TryGetValue(typeMemberName, out var memberNode)) return;

var typeSummaryNode = memberNode.SelectFirstChild("summary");

if (typeSummaryNode != null)
if (typeSummaryNode != null && (context.MemberInfo is null || schema.Description is null))
{
// For a member's schema the type's summary is only a fallback: it must not
// overwrite a description a member's summary has already provided, such as by
// an XmlCommentsSchemaFilter for another XML comments file when comments are
// included from multiple assemblies.
// See https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/3240.
schema.Description = XmlCommentsTextHelper.Humanize(typeSummaryNode.InnerXml, _options?.XmlCommentEndOfLine);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,11 +179,100 @@ public void Apply_UsesInvariantCulture_WhenSettingExample(
Assert.Equal(expectedValue, schema.Example.GetValue<float>());
}

[Fact]
public void Apply_DoesNotOverridePropertySummary_WithTypeSummaryFromAnotherXmlCommentsDocument()
{
// Arrange
var propertyInfo = typeof(XmlAnnotatedType).GetProperty(nameof(XmlAnnotatedType.StringProperty));
var propertyMemberName = XmlCommentsNodeNameHelper.GetMemberNameForFieldOrProperty(propertyInfo);
var typeMemberName = XmlCommentsNodeNameHelper.GetMemberNameForType(propertyInfo.PropertyType);

// Simulates IncludeXmlComments() being called once for the assembly declaring the
// property and once for the assembly declaring the property's type.
// See https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/3240.
var propertyAssemblyFilter = Subject(
$"""
<doc>
<members>
<member name="{propertyMemberName}">
<summary>Summary for StringProperty</summary>
</member>
</members>
</doc>
""");

var typeAssemblyFilter = Subject(
$"""
<doc>
<members>
<member name="{typeMemberName}">
<summary>Summary for the property's type</summary>
</member>
</members>
</doc>
""");

var schema = new OpenApiSchema();
var filterContext = new SchemaFilterContext(propertyInfo.PropertyType, null, null, memberInfo: propertyInfo);

// Act
propertyAssemblyFilter.Apply(schema, filterContext);
typeAssemblyFilter.Apply(schema, filterContext);

// Assert
Assert.Equal("Summary for StringProperty", schema.Description);
}

[Fact]
public void Apply_SetsDescription_FromTypeSummaryTagInAnotherXmlCommentsDocument_WhenPropertyHasNoSummary()
{
// Arrange
var propertyInfo = typeof(XmlAnnotatedType).GetProperty(nameof(XmlAnnotatedType.StringProperty));
var typeMemberName = XmlCommentsNodeNameHelper.GetMemberNameForType(propertyInfo.PropertyType);

var propertyAssemblyFilter = Subject(
"""
<doc>
<members>
</members>
</doc>
""");

var typeAssemblyFilter = Subject(
$"""
<doc>
<members>
<member name="{typeMemberName}">
<summary>Summary for the property's type</summary>
</member>
</members>
</doc>
""");

var schema = new OpenApiSchema();
var filterContext = new SchemaFilterContext(propertyInfo.PropertyType, null, null, memberInfo: propertyInfo);

// Act
propertyAssemblyFilter.Apply(schema, filterContext);
typeAssemblyFilter.Apply(schema, filterContext);

// Assert
Assert.Equal("Summary for the property's type", schema.Description);
}

private static XmlCommentsSchemaFilter Subject()
{
using var xml = File.OpenText(typeof(FakeControllerWithXmlComments).Assembly.GetName().Name + ".xml");
var document = new XPathDocument(xml);
var members = XmlCommentsDocumentHelper.CreateMemberDictionary(document);
return new(members, new());
}

private static XmlCommentsSchemaFilter Subject(string xml)
{
using var reader = new StringReader(xml);
var document = new XPathDocument(reader);
var members = XmlCommentsDocumentHelper.CreateMemberDictionary(document);
return new(members, new());
}
}