diff --git a/samples/sqlservices/package.json b/samples/sqlservices/package.json index 4e8011c41f..2cb677a53c 100644 --- a/samples/sqlservices/package.json +++ b/samples/sqlservices/package.json @@ -21,6 +21,10 @@ "command": "sqlservices.openDialog", "title": "sqlservices.openDialog" }, + { + "command": "sqlservices.openConnectionDialog", + "title": "sqlservices.openConnectionDialog" + }, { "command": "sqlservices.openEditor", "title": "sqlservices.openEditor" diff --git a/samples/sqlservices/src/controllers/mainController.ts b/samples/sqlservices/src/controllers/mainController.ts index def3ae1039..220f40d7c0 100644 --- a/samples/sqlservices/src/controllers/mainController.ts +++ b/samples/sqlservices/src/controllers/mainController.ts @@ -49,6 +49,13 @@ export default class MainController implements vscode.Disposable { this.openDialog(); }); + vscode.commands.registerCommand('sqlservices.openConnectionDialog', async () => { + let connection = await sqlops.connection.openConnectionDialog(); + if (connection) { + console.info('Connection Opened: ' + connection.options['server']); + } + }); + vscode.commands.registerCommand('sqlservices.openEditor', () => { this.openEditor(); }); @@ -378,18 +385,20 @@ export default class MainController implements vscode.Disposable { page1.registerContent(async (view) => { await this.getTabContent(view, customButton1, customButton2, 800); }); - /* + wizard.registerOperation({ displayName: 'test task', description: 'task description', - isCancelable: true - }, op => { - op.updateStatus(sqlops.TaskStatus.InProgress); - op.updateStatus(sqlops.TaskStatus.InProgress, 'Task is running'); - setTimeout(() => { - op.updateStatus(sqlops.TaskStatus.Succeeded); - }, 5000); - });*/ + isCancelable: true, + connection: undefined, + operation: op => { + op.updateStatus(sqlops.TaskStatus.InProgress); + op.updateStatus(sqlops.TaskStatus.InProgress, 'Task is running'); + setTimeout(() => { + op.updateStatus(sqlops.TaskStatus.Succeeded); + }, 5000); + } + }); wizard.pages = [page1, page2]; wizard.open(); } diff --git a/src/sql/parts/connection/common/connectionManagement.ts b/src/sql/parts/connection/common/connectionManagement.ts index 575e1283bb..a2aa2c6818 100644 --- a/src/sql/parts/connection/common/connectionManagement.ts +++ b/src/sql/parts/connection/common/connectionManagement.ts @@ -59,6 +59,7 @@ export interface IConnectionResult { errorCode: number; callStack: string; errorHandled?: boolean; + connectionProfile?: IConnectionProfile; } export interface IConnectionCallbacks { @@ -130,15 +131,15 @@ export interface IConnectionManagementService { onConnectionChangedNotification(handle: number, changedConnInfo: sqlops.ChangedConnectionInfo); - getConnectionGroups(): ConnectionProfileGroup[]; + getConnectionGroups(providers?: string[]): ConnectionProfileGroup[]; - getRecentConnections(): ConnectionProfile[]; + getRecentConnections(providers?: string[]): ConnectionProfile[]; clearRecentConnectionsList(): void; clearRecentConnection(connectionProfile: IConnectionProfile): void; - getActiveConnections(): ConnectionProfile[]; + getActiveConnections(providers?: string[]): ConnectionProfile[]; saveProfileGroup(profile: IConnectionProfileGroup): Promise; @@ -270,7 +271,24 @@ export interface IConnectionManagementService { export const IConnectionDialogService = createDecorator('connectionDialogService'); export interface IConnectionDialogService { _serviceBrand: any; + /** + * Opens the connection dialog and returns the promise for successfully opening the dialog + * @param connectionManagementService + * @param params + * @param model + * @param connectionResult + */ showDialog(connectionManagementService: IConnectionManagementService, params: INewConnectionParams, model: IConnectionProfile, connectionResult?: IConnectionResult): Thenable; + + /** + * Opens the connection dialog and returns the promise when connection is made + * or dialog is closed + * @param connectionManagementService + * @param params + * @param model + * @param connectionResult + */ + openDialogAndWait(connectionManagementService: IConnectionManagementService, params?: INewConnectionParams, model?: IConnectionProfile, connectionResult?: IConnectionResult): Thenable; } export interface IServerGroupDialogCallbacks { @@ -304,6 +322,7 @@ export interface INewConnectionParams { runQueryOnCompletion?: RunQueryOnConnectionMode; querySelection?: sqlops.ISelectionData; showDashboard?: boolean; + providers?: string[]; } export interface IConnectableInput { diff --git a/src/sql/parts/connection/common/connectionManagementService.ts b/src/sql/parts/connection/common/connectionManagementService.ts index 3e24bf608c..db2561e6fc 100644 --- a/src/sql/parts/connection/common/connectionManagementService.ts +++ b/src/sql/parts/connection/common/connectionManagementService.ts @@ -627,12 +627,12 @@ export class ConnectionManagementService extends Disposable implements IConnecti } } - public getConnectionGroups(): ConnectionProfileGroup[] { - return this._connectionStore.getConnectionProfileGroups(); + public getConnectionGroups(providers?: string[]): ConnectionProfileGroup[] { + return this._connectionStore.getConnectionProfileGroups(false, providers); } - public getRecentConnections(): ConnectionProfile[] { - return this._connectionStore.getRecentlyUsedConnections(); + public getRecentConnections(providers?: string[]): ConnectionProfile[] { + return this._connectionStore.getRecentlyUsedConnections(providers); } @@ -644,7 +644,7 @@ export class ConnectionManagementService extends Disposable implements IConnecti this._connectionStore.removeConnectionToMemento(connectionProfile, Constants.recentConnections); } - public getActiveConnections(): ConnectionProfile[] { + public getActiveConnections(providers?: string[]): ConnectionProfile[] { return this._connectionStatusManager.getActiveConnectionProfiles(); } @@ -1002,14 +1002,14 @@ export class ConnectionManagementService extends Disposable implements IConnecti let connectionMngInfo = this._connectionStatusManager.findConnection(uri); if (connectionMngInfo && connectionMngInfo.deleted) { this._connectionStatusManager.deleteConnection(uri); - resolve({ connected: connectResult, errorMessage: undefined, errorCode: undefined, callStack: undefined, errorHandled: true }); + resolve({ connected: connectResult, errorMessage: undefined, errorCode: undefined, callStack: undefined, errorHandled: true, connectionProfile: connection }); } else { if (errorMessage) { // Connection to the server failed this._connectionStatusManager.deleteConnection(uri); - resolve({ connected: connectResult, errorMessage: errorMessage, errorCode: errorCode, callStack: callStack }); + resolve({ connected: connectResult, errorMessage: errorMessage, errorCode: errorCode, callStack: callStack, connectionProfile: connection }); } else { - resolve({ connected: connectResult, errorMessage: errorMessage, errorCode: errorCode, callStack: callStack }); + resolve({ connected: connectResult, errorMessage: errorMessage, errorCode: errorCode, callStack: callStack, connectionProfile: connection }); } } }); diff --git a/src/sql/parts/connection/common/connectionStatusManager.ts b/src/sql/parts/connection/common/connectionStatusManager.ts index b0bc190f81..6ef4daa2e9 100644 --- a/src/sql/parts/connection/common/connectionStatusManager.ts +++ b/src/sql/parts/connection/common/connectionStatusManager.ts @@ -193,9 +193,14 @@ export class ConnectionStatusManager { /** * Get a list of the active connection profiles managed by the status manager */ - public getActiveConnectionProfiles(): ConnectionProfile[] { + public getActiveConnectionProfiles(providers?: string[]): ConnectionProfile[] { let profiles = Object.values(this._connections).map((connectionInfo: ConnectionManagementInfo) => connectionInfo.connectionProfile); // Remove duplicate profiles that may be listed multiple times under different URIs by filtering for profiles that don't have the same ID as an earlier profile in the list - return profiles.filter((profile, index) => profiles.findIndex(otherProfile => otherProfile.id === profile.id) === index); + profiles = profiles.filter((profile, index) => profiles.findIndex(otherProfile => otherProfile.id === profile.id) === index); + + if (providers) { + profiles = profiles.filter(f => providers.includes(f.providerName)); + } + return profiles; } } \ No newline at end of file diff --git a/src/sql/parts/connection/common/connectionStore.ts b/src/sql/parts/connection/common/connectionStore.ts index c4333fc1b6..ba81d1ca1e 100644 --- a/src/sql/parts/connection/common/connectionStore.ts +++ b/src/sql/parts/connection/common/connectionStore.ts @@ -213,13 +213,16 @@ export class ConnectionStore { * * @returns {sqlops.ConnectionInfo} the array of connections, empty if none are found */ - public getRecentlyUsedConnections(): ConnectionProfile[] { + public getRecentlyUsedConnections(providers?: string[]): ConnectionProfile[] { let configValues: IConnectionProfile[] = this._memento[Constants.recentConnections]; if (!configValues) { configValues = []; } configValues = configValues.filter(c => !!(c)); + if (providers && providers.length > 0) { + configValues = configValues.filter(c => providers.includes(c.providerName)); + } return this.convertConfigValuesToConnectionProfiles(configValues); } @@ -429,10 +432,13 @@ export class ConnectionStore { }); } - public getConnectionProfileGroups(withoutConnections?: boolean): ConnectionProfileGroup[] { + public getConnectionProfileGroups(withoutConnections?: boolean, providers?: string[]): ConnectionProfileGroup[] { let profilesInConfiguration: ConnectionProfile[]; if (!withoutConnections) { profilesInConfiguration = this._connectionConfig.getConnections(true); + if (providers && providers.length > 0) { + profilesInConfiguration = profilesInConfiguration.filter(x => providers.includes(x.providerName)); + } } let groups = this._connectionConfig.getAllGroups(); diff --git a/src/sql/parts/connection/connectionDialog/connectionController.ts b/src/sql/parts/connection/connectionDialog/connectionController.ts index a65e0eb2a2..da4be5828d 100644 --- a/src/sql/parts/connection/connectionDialog/connectionController.ts +++ b/src/sql/parts/connection/connectionDialog/connectionController.ts @@ -132,8 +132,8 @@ export class ConnectionController implements IConnectionComponentController { } } - private getAllServerGroups(): IConnectionProfileGroup[] { - var connectionGroupRoot = this._connectionManagementService.getConnectionGroups(); + private getAllServerGroups(providers?: string[]): IConnectionProfileGroup[] { + var connectionGroupRoot = this._connectionManagementService.getConnectionGroups(providers); var connectionGroupNames: IConnectionProfileGroup[] = []; if (connectionGroupRoot && connectionGroupRoot.length > 0) { this.getServerGroupHelper(connectionGroupRoot[0], connectionGroupNames); @@ -149,8 +149,8 @@ export class ConnectionController implements IConnectionComponentController { return connectionGroupNames; } - public initDialog(connectionInfo: IConnectionProfile): void { - this._connectionWidget.updateServerGroup(this.getAllServerGroups()); + public initDialog(providers: string[], connectionInfo: IConnectionProfile): void { + this._connectionWidget.updateServerGroup(this.getAllServerGroups(providers)); this._model = connectionInfo; this._model.providerName = this._providerName; let appNameOption = this._providerOptions.find(option => option.specialValueType === ConnectionOptionSpecialType.appName); diff --git a/src/sql/parts/connection/connectionDialog/connectionDialogService.ts b/src/sql/parts/connection/connectionDialog/connectionDialogService.ts index fa8ef384fe..a0b7b72e66 100644 --- a/src/sql/parts/connection/connectionDialog/connectionDialogService.ts +++ b/src/sql/parts/connection/connectionDialog/connectionDialogService.ts @@ -33,6 +33,7 @@ import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService import { ICommandService } from 'vs/platform/commands/common/commands'; import * as types from 'vs/base/common/types'; import { trim } from 'vs/base/common/strings'; +import { Deferred } from 'sql/base/common/promise'; export interface IConnectionValidateResult { isValid: boolean; @@ -49,7 +50,7 @@ export interface IConnectionComponentCallbacks { export interface IConnectionComponentController { showUiComponent(container: HTMLElement): void; - initDialog(model: IConnectionProfile): void; + initDialog(providers: string[], model: IConnectionProfile): void; validateConnection(): IConnectionValidateResult; fillInConnectionInputs(connectionInfo: IConnectionProfile): void; handleOnConnecting(): void; @@ -75,6 +76,7 @@ export class ConnectionDialogService implements IConnectionDialogService { private _currentProviderType: string = 'Microsoft SQL Server'; private _connecting: boolean = false; private _connectionErrorTitle = localize('connectionError', 'Connection error'); + private _dialogDeferredPromise: Deferred; constructor( @IPartService private _partService: IPartService, @@ -82,17 +84,24 @@ export class ConnectionDialogService implements IConnectionDialogService { @ICapabilitiesService private _capabilitiesService: ICapabilitiesService, @IErrorMessageService private _errorMessageService: IErrorMessageService, @IWorkspaceConfigurationService private _workspaceConfigurationService: IWorkspaceConfigurationService, - @IWindowsService private _windowsService: IWindowsService, @IClipboardService private _clipboardService: IClipboardService, @ICommandService private _commandService: ICommandService ) { } private getDefaultProviderName() { - if (this._workspaceConfigurationService) { - let defaultProvider = WorkbenchUtils.getSqlConfigValue(this._workspaceConfigurationService, Constants.defaultEngine); + let defaultProvider: string; + if (this._providerNameToDisplayNameMap) { + let keys = Object.keys(this._providerNameToDisplayNameMap); + if (keys && keys.length > 0) { + defaultProvider = keys[0]; + } + } + + if (!defaultProvider && this._workspaceConfigurationService) { + defaultProvider = WorkbenchUtils.getSqlConfigValue(this._workspaceConfigurationService, Constants.defaultEngine); } // as a fallback, default to MSSQL if the value from settings is not available - return Constants.mssqlProviderName; + return defaultProvider || Constants.mssqlProviderName; } private handleOnConnect(params: INewConnectionParams, profile?: IConnectionProfile): void { @@ -148,26 +157,28 @@ export class ConnectionDialogService implements IConnectionDialogService { this._connecting = false; } this.uiController.databaseDropdownExpanded = false; + this._dialogDeferredPromise.resolve(undefined); } private handleDefaultOnConnect(params: INewConnectionParams, connection: IConnectionProfile): Thenable { let fromEditor = params && params.connectionType === ConnectionType.editor; let uri: string = undefined; - if (fromEditor && params.input) { + if (fromEditor && params && params.input) { uri = params.input.uri; } let options: IConnectionCompletionOptions = { params: params, saveTheConnection: !fromEditor, - showDashboard: params.showDashboard !== undefined ? params.showDashboard : !fromEditor, + showDashboard: params && params.showDashboard !== undefined ? params.showDashboard : !fromEditor, showConnectionDialogOnError: false, showFirewallRuleOnError: true }; - return this._connectionManagementService.connectAndSaveProfile(connection, uri, options, params.input).then(connectionResult => { + return this._connectionManagementService.connectAndSaveProfile(connection, uri, options, params && params.input).then(connectionResult => { this._connecting = false; if (connectionResult && connectionResult.connected) { this._connectionDialog.close(); + this._dialogDeferredPromise.resolve(connectionResult.connectionProfile); } else if (connectionResult && connectionResult.errorHandled) { this._connectionDialog.resetConnection(); } else { @@ -213,7 +224,7 @@ export class ConnectionDialogService implements IConnectionDialogService { } private handleInitDialog() { - this.uiController.initDialog(this._model); + this.uiController.initDialog(this._params && this._params.providers, this._model); } private handleFillInConnectionInputs(connectionInfo: IConnectionProfile): void { @@ -265,9 +276,25 @@ export class ConnectionDialogService implements IConnectionDialogService { }); } + public openDialogAndWait(connectionManagementService: IConnectionManagementService, + params?: INewConnectionParams, + model?: IConnectionProfile, + connectionResult?: IConnectionResult): Thenable { + this._dialogDeferredPromise = new Deferred(); + + this.showDialog(connectionManagementService, + params, + model, + connectionResult).then(() => { + }, error => { + this._dialogDeferredPromise.reject(error); + }); + return this._dialogDeferredPromise; + } + public showDialog( connectionManagementService: IConnectionManagementService, - params: INewConnectionParams, + params?: INewConnectionParams, model?: IConnectionProfile, connectionResult?: IConnectionResult): Thenable { @@ -297,11 +324,12 @@ export class ConnectionDialogService implements IConnectionDialogService { }); } + private doShowDialog(params: INewConnectionParams): TPromise { if (!this._connectionDialog) { let container = document.getElementById(this._partService.getWorkbenchElementId()).parentElement; this._container = container; - this._connectionDialog = this._instantiationService.createInstance(ConnectionDialogWidget, this._providerTypes, this._providerNameToDisplayNameMap[this._model.providerName]); + this._connectionDialog = this._instantiationService.createInstance(ConnectionDialogWidget, this._providerTypes, this._providerNameToDisplayNameMap[this._model.providerName], this._providerNameToDisplayNameMap); this._connectionDialog.onCancel(() => { this._connectionDialog.databaseDropdownExpanded = this.uiController.databaseDropdownExpanded; this.handleOnCancel(this._connectionDialog.newConnectionParams); @@ -316,7 +344,7 @@ export class ConnectionDialogService implements IConnectionDialogService { this._connectionDialog.newConnectionParams = params; return new TPromise(() => { - this._connectionDialog.open(this._connectionManagementService.getRecentConnections().length > 0); + this._connectionDialog.open(this._connectionManagementService.getRecentConnections(params.providers).length > 0); this.uiController.focusOnOpen(); }); } diff --git a/src/sql/parts/connection/connectionDialog/connectionDialogWidget.ts b/src/sql/parts/connection/connectionDialog/connectionDialogWidget.ts index 9b784290ab..4f6aa8007d 100644 --- a/src/sql/parts/connection/connectionDialog/connectionDialogWidget.ts +++ b/src/sql/parts/connection/connectionDialog/connectionDialogWidget.ts @@ -59,6 +59,7 @@ export class ConnectionDialogWidget extends Modal { private $connectionUIContainer: Builder; private _databaseDropdownExpanded: boolean; private _actionbar: ActionBar; + private _providers: string[]; private _panel: TabbedPanel; private _recentConnectionTabId: PanelTabIdentifier; @@ -84,6 +85,7 @@ export class ConnectionDialogWidget extends Modal { constructor( private providerTypeOptions: string[], private selectedProviderType: string, + private providerNameToDisplayNameMap: { [providerDisplayName: string]: string }, @IInstantiationService private _instantiationService: IInstantiationService, @IConnectionManagementService private _connectionManagementService: IConnectionManagementService, @IWorkbenchThemeService private _themeService: IWorkbenchThemeService, @@ -96,6 +98,22 @@ export class ConnectionDialogWidget extends Modal { super(localize('connection', 'Connection'), TelemetryKeys.Connection, _partService, telemetryService, contextKeyService, { hasSpinner: true, hasErrors: true }); } + public refresh(): void { + let filteredProviderTypes = this.providerTypeOptions; + + if (this._newConnectionParams && this._newConnectionParams.providers) { + let validProviderNames = Object.keys(this.providerNameToDisplayNameMap).filter(x => this.includeProvider(x, this._newConnectionParams)); + if (validProviderNames && validProviderNames.length > 0) { + filteredProviderTypes = filteredProviderTypes.filter(x => validProviderNames.find( v => this.providerNameToDisplayNameMap[v] === x) !== undefined); + } + } + this._providerTypeSelectBox.setOptions(filteredProviderTypes); + } + + private includeProvider(providerName: string, params?: INewConnectionParams): Boolean { + return params === undefined || params.providers === undefined || params.providers.find(x => x === providerName) !== undefined; + } + protected renderBody(container: HTMLElement): void { let connectionContainer = $('.connection-dialog'); container.appendChild(connectionContainer.getHTMLElement()); @@ -147,7 +165,7 @@ export class ConnectionDialogWidget extends Modal { this._panel.onTabChange(c => { if (c === savedConnectionTabId && this._savedConnectionTree.getContentHeight() === 0) { // Update saved connection tree - TreeUpdateUtils.structuralTreeUpdate(this._savedConnectionTree, 'saved', this._connectionManagementService); + TreeUpdateUtils.structuralTreeUpdate(this._savedConnectionTree, 'saved', this._connectionManagementService, this._providers); if (this._savedConnectionTree.getContentHeight() > 0) { this._noSavedConnectionBuilder.hide(); @@ -366,7 +384,7 @@ export class ConnectionDialogWidget extends Modal { this._recentConnectionBuilder.hide(); this._noRecentConnectionBuilder.show(); } - TreeUpdateUtils.structuralTreeUpdate(this._recentConnectionTree, 'recent', this._connectionManagementService); + TreeUpdateUtils.structuralTreeUpdate(this._recentConnectionTree, 'recent', this._connectionManagementService, this._providers); // reset saved connection tree this._savedConnectionTree.setInput([]); @@ -415,6 +433,8 @@ export class ConnectionDialogWidget extends Modal { public set newConnectionParams(params: INewConnectionParams) { this._newConnectionParams = params; + this._providers = params && params.providers; + this.refresh(); } public updateProvider(displayName: string) { diff --git a/src/sql/parts/objectExplorer/viewlet/treeUpdateUtils.ts b/src/sql/parts/objectExplorer/viewlet/treeUpdateUtils.ts index 353bd77441..7a7f54b18b 100644 --- a/src/sql/parts/objectExplorer/viewlet/treeUpdateUtils.ts +++ b/src/sql/parts/objectExplorer/viewlet/treeUpdateUtils.ts @@ -22,7 +22,7 @@ export class TreeUpdateUtils { /** * Set input for the tree. */ - public static structuralTreeUpdate(tree: ITree, viewKey: string, connectionManagementService: IConnectionManagementService): void { + public static structuralTreeUpdate(tree: ITree, viewKey: string, connectionManagementService: IConnectionManagementService, providers?: string[]): void { let selectedElement: any; let targetsToExpand: any[]; if (tree) { @@ -35,13 +35,13 @@ export class TreeUpdateUtils { let groups; let treeInput = new ConnectionProfileGroup('root', null, undefined, undefined, undefined); if (viewKey === 'recent') { - groups = connectionManagementService.getRecentConnections(); + groups = connectionManagementService.getRecentConnections(providers); treeInput.addConnections(groups); } else if (viewKey === 'active') { - groups = connectionManagementService.getActiveConnections(); + groups = connectionManagementService.getActiveConnections(providers); treeInput.addConnections(groups); } else if (viewKey === 'saved') { - treeInput = TreeUpdateUtils.getTreeInput(connectionManagementService); + treeInput = TreeUpdateUtils.getTreeInput(connectionManagementService, providers); } tree.setInput(treeInput).done(() => { @@ -96,9 +96,9 @@ export class TreeUpdateUtils { } } - public static getTreeInput(connectionManagementService: IConnectionManagementService): ConnectionProfileGroup { + public static getTreeInput(connectionManagementService: IConnectionManagementService, providers?: string[]): ConnectionProfileGroup { - let groups = connectionManagementService.getConnectionGroups(); + let groups = connectionManagementService.getConnectionGroups(providers); if (groups && groups.length > 0) { let treeInput = groups[0]; treeInput.name = 'root'; diff --git a/src/sql/sqlops.proposed.d.ts b/src/sql/sqlops.proposed.d.ts index b17bef1e9c..420508141f 100644 --- a/src/sql/sqlops.proposed.d.ts +++ b/src/sql/sqlops.proposed.d.ts @@ -1106,7 +1106,7 @@ declare module 'sqlops' { /** * Connection information */ - connection: connection.Connection; + connection?: connection.Connection; /** * Operation Display Name @@ -1153,5 +1153,12 @@ declare module 'sqlops' { * @param connectionId The ID of the connection */ export function getUriForConnection(connectionId: string): Thenable; + + /** + * Opens the connection dialog, calls the callback with the result. If connection was successful + * returns the connection otherwise returns undefined + * @param callback + */ + export function openConnectionDialog(provider?: string[]): Thenable; } } diff --git a/src/sql/workbench/api/node/extHostConnectionManagement.ts b/src/sql/workbench/api/node/extHostConnectionManagement.ts index 2c8b674214..0c3f17bd4a 100644 --- a/src/sql/workbench/api/node/extHostConnectionManagement.ts +++ b/src/sql/workbench/api/node/extHostConnectionManagement.ts @@ -6,6 +6,7 @@ import { ExtHostConnectionManagementShape, SqlMainContext, MainThreadConnectionManagementShape } from 'sql/workbench/api/node/sqlExtHost.protocol'; import { IMainContext } from 'vs/workbench/api/node/extHost.protocol'; +import { generateUuid } from 'vs/base/common/uuid'; import * as sqlops from 'sqlops'; export class ExtHostConnectionManagement extends ExtHostConnectionManagementShape { @@ -31,6 +32,10 @@ export class ExtHostConnectionManagement extends ExtHostConnectionManagementShap return this._proxy.$getCredentials(connectionId); } + public $openConnectionDialog(providers?: string[]): Thenable { + return this._proxy.$openConnectionDialog(providers); + } + public $listDatabases(connectionId: string): Thenable { return this._proxy.$listDatabases(connectionId); } diff --git a/src/sql/workbench/api/node/mainThreadConnectionManagement.ts b/src/sql/workbench/api/node/mainThreadConnectionManagement.ts index 65e1ca323d..1ea1f98166 100644 --- a/src/sql/workbench/api/node/mainThreadConnectionManagement.ts +++ b/src/sql/workbench/api/node/mainThreadConnectionManagement.ts @@ -8,7 +8,7 @@ import { SqlExtHostContext, SqlMainContext, ExtHostConnectionManagementShape, Ma import * as sqlops from 'sqlops'; import { IExtHostContext } from 'vs/workbench/api/node/extHost.protocol'; import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; -import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement'; +import { IConnectionManagementService, IConnectionDialogService } from 'sql/parts/connection/common/connectionManagement'; import { IObjectExplorerService } from 'sql/parts/objectExplorer/common/objectExplorerService'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import * as TaskUtilities from 'sql/workbench/common/taskUtilities'; @@ -25,7 +25,8 @@ export class MainThreadConnectionManagement implements MainThreadConnectionManag extHostContext: IExtHostContext, @IConnectionManagementService private _connectionManagementService: IConnectionManagementService, @IObjectExplorerService private _objectExplorerService: IObjectExplorerService, - @IWorkbenchEditorService private _workbenchEditorService: IWorkbenchEditorService + @IWorkbenchEditorService private _workbenchEditorService: IWorkbenchEditorService, + @IConnectionDialogService private _connectionDialogService: IConnectionDialogService, ) { if (extHostContext) { this._proxy = extHostContext.getProxy(SqlExtHostContext.ExtHostConnectionManagement); @@ -49,6 +50,16 @@ export class MainThreadConnectionManagement implements MainThreadConnectionManag return Promise.resolve(this._connectionManagementService.getActiveConnectionCredentials(connectionId)); } + + public async $openConnectionDialog(providers: string[]): Promise { + let connectionProfile = await this._connectionDialogService.openDialogAndWait(this._connectionManagementService, { connectionType: 1, providers: providers }); + return connectionProfile ? { + connectionId: connectionProfile.id, + options: connectionProfile.options, + providerName: connectionProfile.providerName + } : undefined; + } + public async $listDatabases(connectionId: string): Promise { let connection = this._connectionManagementService.getActiveConnections().find(profile => profile.id === connectionId); let connectionUri = this._connectionManagementService.getConnectionUri(connection); diff --git a/src/sql/workbench/api/node/sqlExtHost.api.impl.ts b/src/sql/workbench/api/node/sqlExtHost.api.impl.ts index 47813acac5..aeb0625889 100644 --- a/src/sql/workbench/api/node/sqlExtHost.api.impl.ts +++ b/src/sql/workbench/api/node/sqlExtHost.api.impl.ts @@ -104,6 +104,9 @@ export function createApiFactory( getCredentials(connectionId: string): Thenable<{ [name: string]: string }> { return extHostConnectionManagement.$getCredentials(connectionId); }, + openConnectionDialog(providers?: string[]): Thenable { + return extHostConnectionManagement.$openConnectionDialog(providers); + }, listDatabases(connectionId: string): Thenable { return extHostConnectionManagement.$listDatabases(connectionId); }, diff --git a/src/sql/workbench/api/node/sqlExtHost.protocol.ts b/src/sql/workbench/api/node/sqlExtHost.protocol.ts index 2e1e9855b8..a035009c69 100644 --- a/src/sql/workbench/api/node/sqlExtHost.protocol.ts +++ b/src/sql/workbench/api/node/sqlExtHost.protocol.ts @@ -33,7 +33,9 @@ export abstract class ExtHostAccountManagementShape { $refresh(handle: number, account: sqlops.Account): Thenable { throw ni(); } } -export abstract class ExtHostConnectionManagementShape { } +export abstract class ExtHostConnectionManagementShape { + $onConnectionOpened(handleId: string, connection: sqlops.connection.Connection): void { throw ni; } + } export abstract class ExtHostDataProtocolShape { @@ -491,6 +493,7 @@ export interface MainThreadConnectionManagementShape extends IDisposable { $getActiveConnections(): Thenable; $getCurrentConnection(): Thenable; $getCredentials(connectionId: string): Thenable<{ [name: string]: string }>; + $openConnectionDialog(providers: string[]): Thenable; $listDatabases(connectionId: string): Thenable; $getConnectionString(connectionId: string, includePassword: boolean): Thenable; $getUriForConnection(connectionId: string): Thenable; diff --git a/src/sqltest/parts/connection/connectionDialogService.test.ts b/src/sqltest/parts/connection/connectionDialogService.test.ts index 732e7bf317..d04e8504f4 100644 --- a/src/sqltest/parts/connection/connectionDialogService.test.ts +++ b/src/sqltest/parts/connection/connectionDialogService.test.ts @@ -23,7 +23,7 @@ suite('ConnectionDialogService tests', () => { setup(() => { let errorMessageService = getMockErrorMessageService(); connectionDialogService = new ConnectionDialogService(undefined, undefined, undefined, errorMessageService.object, - undefined, undefined, undefined, undefined); + undefined, undefined, undefined); mockConnectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Strict, {}, {}); (connectionDialogService as any)._connectionManagementService = mockConnectionManagementService.object; mockConnectionDialog = TypeMoq.Mock.ofType(ConnectionDialogWidget, TypeMoq.MockBehavior.Strict, @@ -34,6 +34,7 @@ suite('ConnectionDialogService tests', () => { undefined, undefined, undefined, + undefined, new ContextKeyServiceStub() ); mockConnectionDialog.setup(c => c.resetConnection()); diff --git a/src/sqltest/parts/connection/connectionManagementService.test.ts b/src/sqltest/parts/connection/connectionManagementService.test.ts index 44070ebb8e..d09f6d44db 100644 --- a/src/sqltest/parts/connection/connectionManagementService.test.ts +++ b/src/sqltest/parts/connection/connectionManagementService.test.ts @@ -108,7 +108,7 @@ suite('SQL ConnectionManagementService tests', () => { c => c.serverName === connectionProfileWithEmptyUnsavedPassword.serverName))).returns( () => Promise.resolve({ profile: connectionProfileWithEmptyUnsavedPassword, savedCred: false })); connectionStore.setup(x => x.isPasswordRequired(TypeMoq.It.isAny())).returns(() => true); - connectionStore.setup(x => x.getConnectionProfileGroups()).returns(() => [root]); + connectionStore.setup(x => x.getConnectionProfileGroups(false, undefined)).returns(() => [root]); mssqlConnectionProvider.setup(x => x.connect(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => undefined); @@ -172,7 +172,7 @@ suite('SQL ConnectionManagementService tests', () => { connectionDialogService.verify(x => x.showDialog( TypeMoq.It.isAny(), TypeMoq.It.is(p => p.connectionType === connectionType && (uri === undefined || p.input.uri === uri)), - TypeMoq.It.is(c => c.serverName === connectionProfile.serverName), + TypeMoq.It.is(c => c !== undefined && c.serverName === connectionProfile.serverName), connectionResult ? TypeMoq.It.is(r => r.errorMessage === connectionResult.errorMessage && r.callStack === connectionResult.callStack) : undefined), didShow ? TypeMoq.Times.once() : TypeMoq.Times.never()); diff --git a/src/sqltest/parts/connection/connectionStore.test.ts b/src/sqltest/parts/connection/connectionStore.test.ts index e320f9559d..93d8cfdf0a 100644 --- a/src/sqltest/parts/connection/connectionStore.test.ts +++ b/src/sqltest/parts/connection/connectionStore.test.ts @@ -26,6 +26,7 @@ import { ConnectionProviderProperties } from 'sql/workbench/parts/connection/com suite('SQL ConnectionStore tests', () => { let defaultNamedProfile: IConnectionProfile; let defaultUnnamedProfile: IConnectionProfile; + let profileForProvider2: IConnectionProfile; let context: TypeMoq.Mock; let credentialStore: TypeMoq.Mock; let connectionConfig: TypeMoq.Mock; @@ -35,6 +36,7 @@ suite('SQL ConnectionStore tests', () => { let mementoArray: any = []; let maxRecent = 5; let msSQLCapabilities: ConnectionProviderProperties; + let provider2Capabilities: ConnectionProviderProperties; let defaultNamedConnectionProfile: ConnectionProfile; setup(() => { @@ -72,6 +74,23 @@ suite('SQL ConnectionStore tests', () => { id: undefined }); + profileForProvider2 = Object.assign({}, { + serverName: 'unnamedServer', + databaseName: undefined, + authenticationType: 'SqlLogin', + userName: 'aUser', + password: 'asdf!@#$', + savePassword: true, + groupId: '', + groupFullName: '', + getOptionsKey: undefined, + matches: undefined, + providerName: 'MSSQL', + options: {}, + saveProfile: true, + id: undefined + }); + let momento = new Memento('ConnectionManagement'); context = TypeMoq.Mock.ofInstance(momento); context.setup(x => x.getMemento(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => mementoArray); @@ -164,7 +183,14 @@ suite('SQL ConnectionStore tests', () => { displayName: 'MSSQL', connectionOptions: connectionProvider }; + + provider2Capabilities = { + providerId: 'MSSQL', + displayName: 'MSSQL', + connectionOptions: connectionProvider + }; capabilitiesService.capabilities['MSSQL'] = { connection: msSQLCapabilities }; + capabilitiesService.capabilities['Provider2'] = { connection: provider2Capabilities }; let groups: IConnectionProfileGroup[] = [ { id: 'root', @@ -226,6 +252,14 @@ suite('SQL ConnectionStore tests', () => { }); }); + test('getRecentlyUsedConnections should return connection for given provider', () => { + let connectionStore = new ConnectionStore(storageServiceMock.object, context.object, undefined, workspaceConfigurationServiceMock.object, + credentialStore.object, capabilitiesService, connectionConfig.object); + let connections = connectionStore.getRecentlyUsedConnections(['Provider2']); + assert.notEqual(connections, undefined); + assert.equal(connections.every(c => c.providerName === 'Provider2'), true); + }); + test('addActiveConnection should add same connection exactly once', (done) => { // setup memento for MRU to return a list we have access to credentialStore.setup(x => x.saveCredential(TypeMoq.It.isAny(), TypeMoq.It.isAny())) diff --git a/src/sqltest/stubs/connectionDialogTestService.ts b/src/sqltest/stubs/connectionDialogTestService.ts index 8fc9b0a106..d865540788 100644 --- a/src/sqltest/stubs/connectionDialogTestService.ts +++ b/src/sqltest/stubs/connectionDialogTestService.ts @@ -19,4 +19,8 @@ export class ConnectionDialogTestService implements IConnectionDialogService { return TPromise.as(none); } + public openDialogAndWait(connectionManagementService: IConnectionManagementService, + params?: INewConnectionParams, model?: IConnectionProfile, connectionResult?: IConnectionResult): TPromise { + return TPromise.as(undefined); + } } \ No newline at end of file diff --git a/src/sqltest/stubs/connectionManagementService.test.ts b/src/sqltest/stubs/connectionManagementService.test.ts index 7dc72a9e1d..2b1206ed36 100644 --- a/src/sqltest/stubs/connectionManagementService.test.ts +++ b/src/sqltest/stubs/connectionManagementService.test.ts @@ -63,11 +63,11 @@ export class TestConnectionManagementService implements IConnectionManagementSer return undefined; } - getConnectionGroups(): ConnectionProfileGroup[] { + getConnectionGroups(providers?: string[]): ConnectionProfileGroup[] { return []; } - getActiveConnections(): ConnectionProfile[] { + getActiveConnections(providers?: string[]): ConnectionProfile[] { return []; } @@ -75,7 +75,7 @@ export class TestConnectionManagementService implements IConnectionManagementSer return undefined; } - getRecentConnections(): ConnectionProfile[] { + getRecentConnections(providers?: string[]): ConnectionProfile[] { return []; }