/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { ExtHostConnectionManagementShape, MainThreadConnectionManagementShape } from 'sql/workbench/api/common/sqlExtHost.protocol'; import * as azdata from 'azdata'; import { IConnectionManagementService, ConnectionType, IConnectionParams } from 'sql/platform/connection/common/connectionManagement'; import { IObjectExplorerService } from 'sql/workbench/services/objectExplorer/browser/objectExplorerService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import * as TaskUtilities from 'sql/workbench/browser/taskUtilities'; import { IConnectionProfile } from 'sql/platform/connection/common/interfaces'; import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { isUndefinedOrNull } from 'vs/base/common/types'; import { generateUuid } from 'vs/base/common/uuid'; import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService'; import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile'; import { IConnectionDialogService } from 'sql/workbench/services/connection/common/connectionDialogService'; import { deepClone } from 'vs/base/common/objects'; import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers'; import { SqlExtHostContext, SqlMainContext } from 'vs/workbench/api/common/extHost.protocol'; @extHostNamedCustomer(SqlMainContext.MainThreadConnectionManagement) export class MainThreadConnectionManagement extends Disposable implements MainThreadConnectionManagementShape { private _proxy: ExtHostConnectionManagementShape; private _connectionEventListenerDisposables = new Map(); constructor( extHostContext: IExtHostContext, @IConnectionManagementService private _connectionManagementService: IConnectionManagementService, @IObjectExplorerService private _objectExplorerService: IObjectExplorerService, @IEditorService private _workbenchEditorService: IEditorService, @IConnectionDialogService private _connectionDialogService: IConnectionDialogService, @ICapabilitiesService private _capabilitiesService: ICapabilitiesService ) { super(); if (extHostContext) { this._proxy = extHostContext.getProxy(SqlExtHostContext.ExtHostConnectionManagement); } } public $registerConnectionEventListener(handle: number): void { let stripProfile = (inputProfile: azdata.IConnectionProfile) => { if (!inputProfile) { return inputProfile; } let outputProfile: azdata.IConnectionProfile = { connectionName: inputProfile.connectionName, serverName: inputProfile.serverName, databaseName: inputProfile.databaseName, userName: inputProfile.userName, password: inputProfile.password, authenticationType: inputProfile.authenticationType, savePassword: inputProfile.savePassword, groupFullName: inputProfile.groupFullName, groupId: inputProfile.groupId, providerName: inputProfile.providerName, saveProfile: inputProfile.saveProfile, id: inputProfile.id, azureTenantId: inputProfile.azureTenantId, azureAccount: inputProfile.azureAccount, options: inputProfile.options }; return outputProfile; }; const disposable = new DisposableStore(); disposable.add(this._connectionManagementService.onConnect((params: IConnectionParams) => { this._proxy.$onConnectionEvent(handle, 'onConnect', params.connectionUri, stripProfile(params.connectionProfile)); })); disposable.add(this._connectionManagementService.onConnectionChanged((params: IConnectionParams) => { this._proxy.$onConnectionEvent(handle, 'onConnectionChanged', params.connectionUri, stripProfile(params.connectionProfile)); })); disposable.add(this._connectionManagementService.onDisconnect((params: IConnectionParams) => { this._proxy.$onConnectionEvent(handle, 'onDisconnect', params.connectionUri, stripProfile(params.connectionProfile)); })); this._connectionEventListenerDisposables.set(handle, disposable); } public $unregisterConnectionEventListener(handle: number): void { const disposable = this._connectionEventListenerDisposables.get(handle); if (disposable) { disposable.dispose(); this._connectionEventListenerDisposables.delete(handle); } } public $getConnections(activeConnectionsOnly?: boolean): Thenable { return Promise.resolve(this._connectionManagementService.getConnections(activeConnectionsOnly).map(profile => this.convertToConnectionProfile(profile))); } public $getConnection(uri: string): Thenable { const profile = this._connectionManagementService.getConnectionProfile(uri); if (!profile) { return Promise.resolve(undefined); } let connection: azdata.connection.ConnectionProfile = { providerId: profile.providerName, connectionId: profile.id, connectionName: profile.connectionName, serverName: profile.serverName, databaseName: profile.databaseName, userName: profile.userName, password: profile.password, authenticationType: profile.authenticationType, savePassword: profile.savePassword, groupFullName: profile.groupFullName, groupId: profile.groupId, saveProfile: profile.savePassword, azureTenantId: profile.azureTenantId, options: profile.options }; return Promise.resolve(connection); } public $getActiveConnections(): Thenable { return Promise.resolve(this._connectionManagementService.getActiveConnections().map(profile => this.convertConnection(profile))); } public $getCurrentConnection(): Thenable { return Promise.resolve(this.convertConnection(TaskUtilities.getCurrentGlobalConnection(this._objectExplorerService, this._connectionManagementService, this._workbenchEditorService, true))); } public $getCurrentConnectionProfile(): Thenable { return Promise.resolve(this.convertToConnectionProfile(TaskUtilities.getCurrentGlobalConnection(this._objectExplorerService, this._connectionManagementService, this._workbenchEditorService, true))); } public $getCredentials(connectionId: string): Thenable<{ [name: string]: string }> { return Promise.resolve(this._connectionManagementService.getConnectionCredentials(connectionId)); } public $getServerInfo(connectionId: string): Thenable { return Promise.resolve(this._connectionManagementService.getServerInfo(connectionId)); } public async $openConnectionDialog(providers: string[], initialConnectionProfile?: IConnectionProfile, connectionCompletionOptions?: azdata.IConnectionCompletionOptions): Promise { if (initialConnectionProfile?.providerName && this._capabilitiesService.providers[initialConnectionProfile.providerName] === undefined) { await this._connectionManagementService.handleUnsupportedProvider(initialConnectionProfile.providerName); return undefined; } // Here we default to ConnectionType.default which saves the connection in the connection store and server tree by default let connectionType = ConnectionType.default; // If the API call explicitly set saveConnection to false, set it to ConnectionType.extension // which doesn't save the connection by default if (connectionCompletionOptions && !connectionCompletionOptions.saveConnection) { connectionType = ConnectionType.temporary; } let connectionProfile = await this._connectionDialogService.openDialogAndWait(this._connectionManagementService, { connectionType: connectionType, providers: providers }, initialConnectionProfile, undefined); if (!connectionProfile) { return undefined; } connectionProfile.options.savePassword = connectionProfile.savePassword; const connection = connectionProfile ? { connectionId: connectionProfile.id, options: connectionProfile.options, providerName: connectionProfile.providerName } : undefined; if (connectionCompletionOptions && connectionCompletionOptions.saveConnection) { await this._connectionManagementService.connectAndSaveProfile(connectionProfile, undefined, { saveTheConnection: isUndefinedOrNull(connectionCompletionOptions.saveConnection) ? true : connectionCompletionOptions.saveConnection, showDashboard: isUndefinedOrNull(connectionCompletionOptions.showDashboard) ? false : connectionCompletionOptions.showDashboard, showConnectionDialogOnError: isUndefinedOrNull(connectionCompletionOptions.showConnectionDialogOnError) ? true : connectionCompletionOptions.showConnectionDialogOnError, showFirewallRuleOnError: isUndefinedOrNull(connectionCompletionOptions.showFirewallRuleOnError) ? true : connectionCompletionOptions.showFirewallRuleOnError }); } return connection; } public $openChangePasswordDialog(profile: IConnectionProfile): Thenable { // Need to have access to getOptionsKey, so recreate profile from details. let convertedProfile = new ConnectionProfile(this._capabilitiesService, profile); return this._connectionManagementService.openChangePasswordDialog(convertedProfile); } public async $listDatabases(connectionId: string): Promise { let connectionUri = await this.$getUriForConnection(connectionId); let result = await this._connectionManagementService.listDatabases(connectionUri); return result.databaseNames; } public async $getConnectionString(connectionId: string, includePassword: boolean): Promise { return this._connectionManagementService.getConnectionString(connectionId, includePassword); } public $getUriForConnection(connectionId: string): Thenable { return Promise.resolve(this._connectionManagementService.getConnectionUriFromId(connectionId)); } private convertConnection(profile: IConnectionProfile): azdata.connection.Connection { if (!profile) { return undefined; } profile = this._connectionManagementService.removeConnectionProfileCredentials(profile); let connection: azdata.connection.Connection = { providerName: profile.providerName, connectionId: profile.id, options: deepClone(profile.options) }; return connection; } private convertToConnectionProfile(profile: IConnectionProfile): azdata.connection.ConnectionProfile { if (!profile) { return undefined; } profile = this._connectionManagementService.removeConnectionProfileCredentials(profile); let connection: azdata.connection.ConnectionProfile = { providerId: profile.providerName, connectionId: profile.id, options: deepClone(profile.options), connectionName: profile.connectionName, serverName: profile.serverName, databaseName: profile.databaseName, userName: profile.userName, password: profile.password, authenticationType: profile.authenticationType, savePassword: profile.savePassword, groupFullName: profile.groupFullName, groupId: profile.groupId, saveProfile: profile.saveProfile }; return connection; } public $connect(connectionProfile: IConnectionProfile, saveConnection: boolean = true, showDashboard: boolean = true): Thenable { let profile = new ConnectionProfile(this._capabilitiesService, connectionProfile); profile.id = generateUuid(); return this._connectionManagementService.connectAndSaveProfile(profile, undefined, { saveTheConnection: saveConnection, showDashboard: showDashboard, showConnectionDialogOnError: true, showFirewallRuleOnError: true }).then((result) => { return { connected: result.connected, connectionId: result.connected ? profile.id : undefined, errorCode: result.errorCode, errorMessage: result.errorMessage }; }); } }