mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-17 02:51:36 -05:00
Edit Connection Feature added, edit existing connection in connection tree. (#10214)
* Added Edit Connection Command * Wip changes for new connection dialog * Testing * WIP commit * added ID check to ensure connection * wip commit * model id check implemented * addfooterbutton now accepts events * wip commit * message explaining check * temporary change * connectionManagementService restored * Revert "connectionManagementService restored" This reverts commit 9704a63184a06a33bee2648ef0a899229d117cc0. * formatting test * editConnection promise testing * edit existing connection command added * WIP Connection Edit * disconnect added to editConnection promise * WIP on editExistingConnection * changed isEdit to true * Amir/edit connection (#10112) * Get edit connection working * Delete unused code * check for isEdit as well * connection tree test added * WIP connection management tests * comment out test to find out what's wrong * fix for one error * added note about test skipped * changed signature of saveprofile * saveprofile fixed * wrote working test * added additional test * changed message * Fixes made * fix for matcher Co-authored-by: Amir Omidi <amomidi@microsoft.com>
This commit is contained in:
@@ -7,7 +7,7 @@ import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilit
|
|||||||
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
|
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
|
||||||
import { ConnectionProfileGroup, IConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup';
|
import { ConnectionProfileGroup, IConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup';
|
||||||
import { UNSAVED_GROUP_ID } from 'sql/platform/connection/common/constants';
|
import { UNSAVED_GROUP_ID } from 'sql/platform/connection/common/constants';
|
||||||
import { IConnectionProfile, IConnectionProfileStore } from 'sql/platform/connection/common/interfaces';
|
import { IConnectionProfile, IConnectionProfileStore, ProfileMatcher } from 'sql/platform/connection/common/interfaces';
|
||||||
import * as Utils from 'sql/platform/connection/common/utils';
|
import * as Utils from 'sql/platform/connection/common/utils';
|
||||||
import { generateUuid } from 'vs/base/common/uuid';
|
import { generateUuid } from 'vs/base/common/uuid';
|
||||||
import * as nls from 'vs/nls';
|
import * as nls from 'vs/nls';
|
||||||
@@ -61,7 +61,7 @@ export class ConnectionConfig {
|
|||||||
/**
|
/**
|
||||||
* Add a new connection to the connection config.
|
* Add a new connection to the connection config.
|
||||||
*/
|
*/
|
||||||
public addConnection(profile: IConnectionProfile): Promise<IConnectionProfile> {
|
public addConnection(profile: IConnectionProfile, matcher: ProfileMatcher = ConnectionProfile.matchesProfile): Promise<IConnectionProfile> {
|
||||||
if (profile.saveProfile) {
|
if (profile.saveProfile) {
|
||||||
return this.addGroupFromProfile(profile).then(groupId => {
|
return this.addGroupFromProfile(profile).then(groupId => {
|
||||||
let profiles = deepClone(this.configurationService.inspect<IConnectionProfileStore[]>(CONNECTIONS_CONFIG_KEY).userValue);
|
let profiles = deepClone(this.configurationService.inspect<IConnectionProfileStore[]>(CONNECTIONS_CONFIG_KEY).userValue);
|
||||||
@@ -75,7 +75,7 @@ export class ConnectionConfig {
|
|||||||
// Remove the profile if already set
|
// Remove the profile if already set
|
||||||
let sameProfileInList = find(profiles, value => {
|
let sameProfileInList = find(profiles, value => {
|
||||||
let providerConnectionProfile = ConnectionProfile.createFromStoredProfile(value, this._capabilitiesService);
|
let providerConnectionProfile = ConnectionProfile.createFromStoredProfile(value, this._capabilitiesService);
|
||||||
return providerConnectionProfile.matches(connectionProfile);
|
return matcher(providerConnectionProfile, connectionProfile);
|
||||||
});
|
});
|
||||||
if (sameProfileInList) {
|
if (sameProfileInList) {
|
||||||
let profileIndex = firstIndex(profiles, value => value === sameProfileInList);
|
let profileIndex = firstIndex(profiles, value => value === sameProfileInList);
|
||||||
|
|||||||
@@ -77,6 +77,11 @@ export interface IConnectionManagementService {
|
|||||||
// Properties
|
// Properties
|
||||||
providerNameToDisplayNameMap: { [providerDisplayName: string]: string };
|
providerNameToDisplayNameMap: { [providerDisplayName: string]: string };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the edit connection dialog to change connection.
|
||||||
|
*/
|
||||||
|
showEditConnectionDialog(model: IConnectionProfile): Promise<void>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens the connection dialog to create new connection
|
* Opens the connection dialog to create new connection
|
||||||
*/
|
*/
|
||||||
@@ -300,6 +305,7 @@ export interface INewConnectionParams {
|
|||||||
querySelection?: azdata.ISelectionData;
|
querySelection?: azdata.ISelectionData;
|
||||||
showDashboard?: boolean;
|
showDashboard?: boolean;
|
||||||
providers?: string[];
|
providers?: string[];
|
||||||
|
isEditConnection?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IConnectableInput {
|
export interface IConnectableInput {
|
||||||
|
|||||||
@@ -69,18 +69,23 @@ export class ConnectionProfile extends ProviderConnectionInfo implements interfa
|
|||||||
this.options['databaseDisplayName'] = this.databaseName;
|
this.options['databaseDisplayName'] = this.databaseName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public matches(other: interfaces.IConnectionProfile): boolean {
|
public static matchesProfile(a: interfaces.IConnectionProfile, b: interfaces.IConnectionProfile): boolean {
|
||||||
return other
|
return a && b
|
||||||
&& this.providerName === other.providerName
|
&& a.providerName === b.providerName
|
||||||
&& this.nullCheckEqualsIgnoreCase(this.serverName, other.serverName)
|
&& ConnectionProfile.nullCheckEqualsIgnoreCase(a.serverName, b.serverName)
|
||||||
&& this.nullCheckEqualsIgnoreCase(this.databaseName, other.databaseName)
|
&& ConnectionProfile.nullCheckEqualsIgnoreCase(a.databaseName, b.databaseName)
|
||||||
&& this.nullCheckEqualsIgnoreCase(this.userName, other.userName)
|
&& ConnectionProfile.nullCheckEqualsIgnoreCase(a.userName, b.userName)
|
||||||
&& this.nullCheckEqualsIgnoreCase(this.options['databaseDisplayName'], other.options['databaseDisplayName'])
|
&& ConnectionProfile.nullCheckEqualsIgnoreCase(a.options['databaseDisplayName'], b.options['databaseDisplayName'])
|
||||||
&& this.authenticationType === other.authenticationType
|
&& a.authenticationType === b.authenticationType
|
||||||
&& this.groupId === other.groupId;
|
&& a.groupId === b.groupId;
|
||||||
}
|
}
|
||||||
|
|
||||||
private nullCheckEqualsIgnoreCase(a: string, b: string) {
|
public matches(other: interfaces.IConnectionProfile): boolean {
|
||||||
|
return ConnectionProfile.matchesProfile(this, other);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static nullCheckEqualsIgnoreCase(a: string, b: string) {
|
||||||
let bothNull: boolean = !a && !b;
|
let bothNull: boolean = !a && !b;
|
||||||
return bothNull ? bothNull : equalsIgnoreCase(a, b);
|
return bothNull ? bothNull : equalsIgnoreCase(a, b);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { ConnectionConfig } from 'sql/platform/connection/common/connectionConfi
|
|||||||
import { fixupConnectionCredentials } from 'sql/platform/connection/common/connectionInfo';
|
import { fixupConnectionCredentials } from 'sql/platform/connection/common/connectionInfo';
|
||||||
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
|
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
|
||||||
import { ConnectionProfileGroup, IConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup';
|
import { ConnectionProfileGroup, IConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup';
|
||||||
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
|
import { IConnectionProfile, ProfileMatcher } from 'sql/platform/connection/common/interfaces';
|
||||||
import { ICredentialsService } from 'sql/platform/credentials/common/credentialsService';
|
import { ICredentialsService } from 'sql/platform/credentials/common/credentialsService';
|
||||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||||
@@ -103,21 +103,18 @@ export class ConnectionStore {
|
|||||||
* @param whether the plaintext password should be written to the settings file
|
* @param whether the plaintext password should be written to the settings file
|
||||||
* @returns a Promise that returns the original profile, for help in chaining calls
|
* @returns a Promise that returns the original profile, for help in chaining calls
|
||||||
*/
|
*/
|
||||||
public saveProfile(profile: IConnectionProfile, forceWritePlaintextPassword?: boolean): Promise<IConnectionProfile> {
|
public async saveProfile(profile: IConnectionProfile, forceWritePlaintextPassword?: boolean, matcher?: ProfileMatcher): Promise<IConnectionProfile> {
|
||||||
// Add the profile to the saved list, taking care to clear out the password field if necessary
|
// Add the profile to the saved list, taking care to clear out the password field if necessary
|
||||||
const savedProfile = forceWritePlaintextPassword ? profile : this.getProfileWithoutPassword(profile);
|
const savedProfile = forceWritePlaintextPassword ? profile : this.getProfileWithoutPassword(profile);
|
||||||
return this.saveProfileToConfig(savedProfile)
|
const savedConnectionProfile = await this.saveProfileToConfig(savedProfile, matcher);
|
||||||
.then(savedConnectionProfile => {
|
|
||||||
profile.groupId = savedConnectionProfile.groupId;
|
profile.groupId = savedConnectionProfile.groupId;
|
||||||
profile.id = savedConnectionProfile.id;
|
profile.id = savedConnectionProfile.id;
|
||||||
// Only save if we successfully added the profile
|
// Only save if we successfully added the profile
|
||||||
return this.saveProfilePasswordIfNeeded(profile);
|
await this.saveProfilePasswordIfNeeded(profile);
|
||||||
}).then(() => {
|
|
||||||
// Add necessary default properties before returning
|
// Add necessary default properties before returning
|
||||||
// this is needed to support immediate connections
|
// this is needed to support immediate connections
|
||||||
fixupConnectionCredentials(profile);
|
fixupConnectionCredentials(profile);
|
||||||
return profile;
|
return profile;
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public savePassword(profile: IConnectionProfile): Promise<boolean> {
|
public savePassword(profile: IConnectionProfile): Promise<boolean> {
|
||||||
@@ -134,9 +131,9 @@ export class ConnectionStore {
|
|||||||
return this.connectionConfig.addGroup(profile);
|
return this.connectionConfig.addGroup(profile);
|
||||||
}
|
}
|
||||||
|
|
||||||
private saveProfileToConfig(profile: IConnectionProfile): Promise<IConnectionProfile> {
|
private saveProfileToConfig(profile: IConnectionProfile, matcher?: ProfileMatcher): Promise<IConnectionProfile> {
|
||||||
if (profile.saveProfile) {
|
if (profile.saveProfile) {
|
||||||
return this.connectionConfig.addConnection(profile);
|
return this.connectionConfig.addConnection(profile, matcher);
|
||||||
} else {
|
} else {
|
||||||
return Promise.resolve(profile);
|
return Promise.resolve(profile);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,9 @@
|
|||||||
|
|
||||||
import * as azdata from 'azdata';
|
import * as azdata from 'azdata';
|
||||||
|
|
||||||
|
// Used to allow various methods of matching profiles
|
||||||
|
export type ProfileMatcher = (a: IConnectionProfile, b: IConnectionProfile) => boolean;
|
||||||
|
|
||||||
export interface IConnectionProfile extends azdata.IConnectionProfile {
|
export interface IConnectionProfile extends azdata.IConnectionProfile {
|
||||||
getOptionsKey(): string;
|
getOptionsKey(): string;
|
||||||
matches(profile: azdata.IConnectionProfile): boolean;
|
matches(profile: azdata.IConnectionProfile): boolean;
|
||||||
|
|||||||
@@ -59,6 +59,10 @@ export class TestConnectionManagementService implements IConnectionManagementSer
|
|||||||
return undefined!;
|
return undefined!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
showEditConnectionDialog(model: IConnectionProfile): Promise<void> {
|
||||||
|
return undefined!;
|
||||||
|
}
|
||||||
|
|
||||||
onConnectionComplete(handle: number, connectionInfoSummary: azdata.ConnectionInfoSummary): void {
|
onConnectionComplete(handle: number, connectionInfoSummary: azdata.ConnectionInfoSummary): void {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import * as assert from 'assert';
|
|||||||
import { ConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup';
|
import { ConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup';
|
||||||
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
|
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
|
||||||
import {
|
import {
|
||||||
RefreshAction, AddServerAction, DeleteConnectionAction, DisconnectConnectionAction,
|
RefreshAction, EditConnectionAction, AddServerAction, DeleteConnectionAction, DisconnectConnectionAction,
|
||||||
ActiveConnectionsFilterAction, RecentConnectionsFilterAction
|
ActiveConnectionsFilterAction, RecentConnectionsFilterAction
|
||||||
}
|
}
|
||||||
from 'sql/workbench/services/objectExplorer/browser/connectionTreeAction';
|
from 'sql/workbench/services/objectExplorer/browser/connectionTreeAction';
|
||||||
@@ -68,7 +68,7 @@ suite('SQL Connection Tree Action tests', () => {
|
|||||||
connectionManagementService.setup(x => x.deleteConnectionGroup(TypeMoq.It.isAny())).returns(() => Promise.resolve(true));
|
connectionManagementService.setup(x => x.deleteConnectionGroup(TypeMoq.It.isAny())).returns(() => Promise.resolve(true));
|
||||||
connectionManagementService.setup(x => x.deleteConnection(TypeMoq.It.isAny())).returns(() => Promise.resolve(true));
|
connectionManagementService.setup(x => x.deleteConnection(TypeMoq.It.isAny())).returns(() => Promise.resolve(true));
|
||||||
connectionManagementService.setup(x => x.getConnectionProfile(TypeMoq.It.isAny())).returns(() => profileToReturn);
|
connectionManagementService.setup(x => x.getConnectionProfile(TypeMoq.It.isAny())).returns(() => profileToReturn);
|
||||||
|
connectionManagementService.setup(x => x.showEditConnectionDialog(TypeMoq.It.isAny())).returns(() => new Promise<void>((resolve, reject) => resolve()));
|
||||||
return connectionManagementService;
|
return connectionManagementService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -527,4 +527,32 @@ suite('SQL Connection Tree Action tests', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('EditConnectionAction - test if show connection dialog is called', () => {
|
||||||
|
let connectionManagementService = createConnectionManagementService(true, undefined);
|
||||||
|
|
||||||
|
let connection: ConnectionProfile = new ConnectionProfile(capabilitiesService, {
|
||||||
|
connectionName: 'Test',
|
||||||
|
savePassword: false,
|
||||||
|
groupFullName: 'testGroup',
|
||||||
|
serverName: 'testServerName',
|
||||||
|
databaseName: 'testDatabaseName',
|
||||||
|
authenticationType: 'integrated',
|
||||||
|
password: 'test',
|
||||||
|
userName: 'testUsername',
|
||||||
|
groupId: undefined,
|
||||||
|
providerName: mssqlProviderName,
|
||||||
|
options: {},
|
||||||
|
saveProfile: true,
|
||||||
|
id: 'testId'
|
||||||
|
});
|
||||||
|
|
||||||
|
let connectionAction: EditConnectionAction = new EditConnectionAction(EditConnectionAction.ID,
|
||||||
|
EditConnectionAction.LABEL,
|
||||||
|
connection,
|
||||||
|
connectionManagementService.object);
|
||||||
|
|
||||||
|
return connectionAction.run().then((value) => {
|
||||||
|
connectionManagementService.verify(x => x.showEditConnectionDialog(TypeMoq.It.isAny()), TypeMoq.Times.once());
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ export class ConnectionDialogService implements IConnectionDialogService {
|
|||||||
@IConfigurationService private _configurationService: IConfigurationService,
|
@IConfigurationService private _configurationService: IConfigurationService,
|
||||||
@IClipboardService private _clipboardService: IClipboardService,
|
@IClipboardService private _clipboardService: IClipboardService,
|
||||||
@ICommandService private _commandService: ICommandService,
|
@ICommandService private _commandService: ICommandService,
|
||||||
@ILogService private _logService: ILogService
|
@ILogService private _logService: ILogService,
|
||||||
) {
|
) {
|
||||||
this.initializeConnectionProviders();
|
this.initializeConnectionProviders();
|
||||||
}
|
}
|
||||||
@@ -355,6 +355,7 @@ export class ConnectionDialogService implements IConnectionDialogService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private updateModelServerCapabilities(model: IConnectionProfile) {
|
private updateModelServerCapabilities(model: IConnectionProfile) {
|
||||||
|
|
||||||
if (this._model) {
|
if (this._model) {
|
||||||
this._model.dispose();
|
this._model.dispose();
|
||||||
}
|
}
|
||||||
@@ -410,32 +411,32 @@ export class ConnectionDialogService implements IConnectionDialogService {
|
|||||||
return this._dialogDeferredPromise.promise;
|
return this._dialogDeferredPromise.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
public showDialog(
|
public async showDialog(
|
||||||
connectionManagementService: IConnectionManagementService,
|
connectionManagementService: IConnectionManagementService,
|
||||||
params?: INewConnectionParams,
|
params?: INewConnectionParams,
|
||||||
model?: IConnectionProfile,
|
model?: IConnectionProfile,
|
||||||
connectionResult?: IConnectionResult,
|
connectionResult?: IConnectionResult,
|
||||||
connectionOptions?: IConnectionCompletionOptions): Promise<void> {
|
connectionOptions?: IConnectionCompletionOptions,
|
||||||
|
): Promise<void> {
|
||||||
|
|
||||||
this._connectionManagementService = connectionManagementService;
|
this._connectionManagementService = connectionManagementService;
|
||||||
|
|
||||||
this._options = connectionOptions;
|
this._options = connectionOptions;
|
||||||
this._params = params;
|
this._params = params;
|
||||||
this._inputModel = model;
|
this._inputModel = model;
|
||||||
return new Promise<void>((resolve, reject) => {
|
|
||||||
this.updateModelServerCapabilities(model);
|
this.updateModelServerCapabilities(model);
|
||||||
|
|
||||||
// If connecting from a query editor set "save connection" to false
|
// If connecting from a query editor set "save connection" to false
|
||||||
if (params && (params.input && params.connectionType === ConnectionType.editor ||
|
if (params && (params.input && params.connectionType === ConnectionType.editor ||
|
||||||
params.connectionType === ConnectionType.temporary)) {
|
params.connectionType === ConnectionType.temporary)) {
|
||||||
this._model.saveProfile = false;
|
this._model.saveProfile = false;
|
||||||
}
|
}
|
||||||
|
await this.showDialogWithModel();
|
||||||
|
|
||||||
resolve(this.showDialogWithModel().then(() => {
|
|
||||||
if (connectionResult && connectionResult.errorMessage) {
|
if (connectionResult && connectionResult.errorMessage) {
|
||||||
this.showErrorDialog(Severity.Error, this._connectionErrorTitle, connectionResult.errorMessage, connectionResult.callStack);
|
this.showErrorDialog(Severity.Error, this._connectionErrorTitle, connectionResult.errorMessage, connectionResult.callStack);
|
||||||
}
|
}
|
||||||
}));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async doShowDialog(params: INewConnectionParams): Promise<void> {
|
private async doShowDialog(params: INewConnectionParams): Promise<void> {
|
||||||
|
|||||||
@@ -225,6 +225,26 @@ export class ConnectionManagementService extends Disposable implements IConnecti
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the edit connection dialog
|
||||||
|
* @param model the existing connection profile to edit on.
|
||||||
|
*/
|
||||||
|
public async showEditConnectionDialog(model: interfaces.IConnectionProfile): Promise<void> {
|
||||||
|
if (!model) {
|
||||||
|
throw new Error('Connection Profile is undefined');
|
||||||
|
}
|
||||||
|
let params = {
|
||||||
|
connectionType: ConnectionType.default,
|
||||||
|
isEditConnection: true
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
return await this._connectionDialogService.showDialog(this, params, model);
|
||||||
|
} catch (dialogError) {
|
||||||
|
this._logService.warn('failed to open the connection dialog. error: ' + dialogError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load the password for the profile
|
* Load the password for the profile
|
||||||
* @param connectionProfile Connection Profile
|
* @param connectionProfile Connection Profile
|
||||||
@@ -420,6 +440,8 @@ export class ConnectionManagementService extends Disposable implements IConnecti
|
|||||||
connection.options['groupId'] = connection.groupId;
|
connection.options['groupId'] = connection.groupId;
|
||||||
connection.options['databaseDisplayName'] = connection.databaseName;
|
connection.options['databaseDisplayName'] = connection.databaseName;
|
||||||
|
|
||||||
|
let isEdit = options?.params?.isEditConnection ?? false;
|
||||||
|
|
||||||
if (!uri) {
|
if (!uri) {
|
||||||
uri = Utils.generateUri(connection);
|
uri = Utils.generateUri(connection);
|
||||||
}
|
}
|
||||||
@@ -449,6 +471,7 @@ export class ConnectionManagementService extends Disposable implements IConnecti
|
|||||||
if (!tokenFillSuccess) {
|
if (!tokenFillSuccess) {
|
||||||
throw new Error(nls.localize('connection.noAzureAccount', "Failed to get Azure account token for connection"));
|
throw new Error(nls.localize('connection.noAzureAccount', "Failed to get Azure account token for connection"));
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.createNewConnection(uri, connection).then(async connectionResult => {
|
return this.createNewConnection(uri, connection).then(async connectionResult => {
|
||||||
if (connectionResult && connectionResult.connected) {
|
if (connectionResult && connectionResult.connected) {
|
||||||
// The connected succeeded so add it to our active connections now, optionally adding it to the MRU based on
|
// The connected succeeded so add it to our active connections now, optionally adding it to the MRU based on
|
||||||
@@ -459,8 +482,15 @@ export class ConnectionManagementService extends Disposable implements IConnecti
|
|||||||
if (callbacks.onConnectSuccess) {
|
if (callbacks.onConnectSuccess) {
|
||||||
callbacks.onConnectSuccess(options.params, connectionResult.connectionProfile);
|
callbacks.onConnectSuccess(options.params, connectionResult.connectionProfile);
|
||||||
}
|
}
|
||||||
if (options.saveTheConnection) {
|
if (options.saveTheConnection || isEdit) {
|
||||||
await this.saveToSettings(uri, connection).then(value => {
|
let matcher: interfaces.ProfileMatcher;
|
||||||
|
if (isEdit) {
|
||||||
|
matcher = (a: interfaces.IConnectionProfile, b: interfaces.IConnectionProfile) => {
|
||||||
|
return a.id === b.id;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.saveToSettings(uri, connection, matcher).then(value => {
|
||||||
this._onAddConnectionProfile.fire(connection);
|
this._onAddConnectionProfile.fire(connection);
|
||||||
this.doActionsAfterConnectionComplete(value, options);
|
this.doActionsAfterConnectionComplete(value, options);
|
||||||
});
|
});
|
||||||
@@ -496,6 +526,7 @@ export class ConnectionManagementService extends Disposable implements IConnecti
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private handleConnectionError(connection: interfaces.IConnectionProfile, uri: string, options: IConnectionCompletionOptions, callbacks: IConnectionCallbacks, connectionResult: IConnectionResult) {
|
private handleConnectionError(connection: interfaces.IConnectionProfile, uri: string, options: IConnectionCompletionOptions, callbacks: IConnectionCallbacks, connectionResult: IConnectionResult) {
|
||||||
let connectionNotAcceptedError = nls.localize('connectionNotAcceptedError', "Connection Not Accepted");
|
let connectionNotAcceptedError = nls.localize('connectionNotAcceptedError', "Connection Not Accepted");
|
||||||
if (options.showFirewallRuleOnError && connectionResult.errorCode) {
|
if (options.showFirewallRuleOnError && connectionResult.errorCode) {
|
||||||
@@ -529,7 +560,7 @@ export class ConnectionManagementService extends Disposable implements IConnecti
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private doActionsAfterConnectionComplete(uri: string, options: IConnectionCompletionOptions,) {
|
private doActionsAfterConnectionComplete(uri: string, options: IConnectionCompletionOptions): void {
|
||||||
let connectionManagementInfo = this._connectionStatusManager.findConnection(uri);
|
let connectionManagementInfo = this._connectionStatusManager.findConnection(uri);
|
||||||
if (options.showDashboard) {
|
if (options.showDashboard) {
|
||||||
this.showDashboardForConnectionManagementInfo(connectionManagementInfo.connectionProfile);
|
this.showDashboardForConnectionManagementInfo(connectionManagementInfo.connectionProfile);
|
||||||
@@ -873,11 +904,10 @@ export class ConnectionManagementService extends Disposable implements IConnecti
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private saveToSettings(id: string, connection: interfaces.IConnectionProfile): Promise<string> {
|
|
||||||
return this._connectionStore.saveProfile(connection).then(savedProfile => {
|
private async saveToSettings(id: string, connection: interfaces.IConnectionProfile, matcher?: interfaces.ProfileMatcher): Promise<string> {
|
||||||
let newId = this._connectionStatusManager.updateConnectionProfile(savedProfile, id);
|
const savedProfile = await this._connectionStore.saveProfile(connection, undefined, matcher);
|
||||||
return newId;
|
return this._connectionStatusManager.updateConnectionProfile(savedProfile, id);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ suite('SQL ConnectionManagementService tests', () => {
|
|||||||
connectionDialogService.setup(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(none));
|
connectionDialogService.setup(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(none));
|
||||||
|
|
||||||
connectionStore.setup(x => x.addRecentConnection(TypeMoq.It.isAny())).returns(() => Promise.resolve());
|
connectionStore.setup(x => x.addRecentConnection(TypeMoq.It.isAny())).returns(() => Promise.resolve());
|
||||||
connectionStore.setup(x => x.saveProfile(TypeMoq.It.isAny())).returns(() => Promise.resolve(connectionProfile));
|
connectionStore.setup(x => x.saveProfile(TypeMoq.It.isAny(), TypeMoq.It.is(x => true), TypeMoq.It.is(x => true))).returns(() => Promise.resolve(connectionProfile));
|
||||||
workbenchEditorService.setup(x => x.openEditor(undefined, TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(undefined));
|
workbenchEditorService.setup(x => x.openEditor(undefined, TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(undefined));
|
||||||
connectionStore.setup(x => x.addSavedPassword(TypeMoq.It.is<IConnectionProfile>(
|
connectionStore.setup(x => x.addSavedPassword(TypeMoq.It.is<IConnectionProfile>(
|
||||||
c => c.serverName === connectionProfile.serverName))).returns(() => Promise.resolve({ profile: connectionProfile, savedCred: true }));
|
c => c.serverName === connectionProfile.serverName))).returns(() => Promise.resolve({ profile: connectionProfile, savedCred: true }));
|
||||||
@@ -204,7 +204,7 @@ suite('SQL ConnectionManagementService tests', () => {
|
|||||||
|
|
||||||
if (options) {
|
if (options) {
|
||||||
if (options.saveTheConnection) {
|
if (options.saveTheConnection) {
|
||||||
connectionStore.verify(x => x.saveProfile(TypeMoq.It.isAny()), TypeMoq.Times.once());
|
connectionStore.verify(x => x.saveProfile(TypeMoq.It.isAny(), TypeMoq.It.is(x => true), TypeMoq.It.is(x => true)), TypeMoq.Times.once());
|
||||||
}
|
}
|
||||||
if (options.showDashboard) {
|
if (options.showDashboard) {
|
||||||
workbenchEditorService.verify(x => x.openEditor(undefined, TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
|
workbenchEditorService.verify(x => x.openEditor(undefined, TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
|
||||||
@@ -358,7 +358,7 @@ suite('SQL ConnectionManagementService tests', () => {
|
|||||||
showFirewallRuleOnError: true
|
showFirewallRuleOnError: true
|
||||||
};
|
};
|
||||||
|
|
||||||
return connect(uri, options).then(() => {
|
return connect(uri, options).then((result) => {
|
||||||
verifyOptions(options);
|
verifyOptions(options);
|
||||||
assert.notEqual(paramsInOnConnectSuccess, undefined);
|
assert.notEqual(paramsInOnConnectSuccess, undefined);
|
||||||
assert.equal(paramsInOnConnectSuccess.connectionType, options.params.connectionType);
|
assert.equal(paramsInOnConnectSuccess.connectionType, options.params.connectionType);
|
||||||
@@ -441,6 +441,82 @@ suite('SQL ConnectionManagementService tests', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Edit Connection - Changing connection profile name for same URI should persist after edit', () => {
|
||||||
|
let profile = connectionProfile;
|
||||||
|
let uri1 = 'test_uri1';
|
||||||
|
let newname = 'connection renamed';
|
||||||
|
let options: IConnectionCompletionOptions = {
|
||||||
|
params: {
|
||||||
|
connectionType: ConnectionType.editor,
|
||||||
|
input: {
|
||||||
|
onConnectSuccess: undefined,
|
||||||
|
onConnectReject: undefined,
|
||||||
|
onConnectStart: undefined,
|
||||||
|
onDisconnect: undefined,
|
||||||
|
onConnectCanceled: undefined,
|
||||||
|
uri: uri1,
|
||||||
|
},
|
||||||
|
querySelection: undefined,
|
||||||
|
runQueryOnCompletion: RunQueryOnConnectionMode.none,
|
||||||
|
isEditConnection: false
|
||||||
|
},
|
||||||
|
saveTheConnection: true,
|
||||||
|
showDashboard: false,
|
||||||
|
showConnectionDialogOnError: true,
|
||||||
|
showFirewallRuleOnError: true
|
||||||
|
};
|
||||||
|
|
||||||
|
return connect(uri1, options, true, profile).then(result => {
|
||||||
|
assert.equal(result.connected, true);
|
||||||
|
let newProfile = connectionProfile;
|
||||||
|
newProfile.connectionName = newname;
|
||||||
|
options.params.isEditConnection = true;
|
||||||
|
return connect(uri1, options, true, profile).then(result => {
|
||||||
|
assert.equal(result.connected, true);
|
||||||
|
assert.equal(connectionManagementService.getConnectionProfile(uri1).connectionName, newname);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Edit Connection - Connecting a different URI with same profile via edit should not change profile ID.', () => {
|
||||||
|
let profile = connectionProfile;
|
||||||
|
profile.id = '0451';
|
||||||
|
let uri1 = 'test_uri1';
|
||||||
|
let uri2 = 'test_uri2';
|
||||||
|
let options: IConnectionCompletionOptions = {
|
||||||
|
params: {
|
||||||
|
connectionType: ConnectionType.editor,
|
||||||
|
input: {
|
||||||
|
onConnectSuccess: undefined,
|
||||||
|
onConnectReject: undefined,
|
||||||
|
onConnectStart: undefined,
|
||||||
|
onDisconnect: undefined,
|
||||||
|
onConnectCanceled: undefined,
|
||||||
|
uri: uri1
|
||||||
|
},
|
||||||
|
querySelection: undefined,
|
||||||
|
runQueryOnCompletion: RunQueryOnConnectionMode.none,
|
||||||
|
isEditConnection: false
|
||||||
|
},
|
||||||
|
saveTheConnection: true,
|
||||||
|
showDashboard: false,
|
||||||
|
showConnectionDialogOnError: true,
|
||||||
|
showFirewallRuleOnError: true
|
||||||
|
};
|
||||||
|
|
||||||
|
return connect(uri1, options, true, profile).then(result => {
|
||||||
|
assert.equal(result.connected, true);
|
||||||
|
options.params.isEditConnection = true;
|
||||||
|
return connect(uri2, options, true, profile).then(result => {
|
||||||
|
assert.equal(result.connected, true);
|
||||||
|
let uri1info = connectionManagementService.getConnectionInfo(uri1);
|
||||||
|
let uri2info = connectionManagementService.getConnectionInfo(uri2);
|
||||||
|
assert.equal(uri1info.connectionProfile.id, uri2info.connectionProfile.id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
test('failed firewall rule should open the firewall rule dialog', () => {
|
test('failed firewall rule should open the firewall rule dialog', () => {
|
||||||
handleFirewallRuleResult.canHandleFirewallRule = true;
|
handleFirewallRuleResult.canHandleFirewallRule = true;
|
||||||
resolveHandleFirewallRuleDialog = true;
|
resolveHandleFirewallRuleDialog = true;
|
||||||
|
|||||||
@@ -83,6 +83,30 @@ export class RefreshAction extends Action {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class EditConnectionAction extends Action {
|
||||||
|
public static ID = 'registeredServers.editConnection';
|
||||||
|
public static LABEL = localize('connectionTree.editConnection', "Edit Connection");
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
id: string,
|
||||||
|
label: string,
|
||||||
|
private _connectionProfile: ConnectionProfile,
|
||||||
|
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService
|
||||||
|
) {
|
||||||
|
super(id, label);
|
||||||
|
this.class = 'edit-server-action';
|
||||||
|
}
|
||||||
|
|
||||||
|
public async run(): Promise<boolean> {
|
||||||
|
if (!this._connectionProfile) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this._connectionManagementService.showEditConnectionDialog(this._connectionProfile);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class DisconnectConnectionAction extends Action {
|
export class DisconnectConnectionAction extends Action {
|
||||||
public static ID = 'objectExplorer.disconnect';
|
public static ID = 'objectExplorer.disconnect';
|
||||||
public static LABEL = localize('DisconnectAction', "Disconnect");
|
public static LABEL = localize('DisconnectAction', "Disconnect");
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
|
|||||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
DisconnectConnectionAction, AddServerAction,
|
DisconnectConnectionAction, AddServerAction, EditConnectionAction,
|
||||||
DeleteConnectionAction, RefreshAction, EditServerGroupAction
|
DeleteConnectionAction, RefreshAction, EditServerGroupAction
|
||||||
} from 'sql/workbench/services/objectExplorer/browser/connectionTreeAction';
|
} from 'sql/workbench/services/objectExplorer/browser/connectionTreeAction';
|
||||||
import { TreeNode } from 'sql/workbench/services/objectExplorer/common/treeNode';
|
import { TreeNode } from 'sql/workbench/services/objectExplorer/common/treeNode';
|
||||||
@@ -129,6 +129,7 @@ export class ServerTreeActionProvider {
|
|||||||
if (this._connectionManagementService.isProfileConnected(context.profile)) {
|
if (this._connectionManagementService.isProfileConnected(context.profile)) {
|
||||||
actions.push(this._instantiationService.createInstance(DisconnectConnectionAction, DisconnectConnectionAction.ID, DisconnectConnectionAction.LABEL, context.profile));
|
actions.push(this._instantiationService.createInstance(DisconnectConnectionAction, DisconnectConnectionAction.ID, DisconnectConnectionAction.LABEL, context.profile));
|
||||||
}
|
}
|
||||||
|
actions.push(this._instantiationService.createInstance(EditConnectionAction, EditConnectionAction.ID, EditConnectionAction.LABEL, context.profile));
|
||||||
actions.push(this._instantiationService.createInstance(DeleteConnectionAction, DeleteConnectionAction.ID, DeleteConnectionAction.DELETE_CONNECTION_LABEL, context.profile));
|
actions.push(this._instantiationService.createInstance(DeleteConnectionAction, DeleteConnectionAction.ID, DeleteConnectionAction.DELETE_CONNECTION_LABEL, context.profile));
|
||||||
|
|
||||||
// Contribute refresh action for scriptable objects via contribution
|
// Contribute refresh action for scriptable objects via contribution
|
||||||
|
|||||||
Reference in New Issue
Block a user