-
Notifications
You must be signed in to change notification settings - Fork 229
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Task: create generation command (#5524)
- Loading branch information
Showing
9 changed files
with
453 additions
and
366 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export interface GeneratedOutputState { | ||
outputPath: string; | ||
clientClassName: string; | ||
} |
306 changes: 306 additions & 0 deletions
306
vscode/microsoft-kiota/src/commands/generate/generateClientCommand.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,306 @@ | ||
import TelemetryReporter from "@vscode/extension-telemetry"; | ||
import * as path from "path"; | ||
import * as vscode from "vscode"; | ||
|
||
import { extensionId, treeViewFocusCommand, treeViewId } from "../../constants"; | ||
import { DependenciesViewProvider } from "../../dependenciesViewProvider"; | ||
import { GenerationType, KiotaGenerationLanguage, KiotaPluginType } from "../../enums"; | ||
import { ExtensionSettings, getExtensionSettings } from "../../extensionSettings"; | ||
import { generateClient } from "../../generateClient"; | ||
import { GeneratedOutputState } from "../../GeneratedOutputState"; | ||
import { generatePlugin } from "../../generatePlugin"; | ||
import { getLanguageInformation, getLanguageInformationForDescription } from "../../getLanguageInformation"; | ||
import { setGenerationConfiguration } from "../../handlers/configurationHandler"; | ||
import { clearDeepLinkParams, getDeepLinkParams } from "../../handlers/deepLinkParamsHandler"; | ||
import { setWorkspaceGenerationType } from "../../handlers/workspaceGenerationTypeHandler"; | ||
import { ConsumerOperation, generationLanguageToString, getLogEntriesForLevel, KiotaLogEntry, LogLevel } from "../../kiotaInterop"; | ||
import { OpenApiTreeProvider } from "../../openApiTreeProvider"; | ||
import { GenerateState, generateSteps } from "../../steps"; | ||
import { getSanitizedString, getWorkspaceJsonDirectory, parseGenerationLanguage, parseGenerationType, parsePluginType, updateTreeViewIcons } from "../../util"; | ||
import { isDeeplinkEnabled, transformToGenerationConfig } from "../../utilities/deep-linking"; | ||
import { exportLogsAndShowErrors } from "../../utilities/logging"; | ||
import { showUpgradeWarningMessage } from "../../utilities/messaging"; | ||
import { Command } from "../Command"; | ||
import { checkForSuccess, displayGenerationResults } from "./generation-util"; | ||
|
||
export class GenerateClientCommand extends Command { | ||
private _openApiTreeProvider: OpenApiTreeProvider; | ||
private _context: vscode.ExtensionContext; | ||
private _dependenciesViewProvider: DependenciesViewProvider; | ||
|
||
constructor(openApiTreeProvider: OpenApiTreeProvider, context: vscode.ExtensionContext, dependenciesViewProvider: DependenciesViewProvider) { | ||
super(); | ||
this._openApiTreeProvider = openApiTreeProvider; | ||
this._context = context; | ||
this._dependenciesViewProvider = dependenciesViewProvider; | ||
} | ||
|
||
public getName(): string { | ||
return `${treeViewId}.generateClient`; | ||
} | ||
|
||
public async execute(): Promise<void> { | ||
const deepLinkParams = getDeepLinkParams(); | ||
const selectedPaths = this._openApiTreeProvider.getSelectedPaths(); | ||
if (selectedPaths.length === 0) { | ||
await vscode.window.showErrorMessage( | ||
vscode.l10n.t("No endpoints selected, select endpoints first") | ||
); | ||
return; | ||
} | ||
|
||
let languagesInformation = await getLanguageInformation(this._context); | ||
let availableStateInfo: Partial<GenerateState>; | ||
if (isDeeplinkEnabled(deepLinkParams)) { | ||
if (!deepLinkParams.name && this._openApiTreeProvider.apiTitle) { | ||
deepLinkParams.name = getSanitizedString(this._openApiTreeProvider.apiTitle); | ||
} | ||
availableStateInfo = transformToGenerationConfig(deepLinkParams); | ||
} else { | ||
const pluginName = getSanitizedString(this._openApiTreeProvider.apiTitle); | ||
availableStateInfo = { | ||
clientClassName: this._openApiTreeProvider.clientClassName, | ||
clientNamespaceName: this._openApiTreeProvider.clientNamespaceName, | ||
language: this._openApiTreeProvider.language, | ||
outputPath: this._openApiTreeProvider.outputPath, | ||
pluginName | ||
}; | ||
} | ||
let config = await generateSteps( | ||
availableStateInfo, | ||
languagesInformation, | ||
deepLinkParams | ||
); | ||
setGenerationConfiguration(config); | ||
const generationType = parseGenerationType(config.generationType); | ||
const outputPath = typeof config.outputPath === "string" | ||
? config.outputPath | ||
: "./output"; | ||
await showUpgradeWarningMessage(outputPath, this._context); | ||
if (!this._openApiTreeProvider.descriptionUrl) { | ||
await vscode.window.showErrorMessage( | ||
vscode.l10n.t("No description found, select a description first") | ||
); | ||
return; | ||
} | ||
|
||
const settings = getExtensionSettings(extensionId); | ||
setWorkspaceGenerationType(config.generationType as string); | ||
let result; | ||
switch (generationType) { | ||
case GenerationType.Client: | ||
result = await this.generateClientAndRefreshUI(config, settings, outputPath, selectedPaths); | ||
break; | ||
case GenerationType.Plugin: | ||
result = await this.generatePluginAndRefreshUI(config, settings, outputPath, selectedPaths); | ||
break; | ||
case GenerationType.ApiManifest: | ||
result = await this.generateManifestAndRefreshUI(config, settings, outputPath, selectedPaths); | ||
break; | ||
default: | ||
await vscode.window.showErrorMessage( | ||
vscode.l10n.t("Invalid generation type") | ||
); | ||
return; | ||
} | ||
if (result && getLogEntriesForLevel(result, LogLevel.critical, LogLevel.error).length === 0) { | ||
// Save state before opening the new window | ||
const outputState = { | ||
outputPath, | ||
config, | ||
clientClassName: config.clientClassName || config.pluginName | ||
}; | ||
void this._context.workspaceState.update('generatedOutput', outputState as GeneratedOutputState); | ||
|
||
const pathOfSpec = path.join(outputPath, `${outputState.clientClassName?.toLowerCase()}-openapi.yml`); | ||
const pathPluginManifest = path.join(outputPath, `${outputState.clientClassName?.toLowerCase()}-apiplugin.json`); | ||
if (deepLinkParams.source?.toLowerCase() === 'ttk') { | ||
try { | ||
await vscode.commands.executeCommand( | ||
'fx-extension.createprojectfromkiota', | ||
[ | ||
pathOfSpec, | ||
pathPluginManifest, | ||
deepLinkParams.ttkContext ? deepLinkParams.ttkContext : undefined | ||
] | ||
); | ||
this._openApiTreeProvider.closeDescription(); | ||
await updateTreeViewIcons(treeViewId, false); | ||
} catch (error) { | ||
const reporter = new TelemetryReporter(this._context.extension.packageJSON.telemetryInstrumentationKey); | ||
reporter.sendTelemetryEvent("DeepLinked fx-extension.createprojectfromkiota", { | ||
"error": JSON.stringify(error) | ||
}); | ||
} | ||
} else { | ||
if (!vscode.workspace.workspaceFolders || vscode.workspace.workspaceFolders.length === 0) { | ||
await vscode.commands.executeCommand('vscode.openFolder', vscode.Uri.file(config.workingDirectory ?? getWorkspaceJsonDirectory()), true); | ||
} else { | ||
await displayGenerationResults(this._openApiTreeProvider, config); | ||
} | ||
} | ||
|
||
clearDeepLinkParams(); // Clear the state after the generation | ||
} | ||
} | ||
|
||
private async generateManifestAndRefreshUI(config: Partial<GenerateState>, settings: ExtensionSettings, outputPath: string, selectedPaths: string[]): Promise<KiotaLogEntry[] | undefined> { | ||
const pluginTypes = KiotaPluginType.ApiManifest; | ||
const result = await vscode.window.withProgress({ | ||
location: vscode.ProgressLocation.Notification, | ||
cancellable: false, | ||
title: vscode.l10n.t("Generating manifest...") | ||
}, async (progress, _) => { | ||
const start = performance.now(); | ||
const result = await generatePlugin( | ||
this._context, | ||
this._openApiTreeProvider.descriptionUrl, | ||
outputPath, | ||
[pluginTypes], | ||
selectedPaths, | ||
[], | ||
typeof config.pluginName === "string" | ||
? config.pluginName | ||
: "ApiClient", | ||
settings.clearCache, | ||
settings.cleanOutput, | ||
settings.disableValidationRules, | ||
ConsumerOperation.Add, | ||
config.workingDirectory | ||
); | ||
const duration = performance.now() - start; | ||
const errorsCount = result ? getLogEntriesForLevel(result, LogLevel.critical, LogLevel.error).length : 0; | ||
const reporter = new TelemetryReporter(this._context.extension.packageJSON.telemetryInstrumentationKey); | ||
reporter.sendRawTelemetryEvent(`${extensionId}.generateManifest.completed`, { | ||
"pluginType": pluginTypes.toString(), | ||
"errorsCount": errorsCount.toString(), | ||
}, { | ||
"duration": duration, | ||
}); | ||
return result; | ||
}); | ||
if (result) { | ||
const isSuccess = await checkForSuccess(result); | ||
if (!isSuccess) { | ||
await exportLogsAndShowErrors(result); | ||
} | ||
void vscode.window.showInformationMessage(vscode.l10n.t('Generation completed successfully.')); | ||
} | ||
return result; | ||
} | ||
private async generatePluginAndRefreshUI(config: Partial<GenerateState>, settings: ExtensionSettings, outputPath: string, selectedPaths: string[]): Promise<KiotaLogEntry[] | undefined> { | ||
const pluginTypes = Array.isArray(config.pluginTypes) ? parsePluginType(config.pluginTypes) : [KiotaPluginType.ApiPlugin]; | ||
const result = await vscode.window.withProgress({ | ||
location: vscode.ProgressLocation.Notification, | ||
cancellable: false, | ||
title: vscode.l10n.t("Generating plugin...") | ||
}, async (progress, _) => { | ||
const start = performance.now(); | ||
const result = await generatePlugin( | ||
this._context, | ||
this._openApiTreeProvider.descriptionUrl, | ||
outputPath, | ||
pluginTypes, | ||
selectedPaths, | ||
[], | ||
typeof config.pluginName === "string" | ||
? config.pluginName | ||
: "ApiClient", | ||
settings.clearCache, | ||
settings.cleanOutput, | ||
settings.disableValidationRules, | ||
ConsumerOperation.Add, | ||
config.workingDirectory | ||
); | ||
const duration = performance.now() - start; | ||
const errorsCount = result ? getLogEntriesForLevel(result, LogLevel.critical, LogLevel.error).length : 0; | ||
const reporter = new TelemetryReporter(this._context.extension.packageJSON.telemetryInstrumentationKey); | ||
reporter.sendRawTelemetryEvent(`${extensionId}.generatePlugin.completed`, { | ||
"pluginType": pluginTypes.toString(), | ||
"errorsCount": errorsCount.toString(), | ||
}, { | ||
"duration": duration, | ||
}); | ||
return result; | ||
}); | ||
if (result) { | ||
const isSuccess = await checkForSuccess(result); | ||
if (!isSuccess) { | ||
await exportLogsAndShowErrors(result); | ||
} | ||
const deepLinkParams = getDeepLinkParams(); | ||
const isttkIntegration = deepLinkParams.source?.toLowerCase() === 'ttk'; | ||
if (!isttkIntegration) { | ||
void vscode.window.showInformationMessage(vscode.l10n.t('Plugin generated successfully.')); | ||
} | ||
} | ||
return result; | ||
} | ||
private async generateClientAndRefreshUI(config: Partial<GenerateState>, settings: ExtensionSettings, outputPath: string, selectedPaths: string[]): Promise<KiotaLogEntry[] | undefined> { | ||
const language = | ||
typeof config.language === "string" | ||
? parseGenerationLanguage(config.language) | ||
: KiotaGenerationLanguage.CSharp; | ||
const result = await vscode.window.withProgress({ | ||
location: vscode.ProgressLocation.Notification, | ||
cancellable: false, | ||
title: vscode.l10n.t("Generating client...") | ||
}, async (progress, _) => { | ||
const start = performance.now(); | ||
const result = await generateClient( | ||
this._context, | ||
this._openApiTreeProvider.descriptionUrl, | ||
outputPath, | ||
language, | ||
selectedPaths, | ||
[], | ||
typeof config.clientClassName === "string" | ||
? config.clientClassName | ||
: "ApiClient", | ||
typeof config.clientNamespaceName === "string" | ||
? config.clientNamespaceName | ||
: "ApiSdk", | ||
settings.backingStore, | ||
settings.clearCache, | ||
settings.cleanOutput, | ||
settings.excludeBackwardCompatible, | ||
settings.disableValidationRules, | ||
settings.languagesSerializationConfiguration[language].serializers, | ||
settings.languagesSerializationConfiguration[language].deserializers, | ||
settings.structuredMimeTypes, | ||
settings.includeAdditionalData, | ||
ConsumerOperation.Add, | ||
config.workingDirectory | ||
); | ||
const duration = performance.now() - start; | ||
const errorsCount = result ? getLogEntriesForLevel(result, LogLevel.critical, LogLevel.error).length : 0; | ||
const reporter = new TelemetryReporter(this._context.extension.packageJSON.telemetryInstrumentationKey); | ||
reporter.sendRawTelemetryEvent(`${extensionId}.generateClient.completed`, { | ||
"language": generationLanguageToString(language), | ||
"errorsCount": errorsCount.toString(), | ||
}, { | ||
"duration": duration, | ||
}); | ||
return result; | ||
}); | ||
|
||
let languagesInformation = await getLanguageInformationForDescription( | ||
this._context, | ||
this._openApiTreeProvider.descriptionUrl, | ||
settings.clearCache | ||
); | ||
if (languagesInformation) { | ||
this._dependenciesViewProvider.update(languagesInformation, language); | ||
await vscode.commands.executeCommand(treeViewFocusCommand); | ||
} | ||
if (result) { | ||
const isSuccess = await checkForSuccess(result); | ||
if (!isSuccess) { | ||
await exportLogsAndShowErrors(result); | ||
} | ||
void vscode.window.showInformationMessage(vscode.l10n.t('Generation completed successfully.')); | ||
} | ||
return result; | ||
} | ||
|
||
} |
28 changes: 28 additions & 0 deletions
28
vscode/microsoft-kiota/src/commands/generate/generation-util.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import * as vscode from "vscode"; | ||
|
||
import { treeViewId } from "../../constants"; | ||
import { KiotaLogEntry } from "../../kiotaInterop"; | ||
import { OpenApiTreeProvider } from "../../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(); | ||
const workspaceJsonPath = getWorkspaceJsonPath(); | ||
await loadWorkspaceFile({ fsPath: workspaceJsonPath }, openApiTreeProvider, clientNameOrPluginName); | ||
await vscode.commands.executeCommand('kiota.workspace.refresh'); | ||
openApiTreeProvider.resetInitialState(); | ||
await updateTreeViewIcons(treeViewId, false, true); | ||
} |
Oops, something went wrong.