mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-29 17:23:25 -05:00
Connection URI with complete options (finalized) (#22735)
* Connection URI made to include every option available instead of basic details (#22045) * Revert "Merge remote-tracking branch 'origin' into feat/connectionUri" This reverts commit 11b2d31bf99e216daee823f732254f69a017fee1, reversing changes made to 36e4db8c0744f81565efdfd2f56a3ae3c0026896. * Revert "Revert "Merge remote-tracking branch 'origin' into feat/connectionUri"" This reverts commit f439673c2693e1144c52e04c14e82cd8566c13a6. * Added changes and fixes for feat connectionuri (#22706) * add title generation at start * added await to refreshConnectionTreeTitles
This commit is contained in:
@@ -540,6 +540,22 @@ export class ConnectionManagementService extends Disposable implements IConnecti
|
||||
|
||||
let isEdit = options?.params?.isEditConnection ?? false;
|
||||
|
||||
let matcher: interfaces.ProfileMatcher;
|
||||
if (isEdit) {
|
||||
matcher = (a: interfaces.IConnectionProfile, b: interfaces.IConnectionProfile) => a.id === options.params.oldProfileId;
|
||||
|
||||
//Check to make sure the edits are not identical to another connection.
|
||||
await this._connectionStore.isDuplicateEdit(connection, matcher).then(result => {
|
||||
if (result) {
|
||||
// Must get connection group name here as it may not always be initialized and causes problems when deleting when included with options.
|
||||
this._logService.error(`Profile edit for '${connection.id}' exactly matches an existing profile with data: '${ConnectionProfile.getDisplayOptionsKey(connection.getOptionsKey())}'`);
|
||||
throw new Error(`Cannot save profile, the selected connection options are identical to an existing profile with details: \n
|
||||
${ConnectionProfile.getDisplayOptionsKey(connection.getOptionsKey())}${(connection.groupFullName !== undefined && connection.groupFullName !== '' && connection.groupFullName !== '/') ?
|
||||
ConnectionProfile.displayIdSeparator + 'groupName' + ConnectionProfile.displayNameValueSeparator + connection.groupFullName : ''}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (!uri) {
|
||||
uri = Utils.generateUri(connection);
|
||||
}
|
||||
@@ -588,11 +604,6 @@ export class ConnectionManagementService extends Disposable implements IConnecti
|
||||
callbacks.onConnectSuccess(options.params, connectionResult.connectionProfile);
|
||||
}
|
||||
if (options.saveTheConnection || isEdit) {
|
||||
let matcher: interfaces.ProfileMatcher;
|
||||
if (isEdit) {
|
||||
matcher = (a: interfaces.IConnectionProfile, b: interfaces.IConnectionProfile) => a.id === options.params.oldProfileId;
|
||||
}
|
||||
|
||||
await this.saveToSettings(uri, connection, matcher).then(value => {
|
||||
this._onAddConnectionProfile.fire(connection);
|
||||
if (isEdit) {
|
||||
@@ -700,6 +711,20 @@ export class ConnectionManagementService extends Disposable implements IConnecti
|
||||
return result;
|
||||
}
|
||||
|
||||
public getEditorConnectionProfileTitle(profile: interfaces.IConnectionProfile, getNonDefaultsOnly?: boolean): string {
|
||||
let result = '';
|
||||
if (profile) {
|
||||
let tempProfile = new ConnectionProfile(this._capabilitiesService, profile);
|
||||
if (!getNonDefaultsOnly) {
|
||||
result = tempProfile.getEditorFullTitleWithOptions();
|
||||
}
|
||||
else {
|
||||
result = tempProfile.getNonDefaultOptionsString();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private doActionsAfterConnectionComplete(uri: string, options: IConnectionCompletionOptions): void {
|
||||
let connectionManagementInfo = this._connectionStatusManager.findConnection(uri);
|
||||
if (!connectionManagementInfo) {
|
||||
@@ -1265,7 +1290,7 @@ export class ConnectionManagementService extends Disposable implements IConnecti
|
||||
this._connectionGlobalStatus.setStatusToConnected(info.connectionSummary);
|
||||
}
|
||||
|
||||
const connectionUniqueId = connection.connectionProfile.getConnectionInfoId();
|
||||
const connectionUniqueId = connection.connectionProfile.getOptionsKey();
|
||||
if (info.isSupportedVersion === false
|
||||
&& this._connectionsGotUnsupportedVersionWarning.indexOf(connectionUniqueId) === -1
|
||||
&& this._configurationService.getValue<boolean>('connection.showUnsupportedServerVersionWarning')) {
|
||||
|
||||
@@ -16,7 +16,7 @@ import * as Constants from 'sql/platform/connection/common/constants';
|
||||
import * as Utils from 'sql/platform/connection/common/utils';
|
||||
import { IHandleFirewallRuleResult } from 'sql/workbench/services/resourceProvider/common/resourceProviderService';
|
||||
|
||||
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
|
||||
import { IConnectionProfile, ServiceOptionType } from 'sql/platform/connection/common/interfaces';
|
||||
import { TestCapabilitiesService } from 'sql/platform/capabilities/test/common/testCapabilitiesService';
|
||||
import { TestConnectionProvider } from 'sql/platform/connection/test/common/testConnectionProvider';
|
||||
import { TestResourceProvider } from 'sql/workbench/services/resourceProvider/test/common/testResourceProviderService';
|
||||
@@ -514,7 +514,7 @@ suite('SQL ConnectionManagementService tests', () => {
|
||||
assert.ok(called, 'expected changeGroupIdForConnectionGroup to be called on ConnectionStore');
|
||||
});
|
||||
|
||||
test('findExistingConnection should find connection for connectionProfile with same info', async () => {
|
||||
test('findExistingConnection should find connection for connectionProfile with same basic info', async () => {
|
||||
let profile = <ConnectionProfile>Object.assign({}, connectionProfile);
|
||||
let uri1 = 'connection:connectionId';
|
||||
let options: IConnectionCompletionOptions = {
|
||||
@@ -1013,7 +1013,15 @@ suite('SQL ConnectionManagementService tests', () => {
|
||||
showFirewallRuleOnError: true
|
||||
};
|
||||
|
||||
let originalProfileKey = '';
|
||||
connectionStore.setup(x => x.isDuplicateEdit(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns((inputProfile, matcher) => {
|
||||
let newProfile = ConnectionProfile.fromIConnectionProfile(new TestCapabilitiesService(), inputProfile);
|
||||
let result = newProfile.getOptionsKey() === originalProfileKey;
|
||||
return Promise.resolve(result);
|
||||
});
|
||||
await connect(uri1, options, true, profile);
|
||||
let originalProfile = ConnectionProfile.fromIConnectionProfile(new TestCapabilitiesService(), connectionProfile);
|
||||
originalProfileKey = originalProfile.getOptionsKey();
|
||||
let newProfile = Object.assign({}, connectionProfile);
|
||||
newProfile.connectionName = newname;
|
||||
options.params.isEditConnection = true;
|
||||
@@ -1047,6 +1055,8 @@ suite('SQL ConnectionManagementService tests', () => {
|
||||
showFirewallRuleOnError: true
|
||||
};
|
||||
|
||||
// In an actual edit situation, the profile options would be different for different URIs, as a placeholder, we check the test uris instead here.
|
||||
connectionStore.setup(x => x.isDuplicateEdit(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(uri1 === uri2));
|
||||
await connect(uri1, options, true, profile);
|
||||
options.params.isEditConnection = true;
|
||||
await connect(uri2, options, true, profile);
|
||||
@@ -1055,6 +1065,51 @@ suite('SQL ConnectionManagementService tests', () => {
|
||||
assert.strictEqual(uri1info.connectionProfile.id, uri2info.connectionProfile.id);
|
||||
});
|
||||
|
||||
test('Edit Connection - Connecting with an already connected profile via edit should throw an error', async () => {
|
||||
let uri1 = 'test_uri1';
|
||||
let profile = Object.assign({}, connectionProfile);
|
||||
profile.id = '0451';
|
||||
let options: IConnectionCompletionOptions = {
|
||||
params: {
|
||||
connectionType: ConnectionType.editor,
|
||||
input: {
|
||||
onConnectSuccess: undefined,
|
||||
onConnectReject: undefined,
|
||||
onConnectStart: undefined,
|
||||
onDisconnect: undefined,
|
||||
onConnectCanceled: undefined,
|
||||
uri: uri1
|
||||
},
|
||||
queryRange: undefined,
|
||||
runQueryOnCompletion: RunQueryOnConnectionMode.none,
|
||||
isEditConnection: true
|
||||
},
|
||||
saveTheConnection: true,
|
||||
showDashboard: false,
|
||||
showConnectionDialogOnError: true,
|
||||
showFirewallRuleOnError: true
|
||||
};
|
||||
|
||||
let originalProfileKey = '';
|
||||
connectionStore.setup(x => x.isDuplicateEdit(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns((inputProfile, matcher) => {
|
||||
let newProfile = ConnectionProfile.fromIConnectionProfile(new TestCapabilitiesService(), inputProfile);
|
||||
let result = newProfile.getOptionsKey() === originalProfileKey;
|
||||
return Promise.resolve(result)
|
||||
});
|
||||
|
||||
await connect(uri1, options, true, profile);
|
||||
let originalProfile = ConnectionProfile.fromIConnectionProfile(new TestCapabilitiesService(), connectionProfile);
|
||||
originalProfileKey = originalProfile.getOptionsKey();
|
||||
let newProfile = Object.assign({}, connectionProfile);
|
||||
options.params.isEditConnection = true;
|
||||
try {
|
||||
await connect(uri1, options, true, newProfile);
|
||||
assert.fail;
|
||||
}
|
||||
catch {
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
test('failed firewall rule should open the firewall rule dialog', async () => {
|
||||
handleFirewallRuleResult.canHandleFirewallRule = true;
|
||||
@@ -1992,6 +2047,117 @@ test('clearRecentConnection and ConnectionsList should call connectionStore func
|
||||
assert(called);
|
||||
});
|
||||
|
||||
test('getEditorConnectionProfileTitle should return a correctly formatted title for a connection profile', () => {
|
||||
let profile: IConnectionProfile = {
|
||||
connectionName: 'new name',
|
||||
serverName: 'new server',
|
||||
databaseName: 'database',
|
||||
userName: 'user',
|
||||
password: 'password',
|
||||
authenticationType: Constants.AuthenticationType.Integrated,
|
||||
savePassword: true,
|
||||
groupFullName: 'g2/g2-2',
|
||||
groupId: 'group id',
|
||||
getOptionsKey: () => { return ''; },
|
||||
matches: undefined,
|
||||
providerName: 'MSSQL',
|
||||
options: {},
|
||||
saveProfile: true,
|
||||
id: undefined
|
||||
};
|
||||
|
||||
let capabilitiesService = new TestCapabilitiesService();
|
||||
const testOption1 = {
|
||||
name: 'testOption1',
|
||||
displayName: 'testOption1',
|
||||
description: 'test description',
|
||||
groupName: 'test group name',
|
||||
valueType: ServiceOptionType.string,
|
||||
specialValueType: undefined,
|
||||
defaultValue: '',
|
||||
categoryValues: undefined,
|
||||
isIdentity: false,
|
||||
isRequired: false
|
||||
};
|
||||
|
||||
const testOption2 = {
|
||||
name: 'testOption2',
|
||||
displayName: 'testOption2',
|
||||
description: 'test description',
|
||||
groupName: 'test group name',
|
||||
valueType: ServiceOptionType.number,
|
||||
specialValueType: undefined,
|
||||
defaultValue: '10',
|
||||
categoryValues: undefined,
|
||||
isIdentity: false,
|
||||
isRequired: false
|
||||
};
|
||||
|
||||
const testOption3 = {
|
||||
name: 'testOption3',
|
||||
displayName: 'testOption3',
|
||||
description: 'test description',
|
||||
groupName: 'test group name',
|
||||
valueType: ServiceOptionType.string,
|
||||
specialValueType: undefined,
|
||||
defaultValue: 'default',
|
||||
categoryValues: undefined,
|
||||
isIdentity: false,
|
||||
isRequired: false
|
||||
};
|
||||
|
||||
profile.options['testOption1'] = 'test value';
|
||||
profile.options['testOption2'] = '50';
|
||||
profile.options['testOption3'] = 'default';
|
||||
|
||||
let mainProvider = capabilitiesService.capabilities['MSSQL'];
|
||||
let mainProperties = mainProvider.connection;
|
||||
let mainOptions = mainProperties.connectionOptions;
|
||||
|
||||
mainOptions.push(testOption1);
|
||||
mainOptions.push(testOption2);
|
||||
mainOptions.push(testOption3);
|
||||
|
||||
mainProperties.connectionOptions = mainOptions;
|
||||
mainProvider.connection = mainProperties;
|
||||
|
||||
capabilitiesService.capabilities['MSSQL'] = mainProvider;
|
||||
|
||||
const connectionStoreMock = TypeMoq.Mock.ofType(ConnectionStore, TypeMoq.MockBehavior.Loose, new TestStorageService());
|
||||
const testInstantiationService = new TestInstantiationService();
|
||||
testInstantiationService.stub(IStorageService, new TestStorageService());
|
||||
sinon.stub(testInstantiationService, 'createInstance').withArgs(ConnectionStore).returns(connectionStoreMock.object);
|
||||
const connectionManagementService = new ConnectionManagementService(undefined, testInstantiationService, undefined, undefined, undefined, capabilitiesService, undefined, undefined, undefined, new TestErrorDiagnosticsService(), undefined, undefined, undefined, undefined, getBasicExtensionService(), undefined, undefined, undefined);
|
||||
|
||||
// We should expect that non default options are returned when we try to get the nonDefaultOptions string.
|
||||
let result = connectionManagementService.getEditorConnectionProfileTitle(profile, true);
|
||||
let expectedNonDefaultOption = ' (testOption1=test value; testOption2=50)';
|
||||
assert.strictEqual(result, expectedNonDefaultOption, `Profile non default options contained incorrect options`);
|
||||
|
||||
// We should expect that the string contains the connection name and the server info (with all non default options appended).
|
||||
let generatedProfile = ConnectionProfile.fromIConnectionProfile(capabilitiesService, profile);
|
||||
let profileServerInfo = generatedProfile.serverInfo;
|
||||
result = connectionManagementService.getEditorConnectionProfileTitle(profile);
|
||||
|
||||
assert.strictEqual(result, `${profile.connectionName}: ${profileServerInfo}`, `getEditorConnectionProfileTitle does not return the correct string for ${profile.connectionName}`);
|
||||
|
||||
// We should expect that the string contains only the server info (with non default options) if there is no connection name.
|
||||
profile.connectionName = undefined;
|
||||
|
||||
result = connectionManagementService.getEditorConnectionProfileTitle(profile);
|
||||
|
||||
assert.strictEqual(result, `${profileServerInfo}`, `getEditorConnectionProfileTitle included a connection name when it shouldn't`);
|
||||
|
||||
// We should expect that the string only contains the server info without any non default options if no such options exist.
|
||||
profile.options['testOption1'] = undefined;
|
||||
profile.options['testOption2'] = undefined;
|
||||
profile.options['testOption3'] = undefined;
|
||||
|
||||
result = connectionManagementService.getEditorConnectionProfileTitle(profile);
|
||||
|
||||
assert.notEqual(result, `${profileServerInfo}`, `getEditorConnectionProfileTitle included non default connection options when it shouldn't`);
|
||||
});
|
||||
|
||||
export function createConnectionProfile(id: string, password?: string): ConnectionProfile {
|
||||
const capabilitiesService = new TestCapabilitiesService();
|
||||
return new ConnectionProfile(capabilitiesService, {
|
||||
|
||||
Reference in New Issue
Block a user