diff --git a/CHANGELOG.md b/CHANGELOG.md index d8664e64a9..f4fb3b02a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixed parameters that are in camelcase to snakecase in Python. [#3525](https://github.com/microsoft/kiota/issues/3525) - Fixed missing imports for method parameters that are query parameters. - Replaces the use of "new" by "override" and "virtual" in CSharp models. +- Fixed a bug where namespaces segment names might contain special characters. [#3599](https://github.com/microsoft/kiota/issues/3599) - Fixed a bug in validation rules where form data would wrongfully warn for arrays. [#3569](https://github.com/microsoft/kiota/issues/3569) - Fixed a bug where content type parameter would be missing for TypeScript, Java, PHP, Python, Ruby and Go. [#3610](https://github.com/microsoft/kiota/issues/3610) - Fixed query parameters type mapping for arrays. [#3354](https://github.com/microsoft/kiota/issues/3354) diff --git a/src/Kiota.Builder/KiotaBuilder.cs b/src/Kiota.Builder/KiotaBuilder.cs index 302b49b746..ab39c7c26a 100644 --- a/src/Kiota.Builder/KiotaBuilder.cs +++ b/src/Kiota.Builder/KiotaBuilder.cs @@ -1614,7 +1614,7 @@ private string GetModelsNamespaceNameFromReferenceId(string? referenceId) referenceId = referenceId.Trim(NsNameSeparator); var lastDotIndex = referenceId.LastIndexOf(NsNameSeparator); var namespaceSuffix = lastDotIndex != -1 ? $".{referenceId[..lastDotIndex]}" : string.Empty; - return $"{modelsNamespace?.Name}{namespaceSuffix}"; + return $"{modelsNamespace?.Name}{string.Join(NsNameSeparator, namespaceSuffix.Split(NsNameSeparator).Select(static x => x.CleanupSymbolName()))}"; } private CodeType CreateModelDeclarationAndType(OpenApiUrlTreeNode currentNode, OpenApiSchema schema, OpenApiOperation? operation, CodeNamespace codeNamespace, string classNameSuffix = "", OpenApiResponse? response = default, string typeNameForInlineSchema = "", bool isRequestBody = false) { diff --git a/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs b/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs index 4775493de7..88248c4306 100644 --- a/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs +++ b/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs @@ -146,6 +146,51 @@ await File.WriteAllTextAsync(tempFilePath, @$"swagger: 2.0 Assert.NotNull(constructor); Assert.Equal("https://api.funtranslations.com", constructor.BaseUrl); } + [Fact] + public async Task HandlesSpecialCharactersInPathSegment() + { + var tempFilePath = Path.Combine(Path.GetTempPath(), Path.GetTempFileName()); + await File.WriteAllTextAsync(tempFilePath, @$"openapi: 3.0.1 +info: + title: OData Service for namespace microsoft.graph + description: This OData service is located at https://graph.microsoft.com/v1.0 + version: 1.0.1 +servers: + - url: https://api.funtranslations.com +paths: + /my-api: + get: + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/Specialized-Complex.StorageAccount' +components: + schemas: + Specialized-Complex.StorageAccount: + type: object + properties: + name: + type: string"); + var mockLogger = new Mock>(); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", OpenAPIFilePath = "https://api.apis.guru/v2/specs/funtranslations.com/starwars/2.3/swagger.json" }, _httpClient); + await using var fs = new FileStream(tempFilePath, FileMode.Open); + var document = await builder.CreateOpenApiDocumentAsync(fs); + var node = builder.CreateUriSpace(document); + builder.SetApiRootUrl(); + var codeModel = builder.CreateSourceModel(node); + var rootNS = codeModel.FindNamespaceByName("ApiSdk"); + Assert.NotNull(rootNS); + Assert.Null(codeModel.FindNamespaceByName("ApiSdk.my-api")); + Assert.NotNull(codeModel.FindNamespaceByName("ApiSdk.MyApi")); + var modelsNS = codeModel.FindNamespaceByName("ApiSdk.models"); + Assert.NotNull(modelsNS); + var specializedNS = modelsNS.FindNamespaceByName("ApiSdk.models.SpecializedComplex"); + Assert.NotNull(specializedNS); + Assert.Null(modelsNS.FindNamespaceByName("ApiSdk.models.Specialized-Complex")); + Assert.NotNull(specializedNS.FindChildByName("StorageAccount", false)); + } private readonly HttpClient _httpClient = new(); [Fact] public async Task ParsesEnumDescriptions()