Skip to content

Commit

Permalink
Add IDocumentProvider service and its implementation
Browse files Browse the repository at this point in the history
- RicoSuter#1626
- does not "provide an api to generate multiple / all documents"; could be layered on this work
  - otherwise, meets requirements listed in RicoSuter#1626
- assumes the following answers to open questions in RicoSuter#1626 (generally means everything is opt-in)
  - without an `AddSwagger()` call, `IDocumentProvider` service will not exist
  - registered document name / identifier is new `SwaggerSettings<>.DocumentName` property
    - document is not registered unless `DocumentName` is non-`null` i.e. `IDocumentProvider` won't know about it
    - `SwaggerSettings<>.MiddlewareBasePath` and `SwaggerRoute` are independent of `DocumentName`
  - middleware does not depend on the `IDocumentProvider` or registrar services; duplicating some code
- many thanks to @rynowak for writing this proposal
  • Loading branch information
rynowak authored and dougbu committed Oct 8, 2018
1 parent f7aef82 commit bd9c7fc
Show file tree
Hide file tree
Showing 7 changed files with 337 additions and 11 deletions.
52 changes: 52 additions & 0 deletions src/NSwag.AspNetCore/DocumentRegistry.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//-----------------------------------------------------------------------
// <copyright file="DocumentRegistry.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 System;
using System.Collections.Generic;

namespace NSwag.AspNetCore
{
internal class DocumentRegistry
{
private readonly Dictionary<string, RegisteredDocument> _documents;

public DocumentRegistry()
{
_documents = new Dictionary<string, RegisteredDocument>(StringComparer.Ordinal);
}

public RegisteredDocument this[string documentName]
{
get
{
if (documentName == null)
{
throw new ArgumentNullException(nameof(documentName));
}

_documents.TryGetValue(documentName, out var document);
return document;
}

set
{
if (documentName == null)
{
throw new ArgumentNullException(nameof(documentName));
}

if (value == null)
{
throw new ArgumentNullException(nameof(value));
}

_documents[documentName] = value;
}
}
}
}
20 changes: 20 additions & 0 deletions src/NSwag.AspNetCore/IDocumentProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//-----------------------------------------------------------------------
// <copyright file="IDocumentProvider.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 System.IO;
using System.Threading.Tasks;

namespace Microsoft.Extensions.ApiDescription
{
// This service will be looked up by name from the service collection when using
// the Microsoft.Extensions.ApiDescription tool
internal interface IDocumentProvider
{
Task GenerateAsync(string documentName, TextWriter writer);
}
}
116 changes: 116 additions & 0 deletions src/NSwag.AspNetCore/NSwagDocumentProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
//-----------------------------------------------------------------------
// <copyright file="NSwagDocumentProvider.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 System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.Extensions.ApiDescription;
using Microsoft.Extensions.Options;
using NSwag.SwaggerGeneration;
using NSwag.SwaggerGeneration.AspNetCore;
using NSwag.SwaggerGeneration.WebApi;

namespace NSwag.AspNetCore
{
internal class NSwagDocumentProvider : IDocumentProvider
{
private readonly IApiDescriptionGroupCollectionProvider _apiDescriptionGroupCollectionProvider;
private readonly IOptions<MvcOptions> _mvcOptions;
private readonly IOptions<MvcJsonOptions> _mvcJsonOptions;
private readonly DocumentRegistry _registry;

public NSwagDocumentProvider(
DocumentRegistry registry,
IApiDescriptionGroupCollectionProvider apiDescriptionGroupCollectionProvider,
IOptions<MvcOptions> mvcOptions,
IOptions<MvcJsonOptions> mvcJsonOptions)
{
if (apiDescriptionGroupCollectionProvider == null)
{
throw new ArgumentNullException(nameof(apiDescriptionGroupCollectionProvider));
}

if (mvcOptions == null)
{
throw new ArgumentNullException(nameof(mvcOptions));
}

if (mvcJsonOptions == null)
{
throw new ArgumentNullException(nameof(mvcJsonOptions));
}

if (registry == null)
{
throw new ArgumentNullException(nameof(registry));
}

_apiDescriptionGroupCollectionProvider = apiDescriptionGroupCollectionProvider;
_mvcOptions = mvcOptions;
_mvcJsonOptions = mvcJsonOptions;
_registry = registry;
}

public async Task<SwaggerDocument> GenerateAsync(string documentName)
{
if (documentName == null)
{
throw new ArgumentNullException(nameof(documentName));
}

var documentInfo = _registry[documentName];
if (documentInfo == null)
{
throw new InvalidOperationException($"No registered document found for document name '{documentName}'.");
}

documentInfo.Settings.ApplySettings(_mvcJsonOptions.Value.SerializerSettings, _mvcOptions.Value);

SwaggerDocument document;
if (documentInfo.Settings is AspNetCoreToSwaggerGeneratorSettings aspnetcore)
{
var generator = new AspNetCoreToSwaggerGenerator(aspnetcore, documentInfo.SchemaGenerator);
document = await generator.GenerateAsync(_apiDescriptionGroupCollectionProvider.ApiDescriptionGroups);
}
else if (documentInfo.Settings is WebApiToSwaggerGeneratorSettings webapi)
{
var generator = new WebApiToSwaggerGenerator(webapi, documentInfo.SchemaGenerator);
document = await generator.GenerateForControllersAsync(webapi.ControllerTypes);
}
else
{
throw new InvalidOperationException($"Unsupported settings type '{documentInfo.GetType().FullName}");
}

documentInfo.PostProcess?.Invoke(document);

return document;
}

// Called by the Microsoft.Extensions.ApiDescription tool
async Task IDocumentProvider.GenerateAsync(string documentName, TextWriter writer)
{
if (documentName == null)
{
throw new ArgumentNullException(nameof(documentName));
}

if (writer == null)
{
throw new ArgumentNullException(nameof(writer));
}

var document = await GenerateAsync(documentName);

var json = document.ToJson();
await writer.WriteAsync(json);
}
}
}
61 changes: 61 additions & 0 deletions src/NSwag.AspNetCore/RegisteredDocument.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//-----------------------------------------------------------------------
// <copyright file="RegisteredDocument.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 System;
using System.Collections.Generic;
using NSwag.SwaggerGeneration;
using NSwag.SwaggerGeneration.AspNetCore;
using NSwag.SwaggerGeneration.WebApi;

namespace NSwag.AspNetCore
{
// Captures the state required to produce a document. Mediates between two supported
// document generators.
internal class RegisteredDocument
{
public static RegisteredDocument CreateAspNetCoreGeneratorDocument(
AspNetCoreToSwaggerGeneratorSettings settings,
SwaggerJsonSchemaGenerator schemaGenerator,
Action<SwaggerDocument> postProcess)
{
return new RegisteredDocument()
{
PostProcess = postProcess,
SchemaGenerator = schemaGenerator,
Settings = settings,
};
}

public static RegisteredDocument CreateWebApiGeneratorDocument(
WebApiToSwaggerGeneratorSettings settings,
IEnumerable<Type> controllerTypes,
SwaggerJsonSchemaGenerator schemaGenerator,
Action<SwaggerDocument> postProcess)
{
return new RegisteredDocument()
{
ControllerTypes = controllerTypes,
PostProcess = postProcess,
SchemaGenerator = schemaGenerator,
Settings = settings,
};
}

private RegisteredDocument()
{
}

public IEnumerable<Type> ControllerTypes { get; private set; }

public Action<SwaggerDocument> PostProcess { get; private set; }

public SwaggerJsonSchemaGenerator SchemaGenerator { get; private set; }

public SwaggerGeneratorSettings Settings { get; private set; }
}
}
Loading

0 comments on commit bd9c7fc

Please sign in to comment.