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;
}
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 {

View File

@@ -24,3 +24,4 @@ import './mainThreadResourceProvider';
import './mainThreadErrorDiagnostics';
import './mainThreadTasks';
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 { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService';
import { AuthenticationType } from 'sql/platform/connection/common/constants';
import { ExtHostWindow } from 'sql/workbench/api/common/extHostWindow';
export interface IAzdataExtensionApiFactory {
(extension: IExtensionDescription): typeof azdata;
@@ -102,6 +103,7 @@ export function createAdsApiFactory(accessor: ServicesAccessor): IAdsExtensionAp
const extHostNotebook = rpcProtocol.set(SqlExtHostContext.ExtHostNotebook, new ExtHostNotebook(rpcProtocol));
const extHostExtensionManagement = rpcProtocol.set(SqlExtHostContext.ExtHostExtensionManagement, new ExtHostExtensionManagement(rpcProtocol));
const extHostWorkspace = rpcProtocol.set(SqlExtHostContext.ExtHostWorkspace, new ExtHostWorkspace(rpcProtocol));
const extHostWindow = rpcProtocol.set(SqlExtHostContext.ExtHostWindow, new ExtHostWindow(rpcProtocol));
return {
azdata: function (extension: IExtensionDescription): typeof azdata {
// namespace: connection
@@ -480,6 +482,9 @@ export function createAdsApiFactory(accessor: ServicesAccessor): IAdsExtensionAp
MessageLevel: sqlExtHostTypes.MessageLevel,
openCustomErrorDialog(options: sqlExtHostTypes.IErrorDialogOptions): Thenable<string | undefined> {
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>;
}
export interface ExtHostWindowShape {
$openServerFileBrowserDialog(connectionUri: string, targetPath: string, fileFilters: azdata.window.FileFilters[]): Promise<string | undefined>;
}
export interface MainThreadWorkspaceShape {
$createAndEnterWorkspace(folder: vscode.Uri, workspaceFile: vscode.Uri): Promise<void>;
$enterWorkspace(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 {
$registerTask(taskInfo: azdata.TaskInfo): 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 ((this.getBackupPathCount() < BackupConstants.maxDevices)) {
this.backupPathTypePairs![filepath] = BackupConstants.MediaDeviceType.File;

View File

@@ -25,7 +25,7 @@ export class FileBrowserDialogController implements IFileBrowserDialogController
fileFilters: [{ label: string, filters: string[] }],
fileValidationServiceType: string,
isWide: boolean,
handleOnOk: (path: string) => void
handleOnClosed: (path: string | undefined) => void
): void {
if (!this._fileBrowserDialog) {
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.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);
}
}

View File

@@ -16,5 +16,5 @@ export interface IFileBrowserDialogController {
fileFilters: { label: string, filters: string[] }[],
fileValidationServiceType: string,
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);
}
private onFileBrowsed(filepath: string): void {
const oldFilePath = this._filePathInputBox!.value;
if (strings.isFalsyOrWhitespace(this._filePathInputBox!.value)) {
this._filePathInputBox!.value = filepath;
} else {
this._filePathInputBox!.value = this._filePathInputBox!.value + ', ' + filepath;
}
private onFileBrowsed(filepath?: string): void {
if (filepath) {
const oldFilePath = this._filePathInputBox!.value;
if (strings.isFalsyOrWhitespace(this._filePathInputBox!.value)) {
this._filePathInputBox!.value = filepath;
} else {
this._filePathInputBox!.value = this._filePathInputBox!.value + ', ' + filepath;
}
if (oldFilePath !== this._filePathInputBox!.value) {
this.onFilePathChanged(this._filePathInputBox!.value);
if (oldFilePath !== this._filePathInputBox!.value) {
this.onFilePathChanged(this._filePathInputBox!.value);
}
}
}

View File

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