Add OpenApiServer details to configuration options (#43)

This commit is contained in:
Justin Yoo 2021-02-10 02:42:38 +09:00 коммит произвёл GitHub
Родитель 4ab49bb90c
Коммит 94cca78bfb
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
18 изменённых файлов: 149 добавлений и 37 удалений

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

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Configurations;
using Microsoft.OpenApi.Models;
@ -25,5 +26,7 @@ namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V1Proxy.Configu
Url = new Uri("http://opensource.org/licenses/MIT"),
}
};
public List<OpenApiServer> Servers { get; set; } = new List<OpenApiServer>();
}
}

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

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Configurations;
using Microsoft.OpenApi.Models;
@ -25,5 +26,7 @@ namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V2IoC.Configura
Url = new Uri("http://opensource.org/licenses/MIT"),
}
};
public List<OpenApiServer> Servers { get; set; } = new List<OpenApiServer>();
}
}

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

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Configurations;
using Microsoft.OpenApi.Models;
@ -25,5 +26,7 @@ namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V2Static.Config
Url = new Uri("http://opensource.org/licenses/MIT"),
}
};
public List<OpenApiServer> Servers { get; set; } = new List<OpenApiServer>();
}
}

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

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Configurations;
using Microsoft.OpenApi.Models;
@ -25,5 +26,7 @@ namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V3IoC.Configura
Url = new Uri("http://opensource.org/licenses/MIT"),
}
};
public List<OpenApiServer> Servers { get; set; } = new List<OpenApiServer>();
}
}

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

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Configurations;
using Microsoft.OpenApi.Models;
@ -25,5 +26,11 @@ namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V3Static.Config
Url = new Uri("http://opensource.org/licenses/MIT"),
}
};
public List<OpenApiServer> Servers { get; set; } = new List<OpenApiServer>()
{
new OpenApiServer() { Url = "https://contoso.com/api/" },
new OpenApiServer() { Url = "https://fabrikam.com/api/" },
};
}
}

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

@ -1,3 +1,5 @@
using System.Collections.Generic;
using Microsoft.OpenApi.Models;
namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Configurations
@ -11,5 +13,10 @@ namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Configurations
/// Gets or sets the <see cref="OpenApiInfo"/> instance.
/// </summary>
OpenApiInfo Info { get; set; }
/// <summary>
/// Gets or sets the list of <see cref="OpenApiServer"/> instances.
/// </summary>
List<OpenApiServer> Servers { get; set; }
}
}

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

@ -20,8 +20,9 @@ namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Configurations
{
var basePath = this.GetBasePath();
var host = HostJsonResolver.Resolve(this.Config, basePath);
var options = OpenApiConfigurationResolver.Resolve(Assembly.GetExecutingAssembly());
this.OpenApiInfo = OpenApiInfoResolver.Resolve(Assembly.GetExecutingAssembly());
this.OpenApiInfo = options.Info;
this.SwaggerAuthKey = this.Config.GetValue<string>("OpenApi:ApiKey");
this.HttpSettings = host.GetHttpSettings();

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

@ -1,3 +1,5 @@
using System.Collections.Generic;
using Microsoft.OpenApi.Models;
namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Configurations
@ -13,5 +15,8 @@ namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Configurations
Version = "1.0.0",
Title = "Azure Functions Open API Extension",
};
/// <inheritdoc />
public List<OpenApiServer> Servers { get; set; } = new List<OpenApiServer>();
}
}

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

@ -1,3 +1,4 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
@ -5,7 +6,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Comparers;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Configurations;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Extensions;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Visitors;
using Microsoft.OpenApi;
@ -33,6 +34,13 @@ namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.Core
this._helper = helper.ThrowIfNullOrDefault();
}
/// <inheritdoc />
public Document(OpenApiDocument openApiDocument)
{
this.OpenApiDocument = openApiDocument;
}
/// <inheritdoc />
public OpenApiDocument OpenApiDocument { get; private set; }
@ -56,12 +64,27 @@ namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.Core
}
/// <inheritdoc />
public IDocument AddServer(HttpRequest req, string routePrefix)
public IDocument AddServer(HttpRequest req, string routePrefix, IOpenApiConfigurationOptions options = null)
{
var prefix = string.IsNullOrWhiteSpace(routePrefix) ? string.Empty : $"/{routePrefix}";
var baseUrl = $"{req.Scheme}://{req.Host}{prefix}";
this.OpenApiDocument.Servers.Add(new OpenApiServer { Url = baseUrl });
var server = new OpenApiServer { Url = baseUrl };
if (options.IsNullOrDefault())
{
this.OpenApiDocument.Servers = new List<OpenApiServer>() { server };
return this;
}
// Filters out the existing base URLs that are the same as the current host URL.
var servers = options.Servers
.Where(p => p.Url.TrimEnd('/') != baseUrl.TrimEnd('/'))
.ToList();
servers.Insert(0, server);
this.OpenApiDocument.Servers = servers;
return this;
}

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

@ -2,6 +2,7 @@ using System.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Configurations;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Visitors;
using Microsoft.OpenApi;
using Microsoft.OpenApi.Models;
@ -38,8 +39,9 @@ namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions
/// </summary>
/// <param name="req"><see cref="HttpRequest"/> instance.</param>
/// <param name="routePrefix">Route prefix value.</param>
/// <param name="options"><see cref="IOpenApiConfigurationOptions"/> instance.</param>
/// <returns><see cref="IDocument"/> instance.</returns>
IDocument AddServer(HttpRequest req, string routePrefix);
IDocument AddServer(HttpRequest req, string routePrefix, IOpenApiConfigurationOptions options = null);
/// <summary>
/// Adds the naming strategy.

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

@ -1,6 +1,7 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Configurations;
using Microsoft.OpenApi.Models;
namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions
@ -22,8 +23,9 @@ namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions
/// </summary>
/// <param name="req"><see cref="HttpRequest"/> instance.</param>
/// <param name="routePrefix">Route prefix value.</param>
/// <param name="options"><see cref="IOpenApiConfigurationOptions"/> instance.</param>
/// <returns><see cref="IDocument"/> instance.</returns>
ISwaggerUI AddServer(HttpRequest req, string routePrefix);
ISwaggerUI AddServer(HttpRequest req, string routePrefix, IOpenApiConfigurationOptions options = null);
/// <summary>
/// Builds Open API document.

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

@ -9,16 +9,16 @@ using Microsoft.OpenApi.Models;
namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Resolvers
{
/// <summary>
/// This represents the resolver entity for <see cref="OpenApiInfo"/> from one of host.json, openapisettings.json and environment variables.
/// This represents the resolver entity for <see cref="OpenApiServer"/>.
/// </summary>
public static class OpenApiInfoResolver
public static class OpenApiConfigurationResolver
{
/// <summary>
/// Gets the <see cref="OpenApiInfo"/> instance from one of host.json, openapisettings.json and environment variables.
/// Gets the <see cref="IOpenApiConfigurationOptions"/> instance from the given assembly.
/// </summary>
/// <param name="assembly">The executing assembly instance.</param>
/// <returns>Returns <see cref="OpenApiInfo"/> instance resolved.</returns>
public static OpenApiInfo Resolve(Assembly assembly)
/// <returns>Returns the <see cref="IOpenApiConfigurationOptions"/> instance resolved.</returns>
public static IOpenApiConfigurationOptions Resolve(Assembly assembly)
{
var type = assembly.GetTypes()
.SingleOrDefault(p => p.GetInterface("IOpenApiConfigurationOptions", ignoreCase: true).IsNullOrDefault() == false);
@ -26,12 +26,12 @@ namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Resolvers
{
var settings = new OpenApiSettings();
return settings.Info;
return settings;
}
var options = Activator.CreateInstance(type);
return (options as IOpenApiConfigurationOptions).Info;
return options as IOpenApiConfigurationOptions;
}
}
}

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

@ -1,9 +1,11 @@
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Configurations;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Extensions;
using Microsoft.OpenApi.Models;
@ -41,15 +43,30 @@ namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.Core
}
/// <inheritdoc />
public ISwaggerUI AddServer(HttpRequest req, string routePrefix)
public ISwaggerUI AddServer(HttpRequest req, string routePrefix, IOpenApiConfigurationOptions options = null)
{
var prefix = string.IsNullOrWhiteSpace(routePrefix) ? string.Empty : $"/{routePrefix}";
var baseUrl = $"{req.Scheme}://{req.Host}{prefix}";
if (options.IsNullOrDefault())
{
this._baseUrl = baseUrl;
return this;
}
var server = new OpenApiServer { Url = baseUrl };
// Filters out the existing base URLs that are the same as the current host URL.
var servers = options.Servers
.Where(p => p.Url.TrimEnd('/') != baseUrl.TrimEnd('/'))
.ToList();
servers.Insert(0, server);
this._baseUrl = servers.First().Url;
return this;
}
/// <inheritdoc />
public async Task<ISwaggerUI> BuildAsync()
{
@ -97,7 +114,7 @@ namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.Core
private string Render(string endpoint, string authKey = null)
{
var swaggerUiTitle = $"{this._info.Title} - Swagger UI";
var swaggerUrl = $"{this._baseUrl}/{endpoint}";
var swaggerUrl = $"{this._baseUrl.TrimEnd('/')}/{endpoint}";
if (!string.IsNullOrWhiteSpace(authKey))
{
swaggerUrl += $"?code={authKey}";

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

@ -1,3 +1,4 @@
using System.Collections.Generic;
using System.Reflection;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions;
@ -18,9 +19,9 @@ namespace Microsoft.Azure.WebJobs.Extensions.OpenApi
public interface IOpenApiHttpTriggerContext
{
/// <summary>
/// Gets the <see cref="OpenApiInfo"/> instance.
/// Gets the <see cref="IOpenApiConfigurationOptions"/> instance.
/// </summary>
OpenApiInfo OpenApiInfo { get; }
IOpenApiConfigurationOptions OpenApiConfiguration { get; }
/// <summary>
/// Gets the <see cref="HttpSettings"/> instance.

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

@ -48,8 +48,8 @@ namespace Microsoft.Azure.WebJobs.Extensions.OpenApi
var result = await context.Document
.InitialiseDocument()
.AddMetadata(context.OpenApiInfo)
.AddServer(req, context.HttpSettings.RoutePrefix)
.AddMetadata(context.OpenApiConfiguration.Info)
.AddServer(req, context.HttpSettings.RoutePrefix, context.OpenApiConfiguration)
.AddNamingStrategy(context.NamingStrategy)
.AddVisitors(context.GetVisitorCollection())
.Build(context.GetExecutingAssembly())
@ -86,8 +86,8 @@ namespace Microsoft.Azure.WebJobs.Extensions.OpenApi
var result = await context.Document
.InitialiseDocument()
.AddMetadata(context.OpenApiInfo)
.AddServer(req, context.HttpSettings.RoutePrefix)
.AddMetadata(context.OpenApiConfiguration.Info)
.AddServer(req, context.HttpSettings.RoutePrefix, context.OpenApiConfiguration)
.AddNamingStrategy(context.NamingStrategy)
.AddVisitors(context.GetVisitorCollection())
.Build(context.GetExecutingAssembly())
@ -119,8 +119,8 @@ namespace Microsoft.Azure.WebJobs.Extensions.OpenApi
log.LogInformation($"SwaggerUI page was requested.");
var result = await context.SwaggerUI
.AddMetadata(context.OpenApiInfo)
.AddServer(req, context.HttpSettings.RoutePrefix)
.AddMetadata(context.OpenApiConfiguration.Info)
.AddServer(req, context.HttpSettings.RoutePrefix, context.OpenApiConfiguration)
.BuildAsync()
.RenderAsync("swagger.json", context.GetSwaggerAuthKey())
.ConfigureAwait(false);

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

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
@ -35,7 +36,7 @@ namespace Microsoft.Azure.WebJobs.Extensions.OpenApi
{
var host = HostJsonResolver.Resolve();
this.OpenApiInfo = OpenApiInfoResolver.Resolve(this.GetExecutingAssembly());
this.OpenApiConfiguration = OpenApiConfigurationResolver.Resolve(this.GetExecutingAssembly());
this.HttpSettings = host.GetHttpSettings();
var filter = new RouteConstraintFilter();
@ -47,7 +48,7 @@ namespace Microsoft.Azure.WebJobs.Extensions.OpenApi
}
/// <inheritdoc />
public virtual OpenApiInfo OpenApiInfo { get; }
public virtual IOpenApiConfigurationOptions OpenApiConfiguration { get; }
/// <inheritdoc />
public virtual HttpSettings HttpSettings { get; }

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

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Threading.Tasks;
@ -6,6 +7,7 @@ using FluentAssertions;
using Microsoft.AspNetCore.Http;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Configurations;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Visitors;
using Microsoft.OpenApi;
using Microsoft.OpenApi.Models;
@ -22,9 +24,9 @@ namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Tests
public class DocumentTests
{
[TestMethod]
public void Given_Null_Constructor_Should_Throw_Exception()
public void Given_Null_When_Instantiated_Then_It_Should_Throw_Exception()
{
Action action = () => new Document(null);
Action action = () => new Document((IDocumentHelper)null);
action.Should().Throw<ArgumentNullException>();
}
@ -70,7 +72,7 @@ namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Tests
}
[TestMethod]
public async Task Given_VersionAndFormat_RenderAsync_Should_Return_Result()
public async Task Given_VersionAndFormat_When_RenderAsync_Invoked_Then_It_Should_Return_Result()
{
var helper = new Mock<IDocumentHelper>();
var doc = new Document(helper.Object);
@ -84,7 +86,7 @@ namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Tests
}
[TestMethod]
public async Task Given_Metadata_RenderAsync_Should_Return_Result()
public async Task Given_Metadata_When_RenderAsync_Invoked_Then_It_Should_Return_Result()
{
var helper = new Mock<IDocumentHelper>();
@ -103,13 +105,14 @@ namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Tests
}
[TestMethod]
public async Task Given_ServerDetails_RenderAsync_Should_Return_Result()
public async Task Given_ServerDetails_When_RenderAsync_Invoked_Then_It_Should_Return_Result()
{
var helper = new Mock<IDocumentHelper>();
var scheme = "https";
var host = "localhost";
var routePrefix = "api";
var url = $"{scheme}://{host}";
var req = new Mock<HttpRequest>();
req.SetupGet(p => p.Scheme).Returns(scheme);
@ -129,13 +132,43 @@ namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Tests
}
[TestMethod]
public async Task Given_ServerDetails_WithNullRoutePrefix_RenderAsync_Should_Return_Result()
public async Task Given_ServerDetails_With_ConfigurationOptions_When_RenderAsync_Invoked_Then_It_Should_Return_Result()
{
var helper = new Mock<IDocumentHelper>();
var scheme = "https";
var host = "localhost";
var routePrefix = "api";
var req = new Mock<HttpRequest>();
req.SetupGet(p => p.Scheme).Returns(scheme);
req.SetupGet(p => p.Host).Returns(new HostString(host));
var options = new Mock<IOpenApiConfigurationOptions>();
options.SetupGet(p => p.Servers).Returns(new List<OpenApiServer>() { new OpenApiServer() { Url = $"https://contoso.com/{routePrefix}" } });
var doc = new Document(helper.Object);
var result = await doc.InitialiseDocument()
.AddServer(req.Object, routePrefix, options.Object)
.RenderAsync(OpenApiSpecVersion.OpenApi2_0, OpenApiFormat.Json);
dynamic json = JObject.Parse(result);
((string)json?.host).Should().BeEquivalentTo(host);
((string)json?.basePath).Should().BeEquivalentTo($"/{routePrefix}");
((string)json?.schemes[0]).Should().BeEquivalentTo(scheme);
}
[TestMethod]
public async Task Given_ServerDetails_WithNullRoutePrefix_When_RenderAsync_Invoked_Then_It_Should_Return_Result()
{
var helper = new Mock<IDocumentHelper>();
var scheme = "https";
var host = "localhost";
string routePrefix = null;
var url = $"{scheme}://{host}";
var req = new Mock<HttpRequest>();
req.SetupGet(p => p.Scheme).Returns(scheme);
@ -155,13 +188,14 @@ namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Tests
}
[TestMethod]
public async Task Given_ServerDetails_WithEmptyRoutePrefix_RenderAsync_Should_Return_Result()
public async Task Given_ServerDetails_WithEmptyRoutePrefix_When_RenderAsync_Invoked_Then_It_Should_Return_Result()
{
var helper = new Mock<IDocumentHelper>();
var scheme = "https";
var host = "localhost";
var routePrefix = string.Empty;
var url = $"{scheme}://{host}";
var req = new Mock<HttpRequest>();
req.SetupGet(p => p.Scheme).Returns(scheme);

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

@ -2,21 +2,21 @@ using System.Reflection;
using FluentAssertions;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Configurations;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Resolvers;
using Microsoft.OpenApi.Models;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Tests.Resolvers
{
[TestClass]
public class OpenApiInfoResolverTests
public class OpenApiConfigurationResolverTests
{
[TestMethod]
public void Given_Type_Then_It_Should_Have_Methods()
{
typeof(OpenApiInfoResolver)
typeof(OpenApiConfigurationResolver)
.Should().HaveMethod("Resolve", new[] { typeof(Assembly) })
.Which.Should().Return<OpenApiInfo>();
.Which.Should().Return<IOpenApiConfigurationOptions>();
}
}
}