From 1e61aa87db98541c19a3f6d4b61146fb828f98f9 Mon Sep 17 00:00:00 2001
From: Andrew Omondi <anomondi@microsoft.com>
Date: Fri, 24 May 2024 14:35:19 +0300
Subject: [PATCH 1/2] Fixes lob cleanup regex

---
 CHANGELOG.md                                   |  1 +
 src/Kiota.Builder/KiotaBuilder.cs              |  4 ++--
 tests/Kiota.Builder.Tests/KiotaBuilderTests.cs | 18 ++++++++++++++++++
 3 files changed, 21 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index a8523e155e..5d18fb3eab 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -42,6 +42,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 - Fixes a bug where models would be duplicated in some allOf scenarios [#4191](https://github.com/microsoft/kiota/issues/4191)
 - Fixes a bug where CLI Generation does not handle path parameters of type "string" and format "date", "date-time", "time", etc. [#4615](https://github.com/microsoft/kiota/issues/4615)
 - Fixes a bug where request executors would be missing Untyped parameters in dotnet [#4692](https://github.com/microsoft/kiota/issues/4692)
+- Fixes a bug where indexers in include/exclude patters were not normalized if the indexer was the last segment without a slash at the end [#4715](https://github.com/microsoft/kiota/issues/4715)
 
 ## [1.14.0] - 2024-05-02
 
diff --git a/src/Kiota.Builder/KiotaBuilder.cs b/src/Kiota.Builder/KiotaBuilder.cs
index 0275f5bd50..2b17430f05 100644
--- a/src/Kiota.Builder/KiotaBuilder.cs
+++ b/src/Kiota.Builder/KiotaBuilder.cs
@@ -331,9 +331,9 @@ private async Task FinalizeWorkspaceAsync(Stopwatch sw, int stepId, OpenApiUrlTr
     }
     private readonly WorkspaceManagementService workspaceManagementService;
     private static readonly GlobComparer globComparer = new();
-    [GeneratedRegex(@"([\/\\])\{[\w\d-]+\}([\/\\])", RegexOptions.IgnoreCase | RegexOptions.Singleline, 2000)]
+    [GeneratedRegex(@"([\/\\])\{[\w\d-]+\}([\/\\])?", RegexOptions.IgnoreCase | RegexOptions.Singleline, 2000)]
     private static partial Regex MultiIndexSameLevelCleanupRegex();
-    private static string ReplaceAllIndexesWithWildcard(string path, uint depth = 10) => depth == 0 ? path : ReplaceAllIndexesWithWildcard(MultiIndexSameLevelCleanupRegex().Replace(path, "$1{*}$2"), depth - 1); // the bound needs to be greedy to avoid replacing anything else than single path parameters
+    internal static string ReplaceAllIndexesWithWildcard(string path, uint depth = 10) => depth == 0 ? path : ReplaceAllIndexesWithWildcard(MultiIndexSameLevelCleanupRegex().Replace(path, "$1{*}$2"), depth - 1); // the bound needs to be greedy to avoid replacing anything else than single path parameters
     private static Dictionary<Glob, HashSet<OperationType>> GetFilterPatternsFromConfiguration(HashSet<string> configPatterns)
     {
         return configPatterns.Select(static x =>
diff --git a/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs b/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs
index 1229f61547..b33acbe7f4 100644
--- a/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs
+++ b/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs
@@ -8647,6 +8647,24 @@ public void CleansUpOperationIdAddsMissingOperationId()
         Assert.Equal("directory_administrativeunits_post", operations[1].Value.OperationId);
     }
 
+    [Theory]
+    [InlineData("repos/{id}/","repos/{*}/")] // normalish case
+    [InlineData("repos/{id}","repos/{*}")]// no trailing slash
+    [InlineData("/repos/{id}","/repos/{*}")]// no trailing slash(slash at begining).
+    [InlineData("repos/{id}/dependencies/{dep-id}","repos/{*}/dependencies/{*}")]// multiple indexers
+    [InlineData("/repos/{id}/dependencies/{dep-id}/","/repos/{*}/dependencies/{*}/")]// multiple indexers(slash at begining and end).
+    [InlineData("/repos/{id}/dependencies/{dep-id}","/repos/{*}/dependencies/{*}")]// multiple indexers(slash at begining).
+    [InlineData("repos/{id}/{dep-id}","repos/{*}/{*}")]// indexers following each other.
+    [InlineData("/repos/{id}/{dep-id}","/repos/{*}/{*}")]// indexers following each other(slash at begining).
+    [InlineData("repos/msft","repos/msft")]// no indexers
+    [InlineData("/repos","/repos")]// no indexers(slash at begining).
+    [InlineData("repos","repos")]// no indexers
+    public void ReplacesAllIndexesWithWildcard(string inputPath, string expectedGlob)
+    {
+        var resultGlob = KiotaBuilder.ReplaceAllIndexesWithWildcard(inputPath);
+        Assert.Equal(expectedGlob,resultGlob);
+    }
+
     [Fact]
     public void CleansUpOperationIdChangesOperationId()
     {

From 8451433747c67821b4c14295fe913cffbfbeda5a Mon Sep 17 00:00:00 2001
From: Andrew Omondi <anomondi@microsoft.com>
Date: Fri, 24 May 2024 14:36:13 +0300
Subject: [PATCH 2/2] Format

---
 .../Kiota.Builder.Tests/KiotaBuilderTests.cs  | 24 +++++++++----------
 1 file changed, 12 insertions(+), 12 deletions(-)

diff --git a/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs b/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs
index b33acbe7f4..7240e27a4f 100644
--- a/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs
+++ b/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs
@@ -8648,21 +8648,21 @@ public void CleansUpOperationIdAddsMissingOperationId()
     }
 
     [Theory]
-    [InlineData("repos/{id}/","repos/{*}/")] // normalish case
-    [InlineData("repos/{id}","repos/{*}")]// no trailing slash
-    [InlineData("/repos/{id}","/repos/{*}")]// no trailing slash(slash at begining).
-    [InlineData("repos/{id}/dependencies/{dep-id}","repos/{*}/dependencies/{*}")]// multiple indexers
-    [InlineData("/repos/{id}/dependencies/{dep-id}/","/repos/{*}/dependencies/{*}/")]// multiple indexers(slash at begining and end).
-    [InlineData("/repos/{id}/dependencies/{dep-id}","/repos/{*}/dependencies/{*}")]// multiple indexers(slash at begining).
-    [InlineData("repos/{id}/{dep-id}","repos/{*}/{*}")]// indexers following each other.
-    [InlineData("/repos/{id}/{dep-id}","/repos/{*}/{*}")]// indexers following each other(slash at begining).
-    [InlineData("repos/msft","repos/msft")]// no indexers
-    [InlineData("/repos","/repos")]// no indexers(slash at begining).
-    [InlineData("repos","repos")]// no indexers
+    [InlineData("repos/{id}/", "repos/{*}/")] // normalish case
+    [InlineData("repos/{id}", "repos/{*}")]// no trailing slash
+    [InlineData("/repos/{id}", "/repos/{*}")]// no trailing slash(slash at begining).
+    [InlineData("repos/{id}/dependencies/{dep-id}", "repos/{*}/dependencies/{*}")]// multiple indexers
+    [InlineData("/repos/{id}/dependencies/{dep-id}/", "/repos/{*}/dependencies/{*}/")]// multiple indexers(slash at begining and end).
+    [InlineData("/repos/{id}/dependencies/{dep-id}", "/repos/{*}/dependencies/{*}")]// multiple indexers(slash at begining).
+    [InlineData("repos/{id}/{dep-id}", "repos/{*}/{*}")]// indexers following each other.
+    [InlineData("/repos/{id}/{dep-id}", "/repos/{*}/{*}")]// indexers following each other(slash at begining).
+    [InlineData("repos/msft", "repos/msft")]// no indexers
+    [InlineData("/repos", "/repos")]// no indexers(slash at begining).
+    [InlineData("repos", "repos")]// no indexers
     public void ReplacesAllIndexesWithWildcard(string inputPath, string expectedGlob)
     {
         var resultGlob = KiotaBuilder.ReplaceAllIndexesWithWildcard(inputPath);
-        Assert.Equal(expectedGlob,resultGlob);
+        Assert.Equal(expectedGlob, resultGlob);
     }
 
     [Fact]