Skip to content

Commit fad043d

Browse files
jackbatznerDerich367
authored andcommitted
Support dynamic OpenApiConfigurationOptions (Azure#432)
1 parent bae33a9 commit fad043d

File tree

15 files changed

+613
-423
lines changed

15 files changed

+613
-423
lines changed

docs/openapi-core.md

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,6 @@ public class OpenApiConfigurationOptions : IOpenApiConfigurationOptions
9393
}
9494
```
9595

96-
9796
### Overriding Base URLs ###
9897

9998
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.
@@ -199,6 +198,66 @@ public class MyOpenApiConfigurationOptions : DefaultOpenApiConfigurationOptions
199198
}
200199
```
201200

201+
### Dynamically configuring `IOpenApiConfigurationOptions` at Runtime ###
202+
203+
There may be instances where you want to configure the `IOpenApiConfigurationOptions` at runtime.
204+
205+
To dynamically modify your configuration options at runtime you can use the following examples:
206+
207+
#### In of Proc ####
208+
209+
```csharp
210+
public override void Configure(IFunctionsHostBuilder builder)
211+
{
212+
var fixture = new Fixture();
213+
builder.Services.AddSingleton(fixture);
214+
215+
// Example: If you want to change the configuration during startup of your function you can use the following code:
216+
services.AddSingleton<IOpenApiConfigurationOptions>(x =>
217+
{
218+
return new OpenApiConfigurationOptions()
219+
{
220+
Info = new Microsoft.OpenApi.Models.OpenApiInfo
221+
{
222+
Title = "A dynamic title generated at runtime",
223+
Description = "Dynamic Open API information at runtime"
224+
}
225+
};
226+
});
227+
}
228+
```
229+
230+
#### Out of Proc ####
231+
232+
```csharp
233+
public static void Main()
234+
{
235+
var host = new HostBuilder()
236+
.ConfigureFunctionsWorkerDefaults(worker => worker.UseNewtonsoftJson())
237+
.ConfigureOpenApi()
238+
.ConfigureServices(services => services.AddSingleton<Fixture>())
239+
.ConfigureServices(services => {
240+
services.AddSingleton<Fixture>();
241+
242+
// Example: If you want to change the configuration during startup of your function you can use the following code:
243+
services.AddSingleton<IOpenApiConfigurationOptions>(x =>
244+
{
245+
return new OpenApiConfigurationOptions()
246+
{
247+
Info = new Microsoft.OpenApi.Models.OpenApiInfo
248+
{
249+
Title = "A dynamic title generated at runtime",
250+
Description = "Dynamic Open API information at runtime"
251+
}
252+
};
253+
});
254+
})
255+
.Build();
256+
257+
host.Run();
258+
}
259+
```
260+
202261
## Swagger UI Customisation ##
203262

204263
Suppose you want to customise the look and feels of the Swagger UI page. In this case, there are two options you can choose.

samples/Microsoft.Azure.Functions.Worker.Extensions.OpenApi.FunctionApp.OutOfProc/Program.cs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
using AutoFixture;
22

33
using Microsoft.Azure.Functions.Worker.Extensions.OpenApi.Extensions;
4+
using Microsoft.Azure.Functions.Worker.Extensions.OpenApi.FunctionApp.OutOfProc.Configurations;
5+
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions;
46
using Microsoft.Extensions.DependencyInjection;
57
using Microsoft.Extensions.Hosting;
68

@@ -13,7 +15,23 @@ public static void Main()
1315
var host = new HostBuilder()
1416
.ConfigureFunctionsWorkerDefaults(worker => worker.UseNewtonsoftJson())
1517
.ConfigureOpenApi()
16-
.ConfigureServices(services => services.AddSingleton<Fixture>())
18+
.ConfigureServices(services => {
19+
services.AddSingleton<Fixture>();
20+
21+
// Example: If you want to change the configuration during startup of your function you can use the following code:
22+
23+
//services.AddSingleton<IOpenApiConfigurationOptions>(x =>
24+
//{
25+
// return new OpenApiConfigurationOptions()
26+
// {
27+
// Info = new Microsoft.OpenApi.Models.OpenApiInfo
28+
// {
29+
// Title = "A dynamic title generated at runtime",
30+
// Description = "Dynamic Open API information at runtime"
31+
// }
32+
// };
33+
//});
34+
})
1735
.Build();
1836

1937
host.Run();

samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.InProc/Configurations/OpenApiConfigurationOptions.cs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
11
using System;
2-
using System.Net;
3-
42
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Configurations;
53
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Enums;
64
using Microsoft.OpenApi.Models;
75

8-
using static System.Net.Mime.MediaTypeNames;
9-
106
namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.InProc.Configurations
117
{
128
public class OpenApiConfigurationOptions : DefaultOpenApiConfigurationOptions

samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.InProc/Startup.cs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
using AutoFixture;
22

33
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
4+
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions;
5+
using Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.InProc.Configurations;
46
using Microsoft.Extensions.DependencyInjection;
57

68
[assembly: FunctionsStartup(typeof(Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.InProc.Startup))]
@@ -12,7 +14,21 @@ public class Startup : FunctionsStartup
1214
public override void Configure(IFunctionsHostBuilder builder)
1315
{
1416
var fixture = new Fixture();
15-
builder.Services.AddSingleton(fixture);
17+
builder.Services.AddSingleton(fixture);
18+
19+
// Example: If you want to change the configuration during startup of your function you can use the following code:
20+
21+
//builder.Services.AddSingleton<IOpenApiConfigurationOptions>((_) =>
22+
//{
23+
// return new OpenApiConfigurationOptions()
24+
// {
25+
// Info = new Microsoft.OpenApi.Models.OpenApiInfo
26+
// {
27+
// Title = "A dynamic title generated at runtime",
28+
// Description = "Dynamic Open API information at runtime"
29+
// }
30+
// };
31+
//});
1632
}
1733
}
1834
}

src/Microsoft.Azure.Functions.Worker.Extensions.OpenApi/Extensions/OpenApiHostBuilderExtensions.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using Microsoft.Azure.Functions.Worker.Extensions.OpenApi.Functions;
22
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions;
3+
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Configurations;
34
using Microsoft.Extensions.DependencyInjection;
45
using Microsoft.Extensions.Hosting;
56

@@ -20,7 +21,7 @@ public static IHostBuilder ConfigureOpenApi(this IHostBuilder hostBuilder)
2021
hostBuilder.ConfigureServices(services =>
2122
{
2223
services.AddSingleton<IOpenApiHttpTriggerContext, OpenApiHttpTriggerContext>();
23-
services.AddSingleton<IOpenApiTriggerFunction, OpenApiTriggerFunction>();
24+
services.AddSingleton<IOpenApiTriggerFunction, OpenApiTriggerFunction>();
2425
// services.AddSingleton<DefaultOpenApiHttpTrigger, DefaultOpenApiHttpTrigger>();
2526
});
2627

src/Microsoft.Azure.Functions.Worker.Extensions.OpenApi/OpenApiHttpTriggerContext.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,9 @@ public class OpenApiHttpTriggerContext : IOpenApiHttpTriggerContext
4040
/// <summary>
4141
/// Initializes a new instance of the <see cref="OpenApiHttpTriggerContext"/> class.
4242
/// </summary>
43-
public OpenApiHttpTriggerContext()
43+
public OpenApiHttpTriggerContext(IOpenApiConfigurationOptions configOptions = null)
4444
{
45+
this._configOptions = configOptions;
4546
this.PackageAssembly = this.GetAssembly<ISwaggerUI>();
4647

4748
var host = HostJsonResolver.Resolve();

src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
<ItemGroup>
1313
<PackageReference Include="Cocona.Lite" Version="1.4.0" />
14-
<PackageReference Include="Microsoft.Azure.WebJobs" Version="3.0.5" />
14+
<PackageReference Include="Microsoft.Azure.WebJobs" Version="3.0.33" />
1515
<PackageReference Include="Moq" Version="4.18.1" />
1616
<PackageReference Include="System.IO.FileSystem.Primitives" Version="4.3.0" />
1717
<PackageReference Include="System.Runtime.Handles" Version="4.3.0" />
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using System.Collections.Generic;
2+
3+
using Microsoft.Azure.WebJobs.Extensions.Http;
4+
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Enums;
5+
6+
using Newtonsoft.Json;
7+
8+
namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions
9+
{
10+
/// <summary>
11+
/// This represents the metadata entity for OpenApiHttpTrigger context binding.
12+
/// </summary>
13+
public class OpenApiHttpTriggerContextBindingMetadata
14+
{
15+
/// <summary>
16+
/// Gets or sets the name of the binding parameter. Default value is <c>context</c>.
17+
/// </summary>
18+
[JsonRequired]
19+
[JsonProperty("name")]
20+
public virtual string Name { get; set; } = "context";
21+
22+
/// <summary>
23+
/// Gets or sets the binding type. Default value is <c>openApiHttpTriggerContext</c>.
24+
/// </summary>
25+
[JsonRequired]
26+
[JsonProperty("type")]
27+
public virtual string Type { get; set; } = "openApiHttpTriggerContext";
28+
29+
/// <summary>
30+
/// Gets or sets the binding direction. Default value is <see cref="BindingDirectionType.In"/>.
31+
/// </summary>
32+
[JsonRequired]
33+
[JsonProperty("direction")]
34+
public virtual BindingDirectionType Direction { get; set; } = BindingDirectionType.In;
35+
}
36+
}

src/Microsoft.Azure.WebJobs.Extensions.OpenApi.Core/Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
</PropertyGroup>
2727

2828
<ItemGroup>
29-
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.Http" Version="3.0.2" />
29+
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.Http" Version="3.0.12" />
3030
<PackageReference Include="Microsoft.OpenApi" Version="1.2.3" />
3131
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
3232
<PackageReference Include="System.IO.FileSystem.Primitives" Version="4.3.0" />

src/Microsoft.Azure.WebJobs.Extensions.OpenApi.Core/Resolvers/OpenApiConfigurationResolver.cs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using System;
22
using System.Linq;
3-
using System.Reflection;
4-
3+
using System.Reflection;
54
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions;
65
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Configurations;
76
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Extensions;
@@ -13,7 +12,7 @@ namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Resolvers
1312
/// This represents the resolver entity for <see cref="OpenApiServer"/>.
1413
/// </summary>
1514
public static class OpenApiConfigurationResolver
16-
{
15+
{
1716
/// <summary>
1817
/// Gets the <see cref="IOpenApiConfigurationOptions"/> instance from the given assembly.
1918
/// </summary>
@@ -25,12 +24,12 @@ public static IOpenApiConfigurationOptions Resolve(Assembly assembly)
2524
.SingleOrDefault(p => p.GetInterface("IOpenApiConfigurationOptions", ignoreCase: true).IsNullOrDefault() == false
2625
&& p.IsAbstract == false
2726
&& p.GetCustomAttribute<ObsoleteAttribute>(inherit: false).IsNullOrDefault() == true);
28-
if (type.IsNullOrDefault())
27+
if (type.IsNullOrDefault())
2928
{
3029
var settings = new DefaultOpenApiConfigurationOptions();
3130

3231
return settings;
33-
}
32+
}
3433

3534
var options = Activator.CreateInstance(type);
3635

src/Microsoft.Azure.WebJobs.Extensions.OpenApi/OpenApiHttpTriggerContext.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,11 @@ public class OpenApiHttpTriggerContext : IOpenApiHttpTriggerContext
3838
private IOpenApiCustomUIOptions _uiOptions;
3939

4040
/// <summary>
41-
/// Initializes a new instance of the <see cref="OpenApiTriggerFunctionProvider"/> class.
41+
/// Initializes a new instance of the <see cref="OpenApiHttpTriggerContext"/> class.
4242
/// </summary>
43-
public OpenApiHttpTriggerContext()
44-
{
43+
public OpenApiHttpTriggerContext(IOpenApiConfigurationOptions configOptions = null)
44+
{
45+
this._configOptions = configOptions;
4546
this.PackageAssembly = this.GetAssembly<ISwaggerUI>();
4647

4748
var host = HostJsonResolver.Resolve();

src/Microsoft.Azure.WebJobs.Extensions.OpenApi/OpenApiTriggerFunctionProvider.cs

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@ namespace Microsoft.Azure.WebJobs.Extensions.OpenApi
1717
/// <summary>
1818
/// This represents the function provider entity for OpenAPI HTTP triggers.
1919
/// </summary>
20-
public partial class OpenApiTriggerFunctionProvider : IFunctionProvider
20+
public class OpenApiTriggerFunctionProvider : IFunctionProvider
2121
{
22-
private const string RenderSwaggerDocumentKey = "RenderSwaggerDocument";
23-
private const string RenderOpenApiDocumentKey = "RenderOpenApiDocument";
24-
private const string RenderSwaggerUIKey = "RenderSwaggerUI";
25-
private const string RenderOAuth2RedirectKey = "RenderOAuth2Redirect";
22+
private const string RenderSwaggerDocumentKey = nameof(OpenApiTriggerFunctions.RenderSwaggerDocument);
23+
private const string RenderOpenApiDocumentKey = nameof(OpenApiTriggerFunctions.RenderOpenApiDocument);
24+
private const string RenderSwaggerUIKey = nameof(OpenApiTriggerFunctions.RenderSwaggerUI);
25+
private const string RenderOAuth2RedirectKey = nameof(OpenApiTriggerFunctions.RenderOAuth2Redirect);
2626

2727
private readonly OpenApiSettings _settings;
2828
private readonly Dictionary<string, HttpBindingMetadata> _bindings;
@@ -133,15 +133,21 @@ private FunctionMetadata GetFunctionMetadata(string functionName)
133133
Name = functionName,
134134
FunctionDirectory = null,
135135
ScriptFile = $"assembly:{assembly.FullName}",
136-
EntryPoint = $"{assembly.GetName().Name}.{typeof(OpenApiTriggerFunctionProvider).Name}.{functionName}",
136+
EntryPoint = $"{assembly.GetName().Name}.{typeof(OpenApiTriggerFunctions).Name}.{functionName}",
137137
Language = "DotNetAssembly"
138138
};
139139

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

144143
return functionMetadata;
145144
}
145+
146+
private static void AddBindingMetadata(FunctionMetadata functionMetadata, object bindingInfo)
147+
{
148+
var jsonObject = JObject.FromObject(bindingInfo);
149+
var bindingMetadata = BindingMetadata.Create(jsonObject);
150+
functionMetadata.Bindings.Add(bindingMetadata);
151+
}
146152
}
147153
}

src/Microsoft.Azure.WebJobs.Extensions.OpenApi/OpenApiTriggerFunctions.cs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
using Microsoft.AspNetCore.Http;
66
using Microsoft.AspNetCore.Mvc;
7-
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions;
87
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Attributes;
98
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Extensions;
109
using Microsoft.Extensions.Logging;
@@ -14,15 +13,13 @@ namespace Microsoft.Azure.WebJobs.Extensions.OpenApi
1413
/// <summary>
1514
/// This represents the function provider entity for OpenAPI HTTP triggers.
1615
/// </summary>
17-
public partial class OpenApiTriggerFunctionProvider
16+
public class OpenApiTriggerFunctions
1817
{
1918
private const string ContentTypeText = "text/plain";
2019
private const string ContentTypeHtml = "text/html";
2120
private const string ContentTypeJson = "application/json";
2221
private const string ContentTypeYaml = "text/vnd.yaml";
2322

24-
private static readonly IOpenApiHttpTriggerContext context = new OpenApiHttpTriggerContext();
25-
2623
/// <summary>
2724
/// Invokes the HTTP trigger endpoint to get OpenAPI document.
2825
/// </summary>
@@ -32,7 +29,7 @@ public partial class OpenApiTriggerFunctionProvider
3229
/// <param name="log"><see cref="ILogger"/> instance.</param>
3330
/// <returns>OpenAPI document in a format of either JSON or YAML.</returns>
3431
[OpenApiIgnore]
35-
public static async Task<IActionResult> RenderSwaggerDocument(HttpRequest req, string extension, ExecutionContext ctx, ILogger log)
32+
public static async Task<IActionResult> RenderSwaggerDocument([OpenApiHttpTriggerContext] OpenApiHttpTriggerContext context, HttpRequest req, string extension, ExecutionContext ctx, ILogger log)
3633
{
3734
log.LogInformation($"swagger.{extension} was requested.");
3835

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

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

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

0 commit comments

Comments
 (0)