From 77063ae270bd5c22fa63fba3e3ddb7aeefb5da0b Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Mon, 25 Oct 2021 16:20:49 +0300 Subject: [PATCH 01/11] Add test cases for filtering OpenApiDocument by tags provided --- .../Services/OpenApiFilterServiceTests.cs | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/test/Microsoft.OpenApi.Tests/Services/OpenApiFilterServiceTests.cs b/test/Microsoft.OpenApi.Tests/Services/OpenApiFilterServiceTests.cs index 308f00952..9676bd1f4 100644 --- a/test/Microsoft.OpenApi.Tests/Services/OpenApiFilterServiceTests.cs +++ b/test/Microsoft.OpenApi.Tests/Services/OpenApiFilterServiceTests.cs @@ -19,19 +19,28 @@ 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)] + [InlineData("users.user.GetUser", null)] + [InlineData("administrativeUnits.restore", null)] + [InlineData("graphService.GetGraphService", null)] + [InlineData(null, "users.user")] + [InlineData(null, "applications.application")] + public void ReturnFilteredOpenApiDocumentBasedOnOperationIds(string operationIds, string tags) { // 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); + if (!string.IsNullOrEmpty(operationIds)) + { + Assert.Single(subsetOpenApiDocument.Paths); + } + else if (!string.IsNullOrEmpty(tags)) + { + Assert.NotEmpty(subsetOpenApiDocument.Paths); + } } [Fact] From 8a60acf2958548eebf3bd1f935f6fcdb6f31f40d Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Mon, 25 Oct 2021 16:21:49 +0300 Subject: [PATCH 02/11] Add --filterByTag command option --- src/Microsoft.OpenApi.Tool/Program.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.OpenApi.Tool/Program.cs b/src/Microsoft.OpenApi.Tool/Program.cs index 938986350..5eaedbde8 100644 --- a/src/Microsoft.OpenApi.Tool/Program.cs +++ b/src/Microsoft.OpenApi.Tool/Program.cs @@ -26,9 +26,10 @@ static async Task 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("--filterByOperationId", "Filters by OperationId provided", typeof(string)) + new Option("--filterByOperationId", "Filters OpenApiDocument by OperationId provided", typeof(string)), + new Option("--filterByTag", "Filters OpenApiDocument by Tag(s) provided", typeof(string)) }; - transformCommand.Handler = CommandHandler.Create( + transformCommand.Handler = CommandHandler.Create( OpenApiService.ProcessOpenApiDocument); rootCommand.Add(transformCommand); From c4aed08331877db063c0d44009684b096c088b61 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Mon, 25 Oct 2021 16:26:01 +0300 Subject: [PATCH 03/11] Add a tags parameter to the filtering service to allow for slicing of OpenApiDocument based on tags provided --- .../Services/OpenApiFilterService.cs | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs b/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs index 37f7f5610..94458fc83 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs @@ -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 @@ -17,8 +18,9 @@ public static class OpenApiFilterService /// Create predicate function based on passed query parameters /// /// Comma delimited list of operationIds or * for all operations. + /// Comma delimited list of tags or a single regex. /// A predicate. - public static Func CreatePredicate(string operationIds) + public static Func CreatePredicate(string operationIds = null, string tags = null) { string predicateSource = null; @@ -37,10 +39,26 @@ public static Func CreatePredicate(string operationIds) predicateSource = $"operationIds: {operationIds}"; } + 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)); + } + + predicateSource = $"tags: {tags}"; + } else { - throw new InvalidOperationException("OperationId needs to be specified."); + throw new InvalidOperationException("Either operationId(s) or tag(s) need to be specified."); } return predicate; From 849d84182230e4f44db0e78e84c98c56b9e5959f Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Mon, 25 Oct 2021 16:26:30 +0300 Subject: [PATCH 04/11] Add a filterByTag param and logic --- src/Microsoft.OpenApi.Tool/OpenApiService.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.OpenApi.Tool/OpenApiService.cs b/src/Microsoft.OpenApi.Tool/OpenApiService.cs index fe1a9d9b4..4bc28adcf 100644 --- a/src/Microsoft.OpenApi.Tool/OpenApiService.cs +++ b/src/Microsoft.OpenApi.Tool/OpenApiService.cs @@ -21,7 +21,8 @@ public static void ProcessOpenApiDocument( FileInfo output, OpenApiSpecVersion version, OpenApiFormat format, - string filterbyOperationId, + string filterbyOperationIds, + string filterByTags, bool inline, bool resolveExternal) { @@ -44,9 +45,9 @@ public static void ProcessOpenApiDocument( document = result.OpenApiDocument; // Check if filter options are provided, then execute - if (!string.IsNullOrEmpty(filterbyOperationId)) + if (!string.IsNullOrEmpty(filterbyOperationIds) || !string.IsNullOrEmpty(filterByTags)) { - var predicate = OpenApiFilterService.CreatePredicate(filterbyOperationId); + var predicate = OpenApiFilterService.CreatePredicate(filterbyOperationIds, filterByTags); document = OpenApiFilterService.CreateFilteredDocument(document, predicate); } From 1553a0c862c029a31a45f047cb6920b01e95a6c9 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Tue, 26 Oct 2021 09:36:36 +0300 Subject: [PATCH 05/11] Code refactoring --- src/Microsoft.OpenApi.Tool/OpenApiService.cs | 9 +++++++-- src/Microsoft.OpenApi.Tool/Program.cs | 4 ++-- .../Services/OpenApiFilterServiceTests.cs | 5 +++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/Microsoft.OpenApi.Tool/OpenApiService.cs b/src/Microsoft.OpenApi.Tool/OpenApiService.cs index 4bc28adcf..317306a1f 100644 --- a/src/Microsoft.OpenApi.Tool/OpenApiService.cs +++ b/src/Microsoft.OpenApi.Tool/OpenApiService.cs @@ -45,9 +45,14 @@ public static void ProcessOpenApiDocument( document = result.OpenApiDocument; // Check if filter options are provided, then execute - if (!string.IsNullOrEmpty(filterbyOperationIds) || !string.IsNullOrEmpty(filterByTags)) + if (!string.IsNullOrEmpty(filterbyOperationIds)) { - var predicate = OpenApiFilterService.CreatePredicate(filterbyOperationIds, filterByTags); + 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); } diff --git a/src/Microsoft.OpenApi.Tool/Program.cs b/src/Microsoft.OpenApi.Tool/Program.cs index 5eaedbde8..ae3967181 100644 --- a/src/Microsoft.OpenApi.Tool/Program.cs +++ b/src/Microsoft.OpenApi.Tool/Program.cs @@ -26,8 +26,8 @@ static async Task 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("--filterByOperationId", "Filters OpenApiDocument by OperationId provided", typeof(string)), - new Option("--filterByTag", "Filters OpenApiDocument by Tag(s) provided", typeof(string)) + new Option("--filterByOperationIds", "Filters OpenApiDocument by OperationId provided", typeof(string)), + new Option("--filterByTags", "Filters OpenApiDocument by Tag(s) provided", typeof(string)) }; transformCommand.Handler = CommandHandler.Create( OpenApiService.ProcessOpenApiDocument); diff --git a/test/Microsoft.OpenApi.Tests/Services/OpenApiFilterServiceTests.cs b/test/Microsoft.OpenApi.Tests/Services/OpenApiFilterServiceTests.cs index 9676bd1f4..058fc8c42 100644 --- a/test/Microsoft.OpenApi.Tests/Services/OpenApiFilterServiceTests.cs +++ b/test/Microsoft.OpenApi.Tests/Services/OpenApiFilterServiceTests.cs @@ -25,6 +25,7 @@ public OpenApiFilterServiceTests() [InlineData("graphService.GetGraphService", null)] [InlineData(null, "users.user")] [InlineData(null, "applications.application")] + [InlineData(null, "reports.Functions")] public void ReturnFilteredOpenApiDocumentBasedOnOperationIds(string operationIds, string tags) { // Act @@ -47,8 +48,8 @@ public void ReturnFilteredOpenApiDocumentBasedOnOperationIds(string operationIds public void ThrowsInvalidOperationExceptionInCreatePredicateWhenInvalidOperationIdIsSpecified() { // Act and Assert - var message = Assert.Throws(() =>OpenApiFilterService.CreatePredicate(null)).Message; - Assert.Equal("OperationId needs to be specified.", message); + var message = Assert.Throws(() =>OpenApiFilterService.CreatePredicate(null, null)).Message; + Assert.Equal("Either operationId(s) or tag(s) need to be specified.", message); } } } From 5a3da1a0b5c6ac2fed216f642078c3ab6c23bfbf Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Mon, 1 Nov 2021 12:00:00 +0300 Subject: [PATCH 06/11] Code cleanup --- src/Microsoft.OpenApi.Tool/OpenApiService.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.OpenApi.Tool/OpenApiService.cs b/src/Microsoft.OpenApi.Tool/OpenApiService.cs index b7972bc0a..2431856e8 100644 --- a/src/Microsoft.OpenApi.Tool/OpenApiService.cs +++ b/src/Microsoft.OpenApi.Tool/OpenApiService.cs @@ -20,7 +20,7 @@ public static void ProcessOpenApiDocument( FileInfo output, OpenApiSpecVersion version, OpenApiFormat format, - string filterbyOperationIds, + string filterByOperationIds, string filterByTags, bool inline, bool resolveExternal) @@ -44,9 +44,14 @@ public static void ProcessOpenApiDocument( document = result.OpenApiDocument; // Check if filter options are provided, then execute - if (!string.IsNullOrEmpty(filterbyOperationIds)) + if (!string.IsNullOrEmpty(filterByOperationIds) && !string.IsNullOrEmpty(filterByTags)) { - var predicate = OpenApiFilterService.CreatePredicate(operationIds: filterbyOperationIds); + throw new InvalidOperationException("Cannot filter by operationIds and tags at the same time."); + } + + if (!string.IsNullOrEmpty(filterByOperationIds)) + { + var predicate = OpenApiFilterService.CreatePredicate(operationIds: filterByOperationIds); document = OpenApiFilterService.CreateFilteredDocument(document, predicate); } if (!string.IsNullOrEmpty(filterByTags)) From 9af85db99924d5bb712dd026737a2c928a6cafa0 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Mon, 1 Nov 2021 13:10:23 +0300 Subject: [PATCH 07/11] Update the public API text file --- test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt index 180a7fd81..0b681a8ec 100755 --- a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt +++ b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt @@ -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 predicate) { } - public static System.Func CreatePredicate(string operationIds) { } + public static System.Func CreatePredicate(string operationIds = null, string tags = null) { } } public class OpenApiReferenceError : Microsoft.OpenApi.Models.OpenApiError { From f33d1f6b969e4c2d91009ab92e5f76468ac89553 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Tue, 2 Nov 2021 09:16:37 +0300 Subject: [PATCH 08/11] Add license header --- src/Microsoft.OpenApi.Tool/OpenApiService.cs | 5 ++++- src/Microsoft.OpenApi.Tool/Program.cs | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.OpenApi.Tool/OpenApiService.cs b/src/Microsoft.OpenApi.Tool/OpenApiService.cs index 2431856e8..895d4ff17 100644 --- a/src/Microsoft.OpenApi.Tool/OpenApiService.cs +++ b/src/Microsoft.OpenApi.Tool/OpenApiService.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using System; using System.IO; using System.Linq; using System.Net; diff --git a/src/Microsoft.OpenApi.Tool/Program.cs b/src/Microsoft.OpenApi.Tool/Program.cs index ae3967181..71507ad8b 100644 --- a/src/Microsoft.OpenApi.Tool/Program.cs +++ b/src/Microsoft.OpenApi.Tool/Program.cs @@ -1,4 +1,7 @@ -using System.CommandLine; +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using System.CommandLine; using System.CommandLine.Invocation; using System.IO; using System.Threading.Tasks; From 526162e3a1bc4a45d7142b2d380ba47ab8e81c8b Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Fri, 5 Nov 2021 12:21:01 +0300 Subject: [PATCH 09/11] Remove unused variable --- src/Microsoft.OpenApi/Services/OpenApiFilterService.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs b/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs index cfe1c09eb..4e2ae7051 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs @@ -48,8 +48,6 @@ public static Func CreatePredicate(string operationIds = { predicate = (o) => o.Tags.Any(t => tagsArray.Contains(t.Name)); } - - predicateSource = $"tags: {tags}"; } else From c90020813dba37870c17fb5eaa6e29f3a850fd93 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Tue, 9 Nov 2021 12:35:30 +0300 Subject: [PATCH 10/11] Address PR feedback --- src/Microsoft.OpenApi.Tool/Program.cs | 2 +- .../Services/OpenApiFilterServiceTests.cs | 32 +++++++++---------- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/src/Microsoft.OpenApi.Tool/Program.cs b/src/Microsoft.OpenApi.Tool/Program.cs index 71507ad8b..a4d32c31e 100644 --- a/src/Microsoft.OpenApi.Tool/Program.cs +++ b/src/Microsoft.OpenApi.Tool/Program.cs @@ -29,7 +29,7 @@ static async Task 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 OpenApiDocument 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( diff --git a/test/Microsoft.OpenApi.Tests/Services/OpenApiFilterServiceTests.cs b/test/Microsoft.OpenApi.Tests/Services/OpenApiFilterServiceTests.cs index 058fc8c42..c9b87f57f 100644 --- a/test/Microsoft.OpenApi.Tests/Services/OpenApiFilterServiceTests.cs +++ b/test/Microsoft.OpenApi.Tests/Services/OpenApiFilterServiceTests.cs @@ -19,14 +19,18 @@ public OpenApiFilterServiceTests() } [Theory] - [InlineData("users.user.ListUser", null)] - [InlineData("users.user.GetUser", null)] - [InlineData("administrativeUnits.restore", null)] - [InlineData("graphService.GetGraphService", null)] - [InlineData(null, "users.user")] - [InlineData(null, "applications.application")] - [InlineData(null, "reports.Functions")] - public void ReturnFilteredOpenApiDocumentBasedOnOperationIds(string operationIds, string tags) + [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(operationIds, tags); @@ -34,18 +38,12 @@ public void ReturnFilteredOpenApiDocumentBasedOnOperationIds(string operationIds // Assert Assert.NotNull(subsetOpenApiDocument); - if (!string.IsNullOrEmpty(operationIds)) - { - Assert.Single(subsetOpenApiDocument.Paths); - } - else if (!string.IsNullOrEmpty(tags)) - { - Assert.NotEmpty(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(() =>OpenApiFilterService.CreatePredicate(null, null)).Message; From 6acf634798c74f4fe9f462b95254e85571366dbb Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Tue, 9 Nov 2021 18:43:02 +0300 Subject: [PATCH 11/11] Add validation check and add test --- src/Microsoft.OpenApi/Services/OpenApiFilterService.cs | 5 ++++- .../Services/OpenApiFilterServiceTests.cs | 7 +++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs b/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs index 4e2ae7051..08774995e 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs @@ -23,6 +23,10 @@ public static class OpenApiFilterService public static Func CreatePredicate(string operationIds = null, string tags = null) { Func 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 == "*") @@ -49,7 +53,6 @@ public static Func CreatePredicate(string operationIds = predicate = (o) => o.Tags.Any(t => tagsArray.Contains(t.Name)); } } - else { throw new InvalidOperationException("Either operationId(s) or tag(s) need to be specified."); diff --git a/test/Microsoft.OpenApi.Tests/Services/OpenApiFilterServiceTests.cs b/test/Microsoft.OpenApi.Tests/Services/OpenApiFilterServiceTests.cs index c9b87f57f..ab65ed744 100644 --- a/test/Microsoft.OpenApi.Tests/Services/OpenApiFilterServiceTests.cs +++ b/test/Microsoft.OpenApi.Tests/Services/OpenApiFilterServiceTests.cs @@ -46,8 +46,11 @@ public void ReturnFilteredOpenApiDocumentBasedOnOperationIdsAndTags(string opera public void ThrowsInvalidOperationExceptionInCreatePredicateWhenInvalidArgumentsArePassed() { // Act and Assert - var message = Assert.Throws(() =>OpenApiFilterService.CreatePredicate(null, null)).Message; - Assert.Equal("Either operationId(s) or tag(s) need to be specified.", message); + var message1 = Assert.Throws(() => OpenApiFilterService.CreatePredicate(null, null)).Message; + Assert.Equal("Either operationId(s) or tag(s) need to be specified.", message1); + + var message2 = Assert.Throws(() => OpenApiFilterService.CreatePredicate("users.user.ListUser", "users.user")).Message; + Assert.Equal("Cannot specify both operationIds and tags at the same time.", message2); } } }