diff --git a/CHANGELOG.md b/CHANGELOG.md index b8010b9447..b839fe27a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixed parsing of `DateOnly` values generated in request executors [#3679](https://github.com/microsoft/kiota/issues/3679) - Fixes generation of default values names for go constructor functions [#3436](https://github.com/microsoft/kiota/issues/3436) - [Java] Removed the usage of reflection in `ApiClientBuilder` [`kiota-java#923`](https://github.com/microsoft/kiota-java/issues/923) +- Fixed a bug where path parameter type was not correctly detected during generation. [#3791](https://github.com/microsoft/kiota/issues/3791) ## [1.8.2] - 2023-11-08 diff --git a/src/Kiota.Builder/KiotaBuilder.cs b/src/Kiota.Builder/KiotaBuilder.cs index 376242ffe9..74a75272d5 100644 --- a/src/Kiota.Builder/KiotaBuilder.cs +++ b/src/Kiota.Builder/KiotaBuilder.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics; using System.Globalization; using System.IO; @@ -1080,9 +1081,10 @@ private CodeParameter GetIndexerParameterType(OpenApiUrlTreeNode currentNode, Op var parameterName = string.Join(OpenAPIUrlTreeNodePathSeparator, currentNode.Path.Split(OpenAPIUrlTreeNodePathSeparator, StringSplitOptions.RemoveEmptyEntries) .Skip(parentNode.Path.Count(static x => x == OpenAPIUrlTreeNodePathSeparator))) .Trim(OpenAPIUrlTreeNodePathSeparator, ForwardSlash, '{', '}'); - var parameter = currentNode.PathItems.TryGetValue(Constants.DefaultOpenApiLabel, out var pathItem) ? pathItem.Parameters + var pathItems = GetPathItems(currentNode); + var parameter = pathItems.TryGetValue(Constants.DefaultOpenApiLabel, out var pathItem) ? pathItem.Parameters .Select(static x => new { Parameter = x, IsPathParameter = true }) - .Union(currentNode.PathItems[Constants.DefaultOpenApiLabel].Operations.SelectMany(static x => x.Value.Parameters).Select(static x => new { Parameter = x, IsPathParameter = false })) + .Union(pathItems[Constants.DefaultOpenApiLabel].Operations.SelectMany(static x => x.Value.Parameters).Select(static x => new { Parameter = x, IsPathParameter = false })) .OrderBy(static x => x.IsPathParameter) .Select(static x => x.Parameter) .FirstOrDefault(x => x.Name.Equals(parameterName, StringComparison.OrdinalIgnoreCase) && x.In == ParameterLocation.Path) : @@ -1105,6 +1107,23 @@ private CodeParameter GetIndexerParameterType(OpenApiUrlTreeNode currentNode, Op }; return result; } + private static IDictionary GetPathItems(OpenApiUrlTreeNode currentNode, bool validateIsParameterNode = true) + { + if ((!validateIsParameterNode || currentNode.IsParameter) && currentNode.PathItems.Any()) + { + return currentNode.PathItems; + } + + if (currentNode.Children.Any()) + { + return currentNode.Children + .SelectMany(static x => GetPathItems(x.Value, false)) + .DistinctBy(static x => x.Key, StringComparer.Ordinal) + .ToDictionary(static x => x.Key, static x => x.Value, StringComparer.Ordinal); + } + + return ImmutableDictionary.Empty; + } private CodeIndexer[] CreateIndexer(string childIdentifier, string childType, CodeParameter parameter, OpenApiUrlTreeNode currentNode, OpenApiUrlTreeNode parentNode) { logger.LogTrace("Creating indexer {Name}", childIdentifier); diff --git a/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs b/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs index 93063066e4..102da7644b 100644 --- a/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs +++ b/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs @@ -6204,6 +6204,40 @@ public async Task IndexerTypeIsAccurateAndBackwardCompatibleIndexersAreAdded() application/json: schema: $ref: '#/components/schemas/microsoft.graph.post' + /authors/{author-id}/posts: + get: + parameters: + - name: author-id + in: path + required: true + description: The id of the author's posts to retrieve + schema: + type: string + format: uuid + responses: + 200: + description: Success! + content: + application/json: + schema: + $ref: '#/components/schemas/microsoft.graph.post' + /actors/{actor-id}/foo/baz: + get: + parameters: + - name: actor-id + in: path + required: true + description: The id of the actor + schema: + type: string + format: uuid + responses: + 200: + description: Success! + content: + application/json: + schema: + $ref: '#/components/schemas/microsoft.graph.post' components: schemas: microsoft.graph.post: @@ -6218,24 +6252,63 @@ public async Task IndexerTypeIsAccurateAndBackwardCompatibleIndexersAreAdded() var document = await builder.CreateOpenApiDocumentAsync(fs); var node = builder.CreateUriSpace(document!); var codeModel = builder.CreateSourceModel(node); - var collectionRequestBuilderNamespace = codeModel.FindNamespaceByName("ApiSdk.me.posts"); - Assert.NotNull(collectionRequestBuilderNamespace); - var collectionRequestBuilder = collectionRequestBuilderNamespace.FindChildByName("postsRequestBuilder"); - var collectionIndexer = collectionRequestBuilder.Indexer; - Assert.NotNull(collectionIndexer); - Assert.Equal("integer", collectionIndexer.IndexParameter.Type.Name); - Assert.Equal("The id of the pet to retrieve", collectionIndexer.IndexParameter.Documentation.Description, StringComparer.OrdinalIgnoreCase); - Assert.False(collectionIndexer.IndexParameter.Type.IsNullable); - Assert.False(collectionIndexer.Deprecation.IsDeprecated); - var collectionStringIndexer = collectionRequestBuilder.FindChildByName($"{collectionIndexer.Name}-string"); - Assert.NotNull(collectionStringIndexer); - Assert.Equal("string", collectionStringIndexer.IndexParameter.Type.Name); - Assert.True(collectionStringIndexer.IndexParameter.Type.IsNullable); - Assert.True(collectionStringIndexer.Deprecation.IsDeprecated); - var itemRequestBuilderNamespace = codeModel.FindNamespaceByName("ApiSdk.me.posts.item"); - Assert.NotNull(itemRequestBuilderNamespace); - var itemRequestBuilder = itemRequestBuilderNamespace.FindChildByName("postItemRequestBuilder"); - Assert.Equal(collectionIndexer.ReturnType.Name, itemRequestBuilder.Name); + + var postsCollectionRequestBuilderNamespace = codeModel.FindNamespaceByName("ApiSdk.me.posts"); + Assert.NotNull(postsCollectionRequestBuilderNamespace); + var postsCollectionRequestBuilder = postsCollectionRequestBuilderNamespace.FindChildByName("postsRequestBuilder"); + var postsCollectionIndexer = postsCollectionRequestBuilder.Indexer; + Assert.NotNull(postsCollectionIndexer); + Assert.Equal("integer", postsCollectionIndexer.IndexParameter.Type.Name); + Assert.Equal("The id of the pet to retrieve", postsCollectionIndexer.IndexParameter.Documentation.Description, StringComparer.OrdinalIgnoreCase); + Assert.False(postsCollectionIndexer.IndexParameter.Type.IsNullable); + Assert.False(postsCollectionIndexer.Deprecation.IsDeprecated); + var postsCollectionStringIndexer = postsCollectionRequestBuilder.FindChildByName($"{postsCollectionIndexer.Name}-string"); + Assert.NotNull(postsCollectionStringIndexer); + Assert.Equal("string", postsCollectionStringIndexer.IndexParameter.Type.Name); + Assert.True(postsCollectionStringIndexer.IndexParameter.Type.IsNullable); + Assert.True(postsCollectionStringIndexer.Deprecation.IsDeprecated); + var postsItemRequestBuilderNamespace = codeModel.FindNamespaceByName("ApiSdk.me.posts.item"); + Assert.NotNull(postsItemRequestBuilderNamespace); + var postsItemRequestBuilder = postsItemRequestBuilderNamespace.FindChildByName("postItemRequestBuilder"); + Assert.Equal(postsCollectionIndexer.ReturnType.Name, postsItemRequestBuilder.Name); + + var authorsCollectionRequestBuilderNamespace = codeModel.FindNamespaceByName("ApiSdk.authors"); + Assert.NotNull(authorsCollectionRequestBuilderNamespace); + var authorsCollectionRequestBuilder = authorsCollectionRequestBuilderNamespace.FindChildByName("authorsRequestBuilder"); + var authorsCollectionIndexer = authorsCollectionRequestBuilder.Indexer; + Assert.NotNull(authorsCollectionIndexer); + Assert.Equal("Guid", authorsCollectionIndexer.IndexParameter.Type.Name); + Assert.Equal("The id of the author's posts to retrieve", authorsCollectionIndexer.IndexParameter.Documentation.Description, StringComparer.OrdinalIgnoreCase); + Assert.False(authorsCollectionIndexer.IndexParameter.Type.IsNullable); + Assert.False(authorsCollectionIndexer.Deprecation.IsDeprecated); + var authorsCllectionStringIndexer = authorsCollectionRequestBuilder.FindChildByName($"{authorsCollectionIndexer.Name}-string"); + Assert.NotNull(authorsCllectionStringIndexer); + Assert.Equal("string", authorsCllectionStringIndexer.IndexParameter.Type.Name); + Assert.True(authorsCllectionStringIndexer.IndexParameter.Type.IsNullable); + Assert.True(authorsCllectionStringIndexer.Deprecation.IsDeprecated); + var authorsItemRequestBuilderNamespace = codeModel.FindNamespaceByName("ApiSdk.authors.item"); + Assert.NotNull(authorsItemRequestBuilderNamespace); + var authorsItemRequestBuilder = authorsItemRequestBuilderNamespace.FindChildByName("authorItemRequestBuilder"); + Assert.Equal(authorsCollectionIndexer.ReturnType.Name, authorsItemRequestBuilder.Name); + + var actorsCollectionRequestBuilderNamespace = codeModel.FindNamespaceByName("ApiSdk.actors"); + Assert.NotNull(actorsCollectionRequestBuilderNamespace); + var actorsCollectionRequestBuilder = actorsCollectionRequestBuilderNamespace.FindChildByName("actorsRequestBuilder"); + var actorsCollectionIndexer = actorsCollectionRequestBuilder.Indexer; + Assert.NotNull(actorsCollectionIndexer); + Assert.Equal("Guid", actorsCollectionIndexer.IndexParameter.Type.Name); + Assert.Equal("The id of the actor", actorsCollectionIndexer.IndexParameter.Documentation.Description, StringComparer.OrdinalIgnoreCase); + Assert.False(actorsCollectionIndexer.IndexParameter.Type.IsNullable); + Assert.False(actorsCollectionIndexer.Deprecation.IsDeprecated); + var actorsCllectionStringIndexer = actorsCollectionRequestBuilder.FindChildByName($"{actorsCollectionIndexer.Name}-string"); + Assert.NotNull(actorsCllectionStringIndexer); + Assert.Equal("string", actorsCllectionStringIndexer.IndexParameter.Type.Name); + Assert.True(actorsCllectionStringIndexer.IndexParameter.Type.IsNullable); + Assert.True(actorsCllectionStringIndexer.Deprecation.IsDeprecated); + var actorsItemRequestBuilderNamespace = codeModel.FindNamespaceByName("ApiSdk.actors.item"); + Assert.NotNull(actorsItemRequestBuilderNamespace); + var actorsItemRequestBuilder = actorsItemRequestBuilderNamespace.FindChildByName("actorItemRequestBuilder"); + Assert.Equal(actorsCollectionIndexer.ReturnType.Name, actorsItemRequestBuilder.Name); } [Fact] public async Task MapsBooleanEnumToBooleanType()