Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
2 changes: 1 addition & 1 deletion src/Refitter.Core/RefitMultipleInterfaceByTagGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public override IEnumerable<GeneratedCode> GenerateCode()
if (!interfacesByGroup.TryGetValue(kv.Key, out var sb))
{
interfacesByGroup[kv.Key] = sb = new StringBuilder();
this.docGenerator.AppendInterfaceDocumentation(operation, sb);
this.docGenerator.AppendInterfaceDocumentation(document, operation, sb);

interfaceName = GetInterfaceName(kv.Key);
sb.AppendLine($$"""
Expand Down
2 changes: 1 addition & 1 deletion src/Refitter.Core/RefitMultipleInterfaceGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public override IEnumerable<GeneratedCode> GenerateCode()
: "Execute";
var code = new StringBuilder();

this.docGenerator.AppendInterfaceDocumentation(operation, code);
this.docGenerator.AppendInterfaceDocumentation(document, operation, code);

var interfaceName = GetInterfaceName(kv, verb, operation);
code.AppendLine($$"""
Expand Down
13 changes: 11 additions & 2 deletions src/Refitter.Core/XmlDocumentationGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,25 @@ 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.
/// </summary>
/// <param name="document">The parent document of the group.</param>
/// <param name="group">The OpenAPI definition of the interface.</param>
/// <param name="code">The builder to append the documentation to.</param>
public void AppendInterfaceDocumentation(OpenApiOperation group, StringBuilder code)
public void AppendInterfaceDocumentation(OpenApiDocument document, OpenApiOperation group, StringBuilder code)
Comment thread
DJ4ddi marked this conversation as resolved.
Outdated
{
if (!_settings.GenerateXmlDocCodeComments)
{
return;
}

this.AppendXmlCommentBlock("summary", group?.Summary ?? "No summary available", code, indent: Separator);
var controllerTag = document.Tags.FirstOrDefault(tag => group.Tags.Contains(tag.Name));
var content = controllerTag?.Description;
if (string.IsNullOrEmpty(content))
{
content = group.Summary;
}
Comment thread
DJ4ddi marked this conversation as resolved.
Outdated

content ??= "No summary available";
this.AppendXmlCommentBlock("summary", EscapeSymbols(content), code, indent: Separator);
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

namespace Refitter.Tests.AdditionalFiles.ByEndpoint
{
/// <summary>Update an existing pet</summary>
/// <summary>Everything about your Pets</summary>
Comment thread
DJ4ddi marked this conversation as resolved.
Outdated
[System.CodeDom.Compiler.GeneratedCode("Refitter", "1.0.0.0")]
public partial interface IUpdatePetEndpoint
{
Expand Down Expand Up @@ -50,7 +50,7 @@ public partial interface IUpdatePetEndpoint
Task<Pet> Execute([Body] Pet body, CancellationToken cancellationToken = default);
}

/// <summary>Add a new pet to the store</summary>
/// <summary>Everything about your Pets</summary>
[System.CodeDom.Compiler.GeneratedCode("Refitter", "1.0.0.0")]
public partial interface IAddPetEndpoint
{
Expand All @@ -77,7 +77,7 @@ public partial interface IAddPetEndpoint
Task<Pet> Execute([Body] Pet body, CancellationToken cancellationToken = default);
}

/// <summary>Finds Pets by status</summary>
/// <summary>Everything about your Pets</summary>
[System.CodeDom.Compiler.GeneratedCode("Refitter", "1.0.0.0")]
public partial interface IFindPetsByStatusEndpoint
{
Expand All @@ -103,7 +103,7 @@ public partial interface IFindPetsByStatusEndpoint
Task<ICollection<Pet>> Execute([Query] Status? status, CancellationToken cancellationToken = default);
}

/// <summary>Finds Pets by tags</summary>
/// <summary>Everything about your Pets</summary>
[System.CodeDom.Compiler.GeneratedCode("Refitter", "1.0.0.0")]
public partial interface IFindPetsByTagsEndpoint
{
Expand All @@ -129,7 +129,7 @@ public partial interface IFindPetsByTagsEndpoint
Task<ICollection<Pet>> Execute([Query(CollectionFormat.Multi)] IEnumerable<string> tags, CancellationToken cancellationToken = default);
}

/// <summary>Find pet by ID</summary>
/// <summary>Everything about your Pets</summary>
[System.CodeDom.Compiler.GeneratedCode("Refitter", "1.0.0.0")]
public partial interface IGetPetByIdEndpoint
{
Expand Down Expand Up @@ -159,7 +159,7 @@ public partial interface IGetPetByIdEndpoint
Task<Pet> Execute(long petId, CancellationToken cancellationToken = default);
}

/// <summary>Updates a pet in the store with form data</summary>
/// <summary>Everything about your Pets</summary>
[System.CodeDom.Compiler.GeneratedCode("Refitter", "1.0.0.0")]
public partial interface IUpdatePetWithFormEndpoint
{
Expand All @@ -186,12 +186,11 @@ public partial interface IUpdatePetWithFormEndpoint
Task Execute(long petId, [Query] string name, [Query] string status, CancellationToken cancellationToken = default);
}

/// <summary>Deletes a pet</summary>
/// <summary>Everything about your Pets</summary>
[System.CodeDom.Compiler.GeneratedCode("Refitter", "1.0.0.0")]
public partial interface IDeletePetEndpoint
{
/// <summary>Deletes a pet</summary>
/// <param name="api_key">api_key parameter</param>
/// <param name="petId">Pet id to delete</param>
/// <param name="cancellationToken">The cancellation token to cancel the request.</param>
/// <returns>A <see cref="Task"/> that completes when the request is finished.</returns>
Expand All @@ -212,14 +211,13 @@ public partial interface IDeletePetEndpoint
Task Execute(long petId, [Header("api_key")] string api_key, CancellationToken cancellationToken = default);
}

/// <summary>uploads an image</summary>
/// <summary>Everything about your Pets</summary>
[System.CodeDom.Compiler.GeneratedCode("Refitter", "1.0.0.0")]
public partial interface IUploadFileEndpoint
{
/// <summary>uploads an image</summary>
/// <param name="petId">ID of pet to update</param>
/// <param name="additionalMetadata">Additional Metadata</param>
/// <param name="body">body parameter</param>
/// <param name="cancellationToken">The cancellation token to cancel the request.</param>
/// <returns>
/// A <see cref="Task"/> representing the <see cref="IApiResponse"/> instance containing the result:
Expand All @@ -239,7 +237,7 @@ public partial interface IUploadFileEndpoint
Task<ApiResponse> Execute(long petId, [Query] string additionalMetadata, StreamPart body, CancellationToken cancellationToken = default);
}

/// <summary>Returns pet inventories by status</summary>
/// <summary>Operations about user</summary>
[System.CodeDom.Compiler.GeneratedCode("Refitter", "1.0.0.0")]
public partial interface IGetInventoryEndpoint
{
Expand All @@ -252,13 +250,12 @@ public partial interface IGetInventoryEndpoint
Task<IDictionary<string, int>> Execute(CancellationToken cancellationToken = default);
}

/// <summary>Place an order for a pet</summary>
/// <summary>Operations about user</summary>
[System.CodeDom.Compiler.GeneratedCode("Refitter", "1.0.0.0")]
public partial interface IPlaceOrderEndpoint
{
/// <summary>Place an order for a pet</summary>
/// <remarks>Place a new order in the store</remarks>
/// <param name="body">body parameter</param>
/// <param name="cancellationToken">The cancellation token to cancel the request.</param>
/// <returns>successful operation</returns>
/// <exception cref="ApiException">
Expand All @@ -279,7 +276,7 @@ public partial interface IPlaceOrderEndpoint
Task<Order> Execute([Body] Order body, CancellationToken cancellationToken = default);
}

/// <summary>Find purchase order by ID</summary>
/// <summary>Operations about user</summary>
[System.CodeDom.Compiler.GeneratedCode("Refitter", "1.0.0.0")]
public partial interface IGetOrderByIdEndpoint
{
Expand Down Expand Up @@ -309,7 +306,7 @@ public partial interface IGetOrderByIdEndpoint
Task<Order> Execute(long orderId, CancellationToken cancellationToken = default);
}

/// <summary>Delete purchase order by ID</summary>
/// <summary>Operations about user</summary>
[System.CodeDom.Compiler.GeneratedCode("Refitter", "1.0.0.0")]
public partial interface IDeleteOrderEndpoint
{
Expand Down Expand Up @@ -339,7 +336,7 @@ public partial interface IDeleteOrderEndpoint
Task Execute(long orderId, CancellationToken cancellationToken = default);
}

/// <summary>Create user</summary>
/// <summary>Access to Petstore orders</summary>
[System.CodeDom.Compiler.GeneratedCode("Refitter", "1.0.0.0")]
public partial interface ICreateUserEndpoint
{
Expand All @@ -354,13 +351,12 @@ public partial interface ICreateUserEndpoint
Task<User> Execute([Body] User body, CancellationToken cancellationToken = default);
}

/// <summary>Creates list of users with given input array</summary>
/// <summary>Access to Petstore orders</summary>
[System.CodeDom.Compiler.GeneratedCode("Refitter", "1.0.0.0")]
public partial interface ICreateUsersWithListInputEndpoint
{
/// <summary>Creates list of users with given input array</summary>
/// <remarks>Creates list of users with given input array</remarks>
/// <param name="body">body parameter</param>
/// <param name="cancellationToken">The cancellation token to cancel the request.</param>
/// <returns>Successful operation</returns>
/// <exception cref="ApiException">Thrown when the request returns a non-success status code.</exception>
Expand All @@ -369,7 +365,7 @@ public partial interface ICreateUsersWithListInputEndpoint
Task<User> Execute([Body] IEnumerable<User> body, CancellationToken cancellationToken = default);
}

/// <summary>Logs user into the system</summary>
/// <summary>Access to Petstore orders</summary>
[System.CodeDom.Compiler.GeneratedCode("Refitter", "1.0.0.0")]
public partial interface ILoginUserEndpoint
{
Expand All @@ -395,7 +391,7 @@ public partial interface ILoginUserEndpoint
Task<string> Execute([Query] string username, [Query] string password, CancellationToken cancellationToken = default);
}

/// <summary>Logs out current logged in user session</summary>
/// <summary>Access to Petstore orders</summary>
[System.CodeDom.Compiler.GeneratedCode("Refitter", "1.0.0.0")]
public partial interface ILogoutUserEndpoint
{
Expand All @@ -407,7 +403,7 @@ public partial interface ILogoutUserEndpoint
Task Execute(CancellationToken cancellationToken = default);
}

/// <summary>Get user by user name</summary>
/// <summary>Access to Petstore orders</summary>
[System.CodeDom.Compiler.GeneratedCode("Refitter", "1.0.0.0")]
public partial interface IGetUserByNameEndpoint
{
Expand Down Expand Up @@ -436,7 +432,7 @@ public partial interface IGetUserByNameEndpoint
Task<User> Execute(string username, CancellationToken cancellationToken = default);
}

/// <summary>Update user</summary>
/// <summary>Access to Petstore orders</summary>
[System.CodeDom.Compiler.GeneratedCode("Refitter", "1.0.0.0")]
public partial interface IUpdateUserEndpoint
{
Expand All @@ -452,7 +448,7 @@ public partial interface IUpdateUserEndpoint
Task Execute(string username, [Body] User body, CancellationToken cancellationToken = default);
}

/// <summary>Delete user</summary>
/// <summary>Access to Petstore orders</summary>
[System.CodeDom.Compiler.GeneratedCode("Refitter", "1.0.0.0")]
public partial interface IDeleteUserEndpoint
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

namespace Refitter.Tests.AdditionalFiles.ByTag
{
/// <summary>Update an existing pet</summary>
/// <summary>Everything about your Pets</summary>
[System.CodeDom.Compiler.GeneratedCode("Refitter", "1.0.0.0")]
public partial interface IPetApi
{
Expand Down Expand Up @@ -195,7 +195,7 @@ public partial interface IPetApi
Task<ApiResponse> UploadFile(long petId, [Query] string additionalMetadata, StreamPart body);
}

/// <summary>Returns pet inventories by status</summary>
/// <summary>Operations about user</summary>
[System.CodeDom.Compiler.GeneratedCode("Refitter", "1.0.0.0")]
public partial interface IStoreApi
{
Expand Down Expand Up @@ -276,7 +276,7 @@ public partial interface IStoreApi
Task DeleteOrder(long orderId);
}

/// <summary>Create user</summary>
/// <summary>Access to Petstore orders</summary>
[System.CodeDom.Compiler.GeneratedCode("Refitter", "1.0.0.0")]
public partial interface IUserApi
{
Expand Down
17 changes: 15 additions & 2 deletions src/Refitter.Tests/XmlDocumentationGeneratorTests.cs
Comment thread
DJ4ddi marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -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.AppendInterfaceDocumentation(new OpenApiDocument(), interfaceDefinition, docs);
docs.ToString().Trim().Should().Be("/// <summary>Test</summary>");
}

Expand All @@ -31,12 +31,25 @@ 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.AppendInterfaceDocumentation(new OpenApiDocument(), interfaceDefinition, docs);
docs.ToString().Trim().Should().NotBe("/// <summary>Test</summary>");
docs.ToString().Trim().Should().Contain("<summary>")
.And.Contain("Test");
}

[Test]
public void Can_Generate_Interface_Doc_From_Controller_Tag()
{
var docs = new StringBuilder();
var interfaceDefinition = new OpenApiOperation { Summary = "Test", Tags = ["TestController"] };
var controllerTag = new OpenApiTag { Name = "TestController", Description = "TestControllerDescription" };
var document = new OpenApiDocument { Tags = [controllerTag] };

this._generator.AppendInterfaceDocumentation(document, interfaceDefinition, docs);

docs.ToString().Trim().Should().Be("/// <summary>TestControllerDescription</summary>");
}
Comment on lines +40 to +50

Copilot AI Feb 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding test cases for edge scenarios such as: tags with null or empty descriptions, tags that don't exist in the document, and tag descriptions containing XML special characters (e.g., "<", ">", "&") to verify the EscapeSymbols functionality works correctly in this context.

Copilot uses AI. Check for mistakes.

[Test]
public void Can_Generate_Method_Summary()
{
Expand Down
Loading