Feature/ext connection dialog (#2201)

* Connection Dialog API for extensions
This commit is contained in:
Leila Lali
2018-08-10 09:29:46 -07:00
committed by GitHub
parent 2a3195636e
commit e5096e61e5
20 changed files with 217 additions and 58 deletions

View File

@@ -21,6 +21,10 @@
"command": "sqlservices.openDialog", "command": "sqlservices.openDialog",
"title": "sqlservices.openDialog" "title": "sqlservices.openDialog"
}, },
{
"command": "sqlservices.openConnectionDialog",
"title": "sqlservices.openConnectionDialog"
},
{ {
"command": "sqlservices.openEditor", "command": "sqlservices.openEditor",
"title": "sqlservices.openEditor" "title": "sqlservices.openEditor"

View File

@@ -49,6 +49,13 @@ export default class MainController implements vscode.Disposable {
this.openDialog(); 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', () => { vscode.commands.registerCommand('sqlservices.openEditor', () => {
this.openEditor(); this.openEditor();
}); });
@@ -378,18 +385,20 @@ export default class MainController implements vscode.Disposable {
page1.registerContent(async (view) => { page1.registerContent(async (view) => {
await this.getTabContent(view, customButton1, customButton2, 800); await this.getTabContent(view, customButton1, customButton2, 800);
}); });
/*
wizard.registerOperation({ wizard.registerOperation({
displayName: 'test task', displayName: 'test task',
description: 'task description', description: 'task description',
isCancelable: true isCancelable: true,
}, op => { connection: undefined,
operation: op => {
op.updateStatus(sqlops.TaskStatus.InProgress); op.updateStatus(sqlops.TaskStatus.InProgress);
op.updateStatus(sqlops.TaskStatus.InProgress, 'Task is running'); op.updateStatus(sqlops.TaskStatus.InProgress, 'Task is running');
setTimeout(() => { setTimeout(() => {
op.updateStatus(sqlops.TaskStatus.Succeeded); op.updateStatus(sqlops.TaskStatus.Succeeded);
}, 5000); }, 5000);
});*/ }
});
wizard.pages = [page1, page2]; wizard.pages = [page1, page2];
wizard.open(); wizard.open();
} }

View File

@@ -59,6 +59,7 @@ export interface IConnectionResult {
errorCode: number; errorCode: number;
callStack: string; callStack: string;
errorHandled?: boolean; errorHandled?: boolean;
connectionProfile?: IConnectionProfile;
} }
export interface IConnectionCallbacks { export interface IConnectionCallbacks {
@@ -130,15 +131,15 @@ export interface IConnectionManagementService {
onConnectionChangedNotification(handle: number, changedConnInfo: sqlops.ChangedConnectionInfo); onConnectionChangedNotification(handle: number, changedConnInfo: sqlops.ChangedConnectionInfo);
getConnectionGroups(): ConnectionProfileGroup[]; getConnectionGroups(providers?: string[]): ConnectionProfileGroup[];
getRecentConnections(): ConnectionProfile[]; getRecentConnections(providers?: string[]): ConnectionProfile[];
clearRecentConnectionsList(): void; clearRecentConnectionsList(): void;
clearRecentConnection(connectionProfile: IConnectionProfile): void; clearRecentConnection(connectionProfile: IConnectionProfile): void;
getActiveConnections(): ConnectionProfile[]; getActiveConnections(providers?: string[]): ConnectionProfile[];
saveProfileGroup(profile: IConnectionProfileGroup): Promise<string>; saveProfileGroup(profile: IConnectionProfileGroup): Promise<string>;
@@ -270,7 +271,24 @@ export interface IConnectionManagementService {
export const IConnectionDialogService = createDecorator<IConnectionDialogService>('connectionDialogService'); export const IConnectionDialogService = createDecorator<IConnectionDialogService>('connectionDialogService');
export interface IConnectionDialogService { export interface IConnectionDialogService {
_serviceBrand: any; _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<void>; showDialog(connectionManagementService: IConnectionManagementService, params: INewConnectionParams, model: IConnectionProfile, connectionResult?: IConnectionResult): Thenable<void>;
/**
* 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<IConnectionProfile>;
} }
export interface IServerGroupDialogCallbacks { export interface IServerGroupDialogCallbacks {
@@ -304,6 +322,7 @@ export interface INewConnectionParams {
runQueryOnCompletion?: RunQueryOnConnectionMode; runQueryOnCompletion?: RunQueryOnConnectionMode;
querySelection?: sqlops.ISelectionData; querySelection?: sqlops.ISelectionData;
showDashboard?: boolean; showDashboard?: boolean;
providers?: string[];
} }
export interface IConnectableInput { export interface IConnectableInput {

View File

@@ -627,12 +627,12 @@ export class ConnectionManagementService extends Disposable implements IConnecti
} }
} }
public getConnectionGroups(): ConnectionProfileGroup[] { public getConnectionGroups(providers?: string[]): ConnectionProfileGroup[] {
return this._connectionStore.getConnectionProfileGroups(); return this._connectionStore.getConnectionProfileGroups(false, providers);
} }
public getRecentConnections(): ConnectionProfile[] { public getRecentConnections(providers?: string[]): ConnectionProfile[] {
return this._connectionStore.getRecentlyUsedConnections(); return this._connectionStore.getRecentlyUsedConnections(providers);
} }
@@ -644,7 +644,7 @@ export class ConnectionManagementService extends Disposable implements IConnecti
this._connectionStore.removeConnectionToMemento(connectionProfile, Constants.recentConnections); this._connectionStore.removeConnectionToMemento(connectionProfile, Constants.recentConnections);
} }
public getActiveConnections(): ConnectionProfile[] { public getActiveConnections(providers?: string[]): ConnectionProfile[] {
return this._connectionStatusManager.getActiveConnectionProfiles(); return this._connectionStatusManager.getActiveConnectionProfiles();
} }
@@ -1002,14 +1002,14 @@ export class ConnectionManagementService extends Disposable implements IConnecti
let connectionMngInfo = this._connectionStatusManager.findConnection(uri); let connectionMngInfo = this._connectionStatusManager.findConnection(uri);
if (connectionMngInfo && connectionMngInfo.deleted) { if (connectionMngInfo && connectionMngInfo.deleted) {
this._connectionStatusManager.deleteConnection(uri); 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 { } else {
if (errorMessage) { if (errorMessage) {
// Connection to the server failed // Connection to the server failed
this._connectionStatusManager.deleteConnection(uri); 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 { } else {
resolve({ connected: connectResult, errorMessage: errorMessage, errorCode: errorCode, callStack: callStack }); resolve({ connected: connectResult, errorMessage: errorMessage, errorCode: errorCode, callStack: callStack, connectionProfile: connection });
} }
} }
}); });

View File

@@ -193,9 +193,14 @@ export class ConnectionStatusManager {
/** /**
* Get a list of the active connection profiles managed by the status manager * 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); 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 // 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;
} }
} }

View File

@@ -213,13 +213,16 @@ export class ConnectionStore {
* *
* @returns {sqlops.ConnectionInfo} the array of connections, empty if none are found * @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]; let configValues: IConnectionProfile[] = this._memento[Constants.recentConnections];
if (!configValues) { if (!configValues) {
configValues = []; configValues = [];
} }
configValues = configValues.filter(c => !!(c)); configValues = configValues.filter(c => !!(c));
if (providers && providers.length > 0) {
configValues = configValues.filter(c => providers.includes(c.providerName));
}
return this.convertConfigValuesToConnectionProfiles(configValues); 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[]; let profilesInConfiguration: ConnectionProfile[];
if (!withoutConnections) { if (!withoutConnections) {
profilesInConfiguration = this._connectionConfig.getConnections(true); profilesInConfiguration = this._connectionConfig.getConnections(true);
if (providers && providers.length > 0) {
profilesInConfiguration = profilesInConfiguration.filter(x => providers.includes(x.providerName));
}
} }
let groups = this._connectionConfig.getAllGroups(); let groups = this._connectionConfig.getAllGroups();

View File

@@ -132,8 +132,8 @@ export class ConnectionController implements IConnectionComponentController {
} }
} }
private getAllServerGroups(): IConnectionProfileGroup[] { private getAllServerGroups(providers?: string[]): IConnectionProfileGroup[] {
var connectionGroupRoot = this._connectionManagementService.getConnectionGroups(); var connectionGroupRoot = this._connectionManagementService.getConnectionGroups(providers);
var connectionGroupNames: IConnectionProfileGroup[] = []; var connectionGroupNames: IConnectionProfileGroup[] = [];
if (connectionGroupRoot && connectionGroupRoot.length > 0) { if (connectionGroupRoot && connectionGroupRoot.length > 0) {
this.getServerGroupHelper(connectionGroupRoot[0], connectionGroupNames); this.getServerGroupHelper(connectionGroupRoot[0], connectionGroupNames);
@@ -149,8 +149,8 @@ export class ConnectionController implements IConnectionComponentController {
return connectionGroupNames; return connectionGroupNames;
} }
public initDialog(connectionInfo: IConnectionProfile): void { public initDialog(providers: string[], connectionInfo: IConnectionProfile): void {
this._connectionWidget.updateServerGroup(this.getAllServerGroups()); this._connectionWidget.updateServerGroup(this.getAllServerGroups(providers));
this._model = connectionInfo; this._model = connectionInfo;
this._model.providerName = this._providerName; this._model.providerName = this._providerName;
let appNameOption = this._providerOptions.find(option => option.specialValueType === ConnectionOptionSpecialType.appName); let appNameOption = this._providerOptions.find(option => option.specialValueType === ConnectionOptionSpecialType.appName);

View File

@@ -33,6 +33,7 @@ import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService
import { ICommandService } from 'vs/platform/commands/common/commands'; import { ICommandService } from 'vs/platform/commands/common/commands';
import * as types from 'vs/base/common/types'; import * as types from 'vs/base/common/types';
import { trim } from 'vs/base/common/strings'; import { trim } from 'vs/base/common/strings';
import { Deferred } from 'sql/base/common/promise';
export interface IConnectionValidateResult { export interface IConnectionValidateResult {
isValid: boolean; isValid: boolean;
@@ -49,7 +50,7 @@ export interface IConnectionComponentCallbacks {
export interface IConnectionComponentController { export interface IConnectionComponentController {
showUiComponent(container: HTMLElement): void; showUiComponent(container: HTMLElement): void;
initDialog(model: IConnectionProfile): void; initDialog(providers: string[], model: IConnectionProfile): void;
validateConnection(): IConnectionValidateResult; validateConnection(): IConnectionValidateResult;
fillInConnectionInputs(connectionInfo: IConnectionProfile): void; fillInConnectionInputs(connectionInfo: IConnectionProfile): void;
handleOnConnecting(): void; handleOnConnecting(): void;
@@ -75,6 +76,7 @@ export class ConnectionDialogService implements IConnectionDialogService {
private _currentProviderType: string = 'Microsoft SQL Server'; private _currentProviderType: string = 'Microsoft SQL Server';
private _connecting: boolean = false; private _connecting: boolean = false;
private _connectionErrorTitle = localize('connectionError', 'Connection error'); private _connectionErrorTitle = localize('connectionError', 'Connection error');
private _dialogDeferredPromise: Deferred<IConnectionProfile>;
constructor( constructor(
@IPartService private _partService: IPartService, @IPartService private _partService: IPartService,
@@ -82,17 +84,24 @@ export class ConnectionDialogService implements IConnectionDialogService {
@ICapabilitiesService private _capabilitiesService: ICapabilitiesService, @ICapabilitiesService private _capabilitiesService: ICapabilitiesService,
@IErrorMessageService private _errorMessageService: IErrorMessageService, @IErrorMessageService private _errorMessageService: IErrorMessageService,
@IWorkspaceConfigurationService private _workspaceConfigurationService: IWorkspaceConfigurationService, @IWorkspaceConfigurationService private _workspaceConfigurationService: IWorkspaceConfigurationService,
@IWindowsService private _windowsService: IWindowsService,
@IClipboardService private _clipboardService: IClipboardService, @IClipboardService private _clipboardService: IClipboardService,
@ICommandService private _commandService: ICommandService @ICommandService private _commandService: ICommandService
) { } ) { }
private getDefaultProviderName() { private getDefaultProviderName() {
if (this._workspaceConfigurationService) { let defaultProvider: string;
let defaultProvider = WorkbenchUtils.getSqlConfigValue<string>(this._workspaceConfigurationService, Constants.defaultEngine); if (this._providerNameToDisplayNameMap) {
let keys = Object.keys(this._providerNameToDisplayNameMap);
if (keys && keys.length > 0) {
defaultProvider = keys[0];
}
}
if (!defaultProvider && this._workspaceConfigurationService) {
defaultProvider = WorkbenchUtils.getSqlConfigValue<string>(this._workspaceConfigurationService, Constants.defaultEngine);
} }
// as a fallback, default to MSSQL if the value from settings is not available // 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 { private handleOnConnect(params: INewConnectionParams, profile?: IConnectionProfile): void {
@@ -148,26 +157,28 @@ export class ConnectionDialogService implements IConnectionDialogService {
this._connecting = false; this._connecting = false;
} }
this.uiController.databaseDropdownExpanded = false; this.uiController.databaseDropdownExpanded = false;
this._dialogDeferredPromise.resolve(undefined);
} }
private handleDefaultOnConnect(params: INewConnectionParams, connection: IConnectionProfile): Thenable<void> { private handleDefaultOnConnect(params: INewConnectionParams, connection: IConnectionProfile): Thenable<void> {
let fromEditor = params && params.connectionType === ConnectionType.editor; let fromEditor = params && params.connectionType === ConnectionType.editor;
let uri: string = undefined; let uri: string = undefined;
if (fromEditor && params.input) { if (fromEditor && params && params.input) {
uri = params.input.uri; uri = params.input.uri;
} }
let options: IConnectionCompletionOptions = { let options: IConnectionCompletionOptions = {
params: params, params: params,
saveTheConnection: !fromEditor, saveTheConnection: !fromEditor,
showDashboard: params.showDashboard !== undefined ? params.showDashboard : !fromEditor, showDashboard: params && params.showDashboard !== undefined ? params.showDashboard : !fromEditor,
showConnectionDialogOnError: false, showConnectionDialogOnError: false,
showFirewallRuleOnError: true 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; this._connecting = false;
if (connectionResult && connectionResult.connected) { if (connectionResult && connectionResult.connected) {
this._connectionDialog.close(); this._connectionDialog.close();
this._dialogDeferredPromise.resolve(connectionResult.connectionProfile);
} else if (connectionResult && connectionResult.errorHandled) { } else if (connectionResult && connectionResult.errorHandled) {
this._connectionDialog.resetConnection(); this._connectionDialog.resetConnection();
} else { } else {
@@ -213,7 +224,7 @@ export class ConnectionDialogService implements IConnectionDialogService {
} }
private handleInitDialog() { private handleInitDialog() {
this.uiController.initDialog(this._model); this.uiController.initDialog(this._params && this._params.providers, this._model);
} }
private handleFillInConnectionInputs(connectionInfo: IConnectionProfile): void { 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<IConnectionProfile> {
this._dialogDeferredPromise = new Deferred<IConnectionProfile>();
this.showDialog(connectionManagementService,
params,
model,
connectionResult).then(() => {
}, error => {
this._dialogDeferredPromise.reject(error);
});
return this._dialogDeferredPromise;
}
public showDialog( public showDialog(
connectionManagementService: IConnectionManagementService, connectionManagementService: IConnectionManagementService,
params: INewConnectionParams, params?: INewConnectionParams,
model?: IConnectionProfile, model?: IConnectionProfile,
connectionResult?: IConnectionResult): Thenable<void> { connectionResult?: IConnectionResult): Thenable<void> {
@@ -297,11 +324,12 @@ export class ConnectionDialogService implements IConnectionDialogService {
}); });
} }
private doShowDialog(params: INewConnectionParams): TPromise<void> { private doShowDialog(params: INewConnectionParams): TPromise<void> {
if (!this._connectionDialog) { if (!this._connectionDialog) {
let container = document.getElementById(this._partService.getWorkbenchElementId()).parentElement; let container = document.getElementById(this._partService.getWorkbenchElementId()).parentElement;
this._container = container; 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.onCancel(() => {
this._connectionDialog.databaseDropdownExpanded = this.uiController.databaseDropdownExpanded; this._connectionDialog.databaseDropdownExpanded = this.uiController.databaseDropdownExpanded;
this.handleOnCancel(this._connectionDialog.newConnectionParams); this.handleOnCancel(this._connectionDialog.newConnectionParams);
@@ -316,7 +344,7 @@ export class ConnectionDialogService implements IConnectionDialogService {
this._connectionDialog.newConnectionParams = params; this._connectionDialog.newConnectionParams = params;
return new TPromise<void>(() => { return new TPromise<void>(() => {
this._connectionDialog.open(this._connectionManagementService.getRecentConnections().length > 0); this._connectionDialog.open(this._connectionManagementService.getRecentConnections(params.providers).length > 0);
this.uiController.focusOnOpen(); this.uiController.focusOnOpen();
}); });
} }

View File

@@ -59,6 +59,7 @@ export class ConnectionDialogWidget extends Modal {
private $connectionUIContainer: Builder; private $connectionUIContainer: Builder;
private _databaseDropdownExpanded: boolean; private _databaseDropdownExpanded: boolean;
private _actionbar: ActionBar; private _actionbar: ActionBar;
private _providers: string[];
private _panel: TabbedPanel; private _panel: TabbedPanel;
private _recentConnectionTabId: PanelTabIdentifier; private _recentConnectionTabId: PanelTabIdentifier;
@@ -84,6 +85,7 @@ export class ConnectionDialogWidget extends Modal {
constructor( constructor(
private providerTypeOptions: string[], private providerTypeOptions: string[],
private selectedProviderType: string, private selectedProviderType: string,
private providerNameToDisplayNameMap: { [providerDisplayName: string]: string },
@IInstantiationService private _instantiationService: IInstantiationService, @IInstantiationService private _instantiationService: IInstantiationService,
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService, @IConnectionManagementService private _connectionManagementService: IConnectionManagementService,
@IWorkbenchThemeService private _themeService: IWorkbenchThemeService, @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 }); 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 { protected renderBody(container: HTMLElement): void {
let connectionContainer = $('.connection-dialog'); let connectionContainer = $('.connection-dialog');
container.appendChild(connectionContainer.getHTMLElement()); container.appendChild(connectionContainer.getHTMLElement());
@@ -147,7 +165,7 @@ export class ConnectionDialogWidget extends Modal {
this._panel.onTabChange(c => { this._panel.onTabChange(c => {
if (c === savedConnectionTabId && this._savedConnectionTree.getContentHeight() === 0) { if (c === savedConnectionTabId && this._savedConnectionTree.getContentHeight() === 0) {
// Update saved connection tree // 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) { if (this._savedConnectionTree.getContentHeight() > 0) {
this._noSavedConnectionBuilder.hide(); this._noSavedConnectionBuilder.hide();
@@ -366,7 +384,7 @@ export class ConnectionDialogWidget extends Modal {
this._recentConnectionBuilder.hide(); this._recentConnectionBuilder.hide();
this._noRecentConnectionBuilder.show(); this._noRecentConnectionBuilder.show();
} }
TreeUpdateUtils.structuralTreeUpdate(this._recentConnectionTree, 'recent', this._connectionManagementService); TreeUpdateUtils.structuralTreeUpdate(this._recentConnectionTree, 'recent', this._connectionManagementService, this._providers);
// reset saved connection tree // reset saved connection tree
this._savedConnectionTree.setInput([]); this._savedConnectionTree.setInput([]);
@@ -415,6 +433,8 @@ export class ConnectionDialogWidget extends Modal {
public set newConnectionParams(params: INewConnectionParams) { public set newConnectionParams(params: INewConnectionParams) {
this._newConnectionParams = params; this._newConnectionParams = params;
this._providers = params && params.providers;
this.refresh();
} }
public updateProvider(displayName: string) { public updateProvider(displayName: string) {

View File

@@ -22,7 +22,7 @@ export class TreeUpdateUtils {
/** /**
* Set input for the tree. * 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 selectedElement: any;
let targetsToExpand: any[]; let targetsToExpand: any[];
if (tree) { if (tree) {
@@ -35,13 +35,13 @@ export class TreeUpdateUtils {
let groups; let groups;
let treeInput = new ConnectionProfileGroup('root', null, undefined, undefined, undefined); let treeInput = new ConnectionProfileGroup('root', null, undefined, undefined, undefined);
if (viewKey === 'recent') { if (viewKey === 'recent') {
groups = connectionManagementService.getRecentConnections(); groups = connectionManagementService.getRecentConnections(providers);
treeInput.addConnections(groups); treeInput.addConnections(groups);
} else if (viewKey === 'active') { } else if (viewKey === 'active') {
groups = connectionManagementService.getActiveConnections(); groups = connectionManagementService.getActiveConnections(providers);
treeInput.addConnections(groups); treeInput.addConnections(groups);
} else if (viewKey === 'saved') { } else if (viewKey === 'saved') {
treeInput = TreeUpdateUtils.getTreeInput(connectionManagementService); treeInput = TreeUpdateUtils.getTreeInput(connectionManagementService, providers);
} }
tree.setInput(treeInput).done(() => { 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) { if (groups && groups.length > 0) {
let treeInput = groups[0]; let treeInput = groups[0];
treeInput.name = 'root'; treeInput.name = 'root';

View File

@@ -1106,7 +1106,7 @@ declare module 'sqlops' {
/** /**
* Connection information * Connection information
*/ */
connection: connection.Connection; connection?: connection.Connection;
/** /**
* Operation Display Name * Operation Display Name
@@ -1153,5 +1153,12 @@ declare module 'sqlops' {
* @param connectionId The ID of the connection * @param connectionId The ID of the connection
*/ */
export function getUriForConnection(connectionId: string): Thenable<string>; export function getUriForConnection(connectionId: string): Thenable<string>;
/**
* 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<connection.Connection>;
} }
} }

View File

@@ -6,6 +6,7 @@
import { ExtHostConnectionManagementShape, SqlMainContext, MainThreadConnectionManagementShape } from 'sql/workbench/api/node/sqlExtHost.protocol'; import { ExtHostConnectionManagementShape, SqlMainContext, MainThreadConnectionManagementShape } from 'sql/workbench/api/node/sqlExtHost.protocol';
import { IMainContext } from 'vs/workbench/api/node/extHost.protocol'; import { IMainContext } from 'vs/workbench/api/node/extHost.protocol';
import { generateUuid } from 'vs/base/common/uuid';
import * as sqlops from 'sqlops'; import * as sqlops from 'sqlops';
export class ExtHostConnectionManagement extends ExtHostConnectionManagementShape { export class ExtHostConnectionManagement extends ExtHostConnectionManagementShape {
@@ -31,6 +32,10 @@ export class ExtHostConnectionManagement extends ExtHostConnectionManagementShap
return this._proxy.$getCredentials(connectionId); return this._proxy.$getCredentials(connectionId);
} }
public $openConnectionDialog(providers?: string[]): Thenable<sqlops.connection.Connection> {
return this._proxy.$openConnectionDialog(providers);
}
public $listDatabases(connectionId: string): Thenable<string[]> { public $listDatabases(connectionId: string): Thenable<string[]> {
return this._proxy.$listDatabases(connectionId); return this._proxy.$listDatabases(connectionId);
} }

View File

@@ -8,7 +8,7 @@ import { SqlExtHostContext, SqlMainContext, ExtHostConnectionManagementShape, Ma
import * as sqlops from 'sqlops'; import * as sqlops from 'sqlops';
import { IExtHostContext } from 'vs/workbench/api/node/extHost.protocol'; import { IExtHostContext } from 'vs/workbench/api/node/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; 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 { IObjectExplorerService } from 'sql/parts/objectExplorer/common/objectExplorerService';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import * as TaskUtilities from 'sql/workbench/common/taskUtilities'; import * as TaskUtilities from 'sql/workbench/common/taskUtilities';
@@ -25,7 +25,8 @@ export class MainThreadConnectionManagement implements MainThreadConnectionManag
extHostContext: IExtHostContext, extHostContext: IExtHostContext,
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService, @IConnectionManagementService private _connectionManagementService: IConnectionManagementService,
@IObjectExplorerService private _objectExplorerService: IObjectExplorerService, @IObjectExplorerService private _objectExplorerService: IObjectExplorerService,
@IWorkbenchEditorService private _workbenchEditorService: IWorkbenchEditorService @IWorkbenchEditorService private _workbenchEditorService: IWorkbenchEditorService,
@IConnectionDialogService private _connectionDialogService: IConnectionDialogService,
) { ) {
if (extHostContext) { if (extHostContext) {
this._proxy = extHostContext.getProxy(SqlExtHostContext.ExtHostConnectionManagement); this._proxy = extHostContext.getProxy(SqlExtHostContext.ExtHostConnectionManagement);
@@ -49,6 +50,16 @@ export class MainThreadConnectionManagement implements MainThreadConnectionManag
return Promise.resolve(this._connectionManagementService.getActiveConnectionCredentials(connectionId)); return Promise.resolve(this._connectionManagementService.getActiveConnectionCredentials(connectionId));
} }
public async $openConnectionDialog(providers: string[]): Promise<sqlops.connection.Connection> {
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<string[]> { public async $listDatabases(connectionId: string): Promise<string[]> {
let connection = this._connectionManagementService.getActiveConnections().find(profile => profile.id === connectionId); let connection = this._connectionManagementService.getActiveConnections().find(profile => profile.id === connectionId);
let connectionUri = this._connectionManagementService.getConnectionUri(connection); let connectionUri = this._connectionManagementService.getConnectionUri(connection);

View File

@@ -104,6 +104,9 @@ export function createApiFactory(
getCredentials(connectionId: string): Thenable<{ [name: string]: string }> { getCredentials(connectionId: string): Thenable<{ [name: string]: string }> {
return extHostConnectionManagement.$getCredentials(connectionId); return extHostConnectionManagement.$getCredentials(connectionId);
}, },
openConnectionDialog(providers?: string[]): Thenable<sqlops.connection.Connection> {
return extHostConnectionManagement.$openConnectionDialog(providers);
},
listDatabases(connectionId: string): Thenable<string[]> { listDatabases(connectionId: string): Thenable<string[]> {
return extHostConnectionManagement.$listDatabases(connectionId); return extHostConnectionManagement.$listDatabases(connectionId);
}, },

View File

@@ -33,7 +33,9 @@ export abstract class ExtHostAccountManagementShape {
$refresh(handle: number, account: sqlops.Account): Thenable<sqlops.Account> { throw ni(); } $refresh(handle: number, account: sqlops.Account): Thenable<sqlops.Account> { throw ni(); }
} }
export abstract class ExtHostConnectionManagementShape { } export abstract class ExtHostConnectionManagementShape {
$onConnectionOpened(handleId: string, connection: sqlops.connection.Connection): void { throw ni; }
}
export abstract class ExtHostDataProtocolShape { export abstract class ExtHostDataProtocolShape {
@@ -491,6 +493,7 @@ export interface MainThreadConnectionManagementShape extends IDisposable {
$getActiveConnections(): Thenable<sqlops.connection.Connection[]>; $getActiveConnections(): Thenable<sqlops.connection.Connection[]>;
$getCurrentConnection(): Thenable<sqlops.connection.Connection>; $getCurrentConnection(): Thenable<sqlops.connection.Connection>;
$getCredentials(connectionId: string): Thenable<{ [name: string]: string }>; $getCredentials(connectionId: string): Thenable<{ [name: string]: string }>;
$openConnectionDialog(providers: string[]): Thenable<sqlops.connection.Connection>;
$listDatabases(connectionId: string): Thenable<string[]>; $listDatabases(connectionId: string): Thenable<string[]>;
$getConnectionString(connectionId: string, includePassword: boolean): Thenable<string>; $getConnectionString(connectionId: string, includePassword: boolean): Thenable<string>;
$getUriForConnection(connectionId: string): Thenable<string>; $getUriForConnection(connectionId: string): Thenable<string>;

View File

@@ -23,7 +23,7 @@ suite('ConnectionDialogService tests', () => {
setup(() => { setup(() => {
let errorMessageService = getMockErrorMessageService(); let errorMessageService = getMockErrorMessageService();
connectionDialogService = new ConnectionDialogService(undefined, undefined, undefined, errorMessageService.object, connectionDialogService = new ConnectionDialogService(undefined, undefined, undefined, errorMessageService.object,
undefined, undefined, undefined, undefined); undefined, undefined, undefined);
mockConnectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Strict, {}, {}); mockConnectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Strict, {}, {});
(connectionDialogService as any)._connectionManagementService = mockConnectionManagementService.object; (connectionDialogService as any)._connectionManagementService = mockConnectionManagementService.object;
mockConnectionDialog = TypeMoq.Mock.ofType(ConnectionDialogWidget, TypeMoq.MockBehavior.Strict, mockConnectionDialog = TypeMoq.Mock.ofType(ConnectionDialogWidget, TypeMoq.MockBehavior.Strict,
@@ -34,6 +34,7 @@ suite('ConnectionDialogService tests', () => {
undefined, undefined,
undefined, undefined,
undefined, undefined,
undefined,
new ContextKeyServiceStub() new ContextKeyServiceStub()
); );
mockConnectionDialog.setup(c => c.resetConnection()); mockConnectionDialog.setup(c => c.resetConnection());

View File

@@ -108,7 +108,7 @@ suite('SQL ConnectionManagementService tests', () => {
c => c.serverName === connectionProfileWithEmptyUnsavedPassword.serverName))).returns( c => c.serverName === connectionProfileWithEmptyUnsavedPassword.serverName))).returns(
() => Promise.resolve({ profile: connectionProfileWithEmptyUnsavedPassword, savedCred: false })); () => Promise.resolve({ profile: connectionProfileWithEmptyUnsavedPassword, savedCred: false }));
connectionStore.setup(x => x.isPasswordRequired(TypeMoq.It.isAny())).returns(() => true); 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); 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( connectionDialogService.verify(x => x.showDialog(
TypeMoq.It.isAny(), TypeMoq.It.isAny(),
TypeMoq.It.is<INewConnectionParams>(p => p.connectionType === connectionType && (uri === undefined || p.input.uri === uri)), TypeMoq.It.is<INewConnectionParams>(p => p.connectionType === connectionType && (uri === undefined || p.input.uri === uri)),
TypeMoq.It.is<IConnectionProfile>(c => c.serverName === connectionProfile.serverName), TypeMoq.It.is<IConnectionProfile>(c => c !== undefined && c.serverName === connectionProfile.serverName),
connectionResult ? TypeMoq.It.is<IConnectionResult>(r => r.errorMessage === connectionResult.errorMessage && r.callStack === connectionResult.callStack) : undefined), connectionResult ? TypeMoq.It.is<IConnectionResult>(r => r.errorMessage === connectionResult.errorMessage && r.callStack === connectionResult.callStack) : undefined),
didShow ? TypeMoq.Times.once() : TypeMoq.Times.never()); didShow ? TypeMoq.Times.once() : TypeMoq.Times.never());

View File

@@ -26,6 +26,7 @@ import { ConnectionProviderProperties } from 'sql/workbench/parts/connection/com
suite('SQL ConnectionStore tests', () => { suite('SQL ConnectionStore tests', () => {
let defaultNamedProfile: IConnectionProfile; let defaultNamedProfile: IConnectionProfile;
let defaultUnnamedProfile: IConnectionProfile; let defaultUnnamedProfile: IConnectionProfile;
let profileForProvider2: IConnectionProfile;
let context: TypeMoq.Mock<Memento>; let context: TypeMoq.Mock<Memento>;
let credentialStore: TypeMoq.Mock<CredentialsService>; let credentialStore: TypeMoq.Mock<CredentialsService>;
let connectionConfig: TypeMoq.Mock<ConnectionConfig>; let connectionConfig: TypeMoq.Mock<ConnectionConfig>;
@@ -35,6 +36,7 @@ suite('SQL ConnectionStore tests', () => {
let mementoArray: any = []; let mementoArray: any = [];
let maxRecent = 5; let maxRecent = 5;
let msSQLCapabilities: ConnectionProviderProperties; let msSQLCapabilities: ConnectionProviderProperties;
let provider2Capabilities: ConnectionProviderProperties;
let defaultNamedConnectionProfile: ConnectionProfile; let defaultNamedConnectionProfile: ConnectionProfile;
setup(() => { setup(() => {
@@ -72,6 +74,23 @@ suite('SQL ConnectionStore tests', () => {
id: undefined 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'); let momento = new Memento('ConnectionManagement');
context = TypeMoq.Mock.ofInstance(momento); context = TypeMoq.Mock.ofInstance(momento);
context.setup(x => x.getMemento(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => mementoArray); context.setup(x => x.getMemento(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => mementoArray);
@@ -164,7 +183,14 @@ suite('SQL ConnectionStore tests', () => {
displayName: 'MSSQL', displayName: 'MSSQL',
connectionOptions: connectionProvider connectionOptions: connectionProvider
}; };
provider2Capabilities = {
providerId: 'MSSQL',
displayName: 'MSSQL',
connectionOptions: connectionProvider
};
capabilitiesService.capabilities['MSSQL'] = { connection: msSQLCapabilities }; capabilitiesService.capabilities['MSSQL'] = { connection: msSQLCapabilities };
capabilitiesService.capabilities['Provider2'] = { connection: provider2Capabilities };
let groups: IConnectionProfileGroup[] = [ let groups: IConnectionProfileGroup[] = [
{ {
id: 'root', 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) => { test('addActiveConnection should add same connection exactly once', (done) => {
// setup memento for MRU to return a list we have access to // setup memento for MRU to return a list we have access to
credentialStore.setup(x => x.saveCredential(TypeMoq.It.isAny(), TypeMoq.It.isAny())) credentialStore.setup(x => x.saveCredential(TypeMoq.It.isAny(), TypeMoq.It.isAny()))

View File

@@ -19,4 +19,8 @@ export class ConnectionDialogTestService implements IConnectionDialogService {
return TPromise.as(none); return TPromise.as(none);
} }
public openDialogAndWait(connectionManagementService: IConnectionManagementService,
params?: INewConnectionParams, model?: IConnectionProfile, connectionResult?: IConnectionResult): TPromise<IConnectionProfile> {
return TPromise.as(undefined);
}
} }

View File

@@ -63,11 +63,11 @@ export class TestConnectionManagementService implements IConnectionManagementSer
return undefined; return undefined;
} }
getConnectionGroups(): ConnectionProfileGroup[] { getConnectionGroups(providers?: string[]): ConnectionProfileGroup[] {
return []; return [];
} }
getActiveConnections(): ConnectionProfile[] { getActiveConnections(providers?: string[]): ConnectionProfile[] {
return []; return [];
} }
@@ -75,7 +75,7 @@ export class TestConnectionManagementService implements IConnectionManagementSer
return undefined; return undefined;
} }
getRecentConnections(): ConnectionProfile[] { getRecentConnections(providers?: string[]): ConnectionProfile[] {
return []; return [];
} }