Skip to content
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
13 changes: 12 additions & 1 deletion src/Microsoft.OpenApi.Tool/OpenApiService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public static void ProcessOpenApiDocument(
OpenApiSpecVersion version,
OpenApiFormat format,
string filterByOperationIds,
string filterByTags,
bool inline,
bool resolveExternal)
{
Expand Down Expand Up @@ -52,9 +53,19 @@ public static void ProcessOpenApiDocument(
document = result.OpenApiDocument;

// Check if filter options are provided, then execute
if (!string.IsNullOrEmpty(filterByOperationIds) && !string.IsNullOrEmpty(filterByTags))
{
throw new InvalidOperationException("Cannot filter by operationIds and tags at the same time.");
}

if (!string.IsNullOrEmpty(filterByOperationIds))
{
var predicate = OpenApiFilterService.CreatePredicate(filterByOperationIds);
var predicate = OpenApiFilterService.CreatePredicate(operationIds: filterByOperationIds);
document = OpenApiFilterService.CreateFilteredDocument(document, predicate);
}
if (!string.IsNullOrEmpty(filterByTags))
{
var predicate = OpenApiFilterService.CreatePredicate(tags: filterByTags);
document = OpenApiFilterService.CreateFilteredDocument(document, predicate);
}

Expand Down
5 changes: 3 additions & 2 deletions src/Microsoft.OpenApi.Tool/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,10 @@ static async Task<int> Main(string[] args)
new Option("--format", "File format",typeof(OpenApiFormat) ),
new Option("--inline", "Inline $ref instances", typeof(bool) ),
new Option("--resolveExternal","Resolve external $refs", typeof(bool)),
new Option("--filterByOperationIds", "Filters by OperationId provided", typeof(string))
new Option("--filterByOperationIds", "Filters OpenApiDocument by OperationId(s) provided", typeof(string)),
new Option("--filterByTags", "Filters OpenApiDocument by Tag(s) provided", typeof(string))
};
transformCommand.Handler = CommandHandler.Create<string, FileInfo, OpenApiSpecVersion, OpenApiFormat, string, bool, bool>(
transformCommand.Handler = CommandHandler.Create<string, FileInfo, OpenApiSpecVersion, OpenApiFormat, string, string, bool, bool>(
OpenApiService.ProcessOpenApiDocument);

rootCommand.Add(transformCommand);
Expand Down
23 changes: 21 additions & 2 deletions src/Microsoft.OpenApi/Services/OpenApiFilterService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using Microsoft.OpenApi.Models;

namespace Microsoft.OpenApi.Services
Expand All @@ -17,10 +18,15 @@ public static class OpenApiFilterService
/// Create predicate function based on passed query parameters
/// </summary>
/// <param name="operationIds">Comma delimited list of operationIds or * for all operations.</param>
/// <param name="tags">Comma delimited list of tags or a single regex.</param>
/// <returns>A predicate.</returns>
public static Func<OpenApiOperation, bool> CreatePredicate(string operationIds)
public static Func<OpenApiOperation, bool> CreatePredicate(string operationIds = null, string tags = null)
{
Func<OpenApiOperation, bool> predicate;
if (!string.IsNullOrEmpty(operationIds) && !string.IsNullOrEmpty(tags))
{
throw new InvalidOperationException("Cannot specify both operationIds and tags at the same time.");
}
if (operationIds != null)
{
if (operationIds == "*")
Expand All @@ -33,10 +39,23 @@ public static Func<OpenApiOperation, bool> CreatePredicate(string operationIds)
predicate = (o) => operationIdsArray.Contains(o.OperationId);
}
}
else if (tags != null)
{
var tagsArray = tags.Split(',');
if (tagsArray.Length == 1)
{
var regex = new Regex(tagsArray[0]);

predicate = (o) => o.Tags.Any(t => regex.IsMatch(t.Name));
}
else
{
predicate = (o) => o.Tags.Any(t => tagsArray.Contains(t.Name));
}
}
else
{
throw new InvalidOperationException("OperationId needs to be specified.");
throw new InvalidOperationException("Either operationId(s) or tag(s) need to be specified.");
}

return predicate;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -957,7 +957,7 @@ namespace Microsoft.OpenApi.Services
public static class OpenApiFilterService
{
public static Microsoft.OpenApi.Models.OpenApiDocument CreateFilteredDocument(Microsoft.OpenApi.Models.OpenApiDocument source, System.Func<Microsoft.OpenApi.Models.OpenApiOperation, bool> predicate) { }
public static System.Func<Microsoft.OpenApi.Models.OpenApiOperation, bool> CreatePredicate(string operationIds) { }
public static System.Func<Microsoft.OpenApi.Models.OpenApiOperation, bool> CreatePredicate(string operationIds = null, string tags = null) { }
}
public class OpenApiReferenceError : Microsoft.OpenApi.Models.OpenApiError
{
Expand Down
31 changes: 21 additions & 10 deletions test/Microsoft.OpenApi.Tests/Services/OpenApiFilterServiceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,27 +19,38 @@ public OpenApiFilterServiceTests()
}

[Theory]
[InlineData("users.user.ListUser")]
[InlineData("users.user.GetUser")]
[InlineData("administrativeUnits.restore")]
[InlineData("graphService.GetGraphService")]
public void ReturnFilteredOpenApiDocumentBasedOnOperationIds(string operationId)
[InlineData("users.user.ListUser", null, 1)]
[InlineData("users.user.GetUser", null, 1)]
[InlineData("users.user.ListUser,users.user.GetUser", null, 2)]
[InlineData("*", null, 12)]
[InlineData("administrativeUnits.restore", null, 1)]
[InlineData("graphService.GetGraphService", null, 1)]
[InlineData(null, "users.user,applications.application", 3)]
[InlineData(null, "^users\\.", 3)]
[InlineData(null, "users.user", 2)]
[InlineData(null, "applications.application", 1)]
[InlineData(null, "reports.Functions", 2)]
public void ReturnFilteredOpenApiDocumentBasedOnOperationIdsAndTags(string operationIds, string tags, int expectedPathCount)
{
// Act
var predicate = OpenApiFilterService.CreatePredicate(operationId);
var predicate = OpenApiFilterService.CreatePredicate(operationIds, tags);
var subsetOpenApiDocument = OpenApiFilterService.CreateFilteredDocument(_openApiDocumentMock, predicate);

// Assert
Assert.NotNull(subsetOpenApiDocument);
Assert.Single(subsetOpenApiDocument.Paths);
Assert.NotEmpty(subsetOpenApiDocument.Paths);
Assert.Equal(expectedPathCount, subsetOpenApiDocument.Paths.Count);
}

[Fact]
public void ThrowsInvalidOperationExceptionInCreatePredicateWhenInvalidOperationIdIsSpecified()
public void ThrowsInvalidOperationExceptionInCreatePredicateWhenInvalidArgumentsArePassed()
{
// Act and Assert
var message = Assert.Throws<InvalidOperationException>(() =>OpenApiFilterService.CreatePredicate(null)).Message;
Assert.Equal("OperationId needs to be specified.", message);
var message1 = Assert.Throws<InvalidOperationException>(() => OpenApiFilterService.CreatePredicate(null, null)).Message;
Assert.Equal("Either operationId(s) or tag(s) need to be specified.", message1);

var message2 = Assert.Throws<InvalidOperationException>(() => OpenApiFilterService.CreatePredicate("users.user.ListUser", "users.user")).Message;
Assert.Equal("Cannot specify both operationIds and tags at the same time.", message2);
}
}
}