Add extension API for using the same file browser dialog that's used for Restore (#24151)

Co-authored-by: Charles Gagnon <chgagnon@microsoft.com>
This commit is contained in:
Cory Rivera
2023-08-17 14:39:55 -07:00
committed by GitHub
parent 0e7942913b
commit a2336a2617
11 changed files with 124 additions and 14 deletions

View File

@@ -2011,6 +2011,27 @@ declare module 'azdata' {
*/ */
isPrimary: boolean; isPrimary: boolean;
} }
export interface FileFilters {
/**
* The label to display in the file filter field next to the list of filters.
*/
label: string;
/**
* The filters to limit what files are visible in the file browser (e.g. '*.sql' for SQL files).
*/
filters: string[];
}
/**
* Opens a dialog to select a file path on the specified server's machine. Note: The dialog for just browsing local
* files without any connection is opened via vscode.window.showOpenDialog.
* @param connectionUri The URI of the connection to the target server
* @param targetPath The file path on the server machine to open by default in the dialog
* @param fileFilters The filters used to limit which files are displayed in the file browser
* @returns The path of the file chosen from the dialog, and undefined if the dialog is closed without selecting anything.
*/
export function openServerFileBrowserDialog(connectionUri: string, targetPath: string, fileFilters: FileFilters[]): Thenable<string | undefined>;
} }
export interface TableComponent { export interface TableComponent {

View File

@@ -24,3 +24,4 @@ import './mainThreadResourceProvider';
import './mainThreadErrorDiagnostics'; import './mainThreadErrorDiagnostics';
import './mainThreadTasks'; import './mainThreadTasks';
import './mainThreadWorkspace'; import './mainThreadWorkspace';
import './mainThreadWindow';

View File

@@ -0,0 +1,36 @@
/*---------------------------------------------------------------------------------------------
* 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 { MainThreadWindowShape } from 'sql/workbench/api/common/sqlExtHost.protocol';
import { IFileBrowserDialogController } from 'sql/workbench/services/fileBrowser/common/fileBrowserDialogController';
import { Disposable } from 'vs/base/common/lifecycle';
import { SqlMainContext } from 'vs/workbench/api/common/extHost.protocol';
import { IExtHostContext, extHostNamedCustomer } from 'vs/workbench/services/extensions/common/extHostCustomers';
@extHostNamedCustomer(SqlMainContext.MainThreadWindow)
export class MainThreadWindow extends Disposable implements MainThreadWindowShape {
constructor(
extHostContext: IExtHostContext,
@IFileBrowserDialogController private _fileBrowserDialogService: IFileBrowserDialogController
) {
super();
}
public async $openServerFileBrowserDialog(connectionUri: string, targetPath: string, fileFilters: azdata.window.FileFilters[]): Promise<string | undefined> {
let completion = new Promise<string | undefined>((resolve, reject) => {
try {
const handleOnClosed = (path: string | undefined) => {
resolve(path);
};
this._fileBrowserDialogService.showDialog(connectionUri, targetPath, fileFilters, '', true, handleOnClosed);
} catch (error) {
reject(error);
}
});
return await completion;
}
}

View File

@@ -0,0 +1,23 @@
/*---------------------------------------------------------------------------------------------
* 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 { IMainContext } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostWindowShape, MainThreadWindowShape } from 'sql/workbench/api/common/sqlExtHost.protocol';
import { SqlMainContext } from 'vs/workbench/api/common/extHost.protocol';
export class ExtHostWindow implements ExtHostWindowShape {
private readonly _proxy: MainThreadWindowShape;
constructor(_mainContext: IMainContext) {
this._proxy = _mainContext.getProxy(SqlMainContext.MainThreadWindow);
}
$openServerFileBrowserDialog(connectionUri: string, targetPath: string, fileFilters: azdata.window.FileFilters[]): Promise<string | undefined> {
return this._proxy.$openServerFileBrowserDialog(connectionUri, targetPath, fileFilters);
}
}

View File

@@ -42,6 +42,7 @@ import { ExtHostAzureBlob } from 'sql/workbench/api/common/extHostAzureBlob';
import { ExtHostAzureAccount } from 'sql/workbench/api/common/extHostAzureAccount'; import { ExtHostAzureAccount } from 'sql/workbench/api/common/extHostAzureAccount';
import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService'; import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService';
import { AuthenticationType } from 'sql/platform/connection/common/constants'; import { AuthenticationType } from 'sql/platform/connection/common/constants';
import { ExtHostWindow } from 'sql/workbench/api/common/extHostWindow';
export interface IAzdataExtensionApiFactory { export interface IAzdataExtensionApiFactory {
(extension: IExtensionDescription): typeof azdata; (extension: IExtensionDescription): typeof azdata;
@@ -102,6 +103,7 @@ export function createAdsApiFactory(accessor: ServicesAccessor): IAdsExtensionAp
const extHostNotebook = rpcProtocol.set(SqlExtHostContext.ExtHostNotebook, new ExtHostNotebook(rpcProtocol)); const extHostNotebook = rpcProtocol.set(SqlExtHostContext.ExtHostNotebook, new ExtHostNotebook(rpcProtocol));
const extHostExtensionManagement = rpcProtocol.set(SqlExtHostContext.ExtHostExtensionManagement, new ExtHostExtensionManagement(rpcProtocol)); const extHostExtensionManagement = rpcProtocol.set(SqlExtHostContext.ExtHostExtensionManagement, new ExtHostExtensionManagement(rpcProtocol));
const extHostWorkspace = rpcProtocol.set(SqlExtHostContext.ExtHostWorkspace, new ExtHostWorkspace(rpcProtocol)); const extHostWorkspace = rpcProtocol.set(SqlExtHostContext.ExtHostWorkspace, new ExtHostWorkspace(rpcProtocol));
const extHostWindow = rpcProtocol.set(SqlExtHostContext.ExtHostWindow, new ExtHostWindow(rpcProtocol));
return { return {
azdata: function (extension: IExtensionDescription): typeof azdata { azdata: function (extension: IExtensionDescription): typeof azdata {
// namespace: connection // namespace: connection
@@ -480,6 +482,9 @@ export function createAdsApiFactory(accessor: ServicesAccessor): IAdsExtensionAp
MessageLevel: sqlExtHostTypes.MessageLevel, MessageLevel: sqlExtHostTypes.MessageLevel,
openCustomErrorDialog(options: sqlExtHostTypes.IErrorDialogOptions): Thenable<string | undefined> { openCustomErrorDialog(options: sqlExtHostTypes.IErrorDialogOptions): Thenable<string | undefined> {
return extHostModelViewDialog.openCustomErrorDialog(options); return extHostModelViewDialog.openCustomErrorDialog(options);
},
openServerFileBrowserDialog(connectionUri: string, targetPath: string, fileFilters: azdata.window.FileFilters[]): Thenable<string | undefined> {
return extHostWindow.$openServerFileBrowserDialog(connectionUri, targetPath, fileFilters);
} }
}; };

View File

@@ -827,12 +827,20 @@ export interface ExtHostWorkspaceShape {
$saveAndEnterWorkspace(workspaceFile: vscode.Uri): Promise<void>; $saveAndEnterWorkspace(workspaceFile: vscode.Uri): Promise<void>;
} }
export interface ExtHostWindowShape {
$openServerFileBrowserDialog(connectionUri: string, targetPath: string, fileFilters: azdata.window.FileFilters[]): Promise<string | undefined>;
}
export interface MainThreadWorkspaceShape { export interface MainThreadWorkspaceShape {
$createAndEnterWorkspace(folder: vscode.Uri, workspaceFile: vscode.Uri): Promise<void>; $createAndEnterWorkspace(folder: vscode.Uri, workspaceFile: vscode.Uri): Promise<void>;
$enterWorkspace(workspaceFile: vscode.Uri): Promise<void>; $enterWorkspace(workspaceFile: vscode.Uri): Promise<void>;
$saveAndEnterWorkspace(workspaceFile: vscode.Uri): Promise<void>; $saveAndEnterWorkspace(workspaceFile: vscode.Uri): Promise<void>;
} }
export interface MainThreadWindowShape {
$openServerFileBrowserDialog(connectionUri: string, targetPath: string, fileFilters: azdata.window.FileFilters[]): Promise<string | undefined>;
}
export interface MainThreadBackgroundTaskManagementShape extends IDisposable { export interface MainThreadBackgroundTaskManagementShape extends IDisposable {
$registerTask(taskInfo: azdata.TaskInfo): void; $registerTask(taskInfo: azdata.TaskInfo): void;
$updateTask(taskProgressInfo: azdata.TaskProgressInfo): void; $updateTask(taskProgressInfo: azdata.TaskProgressInfo): void;

View File

@@ -766,7 +766,7 @@ export class BackupComponent extends AngularDisposable {
} }
} }
private handleFilePathAdded(filepath: string): void { private handleFilePathAdded(filepath?: string): void {
if (filepath && !this.backupPathTypePairs![filepath]) { if (filepath && !this.backupPathTypePairs![filepath]) {
if ((this.getBackupPathCount() < BackupConstants.maxDevices)) { if ((this.getBackupPathCount() < BackupConstants.maxDevices)) {
this.backupPathTypePairs![filepath] = BackupConstants.MediaDeviceType.File; this.backupPathTypePairs![filepath] = BackupConstants.MediaDeviceType.File;

View File

@@ -25,7 +25,7 @@ export class FileBrowserDialogController implements IFileBrowserDialogController
fileFilters: [{ label: string, filters: string[] }], fileFilters: [{ label: string, filters: string[] }],
fileValidationServiceType: string, fileValidationServiceType: string,
isWide: boolean, isWide: boolean,
handleOnOk: (path: string) => void handleOnClosed: (path: string | undefined) => void
): void { ): void {
if (!this._fileBrowserDialog) { if (!this._fileBrowserDialog) {
this._fileBrowserDialog = this._instantiationService.createInstance(FileBrowserDialog, localize('filebrowser.selectFile', "Select a file")); this._fileBrowserDialog = this._instantiationService.createInstance(FileBrowserDialog, localize('filebrowser.selectFile', "Select a file"));
@@ -33,7 +33,17 @@ export class FileBrowserDialogController implements IFileBrowserDialogController
} }
this._fileBrowserDialog.setWide(isWide); this._fileBrowserDialog.setWide(isWide);
this._fileBrowserDialog.onOk((filepath) => handleOnOk(filepath)); var onOK = this._fileBrowserDialog.onOk((filepath) => handleOnClosed(filepath));
var onClosed = this._fileBrowserDialog.onClosed((hideReason) => {
if (hideReason !== 'ok') {
handleOnClosed(undefined);
}
onOK.dispose();
onClosed.dispose();
this._fileBrowserDialog.dispose();
this._fileBrowserDialog = undefined;
});
this._fileBrowserDialog.open(ownerUri, expandPath, fileFilters, fileValidationServiceType); this._fileBrowserDialog.open(ownerUri, expandPath, fileFilters, fileValidationServiceType);
} }
} }

View File

@@ -16,5 +16,5 @@ export interface IFileBrowserDialogController {
fileFilters: { label: string, filters: string[] }[], fileFilters: { label: string, filters: string[] }[],
fileValidationServiceType: string, fileValidationServiceType: string,
isWide: boolean, isWide: boolean,
handleOnOk: (path: string) => void): void; handleOnOk: (path: string | undefined) => void): void;
} }

View File

@@ -725,16 +725,18 @@ export class RestoreDialog extends Modal {
.then(url => this._urlInputBox!.value = url); .then(url => this._urlInputBox!.value = url);
} }
private onFileBrowsed(filepath: string): void { private onFileBrowsed(filepath?: string): void {
const oldFilePath = this._filePathInputBox!.value; if (filepath) {
if (strings.isFalsyOrWhitespace(this._filePathInputBox!.value)) { const oldFilePath = this._filePathInputBox!.value;
this._filePathInputBox!.value = filepath; if (strings.isFalsyOrWhitespace(this._filePathInputBox!.value)) {
} else { this._filePathInputBox!.value = filepath;
this._filePathInputBox!.value = this._filePathInputBox!.value + ', ' + filepath; } else {
} this._filePathInputBox!.value = this._filePathInputBox!.value + ', ' + filepath;
}
if (oldFilePath !== this._filePathInputBox!.value) { if (oldFilePath !== this._filePathInputBox!.value) {
this.onFilePathChanged(this._filePathInputBox!.value); this.onFilePathChanged(this._filePathInputBox!.value);
}
} }
} }

View File

@@ -87,11 +87,13 @@ import {
MainThreadModalDialogShape, MainThreadModelViewDialogShape, MainThreadModelViewShape, MainThreadNotebookDocumentsAndEditorsShape, MainThreadModalDialogShape, MainThreadModelViewDialogShape, MainThreadModelViewShape, MainThreadNotebookDocumentsAndEditorsShape,
MainThreadObjectExplorerShape, MainThreadQueryEditorShape, MainThreadResourceProviderShape, MainThreadErrorDiagnosticsShape, MainThreadTasksShape, MainThreadObjectExplorerShape, MainThreadQueryEditorShape, MainThreadResourceProviderShape, MainThreadErrorDiagnosticsShape, MainThreadTasksShape,
MainThreadNotebookShape as SqlMainThreadNotebookShape, MainThreadWorkspaceShape as SqlMainThreadWorkspaceShape, MainThreadNotebookShape as SqlMainThreadNotebookShape, MainThreadWorkspaceShape as SqlMainThreadWorkspaceShape,
MainThreadWindowShape as SqlMainThreadWindowShape,
ExtHostAccountManagementShape, ExtHostAzureAccountShape, ExtHostConnectionManagementShape, ExtHostCredentialManagementShape, ExtHostAccountManagementShape, ExtHostAzureAccountShape, ExtHostConnectionManagementShape, ExtHostCredentialManagementShape,
ExtHostDataProtocolShape, ExtHostObjectExplorerShape, ExtHostResourceProviderShape, ExtHostErrorDiagnosticsShape, ExtHostModalDialogsShape, ExtHostTasksShape, ExtHostDataProtocolShape, ExtHostObjectExplorerShape, ExtHostResourceProviderShape, ExtHostErrorDiagnosticsShape, ExtHostModalDialogsShape, ExtHostTasksShape,
ExtHostBackgroundTaskManagementShape, ExtHostDashboardWebviewsShape, ExtHostModelViewShape, ExtHostModelViewTreeViewsShape, ExtHostBackgroundTaskManagementShape, ExtHostDashboardWebviewsShape, ExtHostModelViewShape, ExtHostModelViewTreeViewsShape,
ExtHostDashboardShape, ExtHostModelViewDialogShape, ExtHostQueryEditorShape, ExtHostExtensionManagementShape, ExtHostAzureBlobShape, ExtHostDashboardShape, ExtHostModelViewDialogShape, ExtHostQueryEditorShape, ExtHostExtensionManagementShape, ExtHostAzureBlobShape,
ExtHostNotebookShape as SqlExtHostNotebookShape, ExtHostWorkspaceShape as SqlExtHostWorkspaceShape, ExtHostNotebookShape as SqlExtHostNotebookShape, ExtHostWorkspaceShape as SqlExtHostWorkspaceShape,
ExtHostWindowShape as SqlExtHostWindowShape,
ExtHostNotebookDocumentsAndEditorsShape as SqlExtHostNotebookDocumentsAndEditorsShape, ExtHostNotebookDocumentsAndEditorsShape as SqlExtHostNotebookDocumentsAndEditorsShape,
ExtHostPerfShape, ExtHostPerfShape,
MainThreadPerfShape MainThreadPerfShape
@@ -2684,7 +2686,8 @@ export const SqlMainContext = {
MainThreadExtensionManagement: createProxyIdentifier<MainThreadExtensionManagementShape>('MainThreadExtensionManagement'), MainThreadExtensionManagement: createProxyIdentifier<MainThreadExtensionManagementShape>('MainThreadExtensionManagement'),
MainThreadWorkspace: createProxyIdentifier<SqlMainThreadWorkspaceShape>('MainThreadWorkspace'), MainThreadWorkspace: createProxyIdentifier<SqlMainThreadWorkspaceShape>('MainThreadWorkspace'),
MainThreadAzureBlob: createProxyIdentifier<MainThreadAzureBlobShape>('MainThreadAzureBlob'), MainThreadAzureBlob: createProxyIdentifier<MainThreadAzureBlobShape>('MainThreadAzureBlob'),
MainThreadPerf: createProxyIdentifier<MainThreadPerfShape>('MainThreadPerf') MainThreadPerf: createProxyIdentifier<MainThreadPerfShape>('MainThreadPerf'),
MainThreadWindow: createProxyIdentifier<SqlMainThreadWindowShape>('MainThreadWindow')
}; };
export const SqlExtHostContext = { export const SqlExtHostContext = {
@@ -2709,6 +2712,7 @@ export const SqlExtHostContext = {
ExtHostNotebookDocumentsAndEditors: createProxyIdentifier<SqlExtHostNotebookDocumentsAndEditorsShape>('ExtHostNotebookDocumentsAndEditors'), ExtHostNotebookDocumentsAndEditors: createProxyIdentifier<SqlExtHostNotebookDocumentsAndEditorsShape>('ExtHostNotebookDocumentsAndEditors'),
ExtHostExtensionManagement: createProxyIdentifier<ExtHostExtensionManagementShape>('ExtHostExtensionManagement'), ExtHostExtensionManagement: createProxyIdentifier<ExtHostExtensionManagementShape>('ExtHostExtensionManagement'),
ExtHostWorkspace: createProxyIdentifier<SqlExtHostWorkspaceShape>('ExtHostWorkspace'), ExtHostWorkspace: createProxyIdentifier<SqlExtHostWorkspaceShape>('ExtHostWorkspace'),
ExtHostWindow: createProxyIdentifier<SqlExtHostWindowShape>('ExtHostWindow'),
ExtHostAzureBlob: createProxyIdentifier<ExtHostAzureBlobShape>('ExtHostAzureBlob'), ExtHostAzureBlob: createProxyIdentifier<ExtHostAzureBlobShape>('ExtHostAzureBlob'),
ExtHostPerf: createProxyIdentifier<ExtHostPerfShape>('ExtHostPerf') ExtHostPerf: createProxyIdentifier<ExtHostPerfShape>('ExtHostPerf')
}; };