Skip to content

Commit

Permalink
Use AddOpenApiDocument/AddSwaggerDocument directly on services (#1714)
Browse files Browse the repository at this point in the history
  • Loading branch information
RicoSuter committed Nov 5, 2018
1 parent 0c6c759 commit 4f43152
Show file tree
Hide file tree
Showing 10 changed files with 133 additions and 147 deletions.
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Options;
using NSwag.AspNetCore;
using NSwag.AspNetCore.Middlewares;
using NSwag.SwaggerGeneration;
using NSwag.SwaggerGeneration.WebApi;
using System;
using System.Collections.Generic;
Expand Down Expand Up @@ -34,11 +32,10 @@ private static IApplicationBuilder UseSwaggerWithApiExplorerCore(IApplicationBui

if (settings.Path.Contains("{documentName}"))
{
var registry = app.ApplicationServices.GetRequiredService<SwaggerDocumentRegistry>();

foreach (var document in registry.Documents)
var documents = app.ApplicationServices.GetRequiredService<IEnumerable<SwaggerDocumentRegistration>>();
foreach (var document in documents)
{
app = app.UseMiddleware<SwaggerMiddleware>(document.Key, settings.Path.Replace("{documentName}", document.Key), settings);
app = app.UseMiddleware<SwaggerMiddleware>(document.DocumentName, settings.Path.Replace("{documentName}", document.DocumentName), settings);
}

return app;
Expand Down Expand Up @@ -82,8 +79,8 @@ public static IApplicationBuilder UseSwaggerUi3(
settings.SwaggerRoutes.Clear();
foreach (var document in documents)
{
var swaggerRoute = swaggerRouteWithPlaceholder.Replace("{documentName}", document.Key);
settings.SwaggerRoutes.Add(new SwaggerUi3Route(document.Key, swaggerRoute));
var swaggerRoute = swaggerRouteWithPlaceholder.Replace("{documentName}", document.DocumentName);
settings.SwaggerRoutes.Add(new SwaggerUi3Route(document.DocumentName, swaggerRoute));
}
});

Expand Down Expand Up @@ -120,25 +117,25 @@ public static IApplicationBuilder UseReDoc(
private static void UseSwaggerUiWithDocumentNamePlaceholderExpanding(IApplicationBuilder app,
SwaggerUiSettingsBase<WebApiToSwaggerGeneratorSettings> settings,
Action<string, string> register,
Action<IReadOnlyDictionary<string, ISwaggerGenerator>> registerMultiple)
Action<IEnumerable<SwaggerDocumentRegistration>> registerMultiple)
{
if (settings.ActualSwaggerRoute.Contains("{documentName}"))
{
var registry = app.ApplicationServices.GetRequiredService<SwaggerDocumentRegistry>();
var documents = app.ApplicationServices.GetRequiredService<IEnumerable<SwaggerDocumentRegistration>>();
if (settings.ActualSwaggerUiRoute.Contains("{documentName}"))
{
// Register multiple uis
foreach (var document in registry.Documents)
foreach (var document in documents)
{
register(
settings.ActualSwaggerRoute.Replace("{documentName}", document.Key),
settings.ActualSwaggerUiRoute.Replace("{documentName}", document.Key));
settings.ActualSwaggerRoute.Replace("{documentName}", document.DocumentName),
settings.ActualSwaggerUiRoute.Replace("{documentName}", document.DocumentName));
}
}
else
{
// Register single ui with multiple documents
registerMultiple(registry.Documents);
registerMultiple(documents);
register(settings.ActualSwaggerRoute, settings.ActualSwaggerUiRoute);
}
}
Expand Down
77 changes: 38 additions & 39 deletions src/NSwag.AspNetCore/Extensions/NSwagServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using NSwag.SwaggerGeneration;
using NSwag.SwaggerGeneration.AspNetCore;
using System;
using System.Linq;

namespace Microsoft.Extensions.DependencyInjection
{
Expand All @@ -13,59 +14,57 @@ public static class NSwagServiceCollectionExtensions
{
/// <summary>Adds services required for Swagger 2.0 generation (change document settings to generate OpenAPI 3.0).</summary>
/// <param name="serviceCollection">The <see cref="IServiceCollection"/>.</param>
/// <param name="configure">Configure the document registry.</param>
public static IServiceCollection AddSwagger(this IServiceCollection serviceCollection, Action<ISwaggerDocumentBuilder> configure = null)
/// <param name="configure">Configure the document.</param>
public static IServiceCollection AddOpenApiDocument(this IServiceCollection serviceCollection, Action<SwaggerDocumentSettings> configure = null)
{
if (configure == null)
return AddSwaggerDocument(serviceCollection, settings =>
{
configure = registry => registry.AddSwaggerDocument();
}
settings.SchemaType = SchemaType.OpenApi3;
configure?.Invoke(settings);
});
}

/// <summary>Adds services required for Swagger 2.0 generation (change document settings to generate OpenAPI 3.0).</summary>
/// <param name="serviceCollection">The <see cref="IServiceCollection"/>.</param>
/// <param name="configure">Configure the document.</param>
public static IServiceCollection AddSwaggerDocument(this IServiceCollection serviceCollection, Action<SwaggerDocumentSettings> configure = null)
{
serviceCollection.AddSingleton(s =>
{
var registry = new SwaggerDocumentRegistry();
configure?.Invoke(registry);
return registry;
var settings = new SwaggerDocumentSettings();
settings.SchemaType = SchemaType.Swagger2;
configure?.Invoke(settings);
var generator = new AspNetCoreToSwaggerGenerator(settings, settings.SchemaGenerator ??
new SwaggerJsonSchemaGenerator(settings));
return new SwaggerDocumentRegistration(settings.DocumentName, generator);
});

serviceCollection.AddSingleton<IConfigureOptions<MvcOptions>, SwaggerConfigureMvcOptions>();
serviceCollection.AddSingleton<SwaggerDocumentProvider>();
var descriptor = serviceCollection.SingleOrDefault(d => d.ServiceType == typeof(SwaggerDocumentProvider));
if (descriptor == null)
{
serviceCollection.AddSingleton<SwaggerDocumentProvider>();
serviceCollection.AddSingleton<IConfigureOptions<MvcOptions>, SwaggerConfigureMvcOptions>();

// Used by UseDocumentProvider CLI setting
serviceCollection.AddSingleton<ISwaggerDocumentProvider>(s => s.GetRequiredService<SwaggerDocumentProvider>());
// Used by UseDocumentProvider CLI setting
serviceCollection.AddSingleton<ISwaggerDocumentProvider>(s => s.GetRequiredService<SwaggerDocumentProvider>());

// Used by the Microsoft.Extensions.ApiDescription tool
serviceCollection.AddSingleton<ApiDescription.IDocumentProvider>(s => s.GetRequiredService<SwaggerDocumentProvider>());
// Used by the Microsoft.Extensions.ApiDescription tool
serviceCollection.AddSingleton<ApiDescription.IDocumentProvider>(s => s.GetRequiredService<SwaggerDocumentProvider>());
}

return serviceCollection;
}

/// <summary>Adds a document to the registry.</summary>
/// <param name="registry">The registry.</param>
/// <param name="configure">The configure action.</param>
/// <returns>The registry.</returns>
public static ISwaggerDocumentBuilder AddOpenApiDocument(this ISwaggerDocumentBuilder registry, Action<SwaggerDocumentSettings> configure = null)
{
return AddSwaggerDocument(registry, settings =>
{
settings.SchemaType = SchemaType.OpenApi3;
configure?.Invoke(settings);
});
}

/// <summary>Adds a document to the registry.</summary>
/// <param name="registry">The registry.</param>
/// <param name="configure">The configure action.</param>
/// <returns>The registry.</returns>
public static ISwaggerDocumentBuilder AddSwaggerDocument(this ISwaggerDocumentBuilder registry, Action<SwaggerDocumentSettings> configure = null)
/// <summary>Adds services required for Swagger 2.0 generation (change document settings to generate OpenAPI 3.0).</summary>
/// <param name="serviceCollection">The <see cref="IServiceCollection"/>.</param>
/// <param name="configure">Configure the document.</param>
[Obsolete("Use " + nameof(AddSwaggerDocument) + "() instead.")]
public static IServiceCollection AddSwagger(this IServiceCollection serviceCollection, Action<SwaggerDocumentSettings> configure = null)
{
var settings = new SwaggerDocumentSettings();
settings.SchemaType = SchemaType.Swagger2;

configure?.Invoke(settings);

var generator = new AspNetCoreToSwaggerGenerator(settings, settings.SchemaGenerator ?? new SwaggerJsonSchemaGenerator(settings));
return ((SwaggerDocumentRegistry)registry).AddDocument(settings.DocumentName, generator);
return AddSwaggerDocument(serviceCollection, configure);
}
}
}
2 changes: 1 addition & 1 deletion src/NSwag.AspNetCore/Middlewares/SwaggerMiddleware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public SwaggerMiddleware(RequestDelegate nextDelegate, IServiceProvider serviceP
_apiDescriptionGroupCollectionProvider = serviceProvider.GetService<IApiDescriptionGroupCollectionProvider>() ??
throw new InvalidOperationException("API Explorer not registered in DI.");
_documentProvider = serviceProvider.GetService<SwaggerDocumentProvider>() ??
throw new InvalidOperationException("The NSwag DI services are not registered: Call " + nameof(NSwagServiceCollectionExtensions.AddSwagger) + "() in ConfigureServices().");
throw new InvalidOperationException("The NSwag DI services are not registered: Call " + nameof(NSwagServiceCollectionExtensions.AddSwaggerDocument) + "() in ConfigureServices().");

_settings = settings;
}
Expand Down
26 changes: 20 additions & 6 deletions src/NSwag.AspNetCore/SwaggerDocumentProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,25 @@
//-----------------------------------------------------------------------

using Microsoft.Extensions.ApiDescription;
using Microsoft.Extensions.DependencyInjection;
using NSwag.SwaggerGeneration;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

namespace NSwag.AspNetCore
{
internal class SwaggerDocumentProvider : IDocumentProvider, ISwaggerDocumentProvider
{
private readonly IServiceProvider _serviceProvider;
private readonly SwaggerDocumentRegistry _registry;
private readonly IEnumerable<SwaggerDocumentRegistration> _documents;

public SwaggerDocumentProvider(IServiceProvider serviceProvider, SwaggerDocumentRegistry registry)
public SwaggerDocumentProvider(IServiceProvider serviceProvider, IEnumerable<SwaggerDocumentRegistration> documents)
{
_serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
_registry = registry ?? throw new ArgumentNullException(nameof(registry));
_documents = documents ?? throw new ArgumentNullException(nameof(documents));
}

public async Task<SwaggerDocument> GenerateAsync(string documentName)
Expand All @@ -32,14 +35,25 @@ public async Task<SwaggerDocument> GenerateAsync(string documentName)
throw new ArgumentNullException(nameof(documentName));
}

_registry.Documents.TryGetValue(documentName, out var settings);
if (settings == null)
foreach (var group in _documents.GroupBy(g => g.DocumentName))
{
if (group.Count() > 1)
{
throw new ArgumentException("The OpenAPI/Swagger document '" + group.Key + "' registered multiple times: " +
"Explicitely set the DocumentName property in " +
nameof(NSwagServiceCollectionExtensions.AddSwaggerDocument) + "() or " +
nameof(NSwagServiceCollectionExtensions.AddOpenApiDocument) + "().");
}
}

var document = _documents.Single(g => g.DocumentName == documentName);
if (document.Generator == null)
{
throw new InvalidOperationException($"No registered OpenAPI/Swagger document found for the document name '{documentName}'. " +
$"Add with the AddSwagger()/AddOpenApi() methods in ConfigureServices().");
}

return await settings.GenerateAsync(_serviceProvider);
return await document.Generator.GenerateAsync(_serviceProvider);
}

// Called by the Microsoft.Extensions.ApiDescription tool
Expand Down
25 changes: 25 additions & 0 deletions src/NSwag.AspNetCore/SwaggerDocumentRegistration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//-----------------------------------------------------------------------
// <copyright file="RegisteredSwaggerDocument.cs" company="NSwag">
// Copyright (c) Rico Suter. All rights reserved.
// </copyright>
// <license>https://github.com/NSwag/NSwag/blob/master/LICENSE.md</license>
// <author>Rico Suter, mail@rsuter.com</author>
//-----------------------------------------------------------------------

using NSwag.SwaggerGeneration;

namespace NSwag.AspNetCore
{
internal class SwaggerDocumentRegistration
{
public SwaggerDocumentRegistration(string documentName, ISwaggerGenerator generator)
{
DocumentName = documentName;
Generator = generator;
}

public string DocumentName { get; }

public ISwaggerGenerator Generator { get; }
}
}
48 changes: 0 additions & 48 deletions src/NSwag.AspNetCore/SwaggerDocumentRegistry.cs

This file was deleted.

Loading

0 comments on commit 4f43152

Please sign in to comment.