Files
DocumentService/DocumentService.API/Configuration/DualInputDocumentFilter.cs
TekH 207c778f3a Refactor: Rename DocumentOperator to DocumentService
Updated project and solution files to rename and restructure
`DocumentOperator` to `DocumentService`. Modified project
references in `DocumentService.API.csproj`, `DocumentService.Application.csproj`,
and `DocumentService.Infrastructure.csproj` to reflect the new
naming convention. Updated `DocumentService.sln` to remove
old project references, add new ones, and adjust configuration
and nested project mappings accordingly. This change aligns
the project structure with the new naming convention.
2026-08-30 21:33:25 +02:00

126 lines
5.2 KiB
C#

using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
namespace DocumentService.API.Configuration
{
/// <summary>
/// Swagger document filter that merges operations with same path but different [Consumes] attributes.
/// Ensures both multipart/form-data and application/json variants are visible in Swagger UI.
/// </summary>
public class DualInputDocumentFilter : IDocumentFilter
{
private readonly IApiDescriptionGroupCollectionProvider _apiDescriptionProvider;
/// <summary>
/// Initializes a new instance of the <see cref="DualInputDocumentFilter"/> class.
/// </summary>
/// <param name="apiDescriptionProvider">API description provider to access all endpoints</param>
public DualInputDocumentFilter(IApiDescriptionGroupCollectionProvider apiDescriptionProvider)
{
_apiDescriptionProvider = apiDescriptionProvider;
}
/// <summary>
/// Applies the filter to merge operations with different content types.
/// </summary>
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
{
var allApiDescriptions = _apiDescriptionProvider.ApiDescriptionGroups.Items
.SelectMany(g => g.Items)
.ToList();
// Group by path
var groupedByPath = allApiDescriptions
.GroupBy(x => "/" + x.RelativePath)
.ToList();
foreach (var group in groupedByPath)
{
var path = group.Key;
if (!swaggerDoc.Paths.ContainsKey(path))
continue;
var pathItem = swaggerDoc.Paths[path];
// Find multipart and JSON variants
var multipartDesc = group.FirstOrDefault(x =>
x.SupportedRequestFormats.Any(f => f.MediaType == "multipart/form-data"));
var jsonDesc = group.FirstOrDefault(x =>
x.SupportedRequestFormats.Any(f => f.MediaType == "application/json"));
// If we have both variants, merge them into single operation
if (multipartDesc != null && jsonDesc != null)
{
var httpMethod = multipartDesc.HttpMethod?.ToLowerInvariant();
OperationType operationType;
if (!Enum.TryParse<OperationType>(httpMethod, true, out operationType))
continue;
if (!pathItem.Operations.ContainsKey(operationType))
continue;
var operation = pathItem.Operations[operationType];
// Ensure RequestBody exists
if (operation.RequestBody == null)
{
operation.RequestBody = new OpenApiRequestBody
{
Required = true,
Content = new Dictionary<string, OpenApiMediaType>()
};
}
// Add multipart/form-data if missing
if (!operation.RequestBody.Content.ContainsKey("multipart/form-data"))
{
operation.RequestBody.Content.Add("multipart/form-data", new OpenApiMediaType
{
Schema = new OpenApiSchema
{
Type = "object",
Properties = new Dictionary<string, OpenApiSchema>
{
["file"] = new OpenApiSchema
{
Type = "string",
Format = "binary",
Description = "PDF file to upload"
}
},
Required = new HashSet<string> { "file" }
}
});
}
// Add application/json if missing
if (!operation.RequestBody.Content.ContainsKey("application/json"))
{
operation.RequestBody.Content.Add("application/json", new OpenApiMediaType
{
Schema = new OpenApiSchema
{
Type = "object",
Properties = new Dictionary<string, OpenApiSchema>
{
["base64Pdf"] = new OpenApiSchema
{
Type = "string",
Format = "byte",
Description = "Base64-encoded PDF file content"
}
},
Required = new HashSet<string> { "base64Pdf" }
}
});
}
}
}
}
}
}