Revert new connection string format (#22997)

This commit is contained in:
Alex Ma
2023-05-05 13:41:40 -07:00
committed by GitHub
parent 27e0d67dec
commit 898bb73a34
34 changed files with 77 additions and 1690 deletions

View File

@@ -67,12 +67,6 @@ export interface ConnectionProviderProperties {
*/
displayName: string;
/**
* Enable to use all connection properties for URI generation (ServiceLayer requires the same options as well.)
* If not specified, only IsIdentity options will be used instead (URI with basic info).
*/
useFullOptions?: boolean;
/**
* Alias to be used for the kernel in notebooks
*/

View File

@@ -101,7 +101,6 @@ export class TestCapabilitiesService implements ICapabilitiesService {
providerId: mssqlProviderName,
displayName: 'MSSQL',
connectionOptions: connectionProvider,
useFullOptions: true,
};
let pgSQLCapabilities = {
providerId: this.pgsqlProviderName,

View File

@@ -70,51 +70,6 @@ export class ConnectionConfig {
});
}
/**
* Checks to make sure that the profile that is being edited is not identical to another profile.
*/
public isDuplicateEdit(profile: IConnectionProfile, matcher: ProfileMatcher = ConnectionProfile.matchesProfile): Promise<boolean> {
let profiles = deepClone(this.configurationService.inspect<IConnectionProfileStore[]>(CONNECTIONS_CONFIG_KEY).userValue as IConnectionProfileStore[]);
if (!profiles) {
profiles = [];
}
return this.addGroupFromProfile(profile).then(groupId => {
let connectionProfile = this.getConnectionProfileInstance(profile, groupId);
// Profile to be stored during an edit, used to check for duplicate profile edits.
let firstMatchProfile = undefined;
profiles.find(value => {
const providerConnectionProfile = ConnectionProfile.createFromStoredProfile(value, this._capabilitiesService);
const match = matcher(providerConnectionProfile, connectionProfile);
// If we have a profile match, and the matcher is an edit, we must store this match.
if (match && (matcher.toString() !== ConnectionProfile.matchesProfile.toString())) {
firstMatchProfile = value;
}
return match;
});
// If a profile edit, we must now check to see it does not match the other profiles available.
if (firstMatchProfile) {
// Copy over profile list so that we can remove the actual profile we want to edit.
const index = profiles.indexOf(firstMatchProfile);
if (index > -1) {
profiles.splice(index, 1);
}
// Use the regular profile matching here to find if edit is duplicate.
let matchesExistingProfile = profiles.find(value => {
const providerConnectionProfile = ConnectionProfile.createFromStoredProfile(value, this._capabilitiesService);
const match = ConnectionProfile.matchesProfile(providerConnectionProfile, connectionProfile);
return match;
});
return Promise.resolve(matchesExistingProfile !== undefined);
}
return Promise.resolve(false);
});
}
/**
* Add a new connection to the connection config.
*/
@@ -362,18 +317,10 @@ export class ConnectionConfig {
p.options.database === profile.options.database &&
p.options.server === profile.options.server &&
p.options.user === profile.options.user &&
p.options.connectionName === profile.options.connectionName &&
p.groupId === newGroupID &&
this.checkIfNonDefaultOptionsMatch(p, profile));
p.groupId === newGroupID);
return existingProfile === undefined;
}
private checkIfNonDefaultOptionsMatch(profileStore: IConnectionProfileStore, profile: ConnectionProfile): boolean {
let tempProfile = ConnectionProfile.createFromStoredProfile(profileStore, this._capabilitiesService);
let result = profile.getNonDefaultOptionsString() === tempProfile.getNonDefaultOptionsString();
return result;
}
/**
* Moves the connection under the target group with the new ID.
*/

View File

@@ -378,14 +378,6 @@ export interface IConnectionManagementService {
* @returns the new valid password that is entered, or undefined if cancelled or errored.
*/
openChangePasswordDialog(profile: IConnectionProfile): Promise<string | undefined>;
/**
* Gets the formatted title of the connection profile for display.
* @param profile The connection profile to change the password.
* @param getNonDefaultsOnly Provide if you only want to get the non default options string (for some titles).
* @returns the new valid password that is entered, or undefined if cancelled or errored.
*/
getEditorConnectionProfileTitle(profile: IConnectionProfile, getNonDefaultsOnly?: boolean): string;
}
export enum RunQueryOnConnectionMode {

View File

@@ -39,9 +39,6 @@ export class ConnectionProfile extends ProviderConnectionInfo implements interfa
public isDisconnecting: boolean = false;
// title from ProviderConnectionInfo cannot be changed, in order to show different dynamic options appended, we must override the title with our own.
private _title?: string;
public constructor(
capabilitiesService: ICapabilitiesService,
model: string | azdata.IConnectionProfile | azdata.connection.ConnectionProfile | undefined) {
@@ -202,33 +199,6 @@ export class ConnectionProfile extends ProviderConnectionInfo implements interfa
return (this._groupName === ConnectionProfile.RootGroupName);
}
public override get title(): string {
if (this._title) {
return this._title;
}
return this.getOriginalTitle();
}
public getOriginalTitle(): string {
return super.title;
}
public getEditorFullTitleWithOptions(): string {
let textResult = '';
if (this.connectionName) {
textResult += `${this.connectionName}: `;
}
textResult += this.serverInfo;
if (textResult.length === 0) {
return undefined;
}
return textResult;
}
public override set title(value: string) {
this._title = value;
}
public override clone(): ConnectionProfile {
let instance = new ConnectionProfile(this.capabilitiesService, this);
return instance;
@@ -257,33 +227,24 @@ export class ConnectionProfile extends ProviderConnectionInfo implements interfa
/**
* Returns a key derived the connections options (providerName, authenticationType, serverName, databaseName, userName, groupid)
* and all the other properties if useFullOptions is enabled for the provider.
* This key uniquely identifies a connection in a group
* Example: "providerName:MSSQL|authenticationType:|databaseName:database|serverName:server3|userName:user|group:testid"
* @param getOriginalOptions will return the original URI format regardless if useFullOptions was set or not. (used for retrieving passwords)
*/
public override getOptionsKey(getOriginalOptions?: boolean): string {
let id = super.getOptionsKey(getOriginalOptions);
public override getOptionsKey(): string {
let id = super.getOptionsKey();
let databaseDisplayName: string = this.options['databaseDisplayName'];
if (databaseDisplayName) {
id += ProviderConnectionInfo.idSeparator + 'databaseDisplayName' + ProviderConnectionInfo.nameValueSeparator + databaseDisplayName;
}
return id + ProviderConnectionInfo.idSeparator + 'groupId' + ProviderConnectionInfo.nameValueSeparator + this.groupId;
return id + ProviderConnectionInfo.idSeparator + 'group' + ProviderConnectionInfo.nameValueSeparator + this.groupId;
}
/**
* Returns the unique id for the connection that doesn't include the group name.
* Used primarily for retrieving shared passwords among different connections in default state.
* @param getOriginalOptions will return the original URI format regardless if useFullOptions was set or not. (used for retrieving passwords)
* Returns the unique id for the connection that doesn't include the group name
*/
public getConnectionInfoId(getOriginalOptions = true): string {
let id = super.getOptionsKey(getOriginalOptions);
let databaseDisplayName: string = this.options['databaseDisplayName'];
if (databaseDisplayName && !getOriginalOptions && this.serverCapabilities?.useFullOptions) {
id += ProviderConnectionInfo.idSeparator + 'databaseDisplayName' + ProviderConnectionInfo.nameValueSeparator + databaseDisplayName;
}
return id;
public getConnectionInfoId(): string {
return super.getOptionsKey();
}
public toIConnectionProfile(): interfaces.IConnectionProfile {
@@ -292,7 +253,6 @@ export class ConnectionProfile extends ProviderConnectionInfo implements interfa
serverName: this.serverName,
databaseName: this.databaseName,
authenticationType: this.authenticationType,
serverCapabilities: this.serverCapabilities,
getOptionsKey: this.getOptionsKey,
matches: this.matches,
groupId: this.groupId,

View File

@@ -67,7 +67,6 @@ export class ConnectionStore {
}
cred.push(CRED_ITEMTYPE_PREFIX.concat(itemType));
// Use basic info for credentials so that passwords can be shared among similar profiles for now.
cred.push(CRED_ID_PREFIX.concat(connectionProfileInstance.getConnectionInfoId()));
return cred.join(CRED_SEPARATOR);
}
@@ -150,17 +149,6 @@ export class ConnectionStore {
}
}
/**
* Checks to see if a connection profile edit is not identical to an existing saved profile.
*
* @param profile the profile group that is being edited.
* @param matcher the profile matching function for the actual connection we want to edit.
* @returns a boolean value indicating if there's an identical profile to the edit.
*/
public isDuplicateEdit(profile: IConnectionProfile, matcher?: ProfileMatcher): Promise<boolean> {
return this.connectionConfig.isDuplicateEdit(profile, matcher);
}
/**
* Gets the list of recently used connections. These will not include the password - a separate call to
* {addSavedPassword} is needed to fill that before connecting
@@ -229,7 +217,7 @@ export class ConnectionStore {
// Remove the connection from the list if it already exists
list = list.filter(value => {
let equal = value && value.getConnectionInfoId(false) === savedProfile.getConnectionInfoId(false);
let equal = value && value.getConnectionInfoId() === savedProfile.getConnectionInfoId();
if (equal && savedProfile.saveProfile) {
equal = value.groupId === savedProfile.groupId ||
ConnectionProfileGroup.sameGroupName(value.groupFullName, savedProfile.groupFullName);
@@ -247,7 +235,7 @@ export class ConnectionStore {
// Remove the connection from the list if it already exists
list = list.filter(value => {
let equal = value && value.getConnectionInfoId(false) === savedProfile.getConnectionInfoId(false);
let equal = value && value.getConnectionInfoId() === savedProfile.getConnectionInfoId();
if (equal && savedProfile.saveProfile) {
equal = value.groupId === savedProfile.groupId ||
ConnectionProfileGroup.sameGroupName(value.groupFullName, savedProfile.groupFullName);
@@ -282,8 +270,6 @@ export class ConnectionStore {
private doSavePassword(conn: IConnectionProfile): Promise<boolean> {
if (conn.password) {
// Credentials are currently shared between profiles with the same basic details.
// Credentials are currently not cleared upon deletion of a profile.
const credentialId = this.formatCredentialId(conn);
return this.credentialService.saveCredential(credentialId, conn.password);
} else {

View File

@@ -4,14 +4,12 @@
*--------------------------------------------------------------------------------------------*/
import * as azdata from 'azdata';
import { ConnectionProviderProperties } from 'sql/platform/capabilities/common/capabilitiesService';
// Used to allow various methods of matching profiles
export type ProfileMatcher = (a: IConnectionProfile, b: IConnectionProfile) => boolean;
export interface IConnectionProfile extends azdata.IConnectionProfile {
serverCapabilities: ConnectionProviderProperties | undefined;
getOptionsKey(getOriginalOptions?: boolean): string;
getOptionsKey(): string;
matches(profile: azdata.IConnectionProfile): boolean;
}

View File

@@ -8,7 +8,7 @@ import { isString } from 'vs/base/common/types';
import * as azdata from 'azdata';
import * as Constants from 'sql/platform/connection/common/constants';
import { ICapabilitiesService, ConnectionProviderProperties } from 'sql/platform/capabilities/common/capabilitiesService';
import { ConnectionOptionSpecialType } from 'sql/platform/connection/common/interfaces';
import { ConnectionOptionSpecialType, ServiceOptionType } from 'sql/platform/connection/common/interfaces';
import { localize } from 'vs/nls';
type SettableProperty = 'serverName' | 'authenticationType' | 'databaseName' | 'password' | 'connectionName' | 'userName';
@@ -161,7 +161,7 @@ export class ProviderConnectionInfo implements azdata.ConnectionInfo {
}
}
// The provider capabilities are registered at the same time at load time, we can assume all providers are registered as long as the collection is not empty.
else if (this.hasLoaded()) {
else if (Object.keys(this.capabilitiesService.providers).length > 0) {
return localize('connection.unsupported', "Unsupported connection");
} else {
return localize('loading', "Loading...");
@@ -169,16 +169,8 @@ export class ProviderConnectionInfo implements azdata.ConnectionInfo {
return label;
}
public hasLoaded(): boolean {
return Object.keys(this.capabilitiesService.providers).length > 0;
}
public get serverInfo(): string {
let value = this.getServerInfo();
if (this.serverCapabilities?.useFullOptions) {
value += this.getNonDefaultOptionsString();
}
return value;
return this.getServerInfo();
}
public isPasswordRequired(): boolean {
@@ -206,23 +198,16 @@ export class ProviderConnectionInfo implements azdata.ConnectionInfo {
/**
* Returns a key derived the connections options (providerName, authenticationType, serverName, databaseName, userName, groupid)
* and all the other properties if useFullOptions is enabled for the provider.
* This key uniquely identifies a connection in a group
* Example: "providerName:MSSQL|authenticationType:|databaseName:database|serverName:server3|userName:user|group:testid"
* @param getOriginalOptions will return the original URI format regardless if useFullOptions was set or not. (used for retrieving passwords)
*/
public getOptionsKey(getOriginalOptions?: boolean): string {
let useFullOptions = false;
public getOptionsKey(): string {
let idNames = [];
if (this.serverCapabilities) {
useFullOptions = this.serverCapabilities.useFullOptions;
idNames = this.serverCapabilities.connectionOptions.map(o => {
// All options enabled, use every property besides password.
let newProperty = useFullOptions && o.specialValueType !== ConnectionOptionSpecialType.password && !getOriginalOptions;
// Fallback to original base IsIdentity properties otherwise.
let originalProperty = (o.specialValueType || o.isIdentity) && o.specialValueType !== ConnectionOptionSpecialType.password
&& o.specialValueType !== ConnectionOptionSpecialType.connectionName;
if (newProperty || originalProperty) {
if ((o.specialValueType || o.isIdentity)
&& o.specialValueType !== ConnectionOptionSpecialType.password
&& o.specialValueType !== ConnectionOptionSpecialType.connectionName) {
return o.name;
} else {
return undefined;
@@ -241,45 +226,14 @@ export class ProviderConnectionInfo implements azdata.ConnectionInfo {
let idValues: string[] = [];
for (let index = 0; index < idNames.length; index++) {
let value = this.options[idNames[index]!];
// If we're using the new URI format, we do not include any values that are empty or are default.
let isFullOptions = useFullOptions && !getOriginalOptions;
if (isFullOptions) {
let finalValue = undefined;
let options = this.serverCapabilities.connectionOptions.filter(value => value.name === idNames[index]!);
if (options.length > 0 && value) {
finalValue = value !== options[0].defaultValue ? value : undefined;
}
value = finalValue;
}
else {
value = value ? value : '';
}
if ((isFullOptions && value !== undefined) || !isFullOptions) {
idValues.push(`${idNames[index]}${ProviderConnectionInfo.nameValueSeparator}${value}`);
}
value = value ? value : '';
idValues.push(`${idNames[index]}${ProviderConnectionInfo.nameValueSeparator}${value}`);
}
return ProviderConnectionInfo.ProviderPropertyName + ProviderConnectionInfo.nameValueSeparator +
this.providerName + ProviderConnectionInfo.idSeparator + idValues.join(ProviderConnectionInfo.idSeparator);
}
/**
* Returns a more readable version of the options key intended for display areas, replaces the regular separators with display separators
* @param optionsKey options key in the original format.
*/
public static getDisplayOptionsKey(optionsKey: string) {
let ids: string[] = optionsKey.split(ProviderConnectionInfo.idSeparator);
ids = ids.map(id => {
let idParts = id.split(ProviderConnectionInfo.nameValueSeparator);
let result = idParts[0] + ProviderConnectionInfo.displayNameValueSeparator;
if (idParts.length >= 2) {
result += idParts.slice(1).join(ProviderConnectionInfo.nameValueSeparator);
}
return result;
});
return ids.join(ProviderConnectionInfo.displayIdSeparator);
}
public static getProviderFromOptionsKey(optionsKey: string) {
let providerId: string = '';
if (optionsKey) {
@@ -337,68 +291,28 @@ export class ProviderConnectionInfo implements azdata.ConnectionInfo {
return ':';
}
public static get displayIdSeparator(): string {
return '; ';
}
public static get displayNameValueSeparator(): string {
return '=';
}
/**
* Get all non specialValueType (or if distinct connections share same connection name, everything but connectionName and password).
* Also allows for getting the non default options for this profile. (this function is used for changing the title).
* @param needSpecial include all the special options key besides connection name or password in case we have multiple
* distinct connections sharing the same connection name.
* @param getNonDefault get only the non default options (for individual connections) to be used for identfying different properties
* among connections sharing the same title.
*/
public getConnectionOptionsList(needSpecial: boolean, getNonDefault: boolean): azdata.ConnectionOption[] {
let connectionOptions: azdata.ConnectionOption[] = [];
public get titleParts(): string[] {
let parts: string[] = [];
// Always put these three on top. TODO: maybe only for MSSQL?
parts.push(this.serverName);
parts.push(this.databaseName);
parts.push(this.authenticationTypeDisplayName);
if (this.serverCapabilities) {
this.serverCapabilities.connectionOptions.forEach(element => {
if (((!needSpecial && element.specialValueType !== ConnectionOptionSpecialType.serverName &&
if (element.specialValueType !== ConnectionOptionSpecialType.serverName &&
element.specialValueType !== ConnectionOptionSpecialType.databaseName &&
element.specialValueType !== ConnectionOptionSpecialType.authType &&
element.specialValueType !== ConnectionOptionSpecialType.userName) || needSpecial) &&
element.specialValueType !== ConnectionOptionSpecialType.password &&
element.specialValueType !== ConnectionOptionSpecialType.connectionName &&
element.specialValueType !== ConnectionOptionSpecialType.password) {
if (getNonDefault) {
let value = this.getOptionValue(element.name);
if (value && value !== element.defaultValue) {
connectionOptions.push(element);
}
}
else {
connectionOptions.push(element);
element.isIdentity && element.valueType === ServiceOptionType.string) {
let value = this.getOptionValue(element.name);
if (value) {
parts.push(value);
}
}
});
}
//Need to sort for consistency.
connectionOptions.sort();
return connectionOptions;
}
/**
* Append all non default options to tooltip string if useFullOptions is enabled.
*/
public getNonDefaultOptionsString(): string {
let parts: string = "";
let nonDefaultOptions = this.getConnectionOptionsList(false, true);
nonDefaultOptions.forEach(element => {
let value = this.getOptionValue(element.name);
if (parts.length === 0) {
parts = " (";
}
let addValue = element.name + ProviderConnectionInfo.displayNameValueSeparator + `${value}`;
parts += parts === " (" ? addValue : (ProviderConnectionInfo.displayIdSeparator + addValue);
});
if (parts.length > 0) {
parts += ")";
}
return parts;
}

View File

@@ -181,30 +181,6 @@ 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
}
]
};
@@ -301,7 +277,6 @@ suite('ConnectionConfig', () => {
groupFullName: undefined,
groupId: undefined,
getOptionsKey: undefined!,
serverCapabilities: undefined,
matches: undefined!,
providerName: 'MSSQL',
options: {},
@@ -334,7 +309,6 @@ suite('ConnectionConfig', () => {
savePassword: true,
groupFullName: undefined,
getOptionsKey: undefined!,
serverCapabilities: undefined,
matches: undefined!,
providerName: 'MSSQL',
options: {},
@@ -367,7 +341,6 @@ suite('ConnectionConfig', () => {
groupFullName: 'g2/g2-2',
groupId: undefined,
getOptionsKey: undefined!,
serverCapabilities: undefined,
matches: undefined!,
providerName: 'MSSQL',
options: {},
@@ -511,7 +484,6 @@ suite('ConnectionConfig', () => {
groupFullName: 'g3',
groupId: 'g3',
getOptionsKey: undefined!,
serverCapabilities: undefined,
matches: undefined!,
providerName: 'MSSQL',
options: {},
@@ -542,7 +514,6 @@ suite('ConnectionConfig', () => {
groupFullName: 'g3',
groupId: 'g3',
getOptionsKey: undefined!,
serverCapabilities: undefined,
matches: undefined!,
providerName: 'MSSQL',
options: {},
@@ -580,7 +551,6 @@ suite('ConnectionConfig', () => {
groupFullName: 'g3',
groupId: 'newid',
getOptionsKey: undefined!,
serverCapabilities: undefined,
matches: undefined!,
providerName: 'MSSQL',
options: {},
@@ -661,7 +631,6 @@ suite('ConnectionConfig', () => {
groupFullName: 'g3',
groupId: 'g3',
getOptionsKey: () => { return 'connectionId'; },
serverCapabilities: undefined,
matches: undefined!,
providerName: 'MSSQL',
options: {},
@@ -679,7 +648,6 @@ suite('ConnectionConfig', () => {
groupFullName: 'test',
groupId: 'test',
getOptionsKey: () => { return 'connectionId'; },
serverCapabilities: undefined,
matches: undefined!,
providerName: 'MSSQL',
options: {},
@@ -709,189 +677,6 @@ 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'; },
serverCapabilities: undefined,
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'; },
serverCapabilities: undefined,
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'; },
serverCapabilities: undefined,
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'; },
serverCapabilities: undefined,
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'; },
serverCapabilities: undefined,
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'; },
serverCapabilities: undefined,
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',
@@ -903,7 +688,6 @@ suite('ConnectionConfig', () => {
groupFullName: 'g3',
groupId: 'g3',
getOptionsKey: () => { return 'connectionId'; },
serverCapabilities: undefined,
matches: undefined!,
providerName: 'MSSQL',
options: {},
@@ -994,146 +778,4 @@ 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'; },
serverCapabilities: undefined,
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'; },
serverCapabilities: undefined,
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'; },
serverCapabilities: undefined,
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'; },
serverCapabilities: undefined,
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'; },
serverCapabilities: undefined,
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'; },
serverCapabilities: undefined,
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

@@ -26,7 +26,6 @@ suite('SQL ConnectionProfileInfo tests', () => {
groupFullName: 'g2/g2-2',
groupId: 'group id',
getOptionsKey: undefined!,
serverCapabilities: undefined,
matches: undefined!,
providerName: mssqlProviderName,
options: {},
@@ -172,8 +171,7 @@ suite('SQL ConnectionProfileInfo tests', () => {
msSQLCapabilities = {
providerId: mssqlProviderName,
displayName: 'MSSQL',
connectionOptions: connectionProvider,
useFullOptions: true
connectionOptions: connectionProvider
};
capabilitiesService = new TestCapabilitiesService();
capabilitiesService.capabilities[mssqlProviderName] = { connection: msSQLCapabilities };
@@ -236,7 +234,7 @@ suite('SQL ConnectionProfileInfo tests', () => {
test('getOptionsKey should create a valid unique id', () => {
let conn = new ConnectionProfile(capabilitiesService, iConnectionProfile);
let expectedId = 'providerName:MSSQL|connectionName:new name|databaseName:database|serverName:new server|userName:user|databaseDisplayName:database|groupId:group id';
let expectedId = 'providerName:MSSQL|authenticationType:|databaseName:database|serverName:new server|userName:user|databaseDisplayName:database|group:group id';
let id = conn.getOptionsKey();
assert.strictEqual(id, expectedId);
});
@@ -283,10 +281,4 @@ suite('SQL ConnectionProfileInfo tests', () => {
test('an empty connection profile does not cause issues', () => {
assert.doesNotThrow(() => new ConnectionProfile(capabilitiesService, {} as IConnectionProfile));
});
test('getOptionsKey should produce the same optionsKey after converting to IConnectionProfile', () => {
let conn = new ConnectionProfile(capabilitiesService, iConnectionProfile);
const myIConnectionProfile = conn.toIConnectionProfile();
assert.equal(conn.getOptionsKey(), myIConnectionProfile.getOptionsKey());
});
});

View File

@@ -32,7 +32,6 @@ suite('ConnectionStore', () => {
groupId: '',
groupFullName: '',
getOptionsKey: undefined!,
serverCapabilities: undefined,
matches: () => false,
providerName: mssqlProviderName,
options: {},

View File

@@ -25,7 +25,6 @@ suite('SQL ProviderConnectionInfo tests', () => {
groupFullName: 'g2/g2-2',
groupId: undefined,
getOptionsKey: undefined!,
serverCapabilities: undefined,
matches: undefined!,
providerName: mssqlProviderName,
options: undefined!,
@@ -34,6 +33,7 @@ suite('SQL ProviderConnectionInfo tests', () => {
};
setup(() => {
let capabilities: azdata.DataProtocolServerCapabilities[] = [];
let connectionProvider: azdata.ConnectionOption[] = [
{
name: 'connectionName',
@@ -125,8 +125,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,37 +230,15 @@ suite('SQL ProviderConnectionInfo tests', () => {
});
test('getOptionsKey should create a valid unique id', () => {
// Test the new option key format
let conn = new ProviderConnectionInfo(capabilitiesService, connectionProfile);
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
// **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.
expectedId = 'providerName:MSSQL|authenticationType:|databaseName:database|serverName:new server|userName:user';
id = conn.getOptionsKey(true);
assert.strictEqual(id, expectedId);
});
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', () => {
test('getOptionsKey should create the same ID regardless of optional options', () => {
const conn1 = new ProviderConnectionInfo(capabilitiesService, connectionProfile);
let id1 = conn1.getOptionsKey();
@@ -270,19 +248,6 @@ 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);
});
@@ -293,6 +258,16 @@ 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,10 +354,6 @@ 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;
}

View File

@@ -27,7 +27,6 @@ let connectionProfile: IConnectionProfile = {
groupFullName: 'g2/g2-2',
groupId: 'group id',
getOptionsKey: () => 'connection1',
serverCapabilities: undefined,
matches: undefined!,
providerName: mssqlProviderName,
options: {},
@@ -45,7 +44,6 @@ let editorConnectionProfile: IConnectionProfile = {
groupFullName: 'g2/g2-2',
groupId: 'group id',
getOptionsKey: () => 'connection2',
serverCapabilities: undefined,
matches: undefined!,
providerName: mssqlProviderName,
options: {},
@@ -63,7 +61,6 @@ let connectionProfileWithoutDbName: IConnectionProfile = {
groupFullName: 'g2/g2-2',
groupId: 'group id',
getOptionsKey: () => 'connection1',
serverCapabilities: undefined,
matches: undefined!,
providerName: mssqlProviderName,
options: {},