Skip to content

Commit

Permalink
Fix: Kiota extension works without a workspace folder open (#4958)
Browse files Browse the repository at this point in the history
  • Loading branch information
ElinorW authored Jul 26, 2024
1 parent 1ca7aff commit cbea6da
Show file tree
Hide file tree
Showing 7 changed files with 200 additions and 104 deletions.
110 changes: 61 additions & 49 deletions vscode/microsoft-kiota/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ import { getLanguageInformation, getLanguageInformationForDescription } from "./
import { DependenciesViewProvider } from "./dependenciesViewProvider";
import { updateClients } from "./updateClients";
import { ExtensionSettings, getExtensionSettings } from "./extensionSettings";
import { KiotaWorkspace } from "./workspaceTreeProvider";
import { loadTreeView } from "./workspaceTreeProvider";
import { generatePlugin } from "./generatePlugin";
import { CodeLensProvider } from "./codelensProvider";
import { KIOTA_DIRECTORY, KIOTA_WORKSPACE_FILE, dependenciesInfo, extensionId, statusBarCommandId, treeViewFocusCommand, treeViewId } from "./constants";
import { isClientType, isPluginType, updateTreeViewIcons } from "./util";
import { getWorkspaceJsonDirectory, getWorkspaceJsonPath, isClientType, isPluginType, updateTreeViewIcons } from "./util";

let kiotaStatusBarItem: vscode.StatusBarItem;
let kiotaOutputChannel: vscode.LogOutputChannel;
Expand All @@ -38,6 +38,11 @@ let clientOrPluginObject: ClientOrPluginProperties;
let workspaceGenerationType: string;
let config: Partial<GenerateState>;

interface GeneratedOutputState {
outputPath: string;
clientClassName: string;
}

// This method is called when your extension is activated
// Your extension is activated the very first time the command is executed
export async function activate(
Expand All @@ -46,13 +51,12 @@ export async function activate(
kiotaOutputChannel = vscode.window.createOutputChannel("Kiota", {
log: true,
});
const workspaceJsonPath = path.join(vscode.workspace.workspaceFolders?.map(folder => folder.uri.fsPath).join('') || '', KIOTA_DIRECTORY, KIOTA_WORKSPACE_FILE);
const openApiTreeProvider = new OpenApiTreeProvider(context, () => getExtensionSettings(extensionId));
const dependenciesInfoProvider = new DependenciesViewProvider(
context.extensionUri
);
const reporter = new TelemetryReporter(context.extension.packageJSON.telemetryInstrumentationKey);
new KiotaWorkspace(context);
await loadTreeView(context);
let codeLensProvider = new CodeLensProvider();
context.subscriptions.push(
vscode.window.registerUriHandler({
Expand Down Expand Up @@ -127,15 +131,7 @@ export async function activate(
);
return;
}
if (
!vscode.workspace.workspaceFolders ||
vscode.workspace.workspaceFolders.length === 0
) {
await vscode.window.showErrorMessage(
vscode.l10n.t("No workspace folder found, open a folder first")
);
return;
}

let languagesInformation = await getLanguageInformation(context);
config = await generateSteps(
{
Expand All @@ -157,27 +153,50 @@ export async function activate(
);
return;
}

const settings = getExtensionSettings(extensionId);
workspaceGenerationType = config.generationType as string;
let result;
switch (generationType) {
case GenerationType.Client:
await generateClientAndRefreshUI(config, settings, outputPath, selectedPaths);
break;
result = await generateClientAndRefreshUI(config, settings, outputPath, selectedPaths);
break;
case GenerationType.Plugin:
await generatePluginAndRefreshUI(config, settings, outputPath, selectedPaths);
break;
result = await generatePluginAndRefreshUI(config, settings, outputPath, selectedPaths);
break;
case GenerationType.ApiManifest:
await generateManifestAndRefreshUI(config, settings, outputPath, selectedPaths);
break;
result = await generateManifestAndRefreshUI(config, settings, outputPath, selectedPaths);
break;
default:
await vscode.window.showErrorMessage(
vscode.l10n.t("Invalid generation type")
);
break;
return;
}
if (result && getLogEntriesForLevel(result, LogLevel.critical, LogLevel.error).length === 0) {
// Save state before opening the new window
void context.workspaceState.update('generatedOutput', {
outputPath,
config,
clientClassName: config.clientClassName || config.pluginName
} as GeneratedOutputState);
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(context, openApiTreeProvider, config, outputPath);
}
}
}
),
vscode.workspace.onDidChangeWorkspaceFolders(async () => {
const generatedOutput = context.workspaceState.get<GeneratedOutputState>('generatedOutput');
if (generatedOutput) {
const { outputPath} = generatedOutput;
await displayGenerationResults(context, openApiTreeProvider, config, outputPath);
// Clear the state
void context.workspaceState.update('generatedOutput', undefined);
}
}),
registerCommandWithTelemetry(reporter,
`${treeViewId}.searchOrOpenApiDescription`,
async () => {
Expand Down Expand Up @@ -276,7 +295,7 @@ export async function activate(
}),
);

async function generateManifestAndRefreshUI(config: Partial<GenerateState>, settings: ExtensionSettings, outputPath: string, selectedPaths: string[]):Promise<void> {
async function 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,
Expand All @@ -297,7 +316,8 @@ export async function activate(
settings.clearCache,
settings.cleanOutput,
settings.disableValidationRules,
ConsumerOperation.Add
ConsumerOperation.Add,
config.workingDirectory
);
const duration = performance.now() - start;
const errorsCount = result ? getLogEntriesForLevel(result, LogLevel.critical, LogLevel.error).length : 0;
Expand All @@ -311,15 +331,11 @@ export async function activate(
});
if (result)
{
await checkForSuccess(result);
openApiTreeProvider.refreshView();
openApiTreeProvider.setSelectionChanged(false);
await loadLockFile({fsPath: workspaceJsonPath}, openApiTreeProvider, config.pluginName);
await updateTreeViewIcons(treeViewId, false, true);
await exportLogsAndShowErrors(result);
}
return result;
}
async function generatePluginAndRefreshUI(config: Partial<GenerateState>, settings: ExtensionSettings, outputPath: string, selectedPaths: string[]):Promise<void> {
async function 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,
Expand All @@ -340,7 +356,8 @@ export async function activate(
settings.clearCache,
settings.cleanOutput,
settings.disableValidationRules,
ConsumerOperation.Add
ConsumerOperation.Add,
config.workingDirectory
);
const duration = performance.now() - start;
const errorsCount = result ? getLogEntriesForLevel(result, LogLevel.critical, LogLevel.error).length : 0;
Expand All @@ -354,15 +371,11 @@ export async function activate(
});
if (result)
{
await checkForSuccess(result);
openApiTreeProvider.refreshView();
openApiTreeProvider.setSelectionChanged(false);
await loadLockFile({fsPath: workspaceJsonPath}, openApiTreeProvider, config.pluginName);
await updateTreeViewIcons(treeViewId, false, true);
await exportLogsAndShowErrors(result);
}
return result;
}
async function generateClientAndRefreshUI(config: Partial<GenerateState>, settings: ExtensionSettings, outputPath: string, selectedPaths: string[]):Promise<void> {
async function generateClientAndRefreshUI(config: Partial<GenerateState>, settings: ExtensionSettings, outputPath: string, selectedPaths: string[]):Promise<KiotaLogEntry[] | undefined> {
const language =
typeof config.language === "string"
? parseGenerationLanguage(config.language)
Expand Down Expand Up @@ -395,7 +408,8 @@ export async function activate(
settings.languagesSerializationConfiguration[language].deserializers,
settings.structuredMimeTypes,
settings.includeAdditionalData,
ConsumerOperation.Add
ConsumerOperation.Add,
config.workingDirectory
);
const duration = performance.now() - start;
const errorsCount = result ? getLogEntriesForLevel(result, LogLevel.critical, LogLevel.error).length : 0;
Expand All @@ -417,22 +431,20 @@ export async function activate(
dependenciesInfoProvider.update(languagesInformation, language);
await vscode.commands.executeCommand(treeViewFocusCommand);
}
if (typeof config.outputPath === "string" && !openApiTreeProvider.isLockFileLoaded &&
vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders.length > 0 &&
result && getLogEntriesForLevel(result, LogLevel.critical, LogLevel.error).length === 0) {
const WORKSPACE_FOLDER = vscode.workspace.workspaceFolders[0].uri.fsPath;
const KIOTA_WORKSPACE_PATH = path.join(WORKSPACE_FOLDER, KIOTA_DIRECTORY, KIOTA_WORKSPACE_FILE);
await openApiTreeProvider.loadLockFile(KIOTA_WORKSPACE_PATH, config.clientClassName);
}
if (result)
{
await checkForSuccess(result);
openApiTreeProvider.refreshView();
openApiTreeProvider.setSelectionChanged(false);
await loadLockFile({fsPath: workspaceJsonPath}, openApiTreeProvider, config.clientClassName);
await updateTreeViewIcons(treeViewId, false, true);
await exportLogsAndShowErrors(result);
}
return result;
}

async function displayGenerationResults(context: vscode.ExtensionContext, openApiTreeProvider: OpenApiTreeProvider, config: any, outputPath: string) {
const clientNameOrPluginName = config.clientClassName || config.pluginName;
openApiTreeProvider.refreshView();
openApiTreeProvider.setSelectionChanged(false);
const workspaceJsonPath = getWorkspaceJsonPath();
await loadLockFile({fsPath: workspaceJsonPath}, openApiTreeProvider, clientNameOrPluginName );
await updateTreeViewIcons(treeViewId, false, true);
}
async function regenerateClient(clientKey: string, clientObject:any, settings: ExtensionSettings, selectedPaths?: string[]): Promise<void> {
const language =
Expand Down
6 changes: 4 additions & 2 deletions vscode/microsoft-kiota/src/generateClient.ts
Original file line number Diff line number Diff line change
@@ -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 { getWorkspaceJsonDirectory } from "./util";

export function generateClient(context: vscode.ExtensionContext,
descriptionPath: string,
Expand All @@ -19,7 +20,8 @@ export function generateClient(context: vscode.ExtensionContext,
deserializers: string[],
structuredMimeTypes: string[],
includeAdditionalData: boolean,
operation: ConsumerOperation
operation: ConsumerOperation,
workingDirectory: string = getWorkspaceJsonDirectory()
): Promise<KiotaLogEntry[] | undefined> {
return connectToKiota<KiotaLogEntry[]>(context, async (connection) => {
const request = new rpc.RequestType1<GenerationConfiguration, KiotaLogEntry[], void>(
Expand Down Expand Up @@ -47,5 +49,5 @@ export function generateClient(context: vscode.ExtensionContext,
operation: operation
} as GenerationConfiguration,
);
});
}, workingDirectory);
};
6 changes: 4 additions & 2 deletions vscode/microsoft-kiota/src/generatePlugin.ts
Original file line number Diff line number Diff line change
@@ -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 { getWorkspaceJsonDirectory } from "./util";

export function generatePlugin(context: vscode.ExtensionContext,
descriptionPath: string,
Expand All @@ -12,7 +13,8 @@ export function generatePlugin(context: vscode.ExtensionContext,
clearCache: boolean,
cleanOutput: boolean,
disableValidationRules: string[],
operation: ConsumerOperation ): Promise<KiotaLogEntry[] | undefined> {
operation: ConsumerOperation,
workingDirectory: string = getWorkspaceJsonDirectory() ): Promise<KiotaLogEntry[] | undefined> {
return connectToKiota<KiotaLogEntry[]>(context, async (connection) => {
const request = new rpc.RequestType1<GenerationConfiguration, KiotaLogEntry[], void>(
"GeneratePlugin"
Expand All @@ -32,5 +34,5 @@ export function generatePlugin(context: vscode.ExtensionContext,
operation: operation
} as GenerationConfiguration,
);
});
}, workingDirectory);
};
5 changes: 3 additions & 2 deletions vscode/microsoft-kiota/src/kiotaInterop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ import * as vscode from "vscode";
import * as cp from 'child_process';
import * as rpc from 'vscode-jsonrpc/node';
import { ensureKiotaIsPresent, getKiotaPath } from './kiotaInstall';
import { getWorkspaceJsonDirectory } from "./util";

export async function connectToKiota<T>(context: vscode.ExtensionContext, callback:(connection: rpc.MessageConnection) => Promise<T | undefined>): Promise<T | undefined> {
export async function connectToKiota<T>(context: vscode.ExtensionContext, callback:(connection: rpc.MessageConnection) => Promise<T | undefined>, workingDirectory:string = getWorkspaceJsonDirectory()): Promise<T | undefined> {
const kiotaPath = getKiotaPath(context);
await ensureKiotaIsPresent(context);
const childProcess = cp.spawn(kiotaPath, ["rpc"],{
cwd: vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders.length > 0 ? vscode.workspace.workspaceFolders[0].uri.fsPath : undefined,
cwd: workingDirectory,
env: {
...process.env,
// eslint-disable-next-line @typescript-eslint/naming-convention
Expand Down
Loading

0 comments on commit cbea6da

Please sign in to comment.