From 921b39b35aa227a43b83d6191bbe57ca4f152f26 Mon Sep 17 00:00:00 2001 From: Faith Kangai Date: Mon, 26 Aug 2024 17:26:15 +0300 Subject: [PATCH] skip predefined q&a when deeplinking --- .../src/dependenciesViewProvider.ts | 5 +- vscode/microsoft-kiota/src/enums.ts | 38 +++ vscode/microsoft-kiota/src/extension.ts | 70 +++-- .../microsoft-kiota/src/extensionSettings.ts | 4 +- vscode/microsoft-kiota/src/generateClient.ts | 7 +- vscode/microsoft-kiota/src/generatePlugin.ts | 7 +- vscode/microsoft-kiota/src/kiotaInterop.ts | 73 +---- vscode/microsoft-kiota/src/steps.ts | 251 ++++++++++-------- vscode/microsoft-kiota/src/util.ts | 134 +++++++++- 9 files changed, 368 insertions(+), 221 deletions(-) create mode 100644 vscode/microsoft-kiota/src/enums.ts diff --git a/vscode/microsoft-kiota/src/dependenciesViewProvider.ts b/vscode/microsoft-kiota/src/dependenciesViewProvider.ts index f4855b723f..9a4b7886ae 100644 --- a/vscode/microsoft-kiota/src/dependenciesViewProvider.ts +++ b/vscode/microsoft-kiota/src/dependenciesViewProvider.ts @@ -1,5 +1,6 @@ import * as vscode from 'vscode'; -import { generationLanguageToString, KiotaGenerationLanguage, LanguageInformation, LanguagesInformation } from './kiotaInterop'; +import { KiotaGenerationLanguage } from './enums'; +import { generationLanguageToString, LanguageInformation, LanguagesInformation } from './kiotaInterop'; export class DependenciesViewProvider implements vscode.WebviewViewProvider { private _view?: vscode.WebviewView; @@ -62,4 +63,4 @@ export class DependenciesViewProvider implements vscode.WebviewViewProvider { `; } -} \ No newline at end of file +} diff --git a/vscode/microsoft-kiota/src/enums.ts b/vscode/microsoft-kiota/src/enums.ts new file mode 100644 index 0000000000..e3340f0b2c --- /dev/null +++ b/vscode/microsoft-kiota/src/enums.ts @@ -0,0 +1,38 @@ +export enum GenerationType { + // eslint-disable-next-line @typescript-eslint/naming-convention + Client = 0, + // eslint-disable-next-line @typescript-eslint/naming-convention + Plugin = 1, + // eslint-disable-next-line @typescript-eslint/naming-convention + ApiManifest = 2, +}; + +export enum KiotaGenerationLanguage { + // eslint-disable-next-line @typescript-eslint/naming-convention + CSharp = 0, + // eslint-disable-next-line @typescript-eslint/naming-convention + Java = 1, + // eslint-disable-next-line @typescript-eslint/naming-convention + TypeScript = 2, + // eslint-disable-next-line @typescript-eslint/naming-convention + PHP = 3, + // eslint-disable-next-line @typescript-eslint/naming-convention + Python = 4, + // eslint-disable-next-line @typescript-eslint/naming-convention + Go = 5, + // eslint-disable-next-line @typescript-eslint/naming-convention + Swift = 6, + // eslint-disable-next-line @typescript-eslint/naming-convention + Ruby = 7, + // eslint-disable-next-line @typescript-eslint/naming-convention + CLI = 8, +}; + +export enum KiotaPluginType { + // eslint-disable-next-line @typescript-eslint/naming-convention + OpenAI = 0, + // eslint-disable-next-line @typescript-eslint/naming-convention + ApiManifest = 1, + // eslint-disable-next-line @typescript-eslint/naming-convention + ApiPlugin = 2, +}; diff --git a/vscode/microsoft-kiota/src/extension.ts b/vscode/microsoft-kiota/src/extension.ts index 046df51742..bcd7ca8641 100644 --- a/vscode/microsoft-kiota/src/extension.ts +++ b/vscode/microsoft-kiota/src/extension.ts @@ -1,36 +1,37 @@ // The module 'vscode' contains the VS Code extensibility API // Import the module and reference it with the alias vscode in your code below -import * as vscode from "vscode"; import TelemetryReporter from '@vscode/extension-telemetry'; -import * as path from 'path'; import * as fs from 'fs'; -import { OpenApiTreeNode, OpenApiTreeProvider } from "./openApiTreeProvider"; +import * as path from 'path'; +import * as vscode from "vscode"; +import { CodeLensProvider } from "./codelensProvider"; +import { KIOTA_WORKSPACE_FILE, dependenciesInfo, extensionId, statusBarCommandId, treeViewFocusCommand, treeViewId } from "./constants"; +import { DependenciesViewProvider } from "./dependenciesViewProvider"; +import { GenerationType, KiotaGenerationLanguage, KiotaPluginType } from "./enums"; +import { ExtensionSettings, getExtensionSettings } from "./extensionSettings"; +import { generateClient } from "./generateClient"; +import { generatePlugin } from "./generatePlugin"; +import { getKiotaVersion } from "./getKiotaVersion"; +import { getLanguageInformation, getLanguageInformationForDescription } from "./getLanguageInformation"; import { ClientOrPluginProperties, ConsumerOperation, - generationLanguageToString, - getLogEntriesForLevel, - KiotaGenerationLanguage, KiotaLogEntry, - KiotaPluginType, LogLevel, - parseGenerationLanguage, - parsePluginType, + generationLanguageToString, + getLogEntriesForLevel, } from "./kiotaInterop"; -import { GenerateState, GenerationType, filterSteps, generateSteps, parseGenerationType, searchSteps } from "./steps"; -import { getKiotaVersion } from "./getKiotaVersion"; +import { checkForLockFileAndPrompt } from "./migrateFromLockFile"; +import { OpenApiTreeNode, OpenApiTreeProvider } from "./openApiTreeProvider"; import { searchDescription } from "./searchDescription"; -import { generateClient } from "./generateClient"; -import { getLanguageInformation, getLanguageInformationForDescription } from "./getLanguageInformation"; -import { DependenciesViewProvider } from "./dependenciesViewProvider"; +import { GenerateState, filterSteps, generateSteps, searchSteps, transformToGenerationconfig } from "./steps"; import { updateClients } from "./updateClients"; -import { ExtensionSettings, getExtensionSettings } from "./extensionSettings"; +import { + getSanitizedString, getWorkspaceJsonDirectory, getWorkspaceJsonPath, + handleMigration, isClientType, isPluginType, parseGenerationLanguage, + parseGenerationType, parsePluginType, updateTreeViewIcons, validateDeepLinkQueryParams +} from "./util"; import { loadTreeView } from "./workspaceTreeProvider"; -import { generatePlugin } from "./generatePlugin"; -import { CodeLensProvider } from "./codelensProvider"; -import { KIOTA_WORKSPACE_FILE, dependenciesInfo, extensionId, statusBarCommandId, treeViewFocusCommand, treeViewId } from "./constants"; -import { getWorkspaceJsonDirectory, getWorkspaceJsonPath, handleMigration, isClientType, isPluginType, updateTreeViewIcons } from "./util"; -import { checkForLockFileAndPrompt } from "./migrateFromLockFile"; let kiotaStatusBarItem: vscode.StatusBarItem; let kiotaOutputChannel: vscode.LogOutputChannel; @@ -60,18 +61,22 @@ export async function activate( await loadTreeView(context); await checkForLockFileAndPrompt(context); let codeLensProvider = new CodeLensProvider(); + let deepLinkParams: Record = {}; context.subscriptions.push( vscode.window.registerUriHandler({ handleUri: async (uri: vscode.Uri) => { if (uri.path === "/") { return; } + console.log(uri); const queryParameters = getQueryParameters(uri); + console.log(queryParameters); if (uri.path.toLowerCase() === "/opendescription") { - reporter.sendTelemetryEvent("DeepLink.OpenDescription"); - const descriptionUrl = queryParameters["descriptionurl"]; - if (descriptionUrl) { - await openTreeViewWithProgress(() => openApiTreeProvider.setDescriptionUrl(descriptionUrl)); + deepLinkParams = validateDeepLinkQueryParams(queryParameters); + reporter.sendTelemetryEvent("DeepLink.OpenDescription", deepLinkParams); + + if (deepLinkParams.descriptionUrl) { + await openTreeViewWithProgress(() => openApiTreeProvider.setDescriptionUrl(deepLinkParams.descriptionUrl!)); return; } } @@ -135,15 +140,24 @@ export async function activate( } let languagesInformation = await getLanguageInformation(context); - const pluginName = getPluginName(); - config = await generateSteps( - { + let availableStateInfo: any = {}; + if(deepLinkParams){ + if (!deepLinkParams["name"] && openApiTreeProvider.apiTitle ){ + deepLinkParams["name"] = getSanitizedString(openApiTreeProvider.apiTitle); + } + availableStateInfo = transformToGenerationconfig(deepLinkParams); + }else{ + const pluginName = getPluginName(); + availableStateInfo = { clientClassName: openApiTreeProvider.clientClassName, clientNamespaceName: openApiTreeProvider.clientNamespaceName, language: openApiTreeProvider.language, outputPath: openApiTreeProvider.outputPath, pluginName - }, + }; + } + config = await generateSteps( + availableStateInfo, languagesInformation ); const generationType = parseGenerationType(config.generationType); diff --git a/vscode/microsoft-kiota/src/extensionSettings.ts b/vscode/microsoft-kiota/src/extensionSettings.ts index 1f8883b040..531b2af813 100644 --- a/vscode/microsoft-kiota/src/extensionSettings.ts +++ b/vscode/microsoft-kiota/src/extensionSettings.ts @@ -1,5 +1,5 @@ import * as vscode from "vscode"; -import { KiotaGenerationLanguage } from "./kiotaInterop"; +import { KiotaGenerationLanguage } from "./enums"; export function getExtensionSettings(extensionId: string) : ExtensionSettings { return { @@ -50,4 +50,4 @@ export interface ExtensionSettings { interface LanguageSerializationConfiguration { serializers: string[]; deserializers: string[]; -} \ No newline at end of file +} diff --git a/vscode/microsoft-kiota/src/generateClient.ts b/vscode/microsoft-kiota/src/generateClient.ts index 97475b8747..1c51748d5e 100644 --- a/vscode/microsoft-kiota/src/generateClient.ts +++ b/vscode/microsoft-kiota/src/generateClient.ts @@ -1,6 +1,7 @@ -import { connectToKiota, ConsumerOperation, GenerationConfiguration, KiotaGenerationLanguage, KiotaLogEntry } from "./kiotaInterop"; -import * as rpc from "vscode-jsonrpc/node"; import * as vscode from "vscode"; +import * as rpc from "vscode-jsonrpc/node"; +import { KiotaGenerationLanguage } from "./enums"; +import { connectToKiota, ConsumerOperation, GenerationConfiguration, KiotaLogEntry } from "./kiotaInterop"; import { getWorkspaceJsonDirectory } from "./util"; export function generateClient(context: vscode.ExtensionContext, @@ -50,4 +51,4 @@ export function generateClient(context: vscode.ExtensionContext, } as GenerationConfiguration, ); }, workingDirectory); -}; \ No newline at end of file +}; diff --git a/vscode/microsoft-kiota/src/generatePlugin.ts b/vscode/microsoft-kiota/src/generatePlugin.ts index 8e9948bcf0..221b821538 100644 --- a/vscode/microsoft-kiota/src/generatePlugin.ts +++ b/vscode/microsoft-kiota/src/generatePlugin.ts @@ -1,6 +1,7 @@ -import { connectToKiota, ConsumerOperation, GenerationConfiguration, KiotaGenerationLanguage, KiotaLogEntry, KiotaPluginType } from "./kiotaInterop"; -import * as rpc from "vscode-jsonrpc/node"; import * as vscode from "vscode"; +import * as rpc from "vscode-jsonrpc/node"; +import { KiotaPluginType } from "./enums"; +import { connectToKiota, ConsumerOperation, GenerationConfiguration, KiotaLogEntry } from "./kiotaInterop"; import { getWorkspaceJsonDirectory } from "./util"; export function generatePlugin(context: vscode.ExtensionContext, @@ -35,4 +36,4 @@ export function generatePlugin(context: vscode.ExtensionContext, } as GenerationConfiguration, ); }, workingDirectory); -}; \ No newline at end of file +}; diff --git a/vscode/microsoft-kiota/src/kiotaInterop.ts b/vscode/microsoft-kiota/src/kiotaInterop.ts index 3aea7cc663..8e2636f675 100644 --- a/vscode/microsoft-kiota/src/kiotaInterop.ts +++ b/vscode/microsoft-kiota/src/kiotaInterop.ts @@ -1,6 +1,7 @@ -import * as vscode from "vscode"; import * as cp from 'child_process'; +import * as vscode from "vscode"; import * as rpc from 'vscode-jsonrpc/node'; +import { KiotaGenerationLanguage, KiotaPluginType } from './enums'; import { ensureKiotaIsPresent, getKiotaPath } from './kiotaInstall'; import { getWorkspaceJsonDirectory } from "./util"; @@ -87,35 +88,6 @@ export interface KiotaSearchResultItem { VersionLabels?: string[]; } -export enum KiotaGenerationLanguage { - // eslint-disable-next-line @typescript-eslint/naming-convention - CSharp = 0, - // eslint-disable-next-line @typescript-eslint/naming-convention - Java = 1, - // eslint-disable-next-line @typescript-eslint/naming-convention - TypeScript = 2, - // eslint-disable-next-line @typescript-eslint/naming-convention - PHP = 3, - // eslint-disable-next-line @typescript-eslint/naming-convention - Python = 4, - // eslint-disable-next-line @typescript-eslint/naming-convention - Go = 5, - // eslint-disable-next-line @typescript-eslint/naming-convention - Swift = 6, - // eslint-disable-next-line @typescript-eslint/naming-convention - Ruby = 7, - // eslint-disable-next-line @typescript-eslint/naming-convention - CLI = 8, -} -export enum KiotaPluginType { - // eslint-disable-next-line @typescript-eslint/naming-convention - OpenAI = 0, - // eslint-disable-next-line @typescript-eslint/naming-convention - ApiManifest = 1, - // eslint-disable-next-line @typescript-eslint/naming-convention - ApiPlugin = 2, -} - export enum ConsumerOperation { // eslint-disable-next-line @typescript-eslint/naming-convention Add, @@ -126,20 +98,6 @@ export enum ConsumerOperation { // eslint-disable-next-line @typescript-eslint/naming-convention Generate } -export function parsePluginType(values: string[]): KiotaPluginType[] { - return values.map(value => { - switch (value.toLowerCase()) { - case "openai": - return KiotaPluginType.OpenAI; - case "apimanifest": - return KiotaPluginType.ApiManifest; - case "apiplugin": - return KiotaPluginType.ApiPlugin; - default: - throw new Error(`unknown plugin type: ${value}`); - } - }); -} export function generationLanguageToString(language: KiotaGenerationLanguage): string { switch (language) { @@ -165,30 +123,7 @@ export function generationLanguageToString(language: KiotaGenerationLanguage): s throw new Error("unknown language"); } } -export function parseGenerationLanguage(value: string): KiotaGenerationLanguage { - switch (value) { - case "CSharp": - return KiotaGenerationLanguage.CSharp; - case "Java": - return KiotaGenerationLanguage.Java; - case "TypeScript": - return KiotaGenerationLanguage.TypeScript; - case "PHP": - return KiotaGenerationLanguage.PHP; - case "Python": - return KiotaGenerationLanguage.Python; - case "Go": - return KiotaGenerationLanguage.Go; - case "Swift": - return KiotaGenerationLanguage.Swift; - case "Ruby": - return KiotaGenerationLanguage.Ruby; - case "CLI": - return KiotaGenerationLanguage.CLI; - default: - throw new Error("unknown language"); - } -} + export const allGenerationLanguages = [ KiotaGenerationLanguage.CSharp, KiotaGenerationLanguage.Go, @@ -306,4 +241,4 @@ export interface PluginObjectProperties extends WorkspaceObjectProperties { types: string[]; } -export type ClientOrPluginProperties = ClientObjectProperties | PluginObjectProperties; \ No newline at end of file +export type ClientOrPluginProperties = ClientObjectProperties | PluginObjectProperties; diff --git a/vscode/microsoft-kiota/src/steps.ts b/vscode/microsoft-kiota/src/steps.ts index 4a0788ebe1..be347b27ff 100644 --- a/vscode/microsoft-kiota/src/steps.ts +++ b/vscode/microsoft-kiota/src/steps.ts @@ -1,8 +1,8 @@ +import * as path from 'path'; import * as vscode from 'vscode'; -import { QuickPickItem, window, Disposable, QuickInputButton, QuickInput, QuickInputButtons, workspace, l10n, Uri, OpenDialogOptions } from 'vscode'; +import { Disposable, l10n, OpenDialogOptions, QuickInput, QuickInputButton, QuickInputButtons, QuickPickItem, Uri, window, workspace } from 'vscode'; import { allGenerationLanguages, generationLanguageToString, KiotaSearchResultItem, LanguagesInformation, maturityLevelToString } from './kiotaInterop'; import { findAppPackageDirectory, getWorkspaceJsonDirectory } from './util'; -import * as path from 'path'; export async function filterSteps(existingFilter: string, filterCallback: (searchQuery: string) => void) { const state = {} as Partial; @@ -116,6 +116,36 @@ export async function searchSteps(searchCallBack: (searchQuery: string) => Thena return state; } +export function transformToGenerationconfig(deepLinkParams: Record) + : Record +{ + const generationConfig: Record = {}; + if (deepLinkParams.kind === "client") + { + generationConfig["generationType"] = "client"; + generationConfig["clientClassName"] = deepLinkParams.name; + generationConfig["language"] = deepLinkParams.language; + } + else if (deepLinkParams.kind === "plugin") + { + generationConfig["pluginName"] = deepLinkParams.name; + switch(deepLinkParams.type){ + case "apiplugin": + generationConfig["generationType"] = "plugin"; + generationConfig["pluginTypes"] = ["ApiPlugin"]; + break; + case "openai": + generationConfig["generationType"] = "plugin"; + generationConfig["pluginTypes"] = ['OpenAI']; + break; + case "apimanifest": + generationConfig["generationType"] = "apimanifest"; + break; + } + } + return generationConfig; +} + export async function generateSteps(existingConfiguration: Partial, languagesInformation?: LanguagesInformation) { const state = { ...existingConfiguration } as Partial; @@ -154,41 +184,56 @@ export async function generateSteps(existingConfiguration: Partial) { - const items = [l10n.t('Generate an API client'), l10n.t('Generate a plugin'), l10n.t('Generate an API manifest')]; - const option = await input.showQuickPick({ - title: l10n.t('What do you want to generate?'), - step: 1, - totalSteps: 1, - placeholder: l10n.t('Select an option'), - items: items.map(x => ({ label: x })), - validate: validateIsNotEmpty, - shouldResume: shouldResume - }); - if (option.label === l10n.t('Generate an API client')) { - state.generationType = "client"; - return (input: MultiStepInput) => inputClientClassName(input, state); - } - else if (option.label === l10n.t('Generate a plugin')) { - state.generationType = "plugin"; - return (input: MultiStepInput) => inputPluginName(input, state); - } - else if (option.label === l10n.t('Generate an API manifest')) { - state.generationType = "apimanifest"; - return (input: MultiStepInput) => inputManifestName(input, state); + function getNextStepForGenerationType(generationType: string|QuickPickItem) { + switch(generationType) { + case 'client': + return inputClientClassName; + case 'plugin': + return inputPluginName; + case 'apimanifest': + return inputManifestName; + default: + throw new Error('unknown generation type'); } } + async function inputGenerationType(input: MultiStepInput, state: Partial) { + if (!state.generationType){ + const items = [l10n.t('Generate an API client'), l10n.t('Generate a plugin'), l10n.t('Generate an API manifest')]; + const option = await input.showQuickPick({ + title: l10n.t('What do you want to generate?'), + step: 1, + totalSteps: 1, + placeholder: l10n.t('Select an option'), + items: items.map(x => ({label: x})), + validate: validateIsNotEmpty, + shouldResume: shouldResume + }); + if(option.label === l10n.t('Generate an API client')) { + state.generationType = "client"; + } + else if(option.label === l10n.t('Generate a plugin')) { + state.generationType = "plugin"; + } + else if(option.label === l10n.t('Generate an API manifest')) { + state.generationType = "apimanifest"; + } + } + let nextStep = getNextStepForGenerationType(state.generationType?.toString() || ''); + return (input: MultiStepInput) => nextStep(input, state); + } async function inputClientClassName(input: MultiStepInput, state: Partial) { - state.clientClassName = await input.showInputBox({ - title: `${l10n.t('Create a new API client')} - ${l10n.t('class')}`, - step: step++, - totalSteps: totalSteps, - value: state.clientClassName ?? '', - placeholder: 'ApiClient', - prompt: l10n.t('Choose a name for the client class'), - validate: validateIsNotEmpty, - shouldResume: shouldResume - }); + if (!state.clientClassName) { + state.clientClassName = await input.showInputBox({ + title: `${l10n.t('Create a new API client')} - ${l10n.t('class')}`, + step: step++, + totalSteps: totalSteps, + value: state.clientClassName ?? '', + placeholder: 'ApiClient', + prompt: l10n.t('Choose a name for the client class'), + validate: validateIsNotEmpty, + shouldResume: shouldResume + }); + } updateWorkspaceFolder(state.clientClassName); return (input: MultiStepInput) => inputClientNamespaceName(input, state); } @@ -244,52 +289,58 @@ export async function generateSteps(existingConfiguration: Partial) { - const items = allGenerationLanguages.map(x => { - const lngName = generationLanguageToString(x); - const lngInfo = languagesInformation ? languagesInformation[lngName] : undefined; - const lngMaturity = lngInfo ? ` - ${maturityLevelToString(lngInfo.MaturityLevel)}` : ''; - return { - label: `${lngName}${lngMaturity}`, - languageName: lngName, - } as (QuickPickItem & { languageName: string }); - }); - const pick = await input.showQuickPick({ - title: `${l10n.t('Create a new API client')} - ${l10n.t('language')}`, - step: step++, - totalSteps: totalSteps, - placeholder: l10n.t('Pick a language'), - items, - activeItem: typeof state.language === 'string' ? items.find(x => x.languageName === state.language) : undefined, - shouldResume: shouldResume - }); - state.language = pick.label.split('-')[0].trim(); + if (!state.language) { + const items = allGenerationLanguages.map(x => { + const lngName = generationLanguageToString(x); + const lngInfo = languagesInformation ? languagesInformation[lngName] : undefined; + const lngMaturity = lngInfo ? ` - ${maturityLevelToString(lngInfo.MaturityLevel)}` : ''; + return { + label: `${lngName}${lngMaturity}`, + languageName: lngName, + } as (QuickPickItem & { languageName: string }); + }); + const pick = await input.showQuickPick({ + title: `${l10n.t('Create a new API client')} - ${l10n.t('language')}`, + step: step++, + totalSteps: totalSteps, + placeholder: l10n.t('Pick a language'), + items, + activeItem: typeof state.language === 'string' ? items.find(x => x.languageName === state.language) : undefined, + shouldResume: shouldResume + }); + state.language = pick.label.split('-')[0].trim(); + } } async function inputPluginName(input: MultiStepInput, state: Partial) { - state.pluginName = await input.showInputBox({ - title: `${l10n.t('Create a new plugin')} - ${l10n.t('plugin name')}`, - step: step++, - totalSteps: 3, - value: state.pluginName ?? '', - placeholder: 'MyPlugin', - prompt: l10n.t('Choose a name for the plugin'), - validate: validateIsNotEmpty, - shouldResume: shouldResume - }); + if (!state.pluginName){ + state.pluginName = await input.showInputBox({ + title: `${l10n.t('Create a new plugin')} - ${l10n.t('plugin name')}`, + step: step++, + totalSteps: 3, + value: state.pluginName ?? '', + placeholder: 'MyPlugin', + prompt: l10n.t('Choose a name for the plugin'), + validate: validateIsNotEmpty, + shouldResume: shouldResume + }); + } updateWorkspaceFolder(state.pluginName); - return (input: MultiStepInput) => inputPluginType(input, state); - } + return (input: MultiStepInput) => inputPluginType(input, state); + } async function inputPluginType(input: MultiStepInput, state: Partial) { - const items = ['API Plugin', 'Open AI'].map(x => ({ label: x }) as QuickPickItem); - const pluginTypes = await input.showQuickPick({ - title: l10n.t('Choose a plugin type'), - step: step++, - totalSteps: 3, - placeholder: l10n.t('Select an option'), - items: items, - validate: validateIsNotEmpty, - shouldResume: shouldResume - }); - pluginTypes.label === 'API Plugin' ? state.pluginTypes = ['ApiPlugin'] : state.pluginTypes = ['OpenAI']; + if (!state.pluginTypes) { + const items = ['API Plugin','Open AI'].map(x => ({ label: x})as QuickPickItem); + const pluginTypes = await input.showQuickPick({ + title: l10n.t('Choose a plugin type'), + step: step++, + totalSteps: 3, + placeholder: l10n.t('Select an option'), + items: items, + validate: validateIsNotEmpty, + shouldResume: shouldResume + }); + pluginTypes.label === 'API Plugin' ? state.pluginTypes = ['ApiPlugin'] : state.pluginTypes = ['OpenAI']; + } return (input: MultiStepInput) => inputPluginOutputPath(input, state); } async function inputPluginOutputPath(input: MultiStepInput, state: Partial) { @@ -331,16 +382,18 @@ export async function generateSteps(existingConfiguration: Partial) { - state.pluginName = await input.showInputBox({ - title: `${l10n.t('Create a new manifest')} - ${l10n.t('manifest name')}`, - step: step++, - totalSteps: 3, - value: state.pluginName ?? '', - placeholder: 'MyManifest', - prompt: l10n.t('Choose a name for the manifest'), - validate: validateIsNotEmpty, - shouldResume: shouldResume - }); + if (!state.pluginName){ + state.pluginName = await input.showInputBox({ + title: `${l10n.t('Create a new manifest')} - ${l10n.t('manifest name')}`, + step: step++, + totalSteps: 3, + value: state.pluginName ?? '', + placeholder: 'MyManifest', + prompt: l10n.t('Choose a name for the manifest'), + validate: validateIsNotEmpty, + shouldResume: shouldResume + }); + } updateWorkspaceFolder(state.pluginName); return (input: MultiStepInput) => inputManifestOutputPath(input, state); } @@ -431,29 +484,7 @@ export interface GenerateState extends BaseStepsState { outputPath: QuickPickItem | string; workingDirectory: string; } -export enum GenerationType { - // eslint-disable-next-line @typescript-eslint/naming-convention - Client = 0, - // eslint-disable-next-line @typescript-eslint/naming-convention - Plugin = 1, - // eslint-disable-next-line @typescript-eslint/naming-convention - ApiManifest = 2, -} -export function parseGenerationType(generationType: string | QuickPickItem | undefined): GenerationType { - if (typeof generationType !== 'string') { - throw new Error('generationType has not been selected yet'); - } - switch (generationType) { - case "client": - return GenerationType.Client; - case "plugin": - return GenerationType.Plugin; - case "apimanifest": - return GenerationType.ApiManifest; - default: - throw new Error(`Unknown generation type ${generationType}`); - } -} + class InputFlowAction { static back = new InputFlowAction(); static cancel = new InputFlowAction(); @@ -664,4 +695,4 @@ class MultiStepInput { disposables.forEach(d => d.dispose()); } } -} \ No newline at end of file +} diff --git a/vscode/microsoft-kiota/src/util.ts b/vscode/microsoft-kiota/src/util.ts index 86c2a2e4ef..04f1c0f2e3 100644 --- a/vscode/microsoft-kiota/src/util.ts +++ b/vscode/microsoft-kiota/src/util.ts @@ -1,8 +1,11 @@ -import * as vscode from 'vscode'; -import * as path from 'path'; import * as fs from 'fs'; +import * as path from 'path'; +import * as vscode from 'vscode'; +import { QuickPickItem } from "vscode"; import { APIMANIFEST, CLIENT, CLIENTS, KIOTA_DIRECTORY, KIOTA_WORKSPACE_FILE, PLUGIN, PLUGINS } from './constants'; -import { migrateFromLockFile, displayMigrationMessages } from './migrateFromLockFile'; +import { GenerationType, KiotaGenerationLanguage, KiotaPluginType } from './enums'; +import { allGenerationLanguages } from './kiotaInterop'; +import { displayMigrationMessages, migrateFromLockFile } from './migrateFromLockFile'; const clientTypes = [CLIENT, CLIENTS]; const pluginTypes = [PLUGIN, PLUGINS, APIMANIFEST]; @@ -92,4 +95,127 @@ export async function handleMigration( vscode.window.showErrorMessage(vscode.l10n.t(`Migration failed: ${error}`)); } }); -} \ No newline at end of file +} + +export function getSanitizedString(rawValue?: string): string| undefined{ + return rawValue?.replace(/[^a-zA-Z0-9_]+/g, ''); +}; + +export function parseGenerationType(generationType: string | QuickPickItem | undefined): GenerationType { + if(typeof generationType !== 'string') { + throw new Error('generationType has not been selected yet'); + } + switch(generationType) { + case "client": + return GenerationType.Client; + case "plugin": + return GenerationType.Plugin; + case "apimanifest": + return GenerationType.ApiManifest; + default: + throw new Error(`Unknown generation type ${generationType}`); + } +} + +export function parseGenerationLanguage(value: string): KiotaGenerationLanguage { + switch (value) { + case "CSharp": + case "csharp": + return KiotaGenerationLanguage.CSharp; + case "Java": + case "java": + return KiotaGenerationLanguage.Java; + case "TypeScript": + case "typescript": + return KiotaGenerationLanguage.TypeScript; + case "PHP": + case "php": + return KiotaGenerationLanguage.PHP; + case "Python": + case "python": + return KiotaGenerationLanguage.Python; + case "Go": + case "go": + return KiotaGenerationLanguage.Go; + case "Swift": + case "swift": + return KiotaGenerationLanguage.Swift; + case "Ruby": + case "ruby": + return KiotaGenerationLanguage.Ruby; + case "CLI": + case "cli": + return KiotaGenerationLanguage.CLI; + default: + throw new Error("unknown language"); + } +} + +export function parsePluginType(values: string[]): KiotaPluginType[] { + return values.map(value => { + switch (value.toLowerCase()) { + case "openai": + return KiotaPluginType.OpenAI; + case "apimanifest": + return KiotaPluginType.ApiManifest; + case "apiplugin": + return KiotaPluginType.ApiPlugin; + default: + throw new Error(`unknown plugin type: ${value}`); + } + }); +} + +export function allGenerationLanguagesToString(): string[] { + let allSupportedLanguages: string[] = allGenerationLanguages.map(langEnum => KiotaGenerationLanguage[langEnum]); + return allSupportedLanguages; +} + +export function validateDeepLinkQueryParams(queryParameters: Record): + Record +{ + let validQueryParams: Record = {}; + const descriptionUrl = queryParameters["descriptionurl"]; + const name = getSanitizedString(queryParameters["name"]); + const source = getSanitizedString(queryParameters["source"]); + let lowercasedKind: string = queryParameters["kind"]?.toLowerCase(); + let validKind: string | undefined = ["plugin", "client"].indexOf(lowercasedKind) > -1 ? lowercasedKind : undefined ; + if (!validKind){ + vscode.window.showInformationMessage( + "Invalid parameter 'kind' deeplinked. Actual value: " + lowercasedKind + + "Expected values: 'plugin' or 'client'" + ); + } + let givenLanguage: string | undefined; + try{ + let languageEnumerator = parseGenerationLanguage(queryParameters["language"].toLowerCase()); + givenLanguage = KiotaGenerationLanguage[languageEnumerator]; + }catch (e){ + givenLanguage = undefined; + } + if (!givenLanguage && validKind === "client"){ + let acceptedLanguages: string [] = allGenerationLanguagesToString(); + vscode.window.showInformationMessage("Invalid 'language' parameter deeplinked. Supported languages are : " + acceptedLanguages.join(",")); + } + let providedType: string | undefined; + try{ + let pluginTypeEnumerator : KiotaPluginType = parsePluginType([queryParameters["type"]])[0]; + providedType = KiotaPluginType[pluginTypeEnumerator].toLowerCase(); + }catch(e){ + providedType = undefined; + } + if (!providedType && validKind === "plugin"){ + let acceptedPluginTypes: string [] = Object.keys(KiotaPluginType).filter(x => !Number(x) && x !=='0').map(x => x.toString().toLowerCase()); + vscode.window.showInformationMessage("Invalid parameter 'type' deeplinked. Expected values: " + acceptedPluginTypes.join(",")); + } + + validQueryParams = { + descriptionUrl: descriptionUrl, + name: name, + kind: validKind, + type: providedType, + language: givenLanguage, + source: source, + }; + return validQueryParams; +}