mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-25 01:25:36 -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:
@@ -181,6 +181,30 @@ suite('ConnectionConfig', () => {
|
||||
isRequired: true,
|
||||
specialValueType: ConnectionOptionSpecialType.password,
|
||||
valueType: ServiceOptionType.string
|
||||
},
|
||||
{
|
||||
name: 'testProperty1',
|
||||
displayName: undefined!,
|
||||
description: undefined!,
|
||||
groupName: undefined!,
|
||||
categoryValues: undefined!,
|
||||
defaultValue: "default",
|
||||
isIdentity: true,
|
||||
isRequired: true,
|
||||
specialValueType: undefined!,
|
||||
valueType: ServiceOptionType.string
|
||||
},
|
||||
{
|
||||
name: 'testProperty2',
|
||||
displayName: undefined!,
|
||||
description: undefined!,
|
||||
groupName: undefined!,
|
||||
categoryValues: undefined!,
|
||||
defaultValue: "10",
|
||||
isIdentity: true,
|
||||
isRequired: true,
|
||||
specialValueType: undefined!,
|
||||
valueType: ServiceOptionType.number
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -677,6 +701,183 @@ suite('ConnectionConfig', () => {
|
||||
}
|
||||
});
|
||||
|
||||
test('change group for connection should accept similar connection with different options', async () => {
|
||||
let changingProfile: IConnectionProfile = {
|
||||
serverName: 'server3',
|
||||
databaseName: 'database',
|
||||
userName: 'user',
|
||||
password: 'password',
|
||||
authenticationType: '',
|
||||
savePassword: true,
|
||||
groupFullName: 'g3',
|
||||
groupId: 'g3',
|
||||
getOptionsKey: () => { return 'connectionId'; },
|
||||
matches: undefined!,
|
||||
providerName: 'MSSQL',
|
||||
options: {
|
||||
'testProperty1': 'nonDefault',
|
||||
'testProperty2': '10',
|
||||
},
|
||||
saveProfile: true,
|
||||
id: 'server3-2',
|
||||
connectionName: undefined!
|
||||
};
|
||||
let existingProfile = ConnectionProfile.convertToProfileStore(capabilitiesService.object, {
|
||||
serverName: 'server3',
|
||||
databaseName: 'database',
|
||||
userName: 'user',
|
||||
password: 'password',
|
||||
authenticationType: '',
|
||||
savePassword: true,
|
||||
groupFullName: 'test',
|
||||
groupId: 'test',
|
||||
getOptionsKey: () => { return 'connectionId'; },
|
||||
matches: undefined!,
|
||||
providerName: 'MSSQL',
|
||||
options: { 'testProperty2': '15' },
|
||||
saveProfile: true,
|
||||
id: 'server3',
|
||||
connectionName: undefined!
|
||||
});
|
||||
|
||||
let _testConnections = [...deepClone(testConnections), existingProfile, changingProfile];
|
||||
|
||||
let configurationService = new TestConfigurationService();
|
||||
configurationService.updateValue('datasource.connections', _testConnections, ConfigurationTarget.USER);
|
||||
|
||||
let connectionProfile = new ConnectionProfile(capabilitiesService.object, changingProfile);
|
||||
|
||||
let config = new ConnectionConfig(configurationService, capabilitiesService.object);
|
||||
|
||||
await config.changeGroupIdForConnection(connectionProfile, 'test');
|
||||
|
||||
let editedConnections = configurationService.inspect<IConnectionProfileStore[]>('datasource.connections').userValue!;
|
||||
|
||||
assert.strictEqual(editedConnections.length, _testConnections.length);
|
||||
let editedConnection = editedConnections.find(con => con.id === 'server3-2');
|
||||
assert.ok(editedConnection);
|
||||
assert.strictEqual(editedConnection!.groupId, 'test');
|
||||
});
|
||||
|
||||
test('change group for connection should not accept similar connection with default options same as another', async () => {
|
||||
let changingProfile: IConnectionProfile = {
|
||||
serverName: 'server3',
|
||||
databaseName: 'database',
|
||||
userName: 'user',
|
||||
password: 'password',
|
||||
authenticationType: '',
|
||||
savePassword: true,
|
||||
groupFullName: 'g3',
|
||||
groupId: 'g3',
|
||||
getOptionsKey: () => { return 'connectionId'; },
|
||||
matches: undefined!,
|
||||
providerName: 'MSSQL',
|
||||
options: {
|
||||
'testProperty1': 'nonDefault',
|
||||
'testProperty2': '10',
|
||||
},
|
||||
saveProfile: true,
|
||||
id: 'server3-2',
|
||||
connectionName: undefined!
|
||||
};
|
||||
let existingProfile = ConnectionProfile.convertToProfileStore(capabilitiesService.object, {
|
||||
serverName: 'server3',
|
||||
databaseName: 'database',
|
||||
userName: 'user',
|
||||
password: 'password',
|
||||
authenticationType: '',
|
||||
savePassword: true,
|
||||
groupFullName: 'test',
|
||||
groupId: 'test',
|
||||
getOptionsKey: () => { return 'connectionId'; },
|
||||
matches: undefined!,
|
||||
providerName: 'MSSQL',
|
||||
options: { 'testProperty1': 'nonDefault' },
|
||||
saveProfile: true,
|
||||
id: 'server3',
|
||||
connectionName: undefined!
|
||||
});
|
||||
|
||||
let _testConnections = [...deepClone(testConnections), existingProfile, changingProfile];
|
||||
|
||||
let configurationService = new TestConfigurationService();
|
||||
configurationService.updateValue('datasource.connections', _testConnections, ConfigurationTarget.USER);
|
||||
|
||||
let connectionProfile = new ConnectionProfile(capabilitiesService.object, changingProfile);
|
||||
|
||||
let config = new ConnectionConfig(configurationService, capabilitiesService.object);
|
||||
|
||||
try {
|
||||
await config.changeGroupIdForConnection(connectionProfile, 'test');
|
||||
assert.fail();
|
||||
} catch (e) {
|
||||
let editedConnections = configurationService.inspect<IConnectionProfileStore[]>('datasource.connections').userValue!;
|
||||
// two
|
||||
assert.strictEqual(editedConnections.length, _testConnections.length);
|
||||
let editedConnection = editedConnections.find(con => con.id === 'server3-2');
|
||||
assert.ok(!!editedConnection);
|
||||
assert.strictEqual(editedConnection!.groupId, 'g3');
|
||||
}
|
||||
});
|
||||
|
||||
test('change group for connection should accept similar connection with a distinguishing option', async () => {
|
||||
let changingProfile: IConnectionProfile = {
|
||||
serverName: 'server3',
|
||||
databaseName: 'database',
|
||||
userName: 'user',
|
||||
password: 'password',
|
||||
authenticationType: '',
|
||||
savePassword: true,
|
||||
groupFullName: 'g3',
|
||||
groupId: 'g3',
|
||||
getOptionsKey: () => { return 'connectionId'; },
|
||||
matches: undefined!,
|
||||
providerName: 'MSSQL',
|
||||
options: {
|
||||
'testProperty1': 'nonDefault',
|
||||
'testProperty2': '15',
|
||||
},
|
||||
saveProfile: true,
|
||||
id: 'server3-2',
|
||||
connectionName: undefined!
|
||||
};
|
||||
let existingProfile = ConnectionProfile.convertToProfileStore(capabilitiesService.object, {
|
||||
serverName: 'server3',
|
||||
databaseName: 'database',
|
||||
userName: 'user',
|
||||
password: 'password',
|
||||
authenticationType: '',
|
||||
savePassword: true,
|
||||
groupFullName: 'test',
|
||||
groupId: 'test',
|
||||
getOptionsKey: () => { return 'connectionId'; },
|
||||
matches: undefined!,
|
||||
providerName: 'MSSQL',
|
||||
options: { 'testProperty2': '15' },
|
||||
saveProfile: true,
|
||||
id: 'server3',
|
||||
connectionName: undefined!
|
||||
});
|
||||
|
||||
let _testConnections = [...deepClone(testConnections), existingProfile, changingProfile];
|
||||
|
||||
let configurationService = new TestConfigurationService();
|
||||
configurationService.updateValue('datasource.connections', _testConnections, ConfigurationTarget.USER);
|
||||
|
||||
let connectionProfile = new ConnectionProfile(capabilitiesService.object, changingProfile);
|
||||
|
||||
let config = new ConnectionConfig(configurationService, capabilitiesService.object);
|
||||
|
||||
await config.changeGroupIdForConnection(connectionProfile, 'test');
|
||||
|
||||
let editedConnections = configurationService.inspect<IConnectionProfileStore[]>('datasource.connections').userValue!;
|
||||
|
||||
assert.strictEqual(editedConnections.length, _testConnections.length);
|
||||
let editedConnection = editedConnections.find(con => con.id === 'server3-2');
|
||||
assert.ok(editedConnection);
|
||||
assert.strictEqual(editedConnection!.groupId, 'test');
|
||||
});
|
||||
|
||||
test('change group(parent) for connection', async () => {
|
||||
let newProfile: IConnectionProfile = {
|
||||
serverName: 'server3',
|
||||
@@ -778,4 +979,140 @@ suite('ConnectionConfig', () => {
|
||||
assert.strictEqual(editGroups.length, testGroups.length);
|
||||
}
|
||||
});
|
||||
|
||||
test('isDuplicateEdit should return true if an edit profile matches an existing profile', async () => {
|
||||
let originalProfile: IConnectionProfile = {
|
||||
serverName: 'server3',
|
||||
databaseName: 'database',
|
||||
userName: 'user',
|
||||
password: 'password',
|
||||
authenticationType: '',
|
||||
savePassword: true,
|
||||
groupFullName: 'g3',
|
||||
groupId: 'g3',
|
||||
getOptionsKey: () => { return 'connectionId'; },
|
||||
matches: undefined!,
|
||||
providerName: 'MSSQL',
|
||||
options: {},
|
||||
saveProfile: true,
|
||||
id: 'server3-2',
|
||||
connectionName: undefined!
|
||||
};
|
||||
let changedProfile: IConnectionProfile = {
|
||||
serverName: 'server3',
|
||||
databaseName: 'database',
|
||||
userName: 'user',
|
||||
password: 'password',
|
||||
authenticationType: '',
|
||||
savePassword: true,
|
||||
groupFullName: 'test',
|
||||
groupId: 'test',
|
||||
getOptionsKey: () => { return 'connectionId'; },
|
||||
matches: undefined!,
|
||||
providerName: 'MSSQL',
|
||||
options: {},
|
||||
saveProfile: true,
|
||||
id: 'server3-2',
|
||||
connectionName: undefined!
|
||||
};
|
||||
let existingProfile = ConnectionProfile.convertToProfileStore(capabilitiesService.object, {
|
||||
serverName: 'server3',
|
||||
databaseName: 'database',
|
||||
userName: 'user',
|
||||
password: 'password',
|
||||
authenticationType: '',
|
||||
savePassword: true,
|
||||
groupFullName: 'test',
|
||||
groupId: 'test',
|
||||
getOptionsKey: () => { return 'connectionId'; },
|
||||
matches: undefined!,
|
||||
providerName: 'MSSQL',
|
||||
options: {},
|
||||
saveProfile: true,
|
||||
id: 'server3',
|
||||
connectionName: undefined!
|
||||
});
|
||||
|
||||
let _testConnections = [...deepClone(testConnections), existingProfile, originalProfile];
|
||||
|
||||
let configurationService = new TestConfigurationService();
|
||||
configurationService.updateValue('datasource.connections', _testConnections, ConfigurationTarget.USER);
|
||||
|
||||
let connectionProfile = new ConnectionProfile(capabilitiesService.object, changedProfile);
|
||||
|
||||
let config = new ConnectionConfig(configurationService, capabilitiesService.object);
|
||||
|
||||
let matcher = (a: IConnectionProfile, b: IConnectionProfile) => a.id === originalProfile.id;
|
||||
let result = await config.isDuplicateEdit(connectionProfile, matcher);
|
||||
|
||||
assert(result, 'Matcher did not find a match for identical edit');
|
||||
});
|
||||
|
||||
test('isDuplicateEdit should return false if an edit profile has different properties', async () => {
|
||||
let originalProfile: IConnectionProfile = {
|
||||
serverName: 'server3',
|
||||
databaseName: 'database',
|
||||
userName: 'user',
|
||||
password: 'password',
|
||||
authenticationType: '',
|
||||
savePassword: true,
|
||||
groupFullName: 'test',
|
||||
groupId: 'test',
|
||||
getOptionsKey: () => { return 'connectionId'; },
|
||||
matches: undefined!,
|
||||
providerName: 'MSSQL',
|
||||
options: {},
|
||||
saveProfile: true,
|
||||
id: 'server3-2',
|
||||
connectionName: undefined!
|
||||
};
|
||||
let changedProfile: IConnectionProfile = {
|
||||
serverName: 'server3',
|
||||
databaseName: 'database',
|
||||
userName: 'user',
|
||||
password: 'password',
|
||||
authenticationType: 'Integrated',
|
||||
savePassword: true,
|
||||
groupFullName: 'test',
|
||||
groupId: 'test',
|
||||
getOptionsKey: () => { return 'connectionId'; },
|
||||
matches: undefined!,
|
||||
providerName: 'MSSQL',
|
||||
options: {},
|
||||
saveProfile: true,
|
||||
id: 'server3-2',
|
||||
connectionName: undefined!
|
||||
};
|
||||
let existingProfile = ConnectionProfile.convertToProfileStore(capabilitiesService.object, {
|
||||
serverName: 'server3',
|
||||
databaseName: 'database',
|
||||
userName: 'user',
|
||||
password: 'password',
|
||||
authenticationType: '',
|
||||
savePassword: true,
|
||||
groupFullName: 'test',
|
||||
groupId: 'test',
|
||||
getOptionsKey: () => { return 'connectionId'; },
|
||||
matches: undefined!,
|
||||
providerName: 'MSSQL',
|
||||
options: {},
|
||||
saveProfile: true,
|
||||
id: 'server3',
|
||||
connectionName: undefined!
|
||||
});
|
||||
|
||||
let _testConnections = [...deepClone(testConnections), existingProfile, originalProfile];
|
||||
|
||||
let configurationService = new TestConfigurationService();
|
||||
configurationService.updateValue('datasource.connections', _testConnections, ConfigurationTarget.USER);
|
||||
|
||||
let connectionProfile = new ConnectionProfile(capabilitiesService.object, changedProfile);
|
||||
|
||||
let config = new ConnectionConfig(configurationService, capabilitiesService.object);
|
||||
|
||||
let matcher = (a: IConnectionProfile, b: IConnectionProfile) => a.id === originalProfile.id;
|
||||
let result = await config.isDuplicateEdit(connectionProfile, matcher);
|
||||
|
||||
assert(!result, 'Matcher matched the profile even when it had a different property');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -171,7 +171,8 @@ suite('SQL ConnectionProfileInfo tests', () => {
|
||||
msSQLCapabilities = {
|
||||
providerId: mssqlProviderName,
|
||||
displayName: 'MSSQL',
|
||||
connectionOptions: connectionProvider
|
||||
connectionOptions: connectionProvider,
|
||||
useFullOptions: true
|
||||
};
|
||||
capabilitiesService = new TestCapabilitiesService();
|
||||
capabilitiesService.capabilities[mssqlProviderName] = { connection: msSQLCapabilities };
|
||||
@@ -234,7 +235,7 @@ suite('SQL ConnectionProfileInfo tests', () => {
|
||||
|
||||
test('getOptionsKey should create a valid unique id', () => {
|
||||
let conn = new ConnectionProfile(capabilitiesService, iConnectionProfile);
|
||||
let expectedId = 'providerName:MSSQL|authenticationType:|databaseName:database|serverName:new server|userName:user|databaseDisplayName:database|group:group id';
|
||||
let expectedId = 'providerName:MSSQL|connectionName:new name|databaseName:database|serverName:new server|userName:user|databaseDisplayName:database|groupId:group id';
|
||||
let id = conn.getOptionsKey();
|
||||
assert.strictEqual(id, expectedId);
|
||||
});
|
||||
|
||||
@@ -33,7 +33,6 @@ suite('SQL ProviderConnectionInfo tests', () => {
|
||||
};
|
||||
|
||||
setup(() => {
|
||||
let capabilities: azdata.DataProtocolServerCapabilities[] = [];
|
||||
let connectionProvider: azdata.ConnectionOption[] = [
|
||||
{
|
||||
name: 'connectionName',
|
||||
@@ -125,8 +124,8 @@ suite('SQL ProviderConnectionInfo tests', () => {
|
||||
providerId: mssqlProviderName,
|
||||
displayName: 'MSSQL',
|
||||
connectionOptions: connectionProvider,
|
||||
useFullOptions: true
|
||||
};
|
||||
capabilities.push(msSQLCapabilities);
|
||||
capabilitiesService = new TestCapabilitiesService();
|
||||
capabilitiesService.capabilities[mssqlProviderName] = { connection: msSQLCapabilities };
|
||||
});
|
||||
@@ -230,15 +229,37 @@ suite('SQL ProviderConnectionInfo tests', () => {
|
||||
});
|
||||
|
||||
test('getOptionsKey should create a valid unique id', () => {
|
||||
// Test the new option key format
|
||||
let conn = new ProviderConnectionInfo(capabilitiesService, connectionProfile);
|
||||
// **IMPORTANT** This should NEVER change without thorough review and consideration of side effects. This key controls
|
||||
// things like how passwords are saved, which means if its changed then serious side effects will occur.
|
||||
let expectedId = 'providerName:MSSQL|authenticationType:|databaseName:database|serverName:new server|userName:user';
|
||||
let expectedId = 'providerName:MSSQL|connectionName:name|databaseName:database|serverName:new server|userName:user';
|
||||
let id = conn.getOptionsKey();
|
||||
assert.strictEqual(id, expectedId);
|
||||
|
||||
// Test for original options key (used for retrieving passwords and as a fallback for unsupported providers)
|
||||
// **IMPORTANT** The original format option key should NEVER change without thorough review and consideration of side effects. This version of the key controls
|
||||
// things like how passwords are saved, which means if its changed then serious side effects will occur.
|
||||
expectedId = 'providerName:MSSQL|authenticationType:|databaseName:database|serverName:new server|userName:user';
|
||||
id = conn.getOptionsKey(true);
|
||||
assert.strictEqual(id, expectedId);
|
||||
});
|
||||
|
||||
test('getOptionsKey should create the same ID regardless of optional options', () => {
|
||||
test('getOptionsKey should return original formatted ID if useFullOptions is not supported', () => {
|
||||
// Test the new option key format
|
||||
let originalCapabilitiesConnection = capabilitiesService.capabilities[mssqlProviderName].connection;
|
||||
originalCapabilitiesConnection.useFullOptions = false;
|
||||
let newCapabilitiesService = new TestCapabilitiesService();
|
||||
newCapabilitiesService.capabilities[mssqlProviderName] = { connection: originalCapabilitiesConnection }
|
||||
let conn = new ProviderConnectionInfo(newCapabilitiesService, connectionProfile);
|
||||
let expectedId = 'providerName:MSSQL|authenticationType:|databaseName:database|serverName:new server|userName:user';
|
||||
let id = conn.getOptionsKey();
|
||||
assert.strictEqual(id, expectedId);
|
||||
|
||||
// Should be the same when getOriginalOptions is true.
|
||||
id = conn.getOptionsKey(true);
|
||||
assert.strictEqual(id, expectedId);
|
||||
});
|
||||
|
||||
test('getOptionsKey should create different keys based on optional options', () => {
|
||||
const conn1 = new ProviderConnectionInfo(capabilitiesService, connectionProfile);
|
||||
let id1 = conn1.getOptionsKey();
|
||||
|
||||
@@ -248,6 +269,19 @@ suite('SQL ProviderConnectionInfo tests', () => {
|
||||
const conn2 = new ProviderConnectionInfo(capabilitiesService, connectionProfile);
|
||||
const id2 = conn2.getOptionsKey();
|
||||
|
||||
assert.notEqual(id1, id2);
|
||||
});
|
||||
|
||||
test('getOptionsKey should have the same key if original options is used', () => {
|
||||
const conn1 = new ProviderConnectionInfo(capabilitiesService, connectionProfile);
|
||||
let id1 = conn1.getOptionsKey(true);
|
||||
|
||||
connectionProfile.options = {
|
||||
'encrypt': true
|
||||
};
|
||||
const conn2 = new ProviderConnectionInfo(capabilitiesService, connectionProfile);
|
||||
const id2 = conn2.getOptionsKey(true);
|
||||
|
||||
assert.strictEqual(id1, id2);
|
||||
});
|
||||
|
||||
@@ -258,16 +292,6 @@ suite('SQL ProviderConnectionInfo tests', () => {
|
||||
assert.notStrictEqual(conn.getOptionsKey(), conn2.getOptionsKey());
|
||||
});
|
||||
|
||||
test('titleParts should return server, database and auth type as first items', () => {
|
||||
let conn = new ProviderConnectionInfo(capabilitiesService, connectionProfile);
|
||||
let titleParts = conn.titleParts;
|
||||
assert.strictEqual(titleParts.length, 4);
|
||||
assert.strictEqual(titleParts[0], connectionProfile.serverName);
|
||||
assert.strictEqual(titleParts[1], connectionProfile.databaseName);
|
||||
assert.strictEqual(titleParts[2], connectionProfile.authenticationType);
|
||||
assert.strictEqual(titleParts[3], connectionProfile.userName);
|
||||
});
|
||||
|
||||
test('getProviderFromOptionsKey should return the provider name from the options key successfully', () => {
|
||||
let optionsKey = `providerName:${mssqlProviderName}|authenticationType:|databaseName:database|serverName:new server|userName:user`;
|
||||
let actual = ProviderConnectionInfo.getProviderFromOptionsKey(optionsKey);
|
||||
|
||||
@@ -354,6 +354,10 @@ export class TestConnectionManagementService implements IConnectionManagementSer
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getEditorConnectionProfileTitle(profile: IConnectionProfile, getNonDefaultsOnly?: boolean): string {
|
||||
return undefined!;
|
||||
}
|
||||
|
||||
openCustomErrorDialog(options: azdata.window.IErrorDialogOptions): Promise<string | undefined> {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user