Skip to content

Support dynamic OpenApiConfigurationOptions #432

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Aug 8, 2022
61 changes: 60 additions & 1 deletion docs/openapi-core.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ public class OpenApiConfigurationOptions : IOpenApiConfigurationOptions
}
```


### Overriding Base URLs ###

It's often required for the API app to have more than one base URL, with different hostname. To have [additional server URL information](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.1.md#serverObject), declare the `OpenApi__HostNames` value with comma delimited base URLs. Then, it will automatically sets up your base URLs.
Expand Down Expand Up @@ -199,6 +198,66 @@ public class MyOpenApiConfigurationOptions : DefaultOpenApiConfigurationOptions
}
```

### Dynamically configuring `IOpenApiConfigurationOptions` at Runtime ###

There may be instances where you want to configure the `IOpenApiConfigurationOptions` at runtime.

To dynamically modify your configuration options at runtime you can use the following examples:

#### In of Proc ####

```csharp
public override void Configure(IFunctionsHostBuilder builder)
{
var fixture = new Fixture();
builder.Services.AddSingleton(fixture);

// Example: If you want to change the configuration during startup of your function you can use the following code:
services.AddSingleton<IOpenApiConfigurationOptions>(x =>
{
return new OpenApiConfigurationOptions()
{
Info = new Microsoft.OpenApi.Models.OpenApiInfo
{
Title = "A dynamic title generated at runtime",
Description = "Dynamic Open API information at runtime"
}
};
});
}
```

#### Out of Proc ####

```csharp
public static void Main()
{
var host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults(worker => worker.UseNewtonsoftJson())
.ConfigureOpenApi()
.ConfigureServices(services => services.AddSingleton<Fixture>())
.ConfigureServices(services => {
services.AddSingleton<Fixture>();

// Example: If you want to change the configuration during startup of your function you can use the following code:
services.AddSingleton<IOpenApiConfigurationOptions>(x =>
{
return new OpenApiConfigurationOptions()
{
Info = new Microsoft.OpenApi.Models.OpenApiInfo
{
Title = "A dynamic title generated at runtime",
Description = "Dynamic Open API information at runtime"
}
};
});
})
.Build();

host.Run();
}
```

## Swagger UI Customisation ##

Suppose you want to customise the look and feels of the Swagger UI page. In this case, there are two options you can choose.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using AutoFixture;

using Microsoft.Azure.Functions.Worker.Extensions.OpenApi.Extensions;
using Microsoft.Azure.Functions.Worker.Extensions.OpenApi.FunctionApp.OutOfProc.Configurations;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

Expand All @@ -13,7 +15,23 @@ public static void Main()
var host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults(worker => worker.UseNewtonsoftJson())
.ConfigureOpenApi()
.ConfigureServices(services => services.AddSingleton<Fixture>())
.ConfigureServices(services => {
services.AddSingleton<Fixture>();

// Example: If you want to change the configuration during startup of your function you can use the following code:

//services.AddSingleton<IOpenApiConfigurationOptions>(x =>
//{
// return new OpenApiConfigurationOptions()
// {
// Info = new Microsoft.OpenApi.Models.OpenApiInfo
// {
// Title = "A dynamic title generated at runtime",
// Description = "Dynamic Open API information at runtime"
// }
// };
//});
})
.Build();

host.Run();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
using System;
using System.Net;

using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Configurations;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Enums;
using Microsoft.OpenApi.Models;

using static System.Net.Mime.MediaTypeNames;

namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.InProc.Configurations
{
public class OpenApiConfigurationOptions : DefaultOpenApiConfigurationOptions
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using AutoFixture;

using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.InProc.Configurations;
using Microsoft.Extensions.DependencyInjection;

[assembly: FunctionsStartup(typeof(Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.InProc.Startup))]
Expand All @@ -12,7 +14,21 @@ public class Startup : FunctionsStartup
public override void Configure(IFunctionsHostBuilder builder)
{
var fixture = new Fixture();
builder.Services.AddSingleton(fixture);
builder.Services.AddSingleton(fixture);

// Example: If you want to change the configuration during startup of your function you can use the following code:

//builder.Services.AddSingleton<IOpenApiConfigurationOptions>((_) =>
//{
// return new OpenApiConfigurationOptions()
// {
// Info = new Microsoft.OpenApi.Models.OpenApiInfo
// {
// Title = "A dynamic title generated at runtime",
// Description = "Dynamic Open API information at runtime"
// }
// };
//});
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Microsoft.Azure.Functions.Worker.Extensions.OpenApi.Functions;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Configurations;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

Expand All @@ -20,7 +21,7 @@ public static IHostBuilder ConfigureOpenApi(this IHostBuilder hostBuilder)
hostBuilder.ConfigureServices(services =>
{
services.AddSingleton<IOpenApiHttpTriggerContext, OpenApiHttpTriggerContext>();
services.AddSingleton<IOpenApiTriggerFunction, OpenApiTriggerFunction>();
services.AddSingleton<IOpenApiTriggerFunction, OpenApiTriggerFunction>();
// services.AddSingleton<DefaultOpenApiHttpTrigger, DefaultOpenApiHttpTrigger>();
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,9 @@ public class OpenApiHttpTriggerContext : IOpenApiHttpTriggerContext
/// <summary>
/// Initializes a new instance of the <see cref="OpenApiHttpTriggerContext"/> class.
/// </summary>
public OpenApiHttpTriggerContext()
public OpenApiHttpTriggerContext(IOpenApiConfigurationOptions configOptions = null)
{
this._configOptions = configOptions;
this.PackageAssembly = this.GetAssembly<ISwaggerUI>();

var host = HostJsonResolver.Resolve();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

<ItemGroup>
<PackageReference Include="Cocona.Lite" Version="1.4.0" />
<PackageReference Include="Microsoft.Azure.WebJobs" Version="3.0.5" />
<PackageReference Include="Microsoft.Azure.WebJobs" Version="3.0.33" />
<PackageReference Include="Moq" Version="4.18.1" />
<PackageReference Include="System.IO.FileSystem.Primitives" Version="4.3.0" />
<PackageReference Include="System.Runtime.Handles" Version="4.3.0" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System.Collections.Generic;

using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Enums;

using Newtonsoft.Json;

namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions
{
/// <summary>
/// This represents the metadata entity for OpenApiHttpTrigger context binding.
/// </summary>
public class OpenApiHttpTriggerContextBindingMetadata
{
/// <summary>
/// Gets or sets the name of the binding parameter. Default value is <c>context</c>.
/// </summary>
[JsonRequired]
[JsonProperty("name")]
public virtual string Name { get; set; } = "context";

/// <summary>
/// Gets or sets the binding type. Default value is <c>openApiHttpTriggerContext</c>.
/// </summary>
[JsonRequired]
[JsonProperty("type")]
public virtual string Type { get; set; } = "openApiHttpTriggerContext";

/// <summary>
/// Gets or sets the binding direction. Default value is <see cref="BindingDirectionType.In"/>.
/// </summary>
[JsonRequired]
[JsonProperty("direction")]
public virtual BindingDirectionType Direction { get; set; } = BindingDirectionType.In;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.Http" Version="3.0.2" />
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.Http" Version="3.0.12" />
<PackageReference Include="Microsoft.OpenApi" Version="1.2.3" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="System.IO.FileSystem.Primitives" Version="4.3.0" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System;
using System.Linq;
using System.Reflection;

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;
Expand All @@ -13,7 +12,7 @@ namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Resolvers
/// This represents the resolver entity for <see cref="OpenApiServer"/>.
/// </summary>
public static class OpenApiConfigurationResolver
{
{
/// <summary>
/// Gets the <see cref="IOpenApiConfigurationOptions"/> instance from the given assembly.
/// </summary>
Expand All @@ -25,12 +24,12 @@ public static IOpenApiConfigurationOptions Resolve(Assembly assembly)
.SingleOrDefault(p => p.GetInterface("IOpenApiConfigurationOptions", ignoreCase: true).IsNullOrDefault() == false
&& p.IsAbstract == false
&& p.GetCustomAttribute<ObsoleteAttribute>(inherit: false).IsNullOrDefault() == true);
if (type.IsNullOrDefault())
if (type.IsNullOrDefault())
{
var settings = new DefaultOpenApiConfigurationOptions();

return settings;
}
}

var options = Activator.CreateInstance(type);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,11 @@ public class OpenApiHttpTriggerContext : IOpenApiHttpTriggerContext
private IOpenApiCustomUIOptions _uiOptions;

/// <summary>
/// Initializes a new instance of the <see cref="OpenApiTriggerFunctionProvider"/> class.
/// Initializes a new instance of the <see cref="OpenApiHttpTriggerContext"/> class.
/// </summary>
public OpenApiHttpTriggerContext()
{
public OpenApiHttpTriggerContext(IOpenApiConfigurationOptions configOptions = null)
{
this._configOptions = configOptions;
this.PackageAssembly = this.GetAssembly<ISwaggerUI>();

var host = HostJsonResolver.Resolve();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ namespace Microsoft.Azure.WebJobs.Extensions.OpenApi
/// <summary>
/// This represents the function provider entity for OpenAPI HTTP triggers.
/// </summary>
public partial class OpenApiTriggerFunctionProvider : IFunctionProvider
public class OpenApiTriggerFunctionProvider : IFunctionProvider
{
private const string RenderSwaggerDocumentKey = "RenderSwaggerDocument";
private const string RenderOpenApiDocumentKey = "RenderOpenApiDocument";
private const string RenderSwaggerUIKey = "RenderSwaggerUI";
private const string RenderOAuth2RedirectKey = "RenderOAuth2Redirect";
private const string RenderSwaggerDocumentKey = nameof(OpenApiTriggerFunctions.RenderSwaggerDocument);
private const string RenderOpenApiDocumentKey = nameof(OpenApiTriggerFunctions.RenderOpenApiDocument);
private const string RenderSwaggerUIKey = nameof(OpenApiTriggerFunctions.RenderSwaggerUI);
private const string RenderOAuth2RedirectKey = nameof(OpenApiTriggerFunctions.RenderOAuth2Redirect);

private readonly OpenApiSettings _settings;
private readonly Dictionary<string, HttpBindingMetadata> _bindings;
Expand Down Expand Up @@ -133,15 +133,21 @@ private FunctionMetadata GetFunctionMetadata(string functionName)
Name = functionName,
FunctionDirectory = null,
ScriptFile = $"assembly:{assembly.FullName}",
EntryPoint = $"{assembly.GetName().Name}.{typeof(OpenApiTriggerFunctionProvider).Name}.{functionName}",
EntryPoint = $"{assembly.GetName().Name}.{typeof(OpenApiTriggerFunctions).Name}.{functionName}",
Language = "DotNetAssembly"
};

var jo = JObject.FromObject(this._bindings[functionName]);
var binding = BindingMetadata.Create(jo);
functionMetadata.Bindings.Add(binding);
AddBindingMetadata(functionMetadata, this._bindings[functionName]);
AddBindingMetadata(functionMetadata, new OpenApiHttpTriggerContextBindingMetadata());

return functionMetadata;
}

private static void AddBindingMetadata(FunctionMetadata functionMetadata, object bindingInfo)
{
var jsonObject = JObject.FromObject(bindingInfo);
var bindingMetadata = BindingMetadata.Create(jsonObject);
functionMetadata.Bindings.Add(bindingMetadata);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Attributes;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Extensions;
using Microsoft.Extensions.Logging;
Expand All @@ -14,15 +13,13 @@ namespace Microsoft.Azure.WebJobs.Extensions.OpenApi
/// <summary>
/// This represents the function provider entity for OpenAPI HTTP triggers.
/// </summary>
public partial class OpenApiTriggerFunctionProvider
public class OpenApiTriggerFunctions
{
private const string ContentTypeText = "text/plain";
private const string ContentTypeHtml = "text/html";
private const string ContentTypeJson = "application/json";
private const string ContentTypeYaml = "text/vnd.yaml";

private static readonly IOpenApiHttpTriggerContext context = new OpenApiHttpTriggerContext();

/// <summary>
/// Invokes the HTTP trigger endpoint to get OpenAPI document.
/// </summary>
Expand All @@ -32,7 +29,7 @@ public partial class OpenApiTriggerFunctionProvider
/// <param name="log"><see cref="ILogger"/> instance.</param>
/// <returns>OpenAPI document in a format of either JSON or YAML.</returns>
[OpenApiIgnore]
public static async Task<IActionResult> RenderSwaggerDocument(HttpRequest req, string extension, ExecutionContext ctx, ILogger log)
public static async Task<IActionResult> RenderSwaggerDocument([OpenApiHttpTriggerContext] OpenApiHttpTriggerContext context, HttpRequest req, string extension, ExecutionContext ctx, ILogger log)
{
log.LogInformation($"swagger.{extension} was requested.");

Expand Down Expand Up @@ -105,7 +102,7 @@ public static async Task<IActionResult> RenderSwaggerDocument(HttpRequest req, s
/// <param name="log"><see cref="ILogger"/> instance.</param>
/// <returns>OpenAPI document in a format of either JSON or YAML.</returns>
[OpenApiIgnore]
public static async Task<IActionResult> RenderOpenApiDocument(HttpRequest req, string version, string extension, ExecutionContext ctx, ILogger log)
public static async Task<IActionResult> RenderOpenApiDocument([OpenApiHttpTriggerContext] OpenApiHttpTriggerContext context, HttpRequest req, string version, string extension, ExecutionContext ctx, ILogger log)
{
log.LogInformation($"{version}.{extension} was requested.");

Expand Down Expand Up @@ -176,7 +173,7 @@ public static async Task<IActionResult> RenderOpenApiDocument(HttpRequest req, s
/// <param name="log"><see cref="ILogger"/> instance.</param>
/// <returns>Swagger UI in HTML.</returns>
[OpenApiIgnore]
public static async Task<IActionResult> RenderSwaggerUI(HttpRequest req, ExecutionContext ctx, ILogger log)
public static async Task<IActionResult> RenderSwaggerUI([OpenApiHttpTriggerContext] OpenApiHttpTriggerContext context, HttpRequest req, ExecutionContext ctx, ILogger log)
{
log.LogInformation("SwaggerUI page was requested.");

Expand Down Expand Up @@ -243,7 +240,7 @@ public static async Task<IActionResult> RenderSwaggerUI(HttpRequest req, Executi
/// <param name="log"><see cref="ILogger"/> instance.</param>
/// <returns>oauth2-redirect.html.</returns>
[OpenApiIgnore]
public static async Task<IActionResult> RenderOAuth2Redirect(HttpRequest req, ExecutionContext ctx, ILogger log)
public static async Task<IActionResult> RenderOAuth2Redirect([OpenApiHttpTriggerContext] OpenApiHttpTriggerContext context, HttpRequest req, ExecutionContext ctx, ILogger log)
{
log.LogInformation("The oauth2-redirect.html page was requested.");

Expand Down
Loading