From a938048427fa9f38bdaeb966554b268d76c84311 Mon Sep 17 00:00:00 2001 From: Charles Wahome Date: Mon, 11 Nov 2024 15:38:36 +0300 Subject: [PATCH 01/16] initial validation --- .../Plugins/PluginsGenerationService.cs | 42 ++++++++++++++++++- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/src/Kiota.Builder/Plugins/PluginsGenerationService.cs b/src/Kiota.Builder/Plugins/PluginsGenerationService.cs index ef638601fa..75114f69b7 100644 --- a/src/Kiota.Builder/Plugins/PluginsGenerationService.cs +++ b/src/Kiota.Builder/Plugins/PluginsGenerationService.cs @@ -348,10 +348,20 @@ private static (OpenApiRuntime[], Function[], ConversationStarter[]) GetRuntimes { foreach (var operation in pathItem.Operations.Values.Where(static x => !string.IsNullOrEmpty(x.OperationId))) { + var auth = configAuth; + try + { + auth = configAuth ?? GetAuth(operation.Security ?? document.SecurityRequirements); + } + catch (UnsupportedSecuritySchemeException) + { + throw; + } + runtimes.Add(new OpenApiRuntime { // Configuration overrides document information - Auth = configAuth ?? GetAuth(operation.Security ?? document.SecurityRequirements), + Auth = auth, Spec = new OpenApiRuntimeSpec { Url = openApiDocumentPath }, RunForFunctions = [operation.OperationId] }); @@ -391,7 +401,7 @@ private static Auth GetAuth(IList securityRequiremen const string tooManySchemesError = "Multiple security requirements are not supported. Operations can only list one security requirement."; if (securityRequirements.Count > 1 || securityRequirements.FirstOrDefault()?.Keys.Count > 1) { - throw new InvalidOperationException(tooManySchemesError); + throw new UnsupportedSecuritySchemeException(tooManySchemesError); } var security = securityRequirements.FirstOrDefault(); var opSecurity = security?.Keys.FirstOrDefault(); @@ -454,4 +464,32 @@ rExtRaw is T rExt && return null; } + + private void ValidateAuthentication(OpenApiOperation operation, IList securityRequirements) + { + if (operation is null) return; + if (securityRequirements != null ) + { + if (securityRequirements.Count > 1 || securityRequirements.FirstOrDefault()?.Keys.Count > 1) + { + Console.WriteLine($"{operation.OperationId}: \"There appear to be no compatible security schemes for Copilot usage.\""); + } + + var securityScheme = securityRequirements.FirstOrDefault()?.Keys.FirstOrDefault(); + if (securityScheme != null && !IsSupportedAuthScheme(securityScheme)) + { + Console.WriteLine($"{operation.OperationId}: \"There appear to be no compatible security schemes for Copilot usage.\""); + } + } + } + + private bool IsSupportedAuthScheme(OpenApiSecurityScheme securityScheme) + { + return securityScheme.Type switch + { + SecuritySchemeType.Http when securityScheme.Scheme.Equals("bearer", StringComparison.OrdinalIgnoreCase) => true, + SecuritySchemeType.OAuth2 => true, + _ => false, + }; + } } From ebec1ebab589c80e69e2c1bf8a38a30fc06313b7 Mon Sep 17 00:00:00 2001 From: thewahome Date: Mon, 11 Nov 2024 16:00:04 +0300 Subject: [PATCH 02/16] change thrown error when multiple schemes exist --- .../Plugins/PluginsGenerationServiceTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Kiota.Builder.Tests/Plugins/PluginsGenerationServiceTests.cs b/tests/Kiota.Builder.Tests/Plugins/PluginsGenerationServiceTests.cs index 3f74656037..aa13807142 100644 --- a/tests/Kiota.Builder.Tests/Plugins/PluginsGenerationServiceTests.cs +++ b/tests/Kiota.Builder.Tests/Plugins/PluginsGenerationServiceTests.cs @@ -490,7 +490,7 @@ public static TheoryData { - await Assert.ThrowsAsync(async () => + await Assert.ThrowsAsync(async () => { await action(); }); From 8ac37bffd3cef0aa3b62bcfe8c5a81a48628db1f Mon Sep 17 00:00:00 2001 From: thewahome Date: Wed, 13 Nov 2024 14:24:24 +0300 Subject: [PATCH 03/16] pass anonymous auth when exception is thrown --- src/Kiota.Builder/Plugins/PluginsGenerationService.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Kiota.Builder/Plugins/PluginsGenerationService.cs b/src/Kiota.Builder/Plugins/PluginsGenerationService.cs index 75114f69b7..8901f2ca4b 100644 --- a/src/Kiota.Builder/Plugins/PluginsGenerationService.cs +++ b/src/Kiota.Builder/Plugins/PluginsGenerationService.cs @@ -355,7 +355,8 @@ private static (OpenApiRuntime[], Function[], ConversationStarter[]) GetRuntimes } catch (UnsupportedSecuritySchemeException) { - throw; + // log a warning here that the operation security is not supported + auth = new AnonymousAuth(); } runtimes.Add(new OpenApiRuntime From 3269e17bfb5aa8be01cc14b26231f565fa5402fe Mon Sep 17 00:00:00 2001 From: thewahome Date: Wed, 13 Nov 2024 15:31:55 +0300 Subject: [PATCH 04/16] remove unnecessary validation functions --- .../Plugins/PluginsGenerationService.cs | 28 ------------------- 1 file changed, 28 deletions(-) diff --git a/src/Kiota.Builder/Plugins/PluginsGenerationService.cs b/src/Kiota.Builder/Plugins/PluginsGenerationService.cs index 8901f2ca4b..f5a5b5488d 100644 --- a/src/Kiota.Builder/Plugins/PluginsGenerationService.cs +++ b/src/Kiota.Builder/Plugins/PluginsGenerationService.cs @@ -465,32 +465,4 @@ rExtRaw is T rExt && return null; } - - private void ValidateAuthentication(OpenApiOperation operation, IList securityRequirements) - { - if (operation is null) return; - if (securityRequirements != null ) - { - if (securityRequirements.Count > 1 || securityRequirements.FirstOrDefault()?.Keys.Count > 1) - { - Console.WriteLine($"{operation.OperationId}: \"There appear to be no compatible security schemes for Copilot usage.\""); - } - - var securityScheme = securityRequirements.FirstOrDefault()?.Keys.FirstOrDefault(); - if (securityScheme != null && !IsSupportedAuthScheme(securityScheme)) - { - Console.WriteLine($"{operation.OperationId}: \"There appear to be no compatible security schemes for Copilot usage.\""); - } - } - } - - private bool IsSupportedAuthScheme(OpenApiSecurityScheme securityScheme) - { - return securityScheme.Type switch - { - SecuritySchemeType.Http when securityScheme.Scheme.Equals("bearer", StringComparison.OrdinalIgnoreCase) => true, - SecuritySchemeType.OAuth2 => true, - _ => false, - }; - } } From 0d7ee417e0adb53864ea48912f620fe0ce4de211 Mon Sep 17 00:00:00 2001 From: thewahome Date: Wed, 13 Nov 2024 18:20:55 +0300 Subject: [PATCH 05/16] Add tests to support previously failing schemes --- .../Plugins/PluginsGenerationServiceTests.cs | 118 ++++-------------- 1 file changed, 26 insertions(+), 92 deletions(-) diff --git a/tests/Kiota.Builder.Tests/Plugins/PluginsGenerationServiceTests.cs b/tests/Kiota.Builder.Tests/Plugins/PluginsGenerationServiceTests.cs index aa13807142..d76b656861 100644 --- a/tests/Kiota.Builder.Tests/Plugins/PluginsGenerationServiceTests.cs +++ b/tests/Kiota.Builder.Tests/Plugins/PluginsGenerationServiceTests.cs @@ -408,7 +408,33 @@ public static TheoryData(auth0); } + }, + // multiple security schemes in operation object + { + "{securitySchemes: {apiKey0: {type: apiKey, name: x-api-key0, in: header}, apiKey1: {type: apiKey, name: x-api-key1, in: header}}}", + string.Empty, "security: [apiKey0: [], apiKey1: []]", null, resultingManifest => + { + Assert.NotNull(resultingManifest.Document); + Assert.Empty(resultingManifest.Problems); + Assert.NotEmpty(resultingManifest.Document.Runtimes); + var auth0 = resultingManifest.Document.Runtimes[0].Auth; + Assert.IsType(auth0); + } + }, + // Unsupported security scheme (http basic) + { + "{securitySchemes: {httpBasic0: {type: http, scheme: basic}}}", + string.Empty, "security: [httpBasic0: []]", null, resultingManifest => + { + Assert.NotNull(resultingManifest.Document); + Assert.Empty(resultingManifest.Problems); + Assert.NotEmpty(resultingManifest.Document.Runtimes); + var auth0 = resultingManifest.Document.Runtimes[0].Auth; + Assert.IsType(auth0); + } } + + }; } @@ -480,98 +506,6 @@ public async Task GeneratesManifestWithAuthAsync(string securitySchemesComponent } } - public static TheoryData, Task>> - SecurityInformationFail() - { - return new TheoryData, Task>> - { - // multiple security schemes in operation object - { - "{securitySchemes: {apiKey0: {type: apiKey, name: x-api-key0, in: header}, apiKey1: {type: apiKey, name: x-api-key1, in: header}}}", - string.Empty, "security: [apiKey0: [], apiKey1: []]", null, async (action) => - { - await Assert.ThrowsAsync(async () => - { - await action(); - }); - } - }, - // Unsupported security scheme (http basic) - { - "{securitySchemes: {httpBasic0: {type: http, scheme: basic}}}", - string.Empty, "security: [httpBasic0: []]", null, async (action) => - { - await Assert.ThrowsAsync(async () => - { - await action(); - }); - } - }, - }; - } - - [Theory] - [MemberData(nameof(SecurityInformationFail))] - public async Task FailsToGeneratesManifestWithInvalidAuthAsync(string securitySchemesComponent, string rootSecurity, - string operationSecurity, PluginAuthConfiguration pluginAuthConfiguration, Func, Task> assertions) - { - var apiDescription = $""" - openapi: 3.0.0 - info: - title: test - version: "1.0" - paths: - /test: - get: - description: description for test path - responses: - '200': - description: test - {operationSecurity} - {rootSecurity} - components: {securitySchemesComponent} - """; - var workingDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); - var simpleDescriptionPath = Path.Combine(workingDirectory) + "description.yaml"; - await File.WriteAllTextAsync(simpleDescriptionPath, apiDescription); - var mockLogger = new Mock>(); - var openApiDocumentDs = new OpenApiDocumentDownloadService(_httpClient, mockLogger.Object); - var outputDirectory = Path.Combine(workingDirectory, "output"); - var generationConfiguration = new GenerationConfiguration - { - OutputPath = outputDirectory, - OpenAPIFilePath = "openapiPath", - PluginTypes = [PluginType.APIPlugin], - ClientClassName = "client", - ApiRootUrl = "http://localhost/", //Kiota builder would set this for us - PluginAuthInformation = pluginAuthConfiguration, - }; - var (openApiDocumentStream, _) = - await openApiDocumentDs.LoadStreamAsync(simpleDescriptionPath, generationConfiguration, null, false); - var openApiDocument = - await openApiDocumentDs.GetDocumentFromStreamAsync(openApiDocumentStream, generationConfiguration); - Assert.NotNull(openApiDocument); - KiotaBuilder.CleanupOperationIdForPlugins(openApiDocument); - var urlTreeNode = OpenApiUrlTreeNode.Create(openApiDocument, Constants.DefaultOpenApiLabel); - - var pluginsGenerationService = - new PluginsGenerationService(openApiDocument, urlTreeNode, generationConfiguration, workingDirectory); - - await assertions(async () => - { - await pluginsGenerationService.GenerateManifestAsync(); - }); - // cleanup - try - { - Directory.Delete(outputDirectory); - } - catch (Exception) - { - // ignored - } - } - [Fact] public async Task GeneratesManifestWithMultipleSecuritySchemesAsync() { From 0d98e86d49f7ee8f1a3bdff568d033bda7bea4a6 Mon Sep 17 00:00:00 2001 From: Charles Wahome Date: Thu, 14 Nov 2024 13:55:55 +0300 Subject: [PATCH 06/16] enable logging to warn users --- src/Kiota.Builder/KiotaBuilder.cs | 2 +- .../Plugins/PluginsGenerationService.cs | 13 ++++---- .../Plugins/PluginsGenerationServiceTests.cs | 30 +++++++++---------- 3 files changed, 24 insertions(+), 21 deletions(-) diff --git a/src/Kiota.Builder/KiotaBuilder.cs b/src/Kiota.Builder/KiotaBuilder.cs index 193f336e99..fd4e0887c2 100644 --- a/src/Kiota.Builder/KiotaBuilder.cs +++ b/src/Kiota.Builder/KiotaBuilder.cs @@ -243,7 +243,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, Directory.GetCurrentDirectory()); + var pluginsService = new PluginsGenerationService(openApiDocument, openApiTree, config, Directory.GetCurrentDirectory(), logger); 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 f5a5b5488d..c16f94236f 100644 --- a/src/Kiota.Builder/Plugins/PluginsGenerationService.cs +++ b/src/Kiota.Builder/Plugins/PluginsGenerationService.cs @@ -9,6 +9,7 @@ using Kiota.Builder.Configuration; using Kiota.Builder.Extensions; using Kiota.Builder.OpenApiExtensions; +using Microsoft.Extensions.Logging; using Microsoft.OpenApi.ApiManifest; using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Services; @@ -23,9 +24,10 @@ public partial class PluginsGenerationService private readonly OpenApiUrlTreeNode TreeNode; private readonly GenerationConfiguration Configuration; private readonly string WorkingDirectory; + private readonly ILogger Logger; public PluginsGenerationService(OpenApiDocument document, OpenApiUrlTreeNode openApiUrlTreeNode, - GenerationConfiguration configuration, string workingDirectory) + GenerationConfiguration configuration, string workingDirectory, ILogger logger) { ArgumentNullException.ThrowIfNull(document); ArgumentNullException.ThrowIfNull(openApiUrlTreeNode); @@ -35,6 +37,7 @@ public PluginsGenerationService(OpenApiDocument document, OpenApiUrlTreeNode ope TreeNode = openApiUrlTreeNode; Configuration = configuration; WorkingDirectory = workingDirectory; + Logger = logger; } private static readonly OpenAPIRuntimeComparer _openAPIRuntimeComparer = new(); @@ -258,7 +261,7 @@ private OpenApiDocument GetDocumentWithTrimmedComponentsAndResponses(OpenApiDocu private PluginManifestDocument GetManifestDocument(string openApiDocumentPath) { - var (runtimes, functions, conversationStarters) = GetRuntimesFunctionsAndConversationStartersFromTree(OAIDocument, Configuration.PluginAuthInformation, TreeNode, openApiDocumentPath); + var (runtimes, functions, conversationStarters) = GetRuntimesFunctionsAndConversationStartersFromTree(OAIDocument, Configuration.PluginAuthInformation, TreeNode, openApiDocumentPath, Logger); var descriptionForHuman = OAIDocument.Info?.Description is string d && !string.IsNullOrEmpty(d) ? d : $"Description for {OAIDocument.Info?.Title}"; var manifestInfo = ExtractInfoFromDocument(OAIDocument.Info); var pluginManifestDocument = new PluginManifestDocument @@ -338,7 +341,7 @@ private sealed record OpenApiManifestInfo( string ContactEmail = DefaultContactEmail); private static (OpenApiRuntime[], Function[], ConversationStarter[]) GetRuntimesFunctionsAndConversationStartersFromTree(OpenApiDocument document, PluginAuthConfiguration? authInformation, OpenApiUrlTreeNode currentNode, - string openApiDocumentPath) + string openApiDocumentPath, ILogger logger) { var runtimes = new List(); var functions = new List(); @@ -355,7 +358,7 @@ private static (OpenApiRuntime[], Function[], ConversationStarter[]) GetRuntimes } catch (UnsupportedSecuritySchemeException) { - // log a warning here that the operation security is not supported + logger.LogWarning("Unsupported security scheme found in operation '{OperationId}'. Using anonymous auth.", operation.OperationId); auth = new AnonymousAuth(); } @@ -387,7 +390,7 @@ private static (OpenApiRuntime[], Function[], ConversationStarter[]) GetRuntimes foreach (var node in currentNode.Children) { - var (childRuntimes, childFunctions, childConversationStarters) = GetRuntimesFunctionsAndConversationStartersFromTree(document, authInformation, node.Value, openApiDocumentPath); + var (childRuntimes, childFunctions, childConversationStarters) = GetRuntimesFunctionsAndConversationStartersFromTree(document, authInformation, node.Value, openApiDocumentPath, logger); runtimes.AddRange(childRuntimes); functions.AddRange(childFunctions); conversationStarters.AddRange(childConversationStarters); diff --git a/tests/Kiota.Builder.Tests/Plugins/PluginsGenerationServiceTests.cs b/tests/Kiota.Builder.Tests/Plugins/PluginsGenerationServiceTests.cs index d76b656861..cd5af624ad 100644 --- a/tests/Kiota.Builder.Tests/Plugins/PluginsGenerationServiceTests.cs +++ b/tests/Kiota.Builder.Tests/Plugins/PluginsGenerationServiceTests.cs @@ -19,14 +19,14 @@ namespace Kiota.Builder.Tests.Plugins; public sealed class PluginsGenerationServiceTests : IDisposable { private readonly HttpClient _httpClient = new(); - + private readonly ILogger _logger = new Mock>().Object; [Fact] public void Defensive() { - 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)); + Assert.Throws(() => new PluginsGenerationService(null, OpenApiUrlTreeNode.Create(), new(), "foo", _logger)); + Assert.Throws(() => new PluginsGenerationService(new(), null, new(), "foo", _logger)); + Assert.Throws(() => new PluginsGenerationService(new(), OpenApiUrlTreeNode.Create(), null, "foo", _logger)); + Assert.Throws(() => new PluginsGenerationService(new(), OpenApiUrlTreeNode.Create(), new(), string.Empty, _logger)); } public void Dispose() @@ -76,7 +76,7 @@ public async Task GeneratesManifestAsync(string inputPluginName, string expected var simpleDescriptionPath = Path.Combine(workingDirectory) + "description.yaml"; await File.WriteAllTextAsync(simpleDescriptionPath, simpleDescriptionContent); var mockLogger = new Mock>(); - var openAPIDocumentDS = new OpenApiDocumentDownloadService(_httpClient, mockLogger.Object); + var openAPIDocumentDS = new OpenApiDocumentDownloadService(_httpClient, _logger); var outputDirectory = Path.Combine(workingDirectory, "output"); var generationConfiguration = new GenerationConfiguration { @@ -91,7 +91,7 @@ public async Task GeneratesManifestAsync(string inputPluginName, string expected KiotaBuilder.CleanupOperationIdForPlugins(openApiDocument); var urlTreeNode = OpenApiUrlTreeNode.Create(openApiDocument, Constants.DefaultOpenApiLabel); - var pluginsGenerationService = new PluginsGenerationService(openApiDocument, urlTreeNode, generationConfiguration, workingDirectory); + var pluginsGenerationService = new PluginsGenerationService(openApiDocument, urlTreeNode, generationConfiguration, workingDirectory, _logger); await pluginsGenerationService.GenerateManifestAsync(); Assert.True(File.Exists(Path.Combine(outputDirectory, $"{expectedPluginName.ToLower()}-apiplugin.json"))); @@ -211,7 +211,7 @@ public async Task GeneratesManifestAndCleansUpInputDescriptionAsync() var simpleDescriptionPath = Path.Combine(workingDirectory) + "description.yaml"; await File.WriteAllTextAsync(simpleDescriptionPath, simpleDescriptionContent); var mockLogger = new Mock>(); - var openAPIDocumentDS = new OpenApiDocumentDownloadService(_httpClient, mockLogger.Object); + var openAPIDocumentDS = new OpenApiDocumentDownloadService(_httpClient, _logger); var outputDirectory = Path.Combine(workingDirectory, "output"); var generationConfiguration = new GenerationConfiguration { @@ -226,7 +226,7 @@ public async Task GeneratesManifestAndCleansUpInputDescriptionAsync() KiotaBuilder.CleanupOperationIdForPlugins(openApiDocument); var urlTreeNode = OpenApiUrlTreeNode.Create(openApiDocument, Constants.DefaultOpenApiLabel); - var pluginsGenerationService = new PluginsGenerationService(openApiDocument, urlTreeNode, generationConfiguration, workingDirectory); + var pluginsGenerationService = new PluginsGenerationService(openApiDocument, urlTreeNode, generationConfiguration, workingDirectory, _logger); await pluginsGenerationService.GenerateManifestAsync(); Assert.True(File.Exists(Path.Combine(outputDirectory, ManifestFileName))); @@ -463,7 +463,7 @@ public async Task GeneratesManifestWithAuthAsync(string securitySchemesComponent var simpleDescriptionPath = Path.Combine(workingDirectory) + "description.yaml"; await File.WriteAllTextAsync(simpleDescriptionPath, apiDescription); var mockLogger = new Mock>(); - var openApiDocumentDs = new OpenApiDocumentDownloadService(_httpClient, mockLogger.Object); + var openApiDocumentDs = new OpenApiDocumentDownloadService(_httpClient, _logger); var outputDirectory = Path.Combine(workingDirectory, "output"); var generationConfiguration = new GenerationConfiguration { @@ -483,7 +483,7 @@ public async Task GeneratesManifestWithAuthAsync(string securitySchemesComponent var urlTreeNode = OpenApiUrlTreeNode.Create(openApiDocument, Constants.DefaultOpenApiLabel); var pluginsGenerationService = - new PluginsGenerationService(openApiDocument, urlTreeNode, generationConfiguration, workingDirectory); + new PluginsGenerationService(openApiDocument, urlTreeNode, generationConfiguration, workingDirectory, _logger); await pluginsGenerationService.GenerateManifestAsync(); Assert.True(File.Exists(Path.Combine(outputDirectory, ManifestFileName))); @@ -542,7 +542,7 @@ public async Task GeneratesManifestWithMultipleSecuritySchemesAsync() var simpleDescriptionPath = Path.Combine(workingDirectory) + "description.yaml"; await File.WriteAllTextAsync(simpleDescriptionPath, apiDescription); var mockLogger = new Mock>(); - var openApiDocumentDs = new OpenApiDocumentDownloadService(_httpClient, mockLogger.Object); + var openApiDocumentDs = new OpenApiDocumentDownloadService(_httpClient, _logger); var outputDirectory = Path.Combine(workingDirectory, "output"); var generationConfiguration = new GenerationConfiguration { @@ -561,7 +561,7 @@ public async Task GeneratesManifestWithMultipleSecuritySchemesAsync() var urlTreeNode = OpenApiUrlTreeNode.Create(openApiDocument, Constants.DefaultOpenApiLabel); var pluginsGenerationService = - new PluginsGenerationService(openApiDocument, urlTreeNode, generationConfiguration, workingDirectory); + new PluginsGenerationService(openApiDocument, urlTreeNode, generationConfiguration, workingDirectory, _logger); await pluginsGenerationService.GenerateManifestAsync(); Assert.True(File.Exists(Path.Combine(outputDirectory, ManifestFileName))); @@ -748,7 +748,7 @@ public async Task MergesAllOfRequestBodyAsync(string content, Action>(); - var openAPIDocumentDS = new OpenApiDocumentDownloadService(_httpClient, mockLogger.Object); + var openAPIDocumentDS = new OpenApiDocumentDownloadService(_httpClient, _logger); var outputDirectory = Path.Combine(workingDirectory, "output"); var generationConfiguration = new GenerationConfiguration { @@ -763,7 +763,7 @@ public async Task MergesAllOfRequestBodyAsync(string content, Action Date: Thu, 14 Nov 2024 14:47:30 +0300 Subject: [PATCH 07/16] Set warning message --- 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 c16f94236f..333af49386 100644 --- a/src/Kiota.Builder/Plugins/PluginsGenerationService.cs +++ b/src/Kiota.Builder/Plugins/PluginsGenerationService.cs @@ -356,10 +356,10 @@ private static (OpenApiRuntime[], Function[], ConversationStarter[]) GetRuntimes { auth = configAuth ?? GetAuth(operation.Security ?? document.SecurityRequirements); } - catch (UnsupportedSecuritySchemeException) + catch (UnsupportedSecuritySchemeException e) { - logger.LogWarning("Unsupported security scheme found in operation '{OperationId}'. Using anonymous auth.", operation.OperationId); auth = new AnonymousAuth(); + logger.LogWarning("{OperationId}: {Message}. Using anonymous auth.", operation.OperationId, e.Message); } runtimes.Add(new OpenApiRuntime From 5cda4940aaa6d51512147b6c25ffb98e3b177e1a Mon Sep 17 00:00:00 2001 From: thewahome Date: Mon, 18 Nov 2024 14:59:42 +0300 Subject: [PATCH 08/16] change message to allow filtering --- 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 333af49386..a413b99e43 100644 --- a/src/Kiota.Builder/Plugins/PluginsGenerationService.cs +++ b/src/Kiota.Builder/Plugins/PluginsGenerationService.cs @@ -339,7 +339,7 @@ private sealed record OpenApiManifestInfo( string? LogoUrl = null, string? PrivacyUrl = null, string ContactEmail = DefaultContactEmail); - + private static (OpenApiRuntime[], Function[], ConversationStarter[]) GetRuntimesFunctionsAndConversationStartersFromTree(OpenApiDocument document, PluginAuthConfiguration? authInformation, OpenApiUrlTreeNode currentNode, string openApiDocumentPath, ILogger logger) { @@ -359,7 +359,7 @@ private static (OpenApiRuntime[], Function[], ConversationStarter[]) GetRuntimes catch (UnsupportedSecuritySchemeException e) { auth = new AnonymousAuth(); - logger.LogWarning("{OperationId}: {Message}. Using anonymous auth.", operation.OperationId, e.Message); + logger.LogWarning("Authentication warning. {OperationId} - {Message}. Using anonymous auth.", operation.OperationId, e.Message); } runtimes.Add(new OpenApiRuntime From e2e97c1511162f713e990f856b9563a0cc30b00b Mon Sep 17 00:00:00 2001 From: thewahome Date: Mon, 18 Nov 2024 15:37:27 +0300 Subject: [PATCH 09/16] Tag warning with categpry --- src/Kiota.Builder/Plugins/PluginsGenerationService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Kiota.Builder/Plugins/PluginsGenerationService.cs b/src/Kiota.Builder/Plugins/PluginsGenerationService.cs index a413b99e43..c4535d5c48 100644 --- a/src/Kiota.Builder/Plugins/PluginsGenerationService.cs +++ b/src/Kiota.Builder/Plugins/PluginsGenerationService.cs @@ -359,7 +359,7 @@ private static (OpenApiRuntime[], Function[], ConversationStarter[]) GetRuntimes catch (UnsupportedSecuritySchemeException e) { auth = new AnonymousAuth(); - logger.LogWarning("Authentication warning. {OperationId} - {Message}. Using anonymous auth.", operation.OperationId, e.Message); + logger.LogWarning("Authentication warning: {OperationId} - {Message}. Using anonymous auth.", operation.OperationId, e.Message); } runtimes.Add(new OpenApiRuntime From 0f1485de9ed1f8e184ded6c5458289dc83a6af16 Mon Sep 17 00:00:00 2001 From: thewahome Date: Mon, 18 Nov 2024 15:40:56 +0300 Subject: [PATCH 10/16] log authentication error warnings in output channel --- .../src/commands/generate/generateClientCommand.ts | 8 ++++++-- .../src/commands/generate/generation-util.ts | 12 ------------ .../src/commands/regenerate/regenerate.service.ts | 3 +-- vscode/microsoft-kiota/src/utilities/logging.ts | 13 ++++++++++++- 4 files changed, 19 insertions(+), 17 deletions(-) diff --git a/vscode/microsoft-kiota/src/commands/generate/generateClientCommand.ts b/vscode/microsoft-kiota/src/commands/generate/generateClientCommand.ts index d570a8bee8..632da647eb 100644 --- a/vscode/microsoft-kiota/src/commands/generate/generateClientCommand.ts +++ b/vscode/microsoft-kiota/src/commands/generate/generateClientCommand.ts @@ -15,12 +15,12 @@ import { GeneratedOutputState } from "../../types/GeneratedOutputState"; import { WorkspaceGenerationContext } from "../../types/WorkspaceGenerationContext"; import { getSanitizedString, getWorkspaceJsonDirectory, parseGenerationLanguage, parseGenerationType, parsePluginType, updateTreeViewIcons } from "../../util"; import { isDeeplinkEnabled, transformToGenerationConfig } from "../../utilities/deep-linking"; -import { exportLogsAndShowErrors } from "../../utilities/logging"; +import { checkForSuccess, exportLogsAndShowErrors, logFromLogLevel } from "../../utilities/logging"; import { showUpgradeWarningMessage } from "../../utilities/messaging"; import { Command } from "../Command"; import { generateClient } from "./generateClient"; import { generatePlugin } from "./generatePlugin"; -import { checkForSuccess, displayGenerationResults } from "./generation-util"; +import { displayGenerationResults } from "./generation-util"; import { getLanguageInformation, getLanguageInformationForDescription } from "./getLanguageInformation"; export class GenerateClientCommand extends Command { @@ -127,6 +127,10 @@ export class GenerateClientCommand extends Command { ); return; } + + const authenticationWarnings = getLogEntriesForLevel(result ?? [], LogLevel.warning).filter(entry => entry.message.startsWith('Authentication warning')); + authenticationWarnings?.forEach(logFromLogLevel); + if (result && getLogEntriesForLevel(result, LogLevel.critical, LogLevel.error).length === 0) { // Save state before opening the new window const outputState = { diff --git a/vscode/microsoft-kiota/src/commands/generate/generation-util.ts b/vscode/microsoft-kiota/src/commands/generate/generation-util.ts index a8782a072a..b3b407a823 100644 --- a/vscode/microsoft-kiota/src/commands/generate/generation-util.ts +++ b/vscode/microsoft-kiota/src/commands/generate/generation-util.ts @@ -1,22 +1,10 @@ import * as vscode from "vscode"; import { treeViewId } from "../../constants"; -import { KiotaLogEntry } from "../../kiotaInterop"; import { OpenApiTreeProvider } from "../../providers/openApiTreeProvider"; import { getWorkspaceJsonPath, updateTreeViewIcons } from "../../util"; import { loadWorkspaceFile } from "../../utilities/file"; -export async function checkForSuccess(results: KiotaLogEntry[]) { - for (const result of results) { - if (result && result.message) { - if (result.message.includes("Generation completed successfully")) { - return true; - } - } - } - return false; -} - export async function displayGenerationResults(openApiTreeProvider: OpenApiTreeProvider, config: any) { const clientNameOrPluginName = config.clientClassName || config.pluginName; openApiTreeProvider.refreshView(); diff --git a/vscode/microsoft-kiota/src/commands/regenerate/regenerate.service.ts b/vscode/microsoft-kiota/src/commands/regenerate/regenerate.service.ts index 139425d8ff..fa6456ed8b 100644 --- a/vscode/microsoft-kiota/src/commands/regenerate/regenerate.service.ts +++ b/vscode/microsoft-kiota/src/commands/regenerate/regenerate.service.ts @@ -9,10 +9,9 @@ import { OpenApiTreeProvider } from "../../providers/openApiTreeProvider"; import { KiotaGenerationLanguage, KiotaPluginType } from "../../types/enums"; import { ExtensionSettings } from "../../types/extensionSettings"; import { parseGenerationLanguage, parsePluginType } from "../../util"; -import { exportLogsAndShowErrors } from "../../utilities/logging"; +import { checkForSuccess, exportLogsAndShowErrors } from "../../utilities/logging"; import { generateClient } from "../generate/generateClient"; import { generatePlugin } from "../generate/generatePlugin"; -import { checkForSuccess } from "../generate/generation-util"; export class RegenerateService { private _context: ExtensionContext; diff --git a/vscode/microsoft-kiota/src/utilities/logging.ts b/vscode/microsoft-kiota/src/utilities/logging.ts index 616e822965..608e0701b9 100644 --- a/vscode/microsoft-kiota/src/utilities/logging.ts +++ b/vscode/microsoft-kiota/src/utilities/logging.ts @@ -21,7 +21,7 @@ export async function exportLogsAndShowErrors(result: KiotaLogEntry[]): Promise< } } -function logFromLogLevel(entry: KiotaLogEntry): void { +export function logFromLogLevel(entry: KiotaLogEntry): void { switch (entry.level) { case LogLevel.critical: case LogLevel.error: @@ -41,3 +41,14 @@ function logFromLogLevel(entry: KiotaLogEntry): void { break; } } + +export async function checkForSuccess(results: KiotaLogEntry[]) { + for (const result of results) { + if (result && result.message) { + if (result.message.includes("Generation completed successfully")) { + return true; + } + } + } + return false; +} From df40f4e298407324283a1318d7bd2b6487a97712 Mon Sep 17 00:00:00 2001 From: thewahome Date: Tue, 19 Nov 2024 09:34:28 +0300 Subject: [PATCH 11/16] fix message --- src/Kiota.Builder/Plugins/PluginsGenerationService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Kiota.Builder/Plugins/PluginsGenerationService.cs b/src/Kiota.Builder/Plugins/PluginsGenerationService.cs index c4535d5c48..a1791e7d78 100644 --- a/src/Kiota.Builder/Plugins/PluginsGenerationService.cs +++ b/src/Kiota.Builder/Plugins/PluginsGenerationService.cs @@ -359,7 +359,7 @@ private static (OpenApiRuntime[], Function[], ConversationStarter[]) GetRuntimes catch (UnsupportedSecuritySchemeException e) { auth = new AnonymousAuth(); - logger.LogWarning("Authentication warning: {OperationId} - {Message}. Using anonymous auth.", operation.OperationId, e.Message); + logger.LogWarning("Authentication warning: {OperationId} - {Message}", operation.OperationId, e.Message); } runtimes.Add(new OpenApiRuntime From 0a4270a8db5f0d8075561548a65f32cb14c95599 Mon Sep 17 00:00:00 2001 From: thewahome Date: Tue, 19 Nov 2024 09:42:45 +0300 Subject: [PATCH 12/16] pass kiotaOutputChannel as dependency --- .../commands/regenerate/regenerate.service.ts | 16 ++++------------ .../regenerate/regenerateButtonCommand.ts | 8 ++------ .../commands/regenerate/regenerateCommand.ts | 8 ++------ .../updateClients/updateClientsCommand.ts | 17 ++++++++--------- vscode/microsoft-kiota/src/extension.ts | 10 +++++----- vscode/microsoft-kiota/src/utilities/logging.ts | 16 ++++++++-------- 6 files changed, 29 insertions(+), 46 deletions(-) diff --git a/vscode/microsoft-kiota/src/commands/regenerate/regenerate.service.ts b/vscode/microsoft-kiota/src/commands/regenerate/regenerate.service.ts index fa6456ed8b..ba72a5b966 100644 --- a/vscode/microsoft-kiota/src/commands/regenerate/regenerate.service.ts +++ b/vscode/microsoft-kiota/src/commands/regenerate/regenerate.service.ts @@ -14,17 +14,9 @@ import { generateClient } from "../generate/generateClient"; import { generatePlugin } from "../generate/generatePlugin"; export class RegenerateService { - private _context: ExtensionContext; - private _openApiTreeProvider: OpenApiTreeProvider; - private _clientKey: string; - private _clientObject: ClientOrPluginProperties; - public constructor(context: ExtensionContext, openApiTreeProvider: OpenApiTreeProvider, - clientKey: string, clientObject: ClientOrPluginProperties) { - this._context = context; - this._openApiTreeProvider = openApiTreeProvider; - this._clientKey = clientKey; - this._clientObject = clientObject; + public constructor(private _context: ExtensionContext, private _openApiTreeProvider: OpenApiTreeProvider, + private _clientKey: string, private _clientObject: ClientOrPluginProperties, private _kiotaOutputChannel: vscode.LogOutputChannel) { } async regenerateClient(settings: ExtensionSettings, selectedPaths?: string[]): Promise { @@ -61,7 +53,7 @@ export class RegenerateService { if (result) { const isSuccess = await checkForSuccess(result); if (!isSuccess) { - await exportLogsAndShowErrors(result); + await exportLogsAndShowErrors(result, this._kiotaOutputChannel); } void vscode.window.showInformationMessage(`Client ${this._clientKey} re-generated successfully.`); } @@ -106,7 +98,7 @@ export class RegenerateService { if (result) { const isSuccess = await checkForSuccess(result); if (!isSuccess) { - await exportLogsAndShowErrors(result); + await exportLogsAndShowErrors(result, this._kiotaOutputChannel); } void vscode.window.showInformationMessage(vscode.l10n.t(`Plugin ${this._clientKey} re-generated successfully.`)); } diff --git a/vscode/microsoft-kiota/src/commands/regenerate/regenerateButtonCommand.ts b/vscode/microsoft-kiota/src/commands/regenerate/regenerateButtonCommand.ts index abfcdffdac..44e07ad3ff 100644 --- a/vscode/microsoft-kiota/src/commands/regenerate/regenerateButtonCommand.ts +++ b/vscode/microsoft-kiota/src/commands/regenerate/regenerateButtonCommand.ts @@ -12,13 +12,9 @@ import { Command } from "../Command"; import { RegenerateService } from "./regenerate.service"; export class RegenerateButtonCommand extends Command { - private _context: ExtensionContext; - private _openApiTreeProvider: OpenApiTreeProvider; - constructor(context: ExtensionContext, openApiTreeProvider: OpenApiTreeProvider) { + constructor(private _context: ExtensionContext, private _openApiTreeProvider: OpenApiTreeProvider, private _kiotaOutputChannel: vscode.LogOutputChannel) { super(); - this._context = context; - this._openApiTreeProvider = openApiTreeProvider; } public getName(): string { @@ -53,7 +49,7 @@ export class RegenerateButtonCommand extends Command { } const configObject = clientOrPluginObject || configuration; - const regenerateService = new RegenerateService(this._context, this._openApiTreeProvider, clientOrPluginKey, configObject); + const regenerateService = new RegenerateService(this._context, this._openApiTreeProvider, clientOrPluginKey, configObject, this._kiotaOutputChannel); if (isClientType(generationType)) { await regenerateService.regenerateClient(settings, selectedPaths); diff --git a/vscode/microsoft-kiota/src/commands/regenerate/regenerateCommand.ts b/vscode/microsoft-kiota/src/commands/regenerate/regenerateCommand.ts index f121131698..d9647fe9b6 100644 --- a/vscode/microsoft-kiota/src/commands/regenerate/regenerateCommand.ts +++ b/vscode/microsoft-kiota/src/commands/regenerate/regenerateCommand.ts @@ -11,13 +11,9 @@ import { Command } from "../Command"; import { RegenerateService } from "./regenerate.service"; export class RegenerateCommand extends Command { - private _context: ExtensionContext; - private _openApiTreeProvider: OpenApiTreeProvider; - constructor(context: ExtensionContext, openApiTreeProvider: OpenApiTreeProvider) { + constructor(private _context: ExtensionContext, private _openApiTreeProvider: OpenApiTreeProvider, private _kiotaOutputChannel: vscode.LogOutputChannel) { super(); - this._context = context; - this._openApiTreeProvider = openApiTreeProvider; } public getName(): string { @@ -40,7 +36,7 @@ export class RegenerateCommand extends Command { return; } - const regenerateService = new RegenerateService(this._context, this._openApiTreeProvider, clientOrPluginKey, clientOrPluginObject); + const regenerateService = new RegenerateService(this._context, this._openApiTreeProvider, clientOrPluginKey, clientOrPluginObject, this._kiotaOutputChannel); if (isClientType(generationType)) { await regenerateService.regenerateClient(settings); } diff --git a/vscode/microsoft-kiota/src/commands/updateClients/updateClientsCommand.ts b/vscode/microsoft-kiota/src/commands/updateClients/updateClientsCommand.ts index 2e05bd7692..7b517c15f6 100644 --- a/vscode/microsoft-kiota/src/commands/updateClients/updateClientsCommand.ts +++ b/vscode/microsoft-kiota/src/commands/updateClients/updateClientsCommand.ts @@ -9,13 +9,12 @@ import { Command } from "../Command"; import { updateClients } from './updateClients'; interface UpdateClientsCommandProps { - kiotaOutputChannel: vscode.LogOutputChannel; kiotaStatusBarItem: vscode.StatusBarItem; } export class UpdateClientsCommand extends Command { - constructor(private context: vscode.ExtensionContext) { + constructor(private context: vscode.ExtensionContext, private kiotaOutputChannel: vscode.LogOutputChannel) { super(); } @@ -23,7 +22,7 @@ export class UpdateClientsCommand extends Command { return `${extensionId}.updateClients`; } - public async execute({ kiotaOutputChannel, kiotaStatusBarItem }: UpdateClientsCommandProps): Promise { + public async execute({ kiotaStatusBarItem }: UpdateClientsCommandProps): Promise { if ( !vscode.workspace.workspaceFolders || vscode.workspace.workspaceFolders.length === 0 @@ -37,11 +36,11 @@ export class UpdateClientsCommand extends Command { if (existingApiManifestFileUris.length > 0) { await Promise.all(existingApiManifestFileUris.map(uri => showUpgradeWarningMessage(uri, null, null, this.context))); } - await updateStatusBarItem(this.context, kiotaOutputChannel, kiotaStatusBarItem); + await updateStatusBarItem(this.context, this.kiotaOutputChannel, kiotaStatusBarItem); try { - kiotaOutputChannel.clear(); - kiotaOutputChannel.show(); - kiotaOutputChannel.info( + this.kiotaOutputChannel.clear(); + this.kiotaOutputChannel.show(); + this.kiotaOutputChannel.info( vscode.l10n.t("updating client with path {path}", { path: vscode.workspace.workspaceFolders[0].uri.fsPath, }) @@ -55,10 +54,10 @@ export class UpdateClientsCommand extends Command { return updateClients(this.context, settings.cleanOutput, settings.clearCache); }); if (res) { - await exportLogsAndShowErrors(res); + await exportLogsAndShowErrors(res, this.kiotaOutputChannel); } } catch (error) { - kiotaOutputChannel.error( + this.kiotaOutputChannel.error( vscode.l10n.t(`error updating the clients {error}`), error ); diff --git a/vscode/microsoft-kiota/src/extension.ts b/vscode/microsoft-kiota/src/extension.ts index 5728e823c7..d681521038 100644 --- a/vscode/microsoft-kiota/src/extension.ts +++ b/vscode/microsoft-kiota/src/extension.ts @@ -70,13 +70,13 @@ export async function activate( const openDocumentationPageCommand = new OpenDocumentationPageCommand(); const editPathsCommand = new EditPathsCommand(openApiTreeProvider); const searchOrOpenApiDescriptionCommand = new SearchOrOpenApiDescriptionCommand(openApiTreeProvider, context); - const generateClientCommand = new GenerateClientCommand(openApiTreeProvider, context, dependenciesInfoProvider, setWorkspaceGenerationContext); - const regenerateCommand = new RegenerateCommand(context, openApiTreeProvider); - const regenerateButtonCommand = new RegenerateButtonCommand(context, openApiTreeProvider); + const generateClientCommand = new GenerateClientCommand(openApiTreeProvider, context, dependenciesInfoProvider, setWorkspaceGenerationContext, kiotaOutputChannel); + const regenerateCommand = new RegenerateCommand(context, openApiTreeProvider, kiotaOutputChannel); + const regenerateButtonCommand = new RegenerateButtonCommand(context, openApiTreeProvider, kiotaOutputChannel); const closeDescriptionCommand = new CloseDescriptionCommand(openApiTreeProvider); const statusCommand = new StatusCommand(); const selectLockCommand = new SelectLockCommand(openApiTreeProvider); - const updateClientsCommand = new UpdateClientsCommand(context); + const updateClientsCommand = new UpdateClientsCommand(context, kiotaOutputChannel); await loadTreeView(context); await checkForLockFileAndPrompt(context); @@ -137,7 +137,7 @@ export async function activate( // update status bar item once at start await updateStatusBarItem(context, kiotaOutputChannel, kiotaStatusBarItem); - context.subscriptions.push(vscode.commands.registerCommand(updateClientsCommand.getName(), async () => await updateClientsCommand.execute({ kiotaOutputChannel, kiotaStatusBarItem }))); + context.subscriptions.push(vscode.commands.registerCommand(updateClientsCommand.getName(), async () => await updateClientsCommand.execute({ kiotaStatusBarItem }))); } function registerCommandWithTelemetry(reporter: TelemetryReporter, command: string, callback: (...args: any[]) => any, thisArg?: any): vscode.Disposable { diff --git a/vscode/microsoft-kiota/src/utilities/logging.ts b/vscode/microsoft-kiota/src/utilities/logging.ts index 608e0701b9..64228de614 100644 --- a/vscode/microsoft-kiota/src/utilities/logging.ts +++ b/vscode/microsoft-kiota/src/utilities/logging.ts @@ -1,18 +1,14 @@ import * as vscode from 'vscode'; +import { LogOutputChannel } from 'vscode'; import { getLogEntriesForLevel, KiotaLogEntry, LogLevel } from '../kiotaInterop'; -let kiotaOutputChannel: vscode.LogOutputChannel; -kiotaOutputChannel = vscode.window.createOutputChannel("Kiota", { - log: true, -}); - -export async function exportLogsAndShowErrors(result: KiotaLogEntry[]): Promise { +export async function exportLogsAndShowErrors(result: KiotaLogEntry[], kiotaOutputChannel: LogOutputChannel): Promise { const errorMessages = result ? getLogEntriesForLevel(result, LogLevel.critical, LogLevel.error) : []; result.forEach((element) => { - logFromLogLevel(element); + logFromLogLevel(element, kiotaOutputChannel); }); if (errorMessages.length > 0) { await Promise.all(errorMessages.map((element) => { @@ -21,7 +17,7 @@ export async function exportLogsAndShowErrors(result: KiotaLogEntry[]): Promise< } } -export function logFromLogLevel(entry: KiotaLogEntry): void { +export function logFromLogLevel(entry: KiotaLogEntry, kiotaOutputChannel: LogOutputChannel): void { switch (entry.level) { case LogLevel.critical: case LogLevel.error: @@ -52,3 +48,7 @@ export async function checkForSuccess(results: KiotaLogEntry[]) { } return false; } + +export function showLogs(kiotaOutputChannel: LogOutputChannel): void { + kiotaOutputChannel.show(); +} From cf674dbe242211c54a204d6348d7975fc2641d31 Mon Sep 17 00:00:00 2001 From: thewahome Date: Tue, 19 Nov 2024 09:43:06 +0300 Subject: [PATCH 13/16] log warnings and open output tab --- .../generate/generateClientCommand.ts | 28 ++++++++----------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/vscode/microsoft-kiota/src/commands/generate/generateClientCommand.ts b/vscode/microsoft-kiota/src/commands/generate/generateClientCommand.ts index 632da647eb..c0945df113 100644 --- a/vscode/microsoft-kiota/src/commands/generate/generateClientCommand.ts +++ b/vscode/microsoft-kiota/src/commands/generate/generateClientCommand.ts @@ -15,7 +15,7 @@ import { GeneratedOutputState } from "../../types/GeneratedOutputState"; import { WorkspaceGenerationContext } from "../../types/WorkspaceGenerationContext"; import { getSanitizedString, getWorkspaceJsonDirectory, parseGenerationLanguage, parseGenerationType, parsePluginType, updateTreeViewIcons } from "../../util"; import { isDeeplinkEnabled, transformToGenerationConfig } from "../../utilities/deep-linking"; -import { checkForSuccess, exportLogsAndShowErrors, logFromLogLevel } from "../../utilities/logging"; +import { checkForSuccess, exportLogsAndShowErrors, logFromLogLevel, showLogs } from "../../utilities/logging"; import { showUpgradeWarningMessage } from "../../utilities/messaging"; import { Command } from "../Command"; import { generateClient } from "./generateClient"; @@ -24,22 +24,15 @@ import { displayGenerationResults } from "./generation-util"; import { getLanguageInformation, getLanguageInformationForDescription } from "./getLanguageInformation"; export class GenerateClientCommand extends Command { - private _openApiTreeProvider: OpenApiTreeProvider; - private _context: vscode.ExtensionContext; - private _dependenciesViewProvider: DependenciesViewProvider; - private _setWorkspaceGenerationContext: (params: Partial) => void; constructor( - openApiTreeProvider: OpenApiTreeProvider, - context: vscode.ExtensionContext, - dependenciesViewProvider: DependenciesViewProvider, - setWorkspaceGenerationContext: (params: Partial) => void + private _openApiTreeProvider: OpenApiTreeProvider, + private _context: vscode.ExtensionContext, + private _dependenciesViewProvider: DependenciesViewProvider, + private _setWorkspaceGenerationContext: (params: Partial) => void, + private _kiotaOutputChannel: vscode.LogOutputChannel ) { super(); - this._openApiTreeProvider = openApiTreeProvider; - this._context = context; - this._dependenciesViewProvider = dependenciesViewProvider; - this._setWorkspaceGenerationContext = setWorkspaceGenerationContext; } public getName(): string { @@ -129,7 +122,8 @@ export class GenerateClientCommand extends Command { } const authenticationWarnings = getLogEntriesForLevel(result ?? [], LogLevel.warning).filter(entry => entry.message.startsWith('Authentication warning')); - authenticationWarnings?.forEach(logFromLogLevel); + authenticationWarnings?.forEach(entry => logFromLogLevel(entry, this._kiotaOutputChannel)); + this._kiotaOutputChannel.show(); if (result && getLogEntriesForLevel(result, LogLevel.critical, LogLevel.error).length === 0) { // Save state before opening the new window @@ -210,7 +204,7 @@ export class GenerateClientCommand extends Command { if (result) { const isSuccess = await checkForSuccess(result); if (!isSuccess) { - await exportLogsAndShowErrors(result); + await exportLogsAndShowErrors(result, this._kiotaOutputChannel); } void vscode.window.showInformationMessage(vscode.l10n.t('Generation completed successfully.')); } @@ -254,7 +248,7 @@ export class GenerateClientCommand extends Command { if (result) { const isSuccess = await checkForSuccess(result); if (!isSuccess) { - await exportLogsAndShowErrors(result); + await exportLogsAndShowErrors(result, this._kiotaOutputChannel); } const deepLinkParams = getDeepLinkParams(); const isttkIntegration = deepLinkParams.source?.toLowerCase() === 'ttk'; @@ -324,7 +318,7 @@ export class GenerateClientCommand extends Command { if (result) { const isSuccess = await checkForSuccess(result); if (!isSuccess) { - await exportLogsAndShowErrors(result); + await exportLogsAndShowErrors(result, this._kiotaOutputChannel); } void vscode.window.showInformationMessage(vscode.l10n.t('Generation completed successfully.')); } From 4cf39a539fb14bef9be24dccecb458892ece51ce Mon Sep 17 00:00:00 2001 From: thewahome Date: Tue, 19 Nov 2024 10:54:15 +0300 Subject: [PATCH 14/16] show warning message and point to logs --- .../commands/generate/generateClientCommand.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/vscode/microsoft-kiota/src/commands/generate/generateClientCommand.ts b/vscode/microsoft-kiota/src/commands/generate/generateClientCommand.ts index c0945df113..172f138fa6 100644 --- a/vscode/microsoft-kiota/src/commands/generate/generateClientCommand.ts +++ b/vscode/microsoft-kiota/src/commands/generate/generateClientCommand.ts @@ -122,8 +122,20 @@ export class GenerateClientCommand extends Command { } const authenticationWarnings = getLogEntriesForLevel(result ?? [], LogLevel.warning).filter(entry => entry.message.startsWith('Authentication warning')); - authenticationWarnings?.forEach(entry => logFromLogLevel(entry, this._kiotaOutputChannel)); - this._kiotaOutputChannel.show(); + if (authenticationWarnings.length > 0) { + authenticationWarnings.forEach(entry => logFromLogLevel(entry, this._kiotaOutputChannel)); + + const showLogs = vscode.l10n.t("Show logs"); + const response = await vscode.window.showWarningMessage( + vscode.l10n.t( + "Incompatible security schemes for Copilot usage detected in the selected endpoints."), + showLogs, + vscode.l10n.t("Cancel") + ); + if (response === showLogs) { + this._kiotaOutputChannel.show(); + } + } if (result && getLogEntriesForLevel(result, LogLevel.critical, LogLevel.error).length === 0) { // Save state before opening the new window From ca0455557e9ac02a17615de9b2176f08551fe156 Mon Sep 17 00:00:00 2001 From: thewahome Date: Tue, 19 Nov 2024 11:11:44 +0300 Subject: [PATCH 15/16] add output channel in tests --- .../suite/commands/generateClientCommand.test.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/vscode/microsoft-kiota/src/test/suite/commands/generateClientCommand.test.ts b/vscode/microsoft-kiota/src/test/suite/commands/generateClientCommand.test.ts index 448448c9d3..5f7b4de9ee 100644 --- a/vscode/microsoft-kiota/src/test/suite/commands/generateClientCommand.test.ts +++ b/vscode/microsoft-kiota/src/test/suite/commands/generateClientCommand.test.ts @@ -75,7 +75,9 @@ const setWorkspaceGenerationContext = (params: Partial { const sanbox = sinon.createSandbox(); - + let myOutputChannel = vscode.window.createOutputChannel("Kiota", { + log: true, + }); teardown(() => { sanbox.restore(); }); @@ -83,7 +85,7 @@ suite('GenerateClientCommand Test Suite', () => { test('test function getName of GenerateClientCommand', () => { var treeProvider = sinon.createStubInstance(treeModule.OpenApiTreeProvider); var viewProvider = sinon.createStubInstance(dependenciesModule.DependenciesViewProvider); - const generateClientCommand = new generateModule.GenerateClientCommand(treeProvider, context, viewProvider, setWorkspaceGenerationContext); + const generateClientCommand = new generateModule.GenerateClientCommand(treeProvider, context, viewProvider, setWorkspaceGenerationContext, myOutputChannel); assert.strictEqual("kiota.openApiExplorer.generateClient", generateClientCommand.getName()); }); @@ -92,7 +94,7 @@ suite('GenerateClientCommand Test Suite', () => { treeProvider.getSelectedPaths.returns([]); var viewProvider = sinon.createStubInstance(dependenciesModule.DependenciesViewProvider); const vscodeWindowSpy = sinon.stub(vscode.window, "showErrorMessage"); - const generateClientCommand = new generateModule.GenerateClientCommand(treeProvider, context, viewProvider, setWorkspaceGenerationContext); + const generateClientCommand = new generateModule.GenerateClientCommand(treeProvider, context, viewProvider, setWorkspaceGenerationContext, myOutputChannel); await generateClientCommand.execute(); assert.strictEqual((treeProvider.getSelectedPaths()).length, 0); sinon.assert.calledOnceWithMatch(vscodeWindowSpy, vscode.l10n.t("No endpoints selected, select endpoints first")); @@ -114,7 +116,7 @@ suite('GenerateClientCommand Test Suite', () => { const generateStepsFn = sinon.stub(generateStepsModule, "generateSteps"); generateStepsFn.resolves(config); const showUpgradeWarningMessageStub = sinon.stub(msgUtilitiesModule, "showUpgradeWarningMessage"); - const generateClientCommand = new generateModule.GenerateClientCommand(treeProvider, context, viewProvider, setWorkspaceGenerationContext); + const generateClientCommand = new generateModule.GenerateClientCommand(treeProvider, context, viewProvider, setWorkspaceGenerationContext, myOutputChannel); await generateClientCommand.execute(); assert.strictEqual((treeProvider.getSelectedPaths()).length, 1); vscodeWindowSpy.verify(); @@ -162,7 +164,7 @@ suite('GenerateClientCommand Test Suite', () => { deepLinkParamsHandler.setDeepLinkParams(pluginParams); //stub and call generateCommand - const generateClientCommand = new generateModule.GenerateClientCommand(treeProvider, context, viewProvider, setWorkspaceGenerationContext); + const generateClientCommand = new generateModule.GenerateClientCommand(treeProvider, context, viewProvider, setWorkspaceGenerationContext, myOutputChannel); const generatePluginAndRefreshUIExpectation = sinon.mock(generateClientCommand).expects( "generatePluginAndRefreshUI").once().withArgs( config, extensionSettings, "path/to/temp/folder", ["repairs"] @@ -216,7 +218,7 @@ suite('GenerateClientCommand Test Suite', () => { deepLinkParamsHandler.setDeepLinkParams(pluginParams); //stub and call generateCommand - const generateClientCommand = new generateModule.GenerateClientCommand(treeProvider, context, viewProvider, setWorkspaceGenerationContext); + const generateClientCommand = new generateModule.GenerateClientCommand(treeProvider, context, viewProvider, setWorkspaceGenerationContext, myOutputChannel); var outputPath = path.join("path", "to", "temp", "folder", "appPackage"); //make it os agnostic const generateManifestAndRefreshUIExpectation = sinon.mock(generateClientCommand).expects( "generateManifestAndRefreshUI").twice().withArgs( From 0543133410d89c7e6772c8e76864724d3821f58a Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Tue, 19 Nov 2024 07:30:10 -0500 Subject: [PATCH 16/16] chore: formatting --- src/Kiota.Builder/Plugins/PluginsGenerationService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Kiota.Builder/Plugins/PluginsGenerationService.cs b/src/Kiota.Builder/Plugins/PluginsGenerationService.cs index a1791e7d78..6a1e73c972 100644 --- a/src/Kiota.Builder/Plugins/PluginsGenerationService.cs +++ b/src/Kiota.Builder/Plugins/PluginsGenerationService.cs @@ -339,7 +339,7 @@ private sealed record OpenApiManifestInfo( string? LogoUrl = null, string? PrivacyUrl = null, string ContactEmail = DefaultContactEmail); - + private static (OpenApiRuntime[], Function[], ConversationStarter[]) GetRuntimesFunctionsAndConversationStartersFromTree(OpenApiDocument document, PluginAuthConfiguration? authInformation, OpenApiUrlTreeNode currentNode, string openApiDocumentPath, ILogger logger) {