Skip to content

Commit 6520c5f

Browse files
authored
Tag filtering for Swagger UI and OpenAPI document (#221)
1 parent f25d11b commit 6520c5f

File tree

19 files changed

+199
-60
lines changed

19 files changed

+199
-60
lines changed

.vscode/settings.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
{
2-
"azureFunctions.deploySubpath": "samples/Microsoft.Azure.Functions.Worker.Extensions.OpenApi.FunctionApp.V3Net5/bin/Release/net5.0/publish",
2+
// "azureFunctions.deploySubpath": "samples/Microsoft.Azure.Functions.Worker.Extensions.OpenApi.FunctionApp.V3Net5/bin/Release/net5.0/publish",
33
// "azureFunctions.deploySubpath": "samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V1Proxy/bin/Release/netcoreapp3.1/publish",
44
// "azureFunctions.deploySubpath": "samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V2IoC/bin/Release/netcoreapp2.1/publish",
55
// "azureFunctions.deploySubpath": "samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V2Static/bin/Release/netcoreapp2.1/publish",
66
// "azureFunctions.deploySubpath": "samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V3IoC/bin/Release/netcoreapp3.1/publish",
7-
// "azureFunctions.deploySubpath": "samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V3Static/bin/Release/netcoreapp3.1/publish",
7+
"azureFunctions.deploySubpath": "samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V3Static/bin/Release/netcoreapp3.1/publish",
88

9-
"azureFunctions.projectSubpath": "samples/Microsoft.Azure.Functions.Worker.Extensions.OpenApi.FunctionApp.V3Net5",
9+
// "azureFunctions.projectSubpath": "samples/Microsoft.Azure.Functions.Worker.Extensions.OpenApi.FunctionApp.V3Net5",
1010
// "azureFunctions.projectSubpath": "samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V1Proxy",
1111
// "azureFunctions.projectSubpath": "samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V2IoC",
1212
// "azureFunctions.projectSubpath": "samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V2Static",
1313
// "azureFunctions.projectSubpath": "samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V3IoC",
14-
// "azureFunctions.projectSubpath": "samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V3Static",
14+
"azureFunctions.projectSubpath": "samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V3Static",
1515

1616
"azureFunctions.projectLanguage": "C#",
1717
"azureFunctions.projectRuntime": "~3",

.vscode/tasks.json

Lines changed: 2 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,6 @@
1212
"type": "process",
1313
"problemMatcher": "$msCompile",
1414
"options": {
15-
"cwd": "${workspaceFolder}/samples/Microsoft.Azure.Functions.Worker.Extensions.OpenApi.FunctionApp.V3Net5"
16-
// "cwd": "${workspaceFolder}/samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V1Proxy"
17-
// "cwd": "${workspaceFolder}/samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V2IoC"
18-
// "cwd": "${workspaceFolder}/samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V2Static"
19-
// "cwd": "${workspaceFolder}/samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V3IoC"
20-
// "cwd": "${workspaceFolder}/samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V3Static"
2115
}
2216
},
2317
{
@@ -36,12 +30,6 @@
3630
},
3731
"problemMatcher": "$msCompile",
3832
"options": {
39-
"cwd": "${workspaceFolder}/samples/Microsoft.Azure.Functions.Worker.Extensions.OpenApi.FunctionApp.V3Net5"
40-
// "cwd": "${workspaceFolder}/samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V1Proxy"
41-
// "cwd": "${workspaceFolder}/samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V2IoC"
42-
// "cwd": "${workspaceFolder}/samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V2Static"
43-
// "cwd": "${workspaceFolder}/samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V3IoC"
44-
// "cwd": "${workspaceFolder}/samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V3Static"
4533
}
4634
},
4735
{
@@ -57,12 +45,6 @@
5745
"type": "process",
5846
"problemMatcher": "$msCompile",
5947
"options": {
60-
"cwd": "${workspaceFolder}/samples/Microsoft.Azure.Functions.Worker.Extensions.OpenApi.FunctionApp.V3Net5"
61-
// "cwd": "${workspaceFolder}/samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V1Proxy"
62-
// "cwd": "${workspaceFolder}/samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V2IoC"
63-
// "cwd": "${workspaceFolder}/samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V2Static"
64-
// "cwd": "${workspaceFolder}/samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V3IoC"
65-
// "cwd": "${workspaceFolder}/samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V3Static"
6648
}
6749
},
6850
{
@@ -79,12 +61,6 @@
7961
"dependsOn": "clean release",
8062
"problemMatcher": "$msCompile",
8163
"options": {
82-
"cwd": "${workspaceFolder}/samples/Microsoft.Azure.Functions.Worker.Extensions.OpenApi.FunctionApp.V3Net5"
83-
// "cwd": "${workspaceFolder}/samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V1Proxy"
84-
// "cwd": "${workspaceFolder}/samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V2IoC"
85-
// "cwd": "${workspaceFolder}/samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V2Static"
86-
// "cwd": "${workspaceFolder}/samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V3IoC"
87-
// "cwd": "${workspaceFolder}/samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V3Static"
8864
}
8965
},
9066
{
@@ -104,12 +80,12 @@
10480
"type": "func",
10581
"dependsOn": "build",
10682
"options": {
107-
"cwd": "${workspaceFolder}/samples/Microsoft.Azure.Functions.Worker.Extensions.OpenApi.FunctionApp.V3Net5/bin/Debug/net5.0"
83+
// "cwd": "${workspaceFolder}/samples/Microsoft.Azure.Functions.Worker.Extensions.OpenApi.FunctionApp.V3Net5/bin/Debug/net5.0"
10884
// "cwd": "${workspaceFolder}/samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V3Proxy/bin/Debug/netcoreapp3.1"
10985
// "cwd": "${workspaceFolder}/samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V2IoC/bin/Debug/netcoreapp2.1"
11086
// "cwd": "${workspaceFolder}/samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V2Static/bin/Debug/netcoreapp2.1"
11187
// "cwd": "${workspaceFolder}/samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V3IoC/bin/Debug/netcoreapp3.1"
112-
// "cwd": "${workspaceFolder}/samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V3Static/bin/Debug/netcoreapp3.1"
88+
"cwd": "${workspaceFolder}/samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V3Static/bin/Debug/netcoreapp3.1"
11389
},
11490
"command": "host start",
11591
"isBackground": true,

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ public class Document : IDocument
2828

2929
private NamingStrategy _strategy;
3030
private VisitorCollection _collection;
31+
private IHttpRequestDataObject _req;
3132

3233
/// <summary>
3334
/// Initializes a new instance of the <see cref="Document"/> class.
@@ -69,8 +70,10 @@ public IDocument AddMetadata(OpenApiInfo info)
6970
/// <inheritdoc />
7071
public IDocument AddServer(IHttpRequestDataObject req, string routePrefix, IOpenApiConfigurationOptions options = null)
7172
{
73+
this._req = req;
74+
7275
var prefix = string.IsNullOrWhiteSpace(routePrefix) ? string.Empty : $"/{routePrefix}";
73-
var baseUrl = $"{req.Scheme}://{req.Host}{prefix}";
76+
var baseUrl = $"{this._req.Scheme}://{this._req.Host}{prefix}";
7477

7578
var server = new OpenApiServer { Url = baseUrl };
7679

@@ -135,7 +138,8 @@ public IDocument Build(Assembly assembly, OpenApiVersionType version = OpenApiVe
135138

136139
var paths = new OpenApiPaths();
137140

138-
var methods = this._helper.GetHttpTriggerMethods(assembly);
141+
var tags = StringExtensions.ToArray(this._req.Query["tag"], ",");
142+
var methods = this._helper.GetHttpTriggerMethods(assembly, tags);
139143
foreach (var method in methods)
140144
{
141145
var trigger = this._helper.GetHttpTriggerAttribute(method);

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

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,9 @@ public static class DocumentHelperExtensions
2626
/// </summary>
2727
/// <param name="helper"><see cref="IDocumentHelper"/> instance.</param>
2828
/// <param name="assembly">Assembly of Azure Function instance.</param>
29+
/// <param name="tags">List of tags to filter methods.</param>
2930
/// <returns>List of <see cref="MethodInfo"/> instances representing HTTP triggers.</returns>
30-
public static List<MethodInfo> GetHttpTriggerMethods(this IDocumentHelper helper, Assembly assembly)
31+
public static List<MethodInfo> GetHttpTriggerMethods(this IDocumentHelper helper, Assembly assembly, IEnumerable<string> tags = null)
3132
{
3233
var methods = assembly.GetLoadableTypes()
3334
.SelectMany(p => p.GetMethods())
@@ -37,6 +38,16 @@ public static List<MethodInfo> GetHttpTriggerMethods(this IDocumentHelper helper
3738
.Where(p => p.GetParameters().FirstOrDefault(q => q.ExistsCustomAttribute<HttpTriggerAttribute>()) != null)
3839
.ToList();
3940

41+
if (!tags.Any())
42+
{
43+
return methods;
44+
}
45+
46+
methods = methods.Where(p => p.GetCustomAttribute<OpenApiOperationAttribute>()
47+
.Tags
48+
.Any(q => tags.Contains(q)))
49+
.ToList();
50+
4051
return methods;
4152
}
4253

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
using System.IO;
12
using System.Linq;
23

34
using Microsoft.AspNetCore.Http;
5+
using Microsoft.Azure.Functions.Worker.Extensions.OpenApi.Extensions;
46
using Microsoft.Azure.Functions.Worker.Http;
57
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions;
68
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Extensions;
@@ -24,12 +26,21 @@ public HttpRequestObject(HttpRequestData req)
2426
this.Host = new[] { 80, 443 }.Contains(req.Url.Port)
2527
? new HostString(req.Url.Authority)
2628
: new HostString(req.Url.Host, req.Url.Port);
29+
30+
this.Query = req.Queries();
31+
this.Body = req.Body;
2732
}
2833

2934
/// <inheritdoc/>
3035
public virtual string Scheme { get; }
3136

3237
/// <inheritdoc/>
3338
public virtual HostString Host { get; }
39+
40+
/// <inheritdoc/>
41+
public virtual IQueryCollection Query { get;}
42+
43+
/// <inheritdoc/>
44+
public virtual Stream Body { get;}
3445
}
3546
}

src/Microsoft.Azure.WebJobs.Extensions.OpenApi.Core/Abstractions/IDocument.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using System.Reflection;
22
using System.Threading.Tasks;
33

4-
using Microsoft.AspNetCore.Http;
54
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Enums;
65
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Visitors;
76
using Microsoft.OpenApi;

src/Microsoft.Azure.WebJobs.Extensions.OpenApi.Core/Abstractions/IHttpRequestDataObject.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
using System.IO;
2+
13
using Microsoft.AspNetCore.Http;
24

35
namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions
@@ -16,5 +18,15 @@ public interface IHttpRequestDataObject
1618
/// Gets the host header that may include the port number.
1719
/// </summary>
1820
HostString Host { get; }
21+
22+
/// <summary>
23+
/// Gets the query collection.
24+
/// </summary>
25+
IQueryCollection Query { get;}
26+
27+
/// <summary>
28+
/// Gets the request payload stream.
29+
/// </summary>
30+
Stream Body { get;}
1931
}
2032
}

src/Microsoft.Azure.WebJobs.Extensions.OpenApi.Core/Extensions/DocumentHelperExtensions.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@ public static class DocumentHelperExtensions
2424
/// </summary>
2525
/// <param name="helper"><see cref="IDocumentHelper"/> instance.</param>
2626
/// <param name="assembly">Assembly of Azure Function instance.</param>
27+
/// <param name="tags">List of tags to filter methods.</param>
2728
/// <returns>List of <see cref="MethodInfo"/> instances representing HTTP triggers.</returns>
28-
public static List<MethodInfo> GetHttpTriggerMethods(this IDocumentHelper helper, Assembly assembly)
29+
public static List<MethodInfo> GetHttpTriggerMethods(this IDocumentHelper helper, Assembly assembly, IEnumerable<string> tags = null)
2930
{
3031
var methods = assembly.GetLoadableTypes()
3132
.SelectMany(p => p.GetMethods())
@@ -35,6 +36,15 @@ public static List<MethodInfo> GetHttpTriggerMethods(this IDocumentHelper helper
3536
.Where(p => p.GetParameters().FirstOrDefault(q => q.ExistsCustomAttribute<HttpTriggerAttribute>()) != null)
3637
.ToList();
3738

39+
if (!tags.Any())
40+
{
41+
return methods;
42+
}
43+
44+
methods = methods.Where(p => p.GetCustomAttribute<OpenApiOperationAttribute>()
45+
.Tags.Any(q => tags.Contains(q)))
46+
.ToList();
47+
3848
return methods;
3949
}
4050

src/Microsoft.Azure.WebJobs.Extensions.OpenApi.Core/Extensions/StringExtensions.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
using System;
22

3+
using Microsoft.Extensions.Primitives;
4+
35
namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Extensions
46
{
57
/// <summary>
@@ -32,5 +34,27 @@ public static string ThrowIfNullOrWhiteSpace(this string value)
3234

3335
return value;
3436
}
37+
38+
/// <summary>
39+
/// Converts the <see cref="StringValues"/> value to array of string.
40+
/// </summary>
41+
/// <param name="value"><see cref="StringValues"/> value.</param>
42+
/// <param name="delimiter">Delimiter to split values.</param>
43+
/// <returns>Returns the array of string.</returns>
44+
public static string[] ToArray(this StringValues value, string delimiter = ",")
45+
{
46+
if (value.IsNullOrDefault())
47+
{
48+
return new string[0];
49+
}
50+
51+
var values = value.ToString();
52+
if (values.IsNullOrWhiteSpace())
53+
{
54+
return new string[0];
55+
}
56+
57+
return values.Split(new[] { delimiter }, StringSplitOptions.RemoveEmptyEntries);
58+
}
3559
}
3660
}

src/Microsoft.Azure.WebJobs.Extensions.OpenApi.Core/SwaggerUI.cs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections.Generic;
23
using System.IO;
34
using System.Linq;
45
using System.Reflection;
@@ -7,6 +8,7 @@
78
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions;
89
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Enums;
910
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Extensions;
11+
using Microsoft.Extensions.Primitives;
1012
using Microsoft.OpenApi.Models;
1113

1214
namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.Core
@@ -32,6 +34,7 @@ public class SwaggerUI : ISwaggerUI
3234
private readonly string swaggerUiStandalonePresetJs = $"{typeof(SwaggerUI).Namespace}.dist.swagger-ui-standalone-preset.js";
3335

3436
private OpenApiInfo _info;
37+
private IHttpRequestDataObject _req;
3538
private string _baseUrl;
3639
private string _swaggerUiCss;
3740
private string _swaggerUiCustomCss;
@@ -53,8 +56,10 @@ public ISwaggerUI AddMetadata(OpenApiInfo info)
5356
/// <inheritdoc />
5457
public ISwaggerUI AddServer(IHttpRequestDataObject req, string routePrefix, IOpenApiConfigurationOptions options = null)
5558
{
59+
this._req = req;
60+
5661
var prefix = string.IsNullOrWhiteSpace(routePrefix) ? string.Empty : $"/{routePrefix}";
57-
var baseUrl = $"{req.Scheme}://{req.Host}{prefix}";
62+
var baseUrl = $"{this._req.Scheme}://{this._req.Host}{prefix}";
5863

5964
if (options.IsNullOrDefault())
6065
{
@@ -160,9 +165,19 @@ private string Render(string endpoint, OpenApiAuthLevelType authLevel = OpenApiA
160165
{
161166
var swaggerUiTitle = $"{this._info.Title} - Swagger UI";
162167
var swaggerUrl = $"{this._baseUrl.TrimEnd('/')}/{endpoint}";
168+
169+
var queries = this._req.Query.ToDictionary(p => p.Key, p => p.Value);
163170
if (this.IsAuthKeyRequired(authLevel, authKey))
164171
{
165-
swaggerUrl += $"?code={authKey}";
172+
if (!queries.ContainsKey("code"))
173+
{
174+
queries.Add("code", new StringValues(authKey));
175+
}
176+
}
177+
178+
if (queries.Any())
179+
{
180+
swaggerUrl += "?" + string.Join("&", queries.SelectMany(p => p.Value.Select(q => $"{p.Key}={q}")));
166181
}
167182

168183
var html = this._indexHtml.Replace(SwaggerUITitlePlaceholder, swaggerUiTitle)

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ public class Document : IDocument
2424

2525
private NamingStrategy _strategy;
2626
private VisitorCollection _collection;
27+
private IHttpRequestDataObject _req;
2728

2829
/// <summary>
2930
/// Initializes a new instance of the <see cref="Document"/> class.
@@ -65,8 +66,10 @@ public IDocument AddMetadata(OpenApiInfo info)
6566
/// <inheritdoc />
6667
public IDocument AddServer(IHttpRequestDataObject req, string routePrefix, IOpenApiConfigurationOptions options = null)
6768
{
69+
this._req = req;
70+
6871
var prefix = string.IsNullOrWhiteSpace(routePrefix) ? string.Empty : $"/{routePrefix}";
69-
var baseUrl = $"{req.Scheme}://{req.Host}{prefix}";
72+
var baseUrl = $"{this._req.Scheme}://{this._req.Host}{prefix}";
7073

7174
var server = new OpenApiServer { Url = baseUrl };
7275

@@ -131,7 +134,8 @@ public IDocument Build(Assembly assembly, OpenApiVersionType version = OpenApiVe
131134

132135
var paths = new OpenApiPaths();
133136

134-
var methods = this._helper.GetHttpTriggerMethods(assembly);
137+
var tags = this._req.Query["tag"].ToArray(",");
138+
var methods = this._helper.GetHttpTriggerMethods(assembly, tags);
135139
foreach (var method in methods)
136140
{
137141
var trigger = this._helper.GetHttpTriggerAttribute(method);

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
using System.IO;
2+
13
using Microsoft.AspNetCore.Http;
24
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions;
35
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Extensions;
@@ -19,12 +21,20 @@ public HttpRequestObject(HttpRequest req)
1921

2022
this.Scheme = req.Scheme;
2123
this.Host = req.Host;
24+
this.Query = req.Query;
25+
this.Body = req.Body;
2226
}
2327

2428
/// <inheritdoc/>
2529
public virtual string Scheme { get; }
2630

2731
/// <inheritdoc/>
2832
public virtual HostString Host { get; }
33+
34+
/// <inheritdoc/>
35+
public virtual IQueryCollection Query { get;}
36+
37+
/// <inheritdoc/>
38+
public virtual Stream Body { get;}
2939
}
3040
}

0 commit comments

Comments
 (0)