Repository: microsoft/kiota (csharp)

Issue:
[Plugin generation] Kiota should warn users when api contains unsupported auth flows
### What are you generating using Kiota, clients or plugins?

Kiota plugin

### In what context or format are you using Kiota?

Windows executable

### Client library/SDK language

None

### Describe the bug

Copilot not support OAuth with implicit flow, currently it only support OAuth with authcode flow. Kiota should warn if user selected API contains unsupported auth flow like OAuth with implicit flow.


```yaml
components:
  securitySchemes:
    oAuth2AuthImplicit:  
      type: oauth2
      description: This API uses OAuth 2 with the implicit grant flow. [More info](https://api.example.com/docs/auth)
      flows:
        implicit:   # <---- OAuth flow(authorizationCode, implicit, password or clientCredentials)
          authorizationUrl: https://api.example.com/oauth2/authorize
          scopes:
            read_pets: read your pets
            write_pets: modify pets in your account
```

Sample schema:
https://raw.githubusercontent.com/SLdragon/example-openapi-spec/main/oauth-implicit.yaml

Related bug: 
https://github.com/microsoft/kiota/issues/5288
https://github.com/microsoft/kiota/issues/5070
https://github.com/microsoft/kiota/issues/5162


### Expected behavior

Kiota should generate API Plugins and neither warn nor error when the OpenAPI description has no security definition.
Kiota should generate API Plugins and warn users when the specified auth flow is different from OAuth code flow and HTTP bearer token (current supported auth).
Kiota should generate API Plugins and warn users when there is a combination of two auth methods for an endpoint (AND scenario, e.g., API Key + OAuth for calling a #GET operation)."

### How to reproduce

Open sample schema: https://raw.githubusercontent.com/SLdragon/example-openapi-spec/main/oauth-implicit.yaml -> click generate plugin

### Open API description file

_No response_

### Kiota Version

v1.18.100000002

### Latest Kiota version known to work for scenario above?(Not required)

_No response_

### Known Workarounds

_No response_

### Configuration

_No response_

### Debug output

<details><summary>Click to expand log</summary>
```


<log output here>

```
</details>


### Other information

_No response_

Pull request:
### Overview

Closes #5403 

### Notes
- [x] Add tests to confirm supporting endpoints
- [x] Add logs to stream warnings
- [x] Stream-line messages to match A/C
- [x] Surface the messages in the extension
- [x] Surface the messages in the CLI

Patch:
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<bool> 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 ef638601fa..6a1e73c972 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<KiotaBuilder> Logger;
 
     public PluginsGenerationService(OpenApiDocument document, OpenApiUrlTreeNode openApiUrlTreeNode,
-        GenerationConfiguration configuration, string workingDirectory)
+        GenerationConfiguration configuration, string workingDirectory, ILogger<KiotaBuilder> 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<KiotaBuilder> logger)
     {
         var runtimes = new List<OpenApiRuntime>();
         var functions = new List<Function>();
@@ -348,10 +351,21 @@ 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 e)
+                {
+                    auth = new AnonymousAuth();
+                    logger.LogWarning("Authentication warning: {OperationId} - {Message}", operation.OperationId, e.Message);
+                }
+
                 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]
                 });
@@ -376,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);
@@ -391,7 +405,7 @@ private static Auth GetAuth(IList<OpenApiSecurityRequirement> 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();
diff --git a/vscode/microsoft-kiota/src/commands/generate/generateClientCommand.ts b/vscode/microsoft-kiota/src/commands/generate/generateClientCommand.ts
index d570a8bee8..172f138fa6 100644
--- a/vscode/microsoft-kiota/src/commands/generate/generateClientCommand.ts
+++ b/vscode/microsoft-kiota/src/commands/generate/generateClientCommand.ts
@@ -15,31 +15,24 @@ 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, showLogs } 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 {
-  private _openApiTreeProvider: OpenApiTreeProvider;
-  private _context: vscode.ExtensionContext;
-  private _dependenciesViewProvider: DependenciesViewProvider;
-  private _setWorkspaceGenerationContext: (params: Partial<WorkspaceGenerationContext>) => void;
 
   constructor(
-    openApiTreeProvider: OpenApiTreeProvider,
-    context: vscode.ExtensionContext,
-    dependenciesViewProvider: DependenciesViewProvider,
-    setWorkspaceGenerationContext: (params: Partial<WorkspaceGenerationContext>) => void
+    private _openApiTreeProvider: OpenApiTreeProvider,
+    private _context: vscode.ExtensionContext,
+    private _dependenciesViewProvider: DependenciesViewProvider,
+    private _setWorkspaceGenerationContext: (params: Partial<WorkspaceGenerationContext>) => void,
+    private _kiotaOutputChannel: vscode.LogOutputChannel
   ) {
     super();
-    this._openApiTreeProvider = openApiTreeProvider;
-    this._context = context;
-    this._dependenciesViewProvider = dependenciesViewProvider;
-    this._setWorkspaceGenerationContext = setWorkspaceGenerationContext;
   }
 
   public getName(): string {
@@ -127,6 +120,23 @@ export class GenerateClientCommand extends Command {
         );
         return;
     }
+
+    const authenticationWarnings = getLogEntriesForLevel(result ?? [], LogLevel.warning).filter(entry => entry.message.startsWith('Authentication warning'));
+    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
       const outputState = {
@@ -206,7 +216,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.'));
     }
@@ -250,7 +260,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';
@@ -320,7 +330,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.'));
     }
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..ba72a5b966 100644
--- a/vscode/microsoft-kiota/src/commands/regenerate/regenerate.service.ts
+++ b/vscode/microsoft-kiota/src/commands/regenerate/regenerate.service.ts
@@ -9,23 +9,14 @@ 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;
-  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<void> {
@@ -62,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.`);
       }
@@ -107,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<void> {
+  public async execute({ kiotaStatusBarItem }: UpdateClientsCommandProps): Promise<void> {
     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 616e822965..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<void> {
+export async function exportLogsAndShowErrors(result: KiotaLogEntry[], kiotaOutputChannel: LogOutputChannel): Promise<void> {
   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<
   }
 }
 
-function logFromLogLevel(entry: KiotaLogEntry): void {
+export function logFromLogLevel(entry: KiotaLogEntry, kiotaOutputChannel: LogOutputChannel): void {
   switch (entry.level) {
     case LogLevel.critical:
     case LogLevel.error:
@@ -41,3 +37,18 @@ 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;
+}
+
+export function showLogs(kiotaOutputChannel: LogOutputChannel): void {
+  kiotaOutputChannel.show();
+}


Test patch:
diff --git a/tests/Kiota.Builder.Tests/Plugins/PluginsGenerationServiceTests.cs b/tests/Kiota.Builder.Tests/Plugins/PluginsGenerationServiceTests.cs
index 3f74656037..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<KiotaBuilder> _logger = new Mock<ILogger<KiotaBuilder>>().Object;
     [Fact]
     public void Defensive()
     {
-        Assert.Throws<ArgumentNullException>(() => new PluginsGenerationService(null, OpenApiUrlTreeNode.Create(), new(), "foo"));
-        Assert.Throws<ArgumentNullException>(() => new PluginsGenerationService(new(), null, new(), "foo"));
-        Assert.Throws<ArgumentNullException>(() => new PluginsGenerationService(new(), OpenApiUrlTreeNode.Create(), null, "foo"));
-        Assert.Throws<ArgumentException>(() => new PluginsGenerationService(new(), OpenApiUrlTreeNode.Create(), new(), string.Empty));
+        Assert.Throws<ArgumentNullException>(() => new PluginsGenerationService(null, OpenApiUrlTreeNode.Create(), new(), "foo", _logger));
+        Assert.Throws<ArgumentNullException>(() => new PluginsGenerationService(new(), null, new(), "foo", _logger));
+        Assert.Throws<ArgumentNullException>(() => new PluginsGenerationService(new(), OpenApiUrlTreeNode.Create(), null, "foo", _logger));
+        Assert.Throws<ArgumentException>(() => 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<ILogger<PluginsGenerationService>>();
-        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<ILogger<PluginsGenerationService>>();
-        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)));
@@ -408,7 +408,33 @@ public static TheoryData<string, string, string, PluginAuthConfiguration, Action
                     var auth0 = resultingManifest.Document.Runtimes[0].Auth;
                     Assert.IsType<AnonymousAuth>(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<AnonymousAuth>(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<AnonymousAuth>(auth0);
+                }
             }
+
+
         };
     }
 
@@ -437,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<ILogger<PluginsGenerationService>>();
-        var openApiDocumentDs = new OpenApiDocumentDownloadService(_httpClient, mockLogger.Object);
+        var openApiDocumentDs = new OpenApiDocumentDownloadService(_httpClient, _logger);
         var outputDirectory = Path.Combine(workingDirectory, "output");
         var generationConfiguration = new GenerationConfiguration
         {
@@ -457,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)));
@@ -480,98 +506,6 @@ public async Task GeneratesManifestWithAuthAsync(string securitySchemesComponent
         }
     }
 
-    public static TheoryData<string, string, string, PluginAuthConfiguration, Func<Func<Task>, Task>>
-        SecurityInformationFail()
-    {
-        return new TheoryData<string, string, string, PluginAuthConfiguration, Func<Func<Task>, 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<InvalidOperationException>(async () =>
-                    {
-                        await action();
-                    });
-                }
-            },
-            // Unsupported security scheme (http basic)
-            {
-                "{securitySchemes: {httpBasic0: {type: http, scheme: basic}}}",
-                string.Empty, "security: [httpBasic0: []]", null, async (action) =>
-                {
-                    await Assert.ThrowsAsync<UnsupportedSecuritySchemeException>(async () =>
-                    {
-                        await action();
-                    });
-                }
-            },
-        };
-    }
-
-    [Theory]
-    [MemberData(nameof(SecurityInformationFail))]
-    public async Task FailsToGeneratesManifestWithInvalidAuthAsync(string securitySchemesComponent, string rootSecurity,
-        string operationSecurity, PluginAuthConfiguration pluginAuthConfiguration, Func<Func<Task>, 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<ILogger<PluginsGenerationService>>();
-        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()
     {
@@ -608,7 +542,7 @@ public async Task GeneratesManifestWithMultipleSecuritySchemesAsync()
         var simpleDescriptionPath = Path.Combine(workingDirectory) + "description.yaml";
         await File.WriteAllTextAsync(simpleDescriptionPath, apiDescription);
         var mockLogger = new Mock<ILogger<PluginsGenerationService>>();
-        var openApiDocumentDs = new OpenApiDocumentDownloadService(_httpClient, mockLogger.Object);
+        var openApiDocumentDs = new OpenApiDocumentDownloadService(_httpClient, _logger);
         var outputDirectory = Path.Combine(workingDirectory, "output");
         var generationConfiguration = new GenerationConfiguration
         {
@@ -627,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)));
@@ -814,7 +748,7 @@ public async Task MergesAllOfRequestBodyAsync(string content, Action<OpenApiDocu
         var simpleDescriptionPath = Path.Combine(workingDirectory) + "description.yaml";
         await File.WriteAllTextAsync(simpleDescriptionPath, apiDescription);
         var mockLogger = new Mock<ILogger<PluginsGenerationService>>();
-        var openAPIDocumentDS = new OpenApiDocumentDownloadService(_httpClient, mockLogger.Object);
+        var openAPIDocumentDS = new OpenApiDocumentDownloadService(_httpClient, _logger);
         var outputDirectory = Path.Combine(workingDirectory, "output");
         var generationConfiguration = new GenerationConfiguration
         {
@@ -829,7 +763,7 @@ public async Task MergesAllOfRequestBodyAsync(string content, Action<OpenApiDocu
         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)));
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<WorkspaceGenerationContex
 
 suite('GenerateClientCommand Test Suite', () => {
     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(