From dfeb30eb9eb70f40cce7fd98937ad7ff118bd598 Mon Sep 17 00:00:00 2001 From: Milan Martiniak Date: Wed, 23 Mar 2022 20:55:46 +0100 Subject: [PATCH 1/2] Support reloading SwaggerUI configuration after ocelot.json change --- .../ServiceCollectionExtensions.cs | 5 +-- .../Middleware/BuilderExtensions.cs | 18 +++++++- .../Middleware/SwaggerForOcelotMiddleware.cs | 6 +-- .../Repositories/SwaggerEndPointProvider.cs | 6 +-- .../SwaggerForOcelotMiddlewareShould.cs | 43 +++++++++++-------- 5 files changed, 51 insertions(+), 27 deletions(-) diff --git a/src/MMLib.SwaggerForOcelot/DependencyInjection/ServiceCollectionExtensions.cs b/src/MMLib.SwaggerForOcelot/DependencyInjection/ServiceCollectionExtensions.cs index 61ed463..58bc4d8 100644 --- a/src/MMLib.SwaggerForOcelot/DependencyInjection/ServiceCollectionExtensions.cs +++ b/src/MMLib.SwaggerForOcelot/DependencyInjection/ServiceCollectionExtensions.cs @@ -38,9 +38,8 @@ public static IServiceCollection AddSwaggerForOcelot( .AddTransient() .AddTransient() .AddTransient() - .Configure>(options => configuration.GetSection("Routes").Bind(options)) - .Configure>(options - => configuration.GetSection(SwaggerEndPointOptions.ConfigurationSectionName).Bind(options)) + .Configure>(configuration.GetSection("Routes")) + .Configure>(configuration.GetSection(SwaggerEndPointOptions.ConfigurationSectionName)) .AddHttpClient() .AddMemoryCache() .AddSingleton(); diff --git a/src/MMLib.SwaggerForOcelot/Middleware/BuilderExtensions.cs b/src/MMLib.SwaggerForOcelot/Middleware/BuilderExtensions.cs index 18e4615..55b9da2 100644 --- a/src/MMLib.SwaggerForOcelot/Middleware/BuilderExtensions.cs +++ b/src/MMLib.SwaggerForOcelot/Middleware/BuilderExtensions.cs @@ -36,12 +36,25 @@ public static IApplicationBuilder UseSwaggerForOcelotUI( InitUIOption(c, options); IReadOnlyList endPoints = app .ApplicationServices.GetService().GetAll(); + + ChangeDetection(app, c, options); AddSwaggerEndPoints(c, endPoints, options.DownstreamSwaggerEndPointBasePath); }); return app; } + private static void ChangeDetection(IApplicationBuilder app, SwaggerUIOptions c, SwaggerForOcelotUIOptions options) + { + IOptionsMonitor> endpointsChangeMonitor = + app.ApplicationServices.GetService>>(); + endpointsChangeMonitor.OnChange((newEndpoints) => + { + c.ConfigObject.Urls = null; + AddSwaggerEndPoints(c, newEndpoints, options.DownstreamSwaggerEndPointBasePath); + }); + } + /// [Obsolete("Use app.UseSwaggerForOcelotUI() instead.")] public static IApplicationBuilder UseSwaggerForOcelotUI( @@ -60,7 +73,10 @@ public static IApplicationBuilder UseSwaggerForOcelotUI( private static void UseSwaggerForOcelot(IApplicationBuilder app, SwaggerForOcelotUIOptions options) => app.Map(options.PathToSwaggerGenerator, builder => builder.UseMiddleware(options)); - private static void AddSwaggerEndPoints(SwaggerUIOptions c, IReadOnlyList endPoints, string basePath) + private static void AddSwaggerEndPoints( + SwaggerUIOptions c, + IReadOnlyList endPoints, + string basePath) { static string GetDescription(SwaggerEndPointConfig config) => config.IsGatewayItSelf ? config.Name : $"{config.Name} - {config.Version}"; diff --git a/src/MMLib.SwaggerForOcelot/Middleware/SwaggerForOcelotMiddleware.cs b/src/MMLib.SwaggerForOcelot/Middleware/SwaggerForOcelotMiddleware.cs index 53a4268..fbfd5c2 100644 --- a/src/MMLib.SwaggerForOcelot/Middleware/SwaggerForOcelotMiddleware.cs +++ b/src/MMLib.SwaggerForOcelot/Middleware/SwaggerForOcelotMiddleware.cs @@ -27,7 +27,7 @@ public class SwaggerForOcelotMiddleware private readonly RequestDelegate _next; #pragma warning restore IDE0052 - private readonly IOptions> _routes; + private readonly IOptionsMonitor> _routes; private readonly ISwaggerJsonTransformer _transformer; private readonly SwaggerForOcelotUIOptions _options; private readonly ISwaggerDownstreamInterceptor _downstreamInterceptor; @@ -45,7 +45,7 @@ public class SwaggerForOcelotMiddleware public SwaggerForOcelotMiddleware( RequestDelegate next, SwaggerForOcelotUIOptions options, - IOptions> routes, + IOptionsMonitor> routes, ISwaggerJsonTransformer transformer, ISwaggerProvider swaggerProvider, ISwaggerDownstreamInterceptor downstreamInterceptor = null) @@ -85,7 +85,7 @@ public async Task Invoke(HttpContext context, return; } - IEnumerable routeOptions = _routes.Value + IEnumerable routeOptions = _routes.CurrentValue .ExpandConfig(endPoint) .GroupByPaths(); diff --git a/src/MMLib.SwaggerForOcelot/Repositories/SwaggerEndPointProvider.cs b/src/MMLib.SwaggerForOcelot/Repositories/SwaggerEndPointProvider.cs index b035647..be5b985 100644 --- a/src/MMLib.SwaggerForOcelot/Repositories/SwaggerEndPointProvider.cs +++ b/src/MMLib.SwaggerForOcelot/Repositories/SwaggerEndPointProvider.cs @@ -13,7 +13,7 @@ namespace MMLib.SwaggerForOcelot.Repositories public class SwaggerEndPointProvider : ISwaggerEndPointProvider { private readonly Lazy> _swaggerEndPoints; - private readonly IOptions> _swaggerEndPointsOptions; + private readonly IOptionsMonitor> _swaggerEndPointsOptions; private readonly OcelotSwaggerGenOptions _options; /// @@ -21,7 +21,7 @@ public class SwaggerEndPointProvider : ISwaggerEndPointProvider /// /// The swagger end points. public SwaggerEndPointProvider( - IOptions> swaggerEndPoints, + IOptionsMonitor> swaggerEndPoints, OcelotSwaggerGenOptions options) { _swaggerEndPointsOptions = Check.NotNull(swaggerEndPoints, nameof(swaggerEndPoints)); @@ -40,7 +40,7 @@ public SwaggerEndPointOptions GetByKey(string key) private Dictionary Init() { - var ret = _swaggerEndPointsOptions.Value.ToDictionary(p => $"/{p.KeyToPath}", p => p); + var ret = _swaggerEndPointsOptions.CurrentValue.ToDictionary(p => $"/{p.KeyToPath}", p => p); if (_options.GenerateDocsForAggregates) { diff --git a/tests/MMLib.SwaggerForOcelot.Tests/SwaggerForOcelotMiddlewareShould.cs b/tests/MMLib.SwaggerForOcelot.Tests/SwaggerForOcelotMiddlewareShould.cs index 0403479..4f6f161 100644 --- a/tests/MMLib.SwaggerForOcelot.Tests/SwaggerForOcelotMiddlewareShould.cs +++ b/tests/MMLib.SwaggerForOcelot.Tests/SwaggerForOcelotMiddlewareShould.cs @@ -40,14 +40,14 @@ public async Task AllowVersionPlaceholder() // What is being tested var swaggerForOcelotOptions = new SwaggerForOcelotUIOptions(); - TestSwaggerEndpointOptions swaggerEndpointOptions = CreateSwaggerEndpointOptions(key,version); + TestSwaggerEndpointOptions swaggerEndpointOptions = CreateSwaggerEndpointOptions(key, version); var routeOptions = new TestRouteOptions(new List { new() { SwaggerKey = "projects", - UpstreamPathTemplate ="/api/{version}/projects/Values/{everything}", - DownstreamPathTemplate ="/api/{version}/Values/{everything}", + UpstreamPathTemplate = "/api/{version}/projects/Values/{everything}", + DownstreamPathTemplate = "/api/{version}/Values/{everything}", }, new() { @@ -83,7 +83,7 @@ public async Task AllowVersionPlaceholder() IEnumerable routeOptions, string serverOverride, SwaggerEndPointOptions options) => new SwaggerJsonTransformer(OcelotSwaggerGenOptions.Default) - .Transform(swaggerJson,routeOptions, serverOverride, options)); + .Transform(swaggerJson, routeOptions, serverOverride, options)); var swaggerForOcelotMiddleware = new SwaggerForOcelotMiddleware( next.Invoke, swaggerForOcelotOptions, @@ -105,6 +105,7 @@ await swaggerForOcelotMiddleware.Invoke( string transformedUpstreamSwagger = await streamReader.ReadToEndAsync(); AreEqual(transformedUpstreamSwagger, expectedSwagger); } + swaggerJsonTransformerMock.Verify(x => x.Transform( It.IsAny(), It.IsAny>(), @@ -127,7 +128,7 @@ public async Task AllowUserDefinedUpstreamTransformer() { ReConfigureUpstreamSwaggerJson = ExampleUserDefinedUpstreamTransformer }; - TestSwaggerEndpointOptions testSwaggerEndpointOptions = CreateSwaggerEndpointOptions(key,version); + TestSwaggerEndpointOptions testSwaggerEndpointOptions = CreateSwaggerEndpointOptions(key, version); var routeOptions = new TestRouteOptions(); // downstreamSwagger is returned when client.GetStringAsync is called by the middleware. @@ -171,7 +172,7 @@ public async Task RespectDownstreamHttpVersionRouteSetting() // What is being tested var swaggerForOcelotOptions = new SwaggerForOcelotUIOptions(); - TestSwaggerEndpointOptions swaggerEndpointOptions = CreateSwaggerEndpointOptions(key,version); + TestSwaggerEndpointOptions swaggerEndpointOptions = CreateSwaggerEndpointOptions(key, version); var routeOptions = new TestRouteOptions(new List { new() @@ -308,10 +309,7 @@ private HttpClient GetHttpClient(string downstreamSwagger) ) .ReturnsAsync((HttpRequestMessage request, CancellationToken token) => { - var response = new HttpResponseMessage(HttpStatusCode.OK) - { - Content = new StringContent(downstreamSwagger) - }; + var response = new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(downstreamSwagger) }; return response; }); return new HttpClient(httpMessageHandlerMock.Object); @@ -349,6 +347,7 @@ public TestHttpClientFactory(HttpClient mockHttpClient) { _mockHttpClient = mockHttpClient; } + public HttpClient CreateClient() { return _mockHttpClient; @@ -360,28 +359,38 @@ public HttpClient CreateClient(string name) } } - private class TestRouteOptions : IOptions> + private class TestRouteOptions : IOptionsMonitor> { public TestRouteOptions() { - Value = new List(); + CurrentValue = new List(); } public TestRouteOptions(List value) { - Value = value; + CurrentValue = value; } - public List Value { get; } + public List Get(string name) => throw new NotImplementedException(); + + public IDisposable OnChange(Action, string> listener) => throw new NotImplementedException(); + + public List CurrentValue { get; } } - private class TestSwaggerEndpointOptions : IOptions> + private class TestSwaggerEndpointOptions : IOptionsMonitor> { public TestSwaggerEndpointOptions(List options) { - Value = options; + CurrentValue = options; } - public List Value { get; } + + public List Get(string name) => throw new NotImplementedException(); + + public IDisposable OnChange(Action, string> listener) => + throw new NotImplementedException(); + + public List CurrentValue { get; } } private class TestSwaggerJsonTransformer : ISwaggerJsonTransformer From 0c214428d9df987cb9c68aa1ed2d7cbd363aa502 Mon Sep 17 00:00:00 2001 From: Milan Martiniak Date: Wed, 23 Mar 2022 21:04:02 +0100 Subject: [PATCH 2/2] new version --- src/MMLib.SwaggerForOcelot/MMLib.SwaggerForOcelot.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MMLib.SwaggerForOcelot/MMLib.SwaggerForOcelot.csproj b/src/MMLib.SwaggerForOcelot/MMLib.SwaggerForOcelot.csproj index ad14e7c..774aab0 100644 --- a/src/MMLib.SwaggerForOcelot/MMLib.SwaggerForOcelot.csproj +++ b/src/MMLib.SwaggerForOcelot/MMLib.SwaggerForOcelot.csproj @@ -2,7 +2,7 @@ net5.0 - 4.8.0 + 4.9.0 Milan Martiniak MMLib Swagger generator for Ocelot downstream services.