diff --git a/.config/1espt/PipelineAutobaseliningConfig.yml b/.config/1espt/PipelineAutobaseliningConfig.yml new file mode 100644 index 000000000..2425160a4 --- /dev/null +++ b/.config/1espt/PipelineAutobaseliningConfig.yml @@ -0,0 +1,15 @@ +## DO NOT MODIFY THIS FILE MANUALLY. This is part of auto-baselining from 1ES Pipeline Templates. Go to [https://aka.ms/1espt-autobaselining] for more details. + +pipelines: + 107: + usedNonDefaultBranch: true + retail: + source: + credscan: + lastModifiedDate: 2024-09-13 + eslint: + lastModifiedDate: 2024-09-13 + psscriptanalyzer: + lastModifiedDate: 2024-09-13 + armory: + lastModifiedDate: 2024-09-13 diff --git a/src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj b/src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj index 120210fb0..377f67995 100644 --- a/src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj +++ b/src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj @@ -9,7 +9,7 @@ enable hidi ./../../artifacts - 1.4.9 + 1.4.10 OpenAPI.NET CLI tool for slicing OpenAPI documents true diff --git a/src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj b/src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj index d33f8a942..ca868b9b6 100644 --- a/src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj +++ b/src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj @@ -3,7 +3,7 @@ netstandard2.0 latest true - 1.6.21 + 1.6.22 OpenAPI.NET Readers for JSON and YAML documents true diff --git a/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj b/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj index 703291eb4..eb0ee6b12 100644 --- a/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj +++ b/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj @@ -3,7 +3,7 @@ netstandard2.0 Latest true - 1.6.21 + 1.6.22 .NET models with JSON and YAML writers for OpenAPI specification true diff --git a/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs b/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs index fcc2af0b2..bd351ce0e 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiFilterService.cs @@ -112,7 +112,10 @@ public static OpenApiDocument CreateFilteredDocument(OpenApiDocument source, Fun { foreach (var parameter in result.Parameters) { - pathItem.Parameters.Add(parameter); + if (!pathItem.Parameters.Contains(parameter)) + { + pathItem.Parameters.Add(parameter); + } } } } diff --git a/src/Microsoft.OpenApi/Services/OpenApiUrlTreeNode.cs b/src/Microsoft.OpenApi/Services/OpenApiUrlTreeNode.cs index 53a79c8a4..e70f129b9 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiUrlTreeNode.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiUrlTreeNode.cs @@ -150,6 +150,13 @@ public OpenApiUrlTreeNode Attach(string path, } var segments = path.Split('/'); + if (path.EndsWith("/", StringComparison.OrdinalIgnoreCase)) + { + // Remove the last element, which is empty, and append the trailing slash to the new last element + // This is to support URLs with trailing slashes + Array.Resize(ref segments, segments.Length - 1); + segments[segments.Length - 1] += @"\"; + } return Attach(segments: segments, pathItem: pathItem, diff --git a/test/Microsoft.OpenApi.Hidi.Tests/Microsoft.OpenApi.Hidi.Tests.csproj b/test/Microsoft.OpenApi.Hidi.Tests/Microsoft.OpenApi.Hidi.Tests.csproj index a0689b473..397831833 100644 --- a/test/Microsoft.OpenApi.Hidi.Tests/Microsoft.OpenApi.Hidi.Tests.csproj +++ b/test/Microsoft.OpenApi.Hidi.Tests/Microsoft.OpenApi.Hidi.Tests.csproj @@ -12,10 +12,10 @@ - - + + - + diff --git a/test/Microsoft.OpenApi.Hidi.Tests/Services/OpenApiFilterServiceTests.cs b/test/Microsoft.OpenApi.Hidi.Tests/Services/OpenApiFilterServiceTests.cs index f91d0db93..ebb863461 100644 --- a/test/Microsoft.OpenApi.Hidi.Tests/Services/OpenApiFilterServiceTests.cs +++ b/test/Microsoft.OpenApi.Hidi.Tests/Services/OpenApiFilterServiceTests.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 Microsoft.Extensions.Logging; @@ -105,6 +105,57 @@ public void TestPredicateFiltersUsingRelativeRequestUrls() Assert.False(predicate("/foo", OperationType.Patch, null)); } + [Fact] + public void CreateFilteredDocumentUsingPredicateFromRequestUrl() + { + // Arrange + var openApiDocument = new OpenApiDocument + { + Info = new() { Title = "Test", Version = "1.0" }, + Servers = new List { new() { Url = "https://localhost/" } }, + Paths = new() + { + ["/test/{id}"] = new() + { + Operations = new Dictionary + { + { OperationType.Get, new() }, + { OperationType.Patch, new() } + }, + Parameters = new List + { + new() + { + Name = "id", + In = ParameterLocation.Path, + Required = true, + Schema = new() + { + Type = "string" + } + } + } + } + + + } + }; + + var requestUrls = new Dictionary> + { + {"/test/{id}", new List {"GET","PATCH"}} + }; + + // Act + var predicate = OpenApiFilterService.CreatePredicate(requestUrls: requestUrls, source: openApiDocument); + var subsetDoc = OpenApiFilterService.CreateFilteredDocument(openApiDocument, predicate); + + // Assert that there's only 1 parameter in the subset document + Assert.NotNull(subsetDoc); + Assert.NotEmpty(subsetDoc.Paths); + Assert.Single(subsetDoc.Paths.First().Value.Parameters); + } + [Fact] public void ShouldParseNestedPostmanCollection() { diff --git a/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj b/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj index ebb063101..d0740f052 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj +++ b/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj @@ -17,11 +17,11 @@ - - + + - + diff --git a/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj b/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj index 9c91a65ab..e3a9fd2ad 100644 --- a/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj +++ b/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj @@ -10,13 +10,13 @@ - - - + + + - - + + diff --git a/test/Microsoft.OpenApi.Tests/Services/OpenApiUrlTreeNodeTests.cs b/test/Microsoft.OpenApi.Tests/Services/OpenApiUrlTreeNodeTests.cs index 8d0bb8cd5..4da7a337f 100644 --- a/test/Microsoft.OpenApi.Tests/Services/OpenApiUrlTreeNodeTests.cs +++ b/test/Microsoft.OpenApi.Tests/Services/OpenApiUrlTreeNodeTests.cs @@ -466,5 +466,38 @@ public async Task VerifyDiagramFromSampleOpenAPIAsync() await Verifier.Verify(diagram); } + + public static TheoryData SupportsTrailingSlashesInPathData => new TheoryData + { + // Path, children up to second to leaf, last expected leaf node name, expected leaf node path + { "/cars/{car-id}/build/", ["cars", "{car-id}"], @"build\", @"\cars\{car-id}\build\" }, + { "/cars/", [], @"cars\", @"\cars\" }, + }; + + [Theory] + [MemberData(nameof(SupportsTrailingSlashesInPathData))] + public void SupportsTrailingSlashesInPath(string path, string[] childrenBeforeLastNode, string expectedLeafNodeName, string expectedLeafNodePath) + { + var openApiDocument = new OpenApiDocument + { + Paths = new() + { + [path] = new() + } + }; + + var label = "trailing-slash"; + var rootNode = OpenApiUrlTreeNode.Create(openApiDocument, label); + + var secondToLeafNode = rootNode; + foreach (var childName in childrenBeforeLastNode) + { + secondToLeafNode = secondToLeafNode.Children[childName]; + } + + Assert.True(secondToLeafNode.Children.TryGetValue(expectedLeafNodeName, out var leafNode)); + Assert.Equal(expectedLeafNodePath, leafNode.Path); + Assert.Empty(leafNode.Children); + } } }