diff --git a/README.md b/README.md index 541243a..d807174 100644 --- a/README.md +++ b/README.md @@ -100,7 +100,7 @@ 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"; }) ``` @@ -108,7 +108,7 @@ app.UseSwaggerForOcelotUI(Configuration, opt => { 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("Auth-Key", "AuthValue"), @@ -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" ; }) diff --git a/demo/ApiGateway/Startup.cs b/demo/ApiGateway/Startup.cs index 31958eb..e25a677 100644 --- a/demo/ApiGateway/Startup.cs +++ b/demo/ApiGateway/Startup.cs @@ -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[] { diff --git a/demo/ApiGatewayWithPath/Startup.cs b/demo/ApiGatewayWithPath/Startup.cs index bc7998a..4ddc0e3 100644 --- a/demo/ApiGatewayWithPath/Startup.cs +++ b/demo/ApiGatewayWithPath/Startup.cs @@ -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"; @@ -44,4 +44,4 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) .Wait(); } } -} \ No newline at end of file +} diff --git a/src/MMLib.SwaggerForOcelot/Middleware/BuilderExtensions.cs b/src/MMLib.SwaggerForOcelot/Middleware/BuilderExtensions.cs index 16fac14..b72ebc6 100644 --- a/src/MMLib.SwaggerForOcelot/Middleware/BuilderExtensions.cs +++ b/src/MMLib.SwaggerForOcelot/Middleware/BuilderExtensions.cs @@ -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 { @@ -16,48 +18,54 @@ public static class BuilderExtensions /// Add Swagger generator for downstream services and UI into application pipeline. /// /// The application builder. - /// The configuration. - /// - /// . - /// - public static IApplicationBuilder UseSwaggerForOcelotUI( - this IApplicationBuilder app, - IConfiguration configuration) - => app.UseSwaggerForOcelotUI(configuration, null); - - /// - /// Add Swagger generator for downstream services and UI into application pipeline. - /// - /// The application builder. - /// The configuration. /// Setup /// /// . /// public static IApplicationBuilder UseSwaggerForOcelotUI( this IApplicationBuilder app, - IConfiguration configuration, - Action setupAction) + Action setupAction = null) { - var options = new SwaggerForOcelotUIOptions(); + SwaggerForOcelotUIOptions options = app.ApplicationServices.GetService>().Value; setupAction?.Invoke(options); UseSwaggerForOcelot(app, options); app.UseSwaggerUI(c => { InitUIOption(c, options); - IEnumerable endPoints = GetConfiguration(configuration); + IReadOnlyList endPoints = app.ApplicationServices.GetService>>().Value; AddSwaggerEndPoints(c, endPoints, options.DownstreamSwaggerEndPointBasePath); }); return app; } + /// + [Obsolete("Use app.UseSwaggerForOcelotUI() instead.")] + public static IApplicationBuilder UseSwaggerForOcelotUI( + this IApplicationBuilder app, + IConfiguration configuration) + => app.UseSwaggerForOcelotUI(); + + /// + [Obsolete("Use app.UseSwaggerForOcelotUI(setupAction) instead.")] + public static IApplicationBuilder UseSwaggerForOcelotUI( + this IApplicationBuilder app, + IConfiguration configuration, + Action setupAction) + => app.UseSwaggerForOcelotUI(setupAction); + private static void UseSwaggerForOcelot(IApplicationBuilder app, SwaggerForOcelotUIOptions options) => app.Map(options.PathToSwaggerGenerator, builder => builder.UseMiddleware(options)); - private static void AddSwaggerEndPoints(SwaggerUIOptions c, IEnumerable endPoints, string basePath) + private static void AddSwaggerEndPoints(SwaggerUIOptions c, IReadOnlyList 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) @@ -76,20 +84,5 @@ private static void InitUIOption(SwaggerUIOptions c, SwaggerForOcelotUIOptions o c.OAuthConfigObject = options.OAuthConfigObject; c.RoutePrefix = options.RoutePrefix; } - - private static IEnumerable GetConfiguration(IConfiguration configuration) - { - IEnumerable options = - configuration.GetSection(SwaggerEndPointOptions.ConfigurationSectionName) - .Get>(); - - if (options is null) - { - throw new InvalidOperationException( - $"{SwaggerEndPointOptions.ConfigurationSectionName} configuration section is missing or empty."); - } - - return options; - } } -} \ No newline at end of file +} diff --git a/tests/MMLib.SwaggerForOcelot.Tests/BuilderExtensionsShould.cs b/tests/MMLib.SwaggerForOcelot.Tests/BuilderExtensionsShould.cs index cf58e80..450f146 100644 --- a/tests/MMLib.SwaggerForOcelot.Tests/BuilderExtensionsShould.cs +++ b/tests/MMLib.SwaggerForOcelot.Tests/BuilderExtensionsShould.cs @@ -2,16 +2,35 @@ 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 + { + new SwaggerEndPointConfig + { + Name = "Contacts API", + Version = "v1", + Url = TestEndpointUrl, + }, + }, + }; + [Fact] public void ThrowWhenSwaggerEndPointsSectionIsMissing() => TestWithInvalidConfiguration("{}"); @@ -19,11 +38,52 @@ public class BuilderExtensionsShould 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>(options => + { + options.Add(_testSwaggerEndpointOptions); + }); + } + + TestWithValidConfiguration("{}", configureServices); + } + + private void TestWithValidConfiguration(string jsonConfiguration, Action configureServices = null) + { + IApplicationBuilder builder = GetApplicationBuilderWithJsonConfiguration(jsonConfiguration, configureServices); + Action action = () => builder.UseSwaggerForOcelotUI(); + + action.Should().NotThrow(); + } + + private IApplicationBuilder GetApplicationBuilderWithJsonConfiguration(string jsonConfiguration, Action 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(); } @@ -38,9 +98,13 @@ private IConfiguration GetConfiguration(string jsonConfiguration) return configuration; } - private IApplicationBuilder GetApplicationBuilder(IConfiguration configuration) + private IApplicationBuilder GetApplicationBuilder(IConfiguration configuration, Action 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); }