Provide extensibility for UI customizations (#74)

This commit is contained in:
Steve Ashman 2021-04-01 11:08:46 -04:00 коммит произвёл GitHub
Родитель 93423b6217
Коммит ee89ea3461
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
13 изменённых файлов: 192 добавлений и 4 удалений

Просмотреть файл

@ -0,0 +1,18 @@
using System.Reflection;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Configurations;
namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V3Static.Configurations
{
public class OpenApiCustomUIOptions : DefaultOpenApiCustomUIOptions
{
public OpenApiCustomUIOptions(Assembly assembly)
: base(assembly)
{
}
public override string CustomStylesheetPath { get; } = "dist.my-custom.css";
public override string CustomJavaScriptPath { get; } = "dist.my-custom.js";
}
}

Просмотреть файл

@ -40,6 +40,13 @@
</None>
</ItemGroup>
<!-- Uncomment this block if you want to use custom UI -->
<ItemGroup>
<EmbeddedResource Include="dist\my-custom.css" />
<EmbeddedResource Include="dist\my-custom.js" />
</ItemGroup>
<!-- Uncomment this block if you want to use custom UI -->
<!-- Comment this block if you want to use NuGet package from https://nuget.org -->
<ItemGroup>
<Compile Include="..\..\templates\OpenApiEndpoints\IOpenApiHttpTriggerContext.cs" />

Просмотреть файл

@ -0,0 +1,3 @@
.swagger-ui .topbar {
background-color: #003399
}

Просмотреть файл

@ -0,0 +1 @@
console.log("Custom UI: Open API Sample on Azure Functions (STATIC)");

Просмотреть файл

@ -0,0 +1,67 @@
using System.IO;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Extensions;
namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Configurations
{
/// <summary>
/// Default implementation of <see cref="IOpenApiCustomUIOptions"/>, providing
/// empty replacements for custom javascript and stylesheets
/// </summary>
public class DefaultOpenApiCustomUIOptions : IOpenApiCustomUIOptions
{
private readonly Assembly _assembly;
/// <summary>
/// Initializes a new instance of the <see cref="DefaultOpenApiCustomUIOptions"/> class.
/// </summary>
/// <param name="assembly"><see cref="Assembly"/> instance.</param>
public DefaultOpenApiCustomUIOptions(Assembly assembly)
{
this._assembly = assembly.ThrowIfNullOrDefault();
}
/// <inheritdoc/>
public virtual string CustomStylesheetPath { get; } = "dist.custom.css";
/// <inheritdoc/>
public virtual string CustomJavaScriptPath { get; } = "dist.custom.js";
/// <inheritdoc/>
public virtual async Task<string> GetStylesheetAsync()
{
using (var stream = this._assembly.GetManifestResourceStream($"{this._assembly.GetName().Name}.{this.CustomStylesheetPath}"))
{
if (stream.IsNullOrDefault())
{
return string.Empty;
}
using (var reader = new StreamReader(stream))
{
return await reader.ReadToEndAsync();
}
}
}
/// <inheritdoc/>
public virtual async Task<string> GetJavaScriptAsync()
{
using (var stream = this._assembly.GetManifestResourceStream($"{this._assembly.GetName().Name}.{this.CustomJavaScriptPath}"))
{
if (stream.IsNullOrDefault())
{
return string.Empty;
}
using (var reader = new StreamReader(stream))
{
return await reader.ReadToEndAsync();
}
}
}
}
}

Просмотреть файл

@ -0,0 +1,33 @@
using System.Threading.Tasks;
namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions
{
/// <summary>
/// Interface for a custom UI provider that can provide custom javascript
/// and CSS to be populated on a page
/// </summary>
public interface IOpenApiCustomUIOptions
{
/// <summary>
/// Gets the filepath for stylesheet for custom UI.
/// </summary>
string CustomStylesheetPath { get; }
/// <summary>
/// Gets filepath for JavaScript for custom UI.
/// </summary>
string CustomJavaScriptPath { get; }
/// <summary>
/// Gets the stylesheet to be rendered on the page.
/// </summary>
/// <returns>The stylesheet string for custom UI.</returns>
Task<string> GetStylesheetAsync();
/// <summary>
/// Gets the javascript to be rendered on the page.
/// </summary>
/// <returns>The JavaScript string for custom UI.</returns>
Task<string> GetJavaScriptAsync();
}
}

Просмотреть файл

@ -30,8 +30,9 @@ namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions
/// <summary>
/// Builds Swagger UI document.
/// </summary>
/// <param name="options"><see cref="IOpenApiCustomUIOptions"/> instance.</param>
/// <returns><see cref="ISwaggerUI"/> instance.</returns>
Task<ISwaggerUI> BuildAsync();
Task<ISwaggerUI> BuildAsync(IOpenApiCustomUIOptions options = null);
/// <summary>
/// Builds OAuth2 Redirect document.

Просмотреть файл

@ -0,0 +1,35 @@
using System;
using System.Linq;
using System.Reflection;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Configurations;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Extensions;
namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Resolvers
{
/// <summary>
/// This represents the resolver entity for <see cref="IOpenApiCustomUIOptions"/>.
/// </summary>
public static class OpenApiCustomUIResolver
{
/// <summary>
/// Gets the <see cref="IOpenApiCustomUIOptions"/> instance from the given assembly.
/// </summary>
/// <param name="assembly">The executing assembly instance.</param>
/// <returns>Returns the <see cref="IOpenApiCustomUIOptions"/> instance resolved.</returns>
public static IOpenApiCustomUIOptions Resolve(Assembly assembly)
{
var type = assembly.GetTypes()
.SingleOrDefault(p => p.GetInterface("IOpenApiCustomUIOptions", ignoreCase: true).IsNullOrDefault() == false);
if (type.IsNullOrDefault())
{
return new DefaultOpenApiCustomUIOptions(assembly);
}
var options = Activator.CreateInstance(type, assembly);
return options as IOpenApiCustomUIOptions;
}
}
}

Просмотреть файл

@ -18,7 +18,9 @@ namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.Core
{
private const string SwaggerUITitlePlaceholder = "[[SWAGGER_UI_TITLE]]";
private const string SwaggerUICssPlaceholder = "[[SWAGGER_UI_CSS]]";
private const string SwaggerUICustomCssPlaceholder = "[[SWAGGER_UI_CUSTOM_CSS]]";
private const string SwaggerUIBundleJsPlaceholder = "[[SWAGGER_UI_BUNDLE_JS]]";
private const string SwaggerUICustomJsPlaceholder = "[[SWAGGER_UI_CUSTOM_JS]]";
private const string SwaggerUIStandalonePresetJsPlaceholder = "[[SWAGGER_UI_STANDALONE_PRESET_JS]]";
private const string SwaggerUIApiPrefix = "[[SWAGGER_UI_API_PREFIX]]";
private const string SwaggerUrlPlaceholder = "[[SWAGGER_URL]]";
@ -32,7 +34,9 @@ namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.Core
private OpenApiInfo _info;
private string _baseUrl;
private string _swaggerUiCss;
private string _swaggerUiCustomCss;
private string _swaggerUiBundleJs;
private string _swaggerUiCustomJs;
private string _swaggerUiStandalonePresetJs;
private string _swaggerUiApiPrefix;
private string _indexHtml;
@ -73,10 +77,16 @@ namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.Core
}
/// <inheritdoc />
public async Task<ISwaggerUI> BuildAsync()
public async Task<ISwaggerUI> BuildAsync(IOpenApiCustomUIOptions options = null)
{
var assembly = Assembly.GetExecutingAssembly();
if (!options.IsNullOrDefault())
{
this._swaggerUiCustomCss = await options.GetStylesheetAsync();
this._swaggerUiCustomJs = await options.GetJavaScriptAsync();
}
using (var stream = assembly.GetManifestResourceStream(swaggerUiCss))
using (var reader = new StreamReader(stream))
{
@ -152,7 +162,9 @@ namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.Core
var html = this._indexHtml.Replace(SwaggerUITitlePlaceholder, swaggerUiTitle)
.Replace(SwaggerUICssPlaceholder, this._swaggerUiCss)
.Replace(SwaggerUICustomCssPlaceholder, this._swaggerUiCustomCss)
.Replace(SwaggerUIBundleJsPlaceholder, this._swaggerUiBundleJs)
.Replace(SwaggerUICustomJsPlaceholder, this._swaggerUiCustomJs)
.Replace(SwaggerUIStandalonePresetJsPlaceholder, this._swaggerUiStandalonePresetJs)
.Replace(SwaggerUrlPlaceholder, swaggerUrl);

Просмотреть файл

@ -28,12 +28,14 @@
margin:0;
background: #fafafa;
}
[[SWAGGER_UI_CUSTOM_CSS]]
</style>
</head>
<body>
<div id="swagger-ui"></div>
<script>[[SWAGGER_UI_CUSTOM_JS]]</script>
<script>[[SWAGGER_UI_BUNDLE_JS]]</script>
<script>[[SWAGGER_UI_STANDALONE_PRESET_JS]]</script>
<script>

Просмотреть файл

@ -23,6 +23,11 @@ namespace Microsoft.Azure.WebJobs.Extensions.OpenApi
/// </summary>
IOpenApiConfigurationOptions OpenApiConfiguration { get; }
/// <summary>
/// Gets the <see cref="IOpenApiCustomUIOptions"/> instance.
/// </summary>
IOpenApiCustomUIOptions OpenApiCustomUIOptions { get; }
/// <summary>
/// Gets the <see cref="HttpSettings"/> instance.
/// </summary>

Просмотреть файл

@ -121,7 +121,7 @@ namespace Microsoft.Azure.WebJobs.Extensions.OpenApi
var result = await context.SwaggerUI
.AddMetadata(context.OpenApiConfiguration.Info)
.AddServer(req, context.HttpSettings.RoutePrefix, context.OpenApiConfiguration)
.BuildAsync()
.BuildAsync(context.OpenApiCustomUIOptions)
.RenderAsync("swagger.json", context.GetSwaggerAuthKey())
.ConfigureAwait(false);

Просмотреть файл

@ -37,6 +37,7 @@ namespace Microsoft.Azure.WebJobs.Extensions.OpenApi
var host = HostJsonResolver.Resolve();
this.OpenApiConfiguration = OpenApiConfigurationResolver.Resolve(this.GetExecutingAssembly());
this.OpenApiCustomUIOptions = OpenApiCustomUIResolver.Resolve(this.GetExecutingAssembly());
this.HttpSettings = host.GetHttpSettings();
var filter = new RouteConstraintFilter();
@ -50,6 +51,9 @@ namespace Microsoft.Azure.WebJobs.Extensions.OpenApi
/// <inheritdoc />
public virtual IOpenApiConfigurationOptions OpenApiConfiguration { get; }
/// <inheritdoc />
public virtual IOpenApiCustomUIOptions OpenApiCustomUIOptions { get; }
/// <inheritdoc />
public virtual HttpSettings HttpSettings { get; }