Class XmlDocumentationOperationTransformer
Namespace: Momentum.ServiceDefaults.Api.OpenApi.Transformers
Assembly: Momentum.ServiceDefaults.Api.dll
Transforms OpenAPI Momentum by enriching them with XML documentation from action methods.
public class XmlDocumentationOperationTransformer : IOpenApiOperationTransformer
Inheritance
object ← XmlDocumentationOperationTransformer
Implements
Inherited Members
object.GetType(), object.MemberwiseClone(), object.ToString(), object.Equals(object?), object.Equals(object?, object?), object.ReferenceEquals(object?, object?), object.GetHashCode()
Remarks
XML Documentation Operation Transformer
Transformer Overview
This transformer enhances operation specifications with:
- Method XML Documentation: As operation summary and description
- Parameter Documentation: Including examples and default values
- Response Documentation: From XML response tags
- Return Type Documentation: For successful responses
- Default Response Descriptions: For common HTTP status codes
Operation Enhancement Process
XML Documentation Integration
private void EnrichOperation(OpenApiOperation operation, MethodInfo methodInfo)
{
operation.OperationId = methodInfo.Name;
var xmlDocs = xmlDocumentationService.GetMethodDocumentation(methodInfo);
if (xmlDocs is null)
return;
if (xmlDocs.Summary is not null)
{
operation.Summary = xmlDocs.Summary;
operation.Description = xmlDocs.Summary;
}
if (xmlDocs.Remarks is not null)
{
operation.Description += $"\n\n{xmlDocs.Remarks}";
}
Parameter Enhancement
private static void EnrichParameters(OpenApiOperation operation, XmlDocumentationInfo xmlDocs, MethodInfo methodInfo)
{
if (operation.Parameters is null)
return;
var parametersByName = methodInfo.GetParameters().ToDictionary(p => p.Name!, p => p);
foreach (var parameter in operation.Parameters)
{
if (xmlDocs.Parameters.TryGetValue(parameter.Name, out var paramDoc))
{
parameter.Description = paramDoc.Description;
}
Parameter Examples and Defaults
if (parametersByName.TryGetValue(parameter.Name, out var paramInfo))
{
if (paramDoc?.Example is not null)
{
parameter.Example = paramInfo.ParameterType.ConvertToOpenApiType(paramDoc.Example);
}
if (paramInfo.HasDefaultValue)
{
var defaultValue = paramInfo.DefaultValue?.ToString();
if (!string.IsNullOrEmpty(defaultValue))
{
parameter.Description = string.IsNullOrEmpty(parameter.Description)
? $"Default value: {defaultValue}"
: $"{parameter.Description} (Default: {defaultValue})";
}
}
}
Response Documentation
XML Response Tags
private static void EnrichResponses(OpenApiOperation operation, XmlDocumentationInfo xmlDocs)
{
ReplaceAutoProducedResponseToOperation(operation, xmlDocs);
foreach (var (responseCode, responseDoc) in xmlDocs.Responses)
{
if (!operation.Responses.TryGetValue(responseCode, out var response))
{
response = new OpenApiResponse();
operation.Responses[responseCode] = response;
}
response.Description = responseDoc;
}
Returns Documentation
if (xmlDocs.Returns is not null)
{
var successResponse = operation.Responses.FirstOrDefault(r => r.Key.StartsWith('2'));
if (successResponse.Key is not null)
successResponse.Value.Description ??= xmlDocs.Returns;
}
Auto-Produced Response Handling
Convention Integration
This method checks if the operation has an auto-produced successful response (e.g., 200 OK) added by the AutoProducesResponseTypeConvention
(only added if no other 2XX already exists) and replaces the status code with the actual documented successful response code from the XML documentation. If there are no documented successful response code, the auto-produced response is removed.
private static void ReplaceAutoProducedResponseToOperation(OpenApiOperation operation, XmlDocumentationInfo xmlDocs)
{
if (operation.Responses.TryGetValue(AutoProducesStatusCode, out var autoProducedResponse))
{
var successXmlResponse = xmlDocs.Responses.FirstOrDefault(r => r.Key.StartsWith('2'));
if (successXmlResponse.Key is not null)
{
operation.Responses[successXmlResponse.Key] = autoProducedResponse;
}
operation.Responses.Remove(AutoProducesStatusCode);
}
}
Default Response Descriptions
Standard HTTP Status Codes
private static string GetDefaultResponseDescription(string statusCode) =>
statusCode switch
{
"200" => "Success",
"201" => "Created",
"202" => "Accepted",
"204" => "No Content",
"400" => "Bad Request",
"401" => "Unauthorized",
"403" => "Forbidden",
"404" => "Not Found",
"409" => "Conflict",
"500" => "Internal Server Error",
"503" => "Service Unavailable",
_ => "Response"
};
Response Completion
// Ensure all responses have descriptions
foreach (var (statusCode, response) in operation.Responses.Where(r => r.Value.Description is null))
{
response.Description = GetDefaultResponseDescription(statusCode);
}
Error Handling and Safety
Exception Protection
public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransformerContext context, CancellationToken cancellationToken)
{
try
{
if (context.Description.ActionDescriptor is ControllerActionDescriptor controllerDescriptor)
{
EnrichOperation(operation, controllerDescriptor.MethodInfo);
}
}
catch (Exception ex)
{
logger.LogWarning(ex, "Failed to transform operation with XML documentation");
}
return Task.CompletedTask;
}
Type Conversion Integration
OpenAPI Type Conversion
parameter.Example = paramInfo.ParameterType.ConvertToOpenApiType(paramDoc.Example);
This utilizes extension methods for proper type conversion between .NET types and OpenAPI representations.
XML Documentation Structure
Parameter Documentation
/// <param name="userId" example="123e4567-e89b-12d3-a456-426614174000">The unique identifier of the user</param>
Response Documentation
/// <response code="200">User retrieved successfully</response>
/// <response code="404">User not found</response>
Returns Documentation
/// <returns>The user information</returns>
Integration Benefits
Enhanced API Documentation
- Rich Operation Descriptions: Detailed explanations from XML comments
- Parameter Guidance: Examples and default values for developers
- Response Clarity: Clear descriptions for all status codes
- Type Safety: Proper parameter examples with type conversion
Developer Experience
- IntelliSense Integration: IDE support for XML documentation
- Automated Generation: No manual OpenAPI annotation needed
- Consistency: Uniform documentation across operations
- Maintenance: Documentation updates with code changes
Constructors
XmlDocumentationOperationTransformer(ILogger<XmlDocumentationOperationTransformer>, IXmlDocumentationService)
Transforms OpenAPI Momentum by enriching them with XML documentation from action methods.
public XmlDocumentationOperationTransformer(ILogger<XmlDocumentationOperationTransformer> logger, IXmlDocumentationService xmlDocumentationService)
Parameters
logger
ILogger<XmlDocumentationOperationTransformer>
xmlDocumentationService
IXmlDocumentationService
Remarks
XML Documentation Operation Transformer
Transformer Overview
This transformer enhances operation specifications with:
- Method XML Documentation: As operation summary and description
- Parameter Documentation: Including examples and default values
- Response Documentation: From XML response tags
- Return Type Documentation: For successful responses
- Default Response Descriptions: For common HTTP status codes
Operation Enhancement Process
XML Documentation Integration
private void EnrichOperation(OpenApiOperation operation, MethodInfo methodInfo)
{
operation.OperationId = methodInfo.Name;
var xmlDocs = xmlDocumentationService.GetMethodDocumentation(methodInfo);
if (xmlDocs is null)
return;
if (xmlDocs.Summary is not null)
{
operation.Summary = xmlDocs.Summary;
operation.Description = xmlDocs.Summary;
}
if (xmlDocs.Remarks is not null)
{
operation.Description += $"\n\n{xmlDocs.Remarks}";
}
Parameter Enhancement
private static void EnrichParameters(OpenApiOperation operation, XmlDocumentationInfo xmlDocs, MethodInfo methodInfo)
{
if (operation.Parameters is null)
return;
var parametersByName = methodInfo.GetParameters().ToDictionary(p => p.Name!, p => p);
foreach (var parameter in operation.Parameters)
{
if (xmlDocs.Parameters.TryGetValue(parameter.Name, out var paramDoc))
{
parameter.Description = paramDoc.Description;
}
Parameter Examples and Defaults
if (parametersByName.TryGetValue(parameter.Name, out var paramInfo))
{
if (paramDoc?.Example is not null)
{
parameter.Example = paramInfo.ParameterType.ConvertToOpenApiType(paramDoc.Example);
}
if (paramInfo.HasDefaultValue)
{
var defaultValue = paramInfo.DefaultValue?.ToString();
if (!string.IsNullOrEmpty(defaultValue))
{
parameter.Description = string.IsNullOrEmpty(parameter.Description)
? $"Default value: {defaultValue}"
: $"{parameter.Description} (Default: {defaultValue})";
}
}
}
Response Documentation
XML Response Tags
private static void EnrichResponses(OpenApiOperation operation, XmlDocumentationInfo xmlDocs)
{
ReplaceAutoProducedResponseToOperation(operation, xmlDocs);
foreach (var (responseCode, responseDoc) in xmlDocs.Responses)
{
if (!operation.Responses.TryGetValue(responseCode, out var response))
{
response = new OpenApiResponse();
operation.Responses[responseCode] = response;
}
response.Description = responseDoc;
}
Returns Documentation
if (xmlDocs.Returns is not null)
{
var successResponse = operation.Responses.FirstOrDefault(r => r.Key.StartsWith('2'));
if (successResponse.Key is not null)
successResponse.Value.Description ??= xmlDocs.Returns;
}
Auto-Produced Response Handling
Convention Integration
This method checks if the operation has an auto-produced successful response (e.g., 200 OK) added by the AutoProducesResponseTypeConvention
(only added if no other 2XX already exists) and replaces the status code with the actual documented successful response code from the XML documentation. If there are no documented successful response code, the auto-produced response is removed.
private static void ReplaceAutoProducedResponseToOperation(OpenApiOperation operation, XmlDocumentationInfo xmlDocs)
{
if (operation.Responses.TryGetValue(AutoProducesStatusCode, out var autoProducedResponse))
{
var successXmlResponse = xmlDocs.Responses.FirstOrDefault(r => r.Key.StartsWith('2'));
if (successXmlResponse.Key is not null)
{
operation.Responses[successXmlResponse.Key] = autoProducedResponse;
}
operation.Responses.Remove(AutoProducesStatusCode);
}
}
Default Response Descriptions
Standard HTTP Status Codes
private static string GetDefaultResponseDescription(string statusCode) =>
statusCode switch
{
"200" => "Success",
"201" => "Created",
"202" => "Accepted",
"204" => "No Content",
"400" => "Bad Request",
"401" => "Unauthorized",
"403" => "Forbidden",
"404" => "Not Found",
"409" => "Conflict",
"500" => "Internal Server Error",
"503" => "Service Unavailable",
_ => "Response"
};
Response Completion
// Ensure all responses have descriptions
foreach (var (statusCode, response) in operation.Responses.Where(r => r.Value.Description is null))
{
response.Description = GetDefaultResponseDescription(statusCode);
}
Error Handling and Safety
Exception Protection
public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransformerContext context, CancellationToken cancellationToken)
{
try
{
if (context.Description.ActionDescriptor is ControllerActionDescriptor controllerDescriptor)
{
EnrichOperation(operation, controllerDescriptor.MethodInfo);
}
}
catch (Exception ex)
{
logger.LogWarning(ex, "Failed to transform operation with XML documentation");
}
return Task.CompletedTask;
}
Type Conversion Integration
OpenAPI Type Conversion
parameter.Example = paramInfo.ParameterType.ConvertToOpenApiType(paramDoc.Example);
This utilizes extension methods for proper type conversion between .NET types and OpenAPI representations.
XML Documentation Structure
Parameter Documentation
/// <param name="userId" example="123e4567-e89b-12d3-a456-426614174000">The unique identifier of the user</param>
Response Documentation
/// <response code="200">User retrieved successfully</response>
/// <response code="404">User not found</response>
Returns Documentation
/// <returns>The user information</returns>
Integration Benefits
Enhanced API Documentation
- Rich Operation Descriptions: Detailed explanations from XML comments
- Parameter Guidance: Examples and default values for developers
- Response Clarity: Clear descriptions for all status codes
- Type Safety: Proper parameter examples with type conversion
Developer Experience
- IntelliSense Integration: IDE support for XML documentation
- Automated Generation: No manual OpenAPI annotation needed
- Consistency: Uniform documentation across operations
- Maintenance: Documentation updates with code changes
Methods
TransformAsync(OpenApiOperation, OpenApiOperationTransformerContext, CancellationToken)
Transforms the specified OpenAPI operation.
public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransformerContext context, CancellationToken cancellationToken)
Parameters
operation
OpenApiOperation
The to modify.
context
OpenApiOperationTransformerContext
The associated with the .
cancellationToken
CancellationToken
The cancellation token to use.
Returns
The task object representing the asynchronous operation.