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:
Alex Ma
2023-04-18 11:08:48 -07:00
committed by GitHub
parent a9bc34acf0
commit b69e87df15
27 changed files with 1641 additions and 86 deletions

View File

@@ -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');
});
});

View File

@@ -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);
});

View File

@@ -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);

View File

@@ -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;
}