diff --git a/src/Refitter.Core/IdentifierUtils.cs b/src/Refitter.Core/IdentifierUtils.cs
index 223bb9fde..a774f8db8 100644
--- a/src/Refitter.Core/IdentifierUtils.cs
+++ b/src/Refitter.Core/IdentifierUtils.cs
@@ -53,4 +53,14 @@ public static string Sanitize(this string value)
return string.Join(string.Empty, value.Split(IllegalSymbols, StringSplitOptions.RemoveEmptyEntries))
.Trim(dash);
}
+
+ ///
+ /// Sanitizes and formats controller tags for identifier usage.
+ ///
+ /// The tag to sanitize.
+ /// A sanitized, title-cased identifier string.
+ public static string SanitizeControllerTag(this string tag)
+ {
+ return tag.Sanitize().CapitalizeFirstCharacter();
+ }
}
diff --git a/src/Refitter.Core/RefitInterfaceGenerator.cs b/src/Refitter.Core/RefitInterfaceGenerator.cs
index dc5623080..68cb74d0e 100644
--- a/src/Refitter.Core/RefitInterfaceGenerator.cs
+++ b/src/Refitter.Core/RefitInterfaceGenerator.cs
@@ -363,7 +363,7 @@ private string GenerateInterfaceDeclaration(out string interfaceName)
var modifier = settings.TypeAccessibility.ToString().ToLowerInvariant();
var code = new StringBuilder();
- docGenerator.AppendInterfaceDocumentation(document, code);
+ docGenerator.AppendSingleInterfaceDocumentation(document, code);
code.Append($"""
{Separator}{GetGeneratedCodeAttribute()}
{Separator}{modifier} partial interface {interfaceName}{inheritance}
diff --git a/src/Refitter.Core/RefitMultipleInterfaceByTagGenerator.cs b/src/Refitter.Core/RefitMultipleInterfaceByTagGenerator.cs
index d379a16c3..b3b2afae9 100644
--- a/src/Refitter.Core/RefitMultipleInterfaceByTagGenerator.cs
+++ b/src/Refitter.Core/RefitMultipleInterfaceByTagGenerator.cs
@@ -49,7 +49,7 @@ public override IEnumerable GenerateCode()
if (!interfacesByGroup.TryGetValue(kv.Key, out var sb))
{
interfacesByGroup[kv.Key] = sb = new StringBuilder();
- this.docGenerator.AppendInterfaceDocumentation(operation, sb);
+ this.docGenerator.AppendInterfaceDocumentationByTag(document, kv.Key, sb);
interfaceName = GetInterfaceName(kv.Key);
sb.AppendLine($$"""
@@ -131,8 +131,7 @@ private string GetGroupName(OpenApiOperation operation, string ungroupedTitle)
{
if (operation.Tags.FirstOrDefault() is string group && !string.IsNullOrWhiteSpace(group))
{
- return IdentifierUtils.Sanitize(group)
- .CapitalizeFirstCharacter();
+ return group.SanitizeControllerTag();
}
return ungroupedTitle;
diff --git a/src/Refitter.Core/RefitMultipleInterfaceGenerator.cs b/src/Refitter.Core/RefitMultipleInterfaceGenerator.cs
index 6f744fcf2..224356a30 100644
--- a/src/Refitter.Core/RefitMultipleInterfaceGenerator.cs
+++ b/src/Refitter.Core/RefitMultipleInterfaceGenerator.cs
@@ -36,7 +36,7 @@ public override IEnumerable GenerateCode()
: "Execute";
var code = new StringBuilder();
- this.docGenerator.AppendInterfaceDocumentation(operation, code);
+ this.docGenerator.AppendInterfaceDocumentationByEndpoint(operation, code);
var interfaceName = GetInterfaceName(kv, verb, operation);
code.AppendLine($$"""
diff --git a/src/Refitter.Core/XmlDocumentationGenerator.cs b/src/Refitter.Core/XmlDocumentationGenerator.cs
index c492ab7c9..9f798000a 100644
--- a/src/Refitter.Core/XmlDocumentationGenerator.cs
+++ b/src/Refitter.Core/XmlDocumentationGenerator.cs
@@ -19,6 +19,11 @@ public class XmlDocumentationGenerator
///
private const string Separator = " ";
+ ///
+ /// The name of the XML documentation tag used for summaries.
+ ///
+ private const string SummaryTag = "summary";
+
///
/// Instantiates a new instance of the class.
///
@@ -29,39 +34,62 @@ internal XmlDocumentationGenerator(RefitGeneratorSettings settings)
}
///
- /// Appends XML docs for the given interface definition to the given code builder.
- /// This uses the OpenAPI operation info to generate the summary and remarks.
+ /// Generates an interface description from the tags of the given OpenAPI document and appends it to the builder.
+ ///
+ /// The parent document of the controller.
+ /// The controller tag that the endpoints were grouped by.
+ /// The builder to append the documentation to.
+ public void AppendInterfaceDocumentationByTag(OpenApiDocument document, string tag, StringBuilder code)
+ {
+ if (!_settings.GenerateXmlDocCodeComments)
+ {
+ return;
+ }
+
+ var controllerTag = document.Tags.FirstOrDefault(t => t.Name.SanitizeControllerTag() == tag);
+ var controllerDescription = controllerTag?.Description;
+ if (!string.IsNullOrEmpty(controllerDescription))
+ {
+ this.AppendXmlCommentBlock(SummaryTag, EscapeSymbols(controllerDescription), code, indent: Separator);
+ }
+ }
+
+ ///
+ /// Generates an interface description from the summary of the given endpoint and appends it to the builder.
///
- /// The OpenAPI definition of the interface.
+ /// The OpenAPI definition of the endpoint.
/// The builder to append the documentation to.
- public void AppendInterfaceDocumentation(OpenApiOperation group, StringBuilder code)
+ public void AppendInterfaceDocumentationByEndpoint(OpenApiOperation endpoint, StringBuilder code)
{
if (!_settings.GenerateXmlDocCodeComments)
{
return;
}
- this.AppendXmlCommentBlock("summary", group?.Summary ?? "No summary available", code, indent: Separator);
+ var summary = endpoint.Summary;
+ if (!string.IsNullOrEmpty(summary))
+ {
+ this.AppendXmlCommentBlock(SummaryTag, EscapeSymbols(summary), code, indent: Separator);
+ }
}
///
- /// Appends XML docs for the given interface definition to the given code builder.
- /// This uses the OpenAPI document's info description as the summary.
+ /// Generates an interface description from the title of the given document and appends it to the builder.
///
- /// The OpenAPI definition of the interface.
+ /// The OpenAPI definition of the document.
/// The builder to append the documentation to.
- public void AppendInterfaceDocumentation(OpenApiDocument document, StringBuilder code)
+ public void AppendSingleInterfaceDocumentation(OpenApiDocument document, StringBuilder code)
{
if (!_settings.GenerateXmlDocCodeComments)
{
return;
}
- this.AppendXmlCommentBlock(
- "summary",
- document.Info?.Title ?? "Refit interface - no description available",
- code,
- indent: Separator);
+ var title = document.Info?.Title;
+ if (!string.IsNullOrEmpty(title))
+ {
+ this.AppendXmlCommentBlock(SummaryTag, EscapeSymbols(title), code, indent: Separator);
+ }
}
///
@@ -85,7 +113,7 @@ public void AppendMethodDocumentation(
return;
if (!string.IsNullOrWhiteSpace(method.Summary))
- this.AppendXmlCommentBlock("summary", EscapeSymbols(method.Summary), code);
+ this.AppendXmlCommentBlock(SummaryTag, EscapeSymbols(method.Summary), code);
if (!string.IsNullOrWhiteSpace(method.Description))
this.AppendXmlCommentBlock("remarks", EscapeSymbols(method.Description), code);
diff --git a/src/Refitter.SourceGenerator.Tests/AdditionalFiles/Generated/SwaggerPetstoreMultipleInterfaces.g.cs b/src/Refitter.SourceGenerator.Tests/AdditionalFiles/Generated/SwaggerPetstoreMultipleInterfaces.g.cs
index f9aba24c0..cae33f5ad 100644
--- a/src/Refitter.SourceGenerator.Tests/AdditionalFiles/Generated/SwaggerPetstoreMultipleInterfaces.g.cs
+++ b/src/Refitter.SourceGenerator.Tests/AdditionalFiles/Generated/SwaggerPetstoreMultipleInterfaces.g.cs
@@ -191,7 +191,6 @@ public partial interface IUpdatePetWithFormEndpoint
public partial interface IDeletePetEndpoint
{
/// Deletes a pet
- /// api_key parameter
/// Pet id to delete
/// The cancellation token to cancel the request.
/// A that completes when the request is finished.
@@ -219,7 +218,6 @@ public partial interface IUploadFileEndpoint
/// uploads an image
/// ID of pet to update
/// Additional Metadata
- /// body parameter
/// The cancellation token to cancel the request.
///
/// A representing the instance containing the result:
@@ -258,7 +256,6 @@ public partial interface IPlaceOrderEndpoint
{
/// Place an order for a pet
/// Place a new order in the store
- /// body parameter
/// The cancellation token to cancel the request.
/// successful operation
///
@@ -360,7 +357,6 @@ public partial interface ICreateUsersWithListInputEndpoint
{
/// Creates list of users with given input array
/// Creates list of users with given input array
- /// body parameter
/// The cancellation token to cancel the request.
/// Successful operation
/// Thrown when the request returns a non-success status code.
diff --git a/src/Refitter.SourceGenerator.Tests/AdditionalFiles/Generated/SwaggerPetstoreMultipleInterfacesByTag.g.cs b/src/Refitter.SourceGenerator.Tests/AdditionalFiles/Generated/SwaggerPetstoreMultipleInterfacesByTag.g.cs
index f6ab4e9ef..c950f32c2 100644
--- a/src/Refitter.SourceGenerator.Tests/AdditionalFiles/Generated/SwaggerPetstoreMultipleInterfacesByTag.g.cs
+++ b/src/Refitter.SourceGenerator.Tests/AdditionalFiles/Generated/SwaggerPetstoreMultipleInterfacesByTag.g.cs
@@ -14,7 +14,7 @@
namespace Refitter.Tests.AdditionalFiles.ByTag
{
- /// Update an existing pet
+ /// Everything about your Pets
[System.CodeDom.Compiler.GeneratedCode("Refitter", "1.0.0.0")]
public partial interface IPetApi
{
@@ -195,7 +195,7 @@ public partial interface IPetApi
Task UploadFile(long petId, [Query] string additionalMetadata, StreamPart body);
}
- /// Returns pet inventories by status
+ /// Operations about user
[System.CodeDom.Compiler.GeneratedCode("Refitter", "1.0.0.0")]
public partial interface IStoreApi
{
@@ -276,7 +276,7 @@ public partial interface IStoreApi
Task DeleteOrder(long orderId);
}
- /// Create user
+ /// Access to Petstore orders
[System.CodeDom.Compiler.GeneratedCode("Refitter", "1.0.0.0")]
public partial interface IUserApi
{
diff --git a/src/Refitter.SourceGenerator.Tests/AdditionalFiles/Generated/UseJsonInheritanceConverter.g.cs b/src/Refitter.SourceGenerator.Tests/AdditionalFiles/Generated/UseJsonInheritanceConverter.g.cs
index 3e9b0f840..81e0efe25 100644
--- a/src/Refitter.SourceGenerator.Tests/AdditionalFiles/Generated/UseJsonInheritanceConverter.g.cs
+++ b/src/Refitter.SourceGenerator.Tests/AdditionalFiles/Generated/UseJsonInheritanceConverter.g.cs
@@ -12,7 +12,6 @@
namespace Refitter.Tests.UseJsonInheritanceConverter
{
- /// Refit interface - no description available
[System.CodeDom.Compiler.GeneratedCode("Refitter", "1.0.0.0")]
public partial interface IApiClient
{
diff --git a/src/Refitter.SourceGenerator.Tests/AdditionalFiles/Generated/UsePolymorphicSerialization.g.cs b/src/Refitter.SourceGenerator.Tests/AdditionalFiles/Generated/UsePolymorphicSerialization.g.cs
index b20a2322a..9bb27f74b 100644
--- a/src/Refitter.SourceGenerator.Tests/AdditionalFiles/Generated/UsePolymorphicSerialization.g.cs
+++ b/src/Refitter.SourceGenerator.Tests/AdditionalFiles/Generated/UsePolymorphicSerialization.g.cs
@@ -12,7 +12,6 @@
namespace Refitter.Tests.UsePolymorphicSerialization
{
- /// Refit interface - no description available
[System.CodeDom.Compiler.GeneratedCode("Refitter", "1.0.0.0")]
public partial interface IApiClient
{
diff --git a/src/Refitter.Tests/XmlDocumentationGeneratorTests.cs b/src/Refitter.Tests/XmlDocumentationGeneratorTests.cs
index eaf8f6568..2c3550321 100644
--- a/src/Refitter.Tests/XmlDocumentationGeneratorTests.cs
+++ b/src/Refitter.Tests/XmlDocumentationGeneratorTests.cs
@@ -22,7 +22,7 @@ public void Can_Generate_Interface_Doc_Without_Linebreaks()
{
var docs = new StringBuilder();
var interfaceDefinition = new OpenApiOperation { Summary = "Test", };
- this._generator.AppendInterfaceDocumentation(interfaceDefinition, docs);
+ this._generator.AppendInterfaceDocumentationByEndpoint(interfaceDefinition, docs);
docs.ToString().Trim().Should().Be("/// Test");
}
@@ -31,12 +31,24 @@ public void Can_Generate_Interface_Doc_With_Linebreaks()
{
var docs = new StringBuilder();
var interfaceDefinition = new OpenApiOperation { Summary = "Test\n", };
- this._generator.AppendInterfaceDocumentation(interfaceDefinition, docs);
+ this._generator.AppendInterfaceDocumentationByEndpoint(interfaceDefinition, docs);
docs.ToString().Trim().Should().NotBe("/// Test");
docs.ToString().Trim().Should().Contain("")
.And.Contain("Test");
}
+ [Test]
+ public void Can_Generate_Interface_Doc_From_Controller_Tag()
+ {
+ var docs = new StringBuilder();
+ var controllerTag = new OpenApiTag { Name = "TestController", Description = "TestControllerDescription" };
+ var document = new OpenApiDocument { Tags = [controllerTag] };
+
+ this._generator.AppendInterfaceDocumentationByTag(document, "TestController", docs);
+
+ docs.ToString().Trim().Should().Be("/// TestControllerDescription");
+ }
+
[Test]
public void Can_Generate_Method_Summary()
{