From 2b3abef4f9f18be6493381c303e9aef9f10cbc2e Mon Sep 17 00:00:00 2001 From: Irvine Sunday <40403681+irvinesunday@users.noreply.github.com> Date: Mon, 7 Jun 2021 15:29:18 +0300 Subject: [PATCH 01/57] Adds nullability check to HasOperations method and updates/refactors tests for OpenApiUrlTreeNode (#604) * Refactor out common code into re-usable properties This helps with simplifying the tests since we are abstracting out the plumbing work required for some of the tests. Since the code is repetitive, this can be refactored out and reused. * Defensive programming; XML documentation comment update * Add new tests to cover all edge cases; rename two tests appropriately Co-authored-by: Irvine Sunday --- .../Services/OpenApiUrlTreeNode.cs | 9 +- .../Services/OpenApiUrlTreeNodeTests.cs | 213 ++++++++---------- 2 files changed, 98 insertions(+), 124 deletions(-) diff --git a/src/Microsoft.OpenApi/Services/OpenApiUrlTreeNode.cs b/src/Microsoft.OpenApi/Services/OpenApiUrlTreeNode.cs index 296068914..30a47bdd7 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiUrlTreeNode.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiUrlTreeNode.cs @@ -47,11 +47,16 @@ public class OpenApiUrlTreeNode public string Segment { get; private set; } /// - /// Flag indicating whether the node's PathItems has operations. + /// Flag indicating whether the node's PathItems dictionary has operations + /// under a given label. /// + /// The name of the key for the target operations + /// in the node's PathItems dictionary. /// true or false. public bool HasOperations(string label) { + Utils.CheckArgumentNullOrEmpty(label, nameof(label)); + if (!(PathItems?.ContainsKey(label) ?? false)) { return false; @@ -139,6 +144,8 @@ public OpenApiUrlTreeNode Attach(string path, string label) { Utils.CheckArgumentNullOrEmpty(label, nameof(label)); + Utils.CheckArgumentNullOrEmpty(path, nameof(path)); + Utils.CheckArgumentNull(pathItem, nameof(pathItem)); if (path.StartsWith(RootPathSegment)) { diff --git a/test/Microsoft.OpenApi.Tests/Services/OpenApiUrlTreeNodeTests.cs b/test/Microsoft.OpenApi.Tests/Services/OpenApiUrlTreeNodeTests.cs index a246c66ff..944e6c830 100644 --- a/test/Microsoft.OpenApi.Tests/Services/OpenApiUrlTreeNodeTests.cs +++ b/test/Microsoft.OpenApi.Tests/Services/OpenApiUrlTreeNodeTests.cs @@ -11,6 +11,26 @@ namespace Microsoft.OpenApi.Tests.Services { public class OpenApiUrlTreeNodeTests { + private OpenApiDocument OpenApiDocumentSample_1 => new OpenApiDocument() + { + Paths = new OpenApiPaths() + { + ["/"] = new OpenApiPathItem(), + ["/houses"] = new OpenApiPathItem(), + ["/cars"] = new OpenApiPathItem() + } + }; + + private OpenApiDocument OpenApiDocumentSample_2 => new OpenApiDocument() + { + Paths = new OpenApiPaths() + { + ["/"] = new OpenApiPathItem(), + ["/hotels"] = new OpenApiPathItem(), + ["/offices"] = new OpenApiPathItem() + } + }; + [Fact] public void CreateUrlSpaceWithoutOpenApiDocument() { @@ -64,15 +84,7 @@ public void CreatePathWithoutRootWorks() [Fact] public void CreateMultiplePathsWorks() { - var doc = new OpenApiDocument() - { - Paths = new OpenApiPaths() - { - ["/"] = new OpenApiPathItem(), - ["/houses"] = new OpenApiPathItem(), - ["/cars"] = new OpenApiPathItem() - } - }; + var doc = OpenApiDocumentSample_1; string label = "assets"; var rootNode = OpenApiUrlTreeNode.Create(doc, label); @@ -89,25 +101,9 @@ public void CreateMultiplePathsWorks() [Fact] public void AttachDocumentWorks() { - var doc1 = new OpenApiDocument() - { - Paths = new OpenApiPaths() - { - ["/"] = new OpenApiPathItem(), - ["/houses"] = new OpenApiPathItem(), - ["/cars"] = new OpenApiPathItem() - } - }; + var doc1 = OpenApiDocumentSample_1; - var doc2 = new OpenApiDocument() - { - Paths = new OpenApiPaths() - { - ["/"] = new OpenApiPathItem(), - ["/hotels"] = new OpenApiPathItem(), - ["/offices"] = new OpenApiPathItem() - } - }; + var doc2 = OpenApiDocumentSample_2; var label1 = "personal"; var label2 = "business"; @@ -123,15 +119,7 @@ public void AttachDocumentWorks() [Fact] public void AttachPathWorks() { - var doc = new OpenApiDocument() - { - Paths = new OpenApiPaths() - { - ["/"] = new OpenApiPathItem(), - ["/houses"] = new OpenApiPathItem(), - ["/cars"] = new OpenApiPathItem() - } - }; + var doc = OpenApiDocumentSample_1; var label1 = "personal"; var rootNode = OpenApiUrlTreeNode.Create(doc, label1); @@ -335,96 +323,10 @@ public void SegmentIsParameterWorks() Assert.Equal("{apartment-id}", rootNode.Children["houses"].Children["apartments"].Children["{apartment-id}"].Segment); } - [Fact] - public void ThrowsArgumentExceptionForDuplicateLabels() - { - var doc1 = new OpenApiDocument() - { - Paths = new OpenApiPaths() - { - ["/"] = new OpenApiPathItem(), - ["/houses"] = new OpenApiPathItem(), - ["/cars"] = new OpenApiPathItem() - } - }; - - var doc2 = new OpenApiDocument() - { - Paths = new OpenApiPaths() - { - ["/"] = new OpenApiPathItem(), - ["/hotels"] = new OpenApiPathItem(), - ["/offices"] = new OpenApiPathItem() - } - }; - - var label1 = "personal"; - var rootNode = OpenApiUrlTreeNode.Create(doc1, label1); - - Assert.Throws(() => rootNode.Attach(doc2, label1)); - } - - [Fact] - public void ThrowsArgumentNullExceptionForNullArgumentsInCreateMethod() - { - var doc = new OpenApiDocument() - { - Paths = new OpenApiPaths() - { - ["/"] = new OpenApiPathItem(), - ["/houses"] = new OpenApiPathItem(), - ["/cars"] = new OpenApiPathItem() - } - }; - - Assert.Throws(() => OpenApiUrlTreeNode.Create(doc, "")); - Assert.Throws(() => OpenApiUrlTreeNode.Create(doc, null)); - Assert.Throws(() => OpenApiUrlTreeNode.Create(null, "beta")); - } - - [Fact] - public void ThrowsArgumentNullExceptionForNullArgumentsInAttachMethod() - { - var doc1 = new OpenApiDocument() - { - Paths = new OpenApiPaths() - { - ["/"] = new OpenApiPathItem(), - ["/houses"] = new OpenApiPathItem(), - ["/cars"] = new OpenApiPathItem() - } - }; - - var doc2 = new OpenApiDocument() - { - Paths = new OpenApiPaths() - { - ["/"] = new OpenApiPathItem(), - ["/hotels"] = new OpenApiPathItem(), - ["/offices"] = new OpenApiPathItem() - } - }; - - var label1 = "personal"; - var rootNode = OpenApiUrlTreeNode.Create(doc1, label1); - - Assert.Throws(() => rootNode.Attach(doc2, "")); - Assert.Throws(() => rootNode.Attach(doc2, null)); - Assert.Throws(() => rootNode.Attach(null, "beta")); - } - [Fact] public void AdditionalDataWorks() { - var doc = new OpenApiDocument() - { - Paths = new OpenApiPaths() - { - ["/"] = new OpenApiPathItem(), - ["/houses"] = new OpenApiPathItem(), - ["/cars"] = new OpenApiPathItem() - } - }; + var doc = OpenApiDocumentSample_1; var label = "personal"; var rootNode = OpenApiUrlTreeNode.Create(doc, label); @@ -476,5 +378,70 @@ public void AdditionalDataWorks() Assert.Equal("Convertible", item); }); } + + [Fact] + public void ThrowsArgumentExceptionForDuplicateLabels() + { + var doc1 = OpenApiDocumentSample_1; + + var doc2 = OpenApiDocumentSample_2; + + var label1 = "personal"; + var rootNode = OpenApiUrlTreeNode.Create(doc1, label1); + + Assert.Throws(() => rootNode.Attach(doc2, label1)); + } + + [Fact] + public void ThrowsArgumentNullExceptionForNullOrEmptyArgumentsInCreateMethod() + { + var doc = OpenApiDocumentSample_1; + + Assert.Throws(() => OpenApiUrlTreeNode.Create(doc, "")); + Assert.Throws(() => OpenApiUrlTreeNode.Create(doc, null)); + Assert.Throws(() => OpenApiUrlTreeNode.Create(null, "beta")); + Assert.Throws(() => OpenApiUrlTreeNode.Create(null, null)); + Assert.Throws(() => OpenApiUrlTreeNode.Create(null, "")); + } + + [Fact] + public void ThrowsArgumentNullExceptionForNullOrEmptyArgumentsInAttachMethod() + { + var doc1 = OpenApiDocumentSample_1; + + var doc2 = OpenApiDocumentSample_2; + + var label1 = "personal"; + var rootNode = OpenApiUrlTreeNode.Create(doc1, label1); + + Assert.Throws(() => rootNode.Attach(doc2, "")); + Assert.Throws(() => rootNode.Attach(doc2, null)); + Assert.Throws(() => rootNode.Attach(null, "beta")); + Assert.Throws(() => rootNode.Attach(null, null)); + Assert.Throws(() => rootNode.Attach(null, "")); + } + + [Fact] + public void ThrowsArgumentNullExceptionForNullOrEmptyArgumentInHasOperationsMethod() + { + var doc = OpenApiDocumentSample_1; + + var label = "personal"; + var rootNode = OpenApiUrlTreeNode.Create(doc, label); + + Assert.Throws(() => rootNode.HasOperations(null)); + Assert.Throws(() => rootNode.HasOperations("")); + } + + [Fact] + public void ThrowsArgumentNullExceptionForNullArgumentInAddAdditionalDataMethod() + { + var doc = OpenApiDocumentSample_1; + + var label = "personal"; + var rootNode = OpenApiUrlTreeNode.Create(doc, label); + + Assert.Throws(() => rootNode.AddAdditionalData(null)); + } } } From 6bb23b6fee1d45d97bf47be0ce1ab3d20db74df5 Mon Sep 17 00:00:00 2001 From: Irvine Sunday <40403681+irvinesunday@users.noreply.github.com> Date: Mon, 14 Jun 2021 17:30:59 +0300 Subject: [PATCH 02/57] Adds setting to configure leaving stream open (#605) * Add new boolean setting to flag whether or not to leave stream open * Use LeaveStreamOpen setting to check whether to dispose StreamReader * Add new test to validate new LeaveStreamOpen setting * PR review feedback changes - Remove default assignment of false boolean to setting - Code simplification Co-authored-by: Irvine Sunday --- .../OpenApiReaderSettings.cs | 10 ++++-- .../OpenApiStreamReader.cs | 18 ++++++---- .../OpenApiStreamReaderTests.cs | 35 +++++++++++++++++++ 3 files changed, 54 insertions(+), 9 deletions(-) create mode 100644 test/Microsoft.OpenApi.Readers.Tests/OpenApiReaderTests/OpenApiStreamReaderTests.cs diff --git a/src/Microsoft.OpenApi.Readers/OpenApiReaderSettings.cs b/src/Microsoft.OpenApi.Readers/OpenApiReaderSettings.cs index da178ae86..732708459 100644 --- a/src/Microsoft.OpenApi.Readers/OpenApiReaderSettings.cs +++ b/src/Microsoft.OpenApi.Readers/OpenApiReaderSettings.cs @@ -1,5 +1,5 @@ // Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. +// Licensed under the MIT license. using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Interfaces; @@ -61,11 +61,17 @@ public class OpenApiReaderSettings public Uri BaseUrl { get; set; } /// - /// Function used to provide an alternative loader for accessing external references. + /// Function used to provide an alternative loader for accessing external references. /// /// /// Default loader will attempt to dereference http(s) urls and file urls. /// public IStreamLoader CustomExternalLoader { get; set; } + + /// + /// Whether to leave the object open after reading + /// from an object. + /// + public bool LeaveStreamOpen { get; set; } } } diff --git a/src/Microsoft.OpenApi.Readers/OpenApiStreamReader.cs b/src/Microsoft.OpenApi.Readers/OpenApiStreamReader.cs index cab5d1a83..cccf06a68 100644 --- a/src/Microsoft.OpenApi.Readers/OpenApiStreamReader.cs +++ b/src/Microsoft.OpenApi.Readers/OpenApiStreamReader.cs @@ -1,5 +1,5 @@ // Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. +// Licensed under the MIT license. using System.IO; using System.Threading.Tasks; @@ -29,14 +29,18 @@ public OpenApiStreamReader(OpenApiReaderSettings settings = null) /// Reads the stream input and parses it into an Open API document. /// /// Stream containing OpenAPI description to parse. - /// Returns diagnostic object containing errors detected during parsing - /// Instance of newly created OpenApiDocument + /// Returns diagnostic object containing errors detected during parsing. + /// Instance of newly created OpenApiDocument. public OpenApiDocument Read(Stream input, out OpenApiDiagnostic diagnostic) { - using (var reader = new StreamReader(input)) + var reader = new StreamReader(input); + var result = new OpenApiTextReaderReader(_settings).Read(reader, out diagnostic); + if (!_settings.LeaveStreamOpen) { - return new OpenApiTextReaderReader(_settings).Read(reader, out diagnostic); + reader.Dispose(); } + + return result; } /// @@ -50,8 +54,8 @@ public async Task ReadAsync(Stream input) if (input is MemoryStream) { bufferedStream = (MemoryStream)input; - } - else + } + else { // Buffer stream so that OpenApiTextReaderReader can process it synchronously // YamlDocument doesn't support async reading. diff --git a/test/Microsoft.OpenApi.Readers.Tests/OpenApiReaderTests/OpenApiStreamReaderTests.cs b/test/Microsoft.OpenApi.Readers.Tests/OpenApiReaderTests/OpenApiStreamReaderTests.cs new file mode 100644 index 000000000..7567e0b7d --- /dev/null +++ b/test/Microsoft.OpenApi.Readers.Tests/OpenApiReaderTests/OpenApiStreamReaderTests.cs @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using System.IO; +using Xunit; + +namespace Microsoft.OpenApi.Readers.Tests.OpenApiReaderTests +{ + public class OpenApiStreamReaderTests + { + private const string SampleFolderPath = "V3Tests/Samples/OpenApiDocument/"; + + [Fact] + public void StreamShouldCloseIfLeaveStreamOpenSettingEqualsFalse() + { + using (var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "petStore.yaml"))) + { + var reader = new OpenApiStreamReader(new OpenApiReaderSettings { LeaveStreamOpen = false }); + reader.Read(stream, out _); + Assert.False(stream.CanRead); + } + } + + [Fact] + public void StreamShouldNotCloseIfLeaveStreamOpenSettingEqualsTrue() + { + using (var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "petStore.yaml"))) + { + var reader = new OpenApiStreamReader(new OpenApiReaderSettings { LeaveStreamOpen = true}); + reader.Read(stream, out _); + Assert.True(stream.CanRead); + } + } + } +} From 0dc267c1afc44336327b344b151584915de8583a Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Tue, 12 Oct 2021 12:21:47 +0300 Subject: [PATCH 03/57] Add a mock OpenApiDocument --- .../Documents/OpenApiDocumentMock.cs | 725 ++++++++++++++++++ 1 file changed, 725 insertions(+) create mode 100644 test/Microsoft.OpenApi.Tests/Documents/OpenApiDocumentMock.cs diff --git a/test/Microsoft.OpenApi.Tests/Documents/OpenApiDocumentMock.cs b/test/Microsoft.OpenApi.Tests/Documents/OpenApiDocumentMock.cs new file mode 100644 index 000000000..8b354b36f --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Documents/OpenApiDocumentMock.cs @@ -0,0 +1,725 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using Microsoft.OpenApi.Any; +using Microsoft.OpenApi.Interfaces; +using Microsoft.OpenApi.Models; +using System.Collections.Generic; + +namespace OpenAPIService.Test +{ + /// + /// Mock class that creates a sample OpenAPI document. + /// + public static class OpenApiDocumentMock + { + /// + /// Creates an OpenAPI document. + /// + /// Instance of an OpenApi document + public static OpenApiDocument CreateOpenApiDocument() + { + var applicationJsonMediaType = "application/json"; + + var document = new OpenApiDocument() + { + Paths = new OpenApiPaths() + { + ["/"] = new OpenApiPathItem() // root path + { + Operations = new Dictionary + { + { + OperationType.Get, new OpenApiOperation + { + OperationId = "graphService.GetGraphService", + Responses = new OpenApiResponses() + { + { + "200",new OpenApiResponse() + { + Description = "OK" + } + } + } + } + } + } + }, + ["/reports/microsoft.graph.getTeamsUserActivityCounts(period={period})"] = new OpenApiPathItem() + { + Operations = new Dictionary + { + { + OperationType.Get, new OpenApiOperation + { + Tags = new List + { + { + new OpenApiTag() + { + Name = "reports.Functions" + } + } + }, + OperationId = "reports.getTeamsUserActivityCounts", + Summary = "Invoke function getTeamsUserActivityUserCounts", + Parameters = new List + { + { + new OpenApiParameter() + { + Name = "period", + In = ParameterLocation.Path, + Required = true, + Schema = new OpenApiSchema() + { + Type = "string" + } + } + } + }, + Responses = new OpenApiResponses() + { + { + "200", new OpenApiResponse() + { + Description = "Success", + Content = new Dictionary + { + { + applicationJsonMediaType, + new OpenApiMediaType + { + Schema = new OpenApiSchema + { + Type = "array" + } + } + } + } + } + } + } + } + } + } + }, + ["/reports/microsoft.graph.getTeamsUserActivityUserDetail(date={date})"] = new OpenApiPathItem() + { + Operations = new Dictionary + { + { + OperationType.Get, new OpenApiOperation + { + Tags = new List + { + { + new OpenApiTag() + { + Name = "reports.Functions" + } + } + }, + OperationId = "reports.getTeamsUserActivityUserDetail-a3f1", + Summary = "Invoke function getTeamsUserActivityUserDetail", + Parameters = new List + { + { + new OpenApiParameter() + { + Name = "period", + In = ParameterLocation.Path, + Required = true, + Schema = new OpenApiSchema() + { + Type = "string" + } + } + } + }, + Responses = new OpenApiResponses() + { + { + "200", new OpenApiResponse() + { + Description = "Success", + Content = new Dictionary + { + { + applicationJsonMediaType, + new OpenApiMediaType + { + Schema = new OpenApiSchema + { + Type = "array" + } + } + } + } + } + } + } + } + } + } + }, + ["/users"] = new OpenApiPathItem() + { + Operations = new Dictionary + { + { + OperationType.Get, new OpenApiOperation + { + Tags = new List + { + { + new OpenApiTag() + { + Name = "users.user" + } + } + }, + OperationId = "users.user.ListUser", + Summary = "Get entities from users", + Responses = new OpenApiResponses() + { + { + "200", new OpenApiResponse() + { + Description = "Retrieved entities", + Content = new Dictionary + { + { + applicationJsonMediaType, + new OpenApiMediaType + { + Schema = new OpenApiSchema + { + Title = "Collection of user", + Type = "object", + Properties = new Dictionary + { + { + "value", + new OpenApiSchema + { + Type = "array", + Items = new OpenApiSchema + { + Reference = new OpenApiReference + { + Type = ReferenceType.Schema, + Id = "microsoft.graph.user" + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + }, + ["/users/{user-id}"] = new OpenApiPathItem() + { + Operations = new Dictionary + { + { + OperationType.Get, new OpenApiOperation + { + Tags = new List + { + { + new OpenApiTag() + { + Name = "users.user" + } + } + }, + OperationId = "users.user.GetUser", + Summary = "Get entity from users by key", + Responses = new OpenApiResponses() + { + { + "200", new OpenApiResponse() + { + Description = "Retrieved entity", + Content = new Dictionary + { + { + applicationJsonMediaType, + new OpenApiMediaType + { + Schema = new OpenApiSchema + { + Reference = new OpenApiReference + { + Type = ReferenceType.Schema, + Id = "microsoft.graph.user" + } + } + } + } + } + } + } + } + } + }, + { + OperationType.Patch, new OpenApiOperation + { + Tags = new List + { + { + new OpenApiTag() + { + Name = "users.user" + } + } + }, + OperationId = "users.user.UpdateUser", + Summary = "Update entity in users", + Responses = new OpenApiResponses() + { + { + "204", new OpenApiResponse() + { + Description = "Success" + } + } + } + } + } + } + }, + ["/users/{user-id}/messages/{message-id}"] = new OpenApiPathItem() + { + Operations = new Dictionary + { + { + OperationType.Get, new OpenApiOperation + { + Tags = new List + { + { + new OpenApiTag() + { + Name = "users.message" + } + } + }, + OperationId = "users.GetMessages", + Summary = "Get messages from users", + Description = "The messages in a mailbox or folder. Read-only. Nullable.", + Parameters = new List + { + new OpenApiParameter() + { + Name = "$select", + In = ParameterLocation.Query, + Required = true, + Description = "Select properties to be returned", + Schema = new OpenApiSchema() + { + Type = "array" + } + // missing explode parameter + } + }, + Responses = new OpenApiResponses() + { + { + "200", new OpenApiResponse() + { + Description = "Retrieved navigation property", + Content = new Dictionary + { + { + applicationJsonMediaType, + new OpenApiMediaType + { + Schema = new OpenApiSchema + { + Reference = new OpenApiReference + { + Type = ReferenceType.Schema, + Id = "microsoft.graph.message" + } + } + } + } + } + } + } + } + } + } + } + }, + ["/administrativeUnits/{administrativeUnit-id}/microsoft.graph.restore"] = new OpenApiPathItem() + { + Operations = new Dictionary + { + { + OperationType.Post, new OpenApiOperation + { + Tags = new List + { + { + new OpenApiTag() + { + Name = "administrativeUnits.Actions" + } + } + }, + OperationId = "administrativeUnits.restore", + Summary = "Invoke action restore", + Parameters = new List + { + { + new OpenApiParameter() + { + Name = "administrativeUnit-id", + In = ParameterLocation.Path, + Required = true, + Description = "key: id of administrativeUnit", + Schema = new OpenApiSchema() + { + Type = "string" + } + } + } + }, + Responses = new OpenApiResponses() + { + { + "200", new OpenApiResponse() + { + Description = "Success", + Content = new Dictionary + { + { + applicationJsonMediaType, + new OpenApiMediaType + { + Schema = new OpenApiSchema + { + AnyOf = new List + { + new OpenApiSchema + { + Type = "string" + } + }, + Nullable = true + } + } + } + } + } + } + } + } + } + } + }, + ["/applications/{application-id}/logo"] = new OpenApiPathItem() + { + Operations = new Dictionary + { + { + OperationType.Put, new OpenApiOperation + { + Tags = new List + { + { + new OpenApiTag() + { + Name = "applications.application" + } + } + }, + OperationId = "applications.application.UpdateLogo", + Summary = "Update media content for application in applications", + Responses = new OpenApiResponses() + { + { + "204", new OpenApiResponse() + { + Description = "Success" + } + } + } + } + } + } + }, + ["/security/hostSecurityProfiles"] = new OpenApiPathItem() + { + Operations = new Dictionary + { + { + OperationType.Get, new OpenApiOperation + { + Tags = new List + { + { + new OpenApiTag() + { + Name = "security.hostSecurityProfile" + } + } + }, + OperationId = "security.ListHostSecurityProfiles", + Summary = "Get hostSecurityProfiles from security", + Responses = new OpenApiResponses() + { + { + "200", new OpenApiResponse() + { + Description = "Retrieved navigation property", + Content = new Dictionary + { + { + applicationJsonMediaType, + new OpenApiMediaType + { + Schema = new OpenApiSchema + { + Title = "Collection of hostSecurityProfile", + Type = "object", + Properties = new Dictionary + { + { + "value", + new OpenApiSchema + { + Type = "array", + Items = new OpenApiSchema + { + Reference = new OpenApiReference + { + Type = ReferenceType.Schema, + Id = "microsoft.graph.networkInterface" + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + }, + ["/communications/calls/{call-id}/microsoft.graph.keepAlive"] = new OpenApiPathItem() + { + Operations = new Dictionary + { + { + OperationType.Post, new OpenApiOperation + { + Tags = new List + { + { + new OpenApiTag() + { + Name = "communications.Actions" + } + } + }, + OperationId = "communications.calls.call.keepAlive", + Summary = "Invoke action keepAlive", + Parameters = new List + { + new OpenApiParameter() + { + Name = "call-id", + In = ParameterLocation.Path, + Description = "key: id of call", + Required = true, + Schema = new OpenApiSchema() + { + Type = "string" + }, + Extensions = new Dictionary + { + { + "x-ms-docs-key-type", new OpenApiString("call") + } + } + } + }, + Responses = new OpenApiResponses() + { + { + "204", new OpenApiResponse() + { + Description = "Success" + } + } + }, + Extensions = new Dictionary + { + { + "x-ms-docs-operation-type", new OpenApiString("action") + } + } + } + } + } + }, + ["/groups/{group-id}/events/{event-id}/calendar/events/microsoft.graph.delta"] = new OpenApiPathItem() + { + Operations = new Dictionary + { + { + OperationType.Get, new OpenApiOperation + { + Tags = new List + { + new OpenApiTag() + { + Name = "groups.Functions" + } + }, + OperationId = "groups.group.events.event.calendar.events.delta", + Summary = "Invoke function delta", + Parameters = new List + { + new OpenApiParameter() + { + Name = "group-id", + In = ParameterLocation.Path, + Description = "key: id of group", + Required = true, + Schema = new OpenApiSchema() + { + Type = "string" + }, + Extensions = new Dictionary + { + { + "x-ms-docs-key-type", new OpenApiString("group") + } + } + }, + new OpenApiParameter() + { + Name = "event-id", + In = ParameterLocation.Path, + Description = "key: id of event", + Required = true, + Schema = new OpenApiSchema() + { + Type = "string" + }, + Extensions = new Dictionary + { + { + "x-ms-docs-key-type", new OpenApiString("event") + } + } + } + }, + Responses = new OpenApiResponses() + { + { + "200", new OpenApiResponse() + { + Description = "Success", + Content = new Dictionary + { + { + applicationJsonMediaType, + new OpenApiMediaType + { + Schema = new OpenApiSchema + { + Type = "array", + Reference = new OpenApiReference + { + Type = ReferenceType.Schema, + Id = "microsoft.graph.event" + } + } + } + } + } + } + } + }, + Extensions = new Dictionary + { + { + "x-ms-docs-operation-type", new OpenApiString("function") + } + } + } + } + } + }, + ["/applications/{application-id}/createdOnBehalfOf/$ref"] = new OpenApiPathItem() + { + Operations = new Dictionary + { + { + OperationType.Get, new OpenApiOperation + { + Tags = new List + { + new OpenApiTag() + { + Name = "applications.directoryObject" + } + }, + OperationId = "applications.GetRefCreatedOnBehalfOf", + Summary = "Get ref of createdOnBehalfOf from applications" + } + } + } + } + }, + Components = new OpenApiComponents + { + Schemas = new Dictionary + { + { + "microsoft.graph.networkInterface", new OpenApiSchema + { + Title = "networkInterface", + Type = "object", + Properties = new Dictionary + { + { + "description", new OpenApiSchema + { + Type = "string", + Description = "Description of the NIC (e.g. Ethernet adapter, Wireless LAN adapter Local Area Connection <#>, etc.).", + Nullable = true + } + } + } + } + } + } + } + }; + + return document; + } + } +} From 0ecca08551dcab24364ff3e1c02cc88b649a0fb9 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Tue, 12 Oct 2021 12:22:03 +0300 Subject: [PATCH 04/57] Add tests for filtering validation --- .../Services/OpenApiFilterServiceTests.cs | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 test/Microsoft.OpenApi.Tests/Services/OpenApiFilterServiceTests.cs diff --git a/test/Microsoft.OpenApi.Tests/Services/OpenApiFilterServiceTests.cs b/test/Microsoft.OpenApi.Tests/Services/OpenApiFilterServiceTests.cs new file mode 100644 index 000000000..7fedd989c --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Services/OpenApiFilterServiceTests.cs @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using System; +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Services; +using OpenAPIService.Test; +using Xunit; + +namespace Microsoft.OpenApi.Tests.Services +{ + public class OpenApiFilterServiceTests + { + private const string Title = "Partial Graph API"; + private const string GraphVersion = "mock"; + private readonly OpenApiFilterService _openApiFilterService; + private readonly OpenApiDocument _openApiDocumentMock; + + public OpenApiFilterServiceTests() + { + _openApiFilterService = new OpenApiFilterService(); + _openApiDocumentMock = OpenApiDocumentMock.CreateOpenApiDocument(); + } + + [Theory] + [InlineData("users.user.ListUser")] + [InlineData("users.user.GetUser")] + [InlineData("administrativeUnits.restore")] + [InlineData("graphService.GetGraphService")] + public void ReturnFilteredOpenApiDocumentBasedOnOperationIds(string operationId) + { + // Act + var predicate = _openApiFilterService.CreatePredicate(operationId); + var subsetOpenApiDocument = _openApiFilterService.CreateFilteredDocument(_openApiDocumentMock, Title, GraphVersion, predicate); + + // Assert + Assert.NotNull(subsetOpenApiDocument); + Assert.Single(subsetOpenApiDocument.Paths); + } + + [Fact] + public void ThrowsInvalidOperationExceptionInCreatePredicateWhenInvalidOperationIdIsSpecified() + { + // Act and Assert + var message = Assert.Throws(() =>_openApiFilterService.CreatePredicate(null)).Message; + Assert.Equal("OperationId needs to be specified.", message); + } + } +} From 2f4a1c8007a7bb6ad90137a5166563703c6bd81c Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Tue, 12 Oct 2021 12:24:08 +0300 Subject: [PATCH 05/57] Add an OpenApi filtering service for filtering an OpenApiDocument based on OperationId --- .../Services/OpenApiFilterService.cs | 186 ++++++++++++++++++ 1 file changed, 186 insertions(+) create mode 100644 src/Microsoft.OpenApi/Services/OpenApiFilterService.cs diff --git a/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs b/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs new file mode 100644 index 000000000..4f1795bc6 --- /dev/null +++ b/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs @@ -0,0 +1,186 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.OpenApi.Models; + +namespace Microsoft.OpenApi.Services +{ + /// + /// + /// + public class OpenApiFilterService + { + public static readonly string GraphAuthorizationUrl = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize"; + public static readonly string GraphTokenUrl = "https://login.microsoftonline.com/common/oauth2/v2.0/token"; + public static readonly string GraphUrl = "https://graph.microsoft.com/{0}/"; + public const string GraphVersion_V1 = "v1.0"; + + + public OpenApiDocument CreateSubsetOpenApiDocument(string operationIds, OpenApiDocument source, string title) + { + var predicate = CreatePredicate(operationIds); + + var subsetOpenApiDocument = CreateFilteredDocument(source, title, GraphVersion_V1, predicate); + + return subsetOpenApiDocument; + } + + public Func CreatePredicate(string operationIds) + { + string predicateSource = null; + + Func predicate; + if (operationIds != null) + { + if (operationIds == "*") + { + predicate = (o) => true; // All operations + } + else + { + var operationIdsArray = operationIds.Split(','); + predicate = (o) => operationIdsArray.Contains(o.OperationId); + } + + predicateSource = $"operationIds: {operationIds}"; + } + + else + { + throw new InvalidOperationException("OperationId needs to be specified."); + } + + return predicate; + } + /// + /// + /// + /// + /// + /// + /// + /// + public OpenApiDocument CreateFilteredDocument(OpenApiDocument source, string title, string graphVersion, Func predicate) + { + var subset = new OpenApiDocument + { + Info = new OpenApiInfo() + { + Title = title, + Version = graphVersion + }, + + Components = new OpenApiComponents() + }; + var aadv2Scheme = new OpenApiSecurityScheme() + { + Type = SecuritySchemeType.OAuth2, + Flows = new OpenApiOAuthFlows() + { + AuthorizationCode = new OpenApiOAuthFlow() + { + AuthorizationUrl = new Uri(GraphAuthorizationUrl), + TokenUrl = new Uri(GraphTokenUrl) + } + }, + Reference = new OpenApiReference() { Id = "azureaadv2", Type = ReferenceType.SecurityScheme }, + UnresolvedReference = false + }; + subset.Components.SecuritySchemes.Add("azureaadv2", aadv2Scheme); + + subset.SecurityRequirements.Add(new OpenApiSecurityRequirement() { { aadv2Scheme, Array.Empty() } }); + + subset.Servers.Add(new OpenApiServer() { Description = "Core", Url = string.Format(GraphUrl, graphVersion) }); + + var results = FindOperations(source, predicate); + foreach (var result in results) + { + OpenApiPathItem pathItem; + string pathKey = result.CurrentKeys.Path; + + if (subset.Paths == null) + { + subset.Paths = new OpenApiPaths(); + pathItem = new OpenApiPathItem(); + subset.Paths.Add(pathKey, pathItem); + } + else + { + if (!subset.Paths.TryGetValue(pathKey, out pathItem)) + { + pathItem = new OpenApiPathItem(); + subset.Paths.Add(pathKey, pathItem); + } + } + + pathItem.Operations.Add((OperationType)result.CurrentKeys.Operation, result.Operation); + } + + if (subset.Paths == null) + { + throw new ArgumentException("No paths found for the supplied parameters."); + } + + CopyReferences(subset); + + return subset; + } + + private static IList FindOperations(OpenApiDocument graphOpenApi, Func predicate) + { + var search = new OperationSearch(predicate); + var walker = new OpenApiWalker(search); + walker.Walk(graphOpenApi); + return search.SearchResults; + } + + private static void CopyReferences(OpenApiDocument target) + { + bool morestuff; + do + { + var copy = new CopyReferences(target); + var walker = new OpenApiWalker(copy); + walker.Walk(target); + + morestuff = AddReferences(copy.Components, target.Components); + + } while (morestuff); + } + + private static bool AddReferences(OpenApiComponents newComponents, OpenApiComponents target) + { + var moreStuff = false; + foreach (var item in newComponents.Schemas) + { + if (!target.Schemas.ContainsKey(item.Key)) + { + moreStuff = true; + target.Schemas.Add(item); + } + } + + foreach (var item in newComponents.Parameters) + { + if (!target.Parameters.ContainsKey(item.Key)) + { + moreStuff = true; + target.Parameters.Add(item); + } + } + + foreach (var item in newComponents.Responses) + { + if (!target.Responses.ContainsKey(item.Key)) + { + moreStuff = true; + target.Responses.Add(item); + } + } + return moreStuff; + } + } +} From 8e45f8b531cb03db9695c65d18bd6f40c156d746 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Tue, 12 Oct 2021 12:24:44 +0300 Subject: [PATCH 06/57] Add necessary packages --- src/Microsoft.OpenApi/Microsoft.OpenApi.csproj | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj b/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj index d0ff2fbcd..4d305cfdc 100644 --- a/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj +++ b/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj @@ -1,6 +1,7 @@  netstandard2.0 + 9.0 true http://go.microsoft.com/fwlink/?LinkID=288890 https://github.com/Microsoft/OpenAPI.NET @@ -36,7 +37,8 @@ - + + From f270b908247e0bf9d5478d2b0f24e05233fa9ceb Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Tue, 12 Oct 2021 15:54:42 +0300 Subject: [PATCH 07/57] Simplify using statement and switch condition --- src/Microsoft.OpenApi.Tool/OpenApiService.cs | 49 +++++++------------- 1 file changed, 16 insertions(+), 33 deletions(-) diff --git a/src/Microsoft.OpenApi.Tool/OpenApiService.cs b/src/Microsoft.OpenApi.Tool/OpenApiService.cs index c52c08941..80e6bf7bc 100644 --- a/src/Microsoft.OpenApi.Tool/OpenApiService.cs +++ b/src/Microsoft.OpenApi.Tool/OpenApiService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -52,43 +52,26 @@ public static void ProcessOpenApiDocument( errorReport.AppendLine(error.ToString()); } - throw new ArgumentException(String.Join(Environment.NewLine, context.Errors.Select(e => e.Message).ToArray())); + throw new ArgumentException(string.Join(Environment.NewLine, context.Errors.Select(e => e.Message).ToArray())); } - using (var outputStream = output?.Create()) - { - TextWriter textWriter; + using var outputStream = output?.Create(); - if (outputStream != null) - { - textWriter = new StreamWriter(outputStream); - } - else - { - textWriter = Console.Out; - } + var textWriter = outputStream != null ? new StreamWriter(outputStream) : Console.Out; - var settings = new OpenApiWriterSettings() - { - ReferenceInline = inline == true ? ReferenceInlineSetting.InlineLocalReferences : ReferenceInlineSetting.DoNotInlineReferences - }; - IOpenApiWriter writer; - switch (format) - { - case OpenApiFormat.Json: - writer = new OpenApiJsonWriter(textWriter, settings); - break; - case OpenApiFormat.Yaml: - writer = new OpenApiYamlWriter(textWriter, settings); - break; - default: - throw new ArgumentException("Unknown format"); - } - - document.Serialize(writer, version); + var settings = new OpenApiWriterSettings() + { + ReferenceInline = inline ? ReferenceInlineSetting.InlineLocalReferences : ReferenceInlineSetting.DoNotInlineReferences + }; + IOpenApiWriter writer = format switch + { + OpenApiFormat.Json => new OpenApiJsonWriter(textWriter, settings), + OpenApiFormat.Yaml => new OpenApiYamlWriter(textWriter, settings), + _ => throw new ArgumentException("Unknown format"), + }; + document.Serialize(writer, version); - textWriter.Flush(); - } + textWriter.Flush(); } private static Stream GetStream(string input) From 9cda4fb5fc5467c8f5980bba930e27a58b773105 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Mon, 18 Oct 2021 11:00:52 +0300 Subject: [PATCH 08/57] Use static class reference --- .../Services/OpenApiFilterServiceTests.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/test/Microsoft.OpenApi.Tests/Services/OpenApiFilterServiceTests.cs b/test/Microsoft.OpenApi.Tests/Services/OpenApiFilterServiceTests.cs index 7fedd989c..4f6a9bcbf 100644 --- a/test/Microsoft.OpenApi.Tests/Services/OpenApiFilterServiceTests.cs +++ b/test/Microsoft.OpenApi.Tests/Services/OpenApiFilterServiceTests.cs @@ -13,12 +13,10 @@ public class OpenApiFilterServiceTests { private const string Title = "Partial Graph API"; private const string GraphVersion = "mock"; - private readonly OpenApiFilterService _openApiFilterService; private readonly OpenApiDocument _openApiDocumentMock; public OpenApiFilterServiceTests() { - _openApiFilterService = new OpenApiFilterService(); _openApiDocumentMock = OpenApiDocumentMock.CreateOpenApiDocument(); } @@ -30,8 +28,8 @@ public OpenApiFilterServiceTests() public void ReturnFilteredOpenApiDocumentBasedOnOperationIds(string operationId) { // Act - var predicate = _openApiFilterService.CreatePredicate(operationId); - var subsetOpenApiDocument = _openApiFilterService.CreateFilteredDocument(_openApiDocumentMock, Title, GraphVersion, predicate); + var predicate = OpenApiFilterService.CreatePredicate(operationId); + var subsetOpenApiDocument = OpenApiFilterService.CreateFilteredDocument(_openApiDocumentMock, Title, GraphVersion, predicate); // Assert Assert.NotNull(subsetOpenApiDocument); @@ -42,7 +40,7 @@ public void ReturnFilteredOpenApiDocumentBasedOnOperationIds(string operationId) public void ThrowsInvalidOperationExceptionInCreatePredicateWhenInvalidOperationIdIsSpecified() { // Act and Assert - var message = Assert.Throws(() =>_openApiFilterService.CreatePredicate(null)).Message; + var message = Assert.Throws(() =>OpenApiFilterService.CreatePredicate(null)).Message; Assert.Equal("OperationId needs to be specified.", message); } } From af61c14ff792cb3bcdda8b115caa23a7a4c6b774 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Mon, 18 Oct 2021 11:01:53 +0300 Subject: [PATCH 09/57] Add --filterbyOperationId command option --- src/Microsoft.OpenApi.Tool/Program.cs | 31 +++++---------------------- 1 file changed, 5 insertions(+), 26 deletions(-) diff --git a/src/Microsoft.OpenApi.Tool/Program.cs b/src/Microsoft.OpenApi.Tool/Program.cs index 446e2829a..938986350 100644 --- a/src/Microsoft.OpenApi.Tool/Program.cs +++ b/src/Microsoft.OpenApi.Tool/Program.cs @@ -1,34 +1,12 @@ -using System; -using System.CommandLine; +using System.CommandLine; using System.CommandLine.Invocation; using System.IO; using System.Threading.Tasks; -using Microsoft.OpenApi; namespace Microsoft.OpenApi.Tool { - class Program + static class Program { - static async Task OldMain(string[] args) - { - - var command = new RootCommand - { - new Option("--input", "Input OpenAPI description file path or URL", typeof(string) ), - new Option("--output","Output OpenAPI description file", typeof(FileInfo), arity: ArgumentArity.ZeroOrOne), - new Option("--version", "OpenAPI specification version", typeof(OpenApiSpecVersion)), - new Option("--format", "File format",typeof(OpenApiFormat) ), - new Option("--inline", "Inline $ref instances", typeof(bool) ), - new Option("--resolveExternal","Resolve external $refs", typeof(bool)) - }; - - command.Handler = CommandHandler.Create( - OpenApiService.ProcessOpenApiDocument); - - // Parse the incoming args and invoke the handler - return await command.InvokeAsync(args); - } - static async Task Main(string[] args) { var rootCommand = new RootCommand() { @@ -47,9 +25,10 @@ static async Task Main(string[] args) new Option("--version", "OpenAPI specification version", typeof(OpenApiSpecVersion)), 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("--resolveExternal","Resolve external $refs", typeof(bool)), + new Option("--filterByOperationId", "Filters by OperationId provided", typeof(string)) }; - transformCommand.Handler = CommandHandler.Create( + transformCommand.Handler = CommandHandler.Create( OpenApiService.ProcessOpenApiDocument); rootCommand.Add(transformCommand); From d24442d45833859d9c73ccb8840c7f918cd3ac93 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Mon, 18 Oct 2021 11:02:39 +0300 Subject: [PATCH 10/57] Add filterByOperationId param and logic --- src/Microsoft.OpenApi.Tool/OpenApiService.cs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.OpenApi.Tool/OpenApiService.cs b/src/Microsoft.OpenApi.Tool/OpenApiService.cs index 80e6bf7bc..7b56f4516 100644 --- a/src/Microsoft.OpenApi.Tool/OpenApiService.cs +++ b/src/Microsoft.OpenApi.Tool/OpenApiService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -16,11 +16,15 @@ namespace Microsoft.OpenApi.Tool { static class OpenApiService { + public const string GraphVersion_V1 = "v1.0"; + public const string Title = "Partial Graph API"; + public static void ProcessOpenApiDocument( string input, FileInfo output, OpenApiSpecVersion version, OpenApiFormat format, + string filterbyOperationId, bool inline, bool resolveExternal) { @@ -35,12 +39,20 @@ public static void ProcessOpenApiDocument( var result = new OpenApiStreamReader(new OpenApiReaderSettings { - ReferenceResolution = resolveExternal == true ? ReferenceResolutionSetting.ResolveAllReferences : ReferenceResolutionSetting.ResolveLocalReferences, + ReferenceResolution = resolveExternal ? ReferenceResolutionSetting.ResolveAllReferences : ReferenceResolutionSetting.ResolveLocalReferences, RuleSet = ValidationRuleSet.GetDefaultRuleSet() } ).ReadAsync(stream).GetAwaiter().GetResult(); document = result.OpenApiDocument; + + // Check if filter options are provided, then execute + if (!string.IsNullOrEmpty(filterbyOperationId)) + { + var predicate = OpenApiFilterService.CreatePredicate(filterbyOperationId); + document = OpenApiFilterService.CreateFilteredDocument(document, Title, GraphVersion_V1, predicate); + } + var context = result.OpenApiDiagnostic; if (context.Errors.Count != 0) From 501e88cebec533095d1fade8e3e43f45c54d904c Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Mon, 18 Oct 2021 11:56:26 +0300 Subject: [PATCH 11/57] Add static modifier --- src/Microsoft.OpenApi/Services/OpenApiFilterService.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs b/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs index 4f1795bc6..86722316f 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System; @@ -63,7 +63,7 @@ public Func CreatePredicate(string operationIds) /// /// /// - public OpenApiDocument CreateFilteredDocument(OpenApiDocument source, string title, string graphVersion, Func predicate) + public static OpenApiDocument CreateFilteredDocument(OpenApiDocument source, string title, string graphVersion, Func predicate) { var subset = new OpenApiDocument { From d5b1b5e3c61547259f1aa6704c1533ca0c337c6d Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Mon, 18 Oct 2021 12:10:27 +0300 Subject: [PATCH 12/57] Clean up and add xml comments --- .../Services/OpenApiFilterService.cs | 36 +++++++++---------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs b/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs index 86722316f..49cecd41e 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System; @@ -9,26 +9,21 @@ namespace Microsoft.OpenApi.Services { /// - /// + /// A service that slices an OpenApiDocument into a subset document /// - public class OpenApiFilterService + public static class OpenApiFilterService { public static readonly string GraphAuthorizationUrl = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize"; public static readonly string GraphTokenUrl = "https://login.microsoftonline.com/common/oauth2/v2.0/token"; public static readonly string GraphUrl = "https://graph.microsoft.com/{0}/"; public const string GraphVersion_V1 = "v1.0"; - - public OpenApiDocument CreateSubsetOpenApiDocument(string operationIds, OpenApiDocument source, string title) - { - var predicate = CreatePredicate(operationIds); - - var subsetOpenApiDocument = CreateFilteredDocument(source, title, GraphVersion_V1, predicate); - - return subsetOpenApiDocument; - } - - public Func CreatePredicate(string operationIds) + /// + /// Create predicate function based on passed query parameters + /// + /// Comma delimited list of operationIds or * for all operations. + /// A predicate. + public static Func CreatePredicate(string operationIds) { string predicateSource = null; @@ -55,14 +50,15 @@ public Func CreatePredicate(string operationIds) return predicate; } + /// - /// + /// Create partial OpenAPI document based on the provided predicate. /// - /// - /// - /// - /// - /// + /// The target . + /// The OpenAPI document title. + /// Version of the target Microsoft Graph API. + /// A predicate function. + /// A partial OpenAPI document. public static OpenApiDocument CreateFilteredDocument(OpenApiDocument source, string title, string graphVersion, Func predicate) { var subset = new OpenApiDocument From 4016ba1fe3a09bbb1fade1e3d43ea5e8bdbb97b5 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Mon, 18 Oct 2021 12:37:34 +0300 Subject: [PATCH 13/57] Add a class that visits the OpenApi operations and returns the search results --- .../Services/OperationSearch.cs | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 src/Microsoft.OpenApi/Services/OperationSearch.cs diff --git a/src/Microsoft.OpenApi/Services/OperationSearch.cs b/src/Microsoft.OpenApi/Services/OperationSearch.cs new file mode 100644 index 000000000..01b1b5f56 --- /dev/null +++ b/src/Microsoft.OpenApi/Services/OperationSearch.cs @@ -0,0 +1,67 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.OpenApi.Models; + +namespace Microsoft.OpenApi.Services +{ + public class OperationSearch : OpenApiVisitorBase + { + private readonly Func _predicate; + private readonly List _searchResults = new(); + + public IList SearchResults => _searchResults; + + public OperationSearch(Func predicate) + { + _predicate = predicate ?? throw new ArgumentNullException(nameof(predicate)); + } + + /// + /// Visits . + /// + /// The target . + public override void Visit(OpenApiOperation operation) + { + if (_predicate(operation)) + { + _searchResults.Add(new SearchResult() + { + Operation = operation, + CurrentKeys = CopyCurrentKeys(CurrentKeys) + }); + } + } + + /// + /// Visits list of . + /// + /// The target list of . + public override void Visit(IList parameters) + { + /* The Parameter.Explode property should be true + * if Parameter.Style == Form; but OData query params + * as used in Microsoft Graph implement explode: false + * ex: $select=id,displayName,givenName + */ + foreach (var parameter in parameters.Where(x => x.Style == ParameterStyle.Form)) + { + parameter.Explode = false; + } + + base.Visit(parameters); + } + + private static CurrentKeys CopyCurrentKeys(CurrentKeys currentKeys) + { + return new CurrentKeys + { + Path = currentKeys.Path, + Operation = currentKeys.Operation + }; + } + } +} From 73bef228c86b86abf96043855ecb981c2f39e720 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Mon, 18 Oct 2021 12:38:06 +0300 Subject: [PATCH 14/57] Add a search result object model --- src/Microsoft.OpenApi/Services/SearchResult.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 src/Microsoft.OpenApi/Services/SearchResult.cs diff --git a/src/Microsoft.OpenApi/Services/SearchResult.cs b/src/Microsoft.OpenApi/Services/SearchResult.cs new file mode 100644 index 000000000..2b7d9f94a --- /dev/null +++ b/src/Microsoft.OpenApi/Services/SearchResult.cs @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using Microsoft.OpenApi.Models; + +namespace Microsoft.OpenApi.Services +{ + public class SearchResult + { + public CurrentKeys CurrentKeys { get; set; } + public OpenApiOperation Operation { get; set; } + } +} From a1d46e57da451ac5d68ccc2b26f811b4cd607896 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Mon, 18 Oct 2021 12:43:03 +0300 Subject: [PATCH 15/57] Copies OpenApiOperation references to the new subset document --- .../Services/CopyReferences.cs | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 src/Microsoft.OpenApi/Services/CopyReferences.cs diff --git a/src/Microsoft.OpenApi/Services/CopyReferences.cs b/src/Microsoft.OpenApi/Services/CopyReferences.cs new file mode 100644 index 000000000..9fc25b210 --- /dev/null +++ b/src/Microsoft.OpenApi/Services/CopyReferences.cs @@ -0,0 +1,101 @@ +using System.Collections.Generic; +using Microsoft.OpenApi.Interfaces; +using Microsoft.OpenApi.Models; + +namespace Microsoft.OpenApi.Services +{ + internal class CopyReferences : OpenApiVisitorBase + { + private readonly OpenApiDocument target; + public OpenApiComponents Components = new OpenApiComponents(); + + public CopyReferences(OpenApiDocument target) + { + this.target = target; + } + + public override void Visit(IOpenApiReferenceable referenceable) + { + switch (referenceable) + { + case OpenApiSchema schema: + EnsureComponentsExists(); + EnsureSchemasExists(); + if (!Components.Schemas.ContainsKey(schema.Reference.Id)) + { + Components.Schemas.Add(schema.Reference.Id, schema); + } + break; + + case OpenApiParameter parameter: + EnsureComponentsExists(); + EnsureParametersExists(); + if (!Components.Parameters.ContainsKey(parameter.Reference.Id)) + { + Components.Parameters.Add(parameter.Reference.Id, parameter); + } + break; + + case OpenApiResponse response: + EnsureComponentsExists(); + EnsureResponsesExists(); + if (!Components.Responses.ContainsKey(response.Reference.Id)) + { + Components.Responses.Add(response.Reference.Id, response); + } + break; + + default: + break; + } + base.Visit(referenceable); + } + + public override void Visit(OpenApiSchema schema) + { + // This is needed to handle schemas used in Responses in components + if (schema.Reference != null) + { + EnsureComponentsExists(); + EnsureSchemasExists(); + if (!Components.Schemas.ContainsKey(schema.Reference.Id)) + { + Components.Schemas.Add(schema.Reference.Id, schema); + } + } + base.Visit(schema); + } + + private void EnsureComponentsExists() + { + if (target.Components == null) + { + target.Components = new OpenApiComponents(); + } + } + + private void EnsureSchemasExists() + { + if (target.Components.Schemas == null) + { + target.Components.Schemas = new Dictionary(); + } + } + + private void EnsureParametersExists() + { + if (target.Components.Parameters == null) + { + target.Components.Parameters = new Dictionary(); + } + } + + private void EnsureResponsesExists() + { + if (target.Components.Responses == null) + { + target.Components.Responses = new Dictionary(); + } + } + } +} From b3c79eb037df7027f9dc8456d92b17add26e1414 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Wed, 20 Oct 2021 09:57:44 +0300 Subject: [PATCH 16/57] Copy OpenApiInfo and components from the source document to the new subset doc --- .../Services/OpenApiFilterService.cs | 35 ++++--------------- 1 file changed, 7 insertions(+), 28 deletions(-) diff --git a/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs b/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs index 49cecd41e..37f7f5610 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs @@ -13,11 +13,6 @@ namespace Microsoft.OpenApi.Services /// public static class OpenApiFilterService { - public static readonly string GraphAuthorizationUrl = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize"; - public static readonly string GraphTokenUrl = "https://login.microsoftonline.com/common/oauth2/v2.0/token"; - public static readonly string GraphUrl = "https://graph.microsoft.com/{0}/"; - public const string GraphVersion_V1 = "v1.0"; - /// /// Create predicate function based on passed query parameters /// @@ -55,41 +50,25 @@ public static Func CreatePredicate(string operationIds) /// Create partial OpenAPI document based on the provided predicate. /// /// The target . - /// The OpenAPI document title. - /// Version of the target Microsoft Graph API. /// A predicate function. /// A partial OpenAPI document. - public static OpenApiDocument CreateFilteredDocument(OpenApiDocument source, string title, string graphVersion, Func predicate) + public static OpenApiDocument CreateFilteredDocument(OpenApiDocument source, Func predicate) { + // Fetch and copy title, graphVersion and server info from OpenApiDoc var subset = new OpenApiDocument { Info = new OpenApiInfo() { - Title = title, - Version = graphVersion + Title = source.Info.Title, + Version = source.Info.Version }, Components = new OpenApiComponents() }; - var aadv2Scheme = new OpenApiSecurityScheme() - { - Type = SecuritySchemeType.OAuth2, - Flows = new OpenApiOAuthFlows() - { - AuthorizationCode = new OpenApiOAuthFlow() - { - AuthorizationUrl = new Uri(GraphAuthorizationUrl), - TokenUrl = new Uri(GraphTokenUrl) - } - }, - Reference = new OpenApiReference() { Id = "azureaadv2", Type = ReferenceType.SecurityScheme }, - UnresolvedReference = false - }; - subset.Components.SecuritySchemes.Add("azureaadv2", aadv2Scheme); - - subset.SecurityRequirements.Add(new OpenApiSecurityRequirement() { { aadv2Scheme, Array.Empty() } }); - subset.Servers.Add(new OpenApiServer() { Description = "Core", Url = string.Format(GraphUrl, graphVersion) }); + subset.Components.SecuritySchemes = source.Components.SecuritySchemes; + subset.SecurityRequirements = source.SecurityRequirements; + subset.Servers = source.Servers; var results = FindOperations(source, predicate); foreach (var result in results) From cd5f3faff6dd1e962cdaf103513f5e520950af34 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Wed, 20 Oct 2021 09:58:00 +0300 Subject: [PATCH 17/57] Add an info object to the mock document --- .../Microsoft.OpenApi.Tests/Documents/OpenApiDocumentMock.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/Microsoft.OpenApi.Tests/Documents/OpenApiDocumentMock.cs b/test/Microsoft.OpenApi.Tests/Documents/OpenApiDocumentMock.cs index 8b354b36f..676bf8e65 100644 --- a/test/Microsoft.OpenApi.Tests/Documents/OpenApiDocumentMock.cs +++ b/test/Microsoft.OpenApi.Tests/Documents/OpenApiDocumentMock.cs @@ -23,6 +23,11 @@ public static OpenApiDocument CreateOpenApiDocument() var document = new OpenApiDocument() { + Info = new OpenApiInfo() + { + Title = "People", + Version = "v1.0" + }, Paths = new OpenApiPaths() { ["/"] = new OpenApiPathItem() // root path From 3c43ea176ac08db90ea02b561a8fb75092935595 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Wed, 20 Oct 2021 09:58:22 +0300 Subject: [PATCH 18/57] Clean up: Remove unnecessary params --- src/Microsoft.OpenApi.Tool/OpenApiService.cs | 5 +---- .../Services/OpenApiFilterServiceTests.cs | 4 +--- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/Microsoft.OpenApi.Tool/OpenApiService.cs b/src/Microsoft.OpenApi.Tool/OpenApiService.cs index 7b56f4516..fe1a9d9b4 100644 --- a/src/Microsoft.OpenApi.Tool/OpenApiService.cs +++ b/src/Microsoft.OpenApi.Tool/OpenApiService.cs @@ -16,9 +16,6 @@ namespace Microsoft.OpenApi.Tool { static class OpenApiService { - public const string GraphVersion_V1 = "v1.0"; - public const string Title = "Partial Graph API"; - public static void ProcessOpenApiDocument( string input, FileInfo output, @@ -50,7 +47,7 @@ public static void ProcessOpenApiDocument( if (!string.IsNullOrEmpty(filterbyOperationId)) { var predicate = OpenApiFilterService.CreatePredicate(filterbyOperationId); - document = OpenApiFilterService.CreateFilteredDocument(document, Title, GraphVersion_V1, predicate); + document = OpenApiFilterService.CreateFilteredDocument(document, predicate); } var context = result.OpenApiDiagnostic; diff --git a/test/Microsoft.OpenApi.Tests/Services/OpenApiFilterServiceTests.cs b/test/Microsoft.OpenApi.Tests/Services/OpenApiFilterServiceTests.cs index 4f6a9bcbf..308f00952 100644 --- a/test/Microsoft.OpenApi.Tests/Services/OpenApiFilterServiceTests.cs +++ b/test/Microsoft.OpenApi.Tests/Services/OpenApiFilterServiceTests.cs @@ -11,8 +11,6 @@ namespace Microsoft.OpenApi.Tests.Services { public class OpenApiFilterServiceTests { - private const string Title = "Partial Graph API"; - private const string GraphVersion = "mock"; private readonly OpenApiDocument _openApiDocumentMock; public OpenApiFilterServiceTests() @@ -29,7 +27,7 @@ public void ReturnFilteredOpenApiDocumentBasedOnOperationIds(string operationId) { // Act var predicate = OpenApiFilterService.CreatePredicate(operationId); - var subsetOpenApiDocument = OpenApiFilterService.CreateFilteredDocument(_openApiDocumentMock, Title, GraphVersion, predicate); + var subsetOpenApiDocument = OpenApiFilterService.CreateFilteredDocument(_openApiDocumentMock, predicate); // Assert Assert.NotNull(subsetOpenApiDocument); From 43f48ec9fc5caf2ed4a4b5b9b973b1b2c738de78 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Thu, 21 Oct 2021 23:11:11 +0300 Subject: [PATCH 19/57] Remove package reference --- src/Microsoft.OpenApi/Microsoft.OpenApi.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj b/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj index 4d305cfdc..e16b84db0 100644 --- a/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj +++ b/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj @@ -38,7 +38,6 @@ - From 77063ae270bd5c22fa63fba3e3ddb7aeefb5da0b Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Mon, 25 Oct 2021 16:20:49 +0300 Subject: [PATCH 20/57] 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 21/57] 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 22/57] 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 23/57] 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 24/57] 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 8ba33cdbba85b5ce517168e7554e7a503efe6891 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Thu, 21 Oct 2021 23:11:11 +0300 Subject: [PATCH 25/57] Revert "Remove package reference" This reverts commit 43f48ec9fc5caf2ed4a4b5b9b973b1b2c738de78. --- src/Microsoft.OpenApi/Microsoft.OpenApi.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj b/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj index e16b84db0..4d305cfdc 100644 --- a/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj +++ b/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj @@ -38,6 +38,7 @@ + From 2397e9d94370f212668e10eb3eecb21354cc7350 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Tue, 26 Oct 2021 17:09:23 +0300 Subject: [PATCH 26/57] Update the Public API interface text file --- .../PublicApi/PublicApi.approved.txt | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt index d5a89e586..180a7fd81 100755 --- a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt +++ b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt @@ -954,6 +954,11 @@ namespace Microsoft.OpenApi.Services public string Response { get; set; } public string ServerVariable { get; } } + 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 class OpenApiReferenceError : Microsoft.OpenApi.Models.OpenApiError { public OpenApiReferenceError(Microsoft.OpenApi.Exceptions.OpenApiException exception) { } @@ -1044,6 +1049,19 @@ namespace Microsoft.OpenApi.Services public System.IO.Stream GetArtifact(string location) { } public Microsoft.OpenApi.Interfaces.IOpenApiReferenceable ResolveReference(Microsoft.OpenApi.Models.OpenApiReference reference) { } } + public class OperationSearch : Microsoft.OpenApi.Services.OpenApiVisitorBase + { + public OperationSearch(System.Func predicate) { } + public System.Collections.Generic.IList SearchResults { get; } + public override void Visit(Microsoft.OpenApi.Models.OpenApiOperation operation) { } + public override void Visit(System.Collections.Generic.IList parameters) { } + } + public class SearchResult + { + public SearchResult() { } + public Microsoft.OpenApi.Services.CurrentKeys CurrentKeys { get; set; } + public Microsoft.OpenApi.Models.OpenApiOperation Operation { get; set; } + } } namespace Microsoft.OpenApi.Validations { From d13dcf41f987fba5585cfe8186bde9a47a689b20 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Tue, 26 Oct 2021 17:09:50 +0300 Subject: [PATCH 27/57] Remove unnecessary package dependency --- src/Microsoft.OpenApi/Microsoft.OpenApi.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj b/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj index 4d305cfdc..9c36ab07c 100644 --- a/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj +++ b/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj @@ -37,7 +37,6 @@ - From ad0bda07c819f112c4141d7122429db81aeaaf98 Mon Sep 17 00:00:00 2001 From: Peter Ritchie Date: Fri, 29 Oct 2021 13:59:56 -0700 Subject: [PATCH 28/57] Fixed spelling of "Path" (from "Past") --- .../Validations/OpenApiParameterValidationTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiParameterValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiParameterValidationTests.cs index 41a9a6ab0..a7abfd9d8 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiParameterValidationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiParameterValidationTests.cs @@ -218,7 +218,7 @@ public void PathParameterNotInThePathShouldReturnAnError() } [Fact] - public void PathParameterInThePastShouldBeOk() + public void PathParameterInThePathShouldBeOk() { // Arrange IEnumerable errors; From 00dd9c47202e66c3dc031cb5aab72a4c66d8e003 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Mon, 1 Nov 2021 10:49:02 +0300 Subject: [PATCH 29/57] Allow filtering for multiple operationIds --- src/Microsoft.OpenApi.Tool/OpenApiService.cs | 9 ++++----- src/Microsoft.OpenApi.Tool/Program.cs | 2 +- src/Microsoft.OpenApi/Services/OpenApiFilterService.cs | 2 +- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/Microsoft.OpenApi.Tool/OpenApiService.cs b/src/Microsoft.OpenApi.Tool/OpenApiService.cs index fe1a9d9b4..fca5999a1 100644 --- a/src/Microsoft.OpenApi.Tool/OpenApiService.cs +++ b/src/Microsoft.OpenApi.Tool/OpenApiService.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System; using System.IO; using System.Linq; using System.Net; @@ -21,7 +20,7 @@ public static void ProcessOpenApiDocument( FileInfo output, OpenApiSpecVersion version, OpenApiFormat format, - string filterbyOperationId, + string filterByOperationIds, bool inline, bool resolveExternal) { @@ -44,9 +43,9 @@ public static void ProcessOpenApiDocument( document = result.OpenApiDocument; // Check if filter options are provided, then execute - if (!string.IsNullOrEmpty(filterbyOperationId)) + if (!string.IsNullOrEmpty(filterByOperationIds)) { - var predicate = OpenApiFilterService.CreatePredicate(filterbyOperationId); + var predicate = OpenApiFilterService.CreatePredicate(filterByOperationIds); document = OpenApiFilterService.CreateFilteredDocument(document, predicate); } diff --git a/src/Microsoft.OpenApi.Tool/Program.cs b/src/Microsoft.OpenApi.Tool/Program.cs index 938986350..570f2ea1e 100644 --- a/src/Microsoft.OpenApi.Tool/Program.cs +++ b/src/Microsoft.OpenApi.Tool/Program.cs @@ -26,7 +26,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("--filterByOperationId", "Filters by OperationId provided", typeof(string)) + new Option("--filterByOperationIds", "Filters by OperationId provided", typeof(string)) }; transformCommand.Handler = CommandHandler.Create( OpenApiService.ProcessOpenApiDocument); diff --git a/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs b/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs index 37f7f5610..36a31fa26 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs @@ -59,7 +59,7 @@ public static OpenApiDocument CreateFilteredDocument(OpenApiDocument source, Fun { Info = new OpenApiInfo() { - Title = source.Info.Title, + Title = source.Info.Title + " - subset", Version = source.Info.Version }, From 3f3dae04bdff4da9b3801b2f1acd016ee1922b5d Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Mon, 1 Nov 2021 10:49:59 +0300 Subject: [PATCH 30/57] Add check for writing to an already existing file --- src/Microsoft.OpenApi.Tool/OpenApiService.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.OpenApi.Tool/OpenApiService.cs b/src/Microsoft.OpenApi.Tool/OpenApiService.cs index fca5999a1..34f51af64 100644 --- a/src/Microsoft.OpenApi.Tool/OpenApiService.cs +++ b/src/Microsoft.OpenApi.Tool/OpenApiService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; using System.Linq; using System.Net; @@ -63,6 +63,11 @@ public static void ProcessOpenApiDocument( throw new ArgumentException(string.Join(Environment.NewLine, context.Errors.Select(e => e.Message).ToArray())); } + if (output.Exists) + { + throw new IOException("The file you're writing to already exists.Please input a new output path."); + } + using var outputStream = output?.Create(); var textWriter = outputStream != null ? new StreamWriter(outputStream) : Console.Out; From 5a3da1a0b5c6ac2fed216f642078c3ab6c23bfbf Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Mon, 1 Nov 2021 12:00:00 +0300 Subject: [PATCH 31/57] 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 32/57] 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 33/57] 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 27ab7f7655bd74aa038534288d66e8bc8b22c801 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Tue, 2 Nov 2021 09:30:58 +0300 Subject: [PATCH 34/57] Copy over all Info properties --- src/Microsoft.OpenApi/Services/OpenApiFilterService.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs b/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs index 36a31fa26..1ef69ef18 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs @@ -59,7 +59,11 @@ public static OpenApiDocument CreateFilteredDocument(OpenApiDocument source, Fun { Info = new OpenApiInfo() { - Title = source.Info.Title + " - subset", + Title = source.Info.Title + " - Subset", + Description = source.Info.Description, + TermsOfService = source.Info.TermsOfService, + Contact = source.Info.Contact, + License = source.Info.License, Version = source.Info.Version }, From c3c0abcd97d68aa4cae19776c4a02dab9377b413 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Tue, 2 Nov 2021 21:57:25 +0300 Subject: [PATCH 35/57] Add license header --- src/Microsoft.OpenApi.Tool/Program.cs | 5 ++++- src/Microsoft.OpenApi/Services/CopyReferences.cs | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.OpenApi.Tool/Program.cs b/src/Microsoft.OpenApi.Tool/Program.cs index 570f2ea1e..21be3406b 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; diff --git a/src/Microsoft.OpenApi/Services/CopyReferences.cs b/src/Microsoft.OpenApi/Services/CopyReferences.cs index 9fc25b210..e1db1360d 100644 --- a/src/Microsoft.OpenApi/Services/CopyReferences.cs +++ b/src/Microsoft.OpenApi/Services/CopyReferences.cs @@ -1,4 +1,7 @@ -using System.Collections.Generic; +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using System.Collections.Generic; using Microsoft.OpenApi.Interfaces; using Microsoft.OpenApi.Models; From dbd8d925f6ed24a3e20fb5e7008f2ff46b7e352b Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Tue, 2 Nov 2021 21:58:56 +0300 Subject: [PATCH 36/57] Clean up and add XML documentations for public methods --- src/Microsoft.OpenApi.Tool/OpenApiService.cs | 20 ++++++------ .../Services/CopyReferences.cs | 32 ++++++++++++------- .../Services/OpenApiFilterService.cs | 6 +--- 3 files changed, 32 insertions(+), 26 deletions(-) diff --git a/src/Microsoft.OpenApi.Tool/OpenApiService.cs b/src/Microsoft.OpenApi.Tool/OpenApiService.cs index 34f51af64..d9e9fa930 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; @@ -24,9 +27,13 @@ public static void ProcessOpenApiDocument( bool inline, bool resolveExternal) { - if (input == null) + if (string.IsNullOrEmpty(input)) { - throw new ArgumentNullException("input"); + throw new ArgumentNullException(nameof(input)); + } + if (output.Exists) + { + throw new IOException("The file you're writing to already exists. Please input a new output path."); } var stream = GetStream(input); @@ -51,7 +58,7 @@ public static void ProcessOpenApiDocument( var context = result.OpenApiDiagnostic; - if (context.Errors.Count != 0) + if (context.Errors.Count > 0) { var errorReport = new StringBuilder(); @@ -63,11 +70,6 @@ public static void ProcessOpenApiDocument( throw new ArgumentException(string.Join(Environment.NewLine, context.Errors.Select(e => e.Message).ToArray())); } - if (output.Exists) - { - throw new IOException("The file you're writing to already exists.Please input a new output path."); - } - using var outputStream = output?.Create(); var textWriter = outputStream != null ? new StreamWriter(outputStream) : Console.Out; diff --git a/src/Microsoft.OpenApi/Services/CopyReferences.cs b/src/Microsoft.OpenApi/Services/CopyReferences.cs index e1db1360d..24dcfee25 100644 --- a/src/Microsoft.OpenApi/Services/CopyReferences.cs +++ b/src/Microsoft.OpenApi/Services/CopyReferences.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System.Collections.Generic; @@ -9,14 +9,18 @@ namespace Microsoft.OpenApi.Services { internal class CopyReferences : OpenApiVisitorBase { - private readonly OpenApiDocument target; - public OpenApiComponents Components = new OpenApiComponents(); + private readonly OpenApiDocument _target; + public OpenApiComponents Components = new(); public CopyReferences(OpenApiDocument target) { - this.target = target; + _target = target; } + /// + /// Visits IOpenApiReferenceable instances that are references and not in components. + /// + /// An IOpenApiReferenceable object. public override void Visit(IOpenApiReferenceable referenceable) { switch (referenceable) @@ -54,6 +58,10 @@ public override void Visit(IOpenApiReferenceable referenceable) base.Visit(referenceable); } + /// + /// Visits + /// + /// The OpenApiSchema to be visited. public override void Visit(OpenApiSchema schema) { // This is needed to handle schemas used in Responses in components @@ -71,33 +79,33 @@ public override void Visit(OpenApiSchema schema) private void EnsureComponentsExists() { - if (target.Components == null) + if (_target.Components == null) { - target.Components = new OpenApiComponents(); + _target.Components = new OpenApiComponents(); } } private void EnsureSchemasExists() { - if (target.Components.Schemas == null) + if (_target.Components.Schemas == null) { - target.Components.Schemas = new Dictionary(); + _target.Components.Schemas = new Dictionary(); } } private void EnsureParametersExists() { - if (target.Components.Parameters == null) + if (_target.Components.Parameters == null) { - target.Components.Parameters = new Dictionary(); + _target.Components.Parameters = new Dictionary(); } } private void EnsureResponsesExists() { - if (target.Components.Responses == null) + if (_target.Components.Responses == null) { - target.Components.Responses = new Dictionary(); + _target.Components.Responses = new Dictionary(); } } } diff --git a/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs b/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs index 1ef69ef18..ce347c234 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System; @@ -20,8 +20,6 @@ public static class OpenApiFilterService /// A predicate. public static Func CreatePredicate(string operationIds) { - string predicateSource = null; - Func predicate; if (operationIds != null) { @@ -34,8 +32,6 @@ public static Func CreatePredicate(string operationIds) var operationIdsArray = operationIds.Split(','); predicate = (o) => operationIdsArray.Contains(o.OperationId); } - - predicateSource = $"operationIds: {operationIds}"; } else From a689c3631c212d2eccf22d6807ba5fbb413ad5cd Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Tue, 2 Nov 2021 21:59:20 +0300 Subject: [PATCH 37/57] Copy over extensions object to new document --- src/Microsoft.OpenApi/Services/OpenApiFilterService.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs b/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs index ce347c234..4fd09314e 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System; @@ -60,7 +60,8 @@ public static OpenApiDocument CreateFilteredDocument(OpenApiDocument source, Fun TermsOfService = source.Info.TermsOfService, Contact = source.Info.Contact, License = source.Info.License, - Version = source.Info.Version + Version = source.Info.Version, + Extensions = source.Info.Extensions }, Components = new OpenApiComponents() From 8fbb809d4390d97ec32343ebf79abfcaea8108d9 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Tue, 2 Nov 2021 22:07:18 +0300 Subject: [PATCH 38/57] Update ci-build.yml for Azure Pipelines --- .azure-pipelines/ci-build.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.azure-pipelines/ci-build.yml b/.azure-pipelines/ci-build.yml index 5649e50c5..3edcedeb8 100644 --- a/.azure-pipelines/ci-build.yml +++ b/.azure-pipelines/ci-build.yml @@ -94,6 +94,13 @@ steps: configuration: Release msbuildArguments: '/t:pack /p:PackageOutputPath=$(Build.ArtifactStagingDirectory) /p:IncludeSymbols=true /p:SymbolPackageFormat=snupkg' +- task: MSBuild@1 + displayName: 'Pack OpenAPI.Tool' + inputs: + solution: src/Microsoft.OpenApi.Tool/Microsoft.OpenApi.Tool.csproj + configuration: Release + msbuildArguments: '/t:pack /p:PackageOutputPath=$(Build.ArtifactStagingDirectory) /p:IncludeSymbols=true /p:SymbolPackageFormat=snupkg' + - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 displayName: 'ESRP CodeSigning Nuget Packages' inputs: From deaa2fed7de2b21464307018ba5af574c5fd7128 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Wed, 3 Nov 2021 10:01:31 +0300 Subject: [PATCH 39/57] Move declaration closer to first reference point --- src/Microsoft.OpenApi.Tool/OpenApiService.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Microsoft.OpenApi.Tool/OpenApiService.cs b/src/Microsoft.OpenApi.Tool/OpenApiService.cs index d9e9fa930..74f9455f0 100644 --- a/src/Microsoft.OpenApi.Tool/OpenApiService.cs +++ b/src/Microsoft.OpenApi.Tool/OpenApiService.cs @@ -37,9 +37,6 @@ public static void ProcessOpenApiDocument( } var stream = GetStream(input); - - OpenApiDocument document; - var result = new OpenApiStreamReader(new OpenApiReaderSettings { ReferenceResolution = resolveExternal ? ReferenceResolutionSetting.ResolveAllReferences : ReferenceResolutionSetting.ResolveLocalReferences, @@ -47,6 +44,7 @@ public static void ProcessOpenApiDocument( } ).ReadAsync(stream).GetAwaiter().GetResult(); + OpenApiDocument document; document = result.OpenApiDocument; // Check if filter options are provided, then execute From 015b3cd27b7454e7b905c5e804365ec5fa013b5c Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Thu, 4 Nov 2021 13:21:57 +0300 Subject: [PATCH 40/57] Code cleanup --- src/Microsoft.OpenApi.Tool/OpenApiService.cs | 5 ++++- src/Microsoft.OpenApi/Services/OpenApiFilterService.cs | 2 +- src/Microsoft.OpenApi/Services/OperationSearch.cs | 10 ++++++++++ src/Microsoft.OpenApi/Services/SearchResult.cs | 10 ++++++++++ .../Workspaces/OpenApiWorkspaceTests.cs | 9 +++++---- 5 files changed, 30 insertions(+), 6 deletions(-) diff --git a/src/Microsoft.OpenApi.Tool/OpenApiService.cs b/src/Microsoft.OpenApi.Tool/OpenApiService.cs index 74f9455f0..87f02dcce 100644 --- a/src/Microsoft.OpenApi.Tool/OpenApiService.cs +++ b/src/Microsoft.OpenApi.Tool/OpenApiService.cs @@ -31,6 +31,10 @@ public static void ProcessOpenApiDocument( { throw new ArgumentNullException(nameof(input)); } + if(output == null) + { + throw new ArgumentException(nameof(output)); + } if (output.Exists) { throw new IOException("The file you're writing to already exists. Please input a new output path."); @@ -123,7 +127,6 @@ internal static void ValidateOpenApiDocument(string input) document = new OpenApiStreamReader(new OpenApiReaderSettings { - //ReferenceResolution = resolveExternal == true ? ReferenceResolutionSetting.ResolveAllReferences : ReferenceResolutionSetting.ResolveLocalReferences, RuleSet = ValidationRuleSet.GetDefaultRuleSet() } ).Read(stream, out var context); diff --git a/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs b/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs index 4fd09314e..5b5e6f59f 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs @@ -75,7 +75,7 @@ public static OpenApiDocument CreateFilteredDocument(OpenApiDocument source, Fun foreach (var result in results) { OpenApiPathItem pathItem; - string pathKey = result.CurrentKeys.Path; + var pathKey = result.CurrentKeys.Path; if (subset.Paths == null) { diff --git a/src/Microsoft.OpenApi/Services/OperationSearch.cs b/src/Microsoft.OpenApi/Services/OperationSearch.cs index 01b1b5f56..35d36b38f 100644 --- a/src/Microsoft.OpenApi/Services/OperationSearch.cs +++ b/src/Microsoft.OpenApi/Services/OperationSearch.cs @@ -8,13 +8,23 @@ namespace Microsoft.OpenApi.Services { + /// + /// Visits OpenApi operations and parameters. + /// public class OperationSearch : OpenApiVisitorBase { private readonly Func _predicate; private readonly List _searchResults = new(); + /// + /// A list of operations from the operation search. + /// public IList SearchResults => _searchResults; + /// + /// The OperationSearch constructor. + /// + /// A predicate function. public OperationSearch(Func predicate) { _predicate = predicate ?? throw new ArgumentNullException(nameof(predicate)); diff --git a/src/Microsoft.OpenApi/Services/SearchResult.cs b/src/Microsoft.OpenApi/Services/SearchResult.cs index 2b7d9f94a..381a11f95 100644 --- a/src/Microsoft.OpenApi/Services/SearchResult.cs +++ b/src/Microsoft.OpenApi/Services/SearchResult.cs @@ -5,9 +5,19 @@ namespace Microsoft.OpenApi.Services { + /// + /// Defines a search result model for visited operations. + /// public class SearchResult { + /// + /// An object containing contextual information based on where the walker is currently referencing in an OpenApiDocument. + /// public CurrentKeys CurrentKeys { get; set; } + + /// + /// An Operation object. + /// public OpenApiOperation Operation { get; set; } } } diff --git a/test/Microsoft.OpenApi.Tests/Workspaces/OpenApiWorkspaceTests.cs b/test/Microsoft.OpenApi.Tests/Workspaces/OpenApiWorkspaceTests.cs index b82327a5d..2102328eb 100644 --- a/test/Microsoft.OpenApi.Tests/Workspaces/OpenApiWorkspaceTests.cs +++ b/test/Microsoft.OpenApi.Tests/Workspaces/OpenApiWorkspaceTests.cs @@ -1,5 +1,5 @@ // Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. +// Licensed under the MIT license. using System; using System.Collections.Generic; @@ -12,7 +12,7 @@ namespace Microsoft.OpenApi.Tests { - + public class OpenApiWorkspaceTests { [Fact] @@ -61,7 +61,7 @@ public void OpenApiWorkspacesAllowDocumentsToReferenceEachOther() } } } - } + } } }); workspace.AddDocument("common", new OpenApiDocument() { @@ -111,7 +111,7 @@ public void OpenApiWorkspacesAllowDocumentsToReferenceEachOther_short() re.CreateContent("application/json", co => co.Schema = new OpenApiSchema() { - Reference = new OpenApiReference() // Reference + Reference = new OpenApiReference() // Reference { Id = "test", Type = ReferenceType.Schema, @@ -150,6 +150,7 @@ public void OpenApiWorkspacesShouldNormalizeDocumentLocations() // Enable Workspace to load from any reader, not just streams. // Test fragments + [Fact] public void OpenApiWorkspacesShouldLoadDocumentFragments() { Assert.True(false); From d259cc1c40ccdaf91f3608851200d52dca6707a0 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Fri, 5 Nov 2021 11:15:40 +0300 Subject: [PATCH 41/57] Remove [Fact] attribute --- test/Microsoft.OpenApi.Tests/Workspaces/OpenApiWorkspaceTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/test/Microsoft.OpenApi.Tests/Workspaces/OpenApiWorkspaceTests.cs b/test/Microsoft.OpenApi.Tests/Workspaces/OpenApiWorkspaceTests.cs index 2102328eb..dd6e2554b 100644 --- a/test/Microsoft.OpenApi.Tests/Workspaces/OpenApiWorkspaceTests.cs +++ b/test/Microsoft.OpenApi.Tests/Workspaces/OpenApiWorkspaceTests.cs @@ -150,7 +150,6 @@ public void OpenApiWorkspacesShouldNormalizeDocumentLocations() // Enable Workspace to load from any reader, not just streams. // Test fragments - [Fact] public void OpenApiWorkspacesShouldLoadDocumentFragments() { Assert.True(false); From 526162e3a1bc4a45d7142b2d380ba47ab8e81c8b Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Fri, 5 Nov 2021 12:21:01 +0300 Subject: [PATCH 42/57] 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 1321c1e2999b99db476f9cf6b073e6e65228dc41 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Mon, 8 Nov 2021 12:49:28 +0300 Subject: [PATCH 43/57] Rename project and update namespaces --- .../Microsoft.Hidi.csproj} | 2 +- .../OpenApiService.cs | 3 ++- src/{Microsoft.OpenApi.Tool => Microsoft.Hidi}/Program.cs | 3 ++- .../StatsVisitor.cs | 5 +---- 4 files changed, 6 insertions(+), 7 deletions(-) rename src/{Microsoft.OpenApi.Tool/Microsoft.OpenApi.Tool.csproj => Microsoft.Hidi/Microsoft.Hidi.csproj} (93%) rename src/{Microsoft.OpenApi.Tool => Microsoft.Hidi}/OpenApiService.cs (98%) rename src/{Microsoft.OpenApi.Tool => Microsoft.Hidi}/Program.cs (97%) rename src/{Microsoft.OpenApi.Tool => Microsoft.Hidi}/StatsVisitor.cs (95%) diff --git a/src/Microsoft.OpenApi.Tool/Microsoft.OpenApi.Tool.csproj b/src/Microsoft.Hidi/Microsoft.Hidi.csproj similarity index 93% rename from src/Microsoft.OpenApi.Tool/Microsoft.OpenApi.Tool.csproj rename to src/Microsoft.Hidi/Microsoft.Hidi.csproj index 40e46f1a4..27fc4b995 100644 --- a/src/Microsoft.OpenApi.Tool/Microsoft.OpenApi.Tool.csproj +++ b/src/Microsoft.Hidi/Microsoft.Hidi.csproj @@ -4,7 +4,7 @@ Exe netcoreapp3.1 true - openapi-parser + hidi ./../../artifacts 1.3.0-preview diff --git a/src/Microsoft.OpenApi.Tool/OpenApiService.cs b/src/Microsoft.Hidi/OpenApiService.cs similarity index 98% rename from src/Microsoft.OpenApi.Tool/OpenApiService.cs rename to src/Microsoft.Hidi/OpenApiService.cs index 87f02dcce..556eff071 100644 --- a/src/Microsoft.OpenApi.Tool/OpenApiService.cs +++ b/src/Microsoft.Hidi/OpenApiService.cs @@ -7,6 +7,7 @@ using System.Net; using System.Net.Http; using System.Text; +using Microsoft.OpenApi; using Microsoft.OpenApi.Extensions; using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Readers; @@ -14,7 +15,7 @@ using Microsoft.OpenApi.Validations; using Microsoft.OpenApi.Writers; -namespace Microsoft.OpenApi.Tool +namespace Microsoft.Hidi { static class OpenApiService { diff --git a/src/Microsoft.OpenApi.Tool/Program.cs b/src/Microsoft.Hidi/Program.cs similarity index 97% rename from src/Microsoft.OpenApi.Tool/Program.cs rename to src/Microsoft.Hidi/Program.cs index 21be3406b..033e3625b 100644 --- a/src/Microsoft.OpenApi.Tool/Program.cs +++ b/src/Microsoft.Hidi/Program.cs @@ -5,8 +5,9 @@ using System.CommandLine.Invocation; using System.IO; using System.Threading.Tasks; +using Microsoft.OpenApi; -namespace Microsoft.OpenApi.Tool +namespace Microsoft.Hidi { static class Program { diff --git a/src/Microsoft.OpenApi.Tool/StatsVisitor.cs b/src/Microsoft.Hidi/StatsVisitor.cs similarity index 95% rename from src/Microsoft.OpenApi.Tool/StatsVisitor.cs rename to src/Microsoft.Hidi/StatsVisitor.cs index 3c633d860..7617edb97 100644 --- a/src/Microsoft.OpenApi.Tool/StatsVisitor.cs +++ b/src/Microsoft.Hidi/StatsVisitor.cs @@ -3,13 +3,10 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Services; -namespace Microsoft.OpenApi.Tool +namespace Microsoft.Hidi { internal class StatsVisitor : OpenApiVisitorBase { From fb5512ce6cfba4e0d1a0891d607ffe8958051c0a Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Mon, 8 Nov 2021 12:49:46 +0300 Subject: [PATCH 44/57] Update build script and solution file --- Microsoft.OpenApi.sln | 2 +- build.cmd | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Microsoft.OpenApi.sln b/Microsoft.OpenApi.sln index e64ff3a24..71201a3cd 100644 --- a/Microsoft.OpenApi.sln +++ b/Microsoft.OpenApi.sln @@ -26,7 +26,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{6357D7FD-2 EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.OpenApi.SmokeTests", "test\Microsoft.OpenApi.SmokeTests\Microsoft.OpenApi.SmokeTests.csproj", "{AD79B61D-88CF-497C-9ED5-41AE3867C5AC}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.OpenApi.Tool", "src\Microsoft.OpenApi.Tool\Microsoft.OpenApi.Tool.csproj", "{254841B5-7DAC-4D1D-A9C5-44FE5CE467BE}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Hidi", "src\Microsoft.Hidi\Microsoft.Hidi.csproj", "{254841B5-7DAC-4D1D-A9C5-44FE5CE467BE}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/build.cmd b/build.cmd index b3612c9ed..48016eed2 100644 --- a/build.cmd +++ b/build.cmd @@ -1,21 +1,21 @@ @echo off -Echo Building Microsoft.OpenApi +Echo Building Microsoft.OpenApi -SET PROJ=%~dp0src\Microsoft.OpenApi\Microsoft.OpenApi.csproj +SET PROJ=%~dp0src\Microsoft.OpenApi\Microsoft.OpenApi.csproj dotnet msbuild %PROJ% /t:restore /p:Configuration=Release dotnet msbuild %PROJ% /t:build /p:Configuration=Release dotnet msbuild %PROJ% /t:pack /p:Configuration=Release;PackageOutputPath=%~dp0artifacts Echo Building Microsoft.OpenApi.Readers -SET PROJ=%~dp0src\Microsoft.OpenApi.Readers\Microsoft.OpenApi.Readers.csproj +SET PROJ=%~dp0src\Microsoft.OpenApi.Readers\Microsoft.OpenApi.Readers.csproj dotnet msbuild %PROJ% /t:restore /p:Configuration=Release dotnet msbuild %PROJ% /t:build /p:Configuration=Release dotnet msbuild %PROJ% /t:pack /p:Configuration=Release;PackageOutputPath=%~dp0artifacts -Echo Building Microsoft.OpenApi.Tool +Echo Building Microsoft.Hidi -SET PROJ=%~dp0src\Microsoft.OpenApi.Tool\Microsoft.OpenApi.Tool.csproj +SET PROJ=%~dp0src\Microsoft.Hidi\Microsoft.Hidi.csproj dotnet msbuild %PROJ% /t:restore /p:Configuration=Release dotnet msbuild %PROJ% /t:build /p:Configuration=Release dotnet msbuild %PROJ% /t:pack /p:Configuration=Release;PackageOutputPath=%~dp0artifacts From e01b885edaabdfaabb632802829765e670f3e7e1 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Tue, 9 Nov 2021 08:51:19 +0300 Subject: [PATCH 45/57] Rename the OpenApi.Tool to Hidi --- .azure-pipelines/ci-build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.azure-pipelines/ci-build.yml b/.azure-pipelines/ci-build.yml index 3edcedeb8..31364a500 100644 --- a/.azure-pipelines/ci-build.yml +++ b/.azure-pipelines/ci-build.yml @@ -95,9 +95,9 @@ steps: msbuildArguments: '/t:pack /p:PackageOutputPath=$(Build.ArtifactStagingDirectory) /p:IncludeSymbols=true /p:SymbolPackageFormat=snupkg' - task: MSBuild@1 - displayName: 'Pack OpenAPI.Tool' + displayName: 'Pack Hidi' inputs: - solution: src/Microsoft.OpenApi.Tool/Microsoft.OpenApi.Tool.csproj + solution: src/Microsoft.Hidi/Microsoft.Hidi.csproj configuration: Release msbuildArguments: '/t:pack /p:PackageOutputPath=$(Build.ArtifactStagingDirectory) /p:IncludeSymbols=true /p:SymbolPackageFormat=snupkg' From c90020813dba37870c17fb5eaa6e29f3a850fd93 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Tue, 9 Nov 2021 12:35:30 +0300 Subject: [PATCH 46/57] 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 d1a13a56e10c48a3f95c389ff655a81057eeaacd Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Tue, 9 Nov 2021 17:36:35 +0300 Subject: [PATCH 47/57] Update package name --- .azure-pipelines/ci-build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.azure-pipelines/ci-build.yml b/.azure-pipelines/ci-build.yml index 31364a500..4e0f23758 100644 --- a/.azure-pipelines/ci-build.yml +++ b/.azure-pipelines/ci-build.yml @@ -95,9 +95,9 @@ steps: msbuildArguments: '/t:pack /p:PackageOutputPath=$(Build.ArtifactStagingDirectory) /p:IncludeSymbols=true /p:SymbolPackageFormat=snupkg' - task: MSBuild@1 - displayName: 'Pack Hidi' + displayName: 'Pack OpenApi Hidi' inputs: - solution: src/Microsoft.Hidi/Microsoft.Hidi.csproj + solution: src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj configuration: Release msbuildArguments: '/t:pack /p:PackageOutputPath=$(Build.ArtifactStagingDirectory) /p:IncludeSymbols=true /p:SymbolPackageFormat=snupkg' From 6acf634798c74f4fe9f462b95254e85571366dbb Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Tue, 9 Nov 2021 18:43:02 +0300 Subject: [PATCH 48/57] 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); } } } From 403a40ff85eeb780deccef692e48572adc5d9ba0 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Tue, 9 Nov 2021 21:57:22 +0300 Subject: [PATCH 49/57] Rename tool to OpenApi.Hidi --- Microsoft.OpenApi.sln | 2 +- build.cmd | 4 ++-- install-tool.ps1 | 8 ++++---- .../Microsoft.OpenApi.Hidi.csproj} | 0 .../OpenApiService.cs | 3 +-- src/{Microsoft.Hidi => Microsoft.OpenApi.Hidi}/Program.cs | 3 +-- .../StatsVisitor.cs | 2 +- 7 files changed, 10 insertions(+), 12 deletions(-) rename src/{Microsoft.Hidi/Microsoft.Hidi.csproj => Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj} (100%) rename src/{Microsoft.Hidi => Microsoft.OpenApi.Hidi}/OpenApiService.cs (98%) rename src/{Microsoft.Hidi => Microsoft.OpenApi.Hidi}/Program.cs (97%) rename src/{Microsoft.Hidi => Microsoft.OpenApi.Hidi}/StatsVisitor.cs (98%) diff --git a/Microsoft.OpenApi.sln b/Microsoft.OpenApi.sln index 71201a3cd..b24440dc5 100644 --- a/Microsoft.OpenApi.sln +++ b/Microsoft.OpenApi.sln @@ -26,7 +26,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{6357D7FD-2 EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.OpenApi.SmokeTests", "test\Microsoft.OpenApi.SmokeTests\Microsoft.OpenApi.SmokeTests.csproj", "{AD79B61D-88CF-497C-9ED5-41AE3867C5AC}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Hidi", "src\Microsoft.Hidi\Microsoft.Hidi.csproj", "{254841B5-7DAC-4D1D-A9C5-44FE5CE467BE}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.OpenApi.Hidi", "src\Microsoft.Hidi\Microsoft.OpenApi.Hidi.csproj", "{254841B5-7DAC-4D1D-A9C5-44FE5CE467BE}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/build.cmd b/build.cmd index 48016eed2..43cc95956 100644 --- a/build.cmd +++ b/build.cmd @@ -13,9 +13,9 @@ dotnet msbuild %PROJ% /t:restore /p:Configuration=Release dotnet msbuild %PROJ% /t:build /p:Configuration=Release dotnet msbuild %PROJ% /t:pack /p:Configuration=Release;PackageOutputPath=%~dp0artifacts -Echo Building Microsoft.Hidi +Echo Building Microsoft.OpenApi.Hidi -SET PROJ=%~dp0src\Microsoft.Hidi\Microsoft.Hidi.csproj +SET PROJ=%~dp0src\Microsoft.OpenApi.Hidi\Microsoft.OpenApi.Hidi.csproj dotnet msbuild %PROJ% /t:restore /p:Configuration=Release dotnet msbuild %PROJ% /t:build /p:Configuration=Release dotnet msbuild %PROJ% /t:pack /p:Configuration=Release;PackageOutputPath=%~dp0artifacts diff --git a/install-tool.ps1 b/install-tool.ps1 index 0e6521110..d268e826e 100644 --- a/install-tool.ps1 +++ b/install-tool.ps1 @@ -1,7 +1,7 @@ -$latest = Get-ChildItem .\artifacts\ Microsoft.OpenApi.Tool* | select-object -Last 1 +$latest = Get-ChildItem .\artifacts\ Microsoft.OpenApi.Hidi* | select-object -Last 1 $version = $latest.Name.Split(".")[3..5] | join-string -Separator "." -if (Test-Path -Path ./artifacts/openapi-parser.exe) { - dotnet tool uninstall --tool-path artifacts Microsoft.OpenApi.Tool +if (Test-Path -Path ./artifacts/hidi.exe) { + dotnet tool uninstall --tool-path artifacts Microsoft.OpenApi.Hidi } -dotnet tool install --tool-path artifacts --add-source .\artifacts\ --version $version Microsoft.OpenApi.Tool \ No newline at end of file +dotnet tool install --tool-path artifacts --add-source .\artifacts\ --version $version Microsoft.OpenApi.Hidi \ No newline at end of file diff --git a/src/Microsoft.Hidi/Microsoft.Hidi.csproj b/src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj similarity index 100% rename from src/Microsoft.Hidi/Microsoft.Hidi.csproj rename to src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj diff --git a/src/Microsoft.Hidi/OpenApiService.cs b/src/Microsoft.OpenApi.Hidi/OpenApiService.cs similarity index 98% rename from src/Microsoft.Hidi/OpenApiService.cs rename to src/Microsoft.OpenApi.Hidi/OpenApiService.cs index 556eff071..5a415c115 100644 --- a/src/Microsoft.Hidi/OpenApiService.cs +++ b/src/Microsoft.OpenApi.Hidi/OpenApiService.cs @@ -7,7 +7,6 @@ using System.Net; using System.Net.Http; using System.Text; -using Microsoft.OpenApi; using Microsoft.OpenApi.Extensions; using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Readers; @@ -15,7 +14,7 @@ using Microsoft.OpenApi.Validations; using Microsoft.OpenApi.Writers; -namespace Microsoft.Hidi +namespace Microsoft.OpenApi.Hidi { static class OpenApiService { diff --git a/src/Microsoft.Hidi/Program.cs b/src/Microsoft.OpenApi.Hidi/Program.cs similarity index 97% rename from src/Microsoft.Hidi/Program.cs rename to src/Microsoft.OpenApi.Hidi/Program.cs index 033e3625b..31c5b3e69 100644 --- a/src/Microsoft.Hidi/Program.cs +++ b/src/Microsoft.OpenApi.Hidi/Program.cs @@ -5,9 +5,8 @@ using System.CommandLine.Invocation; using System.IO; using System.Threading.Tasks; -using Microsoft.OpenApi; -namespace Microsoft.Hidi +namespace Microsoft.OpenApi.Hidi { static class Program { diff --git a/src/Microsoft.Hidi/StatsVisitor.cs b/src/Microsoft.OpenApi.Hidi/StatsVisitor.cs similarity index 98% rename from src/Microsoft.Hidi/StatsVisitor.cs rename to src/Microsoft.OpenApi.Hidi/StatsVisitor.cs index 7617edb97..b05b0de7c 100644 --- a/src/Microsoft.Hidi/StatsVisitor.cs +++ b/src/Microsoft.OpenApi.Hidi/StatsVisitor.cs @@ -6,7 +6,7 @@ using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Services; -namespace Microsoft.Hidi +namespace Microsoft.OpenApi.Hidi { internal class StatsVisitor : OpenApiVisitorBase { From 97f4ac3c06d99264d67714ea8507568d2b610e36 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Wed, 10 Nov 2021 13:09:22 +0300 Subject: [PATCH 50/57] Remove whitespace --- install-tool.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install-tool.ps1 b/install-tool.ps1 index d268e826e..0b4615c67 100644 --- a/install-tool.ps1 +++ b/install-tool.ps1 @@ -1,4 +1,4 @@ -$latest = Get-ChildItem .\artifacts\ Microsoft.OpenApi.Hidi* | select-object -Last 1 +$latest = Get-ChildItem .\artifacts\Microsoft.OpenApi.Hidi* | select-object -Last 1 $version = $latest.Name.Split(".")[3..5] | join-string -Separator "." if (Test-Path -Path ./artifacts/hidi.exe) { From ede11f10fd7abcc74be2fe66bddb05604da5fd30 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Wed, 10 Nov 2021 13:21:05 +0300 Subject: [PATCH 51/57] Update solution file with the correct directory path --- Microsoft.OpenApi.sln | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Microsoft.OpenApi.sln b/Microsoft.OpenApi.sln index b24440dc5..dc489bff8 100644 --- a/Microsoft.OpenApi.sln +++ b/Microsoft.OpenApi.sln @@ -26,7 +26,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{6357D7FD-2 EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.OpenApi.SmokeTests", "test\Microsoft.OpenApi.SmokeTests\Microsoft.OpenApi.SmokeTests.csproj", "{AD79B61D-88CF-497C-9ED5-41AE3867C5AC}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.OpenApi.Hidi", "src\Microsoft.Hidi\Microsoft.OpenApi.Hidi.csproj", "{254841B5-7DAC-4D1D-A9C5-44FE5CE467BE}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.OpenApi.Hidi", "src\Microsoft.OpenApi.Hidi\Microsoft.OpenApi.Hidi.csproj", "{254841B5-7DAC-4D1D-A9C5-44FE5CE467BE}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution From 431a4e9ff0095db55a35e2d6726c374789f8c42a Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Fri, 12 Nov 2021 11:29:42 +0300 Subject: [PATCH 52/57] Add blank line --- .azure-pipelines/ci-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.azure-pipelines/ci-build.yml b/.azure-pipelines/ci-build.yml index 4e0f23758..b6944af2f 100644 --- a/.azure-pipelines/ci-build.yml +++ b/.azure-pipelines/ci-build.yml @@ -30,7 +30,7 @@ steps: inputs: testAssemblyVer2: | **\*.Tests.dll - + vsTestVersion: 16.0 codeCoverageEnabled: true From 561b07ac7810b6e7b4d5751283de399950783209 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Fri, 12 Nov 2021 11:34:11 +0300 Subject: [PATCH 53/57] Update .sln --- Microsoft.OpenApi.sln | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Microsoft.OpenApi.sln b/Microsoft.OpenApi.sln index dc489bff8..e64ff3a24 100644 --- a/Microsoft.OpenApi.sln +++ b/Microsoft.OpenApi.sln @@ -26,7 +26,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{6357D7FD-2 EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.OpenApi.SmokeTests", "test\Microsoft.OpenApi.SmokeTests\Microsoft.OpenApi.SmokeTests.csproj", "{AD79B61D-88CF-497C-9ED5-41AE3867C5AC}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.OpenApi.Hidi", "src\Microsoft.OpenApi.Hidi\Microsoft.OpenApi.Hidi.csproj", "{254841B5-7DAC-4D1D-A9C5-44FE5CE467BE}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.OpenApi.Tool", "src\Microsoft.OpenApi.Tool\Microsoft.OpenApi.Tool.csproj", "{254841B5-7DAC-4D1D-A9C5-44FE5CE467BE}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution From 7b678a8a2c20d182f20e97735244b8b2c59b75f3 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Fri, 12 Nov 2021 11:47:44 +0300 Subject: [PATCH 54/57] Revert --- Microsoft.OpenApi.sln | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Microsoft.OpenApi.sln b/Microsoft.OpenApi.sln index e64ff3a24..dc489bff8 100644 --- a/Microsoft.OpenApi.sln +++ b/Microsoft.OpenApi.sln @@ -26,7 +26,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{6357D7FD-2 EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.OpenApi.SmokeTests", "test\Microsoft.OpenApi.SmokeTests\Microsoft.OpenApi.SmokeTests.csproj", "{AD79B61D-88CF-497C-9ED5-41AE3867C5AC}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.OpenApi.Tool", "src\Microsoft.OpenApi.Tool\Microsoft.OpenApi.Tool.csproj", "{254841B5-7DAC-4D1D-A9C5-44FE5CE467BE}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.OpenApi.Hidi", "src\Microsoft.OpenApi.Hidi\Microsoft.OpenApi.Hidi.csproj", "{254841B5-7DAC-4D1D-A9C5-44FE5CE467BE}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution From 733ca6b349d5556e352a4f738677145fc6f53c37 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Fri, 12 Nov 2021 13:01:26 +0300 Subject: [PATCH 55/57] Update dll path in launch.json --- .vscode/launch.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 0d20a9b46..c26bf0c9f 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -10,9 +10,9 @@ "request": "launch", "preLaunchTask": "build", // If you have changed target frameworks, make sure to update the program path. - "program": "${workspaceFolder}/src/Microsoft.OpenApi.Tool/bin/Debug/netcoreapp3.1/Microsoft.OpenApi.Tool.dll", + "program": "${workspaceFolder}/src/Microsoft.OpenApi.Hidi/bin/Debug/netcoreapp3.1/Microsoft.OpenApi.Hidi.dll", "args": [], - "cwd": "${workspaceFolder}/src/Microsoft.OpenApi.Tool", + "cwd": "${workspaceFolder}/src/Microsoft.OpenApi.Hidi", // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console "console": "internalConsole", "stopAtEntry": false From 520a55e4d2bb7531dd4d493b534302c8b296e809 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Fri, 12 Nov 2021 13:16:28 +0300 Subject: [PATCH 56/57] Update ci-cd.yml and codeql.yml files --- .github/workflows/ci-cd.yml | 2 +- .github/workflows/codeql-analysis.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 4ab9ed7c3..7afeeebed 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -62,7 +62,7 @@ jobs: $projectsArray = @( '.\src\Microsoft.OpenApi\Microsoft.OpenApi.csproj', '.\src\Microsoft.OpenApi.Readers\Microsoft.OpenApi.Readers.csproj', - '.\src\Microsoft.OpenApi.Tool\Microsoft.OpenApi.Tool.csproj' + '.\src\Microsoft.OpenApi.Hidi\Microsoft.OpenApi.Hidi.csproj' ) $gitNewVersion = if ("${{ steps.tag_generator.outputs.new_version }}") {"${{ steps.tag_generator.outputs.new_version }}"} else {$null} $projectCurrentVersion = ([xml](Get-Content .\src\Microsoft.OpenApi\Microsoft.OpenApi.csproj)).Project.PropertyGroup.Version diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 95c813772..999e48f53 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -33,7 +33,7 @@ jobs: $projectsArray = @( '.\src\Microsoft.OpenApi\Microsoft.OpenApi.csproj', '.\src\Microsoft.OpenApi.Readers\Microsoft.OpenApi.Readers.csproj', - '.\src\Microsoft.OpenApi.Tool\Microsoft.OpenApi.Tool.csproj' + '.\src\Microsoft.OpenApi.Hidi\Microsoft.OpenApi.Hidi.csproj' ) $projectsArray | ForEach-Object { From 39f5beaadd48079a099c9f1713b931ef3ff8fcec Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Tue, 16 Nov 2021 18:29:30 +0300 Subject: [PATCH 57/57] Update the tool's version --- src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj b/src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj index 27fc4b995..f0d7943e7 100644 --- a/src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj +++ b/src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj @@ -6,7 +6,7 @@ true hidi ./../../artifacts - 1.3.0-preview + 0.5.0-preview