From ad775b248d29ed2b1e702c15932751a03c54d6da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Derriey?= Date: Wed, 6 Mar 2024 10:50:35 +0100 Subject: [PATCH 1/5] Add stub test to support trailing slashes --- .../Services/OpenApiUrlTreeNodeTests.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test/Microsoft.OpenApi.Tests/Services/OpenApiUrlTreeNodeTests.cs b/test/Microsoft.OpenApi.Tests/Services/OpenApiUrlTreeNodeTests.cs index 511c6c5bd..76e1c5f31 100644 --- a/test/Microsoft.OpenApi.Tests/Services/OpenApiUrlTreeNodeTests.cs +++ b/test/Microsoft.OpenApi.Tests/Services/OpenApiUrlTreeNodeTests.cs @@ -466,5 +466,23 @@ public async Task VerifyDiagramFromSampleOpenAPI() await Verifier.Verify(diagram); } + + [Fact] + public void SupportsTrailingSlashesInPath() + { + var openApiDocument = new OpenApiDocument + { + Paths = new() + { + ["/cars/{car-id}/build/"] = new() + } + }; + + var label1 = "trailing-slash"; + var rootNode = OpenApiUrlTreeNode.Create(openApiDocument, label1); + var buildNode = rootNode.Children["cars"].Children["{car-id}"].Children["build"]; + + // Should buildNode have a path of "build/" or should it have a child with an empty string key? + } } } From 35398a180542bfa9d36964ce4fa2003cd6e1a443 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Derriey?= Date: Wed, 6 Mar 2024 15:35:38 +0100 Subject: [PATCH 2/5] Attempt at supporting trailing slashes --- .../Services/OpenApiUrlTreeNode.cs | 7 +++++ .../Services/OpenApiUrlTreeNodeTests.cs | 28 ++++++++++++++----- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/src/Microsoft.OpenApi/Services/OpenApiUrlTreeNode.cs b/src/Microsoft.OpenApi/Services/OpenApiUrlTreeNode.cs index 53a79c8a4..38b571112 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("/")) + { + // 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.Tests/Services/OpenApiUrlTreeNodeTests.cs b/test/Microsoft.OpenApi.Tests/Services/OpenApiUrlTreeNodeTests.cs index 76e1c5f31..5303d7390 100644 --- a/test/Microsoft.OpenApi.Tests/Services/OpenApiUrlTreeNodeTests.cs +++ b/test/Microsoft.OpenApi.Tests/Services/OpenApiUrlTreeNodeTests.cs @@ -467,22 +467,36 @@ public async Task VerifyDiagramFromSampleOpenAPI() await Verifier.Verify(diagram); } - [Fact] - public void SupportsTrailingSlashesInPath() + 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() { - ["/cars/{car-id}/build/"] = new() + [path] = new() } }; - var label1 = "trailing-slash"; - var rootNode = OpenApiUrlTreeNode.Create(openApiDocument, label1); - var buildNode = rootNode.Children["cars"].Children["{car-id}"].Children["build"]; + var label = "trailing-slash"; + var rootNode = OpenApiUrlTreeNode.Create(openApiDocument, label); + + var secondToLeafNode = rootNode; + foreach (var childName in childrenBeforeLastNode) + { + secondToLeafNode = secondToLeafNode.Children[childName]; + } - // Should buildNode have a path of "build/" or should it have a child with an empty string key? + Assert.True(secondToLeafNode.Children.TryGetValue(expectedLeafNodeName, out var lastNode)); + Assert.Equal(expectedLeafNodePath, lastNode.Path); } } } From 996407c3680a77c7419eeb9822756b8fcf025aa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Derriey?= Date: Thu, 7 Mar 2024 08:18:12 +0100 Subject: [PATCH 3/5] Ensure leaf nodes have no children --- .../Services/OpenApiUrlTreeNodeTests.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/Microsoft.OpenApi.Tests/Services/OpenApiUrlTreeNodeTests.cs b/test/Microsoft.OpenApi.Tests/Services/OpenApiUrlTreeNodeTests.cs index 5303d7390..4760cf37a 100644 --- a/test/Microsoft.OpenApi.Tests/Services/OpenApiUrlTreeNodeTests.cs +++ b/test/Microsoft.OpenApi.Tests/Services/OpenApiUrlTreeNodeTests.cs @@ -495,8 +495,9 @@ public void SupportsTrailingSlashesInPath(string path, string[] childrenBeforeLa secondToLeafNode = secondToLeafNode.Children[childName]; } - Assert.True(secondToLeafNode.Children.TryGetValue(expectedLeafNodeName, out var lastNode)); - Assert.Equal(expectedLeafNodePath, lastNode.Path); + Assert.True(secondToLeafNode.Children.TryGetValue(expectedLeafNodeName, out var leafNode)); + Assert.Equal(expectedLeafNodePath, leafNode.Path); + Assert.Empty(leafNode.Children); } } } From 7fa2f2c89846f958535b5335d2d47bd49d44ca29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Derriey?= Date: Mon, 11 Mar 2024 08:15:04 +0100 Subject: [PATCH 4/5] Add `StringComparison.OrdinalIgnoreCase` --- src/Microsoft.OpenApi/Services/OpenApiUrlTreeNode.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.OpenApi/Services/OpenApiUrlTreeNode.cs b/src/Microsoft.OpenApi/Services/OpenApiUrlTreeNode.cs index 38b571112..425250fc9 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiUrlTreeNode.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiUrlTreeNode.cs @@ -150,7 +150,7 @@ public OpenApiUrlTreeNode Attach(string path, } var segments = path.Split('/'); - if (path.EndsWith("/")) + 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 From e18748fa264cfd01dd817468ad3b8b374c7d3bc3 Mon Sep 17 00:00:00 2001 From: chelkyl <14041823+chelkyl@users.noreply.github.com> Date: Sun, 22 Sep 2024 16:04:15 -0500 Subject: [PATCH 5/5] Align on backslashes Co-authored-by: John L. Armstrong IV --- src/Microsoft.OpenApi/Services/OpenApiUrlTreeNode.cs | 2 +- .../Services/OpenApiUrlTreeNodeTests.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.OpenApi/Services/OpenApiUrlTreeNode.cs b/src/Microsoft.OpenApi/Services/OpenApiUrlTreeNode.cs index 425250fc9..e70f129b9 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiUrlTreeNode.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiUrlTreeNode.cs @@ -155,7 +155,7 @@ public OpenApiUrlTreeNode Attach(string path, // 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] += "/"; + segments[segments.Length - 1] += @"\"; } return Attach(segments: segments, diff --git a/test/Microsoft.OpenApi.Tests/Services/OpenApiUrlTreeNodeTests.cs b/test/Microsoft.OpenApi.Tests/Services/OpenApiUrlTreeNodeTests.cs index 4760cf37a..8a6f21dd8 100644 --- a/test/Microsoft.OpenApi.Tests/Services/OpenApiUrlTreeNodeTests.cs +++ b/test/Microsoft.OpenApi.Tests/Services/OpenApiUrlTreeNodeTests.cs @@ -470,8 +470,8 @@ public async Task VerifyDiagramFromSampleOpenAPI() 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/" }, + { "/cars/{car-id}/build/", ["cars", "{car-id}"], @"build\", @"\cars\{car-id}\build\" }, + { "/cars/", [], @"cars\", @"\cars\" }, }; [Theory]