From fa8b57acae759ee61e3cd0db95fab112bffa87ac Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 30 Apr 2024 08:52:30 +0000 Subject: [PATCH 1/7] Bump @typescript-eslint/parser from 7.7.1 to 7.8.0 in /it/typescript Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 7.7.1 to 7.8.0. - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v7.8.0/packages/parser) --- updated-dependencies: - dependency-name: "@typescript-eslint/parser" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- it/typescript/package-lock.json | 115 +++++++++++++++++++++++++++++--- it/typescript/package.json | 2 +- 2 files changed, 108 insertions(+), 9 deletions(-) diff --git a/it/typescript/package-lock.json b/it/typescript/package-lock.json index 519084a927..28fbc14b5d 100644 --- a/it/typescript/package-lock.json +++ b/it/typescript/package-lock.json @@ -24,7 +24,7 @@ "@es-exec/esbuild-plugin-start": "^0.0.5", "@types/node": "^20.12.7", "@typescript-eslint/eslint-plugin": "^7.7.1", - "@typescript-eslint/parser": "^7.7.1", + "@typescript-eslint/parser": "^7.8.0", "esbuild": "^0.20.2", "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", @@ -877,15 +877,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.7.1.tgz", - "integrity": "sha512-vmPzBOOtz48F6JAGVS/kZYk4EkXao6iGrD838sp1w3NQQC0W8ry/q641KU4PrG7AKNAf56NOcR8GOpH8l9FPCw==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.8.0.tgz", + "integrity": "sha512-KgKQly1pv0l4ltcftP59uQZCi4HUYswCLbTqVZEJu7uLX8CTLyswqMLqLN+2QFz4jCptqWVV4SB7vdxcH2+0kQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "7.7.1", - "@typescript-eslint/types": "7.7.1", - "@typescript-eslint/typescript-estree": "7.7.1", - "@typescript-eslint/visitor-keys": "7.7.1", + "@typescript-eslint/scope-manager": "7.8.0", + "@typescript-eslint/types": "7.8.0", + "@typescript-eslint/typescript-estree": "7.8.0", + "@typescript-eslint/visitor-keys": "7.8.0", "debug": "^4.3.4" }, "engines": { @@ -904,6 +904,105 @@ } } }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.8.0.tgz", + "integrity": "sha512-viEmZ1LmwsGcnr85gIq+FCYI7nO90DVbE37/ll51hjv9aG+YZMb4WDE2fyWpUR4O/UrhGRpYXK/XajcGTk2B8g==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.8.0", + "@typescript-eslint/visitor-keys": "7.8.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.8.0.tgz", + "integrity": "sha512-wf0peJ+ZGlcH+2ZS23aJbOv+ztjeeP8uQ9GgwMJGVLx/Nj9CJt17GWgWWoSmoRVKAX2X+7fzEnAjxdvK2gqCLw==", + "dev": true, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.8.0.tgz", + "integrity": "sha512-5pfUCOwK5yjPaJQNy44prjCwtr981dO8Qo9J9PwYXZ0MosgAbfEMB008dJ5sNo3+/BN6ytBPuSvXUg9SAqB0dg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.8.0", + "@typescript-eslint/visitor-keys": "7.8.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.8.0.tgz", + "integrity": "sha512-q4/gibTNBQNA0lGyYQCmWRS5D15n8rXh4QjK3KV+MBPlTYHpfBUT3D3PaPR/HeNiI9W6R7FvlkcGhNyAoP+caA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.8.0", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@typescript-eslint/scope-manager": { "version": "7.7.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.7.1.tgz", diff --git a/it/typescript/package.json b/it/typescript/package.json index bea602cf41..4cef77bc50 100644 --- a/it/typescript/package.json +++ b/it/typescript/package.json @@ -21,7 +21,7 @@ "@es-exec/esbuild-plugin-start": "^0.0.5", "@types/node": "^20.12.7", "@typescript-eslint/eslint-plugin": "^7.7.1", - "@typescript-eslint/parser": "^7.7.1", + "@typescript-eslint/parser": "^7.8.0", "esbuild": "^0.20.2", "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", From 97e2b7921c15eb2f67606e0525c0fa6d5b80ffa0 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Tue, 30 Apr 2024 08:11:12 -0400 Subject: [PATCH 2/7] - adds a new eslint group for typescript integration tests Signed-off-by: Vincent Biret --- .github/dependabot.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index ef2cc5f75b..3fd5cefbd5 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -87,6 +87,9 @@ updates: kiota-dependencies: patterns: - "*kiota*" + eslint: + patterns: + - "*eslint*" - package-ecosystem: npm directory: "/vscode/microsoft-kiota" From 70f6a599d332d7838c7e08da32134583ffebd3f3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 May 2024 08:38:56 +0000 Subject: [PATCH 3/7] Bump @azure/identity from 4.1.0 to 4.2.0 in /it/typescript Bumps [@azure/identity](https://github.com/Azure/azure-sdk-for-js) from 4.1.0 to 4.2.0. - [Release notes](https://github.com/Azure/azure-sdk-for-js/releases) - [Changelog](https://github.com/Azure/azure-sdk-for-js/blob/main/documentation/Changelog-for-next-generation.md) - [Commits](https://github.com/Azure/azure-sdk-for-js/compare/@azure/identity_4.1.0...@azure/identity_4.2.0) --- updated-dependencies: - dependency-name: "@azure/identity" dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- it/typescript/package-lock.json | 8 ++++---- it/typescript/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/it/typescript/package-lock.json b/it/typescript/package-lock.json index 28fbc14b5d..5a6c8e9709 100644 --- a/it/typescript/package-lock.json +++ b/it/typescript/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.1", "license": "MIT", "dependencies": { - "@azure/identity": "^4.1.0", + "@azure/identity": "^4.2.0", "@microsoft/kiota-abstractions": "^1.0.0-preview.51", "@microsoft/kiota-authentication-azure": "^1.0.0-preview.46", "@microsoft/kiota-http-fetchlibrary": "^1.0.0-preview.50", @@ -126,9 +126,9 @@ } }, "node_modules/@azure/identity": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.1.0.tgz", - "integrity": "sha512-BhYkF8Xr2gXjyDxocm0pc9RI5J5a1jw8iW0dw6Bx95OGdYbuMyFZrrwNw4eYSqQ2BB6FZOqpJP3vjsAqRcvDhw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.2.0.tgz", + "integrity": "sha512-ve3aYv79qXOJ8wRxQ5jO0eIz2DZ4o0TyME4m4vlGV5YyePddVZ+pFMzusAMODNAflYAAv1cBIhKnd4xytmXyig==", "dependencies": { "@azure/abort-controller": "^1.0.0", "@azure/core-auth": "^1.5.0", diff --git a/it/typescript/package.json b/it/typescript/package.json index 4cef77bc50..c2806b7378 100644 --- a/it/typescript/package.json +++ b/it/typescript/package.json @@ -30,7 +30,7 @@ "typescript": "^4.9.5" }, "dependencies": { - "@azure/identity": "^4.1.0", + "@azure/identity": "^4.2.0", "@microsoft/kiota-abstractions": "^1.0.0-preview.51", "@microsoft/kiota-authentication-azure": "^1.0.0-preview.46", "@microsoft/kiota-http-fetchlibrary": "^1.0.0-preview.50", From ff534e5a1f237676edb9eaa5bbd69dc5c9d0a526 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Wed, 1 May 2024 12:20:12 -0400 Subject: [PATCH 4/7] - fixes empty log file issue --- CHANGELOG.md | 5 ++- src/kiota/Program.cs | 12 +++++- .../Logging/FileLoggerTests.cs | 40 +++++++++++++++++++ 3 files changed, 53 insertions(+), 4 deletions(-) create mode 100644 tests/Kiota.Builder.Tests/Logging/FileLoggerTests.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index e181101aeb..e1334709ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,10 +16,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Fixed a bug where TypeScript deserialization would fail on Uppercase properties.[#4479](https://github.com/microsoft/kiota/issues/4479) +- Fixed a bug where clients and plugins generation would leave empty log files. [#4584](https://github.com/microsoft/kiota/issues/4584) - Changed URI template generation to reuse templates when required templates are absent across operations. -- Fixed path deduplication logic to avoid double `Id` suffixes in indexer names in scenarios where the `Id` suffix is already present.[#4519](https://github.com/microsoft/kiota/issues/4519) +- Fixed path deduplication logic to avoid double `Id` suffixes in indexer names in scenarios where the `Id` suffix is already present.[#4519](https://github.com/microsoft/kiota/issues/4519) - Updated reserved name providers for Java and Php so that "object" can be escaped. -- Fixes request builder disambiguation when child nodes had different prefixes for different paths with same parameters.[#4448](https://github.com/microsoft/kiota/issues/4448) +- Fixes request builder disambiguation when child nodes had different prefixes for different paths with same parameters.[#4448](https://github.com/microsoft/kiota/issues/4448) - Do not generate CS8603 warnings when enabling backing store in CSharp generation. - Fixed excluding operation. [#4399](https://github.com/microsoft/kiota/issues/4399) - Fixed plugin generation of `ApiManifest` type to not include the `x-ms-kiota-hash` in the generated plugin. [#4561](https://github.com/microsoft/kiota/issues/4561) diff --git a/src/kiota/Program.cs b/src/kiota/Program.cs index 1509b077a3..9ddc6255ba 100644 --- a/src/kiota/Program.cs +++ b/src/kiota/Program.cs @@ -7,8 +7,16 @@ static async Task Main(string[] args) { var rootCommand = KiotaHost.GetRootCommand(); var result = await rootCommand.InvokeAsync(args); - foreach (var subCommand in rootCommand.Subcommands.Select(static x => x.Handler).OfType()) - subCommand.Dispose(); + DisposeSubCommands(rootCommand); return result; } + private static void DisposeSubCommands(this Command command) + { + if (command.Handler is IDisposable disposableHandler) + disposableHandler.Dispose(); + foreach (var subCommand in command.Subcommands) + { + DisposeSubCommands(subCommand); + } + } } diff --git a/tests/Kiota.Builder.Tests/Logging/FileLoggerTests.cs b/tests/Kiota.Builder.Tests/Logging/FileLoggerTests.cs new file mode 100644 index 0000000000..7955674a9e --- /dev/null +++ b/tests/Kiota.Builder.Tests/Logging/FileLoggerTests.cs @@ -0,0 +1,40 @@ +using System; +using System.IO; +using Kiota.Builder.Logging; +using Microsoft.Extensions.Logging; +using Xunit; + +namespace Kiota.Builder.Tests.Logging; + +public sealed class FileLoggerTests : IDisposable +{ + private readonly string _logDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + public FileLoggerTests() + { + Directory.CreateDirectory(_logDirectory); + } + [Fact] + public void CleansUpFileWhenNoLogs() + { + using (var logger = new FileLogLogger(_logDirectory, LogLevel.Warning, "test")) + { //using this format intentionally to ensure the dispose is called before the assert + logger.LogInformation("test"); + } + Assert.False(File.Exists(Path.Combine(_logDirectory, FileLogLogger.LogFileName))); + } + [Fact] + public void KeepsLogFileWhenLogs() + { + using (var logger = new FileLogLogger(_logDirectory, LogLevel.Warning, "test")) + { //using this format intentionally to ensure the dispose is called before the assert + logger.LogWarning("test"); + } + Assert.True(File.Exists(Path.Combine(_logDirectory, FileLogLogger.LogFileName))); + } + + public void Dispose() + { + if (Directory.Exists(_logDirectory)) + Directory.Delete(_logDirectory, true); + } +} From e57c84fbe5da09c7917ff1ab6ee4e6f63fae397a Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Wed, 1 May 2024 12:33:20 -0400 Subject: [PATCH 5/7] - fixes manifest serialization --- CHANGELOG.md | 5 +++-- .../Plugins/PluginsGenerationService.cs | 2 +- .../Plugins/PluginsGenerationServiceTests.cs | 13 +++++++++++-- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e181101aeb..7269d8253c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,11 +15,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Fixed a bug where the Microsoft Plugin Manifests would start with relative paths. [#4583](https://github.com/microsoft/kiota/issues/4583) - Fixed a bug where TypeScript deserialization would fail on Uppercase properties.[#4479](https://github.com/microsoft/kiota/issues/4479) - Changed URI template generation to reuse templates when required templates are absent across operations. -- Fixed path deduplication logic to avoid double `Id` suffixes in indexer names in scenarios where the `Id` suffix is already present.[#4519](https://github.com/microsoft/kiota/issues/4519) +- Fixed path deduplication logic to avoid double `Id` suffixes in indexer names in scenarios where the `Id` suffix is already present.[#4519](https://github.com/microsoft/kiota/issues/4519) - Updated reserved name providers for Java and Php so that "object" can be escaped. -- Fixes request builder disambiguation when child nodes had different prefixes for different paths with same parameters.[#4448](https://github.com/microsoft/kiota/issues/4448) +- Fixes request builder disambiguation when child nodes had different prefixes for different paths with same parameters.[#4448](https://github.com/microsoft/kiota/issues/4448) - Do not generate CS8603 warnings when enabling backing store in CSharp generation. - Fixed excluding operation. [#4399](https://github.com/microsoft/kiota/issues/4399) - Fixed plugin generation of `ApiManifest` type to not include the `x-ms-kiota-hash` in the generated plugin. [#4561](https://github.com/microsoft/kiota/issues/4561) diff --git a/src/Kiota.Builder/Plugins/PluginsGenerationService.cs b/src/Kiota.Builder/Plugins/PluginsGenerationService.cs index 93cc546e55..1e66fce8f5 100644 --- a/src/Kiota.Builder/Plugins/PluginsGenerationService.cs +++ b/src/Kiota.Builder/Plugins/PluginsGenerationService.cs @@ -136,7 +136,7 @@ descriptionExtension is OpenApiDescriptionForModelExtension extension && Auth = new AnonymousAuth(), Spec = new OpenApiRuntimeSpec() { - Url = openApiDocumentPath + Url = openApiDocumentPath.TrimStart('.').TrimStart('/'), }, RunForFunctions = [operation.OperationId] }); diff --git a/tests/Kiota.Builder.Tests/Plugins/PluginsGenerationServiceTests.cs b/tests/Kiota.Builder.Tests/Plugins/PluginsGenerationServiceTests.cs index 77e2b4a638..01bd6ba8fc 100644 --- a/tests/Kiota.Builder.Tests/Plugins/PluginsGenerationServiceTests.cs +++ b/tests/Kiota.Builder.Tests/Plugins/PluginsGenerationServiceTests.cs @@ -1,11 +1,14 @@ using System; using System.IO; +using System.Linq; using System.Net.Http; +using System.Text.Json; using System.Threading.Tasks; using Kiota.Builder.Configuration; using Kiota.Builder.Plugins; using Microsoft.Extensions.Logging; using Microsoft.OpenApi.Services; +using Microsoft.Plugins.Manifest; using Moq; using Xunit; @@ -63,8 +66,14 @@ public async Task GeneratesManifest() var pluginsGenerationService = new PluginsGenerationService(openApiDocument, urlTreeNode, generationConfiguration); await pluginsGenerationService.GenerateManifestAsync(); - Assert.True(File.Exists(Path.Combine(outputDirectory, "client-microsoft.json"))); + Assert.True(File.Exists(Path.Combine(outputDirectory, ManifestFileName))); Assert.True(File.Exists(Path.Combine(outputDirectory, "client-apimanifest.json"))); - Assert.True(File.Exists(Path.Combine(outputDirectory, "openapi.yml"))); + Assert.True(File.Exists(Path.Combine(outputDirectory, OpenApiFileName))); + var manifestContent = await File.ReadAllTextAsync(Path.Combine(outputDirectory, ManifestFileName)); + using var jsonDocument = JsonDocument.Parse(manifestContent); + var resultingManifest = PluginManifestDocument.Load(jsonDocument.RootElement); + Assert.Equal(OpenApiFileName, resultingManifest.Document.Runtimes.OfType().First().Spec.Url); } + private const string ManifestFileName = "client-microsoft.json"; + private const string OpenApiFileName = "openapi.yml"; } From 393179b1f04e075816b712519e74772271db3536 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Wed, 1 May 2024 15:08:20 -0400 Subject: [PATCH 6/7] - code linting Signed-off-by: Vincent Biret --- src/Kiota.Builder/Plugins/PluginsGenerationService.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Kiota.Builder/Plugins/PluginsGenerationService.cs b/src/Kiota.Builder/Plugins/PluginsGenerationService.cs index 1e66fce8f5..dc290eeced 100644 --- a/src/Kiota.Builder/Plugins/PluginsGenerationService.cs +++ b/src/Kiota.Builder/Plugins/PluginsGenerationService.cs @@ -33,7 +33,7 @@ public PluginsGenerationService(OpenApiDocument document, OpenApiUrlTreeNode ope } private static readonly OpenAPIRuntimeComparer _openAPIRuntimeComparer = new(); private const string ManifestFileNameSuffix = ".json"; - private const string DescriptionRelativePath = "./openapi.yml"; + private const string DescriptionRelativePath = "openapi.yml"; public async Task GenerateManifestAsync(CancellationToken cancellationToken = default) { // write the decription @@ -136,7 +136,7 @@ descriptionExtension is OpenApiDescriptionForModelExtension extension && Auth = new AnonymousAuth(), Spec = new OpenApiRuntimeSpec() { - Url = openApiDocumentPath.TrimStart('.').TrimStart('/'), + Url = openApiDocumentPath, }, RunForFunctions = [operation.OperationId] }); From 1bdfbf8a09fa471c74de2ef829709a977d8281fd Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Wed, 1 May 2024 15:46:02 -0400 Subject: [PATCH 7/7] - fixes workspace absolute path for relative descriptions --- CHANGELOG.md | 5 ++-- .../Configuration/GenerationConfiguration.cs | 14 +++++++++-- src/Kiota.Builder/KiotaBuilder.cs | 2 +- .../Plugins/PluginsGenerationService.cs | 11 +++++--- .../BaseApiConsumerConfiguration.cs | 7 +++++- .../WorkspaceManagementService.cs | 25 ++++++++++++------- .../GenerationConfigurationTests.cs | 5 ++-- .../Plugins/PluginsGenerationServiceTests.cs | 14 ++++++----- 8 files changed, 56 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e181101aeb..9436c70e4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,11 +17,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixed a bug where TypeScript deserialization would fail on Uppercase properties.[#4479](https://github.com/microsoft/kiota/issues/4479) - Changed URI template generation to reuse templates when required templates are absent across operations. -- Fixed path deduplication logic to avoid double `Id` suffixes in indexer names in scenarios where the `Id` suffix is already present.[#4519](https://github.com/microsoft/kiota/issues/4519) +- Fixed path deduplication logic to avoid double `Id` suffixes in indexer names in scenarios where the `Id` suffix is already present.[#4519](https://github.com/microsoft/kiota/issues/4519) - Updated reserved name providers for Java and Php so that "object" can be escaped. -- Fixes request builder disambiguation when child nodes had different prefixes for different paths with same parameters.[#4448](https://github.com/microsoft/kiota/issues/4448) +- Fixes request builder disambiguation when child nodes had different prefixes for different paths with same parameters.[#4448](https://github.com/microsoft/kiota/issues/4448) - Do not generate CS8603 warnings when enabling backing store in CSharp generation. - Fixed excluding operation. [#4399](https://github.com/microsoft/kiota/issues/4399) +- Fided a bug where absolute path would be used for workspace for local descriptions. [#4582](https://github.com/microsoft/kiota/issues/4582) [#4581](https://github.com/microsoft/kiota/issues/4581) - Fixed plugin generation of `ApiManifest` type to not include the `x-ms-kiota-hash` in the generated plugin. [#4561](https://github.com/microsoft/kiota/issues/4561) ## [1.13.0] - 2024-04-04 diff --git a/src/Kiota.Builder/Configuration/GenerationConfiguration.cs b/src/Kiota.Builder/Configuration/GenerationConfiguration.cs index 79d426987c..d00d410f9b 100644 --- a/src/Kiota.Builder/Configuration/GenerationConfiguration.cs +++ b/src/Kiota.Builder/Configuration/GenerationConfiguration.cs @@ -173,11 +173,11 @@ internal void UpdateConfigurationFromLanguagesInformation(LanguagesInformation l StructuredMimeTypes = new(languageInfo.StructuredMimeTypes); } public const string KiotaHashManifestExtensionKey = "x-ms-kiota-hash"; - public ApiDependency ToApiDependency(string configurationHash, Dictionary> templatesWithOperations) + public ApiDependency ToApiDependency(string configurationHash, Dictionary> templatesWithOperations, string targetDirectory) { var dependency = new ApiDependency() { - ApiDescriptionUrl = OpenAPIFilePath, + ApiDescriptionUrl = NormalizeDescriptionLocation(targetDirectory), ApiDeploymentBaseUrl = ApiRootUrl?.EndsWith('/') ?? false ? ApiRootUrl : $"{ApiRootUrl}/", Extensions = new(), Requests = templatesWithOperations.SelectMany(static x => x.Value.Select(y => new RequestInfo { Method = y.ToUpperInvariant(), UriTemplate = x.Key.DeSanitizeUrlTemplateParameter() })).ToList(), @@ -189,6 +189,16 @@ public ApiDependency ToApiDependency(string configurationHash, Dictionary PluginTypes.Count != 0; } #pragma warning restore CA1056 diff --git a/src/Kiota.Builder/KiotaBuilder.cs b/src/Kiota.Builder/KiotaBuilder.cs index c17d1c51ca..5bcbcf7881 100644 --- a/src/Kiota.Builder/KiotaBuilder.cs +++ b/src/Kiota.Builder/KiotaBuilder.cs @@ -237,7 +237,7 @@ public async Task GeneratePluginAsync(CancellationToken cancellationToken) throw new InvalidOperationException("The OpenAPI document and the URL tree must be loaded before generating the plugins"); // generate plugin sw.Start(); - var pluginsService = new PluginsGenerationService(openApiDocument, openApiTree, config); + var pluginsService = new PluginsGenerationService(openApiDocument, openApiTree, config, Directory.GetCurrentDirectory()); await pluginsService.GenerateManifestAsync(cancellationToken).ConfigureAwait(false); StopLogAndReset(sw, $"step {++stepId} - generate plugin - took"); return stepId; diff --git a/src/Kiota.Builder/Plugins/PluginsGenerationService.cs b/src/Kiota.Builder/Plugins/PluginsGenerationService.cs index 93cc546e55..90085c7d2a 100644 --- a/src/Kiota.Builder/Plugins/PluginsGenerationService.cs +++ b/src/Kiota.Builder/Plugins/PluginsGenerationService.cs @@ -21,22 +21,25 @@ public class PluginsGenerationService private readonly OpenApiDocument OAIDocument; private readonly OpenApiUrlTreeNode TreeNode; private readonly GenerationConfiguration Configuration; + private readonly string WorkingDirectory; - public PluginsGenerationService(OpenApiDocument document, OpenApiUrlTreeNode openApiUrlTreeNode, GenerationConfiguration configuration) + public PluginsGenerationService(OpenApiDocument document, OpenApiUrlTreeNode openApiUrlTreeNode, GenerationConfiguration configuration, string workingDirectory) { ArgumentNullException.ThrowIfNull(document); ArgumentNullException.ThrowIfNull(openApiUrlTreeNode); ArgumentNullException.ThrowIfNull(configuration); + ArgumentException.ThrowIfNullOrEmpty(workingDirectory); OAIDocument = document; TreeNode = openApiUrlTreeNode; Configuration = configuration; + WorkingDirectory = workingDirectory; } private static readonly OpenAPIRuntimeComparer _openAPIRuntimeComparer = new(); private const string ManifestFileNameSuffix = ".json"; private const string DescriptionRelativePath = "./openapi.yml"; public async Task GenerateManifestAsync(CancellationToken cancellationToken = default) { - // write the decription + // write the description var descriptionFullPath = Path.Combine(Configuration.OutputPath, DescriptionRelativePath); var directory = Path.GetDirectoryName(descriptionFullPath); if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory)) @@ -67,7 +70,7 @@ public async Task GenerateManifestAsync(CancellationToken cancellationToken = de case PluginType.APIManifest: var apiManifest = new ApiManifestDocument("application"); //TODO add application name // pass empty cong hash so that its not included in this manifest. - apiManifest.ApiDependencies.AddOrReplace(Configuration.ClientClassName, Configuration.ToApiDependency(string.Empty, TreeNode?.GetRequestInfo().ToDictionary(static x => x.Key, static x => x.Value) ?? [])); + apiManifest.ApiDependencies.AddOrReplace(Configuration.ClientClassName, Configuration.ToApiDependency(string.Empty, TreeNode?.GetRequestInfo().ToDictionary(static x => x.Key, static x => x.Value) ?? [], WorkingDirectory)); apiManifest.Write(writer); break; case PluginType.OpenAI://TODO add support for OpenAI plugin type generation @@ -136,7 +139,7 @@ descriptionExtension is OpenApiDescriptionForModelExtension extension && Auth = new AnonymousAuth(), Spec = new OpenApiRuntimeSpec() { - Url = openApiDocumentPath + Url = openApiDocumentPath, }, RunForFunctions = [operation.OperationId] }); diff --git a/src/Kiota.Builder/WorkspaceManagement/BaseApiConsumerConfiguration.cs b/src/Kiota.Builder/WorkspaceManagement/BaseApiConsumerConfiguration.cs index e2a9487f5e..fa0e320a9c 100644 --- a/src/Kiota.Builder/WorkspaceManagement/BaseApiConsumerConfiguration.cs +++ b/src/Kiota.Builder/WorkspaceManagement/BaseApiConsumerConfiguration.cs @@ -38,11 +38,16 @@ private protected BaseApiConsumerConfiguration(GenerationConfiguration config) /// The output path for the generated code, related to the configuration file. /// public string OutputPath { get; set; } = string.Empty; - public void NormalizePaths(string targetDirectory) + public void NormalizeOutputPath(string targetDirectory) { if (Path.IsPathRooted(OutputPath)) OutputPath = "./" + Path.GetRelativePath(targetDirectory, OutputPath); } + public void NormalizeDescriptionLocation(string targetDirectory) + { + if (Path.IsPathRooted(DescriptionLocation) && Path.GetFullPath(DescriptionLocation).StartsWith(Path.GetFullPath(targetDirectory), StringComparison.Ordinal) && !DescriptionLocation.StartsWith("http", StringComparison.OrdinalIgnoreCase)) + DescriptionLocation = "./" + Path.GetRelativePath(targetDirectory, DescriptionLocation); + } protected void CloneBase(BaseApiConsumerConfiguration target) { ArgumentNullException.ThrowIfNull(target); diff --git a/src/Kiota.Builder/WorkspaceManagement/WorkspaceManagementService.cs b/src/Kiota.Builder/WorkspaceManagement/WorkspaceManagementService.cs index 7248cc9adb..f538b073f4 100644 --- a/src/Kiota.Builder/WorkspaceManagement/WorkspaceManagementService.cs +++ b/src/Kiota.Builder/WorkspaceManagement/WorkspaceManagementService.cs @@ -51,14 +51,16 @@ private BaseApiConsumerConfiguration UpdateConsumerConfiguration(GenerationConfi if (generationConfiguration.IsPluginConfiguration) { var generationPluginConfig = new ApiPluginConfiguration(generationConfiguration); - generationPluginConfig.NormalizePaths(WorkingDirectory); + generationPluginConfig.NormalizeOutputPath(WorkingDirectory); + generationPluginConfig.NormalizeDescriptionLocation(WorkingDirectory); wsConfig.Plugins.AddOrReplace(generationConfiguration.ClientClassName, generationPluginConfig); return generationPluginConfig; } else { var generationClientConfig = new ApiClientConfiguration(generationConfiguration); - generationClientConfig.NormalizePaths(WorkingDirectory); + generationClientConfig.NormalizeOutputPath(WorkingDirectory); + generationClientConfig.NormalizeDescriptionLocation(WorkingDirectory); wsConfig.Clients.AddOrReplace(generationConfiguration.ClientClassName, generationClientConfig); return generationClientConfig; } @@ -70,8 +72,9 @@ public async Task UpdateStateFromConfigurationAsync(GenerationConfiguration gene { var (wsConfig, manifest) = await LoadConfigurationAndManifestAsync(cancellationToken).ConfigureAwait(false); var generationClientConfig = UpdateConsumerConfiguration(generationConfiguration, wsConfig); + generationClientConfig.NormalizeDescriptionLocation(WorkingDirectory); var inputConfigurationHash = await GetConsumerConfigurationHashAsync(generationClientConfig, descriptionHash).ConfigureAwait(false); - manifest.ApiDependencies.AddOrReplace(generationConfiguration.ClientClassName, generationConfiguration.ToApiDependency(inputConfigurationHash, templatesWithOperations)); + manifest.ApiDependencies.AddOrReplace(generationConfiguration.ClientClassName, generationConfiguration.ToApiDependency(inputConfigurationHash, templatesWithOperations, WorkingDirectory)); await workspaceConfigurationStorageService.UpdateWorkspaceConfigurationAsync(wsConfig, manifest, cancellationToken).ConfigureAwait(false); if (descriptionStream != Stream.Null) await descriptionStorageService.UpdateDescriptionAsync(generationConfiguration.ClientClassName, descriptionStream, new Uri(generationConfiguration.OpenAPIFilePath).GetFileExtension(), cancellationToken).ConfigureAwait(false); @@ -116,19 +119,21 @@ public async Task ShouldGenerateAsync(GenerationConfiguration inputConfig, apiManifest.ApiDependencies.TryGetValue(inputConfig.ClientClassName, out var existingApiManifest)) { var inputClientConfig = new ApiClientConfiguration(inputConfig); - inputClientConfig.NormalizePaths(WorkingDirectory); + inputClientConfig.NormalizeOutputPath(WorkingDirectory); + inputClientConfig.NormalizeDescriptionLocation(WorkingDirectory); var inputConfigurationHash = await GetConsumerConfigurationHashAsync(inputClientConfig, descriptionHash).ConfigureAwait(false); return !clientConfigurationComparer.Equals(existingClientConfig, inputClientConfig) || - !apiDependencyComparer.Equals(inputConfig.ToApiDependency(inputConfigurationHash, []), existingApiManifest); + !apiDependencyComparer.Equals(inputConfig.ToApiDependency(inputConfigurationHash, [], WorkingDirectory), existingApiManifest); } if (wsConfig.Plugins.TryGetValue(inputConfig.ClientClassName, out var existingPluginConfig) && apiManifest.ApiDependencies.TryGetValue(inputConfig.ClientClassName, out var existingPluginApiManifest)) { var inputClientConfig = new ApiPluginConfiguration(inputConfig); - inputClientConfig.NormalizePaths(WorkingDirectory); + inputClientConfig.NormalizeOutputPath(WorkingDirectory); + inputClientConfig.NormalizeDescriptionLocation(WorkingDirectory); var inputConfigurationHash = await GetConsumerConfigurationHashAsync(inputClientConfig, descriptionHash).ConfigureAwait(false); return !pluginConfigurationComparer.Equals(existingPluginConfig, inputClientConfig) || - !apiDependencyComparer.Equals(inputConfig.ToApiDependency(inputConfigurationHash, []), existingPluginApiManifest); + !apiDependencyComparer.Equals(inputConfig.ToApiDependency(inputConfigurationHash, [], WorkingDirectory), existingPluginApiManifest); } return true; } @@ -292,7 +297,8 @@ public async Task> MigrateFromLockFileAsync(string clientNam await descriptionStorageService.UpdateDescriptionAsync(generationConfiguration.ClientClassName, ms, new Uri(generationConfiguration.OpenAPIFilePath).GetFileExtension(), cancellationToken).ConfigureAwait(false); var clientConfiguration = new ApiClientConfiguration(generationConfiguration); - clientConfiguration.NormalizePaths(WorkingDirectory); + clientConfiguration.NormalizeOutputPath(WorkingDirectory); + clientConfiguration.NormalizeDescriptionLocation(WorkingDirectory); wsConfig.Clients.Add(generationConfiguration.ClientClassName, clientConfiguration); var inputConfigurationHash = await GetConsumerConfigurationHashAsync(clientConfiguration, "migrated-pending-generate").ConfigureAwait(false); // because it's a migration, we don't want to calculate the exact hash since the description might have changed since the initial generation that created the lock file @@ -302,7 +308,8 @@ public async Task> MigrateFromLockFileAsync(string clientNam inputConfigurationHash, new Dictionary> { { MigrationPlaceholderPath, new HashSet { "GET" } } - })); + }, + WorkingDirectory)); lockManagementService.DeleteLockFile(Path.Combine(WorkingDirectory, clientConfiguration.OutputPath)); } await workspaceConfigurationStorageService.UpdateWorkspaceConfigurationAsync(wsConfig, apiManifest, cancellationToken).ConfigureAwait(false); diff --git a/tests/Kiota.Builder.Tests/Configuration/GenerationConfigurationTests.cs b/tests/Kiota.Builder.Tests/Configuration/GenerationConfigurationTests.cs index 6e3e95e9b5..268ec12326 100644 --- a/tests/Kiota.Builder.Tests/Configuration/GenerationConfigurationTests.cs +++ b/tests/Kiota.Builder.Tests/Configuration/GenerationConfigurationTests.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.IO; using System.Linq; using Kiota.Builder.Configuration; using Xunit; @@ -34,7 +35,7 @@ public void ToApiDependency() }; var apiDependency = generationConfiguration.ToApiDependency("foo", new Dictionary>{ { "foo/bar", new HashSet{"GET"}} - }); + }, Path.GetTempPath()); Assert.NotNull(apiDependency); Assert.NotNull(apiDependency.Extensions); Assert.Equal("foo", apiDependency.Extensions[GenerationConfiguration.KiotaHashManifestExtensionKey].GetValue()); @@ -54,7 +55,7 @@ public void ToApiDependencyDoesNotIncludeConfigHashIfEmpty() }; var apiDependency = generationConfiguration.ToApiDependency(string.Empty, new Dictionary>{ { "foo/bar", new HashSet{"GET"}} - }); + }, Path.GetTempPath()); Assert.NotNull(apiDependency); Assert.NotNull(apiDependency.Extensions); Assert.False(apiDependency.Extensions.ContainsKey(GenerationConfiguration.KiotaHashManifestExtensionKey)); diff --git a/tests/Kiota.Builder.Tests/Plugins/PluginsGenerationServiceTests.cs b/tests/Kiota.Builder.Tests/Plugins/PluginsGenerationServiceTests.cs index 77e2b4a638..5764e672c0 100644 --- a/tests/Kiota.Builder.Tests/Plugins/PluginsGenerationServiceTests.cs +++ b/tests/Kiota.Builder.Tests/Plugins/PluginsGenerationServiceTests.cs @@ -16,9 +16,10 @@ public sealed class PluginsGenerationServiceTests : IDisposable [Fact] public void Defensive() { - Assert.Throws(() => new PluginsGenerationService(null, OpenApiUrlTreeNode.Create(), new())); - Assert.Throws(() => new PluginsGenerationService(new(), null, new())); - Assert.Throws(() => new PluginsGenerationService(new(), OpenApiUrlTreeNode.Create(), null)); + Assert.Throws(() => new PluginsGenerationService(null, OpenApiUrlTreeNode.Create(), new(), "foo")); + Assert.Throws(() => new PluginsGenerationService(new(), null, new(), "foo")); + Assert.Throws(() => new PluginsGenerationService(new(), OpenApiUrlTreeNode.Create(), null, "foo")); + Assert.Throws(() => new PluginsGenerationService(new(), OpenApiUrlTreeNode.Create(), new(), string.Empty)); } public void Dispose() @@ -43,11 +44,12 @@ public async Task GeneratesManifest() responses: '200': description: test"; - var simpleDescriptionPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()) + ".yaml"; + var workingDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + var simpleDescriptionPath = Path.Combine(workingDirectory) + "description.yaml"; await File.WriteAllTextAsync(simpleDescriptionPath, simpleDescriptionContent); var mockLogger = new Mock>(); var openAPIDocumentDS = new OpenApiDocumentDownloadService(_httpClient, mockLogger.Object); - var outputDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + var outputDirectory = Path.Combine(workingDirectory, "output"); var generationConfiguration = new GenerationConfiguration { OutputPath = outputDirectory, @@ -60,7 +62,7 @@ public async Task GeneratesManifest() var openApiDocument = await openAPIDocumentDS.GetDocumentFromStreamAsync(openAPIDocumentStream, generationConfiguration); var urlTreeNode = OpenApiUrlTreeNode.Create(openApiDocument, Constants.DefaultOpenApiLabel); - var pluginsGenerationService = new PluginsGenerationService(openApiDocument, urlTreeNode, generationConfiguration); + var pluginsGenerationService = new PluginsGenerationService(openApiDocument, urlTreeNode, generationConfiguration, workingDirectory); await pluginsGenerationService.GenerateManifestAsync(); Assert.True(File.Exists(Path.Combine(outputDirectory, "client-microsoft.json")));