Skip to content

Commit

Permalink
Allow programmatic configuration of SwaggerEndpoints (#105)
Browse files Browse the repository at this point in the history
* Allow programmatic configuration of SwaggerEndpoints

Use configured options in UseSwaggerForOcelotUI to allow programmatic configuration of services.

Overloads of UseSwaggerForOcelotUI with IConfiguration parameter become deprecated.

Fixes #104

* Updated Readme to use new UseSwaggerForOcelotUI

Updated Readme to use UseSwaggerForOcelotUI without configuration parameter

Co-authored-by: Robin Kaulfuß <[email protected]>
  • Loading branch information
rklfss and Robin Kaulfuß authored Jun 4, 2020
1 parent 791de8c commit 32b28d3
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 48 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,15 +100,15 @@ services.AddSwaggerForOcelot(Configuration);
6. In `Configure` method, insert the `SwaggerForOcelot` middleware to expose interactive documentation.

```CSharp
app.UseSwaggerForOcelotUI(Configuration, opt => {
app.UseSwaggerForOcelotUI(opt => {
opt.PathToSwaggerGenerator = "/swagger/docs";
})
```

You can optionally include headers that your Ocelot Gateway will send when requesting a swagger endpoint. This can be especially useful if your downstream microservices require contents from a header to authenticate.

```CSharp
app.UseSwaggerForOcelotUI(Configuration, opt => {
app.UseSwaggerForOcelotUI(opt => {
opts.DownstreamSwaggerHeaders = new[]
{
new KeyValuePair<string, string>("Auth-Key", "AuthValue"),
Expand All @@ -126,13 +126,13 @@ public string AlterUpstreamSwaggerJson(HttpContext context, string swaggerJson)
return swagger.ToString(Formatting.Indented);
}

app.UseSwaggerForOcelotUI(Configuration, opt => {
app.UseSwaggerForOcelotUI(opt => {
opts.ReConfigureUpstreamSwaggerJson = AlterUpstreamSwaggerJson;
})
```
You can optionally customize the swagger server prior to calling the endpoints of the microservices as follows:
```CSharp
app.UseSwaggerForOcelotUI(Configuration, opt => {
app.UseSwaggerForOcelotUI(opt => {
opts.ReConfigureUpstreamSwaggerJson = AlterUpstreamSwaggerJson;
opts.ServerOcelot = "/siteName/apigateway" ;
})
Expand Down
2 changes: 1 addition & 1 deletion demo/ApiGateway/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
endpoints.MapControllers();
});
app.UseStaticFiles();
app.UseSwaggerForOcelotUI(Configuration, opt =>
app.UseSwaggerForOcelotUI(opt =>
{
opt.DownstreamSwaggerHeaders = new[]
{
Expand Down
4 changes: 2 additions & 2 deletions demo/ApiGatewayWithPath/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
app.UseDeveloperExceptionPage();
}
app.UseStaticFiles();
app.UseSwaggerForOcelotUI(Configuration, opt =>
app.UseSwaggerForOcelotUI(opt =>
{
opt.DownstreamSwaggerEndPointBasePath = "/gateway/swagger/docs";
opt.PathToSwaggerGenerator = "/swagger/docs";
Expand All @@ -44,4 +44,4 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
.Wait();
}
}
}
}
63 changes: 28 additions & 35 deletions src/MMLib.SwaggerForOcelot/Middleware/BuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
using Swashbuckle.AspNetCore.SwaggerUI;
using System;
using System.Collections.Generic;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;

namespace Microsoft.AspNetCore.Builder
{
Expand All @@ -16,48 +18,54 @@ public static class BuilderExtensions
/// Add Swagger generator for downstream services and UI into application pipeline.
/// </summary>
/// <param name="app">The application builder.</param>
/// <param name="configuration">The configuration.</param>
/// <returns>
/// <see cref="IApplicationBuilder"/>.
/// </returns>
public static IApplicationBuilder UseSwaggerForOcelotUI(
this IApplicationBuilder app,
IConfiguration configuration)
=> app.UseSwaggerForOcelotUI(configuration, null);

/// <summary>
/// Add Swagger generator for downstream services and UI into application pipeline.
/// </summary>
/// <param name="app">The application builder.</param>
/// <param name="configuration">The configuration.</param>
/// <param name="setupAction">Setup <see cref="SwaggerForOcelotUIOptions"/></param>
/// <returns>
/// <see cref="IApplicationBuilder"/>.
/// </returns>
public static IApplicationBuilder UseSwaggerForOcelotUI(
this IApplicationBuilder app,
IConfiguration configuration,
Action<SwaggerForOcelotUIOptions> setupAction)
Action<SwaggerForOcelotUIOptions> setupAction = null)
{
var options = new SwaggerForOcelotUIOptions();
SwaggerForOcelotUIOptions options = app.ApplicationServices.GetService<IOptions<SwaggerForOcelotUIOptions>>().Value;
setupAction?.Invoke(options);
UseSwaggerForOcelot(app, options);

app.UseSwaggerUI(c =>
{
InitUIOption(c, options);
IEnumerable<SwaggerEndPointOptions> endPoints = GetConfiguration(configuration);
IReadOnlyList<SwaggerEndPointOptions> endPoints = app.ApplicationServices.GetService<IOptions<List<SwaggerEndPointOptions>>>().Value;
AddSwaggerEndPoints(c, endPoints, options.DownstreamSwaggerEndPointBasePath);
});

return app;
}

/// <inheritdoc cref="UseSwaggerForOcelotUI(IApplicationBuilder,Action{SwaggerForOcelotUIOptions})"/>
[Obsolete("Use app.UseSwaggerForOcelotUI() instead.")]
public static IApplicationBuilder UseSwaggerForOcelotUI(
this IApplicationBuilder app,
IConfiguration configuration)
=> app.UseSwaggerForOcelotUI();

/// <inheritdoc cref="UseSwaggerForOcelotUI(IApplicationBuilder,Action{SwaggerForOcelotUIOptions})"/>
[Obsolete("Use app.UseSwaggerForOcelotUI(setupAction) instead.")]
public static IApplicationBuilder UseSwaggerForOcelotUI(
this IApplicationBuilder app,
IConfiguration configuration,
Action<SwaggerForOcelotUIOptions> setupAction)
=> app.UseSwaggerForOcelotUI(setupAction);

private static void UseSwaggerForOcelot(IApplicationBuilder app, SwaggerForOcelotUIOptions options)
=> app.Map(options.PathToSwaggerGenerator, builder => builder.UseMiddleware<SwaggerForOcelotMiddleware>(options));

private static void AddSwaggerEndPoints(SwaggerUIOptions c, IEnumerable<SwaggerEndPointOptions> endPoints, string basePath)
private static void AddSwaggerEndPoints(SwaggerUIOptions c, IReadOnlyList<SwaggerEndPointOptions> endPoints, string basePath)
{
if (endPoints is null || endPoints.Count == 0)
{
throw new InvalidOperationException(
$"{SwaggerEndPointOptions.ConfigurationSectionName} configuration section is missing or empty.");
}

foreach (SwaggerEndPointOptions endPoint in endPoints)
{
foreach (SwaggerEndPointConfig config in endPoint.Config)
Expand All @@ -76,20 +84,5 @@ private static void InitUIOption(SwaggerUIOptions c, SwaggerForOcelotUIOptions o
c.OAuthConfigObject = options.OAuthConfigObject;
c.RoutePrefix = options.RoutePrefix;
}

private static IEnumerable<SwaggerEndPointOptions> GetConfiguration(IConfiguration configuration)
{
IEnumerable<SwaggerEndPointOptions> options =
configuration.GetSection(SwaggerEndPointOptions.ConfigurationSectionName)
.Get<IEnumerable<SwaggerEndPointOptions>>();

if (options is null)
{
throw new InvalidOperationException(
$"{SwaggerEndPointOptions.ConfigurationSectionName} configuration section is missing or empty.");
}

return options;
}
}
}
}
76 changes: 70 additions & 6 deletions tests/MMLib.SwaggerForOcelot.Tests/BuilderExtensionsShould.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,88 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using MMLib.SwaggerForOcelot.Configuration;
using Newtonsoft.Json.Linq;
using Swashbuckle.AspNetCore.SwaggerUI;
using System;
using System.Collections.Generic;
using System.IO;
using Newtonsoft.Json;
using System.Linq;
using Xunit;

namespace MMLib.SwaggerForOcelot.Tests
{
public class BuilderExtensionsShould
{
private const string TestEndpointUrl = "http://localhost:5100/swagger/v1/swagger.json";
private static readonly SwaggerEndPointOptions _testSwaggerEndpointOptions = new SwaggerEndPointOptions
{
Key = "contacts",
Config = new List<SwaggerEndPointConfig>
{
new SwaggerEndPointConfig
{
Name = "Contacts API",
Version = "v1",
Url = TestEndpointUrl,
},
},
};

[Fact]
public void ThrowWhenSwaggerEndPointsSectionIsMissing() => TestWithInvalidConfiguration("{}");

[Fact]
public void ThrowWhenSwaggerEndPointsSectionIsEmpty()
=> TestWithInvalidConfiguration($"{{\"{SwaggerEndPointOptions.ConfigurationSectionName}\":[]}}");

private void TestWithInvalidConfiguration(string jsonConfiguration)
[Fact]
public void UseSwaggerEndpointsFromConfigurationFile()
{
var configJson = new JObject
{
[SwaggerEndPointOptions.ConfigurationSectionName] = new JArray
{
JObject.FromObject(_testSwaggerEndpointOptions),
},
};

TestWithValidConfiguration(configJson.ToString());
}

[Fact]
public void UseSwaggerEndpointsFromProgrammaticConfiguration()
{
void configureServices(IServiceCollection services)
{
services.Configure<List<SwaggerEndPointOptions>>(options =>
{
options.Add(_testSwaggerEndpointOptions);
});
}

TestWithValidConfiguration("{}", configureServices);
}

private void TestWithValidConfiguration(string jsonConfiguration, Action<IServiceCollection> configureServices = null)
{
IApplicationBuilder builder = GetApplicationBuilderWithJsonConfiguration(jsonConfiguration, configureServices);
Action action = () => builder.UseSwaggerForOcelotUI();

action.Should().NotThrow();
}

private IApplicationBuilder GetApplicationBuilderWithJsonConfiguration(string jsonConfiguration, Action<IServiceCollection> configureServices = null)
{
IConfiguration config = GetConfiguration(jsonConfiguration);
IApplicationBuilder builder = GetApplicationBuilder(config);
Action action = () => { builder.UseSwaggerForOcelotUI(config); };
return GetApplicationBuilder(config, configureServices);
}

private void TestWithInvalidConfiguration(string jsonConfiguration)
{
IApplicationBuilder builder = GetApplicationBuilderWithJsonConfiguration(jsonConfiguration);
Action action = () => builder.UseSwaggerForOcelotUI();

action.Should().Throw<InvalidOperationException>();
}
Expand All @@ -38,9 +98,13 @@ private IConfiguration GetConfiguration(string jsonConfiguration)
return configuration;
}

private IApplicationBuilder GetApplicationBuilder(IConfiguration configuration)
private IApplicationBuilder GetApplicationBuilder(IConfiguration configuration, Action<IServiceCollection> configureServices = null)
{
IServiceProvider serviceProvider = new ServiceCollection().AddSwaggerForOcelot(configuration).BuildServiceProvider();
IServiceCollection serviceCollection = new ServiceCollection();

serviceCollection.AddSwaggerForOcelot(configuration);
configureServices?.Invoke(serviceCollection);
IServiceProvider serviceProvider = serviceCollection.BuildServiceProvider();

return new ApplicationBuilder(serviceProvider);
}
Expand Down

0 comments on commit 32b28d3

Please sign in to comment.