Skip to content

Commit

Permalink
Support SwaggerUI configuration reload after ocelot.json change (#225)
Browse files Browse the repository at this point in the history
* Support reloading SwaggerUI configuration after ocelot.json change

* new version
  • Loading branch information
Burgyn authored Mar 23, 2022
1 parent 85b52d5 commit 84812d7
Show file tree
Hide file tree
Showing 6 changed files with 52 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,8 @@ public static IServiceCollection AddSwaggerForOcelot(
.AddTransient<IDownstreamSwaggerDocsRepository, DownstreamSwaggerDocsRepository>()
.AddTransient<ISwaggerServiceDiscoveryProvider, SwaggerServiceDiscoveryProvider>()
.AddTransient<ISwaggerJsonTransformer, SwaggerJsonTransformer>()
.Configure<List<RouteOptions>>(options => configuration.GetSection("Routes").Bind(options))
.Configure<List<SwaggerEndPointOptions>>(options
=> configuration.GetSection(SwaggerEndPointOptions.ConfigurationSectionName).Bind(options))
.Configure<List<RouteOptions>>(configuration.GetSection("Routes"))
.Configure<List<SwaggerEndPointOptions>>(configuration.GetSection(SwaggerEndPointOptions.ConfigurationSectionName))
.AddHttpClient()
.AddMemoryCache()
.AddSingleton<ISwaggerEndPointProvider, SwaggerEndPointProvider>();
Expand Down
2 changes: 1 addition & 1 deletion src/MMLib.SwaggerForOcelot/MMLib.SwaggerForOcelot.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<Version>4.8.0</Version>
<Version>4.9.0</Version>
<Authors>Milan Martiniak</Authors>
<Company>MMLib</Company>
<Description>Swagger generator for Ocelot downstream services.</Description>
Expand Down
18 changes: 17 additions & 1 deletion src/MMLib.SwaggerForOcelot/Middleware/BuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,25 @@ public static IApplicationBuilder UseSwaggerForOcelotUI(
InitUIOption(c, options);
IReadOnlyList<SwaggerEndPointOptions> endPoints = app
.ApplicationServices.GetService<ISwaggerEndPointProvider>().GetAll();
ChangeDetection(app, c, options);
AddSwaggerEndPoints(c, endPoints, options.DownstreamSwaggerEndPointBasePath);
});

return app;
}

private static void ChangeDetection(IApplicationBuilder app, SwaggerUIOptions c, SwaggerForOcelotUIOptions options)
{
IOptionsMonitor<List<SwaggerEndPointOptions>> endpointsChangeMonitor =
app.ApplicationServices.GetService<IOptionsMonitor<List<SwaggerEndPointOptions>>>();
endpointsChangeMonitor.OnChange((newEndpoints) =>
{
c.ConfigObject.Urls = null;
AddSwaggerEndPoints(c, newEndpoints, options.DownstreamSwaggerEndPointBasePath);
});
}

/// <inheritdoc cref="UseSwaggerForOcelotUI(IApplicationBuilder,Action{SwaggerForOcelotUIOptions})"/>
[Obsolete("Use app.UseSwaggerForOcelotUI() instead.")]
public static IApplicationBuilder UseSwaggerForOcelotUI(
Expand All @@ -60,7 +73,10 @@ public static IApplicationBuilder UseSwaggerForOcelotUI(
private static void UseSwaggerForOcelot(IApplicationBuilder app, SwaggerForOcelotUIOptions options)
=> app.Map(options.PathToSwaggerGenerator, builder => builder.UseMiddleware<SwaggerForOcelotMiddleware>(options));

private static void AddSwaggerEndPoints(SwaggerUIOptions c, IReadOnlyList<SwaggerEndPointOptions> endPoints, string basePath)
private static void AddSwaggerEndPoints(
SwaggerUIOptions c,
IReadOnlyList<SwaggerEndPointOptions> endPoints,
string basePath)
{
static string GetDescription(SwaggerEndPointConfig config)
=> config.IsGatewayItSelf ? config.Name : $"{config.Name} - {config.Version}";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public class SwaggerForOcelotMiddleware
private readonly RequestDelegate _next;
#pragma warning restore IDE0052

private readonly IOptions<List<RouteOptions>> _routes;
private readonly IOptionsMonitor<List<RouteOptions>> _routes;
private readonly ISwaggerJsonTransformer _transformer;
private readonly SwaggerForOcelotUIOptions _options;
private readonly ISwaggerDownstreamInterceptor _downstreamInterceptor;
Expand All @@ -45,7 +45,7 @@ public class SwaggerForOcelotMiddleware
public SwaggerForOcelotMiddleware(
RequestDelegate next,
SwaggerForOcelotUIOptions options,
IOptions<List<RouteOptions>> routes,
IOptionsMonitor<List<RouteOptions>> routes,
ISwaggerJsonTransformer transformer,
ISwaggerProvider swaggerProvider,
ISwaggerDownstreamInterceptor downstreamInterceptor = null)
Expand Down Expand Up @@ -85,7 +85,7 @@ public async Task Invoke(HttpContext context,
return;
}

IEnumerable<RouteOptions> routeOptions = _routes.Value
IEnumerable<RouteOptions> routeOptions = _routes.CurrentValue
.ExpandConfig(endPoint)
.GroupByPaths();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ namespace MMLib.SwaggerForOcelot.Repositories
public class SwaggerEndPointProvider : ISwaggerEndPointProvider
{
private readonly Lazy<Dictionary<string, SwaggerEndPointOptions>> _swaggerEndPoints;
private readonly IOptions<List<SwaggerEndPointOptions>> _swaggerEndPointsOptions;
private readonly IOptionsMonitor<List<SwaggerEndPointOptions>> _swaggerEndPointsOptions;
private readonly OcelotSwaggerGenOptions _options;

/// <summary>
/// Initializes a new instance of the <see cref="SwaggerEndPointProvider"/> class.
/// </summary>
/// <param name="swaggerEndPoints">The swagger end points.</param>
public SwaggerEndPointProvider(
IOptions<List<SwaggerEndPointOptions>> swaggerEndPoints,
IOptionsMonitor<List<SwaggerEndPointOptions>> swaggerEndPoints,
OcelotSwaggerGenOptions options)
{
_swaggerEndPointsOptions = Check.NotNull(swaggerEndPoints, nameof(swaggerEndPoints));
Expand All @@ -40,7 +40,7 @@ public SwaggerEndPointOptions GetByKey(string key)

private Dictionary<string, SwaggerEndPointOptions> Init()
{
var ret = _swaggerEndPointsOptions.Value.ToDictionary(p => $"/{p.KeyToPath}", p => p);
var ret = _swaggerEndPointsOptions.CurrentValue.ToDictionary(p => $"/{p.KeyToPath}", p => p);

if (_options.GenerateDocsForAggregates)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<RouteOptions>
{
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()
{
Expand Down Expand Up @@ -83,7 +83,7 @@ public async Task AllowVersionPlaceholder()
IEnumerable<RouteOptions> 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,
Expand All @@ -105,6 +105,7 @@ await swaggerForOcelotMiddleware.Invoke(
string transformedUpstreamSwagger = await streamReader.ReadToEndAsync();
AreEqual(transformedUpstreamSwagger, expectedSwagger);
}

swaggerJsonTransformerMock.Verify(x => x.Transform(
It.IsAny<string>(),
It.IsAny<IEnumerable<RouteOptions>>(),
Expand All @@ -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.
Expand Down Expand Up @@ -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<RouteOptions>
{
new()
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -349,6 +347,7 @@ public TestHttpClientFactory(HttpClient mockHttpClient)
{
_mockHttpClient = mockHttpClient;
}

public HttpClient CreateClient()
{
return _mockHttpClient;
Expand All @@ -360,28 +359,38 @@ public HttpClient CreateClient(string name)
}
}

private class TestRouteOptions : IOptions<List<RouteOptions>>
private class TestRouteOptions : IOptionsMonitor<List<RouteOptions>>
{
public TestRouteOptions()
{
Value = new List<RouteOptions>();
CurrentValue = new List<RouteOptions>();
}

public TestRouteOptions(List<RouteOptions> value)
{
Value = value;
CurrentValue = value;
}

public List<RouteOptions> Value { get; }
public List<RouteOptions> Get(string name) => throw new NotImplementedException();

public IDisposable OnChange(Action<List<RouteOptions>, string> listener) => throw new NotImplementedException();

public List<RouteOptions> CurrentValue { get; }
}

private class TestSwaggerEndpointOptions : IOptions<List<SwaggerEndPointOptions>>
private class TestSwaggerEndpointOptions : IOptionsMonitor<List<SwaggerEndPointOptions>>
{
public TestSwaggerEndpointOptions(List<SwaggerEndPointOptions> options)
{
Value = options;
CurrentValue = options;
}
public List<SwaggerEndPointOptions> Value { get; }

public List<SwaggerEndPointOptions> Get(string name) => throw new NotImplementedException();

public IDisposable OnChange(Action<List<SwaggerEndPointOptions>, string> listener) =>
throw new NotImplementedException();

public List<SwaggerEndPointOptions> CurrentValue { get; }
}

private class TestSwaggerJsonTransformer : ISwaggerJsonTransformer
Expand Down

0 comments on commit 84812d7

Please sign in to comment.