Skip to content
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

Tag filtering for Swagger UI and OpenAPI document #221

Merged
merged 2 commits into from
Aug 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
{
"azureFunctions.deploySubpath": "samples/Microsoft.Azure.Functions.Worker.Extensions.OpenApi.FunctionApp.V3Net5/bin/Release/net5.0/publish",
// "azureFunctions.deploySubpath": "samples/Microsoft.Azure.Functions.Worker.Extensions.OpenApi.FunctionApp.V3Net5/bin/Release/net5.0/publish",
// "azureFunctions.deploySubpath": "samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V1Proxy/bin/Release/netcoreapp3.1/publish",
// "azureFunctions.deploySubpath": "samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V2IoC/bin/Release/netcoreapp2.1/publish",
// "azureFunctions.deploySubpath": "samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V2Static/bin/Release/netcoreapp2.1/publish",
// "azureFunctions.deploySubpath": "samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V3IoC/bin/Release/netcoreapp3.1/publish",
// "azureFunctions.deploySubpath": "samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V3Static/bin/Release/netcoreapp3.1/publish",
"azureFunctions.deploySubpath": "samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V3Static/bin/Release/netcoreapp3.1/publish",

"azureFunctions.projectSubpath": "samples/Microsoft.Azure.Functions.Worker.Extensions.OpenApi.FunctionApp.V3Net5",
// "azureFunctions.projectSubpath": "samples/Microsoft.Azure.Functions.Worker.Extensions.OpenApi.FunctionApp.V3Net5",
// "azureFunctions.projectSubpath": "samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V1Proxy",
// "azureFunctions.projectSubpath": "samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V2IoC",
// "azureFunctions.projectSubpath": "samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V2Static",
// "azureFunctions.projectSubpath": "samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V3IoC",
// "azureFunctions.projectSubpath": "samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V3Static",
"azureFunctions.projectSubpath": "samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V3Static",

"azureFunctions.projectLanguage": "C#",
"azureFunctions.projectRuntime": "~3",
Expand Down
28 changes: 2 additions & 26 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,6 @@
"type": "process",
"problemMatcher": "$msCompile",
"options": {
"cwd": "${workspaceFolder}/samples/Microsoft.Azure.Functions.Worker.Extensions.OpenApi.FunctionApp.V3Net5"
// "cwd": "${workspaceFolder}/samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V1Proxy"
// "cwd": "${workspaceFolder}/samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V2IoC"
// "cwd": "${workspaceFolder}/samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V2Static"
// "cwd": "${workspaceFolder}/samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V3IoC"
// "cwd": "${workspaceFolder}/samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V3Static"
}
},
{
Expand All @@ -36,12 +30,6 @@
},
"problemMatcher": "$msCompile",
"options": {
"cwd": "${workspaceFolder}/samples/Microsoft.Azure.Functions.Worker.Extensions.OpenApi.FunctionApp.V3Net5"
// "cwd": "${workspaceFolder}/samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V1Proxy"
// "cwd": "${workspaceFolder}/samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V2IoC"
// "cwd": "${workspaceFolder}/samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V2Static"
// "cwd": "${workspaceFolder}/samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V3IoC"
// "cwd": "${workspaceFolder}/samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V3Static"
}
},
{
Expand All @@ -57,12 +45,6 @@
"type": "process",
"problemMatcher": "$msCompile",
"options": {
"cwd": "${workspaceFolder}/samples/Microsoft.Azure.Functions.Worker.Extensions.OpenApi.FunctionApp.V3Net5"
// "cwd": "${workspaceFolder}/samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V1Proxy"
// "cwd": "${workspaceFolder}/samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V2IoC"
// "cwd": "${workspaceFolder}/samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V2Static"
// "cwd": "${workspaceFolder}/samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V3IoC"
// "cwd": "${workspaceFolder}/samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V3Static"
}
},
{
Expand All @@ -79,12 +61,6 @@
"dependsOn": "clean release",
"problemMatcher": "$msCompile",
"options": {
"cwd": "${workspaceFolder}/samples/Microsoft.Azure.Functions.Worker.Extensions.OpenApi.FunctionApp.V3Net5"
// "cwd": "${workspaceFolder}/samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V1Proxy"
// "cwd": "${workspaceFolder}/samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V2IoC"
// "cwd": "${workspaceFolder}/samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V2Static"
// "cwd": "${workspaceFolder}/samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V3IoC"
// "cwd": "${workspaceFolder}/samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V3Static"
}
},
{
Expand All @@ -104,12 +80,12 @@
"type": "func",
"dependsOn": "build",
"options": {
"cwd": "${workspaceFolder}/samples/Microsoft.Azure.Functions.Worker.Extensions.OpenApi.FunctionApp.V3Net5/bin/Debug/net5.0"
// "cwd": "${workspaceFolder}/samples/Microsoft.Azure.Functions.Worker.Extensions.OpenApi.FunctionApp.V3Net5/bin/Debug/net5.0"
// "cwd": "${workspaceFolder}/samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V3Proxy/bin/Debug/netcoreapp3.1"
// "cwd": "${workspaceFolder}/samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V2IoC/bin/Debug/netcoreapp2.1"
// "cwd": "${workspaceFolder}/samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V2Static/bin/Debug/netcoreapp2.1"
// "cwd": "${workspaceFolder}/samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V3IoC/bin/Debug/netcoreapp3.1"
// "cwd": "${workspaceFolder}/samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V3Static/bin/Debug/netcoreapp3.1"
"cwd": "${workspaceFolder}/samples/Microsoft.Azure.WebJobs.Extensions.OpenApi.FunctionApp.V3Static/bin/Debug/netcoreapp3.1"
},
"command": "host start",
"isBackground": true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public class Document : IDocument

private NamingStrategy _strategy;
private VisitorCollection _collection;
private IHttpRequestDataObject _req;

/// <summary>
/// Initializes a new instance of the <see cref="Document"/> class.
Expand Down Expand Up @@ -69,8 +70,10 @@ public IDocument AddMetadata(OpenApiInfo info)
/// <inheritdoc />
public IDocument AddServer(IHttpRequestDataObject req, string routePrefix, IOpenApiConfigurationOptions options = null)
{
this._req = req;

var prefix = string.IsNullOrWhiteSpace(routePrefix) ? string.Empty : $"/{routePrefix}";
var baseUrl = $"{req.Scheme}://{req.Host}{prefix}";
var baseUrl = $"{this._req.Scheme}://{this._req.Host}{prefix}";

var server = new OpenApiServer { Url = baseUrl };

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

var paths = new OpenApiPaths();

var methods = this._helper.GetHttpTriggerMethods(assembly);
var tags = StringExtensions.ToArray(this._req.Query["tag"], ",");
var methods = this._helper.GetHttpTriggerMethods(assembly, tags);
foreach (var method in methods)
{
var trigger = this._helper.GetHttpTriggerAttribute(method);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@ public static class DocumentHelperExtensions
/// </summary>
/// <param name="helper"><see cref="IDocumentHelper"/> instance.</param>
/// <param name="assembly">Assembly of Azure Function instance.</param>
/// <param name="tags">List of tags to filter methods.</param>
/// <returns>List of <see cref="MethodInfo"/> instances representing HTTP triggers.</returns>
public static List<MethodInfo> GetHttpTriggerMethods(this IDocumentHelper helper, Assembly assembly)
public static List<MethodInfo> GetHttpTriggerMethods(this IDocumentHelper helper, Assembly assembly, IEnumerable<string> tags = null)
{
var methods = assembly.GetLoadableTypes()
.SelectMany(p => p.GetMethods())
Expand All @@ -37,6 +38,16 @@ public static List<MethodInfo> GetHttpTriggerMethods(this IDocumentHelper helper
.Where(p => p.GetParameters().FirstOrDefault(q => q.ExistsCustomAttribute<HttpTriggerAttribute>()) != null)
.ToList();

if (!tags.Any())
{
return methods;
}

methods = methods.Where(p => p.GetCustomAttribute<OpenApiOperationAttribute>()
.Tags
.Any(q => tags.Contains(q)))
.ToList();

return methods;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System.IO;
using System.Linq;

using Microsoft.AspNetCore.Http;
using Microsoft.Azure.Functions.Worker.Extensions.OpenApi.Extensions;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Extensions;
Expand All @@ -24,12 +26,21 @@ public HttpRequestObject(HttpRequestData req)
this.Host = new[] { 80, 443 }.Contains(req.Url.Port)
? new HostString(req.Url.Authority)
: new HostString(req.Url.Host, req.Url.Port);

this.Query = req.Queries();
this.Body = req.Body;
}

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

/// <inheritdoc/>
public virtual HostString Host { get; }

/// <inheritdoc/>
public virtual IQueryCollection Query { get;}

/// <inheritdoc/>
public virtual Stream Body { get;}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System.Reflection;
using System.Threading.Tasks;

using Microsoft.AspNetCore.Http;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Enums;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Visitors;
using Microsoft.OpenApi;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using System.IO;

using Microsoft.AspNetCore.Http;

namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions
Expand All @@ -16,5 +18,15 @@ public interface IHttpRequestDataObject
/// Gets the host header that may include the port number.
/// </summary>
HostString Host { get; }

/// <summary>
/// Gets the query collection.
/// </summary>
IQueryCollection Query { get;}

/// <summary>
/// Gets the request payload stream.
/// </summary>
Stream Body { get;}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@ public static class DocumentHelperExtensions
/// </summary>
/// <param name="helper"><see cref="IDocumentHelper"/> instance.</param>
/// <param name="assembly">Assembly of Azure Function instance.</param>
/// <param name="tags">List of tags to filter methods.</param>
/// <returns>List of <see cref="MethodInfo"/> instances representing HTTP triggers.</returns>
public static List<MethodInfo> GetHttpTriggerMethods(this IDocumentHelper helper, Assembly assembly)
public static List<MethodInfo> GetHttpTriggerMethods(this IDocumentHelper helper, Assembly assembly, IEnumerable<string> tags = null)
{
var methods = assembly.GetLoadableTypes()
.SelectMany(p => p.GetMethods())
Expand All @@ -35,6 +36,15 @@ public static List<MethodInfo> GetHttpTriggerMethods(this IDocumentHelper helper
.Where(p => p.GetParameters().FirstOrDefault(q => q.ExistsCustomAttribute<HttpTriggerAttribute>()) != null)
.ToList();

if (!tags.Any())
{
return methods;
}

methods = methods.Where(p => p.GetCustomAttribute<OpenApiOperationAttribute>()
.Tags.Any(q => tags.Contains(q)))
.ToList();

return methods;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System;

using Microsoft.Extensions.Primitives;

namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Extensions
{
/// <summary>
Expand Down Expand Up @@ -32,5 +34,27 @@ public static string ThrowIfNullOrWhiteSpace(this string value)

return value;
}

/// <summary>
/// Converts the <see cref="StringValues"/> value to array of string.
/// </summary>
/// <param name="value"><see cref="StringValues"/> value.</param>
/// <param name="delimiter">Delimiter to split values.</param>
/// <returns>Returns the array of string.</returns>
public static string[] ToArray(this StringValues value, string delimiter = ",")
{
if (value.IsNullOrDefault())
{
return new string[0];
}

var values = value.ToString();
if (values.IsNullOrWhiteSpace())
{
return new string[0];
}

return values.Split(new[] { delimiter }, StringSplitOptions.RemoveEmptyEntries);
}
}
}
19 changes: 17 additions & 2 deletions src/Microsoft.Azure.WebJobs.Extensions.OpenApi.Core/SwaggerUI.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
Expand All @@ -7,6 +8,7 @@
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Enums;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Extensions;
using Microsoft.Extensions.Primitives;
using Microsoft.OpenApi.Models;

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

private OpenApiInfo _info;
private IHttpRequestDataObject _req;
private string _baseUrl;
private string _swaggerUiCss;
private string _swaggerUiCustomCss;
Expand All @@ -53,8 +56,10 @@ public ISwaggerUI AddMetadata(OpenApiInfo info)
/// <inheritdoc />
public ISwaggerUI AddServer(IHttpRequestDataObject req, string routePrefix, IOpenApiConfigurationOptions options = null)
{
this._req = req;

var prefix = string.IsNullOrWhiteSpace(routePrefix) ? string.Empty : $"/{routePrefix}";
var baseUrl = $"{req.Scheme}://{req.Host}{prefix}";
var baseUrl = $"{this._req.Scheme}://{this._req.Host}{prefix}";

if (options.IsNullOrDefault())
{
Expand Down Expand Up @@ -160,9 +165,19 @@ private string Render(string endpoint, OpenApiAuthLevelType authLevel = OpenApiA
{
var swaggerUiTitle = $"{this._info.Title} - Swagger UI";
var swaggerUrl = $"{this._baseUrl.TrimEnd('/')}/{endpoint}";

var queries = this._req.Query.ToDictionary(p => p.Key, p => p.Value);
if (this.IsAuthKeyRequired(authLevel, authKey))
{
swaggerUrl += $"?code={authKey}";
if (!queries.ContainsKey("code"))
{
queries.Add("code", new StringValues(authKey));
}
}

if (queries.Any())
{
swaggerUrl += "?" + string.Join("&", queries.SelectMany(p => p.Value.Select(q => $"{p.Key}={q}")));
}

var html = this._indexHtml.Replace(SwaggerUITitlePlaceholder, swaggerUiTitle)
Expand Down
8 changes: 6 additions & 2 deletions src/Microsoft.Azure.WebJobs.Extensions.OpenApi/Document.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public class Document : IDocument

private NamingStrategy _strategy;
private VisitorCollection _collection;
private IHttpRequestDataObject _req;

/// <summary>
/// Initializes a new instance of the <see cref="Document"/> class.
Expand Down Expand Up @@ -65,8 +66,10 @@ public IDocument AddMetadata(OpenApiInfo info)
/// <inheritdoc />
public IDocument AddServer(IHttpRequestDataObject req, string routePrefix, IOpenApiConfigurationOptions options = null)
{
this._req = req;

var prefix = string.IsNullOrWhiteSpace(routePrefix) ? string.Empty : $"/{routePrefix}";
var baseUrl = $"{req.Scheme}://{req.Host}{prefix}";
var baseUrl = $"{this._req.Scheme}://{this._req.Host}{prefix}";

var server = new OpenApiServer { Url = baseUrl };

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

var paths = new OpenApiPaths();

var methods = this._helper.GetHttpTriggerMethods(assembly);
var tags = this._req.Query["tag"].ToArray(",");
var methods = this._helper.GetHttpTriggerMethods(assembly, tags);
foreach (var method in methods)
{
var trigger = this._helper.GetHttpTriggerAttribute(method);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using System.IO;

using Microsoft.AspNetCore.Http;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Extensions;
Expand All @@ -19,12 +21,20 @@ public HttpRequestObject(HttpRequest req)

this.Scheme = req.Scheme;
this.Host = req.Host;
this.Query = req.Query;
this.Body = req.Body;
}

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

/// <inheritdoc/>
public virtual HostString Host { get; }

/// <inheritdoc/>
public virtual IQueryCollection Query { get;}

/// <inheritdoc/>
public virtual Stream Body { get;}
}
}
Loading