Skip to content
This repository has been archived by the owner on Feb 18, 2024. It is now read-only.

Commit

Permalink
Implemented inheritdoc support for XML parser domaindrivendev/Swashbu…
Browse files Browse the repository at this point in the history
  • Loading branch information
Havunen committed Feb 12, 2024
1 parent 7078ab7 commit 8c301f3
Show file tree
Hide file tree
Showing 7 changed files with 206 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,7 @@ private static string ReadInnerValueAsString(XmlReader xmlReader, string endBloc
CheckCharacters = false,
IgnoreWhitespace = true
});
var membersWithInheritDoc = new List<(string, XmlCommentDescriptor)>();
var xmlMembers = new Dictionary<string, XmlCommentDescriptor>(StringComparer.Ordinal);

xmlReader.MoveToContent();
Expand Down Expand Up @@ -549,6 +550,11 @@ private static string ReadInnerValueAsString(XmlReader xmlReader, string endBloc
}
);
break;
case "inheritdoc":
xmlComment.InheritcDoc = xmlReader.GetAttribute("cref");
membersWithInheritDoc.Add((memberName, xmlComment));
xmlReader.Read();
break;
default:
xmlReader.Read();
break;
Expand All @@ -568,7 +574,47 @@ private static string ReadInnerValueAsString(XmlReader xmlReader, string endBloc
}
}

foreach (var (key, xmlComment) in membersWithInheritDoc)
{
FindFinalNodeWithInheritDoc(xmlComment, key, xmlMembers);
}

return xmlMembers;
}

private static XmlCommentDescriptor FindFinalNodeWithInheritDoc(
XmlCommentDescriptor initialNode,
string initialKey,
Dictionary<string, XmlCommentDescriptor> xmlMembers
)
{
if (initialNode == null || string.IsNullOrEmpty(initialNode.InheritcDoc))
{
return initialNode;
}

var currentNode = initialNode;
var currentKey = initialKey;
var i = 0;
while (!string.IsNullOrEmpty(currentNode.InheritcDoc))
{
if (!xmlMembers.TryGetValue(currentNode.InheritcDoc, out var inheritedNode))
{
break;
}

currentNode = inheritedNode;
xmlMembers[currentKey] = inheritedNode;
currentKey = inheritedNode.InheritcDoc;

// Prevent infinite loop
if (i++ > 100)
{
return initialNode;
}
}

return currentNode;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ public record XmlCommentDescriptor
public string Summary { get; set; }
public string Remarks { get; set; }
public string Example { get; set; }
public string InheritcDoc { get; set; }
public List<XmlParam> Params { get; set; }
public List<XmlResponse> Responses { get; set; }

Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System;

namespace DotSwashbuckle.AspNetCore.SwaggerGen.Test
{
/// <inheritdoc cref = "FakeControllerWithXmlComments" />
public class FakeControllerWithInfiniteLoopInheritDoc : FakeControllerWithXmlComments
{
/// <inheritdoc cref = "FakeControllerWithInfiniteLoopInheritDoc.ActionWithResponseTags"/>
public new void ActionWithSummaryAndRemarksTags()
{
throw new NotImplementedException();
}

/// <inheritdoc cref = "FakeControllerWithInfiniteLoopInheritDoc.ActionWithSummaryAndRemarksTags"/>
public new void ActionWithParamTags(string param1, string param2)
{
throw new NotImplementedException();
}

/// <inheritdoc cref = "FakeControllerWithInfiniteLoopInheritDoc.ActionWithParamTags"/>
public new void ActionWithResponseTags()
{
throw new NotImplementedException();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System;

namespace DotSwashbuckle.AspNetCore.SwaggerGen.Test
{
/// <inheritdoc cref = "FakeControllerWithXmlComments" />
public class FakeControllerWithInheritDoc : FakeControllerWithXmlComments
{
/// <inheritdoc cref = "FakeControllerWithXmlComments.ActionWithSummaryAndRemarksTags"/>
public new void ActionWithSummaryAndRemarksTags()
{
throw new NotImplementedException();
}

/// <inheritdoc cref = "FakeControllerWithXmlComments.ActionWithParamTags"/>
public new void ActionWithParamTags(string param1, string param2)
{
throw new NotImplementedException();
}

/// <inheritdoc cref = "FakeControllerWithXmlComments.ActionWithResponseTags"/>
public new void ActionWithResponseTags()
{
throw new NotImplementedException();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System;

namespace DotSwashbuckle.AspNetCore.SwaggerGen.Test
{
/// <inheritdoc cref = "FakeControllerWithXmlComments" />
public class FakeControllerWithRecursiveInheritDoc : FakeControllerWithXmlComments
{
/// <inheritdoc cref = "FakeControllerWithXmlComments.ActionWithSummaryAndRemarksTags"/>
public new void ActionWithSummaryAndRemarksTags()
{
throw new NotImplementedException();
}

/// <inheritdoc cref = "FakeControllerWithRecursiveInheritDoc.ActionWithSummaryAndRemarksTags"/>
public new void ActionWithParamTags(string param1, string param2)
{
throw new NotImplementedException();
}

/// <inheritdoc cref = "FakeControllerWithRecursiveInheritDoc.ActionWithParamTags"/>
public new void ActionWithResponseTags()
{
throw new NotImplementedException();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,18 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;
using Xunit;
using DotSwashbuckle.AspNetCore.SwaggerGen.Test.Fixtures;
using System;

namespace DotSwashbuckle.AspNetCore.SwaggerGen.Test
{
public class XmlCommentsDocumentFilterTests
{
[Fact]
public void Apply_SetsTagDescription_FromControllerSummaryTags()
[Theory]
[InlineData(typeof(FakeControllerWithXmlComments))]
[InlineData(typeof(FakeControllerWithInheritDoc))]
[InlineData(typeof(FakeControllerWithRecursiveInheritDoc))]
public void Apply_SetsTagDescription_FromControllerSummaryTags(Type fakeController)
{
var document = new OpenApiDocument();
var filterContext = new DocumentFilterContext(
Expand All @@ -22,16 +27,50 @@ public void Apply_SetsTagDescription_FromControllerSummaryTags()
{
ActionDescriptor = new ControllerActionDescriptor
{
ControllerTypeInfo = typeof(FakeControllerWithXmlComments).GetTypeInfo(),
ControllerName = nameof(FakeControllerWithXmlComments)
ControllerTypeInfo = fakeController.GetTypeInfo(),
ControllerName = nameof(fakeController)
}
},
new ApiDescription
{
ActionDescriptor = new ControllerActionDescriptor
{
ControllerTypeInfo = typeof(FakeControllerWithXmlComments).GetTypeInfo(),
ControllerName = nameof(FakeControllerWithXmlComments)
ControllerTypeInfo = fakeController.GetTypeInfo(),
ControllerName = nameof(fakeController)
}
}
},
null,
null);

Subject().Apply(document, filterContext);

Assert.Equal(1, document.Tags.Count);
Assert.Equal("Summary for FakeControllerWithXmlComments", document.Tags[0].Description);
}

[Theory]
[InlineData(typeof(FakeControllerWithInfiniteLoopInheritDoc))]
public void Apply_InfiniteLoopOnInheritDoc_StopsAndResultNull(Type fakeController)
{
var document = new OpenApiDocument();
var filterContext = new DocumentFilterContext(
new[]
{
new ApiDescription
{
ActionDescriptor = new ControllerActionDescriptor
{
ControllerTypeInfo = fakeController.GetTypeInfo(),
ControllerName = nameof(fakeController)
}
},
new ApiDescription
{
ActionDescriptor = new ControllerActionDescriptor
{
ControllerTypeInfo = fakeController.GetTypeInfo(),
ControllerName = nameof(fakeController)
}
}
},
Expand Down

0 comments on commit 8c301f3

Please sign in to comment.