mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-21 01:25:37 -05:00
Adding Execution Plan Editor to ADS (#18696)
* Pushing Execution Plan Editor * Renaming class Handling error * Awaiting for handlers to be registered * Addressing some PR comments * Fixing return type for provider * Fixing editor id and removing unnecessary overrides * Adding a namespace * adding execution plan namespace * Adding protocol comment * Fixing if logic * Fixing error message * Cleaning up code * cleanup code * Adding help comments * Fixing method call * Using path.ts to get the base file name * converting to lambda functions * Adding comment for run action * Fixing pr comments * Fixing editor label * Fixing doc comments * Adding some more comments * Fixign branding in comments
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import type * as azdata from 'azdata';
|
||||
import { IExecutionPlanService } from 'sql/workbench/services/executionPlan/common/interfaces';
|
||||
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
|
||||
import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
|
||||
interface ExecutionPlanProviderRegisteredEvent {
|
||||
id: string,
|
||||
provider: azdata.executionPlan.ExecutionPlanProvider
|
||||
}
|
||||
export class ExecutionPlanService implements IExecutionPlanService {
|
||||
private _providers: { [handle: string]: azdata.executionPlan.ExecutionPlanProvider; } = Object.create(null);
|
||||
private _onProviderRegister: Emitter<ExecutionPlanProviderRegisteredEvent> = new Emitter<ExecutionPlanProviderRegisteredEvent>();
|
||||
private _providerRegisterEvent: Event<ExecutionPlanProviderRegisteredEvent>;
|
||||
constructor(
|
||||
@ICapabilitiesService private _capabilitiesService: ICapabilitiesService,
|
||||
@IQuickInputService private _quickInputService: IQuickInputService,
|
||||
@IExtensionService private _extensionService: IExtensionService
|
||||
) {
|
||||
this._providerRegisterEvent = this._onProviderRegister.event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the actions using the provider that supports the fileFormat provided.
|
||||
* @param fileFormat fileformat of the underlying execution plan file. It is used to get the provider that support it.
|
||||
* @param action executionPlanService action to be performed.
|
||||
*/
|
||||
private async _runAction<T>(fileFormat: string, action: (handler: azdata.executionPlan.ExecutionPlanProvider) => Thenable<T>): Promise<T> {
|
||||
let providers = Object.keys(this._capabilitiesService.providers);
|
||||
if (!providers) {
|
||||
providers = await new Promise(resolve => {
|
||||
this._capabilitiesService.onCapabilitiesRegistered(e => {
|
||||
resolve(Object.keys(this._capabilitiesService.providers));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
let epProviders: string[] = [];
|
||||
for (let i = 0; i < providers.length; i++) {
|
||||
const providerCapabilities = this._capabilitiesService.getCapabilities(providers[i]);
|
||||
if (providerCapabilities.connection.supportedExecutionPlanFileExtensions?.includes(fileFormat)) {
|
||||
epProviders.push(providers[i]);
|
||||
}
|
||||
}
|
||||
|
||||
let selectedProvider: string;
|
||||
|
||||
/**
|
||||
* This handles the case when multiple providers support the same execution plan extension.
|
||||
* The code shows a quick pick and lets user select the provider they want to open the execution plan file with.
|
||||
*/
|
||||
if (epProviders.length > 1) {
|
||||
const providerQuickPick = this._quickInputService.createQuickPick<IQuickPickItem>();
|
||||
providerQuickPick.items = epProviders.map(p => {
|
||||
return {
|
||||
label: p,
|
||||
ariaLabel: p
|
||||
};
|
||||
});
|
||||
providerQuickPick.placeholder = localize('selectExecutionPlanProvider', "Select a provider to open execution plan");
|
||||
|
||||
selectedProvider = await new Promise((resolve) => {
|
||||
providerQuickPick.onDidChangeSelection(e => {
|
||||
providerQuickPick.hide();
|
||||
resolve(e[0].label);
|
||||
});
|
||||
providerQuickPick.show();
|
||||
});
|
||||
} else {
|
||||
selectedProvider = epProviders[0];
|
||||
}
|
||||
|
||||
|
||||
if (!selectedProvider) {
|
||||
return Promise.reject(new Error(localize('providerIdNotValidError', "Valid provider is required in order to interact with ExecutionPlanService")));
|
||||
}
|
||||
await this._extensionService.whenInstalledExtensionsRegistered();
|
||||
let handler = this._providers[selectedProvider];
|
||||
if (!handler) {
|
||||
handler = await new Promise((resolve, reject) => {
|
||||
this._providerRegisterEvent(e => {
|
||||
if (e.id === selectedProvider) {
|
||||
resolve(e.provider);
|
||||
}
|
||||
});
|
||||
setTimeout(() => {
|
||||
/**
|
||||
* Handling a possible edge case where provider registered event
|
||||
* might have been called before we await for it.
|
||||
*/
|
||||
resolve(this._providers[selectedProvider]);
|
||||
}, 30000);
|
||||
});
|
||||
}
|
||||
if (handler) {
|
||||
return Promise.resolve(action(handler));
|
||||
} else {
|
||||
return Promise.reject(new Error(localize('noHandlerRegistered', "No valid execution plan handler is registered")));
|
||||
}
|
||||
}
|
||||
|
||||
registerProvider(providerId: string, provider: azdata.executionPlan.ExecutionPlanProvider): void {
|
||||
if (this._providers[providerId]) {
|
||||
throw new Error(`A execution plan provider with id "${providerId}" is already registered`);
|
||||
}
|
||||
this._providers[providerId] = provider;
|
||||
this._onProviderRegister.fire({
|
||||
id: providerId,
|
||||
provider: provider
|
||||
});
|
||||
}
|
||||
|
||||
getExecutionPlan(planFile: azdata.executionPlan.ExecutionPlanGraphInfo): Promise<azdata.executionPlan.GetExecutionPlanResult> {
|
||||
return this._runAction(planFile.graphFileType, (runner) => {
|
||||
return runner.getExecutionPlan(planFile);
|
||||
});
|
||||
}
|
||||
|
||||
getSupportedExecutionPlanExtensionsForProvider(providerId: string): string[] | undefined {
|
||||
return this._capabilitiesService.getCapabilities(providerId).connection.supportedExecutionPlanFileExtensions;
|
||||
}
|
||||
|
||||
_serviceBrand: undefined;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import type * as azdata from 'azdata';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
|
||||
export const SERVICE_ID = 'executionPlanService';
|
||||
|
||||
export const IExecutionPlanService = createDecorator<IExecutionPlanService>(SERVICE_ID);
|
||||
|
||||
export interface IExecutionPlanService {
|
||||
_serviceBrand: undefined;
|
||||
|
||||
/**
|
||||
* Registers an execution plan service provider.
|
||||
*/
|
||||
registerProvider(providerId: string, provider: azdata.executionPlan.ExecutionPlanProvider): void;
|
||||
/**
|
||||
* Gets an execution plan for the given planFile.
|
||||
*/
|
||||
getExecutionPlan(planFile: azdata.executionPlan.ExecutionPlanGraphInfo): Promise<azdata.executionPlan.GetExecutionPlanResult>;
|
||||
|
||||
/**
|
||||
* Get execution plan file extensions supported by the provider.
|
||||
*/
|
||||
getSupportedExecutionPlanExtensionsForProvider(providerId: string): string[];
|
||||
}
|
||||
Reference in New Issue
Block a user