Burgyn / MMLib.SwaggerForOcelot

This repo contains swagger extension for ocelot.
MIT License
351 stars 93 forks source link

MMLib.SwaggerForOcelot failed to generate aggregate document #297

Open nngochue opened 4 months ago

nngochue commented 4 months ago

I have been use Ocelot to build API gateway and MMLib.SwaggerForOcelot for API document. I used Custom Aggregator: TenantProvinceAggregator, WarehousesAddressAggregator. When I start the API Gateway, select Aggregates to view API document, the error will show: image And the stacktrace:

System.ArgumentException: An item with the same key has already been added. Key: GetTenantResponse
   at System.Collections.Generic.Dictionary`2.TryInsert(TKey key, TValue value, InsertionBehavior behavior)
   at System.Collections.Generic.Dictionary`2.Add(TKey key, TValue value)
   at MMLib.SwaggerForOcelot.Aggregates.AggregateRouteDocumentationGenerator.CreateResponseSchema(IEnumerable`1 routes, SwaggerAggregateRoute aggregateRoute, OpenApiDocument openApiDocument)
   at MMLib.SwaggerForOcelot.Aggregates.AggregateRouteDocumentationGenerator.GenerateDocs(SwaggerAggregateRoute aggregateRoute, IEnumerable`1 routes, OpenApiDocument openApiDocument)
   at MMLib.SwaggerForOcelot.Aggregates.AggregateRouteDocumentationGenerator.GenerateDocs(SwaggerAggregateRoute aggregateRoute, OpenApiDocument openApiDocument)
   at MMLib.SwaggerForOcelot.Aggregates.AggregatesDocumentFilter.Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
   at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GetSwagger(String documentName, String host, String basePath)
   at MMLib.SwaggerForOcelot.Middleware.SwaggerForOcelotMiddleware.Invoke(HttpContext context, ISwaggerEndPointProvider swaggerEndPointRepository, IDownstreamSwaggerDocsRepository downstreamSwaggerDocs)
   at Microsoft.AspNetCore.Builder.Extensions.MapMiddleware.InvokeCore(HttpContext context, PathString matchedPath, PathString remainingPath)
   at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.MiddlewareAnalysis.AnalysisMiddleware.Invoke(HttpContext httpContext)
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.MiddlewareAnalysis.AnalysisMiddleware.Invoke(HttpContext httpContext)
   at Microsoft.AspNetCore.MiddlewareAnalysis.AnalysisMiddleware.Invoke(HttpContext httpContext)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)

The GetTenantResponse is the response of the TenantProvinceAggregator. The TenantProvinceAggregator:

[AggregateResponse("Tenants with Province Name",typeof(PagingResponse<List<GetTenantResponse>>))]
public class TenantProvinceAggregator : IDefinedAggregator
{
    public async Task<DownstreamResponse> Aggregate(List<HttpContext> responses)
    {
        // Remove the code to make it easier to see
    }
}

The WarehousesAddressAggregator:

[AggregateResponse("Warehouses with Address Name", typeof(NonPagingResponse<List<GetAllWarehousesRespone>>))]
public class WarehousesAddressAggregator : IDefinedAggregator
{
    public async Task<DownstreamResponse> Aggregate(List<HttpContext> responses)
    {
        // Removed the code to make it easier to see
    }
}

My configuration:

public static void Main(string[] args)
{
    var environtment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");

    var builder = WebApplication.CreateBuilder(args);

    string routes = "Routes";
    builder.Configuration.AddOcelotWithSwaggerSupport(options =>
    {
        options.Folder = routes;
    });

    builder.Services
        .AddOcelot(builder.Configuration)
        .AddSingletonDefinedAggregator<TenantProvinceAggregator>()
        .AddSingletonDefinedAggregator<WarehousesAddressAggregator>()
        .AddPolly();

    builder.Services.AddSwaggerForOcelot(builder.Configuration, (o) => { 
        o.GenerateDocsForAggregates = true;
       ;
    });

    builder.Configuration.SetBasePath(Directory.GetCurrentDirectory())
        .AddOcelot(routes, builder.Environment)
        .AddEnvironmentVariables();

    builder.Services.AddControllers();        
    builder.Services.AddEndpointsApiExplorer();
    builder.Services.AddSwaggerGen();    

    var app = builder.Build();

    // Configure the HTTP request pipeline.
    if (app.Environment.IsDevelopment())
    {
        app.UseSwagger();
    }

    app.UseHttpsRedirection();
    app.UseCors(_swifthubAllowedSpecificOrigins);
    app.UseAuthorization();

    app.UseSwaggerForOcelotUI(options =>
    {
        options.PathToSwaggerGenerator = "/swagger/docs";
        options.ReConfigureUpstreamSwaggerJson = AlterUpstream.AlterUpstreamSwaggerJson;
    }).UseOcelot().Wait();

    app.MapControllers();

    app.Run();
}

When I commented the AggregateResponse of the WarehousesAddressAggregator as below, the API Gateway will show the API aggregate document, however, it's not my desire, I want to customize the aggregate response: image