Revert "Connection Store Refactor (#4632)" (#4671)

This reverts commit 756f77063a.
This commit is contained in:
Anthony Dresser
2019-03-22 11:30:20 -07:00
committed by GitHub
parent 8d5f676039
commit f5c9174c2f
19 changed files with 1008 additions and 897 deletions

View File

@@ -15,48 +15,3 @@ export function toObject<V>(map: Map<string, V>): { [key: string]: V } {
}
return {};
}
export class ReverseLookUpMap<K, V> {
private forward = new Map<K, V>();
private reverse = new Map<V, K>();
public clear(): void {
this.forward.clear();
this.reverse.clear();
}
public delete(key: K): boolean {
let reverseKey = this.forward.get(key);
return this.forward.delete(key) && this.reverse.delete(reverseKey);
}
public forEach(callbackfn: (value: V, index: K, map: Map<K, V>) => void, thisArg?: any): void {
this.forward.forEach(callbackfn, thisArg);
}
public get(key: K): V {
return this.forward.get(key);
}
public reverseGet(key: V): K {
return this.reverse.get(key);
}
public has(key: K): boolean {
return this.forward.has(key);
}
public reverseHas(key: V): boolean {
return this.reverse.has(key);
}
public set(key: K, value: V): ReverseLookUpMap<K, V> {
this.forward.set(key, value);
this.reverse.set(value, key);
return this;
}
public get size(): number {
return this.forward.size;
}
}

View File

@@ -15,6 +15,7 @@ import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { ConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup';
import * as TaskUtilities from 'sql/workbench/common/taskUtilities';
import { ITree } from 'vs/base/parts/tree/browser/tree';
import * as Constants from 'sql/platform/connection/common/constants';
import { IObjectExplorerService } from 'sql/workbench/services/objectExplorer/common/objectExplorerService';
import { TreeNode } from 'sql/parts/objectExplorer/common/treeNode';
import Severity from 'vs/base/common/severity';
@@ -22,7 +23,6 @@ import { ObjectExplorerActionsContext } from 'sql/parts/objectExplorer/viewlet/o
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IErrorMessageService } from 'sql/platform/errorMessage/common/errorMessageService';
import { ConnectionViewletPanel } from 'sql/workbench/parts/dataExplorer/browser/connectionViewletPanel';
import { UNSAVED_GROUP_ID } from 'sql/platform/connection/common/constants';
export class RefreshAction extends Action {
@@ -356,13 +356,13 @@ export class DeleteConnectionAction extends Action {
) {
super(id, label);
this.class = 'delete-connection-action';
if (element instanceof ConnectionProfileGroup && element.id === UNSAVED_GROUP_ID) {
if (element instanceof ConnectionProfileGroup && element.id === Constants.unsavedGroupId) {
this.enabled = false;
}
if (element instanceof ConnectionProfile) {
let parent: ConnectionProfileGroup = element.parent;
if (parent && parent.id === UNSAVED_GROUP_ID) {
if (parent && parent.id === Constants.unsavedGroupId) {
this.enabled = false;
}
}

View File

@@ -6,11 +6,12 @@
'use strict';
import { ConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup';
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
import { ITree, IDragAndDrop, IDragOverReaction, DRAG_OVER_ACCEPT_BUBBLE_DOWN, DRAG_OVER_REJECT } from 'vs/base/parts/tree/browser/tree';
import * as Constants from 'sql/platform/connection/common/constants';
import { DragMouseEvent } from 'vs/base/browser/mouseEvent';
import { TreeUpdateUtils } from 'sql/parts/objectExplorer/viewlet/treeUpdateUtils';
import { UNSAVED_GROUP_ID } from 'sql/platform/connection/common/constants';
import { IDragAndDropData } from 'vs/base/browser/dnd';
/**
@@ -18,8 +19,8 @@ import { IDragAndDropData } from 'vs/base/browser/dnd';
*/
export class ServerTreeDragAndDrop implements IDragAndDrop {
constructor(
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService
constructor(@IConnectionManagementService private _connectionManagementService: IConnectionManagementService,
@IInstantiationService private _instantiationService: IInstantiationService
) {
}
@@ -142,7 +143,7 @@ export class ServerTreeDragAndDrop implements IDragAndDrop {
let isDropToItself = source && targetConnectionProfileGroup && (source instanceof ConnectionProfileGroup) && source.name === targetConnectionProfileGroup.name;
let isDropToSameLevel = oldParent && oldParent.equals(targetConnectionProfileGroup);
let isUnsavedDrag = source && (source instanceof ConnectionProfileGroup) && (source.id === UNSAVED_GROUP_ID);
let isUnsavedDrag = source && (source instanceof ConnectionProfileGroup) && (source.id === Constants.unsavedGroupId);
return (!isDropToSameLevel && !isDropToItself && !isUnsavedDrag);
}
}
@@ -152,6 +153,11 @@ export class ServerTreeDragAndDrop implements IDragAndDrop {
*/
export class RecentConnectionsDragAndDrop implements IDragAndDrop {
constructor(@IConnectionManagementService private connectionManagementService: IConnectionManagementService,
@IInstantiationService private instantiationService: IInstantiationService
) {
}
/**
* Returns a uri if the given element should be allowed to drag.
* Returns null, otherwise.

View File

@@ -7,16 +7,14 @@
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
import { ConnectionProfileGroup, IConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup';
import { UNSAVED_GROUP_ID } from 'sql/platform/connection/common/constants';
import * as Constants from 'sql/platform/connection/common/constants';
import { IConnectionConfig } from 'sql/platform/connection/common/iconnectionConfig';
import { IConnectionProfile, IConnectionProfileStore } from 'sql/platform/connection/common/interfaces';
import * as Utils from 'sql/platform/connection/common/utils';
import { generateUuid } from 'vs/base/common/uuid';
import * as nls from 'vs/nls';
import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration';
const GROUPS_CONFIG_KEY = 'datasource.connectionGroups';
const CONNECTIONS_CONFIG_KEY = 'datasource.connections';
export interface ISaveGroupResult {
groups: IConnectionProfileGroup[];
newGroupId: string;
@@ -25,7 +23,7 @@ export interface ISaveGroupResult {
/**
* Implements connection profile file storage.
*/
export class ConnectionConfig {
export class ConnectionConfig implements IConnectionConfig {
public constructor(
private configurationService: IConfigurationService,
@@ -38,7 +36,7 @@ export class ConnectionConfig {
public getAllGroups(): IConnectionProfileGroup[] {
let allGroups: IConnectionProfileGroup[] = [];
let { user, workspace } = this.configurationService.inspect<IConnectionProfileGroup[]>(GROUPS_CONFIG_KEY);
let { user, workspace } = this.configurationService.inspect<IConnectionProfileGroup[]>(Constants.connectionGroupsArrayName);
if (user) {
if (workspace) {
@@ -47,12 +45,13 @@ export class ConnectionConfig {
}
allGroups = allGroups.concat(user);
}
return allGroups.map(g => {
allGroups = allGroups.map(g => {
if (g.parentId === '' || !g.parentId) {
g.parentId = undefined;
}
return g;
});
return allGroups;
}
/**
@@ -61,7 +60,7 @@ export class ConnectionConfig {
public addConnection(profile: IConnectionProfile): Promise<IConnectionProfile> {
if (profile.saveProfile) {
return this.addGroupFromProfile(profile).then(groupId => {
let profiles = this.configurationService.inspect<IConnectionProfileStore[]>(CONNECTIONS_CONFIG_KEY).user;
let profiles = this.configurationService.inspect<IConnectionProfileStore[]>(Constants.connectionsArrayName).user;
if (!profiles) {
profiles = [];
}
@@ -70,7 +69,7 @@ export class ConnectionConfig {
let newProfile = ConnectionProfile.convertToProfileStore(this._capabilitiesService, connectionProfile);
// Remove the profile if already set
let sameProfileInList = profiles.find(value => {
var sameProfileInList = profiles.find(value => {
let providerConnectionProfile = ConnectionProfile.createFromStoredProfile(value, this._capabilitiesService);
return providerConnectionProfile.matches(connectionProfile);
});
@@ -83,7 +82,7 @@ export class ConnectionConfig {
profiles.push(newProfile);
}
return this.configurationService.updateValue(CONNECTIONS_CONFIG_KEY, profiles, ConfigurationTarget.USER).then(() => connectionProfile);
return this.configurationService.updateValue(Constants.connectionsArrayName, profiles, ConfigurationTarget.USER).then(() => connectionProfile);
});
} else {
return Promise.resolve(profile);
@@ -107,11 +106,11 @@ export class ConnectionConfig {
if (profile.groupId && profile.groupId !== Utils.defaultGroupId) {
return Promise.resolve(profile.groupId);
} else {
let groups = this.configurationService.inspect<IConnectionProfileGroup[]>(GROUPS_CONFIG_KEY).user;
let groups = this.configurationService.inspect<IConnectionProfileGroup[]>(Constants.connectionGroupsArrayName).user;
let result = this.saveGroup(groups, profile.groupFullName, undefined, undefined);
groups = result.groups;
return this.configurationService.updateValue(GROUPS_CONFIG_KEY, groups, ConfigurationTarget.USER).then(() => result.newGroupId);
return this.configurationService.updateValue(Constants.connectionGroupsArrayName, groups, ConfigurationTarget.USER).then(() => result.newGroupId);
}
}
@@ -123,7 +122,7 @@ export class ConnectionConfig {
if (profileGroup.id) {
return Promise.resolve(profileGroup.id);
} else {
let groups = this.configurationService.inspect<IConnectionProfileGroup[]>(GROUPS_CONFIG_KEY).user;
let groups = this.configurationService.inspect<IConnectionProfileGroup[]>(Constants.connectionGroupsArrayName).user;
let sameNameGroup = groups ? groups.find(group => group.name === profileGroup.name) : undefined;
if (sameNameGroup) {
let errMessage: string = nls.localize('invalidServerName', "A server group with the same name already exists.");
@@ -132,13 +131,13 @@ export class ConnectionConfig {
let result = this.saveGroup(groups, profileGroup.name, profileGroup.color, profileGroup.description);
groups = result.groups;
return this.configurationService.updateValue(GROUPS_CONFIG_KEY, groups, ConfigurationTarget.USER).then(() => result.newGroupId);
return this.configurationService.updateValue(Constants.connectionGroupsArrayName, groups, ConfigurationTarget.USER).then(() => result.newGroupId);
}
}
}
private getConnectionProfilesForTarget(configTarget: ConfigurationTarget): IConnectionProfileStore[] {
let configs = this.configurationService.inspect<IConnectionProfileStore[]>(CONNECTIONS_CONFIG_KEY);
let configs = this.configurationService.inspect<IConnectionProfileStore[]>(Constants.connectionsArrayName);
let profiles: IConnectionProfileStore[];
if (configs) {
if (configTarget === ConfigurationTarget.USER) {
@@ -148,7 +147,7 @@ export class ConnectionConfig {
}
if (profiles) {
if (this.fixConnectionIds(profiles)) {
this.configurationService.updateValue(CONNECTIONS_CONFIG_KEY, profiles, configTarget);
this.configurationService.updateValue(Constants.connectionsArrayName, profiles, configTarget);
}
} else {
profiles = [];
@@ -165,7 +164,8 @@ export class ConnectionConfig {
private fixConnectionIds(profiles: IConnectionProfileStore[]): boolean {
let idsCache: { [label: string]: boolean } = {};
let changed: boolean = false;
for (let profile of profiles) {
for (var index = 0; index < profiles.length; index++) {
var profile = profiles[index];
if (!profile.id) {
profile.id = generateUuid();
changed = true;
@@ -215,7 +215,7 @@ export class ConnectionConfig {
*/
public deleteConnection(profile: ConnectionProfile): Promise<void> {
// Get all connections in the settings
let profiles = this.configurationService.inspect<IConnectionProfileStore[]>(CONNECTIONS_CONFIG_KEY).user;
let profiles = this.configurationService.inspect<IConnectionProfileStore[]>(Constants.connectionsArrayName).user;
// Remove the profile from the connections
profiles = profiles.filter(value => {
let providerConnectionProfile = ConnectionProfile.createFromStoredProfile(value, this._capabilitiesService);
@@ -223,7 +223,7 @@ export class ConnectionConfig {
});
// Write connections back to settings
return this.configurationService.updateValue(CONNECTIONS_CONFIG_KEY, profiles, ConfigurationTarget.USER);
return this.configurationService.updateValue(Constants.connectionsArrayName, profiles, ConfigurationTarget.USER);
}
/**
@@ -236,7 +236,7 @@ export class ConnectionConfig {
// Add selected group to subgroups list
subgroups.push(group);
// Get all connections in the settings
let profiles = this.configurationService.inspect<IConnectionProfileStore[]>(CONNECTIONS_CONFIG_KEY).user;
let profiles = this.configurationService.inspect<IConnectionProfileStore[]>(Constants.connectionsArrayName).user;
// Remove the profiles from the connections
profiles = profiles.filter(value => {
let providerConnectionProfile = ConnectionProfile.createFromStoredProfile(value, this._capabilitiesService);
@@ -244,14 +244,14 @@ export class ConnectionConfig {
});
// Get all groups in the settings
let groups = this.configurationService.inspect<IConnectionProfileGroup[]>(GROUPS_CONFIG_KEY).user;
let groups = this.configurationService.inspect<IConnectionProfileGroup[]>(Constants.connectionGroupsArrayName).user;
// Remove subgroups in the settings
groups = groups.filter((grp) => {
return !subgroups.some((item) => item.id === grp.id);
});
return Promise.all([
this.configurationService.updateValue(CONNECTIONS_CONFIG_KEY, profiles, ConfigurationTarget.USER),
this.configurationService.updateValue(GROUPS_CONFIG_KEY, groups, ConfigurationTarget.USER)
this.configurationService.updateValue(Constants.connectionsArrayName, profiles, ConfigurationTarget.USER),
this.configurationService.updateValue(Constants.connectionGroupsArrayName, groups, ConfigurationTarget.USER)
]).then(() => Promise.resolve());
}
@@ -259,14 +259,14 @@ export class ConnectionConfig {
* Moves the source group under the target group.
*/
public changeGroupIdForConnectionGroup(source: ConnectionProfileGroup, target: ConnectionProfileGroup): Promise<void> {
let groups = this.configurationService.inspect<IConnectionProfileGroup[]>(GROUPS_CONFIG_KEY).user;
let groups = this.configurationService.inspect<IConnectionProfileGroup[]>(Constants.connectionGroupsArrayName).user;
groups = groups.map(g => {
if (g.id === source.id) {
g.parentId = target.id;
}
return g;
});
return this.configurationService.updateValue(GROUPS_CONFIG_KEY, groups, ConfigurationTarget.USER);
return this.configurationService.updateValue(Constants.connectionGroupsArrayName, groups, ConfigurationTarget.USER);
}
/**
@@ -283,10 +283,10 @@ export class ConnectionConfig {
* Moves the connection under the target group with the new ID.
*/
private changeGroupIdForConnectionInSettings(profile: ConnectionProfile, newGroupID: string, target: ConfigurationTarget = ConfigurationTarget.USER): Promise<void> {
let profiles = target === ConfigurationTarget.USER ? this.configurationService.inspect<IConnectionProfileStore[]>(CONNECTIONS_CONFIG_KEY).user :
this.configurationService.inspect<IConnectionProfileStore[]>(CONNECTIONS_CONFIG_KEY).workspace;
let profiles = target === ConfigurationTarget.USER ? this.configurationService.inspect<IConnectionProfileStore[]>(Constants.connectionsArrayName).user :
this.configurationService.inspect<IConnectionProfileStore[]>(Constants.connectionsArrayName).workspace;
if (profiles) {
if (profile.parent && profile.parent.id === UNSAVED_GROUP_ID) {
if (profile.parent && profile.parent.id === Constants.unsavedGroupId) {
profile.groupId = newGroupID;
profiles.push(ConnectionProfile.convertToProfileStore(this._capabilitiesService, profile));
} else {
@@ -298,7 +298,7 @@ export class ConnectionConfig {
});
}
return this.configurationService.updateValue(CONNECTIONS_CONFIG_KEY, profiles, target);
return this.configurationService.updateValue(Constants.connectionsArrayName, profiles, target);
} else {
return Promise.resolve();
}
@@ -320,12 +320,14 @@ export class ConnectionConfig {
}
public saveGroup(groups: IConnectionProfileGroup[], groupFullName: string, color: string, description: string): ISaveGroupResult {
let result: ISaveGroupResult;
let groupNames = ConnectionProfileGroup.getGroupFullNameParts(groupFullName);
return this.saveGroupInTree(groups, undefined, groupNames, color, description, 0);
result = this.saveGroupInTree(groups, undefined, groupNames, color, description, 0);
return result;
}
public editGroup(source: ConnectionProfileGroup): Promise<void> {
let groups = this.configurationService.inspect<IConnectionProfileGroup[]>(GROUPS_CONFIG_KEY).user;
let groups = this.configurationService.inspect<IConnectionProfileGroup[]>(Constants.connectionGroupsArrayName).user;
let sameNameGroup = groups ? groups.find(group => group.name === source.name && group.id !== source.id) : undefined;
if (sameNameGroup) {
let errMessage: string = nls.localize('invalidServerName', "A server group with the same name already exists.");
@@ -340,7 +342,7 @@ export class ConnectionConfig {
}
return g;
});
return this.configurationService.updateValue(GROUPS_CONFIG_KEY, groups, ConfigurationTarget.USER);
return this.configurationService.updateValue(Constants.connectionGroupsArrayName, groups, ConfigurationTarget.USER);
}
private isSameGroupName(group1: IConnectionProfileGroup, group2: IConnectionProfileGroup): boolean {

View File

@@ -17,6 +17,7 @@ import { ConnectionManagementInfo } from 'sql/platform/connection/common/connect
import * as Utils from 'sql/platform/connection/common/utils';
import * as Constants from 'sql/platform/connection/common/constants';
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
import { ICredentialsService } from 'sql/platform/credentials/common/credentialsService';
import * as ConnectionContracts from 'sql/parts/connection/common/connection';
import { ConnectionStatusManager } from 'sql/platform/connection/common/connectionStatusManager';
import { DashboardInput } from 'sql/parts/dashboard/dashboardInput';
@@ -43,7 +44,9 @@ import { Disposable } from 'vs/base/common/lifecycle';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IEditorService, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService';
import * as platform from 'vs/platform/registry/common/platform';
import { Memento } from 'vs/workbench/common/memento';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { ConnectionProfileGroup, IConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup';
import { Event, Emitter } from 'vs/base/common/event';
import { EditorPart } from 'vs/workbench/browser/parts/editor/editorPart';
@@ -74,13 +77,16 @@ export class ConnectionManagementService extends Disposable implements IConnecti
private _connectionGlobalStatus = new ConnectionGlobalStatus(this._statusBarService);
constructor(
private _connectionMemento: Memento,
private _connectionStore: ConnectionStore,
@IStorageService _storageService: IStorageService,
@IConnectionDialogService private _connectionDialogService: IConnectionDialogService,
@IServerGroupController private _serverGroupController: IServerGroupController,
@IInstantiationService private _instantiationService: IInstantiationService,
@IEditorService private _editorService: IEditorService,
@ITelemetryService private _telemetryService: ITelemetryService,
@IConfigurationService private _configurationService: IConfigurationService,
@ICredentialsService private _credentialsService: ICredentialsService,
@ICapabilitiesService private _capabilitiesService: ICapabilitiesService,
@IQuickInputService private _quickInputService: IQuickInputService,
@IEditorGroupsService private _editorGroupService: IEditorGroupsService,
@@ -91,8 +97,13 @@ export class ConnectionManagementService extends Disposable implements IConnecti
) {
super();
// _connectionMemento and _connectionStore are in constructor to enable this class to be more testable
if (!this._connectionMemento) {
this._connectionMemento = new Memento('ConnectionManagement', _storageService);
}
if (!this._connectionStore) {
this._connectionStore = _instantiationService.createInstance(ConnectionStore);
this._connectionStore = new ConnectionStore(this._connectionMemento,
this._configurationService, this._credentialsService, this._capabilitiesService);
}
// Register Statusbar item
@@ -124,6 +135,7 @@ export class ConnectionManagementService extends Disposable implements IConnecti
this.onConnectionChanged(() => this.refreshEditorTitles());
this.onConnect(() => this.refreshEditorTitles());
this.onDisconnect(() => this.refreshEditorTitles());
_storageService.onWillSaveState(() => this.shutdown());
}
public providerRegistered(providerId: string): boolean {
@@ -612,11 +624,11 @@ export class ConnectionManagementService extends Disposable implements IConnecti
}
public clearRecentConnection(connectionProfile: IConnectionProfile): void {
this._connectionStore.removeRecentConnection(connectionProfile);
this._connectionStore.removeConnectionToMemento(connectionProfile, Constants.recentConnections);
}
public getActiveConnections(providers?: string[]): ConnectionProfile[] {
return this._connectionStatusManager.getActiveConnectionProfiles(providers);
return this._connectionStatusManager.getActiveConnectionProfiles();
}
public getConnectionUriFromId(connectionId: string): string {
@@ -849,8 +861,8 @@ export class ConnectionManagementService extends Disposable implements IConnecti
* Add a connection to the active connections list.
*/
private tryAddActiveConnection(connectionManagementInfo: ConnectionManagementInfo, newConnection: IConnectionProfile, addToMru: boolean): void {
if (newConnection && addToMru) {
this._connectionStore.addRecentConnection(newConnection)
if (newConnection) {
this._connectionStore.addActiveConnection(newConnection, addToMru)
.then(() => {
connectionManagementInfo.connectHandler(true);
}, err => {
@@ -918,6 +930,11 @@ export class ConnectionManagementService extends Disposable implements IConnecti
public onIntelliSenseCacheComplete(handle: number, connectionUri: string): void {
}
private shutdown(): void {
this._connectionStore.clearActiveConnections();
this._connectionMemento.saveMemento();
}
public changeGroupIdForConnectionGroup(source: ConnectionProfileGroup, target: ConnectionProfileGroup): Promise<void> {
TelemetryUtils.addTelemetry(this._telemetryService, TelemetryKeys.MoveServerConnection);
return this._connectionStore.changeGroupIdForConnectionGroup(source, target);
@@ -1079,6 +1096,7 @@ export class ConnectionManagementService extends Disposable implements IConnecti
this.doDisconnect(uri, profile).then(result => {
if (result) {
this.addTelemetryForConnectionDisconnected(input);
this._connectionStore.removeActiveConnection(input);
this._connectionStatusManager.removeConnection(uri);
resolve();
} else {

View File

@@ -5,26 +5,21 @@
'use strict';
import { ReverseLookUpMap } from 'sql/base/common/map';
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
import { ConnectionConfig } from 'sql/platform/connection/common/connectionConfig';
import { fixupConnectionCredentials } from 'sql/platform/connection/common/connectionInfo';
import * as Constants from 'sql/platform/connection/common/constants';
import * as ConnInfo from 'sql/platform/connection/common/connectionInfo';
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
import { ConnectionProfileGroup, IConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { ICredentialsService } from 'sql/platform/credentials/common/credentialsService';
import { IConnectionConfig } from 'sql/platform/connection/common/iconnectionConfig';
import { ConnectionConfig } from 'sql/platform/connection/common/connectionConfig';
import { Memento } from 'vs/workbench/common/memento';
import { StorageScope } from 'vs/platform/storage/common/storage';
import { ConnectionProfileGroup, IConnectionProfileGroup } from './connectionProfileGroup';
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IStateService } from 'vs/platform/state/common/state';
const MAX_CONNECTIONS_DEFAULT = 25;
const RECENT_CONNECTIONS_STATE_KEY = 'recentConnections';
const CRED_PREFIX = 'Microsoft.SqlTools';
const CRED_SEPARATOR = '|';
const CRED_ID_PREFIX = 'id:';
const CRED_ITEMTYPE_PREFIX = 'itemtype:';
const CRED_PROFILE_USER = 'Profile';
/**
* Manages the connections list including saved profiles and the most recently used connections
*
@@ -32,15 +27,39 @@ const CRED_PROFILE_USER = 'Profile';
* @class ConnectionStore
*/
export class ConnectionStore {
private groupIdMap = new ReverseLookUpMap<string, string>();
private connectionConfig = new ConnectionConfig(this.configurationService, this.capabilitiesService);
private _memento: any;
private _groupIdToFullNameMap: { [groupId: string]: string };
private _groupFullNameToIdMap: { [groupId: string]: string };
constructor(
@IStateService private stateService: IStateService,
@IConfigurationService private configurationService: IConfigurationService,
@ICredentialsService private credentialService: ICredentialsService,
@ICapabilitiesService private capabilitiesService: ICapabilitiesService
private _context: Memento,
private _configurationService: IConfigurationService,
private _credentialService: ICredentialsService,
private _capabilitiesService: ICapabilitiesService,
private _connectionConfig?: IConnectionConfig
) {
if (_context) {
this._memento = this._context.getMemento(StorageScope.GLOBAL);
}
this._groupIdToFullNameMap = {};
this._groupFullNameToIdMap = {};
if (!this._connectionConfig) {
this._connectionConfig = new ConnectionConfig(this._configurationService, this._capabilitiesService);
}
}
public static get CRED_PREFIX(): string { return 'Microsoft.SqlTools'; }
public static get CRED_SEPARATOR(): string { return '|'; }
public static get CRED_ID_PREFIX(): string { return 'id:'; }
public static get CRED_ITEMTYPE_PREFIX(): string { return 'itemtype:'; }
public static get CRED_PROFILE_USER(): string { return 'Profile'; }
public formatCredentialIdForCred(connectionProfile: IConnectionProfile): string {
if (!connectionProfile) {
throw new Error('Missing Connection which is required');
}
let itemTypeString: string = ConnectionStore.CRED_PROFILE_USER;
return this.formatCredentialId(connectionProfile, itemTypeString);
}
/**
@@ -51,17 +70,26 @@ export class ConnectionStore {
* @param {string} itemType type of the item (MRU or Profile) - optional
* @returns {string} formatted string with server, DB and username
*/
private formatCredentialId(connectionProfile: IConnectionProfile, itemType?: string): string {
public formatCredentialId(connectionProfile: IConnectionProfile, itemType?: string): string {
let connectionProfileInstance: ConnectionProfile = ConnectionProfile.fromIConnectionProfile(
this.capabilitiesService, connectionProfile);
let cred: string[] = [CRED_PREFIX];
this._capabilitiesService, connectionProfile);
if (!connectionProfileInstance.getConnectionInfoId()) {
throw new Error('Missing Id, which is required');
}
let cred: string[] = [ConnectionStore.CRED_PREFIX];
if (!itemType) {
itemType = CRED_PROFILE_USER;
itemType = ConnectionStore.CRED_PROFILE_USER;
}
cred.push(CRED_ITEMTYPE_PREFIX.concat(itemType));
cred.push(CRED_ID_PREFIX.concat(connectionProfileInstance.getConnectionInfoId()));
return cred.join(CRED_SEPARATOR);
ConnectionStore.pushIfNonEmpty(itemType, ConnectionStore.CRED_ITEMTYPE_PREFIX, cred);
ConnectionStore.pushIfNonEmpty(connectionProfileInstance.getConnectionInfoId(), ConnectionStore.CRED_ID_PREFIX, cred);
return cred.join(ConnectionStore.CRED_SEPARATOR);
}
private static pushIfNonEmpty(value: string, prefix: string, arr: string[]): void {
if (value) {
arr.push(prefix.concat(value));
}
}
/**
@@ -70,7 +98,7 @@ export class ConnectionStore {
*/
public isPasswordRequired(connection: IConnectionProfile): boolean {
if (connection) {
let connectionProfile = ConnectionProfile.fromIConnectionProfile(this.capabilitiesService, connection);
let connectionProfile = ConnectionProfile.fromIConnectionProfile(this._capabilitiesService, connection);
return connectionProfile.isPasswordRequired();
} else {
return false;
@@ -78,20 +106,28 @@ export class ConnectionStore {
}
public addSavedPassword(credentialsItem: IConnectionProfile): Promise<{ profile: IConnectionProfile, savedCred: boolean }> {
if (credentialsItem.savePassword && this.isPasswordRequired(credentialsItem) && !credentialsItem.password) {
let credentialId = this.formatCredentialId(credentialsItem, CRED_PROFILE_USER);
return this.credentialService.readCredential(credentialId)
.then(savedCred => {
if (savedCred) {
credentialsItem.password = savedCred.password;
credentialsItem.options['password'] = savedCred.password;
}
return { profile: credentialsItem, savedCred: !!savedCred };
});
} else {
// No need to look up the password
return Promise.resolve({ profile: credentialsItem, savedCred: credentialsItem.savePassword });
}
let self = this;
return new Promise<{ profile: IConnectionProfile, savedCred: boolean }>((resolve, reject) => {
if (credentialsItem.savePassword && this.isPasswordRequired(credentialsItem)
&& !credentialsItem.password) {
let credentialId = this.formatCredentialIdForCred(credentialsItem);
self._credentialService.readCredential(credentialId)
.then(savedCred => {
if (savedCred) {
credentialsItem.password = savedCred.password;
credentialsItem.options['password'] = savedCred.password;
}
resolve({ profile: credentialsItem, savedCred: !!savedCred });
},
reason => {
reject(reason);
});
} else {
// No need to look up the password
resolve({ profile: credentialsItem, savedCred: credentialsItem.savePassword });
}
});
}
/**
@@ -103,20 +139,34 @@ export class ConnectionStore {
* @returns {Promise<IConnectionProfile>} a Promise that returns the original profile, for help in chaining calls
*/
public saveProfile(profile: IConnectionProfile, forceWritePlaintextPassword?: boolean): Promise<IConnectionProfile> {
// Add the profile to the saved list, taking care to clear out the password field if necessary
let savedProfile = forceWritePlaintextPassword ? profile : this.getProfileWithoutPassword(profile);
return this.saveProfileToConfig(savedProfile)
.then(savedConnectionProfile => {
profile.groupId = savedConnectionProfile.groupId;
profile.id = savedConnectionProfile.id;
// Only save if we successfully added the profile
return this.saveProfilePasswordIfNeeded(profile);
}).then(() => {
// Add necessary default properties before returning
// this is needed to support immediate connections
fixupConnectionCredentials(profile);
return profile;
});
const self = this;
return new Promise<IConnectionProfile>((resolve, reject) => {
// Add the profile to the saved list, taking care to clear out the password field if necessary
let savedProfile: IConnectionProfile;
if (forceWritePlaintextPassword) {
savedProfile = profile;
} else {
savedProfile = this.getProfileWithoutPassword(profile);
}
self.saveProfileToConfig(savedProfile)
.then(savedConnectionProfile => {
profile.groupId = savedConnectionProfile.groupId;
profile.id = savedConnectionProfile.id;
// Only save if we successfully added the profile
return self.saveProfilePasswordIfNeeded(profile);
// And resolve / reject at the end of the process
}, err => {
reject(err);
}).then(resolved => {
// Add necessary default properties before returning
// this is needed to support immediate connections
ConnInfo.fixupConnectionCredentials(profile);
resolve(profile);
}, err => {
reject(err);
});
});
}
/**
@@ -126,15 +176,29 @@ export class ConnectionStore {
* @returns {Promise<string>} a Promise that returns the id of connection group
*/
public saveProfileGroup(profile: IConnectionProfileGroup): Promise<string> {
return this.connectionConfig.addGroup(profile);
const self = this;
return new Promise<string>((resolve, reject) => {
self._connectionConfig.addGroup(profile).then(groupId => {
resolve(groupId);
}).catch(error => {
reject(error);
});
});
}
private saveProfileToConfig(profile: IConnectionProfile): Promise<IConnectionProfile> {
if (profile.saveProfile) {
return this.connectionConfig.addConnection(profile);
} else {
return Promise.resolve(profile);
}
const self = this;
return new Promise<IConnectionProfile>((resolve, reject) => {
if (profile.saveProfile) {
self._connectionConfig.addConnection(profile).then(savedProfile => {
resolve(savedProfile);
}).catch(error => {
reject(error);
});
} else {
resolve(profile);
}
});
}
/**
@@ -144,8 +208,12 @@ export class ConnectionStore {
* @returns {azdata.ConnectionInfo} the array of connections, empty if none are found
*/
public getRecentlyUsedConnections(providers?: string[]): ConnectionProfile[] {
let configValues = this.stateService.getItem<IConnectionProfile[]>(RECENT_CONNECTIONS_STATE_KEY, []).filter(c => !!c);
let configValues: IConnectionProfile[] = this._memento[Constants.recentConnections];
if (!configValues) {
configValues = [];
}
configValues = configValues.filter(c => !!(c));
if (providers && providers.length > 0) {
configValues = configValues.filter(c => providers.includes(c.providerName));
}
@@ -155,7 +223,7 @@ export class ConnectionStore {
private convertConfigValuesToConnectionProfiles(configValues: IConnectionProfile[]): ConnectionProfile[] {
return configValues.map(c => {
if (c) {
let connectionProfile = new ConnectionProfile(this.capabilitiesService, c);
let connectionProfile = new ConnectionProfile(this._capabilitiesService, c);
if (connectionProfile.saveProfile) {
if (!connectionProfile.groupFullName && connectionProfile.groupId) {
connectionProfile.groupFullName = this.getGroupFullName(connectionProfile.groupId);
@@ -173,9 +241,24 @@ export class ConnectionStore {
});
}
/**
* Gets the list of active connections. These will not include the password - a separate call to
* {addSavedPassword} is needed to fill that before connecting
*
* @returns {azdata.ConnectionInfo} the array of connections, empty if none are found
*/
public getActiveConnections(): ConnectionProfile[] {
let configValues: IConnectionProfile[] = this._memento[Constants.activeConnections];
if (!configValues) {
configValues = [];
}
return this.convertConfigValuesToConnectionProfiles(configValues);
}
public getProfileWithoutPassword(conn: IConnectionProfile): ConnectionProfile {
if (conn) {
let savedConn: ConnectionProfile = ConnectionProfile.fromIConnectionProfile(this.capabilitiesService, conn);
let savedConn: ConnectionProfile = ConnectionProfile.fromIConnectionProfile(this._capabilitiesService, conn);
savedConn = savedConn.withoutPassword();
return savedConn;
@@ -193,35 +276,71 @@ export class ConnectionStore {
* @param {boolean} addToMru Whether to add this connection to the MRU
* @returns {Promise<void>} a Promise that returns when the connection was saved
*/
public addRecentConnection(conn: IConnectionProfile): Promise<void> {
let maxConnections = this.getMaxRecentConnectionsCount();
return this.addConnectionToState(conn, RECENT_CONNECTIONS_STATE_KEY, maxConnections, conn.savePassword);
}
private addConnectionToState(conn: IConnectionProfile, key: string, maxConnections?: number, savePassword?: boolean): Promise<void> {
// Get all profiles
let configValues = this.getConnectionsFromState(key);
let configToSave = this.addToConnectionList(conn, configValues);
if (maxConnections) {
// Remove last element if needed
if (configToSave.length > maxConnections) {
configToSave = configToSave.slice(0, maxConnections);
}
public async addActiveConnection(conn: IConnectionProfile, addToMru: boolean): Promise<void> {
if (addToMru) {
await this.addConnectionToMru(conn);
}
// Only add connections we don't already know about
if (!this.getActiveConnections().some(existingConn => existingConn.id === conn.id)) {
await this.addConnectionToMemento(conn, Constants.activeConnections, undefined, conn.savePassword);
}
this.stateService.setItem(key, configToSave);
return savePassword ? this.doSavePassword(conn).then() : Promise.resolve();
}
private removeConnectionFromState(conn: IConnectionProfile, key: string): void {
// Get all profiles
let configValues = this.getConnectionsFromState(key);
let configToSave = this.removeFromConnectionList(conn, configValues);
this.stateService.setItem(key, configToSave);
/**
* Adds the specified connection to the MRU list
* @param conn The connection to add
*/
private async addConnectionToMru(conn: IConnectionProfile): Promise<void> {
let maxConnections = this.getMaxRecentConnectionsCount();
if (ConnectionProfile.isConnectionToDefaultDb(conn)) {
conn.databaseName = '';
}
await this.addConnectionToMemento(conn, Constants.recentConnections, maxConnections);
}
private getConnectionsFromState(mementoKey: string): ConnectionProfile[] {
return this.convertConfigValuesToConnectionProfiles(this.stateService.getItem<IConnectionProfile[]>(mementoKey, []));
public addConnectionToMemento(conn: IConnectionProfile, mementoKey: string, maxConnections?: number, savePassword?: boolean): Promise<void> {
const self = this;
return new Promise<void>((resolve, reject) => {
// Get all profiles
let configValues = self.getConnectionsFromMemento(mementoKey);
let configToSave = this.addToConnectionList(conn, configValues);
if (maxConnections) {
// Remove last element if needed
if (configToSave.length > maxConnections) {
configToSave = configToSave.slice(0, maxConnections);
}
}
self._memento[mementoKey] = configToSave;
if (savePassword) {
self.doSavePassword(conn).then(result => {
resolve(undefined);
});
} else {
resolve(undefined);
}
});
}
public removeConnectionToMemento(conn: IConnectionProfile, mementoKey: string): Promise<void> {
const self = this;
return new Promise<void>((resolve, reject) => {
// Get all profiles
let configValues = self.getConnectionsFromMemento(mementoKey);
let configToSave = this.removeFromConnectionList(conn, configValues);
self._memento[mementoKey] = configToSave;
resolve(undefined);
});
}
public getConnectionsFromMemento(mementoKey: string): ConnectionProfile[] {
let configValues: IConnectionProfile[] = this._memento[mementoKey];
if (!configValues) {
configValues = [];
}
return this.convertConfigValuesToConnectionProfiles(configValues);
}
private addToConnectionList(conn: IConnectionProfile, list: ConnectionProfile[]): IConnectionProfile[] {
@@ -239,7 +358,11 @@ export class ConnectionStore {
list.unshift(savedProfile);
return list.filter(n => n !== undefined).map(c => c.toIConnectionProfile());
let newList = list.map(c => {
let connectionProfile = c ? c.toIConnectionProfile() : undefined;
return connectionProfile;
});
return newList.filter(n => n !== undefined);
}
private removeFromConnectionList(conn: IConnectionProfile, list: ConnectionProfile[]): IConnectionProfile[] {
@@ -255,18 +378,37 @@ export class ConnectionStore {
return !equal;
});
return list.filter(n => n !== undefined).map(c => c.toIConnectionProfile());
let newList = list.map(c => {
let connectionProfile = c ? c.toIConnectionProfile() : undefined;
return connectionProfile;
});
return newList.filter(n => n !== undefined);
}
/**
* Clear all recently used connections from the MRU list.
*/
public clearRecentlyUsed(): void {
this.stateService.setItem(RECENT_CONNECTIONS_STATE_KEY, []);
this._memento[Constants.recentConnections] = [];
}
public removeRecentConnection(conn: IConnectionProfile): void {
this.removeConnectionFromState(conn, RECENT_CONNECTIONS_STATE_KEY);
public clearFromMemento(name: string): void {
this._memento[name] = [];
}
/**
* Clear all active connections from the MRU list.
*/
public clearActiveConnections(): void {
this._memento[Constants.activeConnections] = [];
}
/**
* Remove a connection profile from the active connections list.
*/
public removeActiveConnection(conn: IConnectionProfile): Promise<void> {
return this.removeConnectionToMemento(conn, Constants.activeConnections);
}
private saveProfilePasswordIfNeeded(profile: IConnectionProfile): Promise<boolean> {
@@ -277,23 +419,32 @@ export class ConnectionStore {
}
private doSavePassword(conn: IConnectionProfile): Promise<boolean> {
if (conn.password) {
let credentialId = this.formatCredentialId(conn);
return this.credentialService.saveCredential(credentialId, conn.password);
} else {
return Promise.resolve(true);
}
let self = this;
return new Promise<boolean>((resolve, reject) => {
if (conn.password) {
let credentialId = this.formatCredentialId(conn);
self._credentialService.saveCredential(credentialId, conn.password)
.then((result) => {
resolve(result);
}, reason => {
// Bubble up error if there was a problem executing the set command
reject(reason);
});
} else {
resolve(true);
}
});
}
public getConnectionProfileGroups(withoutConnections?: boolean, providers?: string[]): ConnectionProfileGroup[] {
let profilesInConfiguration: ConnectionProfile[];
if (!withoutConnections) {
profilesInConfiguration = this.connectionConfig.getConnections(true);
profilesInConfiguration = this._connectionConfig.getConnections(true);
if (providers && providers.length > 0) {
profilesInConfiguration = profilesInConfiguration.filter(x => providers.includes(x.providerName));
}
}
let groups = this.connectionConfig.getAllGroups();
let groups = this._connectionConfig.getAllGroups();
let connectionProfileGroups = this.convertToConnectionGroup(groups, profilesInConfiguration, undefined);
return connectionProfileGroups;
@@ -328,53 +479,74 @@ export class ConnectionStore {
}
public getGroupFromId(groupId: string): IConnectionProfileGroup {
let groups = this.connectionConfig.getAllGroups();
let groups = this._connectionConfig.getAllGroups();
return groups.find(group => group.id === groupId);
}
private getMaxRecentConnectionsCount(): number {
return this.configurationService.getValue('sql.maxRecentConnections') || MAX_CONNECTIONS_DEFAULT;
let config = this._configurationService.getValue(Constants.sqlConfigSectionName);
let maxConnections: number = config[Constants.configMaxRecentConnections];
if (typeof (maxConnections) !== 'number' || maxConnections <= 0) {
maxConnections = MAX_CONNECTIONS_DEFAULT;
}
return maxConnections;
}
public editGroup(group: ConnectionProfileGroup): Promise<void> {
return this.connectionConfig.editGroup(group).then();
public editGroup(group: ConnectionProfileGroup): Promise<any> {
const self = this;
return new Promise<string>((resolve, reject) => {
self._connectionConfig.editGroup(group).then(() => {
resolve(null);
}).catch(error => {
reject(error);
});
});
}
public deleteConnectionFromConfiguration(connection: ConnectionProfile): Promise<void> {
return this.connectionConfig.deleteConnection(connection);
return this._connectionConfig.deleteConnection(connection);
}
public deleteGroupFromConfiguration(group: ConnectionProfileGroup): Promise<void> {
return this.connectionConfig.deleteGroup(group);
return this._connectionConfig.deleteGroup(group);
}
public changeGroupIdForConnectionGroup(source: ConnectionProfileGroup, target: ConnectionProfileGroup): Promise<void> {
return this.connectionConfig.changeGroupIdForConnectionGroup(source, target);
return this._connectionConfig.changeGroupIdForConnectionGroup(source, target);
}
public canChangeConnectionConfig(profile: ConnectionProfile, newGroupID: string): boolean {
return this.connectionConfig.canChangeConnectionConfig(profile, newGroupID);
return this._connectionConfig.canChangeConnectionConfig(profile, newGroupID);
}
public changeGroupIdForConnection(source: ConnectionProfile, targetGroupId: string): Promise<void> {
return this.connectionConfig.changeGroupIdForConnection(source, targetGroupId).then();
return new Promise<void>((resolve, reject) => {
this._connectionConfig.changeGroupIdForConnection(source, targetGroupId).then(() => {
resolve();
}, (error => {
reject(error);
}));
});
}
private addGroupFullNameToMap(groupId: string, groupFullName: string): void {
if (groupId) {
this.groupIdMap.set(groupId, groupFullName);
this._groupIdToFullNameMap[groupId] = groupFullName;
}
if (groupFullName !== undefined) {
this.groupIdMap.set(groupFullName.toUpperCase(), groupId);
this._groupFullNameToIdMap[groupFullName.toUpperCase()] = groupId;
}
}
private getGroupFullName(groupId: string): string {
if (!this.groupIdMap.has(groupId)) {
if (groupId in this._groupIdToFullNameMap) {
return this._groupIdToFullNameMap[groupId];
} else {
// Load the cache
this.getConnectionProfileGroups(true);
}
return this.groupIdMap.get(groupId);
return this._groupIdToFullNameMap[groupId];
}
private getGroupId(groupFullName: string): string {
@@ -382,10 +554,14 @@ export class ConnectionStore {
groupFullName = '';
}
let key = groupFullName.toUpperCase();
if (!this.groupIdMap.reverseHas(key)) {
let result: string = '';
if (key in this._groupFullNameToIdMap) {
result = this._groupFullNameToIdMap[key];
} else {
// Load the cache
this.getConnectionProfileGroups(true);
result = this._groupFullNameToIdMap[key];
}
return this.groupIdMap.reverseGet(key);
return result;
}
}

View File

@@ -7,9 +7,19 @@
export const sqlConfigSectionName = 'sql';
export const outputChannelName = 'MSSQL';
export const connectionsArrayName = 'datasource.connections';
export const connectionGroupsArrayName = 'datasource.connectionGroups';
/**Unsaved connections Id */
export const unsavedGroupId = 'unsaved';
/* Memento constants */
export const activeConnections = 'ACTIVE_CONNECTIONS';
export const recentConnections = 'RECENT_CONNECTIONS';
export const capabilitiesOptions = 'OPTIONS_METADATA';
export const configMaxRecentConnections = 'maxRecentConnections';
export const mssqlProviderName = 'MSSQL';
export const anyProviderName = '*';
export const connectionProviderContextKey = 'connectionProvider';
@@ -24,5 +34,3 @@ export const passwordChars = '***************';
export const sqlLogin = 'SqlLogin';
export const integrated = 'Integrated';
export const azureMFA = 'AzureMFA';
export const UNSAVED_GROUP_ID = 'unsaved';

View File

@@ -0,0 +1,29 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { IConnectionProfileGroup, ConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup';
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
/**
* Interface for a configuration file that stores connection profiles.
*
* @export
* @interface IConnectionConfig
*/
export interface IConnectionConfig {
addConnection(profile: IConnectionProfile): Promise<IConnectionProfile>;
addGroup(profileGroup: IConnectionProfileGroup): Promise<string>;
getConnections(getWorkspaceConnections: boolean): ConnectionProfile[];
getAllGroups(): IConnectionProfileGroup[];
changeGroupIdForConnectionGroup(source: ConnectionProfileGroup, target: ConnectionProfileGroup): Promise<void>;
changeGroupIdForConnection(source: ConnectionProfile, targetGroupId: string): Promise<void>;
editGroup(group: ConnectionProfileGroup): Promise<void>;
deleteConnection(profile: ConnectionProfile): Promise<void>;
deleteGroup(group: ConnectionProfileGroup): Promise<void>;
canChangeConnectionConfig(profile: ConnectionProfile, newGroupID: string): boolean;
}

View File

@@ -5,20 +5,23 @@
'use strict';
import * as assert from 'assert';
import * as azdata from 'azdata';
import { ICapabilitiesService, ProviderFeatures } from 'sql/platform/capabilities/common/capabilitiesService';
import * as TypeMoq from 'typemoq';
import { ConnectionConfig, ISaveGroupResult } from 'sql/platform/connection/common/connectionConfig';
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
import { ConnectionProfileGroup, IConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup';
import { IConnectionProfile, IConnectionProfileStore } from 'sql/platform/connection/common/interfaces';
import { TestConfigurationService } from 'sql/platform/connection/test/common/testConfigurationService';
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import * as Constants from 'sql/platform/connection/common/constants';
import { IConnectionProfileGroup, ConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup';
import * as assert from 'assert';
import { ProviderFeatures, ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
import * as azdata from 'azdata';
import { Emitter } from 'vs/base/common/event';
import { ConnectionOptionSpecialType, ServiceOptionType } from 'sql/workbench/api/common/sqlExtHostTypes';
import { CapabilitiesTestService } from 'sqltest/stubs/capabilitiesTestService';
import * as TypeMoq from 'typemoq';
import { Emitter } from 'vs/base/common/event';
import { deepClone, deepFreeze } from 'vs/base/common/objects';
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { TestConfigurationService } from 'sql/platform/connection/test/common/testConfigurationService';
import { deepFreeze, deepClone } from 'vs/base/common/objects';
import { isUndefinedOrNull } from 'vs/base/common/types';
import { ConnectionManagementInfo } from 'sql/platform/connection/common/connectionManagementInfo';
suite('ConnectionConfig', () => {
let capabilitiesService: TypeMoq.Mock<ICapabilitiesService>;
@@ -231,9 +234,9 @@ suite('ConnectionConfig', () => {
test('getAllGroups should merge user and workspace settings correctly', () => {
let configurationService = new TestConfigurationService();
configurationService.updateValue('datasource.connectionGroups', deepClone(testGroups).slice(0, 3), ConfigurationTarget.USER);
configurationService.updateValue(Constants.connectionGroupsArrayName, deepClone(testGroups).slice(0, 3), ConfigurationTarget.USER);
// we intentionally overlap these values with the expectation that the function to should return each group once
configurationService.updateValue('datasource.connectionGroups', deepClone(testGroups).slice(2, testGroups.length), ConfigurationTarget.WORKSPACE);
configurationService.updateValue(Constants.connectionGroupsArrayName, deepClone(testGroups).slice(2, testGroups.length), ConfigurationTarget.WORKSPACE);
let config = new ConnectionConfig(configurationService, capabilitiesService.object);
let allGroups = config.getAllGroups();
@@ -242,7 +245,7 @@ suite('ConnectionConfig', () => {
assert.ok(groupsAreEqual(allGroups, testGroups), 'the groups returned did not match expectation');
});
test('addConnection should add the new profile to user settings', async () => {
test('addConnection should add the new profile to user settings if does not exist', async () => {
let newProfile: IConnectionProfile = {
serverName: 'new server',
databaseName: 'database',
@@ -262,15 +265,15 @@ suite('ConnectionConfig', () => {
};
let configurationService = new TestConfigurationService();
configurationService.updateValue('datasource.connectionGroups', deepClone(testGroups), ConfigurationTarget.USER);
configurationService.updateValue('datasource.connections', deepClone(testConnections), ConfigurationTarget.USER);
configurationService.updateValue(Constants.connectionGroupsArrayName, deepClone(testGroups), ConfigurationTarget.USER);
configurationService.updateValue(Constants.connectionsArrayName, deepClone(testConnections), ConfigurationTarget.USER);
let connectionProfile = new ConnectionProfile(capabilitiesService.object, newProfile);
connectionProfile.options['databaseDisplayName'] = 'database';
let config = new ConnectionConfig(configurationService, capabilitiesService.object);
let savedConnectionProfile = await config.addConnection(connectionProfile);
assert.ok(!!savedConnectionProfile.id);
assert.equal(configurationService.inspect<IConnectionProfileStore[]>('datasource.connections').user.length, testConnections.length + 1);
assert.ok(!isUndefinedOrNull(savedConnectionProfile.id));
assert.equal(configurationService.inspect<IConnectionProfileStore[]>(Constants.connectionsArrayName).user.length, testConnections.length + 1);
});
test('addConnection should not add the new profile to user settings if already exists', async () => {
@@ -294,8 +297,8 @@ suite('ConnectionConfig', () => {
};
let configurationService = new TestConfigurationService();
configurationService.updateValue('datasource.connectionGroups', deepClone(testGroups), ConfigurationTarget.USER);
configurationService.updateValue('datasource.connections', deepClone(testConnections), ConfigurationTarget.USER);
configurationService.updateValue(Constants.connectionGroupsArrayName, deepClone(testGroups), ConfigurationTarget.USER);
configurationService.updateValue(Constants.connectionsArrayName, deepClone(testConnections), ConfigurationTarget.USER);
let connectionProfile = new ConnectionProfile(capabilitiesService.object, newProfile);
connectionProfile.options['databaseDisplayName'] = existingConnection.options['databaseName'];
@@ -304,7 +307,7 @@ suite('ConnectionConfig', () => {
let savedConnectionProfile = await config.addConnection(connectionProfile);
assert.equal(savedConnectionProfile.id, existingConnection.id);
assert.equal(configurationService.inspect<IConnectionProfileStore[]>('datasource.connections').user.length, testConnections.length);
assert.equal(configurationService.inspect<IConnectionProfileStore[]>(Constants.connectionsArrayName).user.length, testConnections.length);
});
test('addConnection should add the new group to user settings if does not exist', async () => {
@@ -327,21 +330,21 @@ suite('ConnectionConfig', () => {
};
let configurationService = new TestConfigurationService();
configurationService.updateValue('datasource.connectionGroups', deepClone(testGroups), ConfigurationTarget.USER);
configurationService.updateValue('datasource.connections', deepClone(testConnections), ConfigurationTarget.USER);
configurationService.updateValue(Constants.connectionGroupsArrayName, deepClone(testGroups), ConfigurationTarget.USER);
configurationService.updateValue(Constants.connectionsArrayName, deepClone(testConnections), ConfigurationTarget.USER);
let connectionProfile = new ConnectionProfile(capabilitiesService.object, newProfile);
let config = new ConnectionConfig(configurationService, capabilitiesService.object);
await config.addConnection(connectionProfile);
assert.equal(configurationService.inspect<IConnectionProfileStore[]>('datasource.connections').user.length, testConnections.length + 1);
assert.equal(configurationService.inspect<IConnectionProfileStore[]>('datasource.connectionGroups').user.length, testGroups.length + 1);
assert.equal(configurationService.inspect<IConnectionProfileStore[]>(Constants.connectionsArrayName).user.length, testConnections.length + 1);
assert.equal(configurationService.inspect<IConnectionProfileStore[]>(Constants.connectionGroupsArrayName).user.length, testGroups.length + 1);
});
test('getConnections should return connections from user and workspace settings given getWorkspaceConnections set to true', () => {
let configurationService = new TestConfigurationService();
configurationService.updateValue('datasource.connections', deepClone(testConnections).slice(0, 1), ConfigurationTarget.USER);
configurationService.updateValue('datasource.connections', deepClone(testConnections).slice(1, testConnections.length), ConfigurationTarget.WORKSPACE);
configurationService.updateValue(Constants.connectionsArrayName, deepClone(testConnections).slice(0, 1), ConfigurationTarget.USER);
configurationService.updateValue(Constants.connectionsArrayName, deepClone(testConnections).slice(1, testConnections.length), ConfigurationTarget.WORKSPACE);
let config = new ConnectionConfig(configurationService, capabilitiesService.object);
let allConnections = config.getConnections(true);
@@ -350,8 +353,8 @@ suite('ConnectionConfig', () => {
test('getConnections should return connections from user settings given getWorkspaceConnections set to false', () => {
let configurationService = new TestConfigurationService();
configurationService.updateValue('datasource.connections', deepClone(testConnections).slice(0, 2), ConfigurationTarget.USER);
configurationService.updateValue('datasource.connections', deepClone(testConnections).slice(2, testConnections.length), ConfigurationTarget.WORKSPACE);
configurationService.updateValue(Constants.connectionsArrayName, deepClone(testConnections).slice(0, 2), ConfigurationTarget.USER);
configurationService.updateValue(Constants.connectionsArrayName, deepClone(testConnections).slice(2, testConnections.length), ConfigurationTarget.WORKSPACE);
let config = new ConnectionConfig(configurationService, capabilitiesService.object);
let allConnections = config.getConnections(false);
@@ -368,8 +371,8 @@ suite('ConnectionConfig', () => {
return c;
});
let configurationService = new TestConfigurationService();
configurationService.updateValue('datasource.connections', userConnections, ConfigurationTarget.USER);
configurationService.updateValue('datasource.connections', workspaceConnections, ConfigurationTarget.WORKSPACE);
configurationService.updateValue(Constants.connectionsArrayName, userConnections, ConfigurationTarget.USER);
configurationService.updateValue(Constants.connectionsArrayName, workspaceConnections, ConfigurationTarget.WORKSPACE);
let config = new ConnectionConfig(configurationService, capabilitiesService.object);
let allConnections = config.getConnections(false);
@@ -378,7 +381,7 @@ suite('ConnectionConfig', () => {
let userConnection = testConnections.find(u => u.options['serverName'] === connection.serverName);
if (userConnection !== undefined) {
assert.notEqual(connection.id, connection.getOptionsKey());
assert.ok(!!connection.id);
assert.ok(!isUndefinedOrNull(connection.id));
} else {
let workspaceConnection = workspaceConnections.find(u => u.options['serverName'] === connection.serverName);
assert.notEqual(connection.id, connection.getOptionsKey());
@@ -394,7 +397,7 @@ suite('ConnectionConfig', () => {
let color: string = 'red';
let result: ISaveGroupResult = config.saveGroup(groups, newGroups, color, newGroups);
assert.ok(!!result);
assert.ok(!isUndefinedOrNull(result));
assert.equal(result.groups.length, testGroups.length + 2, 'The result groups length is invalid');
let newGroup = result.groups.find(g => g.name === 'new-group2');
assert.equal(result.newGroupId, newGroup.id, 'The groups id is invalid');
@@ -407,7 +410,7 @@ suite('ConnectionConfig', () => {
let color: string = 'red';
let result: ISaveGroupResult = config.saveGroup(groups, newGroups, color, newGroups);
assert.ok(!!result);
assert.ok(!isUndefinedOrNull(result));
assert.equal(result.groups.length, testGroups.length + 1, 'The result groups length is invalid');
let newGroup = result.groups.find(g => g.name === 'g2-5');
assert.equal(result.newGroupId, newGroup.id, 'The groups id is invalid');
@@ -420,7 +423,7 @@ suite('ConnectionConfig', () => {
let color: string = 'red';
let result: ISaveGroupResult = config.saveGroup(groups, newGroups, color, newGroups);
assert.ok(!!result);
assert.ok(!isUndefinedOrNull(result));
assert.equal(result.groups.length, testGroups.length, 'The result groups length is invalid');
let newGroup = result.groups.find(g => g.name === 'g2-1');
assert.equal(result.newGroupId, newGroup.id, 'The groups id is invalid');
@@ -445,7 +448,7 @@ suite('ConnectionConfig', () => {
connectionName: undefined
};
let configurationService = new TestConfigurationService();
configurationService.updateValue('datasource.connections', deepClone(testConnections), ConfigurationTarget.USER);
configurationService.updateValue(Constants.connectionsArrayName, deepClone(testConnections), ConfigurationTarget.USER);
let connectionProfile = new ConnectionProfile(capabilitiesService.object, newProfile);
connectionProfile.options['databaseDisplayName'] = 'database';
@@ -453,7 +456,7 @@ suite('ConnectionConfig', () => {
let config = new ConnectionConfig(configurationService, capabilitiesService.object);
await config.deleteConnection(connectionProfile);
assert.equal(configurationService.inspect<IConnectionProfileStore[]>('datasource.connections').user.length, testConnections.length - 1);
assert.equal(configurationService.inspect<IConnectionProfileStore[]>(Constants.connectionsArrayName).user.length, testConnections.length - 1);
});
test('deleteConnectionGroup should remove the children connections and subgroups from config', async () => {
@@ -475,8 +478,8 @@ suite('ConnectionConfig', () => {
connectionName: undefined
};
let configurationService = new TestConfigurationService();
configurationService.updateValue('datasource.connections', deepClone(testConnections), ConfigurationTarget.USER);
configurationService.updateValue('datasource.connectionGroups', deepClone(testGroups), ConfigurationTarget.USER);
configurationService.updateValue(Constants.connectionsArrayName, deepClone(testConnections), ConfigurationTarget.USER);
configurationService.updateValue(Constants.connectionGroupsArrayName, deepClone(testGroups), ConfigurationTarget.USER);
let connectionProfile = new ConnectionProfile(capabilitiesService.object, newProfile);
connectionProfile.options['databaseDisplayName'] = 'database';
@@ -489,8 +492,8 @@ suite('ConnectionConfig', () => {
let config = new ConnectionConfig(configurationService, capabilitiesService.object);
await config.deleteGroup(connectionProfileGroup);
assert.equal(configurationService.inspect<IConnectionProfileStore[]>('datasource.connections').user.length, testConnections.length - 1);
assert.equal(configurationService.inspect<IConnectionProfileGroup[]>('datasource.connectionGroups').user.length, testGroups.length - 2);
assert.equal(configurationService.inspect<IConnectionProfileStore[]>(Constants.connectionsArrayName).user.length, testConnections.length - 1);
assert.equal(configurationService.inspect<IConnectionProfileGroup[]>(Constants.connectionGroupsArrayName).user.length, testGroups.length - 2);
});
test('deleteConnection should not throw error for connection not in config', async () => {
@@ -512,34 +515,34 @@ suite('ConnectionConfig', () => {
connectionName: undefined
};
let configurationService = new TestConfigurationService();
configurationService.updateValue('datasource.connections', deepClone(testConnections), ConfigurationTarget.USER);
configurationService.updateValue(Constants.connectionsArrayName, deepClone(testConnections), ConfigurationTarget.USER);
let connectionProfile = new ConnectionProfile(capabilitiesService.object, newProfile);
let config = new ConnectionConfig(configurationService, capabilitiesService.object);
await config.deleteConnection(connectionProfile);
assert.equal(configurationService.inspect<IConnectionProfileStore[]>('datasource.connections').user.length, testConnections.length);
assert.equal(configurationService.inspect<IConnectionProfileStore[]>(Constants.connectionsArrayName).user.length, testConnections.length);
});
test('renameGroup should change group name', async () => {
let configurationService = new TestConfigurationService();
configurationService.updateValue('datasource.connectionGroups', deepClone(testGroups), ConfigurationTarget.USER);
configurationService.updateValue(Constants.connectionGroupsArrayName, deepClone(testGroups), ConfigurationTarget.USER);
let connectionProfileGroup = new ConnectionProfileGroup('g-renamed', undefined, 'g2', undefined, undefined);
let config = new ConnectionConfig(configurationService, capabilitiesService.object);
await config.editGroup(connectionProfileGroup);
let editedGroups = configurationService.inspect<IConnectionProfileGroup[]>('datasource.connectionGroups').user;
let editedGroups = configurationService.inspect<IConnectionProfileGroup[]>(Constants.connectionGroupsArrayName).user;
assert.equal(editedGroups.length, testGroups.length);
let editedGroup = editedGroups.find(group => group.id === 'g2');
assert.ok(!!editedGroup);
assert.ok(!isUndefinedOrNull(editedGroup));
assert.equal(editedGroup.name, 'g-renamed');
});
test('edit group should throw if there is a confliction', async () => {
let configurationService = new TestConfigurationService();
configurationService.updateValue('datasource.connectionGroups', deepClone(testGroups), ConfigurationTarget.USER);
configurationService.updateValue(Constants.connectionGroupsArrayName, deepClone(testGroups), ConfigurationTarget.USER);
let sameNameGroup = new ConnectionProfileGroup('g3', undefined, 'g2', undefined, undefined);
let config = new ConnectionConfig(configurationService, capabilitiesService.object);
@@ -548,27 +551,27 @@ suite('ConnectionConfig', () => {
await config.editGroup(sameNameGroup);
assert.fail();
} catch (e) {
let groups = configurationService.inspect<IConnectionProfileGroup[]>('datasource.connectionGroups').user;
let groups = configurationService.inspect<IConnectionProfileGroup[]>(Constants.connectionGroupsArrayName).user;
let originalGroup = groups.find(g => g.id === 'g2');
assert.ok(!!originalGroup);
assert.ok(!isUndefinedOrNull(originalGroup));
assert.equal(originalGroup.name, 'g2');
}
});
test('change group(parent) for connection group', async () => {
let configurationService = new TestConfigurationService();
configurationService.updateValue('datasource.connectionGroups', deepClone(testGroups), ConfigurationTarget.USER);
configurationService.updateValue(Constants.connectionGroupsArrayName, deepClone(testGroups), ConfigurationTarget.USER);
let sourceProfileGroup = new ConnectionProfileGroup('g2', undefined, 'g2', undefined, undefined);
let targetProfileGroup = new ConnectionProfileGroup('g3', undefined, 'g3', undefined, undefined);
let config = new ConnectionConfig(configurationService, capabilitiesService.object);
await config.changeGroupIdForConnectionGroup(sourceProfileGroup, targetProfileGroup);
let editedGroups = configurationService.inspect<IConnectionProfileGroup[]>('datasource.connectionGroups').user;
let editedGroups = configurationService.inspect<IConnectionProfileGroup[]>(Constants.connectionGroupsArrayName).user;
assert.equal(editedGroups.length, testGroups.length);
let editedGroup = editedGroups.find(group => group.id === 'g2');
assert.ok(!!editedGroup);
assert.ok(!isUndefinedOrNull(editedGroup));
assert.equal(editedGroup.parentId, 'g3');
});
@@ -612,7 +615,7 @@ suite('ConnectionConfig', () => {
let _testConnections = deepClone(testConnections).concat([existingProfile, changingProfile]);
let configurationService = new TestConfigurationService();
configurationService.updateValue('datasource.connections', _testConnections, ConfigurationTarget.USER);
configurationService.updateValue(Constants.connectionsArrayName, _testConnections, ConfigurationTarget.USER);
let connectionProfile = new ConnectionProfile(capabilitiesService.object, changingProfile);
@@ -621,11 +624,11 @@ suite('ConnectionConfig', () => {
await config.changeGroupIdForConnection(connectionProfile, 'test');
assert.fail();
} catch (e) {
let editedConnections = configurationService.inspect<IConnectionProfileStore[]>('datasource.connections').user;
let editedConnections = configurationService.inspect<IConnectionProfileStore[]>(Constants.connectionsArrayName).user;
// two
assert.equal(editedConnections.length, _testConnections.length);
let editedConnection = editedConnections.find(con => con.id === 'server3-2');
assert.ok(!!editedConnection);
assert.ok(!isUndefinedOrNull(editedConnection));
assert.equal(editedConnection.groupId, 'g3');
}
});
@@ -650,7 +653,7 @@ suite('ConnectionConfig', () => {
};
let configurationService = new TestConfigurationService();
configurationService.updateValue('datasource.connections', deepClone(testConnections), ConfigurationTarget.USER);
configurationService.updateValue(Constants.connectionsArrayName, deepClone(testConnections), ConfigurationTarget.USER);
let connectionProfile = new ConnectionProfile(capabilitiesService.object, newProfile);
let newId = 'newid';
@@ -658,18 +661,18 @@ suite('ConnectionConfig', () => {
let config = new ConnectionConfig(configurationService, capabilitiesService.object);
await config.changeGroupIdForConnection(connectionProfile, newId);
let editedConnections = configurationService.inspect<IConnectionProfileStore[]>('datasource.connections').user;
let editedConnections = configurationService.inspect<IConnectionProfileStore[]>(Constants.connectionsArrayName).user;
assert.equal(editedConnections.length, testConnections.length);
let editedConnection = editedConnections.find(con => con.id === 'server3');
assert.ok(!!editedConnection);
assert.ok(!isUndefinedOrNull(editedConnection));
assert.equal(editedConnection.groupId, 'newid');
});
test('addConnection should not move the connection when editing', async () => {
// Set up the connection config
let configurationService = new TestConfigurationService();
configurationService.updateValue('datasource.connections', deepClone(testConnections), ConfigurationTarget.USER);
configurationService.updateValue('datasource.connectionGroups', deepClone(testGroups), ConfigurationTarget.USER);
configurationService.updateValue(Constants.connectionsArrayName, deepClone(testConnections), ConfigurationTarget.USER);
configurationService.updateValue(Constants.connectionGroupsArrayName, deepClone(testGroups), ConfigurationTarget.USER);
let config = new ConnectionConfig(configurationService, capabilitiesService.object);
// Clone a connection and modify an option
@@ -699,13 +702,13 @@ suite('ConnectionConfig', () => {
description: 'new group'
};
let configurationService = new TestConfigurationService();
configurationService.updateValue('datasource.connectionGroups', deepClone(testGroups), ConfigurationTarget.USER);
configurationService.updateValue(Constants.connectionGroupsArrayName, deepClone(testGroups), ConfigurationTarget.USER);
let config = new ConnectionConfig(configurationService, capabilitiesService.object);
await config.addGroup(newGroup);
let editGroups = configurationService.inspect<IConnectionProfileGroup[]>('datasource.connectionGroups').user;
let editGroups = configurationService.inspect<IConnectionProfileGroup[]>(Constants.connectionGroupsArrayName).user;
assert.equal(editGroups.length, testGroups.length + 1);
});
@@ -719,14 +722,14 @@ suite('ConnectionConfig', () => {
description: 'new group'
};
let configurationService = new TestConfigurationService();
configurationService.updateValue('datasource.connectionGroups', deepClone(testGroups), ConfigurationTarget.USER);
configurationService.updateValue(Constants.connectionGroupsArrayName, deepClone(testGroups), ConfigurationTarget.USER);
const config = new ConnectionConfig(configurationService, capabilitiesService.object);
try {
await config.addGroup(existingGroupName);
assert.fail();
} catch (e) {
let editGroups = configurationService.inspect<IConnectionProfileGroup[]>('datasource.connectionGroups').user;
let editGroups = configurationService.inspect<IConnectionProfileGroup[]>(Constants.connectionGroupsArrayName).user;
assert.equal(editGroups.length, testGroups.length);
}

View File

@@ -1,528 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as assert from 'assert';
import * as azdata from 'azdata';
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
import { IConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup';
import { ConnectionStore } from 'sql/platform/connection/common/connectionStore';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { TestConfigurationService } from 'sql/platform/connection/test/common/testConfigurationService';
import { TestStateService } from 'sql/platform/connection/test/common/testStateService';
import { TestCredentialsService } from 'sql/platform/credentials/test/common/testCredentialsService';
import { ConnectionOptionSpecialType, ServiceOptionType } from 'sql/workbench/api/common/sqlExtHostTypes';
import { ConnectionProviderProperties } from 'sql/workbench/parts/connection/common/connectionProviderExtension';
import { CapabilitiesTestService } from 'sqltest/stubs/capabilitiesTestService';
import { deepClone, deepFreeze } from 'vs/base/common/objects';
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
suite('ConnectionStore', () => {
let defaultNamedProfile: IConnectionProfile = deepFreeze({
connectionName: 'new name',
serverName: 'namedServer',
databaseName: 'bcd',
authenticationType: 'SqlLogin',
userName: 'cde',
password: 'asdf!@#$',
savePassword: true,
groupId: '',
groupFullName: '',
getOptionsKey: undefined,
matches: undefined,
providerName: 'MSSQL',
options: {},
saveProfile: true,
id: undefined
});
let capabilitiesService: CapabilitiesTestService;
let maxRecent = 5;
let msSQLCapabilities: ConnectionProviderProperties;
let provider2Capabilities: ConnectionProviderProperties;
let defaultNamedConnectionProfile: ConnectionProfile;
setup(() => {
// setup configuration to return maxRecent for the #MRU items
capabilitiesService = new CapabilitiesTestService();
let connectionProvider: azdata.ConnectionOption[] = [
{
name: 'connectionName',
displayName: undefined,
description: undefined,
groupName: undefined,
categoryValues: undefined,
defaultValue: undefined,
isIdentity: true,
isRequired: true,
specialValueType: ConnectionOptionSpecialType.connectionName,
valueType: ServiceOptionType.string
},
{
name: 'serverName',
displayName: undefined,
description: undefined,
groupName: undefined,
categoryValues: undefined,
defaultValue: undefined,
isIdentity: true,
isRequired: true,
specialValueType: ConnectionOptionSpecialType.serverName,
valueType: ServiceOptionType.string
},
{
name: 'databaseName',
displayName: undefined,
description: undefined,
groupName: undefined,
categoryValues: undefined,
defaultValue: undefined,
isIdentity: true,
isRequired: true,
specialValueType: ConnectionOptionSpecialType.databaseName,
valueType: ServiceOptionType.string
},
{
name: 'userName',
displayName: undefined,
description: undefined,
groupName: undefined,
categoryValues: undefined,
defaultValue: undefined,
isIdentity: true,
isRequired: true,
specialValueType: ConnectionOptionSpecialType.userName,
valueType: ServiceOptionType.string
},
{
name: 'authenticationType',
displayName: undefined,
description: undefined,
groupName: undefined,
categoryValues: undefined,
defaultValue: undefined,
isIdentity: true,
isRequired: true,
specialValueType: ConnectionOptionSpecialType.authType,
valueType: ServiceOptionType.string
},
{
name: 'password',
displayName: undefined,
description: undefined,
groupName: undefined,
categoryValues: undefined,
defaultValue: undefined,
isIdentity: true,
isRequired: true,
specialValueType: ConnectionOptionSpecialType.password,
valueType: ServiceOptionType.string
}
];
msSQLCapabilities = {
providerId: 'MSSQL',
displayName: 'MSSQL',
connectionOptions: connectionProvider
};
provider2Capabilities = {
providerId: 'MSSQL',
displayName: 'MSSQL',
connectionOptions: connectionProvider
};
capabilitiesService.capabilities['MSSQL'] = { connection: msSQLCapabilities };
capabilitiesService.capabilities['Provider2'] = { connection: provider2Capabilities };
defaultNamedConnectionProfile = new ConnectionProfile(capabilitiesService, defaultNamedProfile);
});
test('addActiveConnection should limit recent connection saves to the MaxRecentConnections amount', async () => {
// Given 5 is the max # creds
let numCreds = 6;
let stateService = new TestStateService();
let configurationService = new TestConfigurationService();
let credentialsService = new TestCredentialsService();
await configurationService.updateValue('sql.maxRecentConnections', 5, ConfigurationTarget.USER);
// When saving 4 connections
// Expect all of them to be saved even if size is limited to 3
let connectionStore = new ConnectionStore(stateService, configurationService,
credentialsService, capabilitiesService);
for (let i = 0; i < numCreds; i++) {
let cred = Object.assign({}, defaultNamedProfile, { serverName: defaultNamedProfile.serverName + i });
let connectionProfile = new ConnectionProfile(capabilitiesService, cred);
await connectionStore.addRecentConnection(connectionProfile);
let current = connectionStore.getRecentlyUsedConnections();
if (i >= maxRecent) {
assert.equal(current.length, maxRecent, `expect only top ${maxRecent} creds to be saved`);
} else {
assert.equal(current.length, i + 1, `expect all credentials to be saved ${current.length}|${i + 1} `);
}
assert.equal(current[0].serverName, cred.serverName, 'Expect most recently saved item to be first in list');
assert.ok(!current[0].password);
}
assert.equal(credentialsService.credentials.size, numCreds);
});
test('getRecentlyUsedConnections should return connection for given provider', () => {
let stateService = new TestStateService();
let configurationService = new TestConfigurationService();
let credentialsService = new TestCredentialsService();
let connectionStore = new ConnectionStore(stateService, configurationService,
credentialsService, capabilitiesService);
let connections = connectionStore.getRecentlyUsedConnections(['Provider2']);
assert.ok(!!connections);
assert.ok(connections.every(c => c.providerName === 'Provider2'));
});
test('addActiveConnection should add same connection exactly once', async () => {
let stateService = new TestStateService();
let configurationService = new TestConfigurationService();
let credentialsService = new TestCredentialsService();
// Given we save the same connection twice
// Then expect the only 1 instance of that connection to be listed in the MRU
let connectionStore = new ConnectionStore(stateService, configurationService,
credentialsService, capabilitiesService);
let cred = Object.assign({}, defaultNamedProfile, { serverName: defaultNamedProfile.serverName + 1 });
let connectionProfile = new ConnectionProfile(capabilitiesService, cred);
await connectionStore.addRecentConnection(defaultNamedConnectionProfile);
await connectionStore.addRecentConnection(connectionProfile);
await connectionStore.addRecentConnection(connectionProfile);
let current = connectionStore.getRecentlyUsedConnections();
assert.equal(current.length, 2, 'expect 2 unique credentials to have been added');
assert.equal(current[0].serverName, cred.serverName, 'Expect most recently saved item to be first in list');
assert.ok(!current[0].password);
});
test('addActiveConnection should save password to credential store', async () => {
// Setup credential store to capture credentials sent to it
let stateService = new TestStateService();
let configurationService = new TestConfigurationService();
let credentialsService = new TestCredentialsService();
// Given we save 1 connection with password and multiple other connections without
let connectionStore = new ConnectionStore(stateService, configurationService,
credentialsService, capabilitiesService);
let integratedCred = Object.assign({}, defaultNamedProfile, {
serverName: defaultNamedProfile.serverName + 'Integrated',
authenticationType: 'Integrated',
userName: '',
password: ''
});
let noPwdCred = Object.assign({}, defaultNamedProfile, {
serverName: defaultNamedProfile.serverName + 'NoPwd',
password: ''
});
let connectionProfile = new ConnectionProfile(capabilitiesService, defaultNamedProfile);
let recentCredential: azdata.Credential;
credentialsService.onCredential(e => recentCredential = e);
await connectionStore.addRecentConnection(connectionProfile);
let current = connectionStore.getRecentlyUsedConnections();
// Then verify that since its password based we save the password
assert.equal(credentialsService.credentials.size, 1);
assert.strictEqual(recentCredential.password, defaultNamedProfile.password);
assert.ok(recentCredential.credentialId.includes('Profile'), 'Expect credential to be marked as an Profile cred');
assert.ok(!current[0].password);
// When add integrated auth connection
let integratedCredConnectionProfile = new ConnectionProfile(capabilitiesService, integratedCred);
await connectionStore.addRecentConnection(integratedCredConnectionProfile);
current = connectionStore.getRecentlyUsedConnections();
// then expect not to have credential store called, but MRU count upped to 2
assert.equal(credentialsService.credentials.size, 1);
assert.equal(current.length, 2);
// When add connection without password
let noPwdCredConnectionProfile = new ConnectionProfile(capabilitiesService, noPwdCred);
await connectionStore.addRecentConnection(noPwdCredConnectionProfile);
current = connectionStore.getRecentlyUsedConnections();
// then expect not to have credential store called, but MRU count upped to 3
assert.equal(current.length, 3);
assert.equal(credentialsService.credentials.size, 1);
});
test('can clear connections list', async () => {
let stateService = new TestStateService();
let configurationService = new TestConfigurationService();
let credentialsService = new TestCredentialsService();
let connectionStore = new ConnectionStore(stateService, configurationService,
credentialsService, capabilitiesService);
await connectionStore.addRecentConnection(defaultNamedProfile);
let result = connectionStore.getRecentlyUsedConnections();
assert.equal(result.length, 1);
connectionStore.clearRecentlyUsed();
result = connectionStore.getRecentlyUsedConnections();
assert.equal(result.length, 0);
// Then test is complete
});
test('isPasswordRequired should return true for MSSQL SqlLogin', () => {
let stateService = new TestStateService();
let configurationService = new TestConfigurationService();
let credentialsService = new TestCredentialsService();
let connectionStore = new ConnectionStore(stateService, configurationService,
credentialsService, capabilitiesService);
assert.ok(connectionStore.isPasswordRequired(defaultNamedProfile));
});
test('isPasswordRequired should return true for MSSQL SqlLogin for connection profile object', () => {
let stateService = new TestStateService();
let configurationService = new TestConfigurationService();
let credentialsService = new TestCredentialsService();
let connectionStore = new ConnectionStore(stateService, configurationService,
credentialsService, capabilitiesService);
let connectionProfile = new ConnectionProfile(capabilitiesService, defaultNamedProfile);
assert.ok(connectionStore.isPasswordRequired(connectionProfile));
});
test('isPasswordRequired should return false if the password is not required in capabilities', () => {
let stateService = new TestStateService();
let configurationService = new TestConfigurationService();
let credentialsService = new TestCredentialsService();
let providerName: string = 'providername';
let connectionProvider = msSQLCapabilities.connectionOptions.map(o => {
if (o.name === 'password') {
o.isRequired = false;
}
return o;
});
let providerCapabilities = {
providerId: providerName,
displayName: providerName,
connectionOptions: connectionProvider
};
capabilitiesService.capabilities[providerName] = { connection: providerCapabilities };
let connectionStore = new ConnectionStore(stateService, configurationService,
credentialsService, capabilitiesService);
let connectionProfile: IConnectionProfile = Object.assign({}, defaultNamedProfile, { providerName: providerName });
assert.ok(!connectionStore.isPasswordRequired(connectionProfile));
});
test('saveProfile should save the password after the profile is saved', async () => {
let stateService = new TestStateService();
let configurationService = new TestConfigurationService();
let credentialsService = new TestCredentialsService();
let password: string = 'asdf!@#$';
let connectionProfile: IConnectionProfile = Object.assign({}, defaultNamedProfile, { password });
let connectionStore = new ConnectionStore(stateService, configurationService,
credentialsService, capabilitiesService);
let profile = await connectionStore.saveProfile(connectionProfile);
// add connection should be called with a profile without password
assert.equal(profile.password, password, 'The returned profile should still keep the password');
assert.ok(!!profile.groupId, 'Group id should be set in the profile');
});
test('getGroupFromId returns undefined when there is no group with the given ID', () => {
let stateService = new TestStateService();
let configurationService = new TestConfigurationService();
let credentialsService = new TestCredentialsService();
let connectionStore = new ConnectionStore(stateService, configurationService,
credentialsService, capabilitiesService);
let group = connectionStore.getGroupFromId('invalidId');
assert.equal(group, undefined, 'Returned group was not undefined when there was no group with the given ID');
});
test('getGroupFromId returns the group that has the given ID', () => {
let stateService = new TestStateService();
let configurationService = new TestConfigurationService();
let credentialsService = new TestCredentialsService();
let parentGroupId = 'parentGroup';
let childGroupId = 'childGroup';
let groups: IConnectionProfileGroup[] = [
{
id: parentGroupId,
name: parentGroupId,
color: undefined,
description: '',
parentId: ''
},
{
id: childGroupId,
name: childGroupId,
color: undefined,
description: '',
parentId: parentGroupId
}
];
configurationService.updateValue('datasource.connectionGroups', groups, ConfigurationTarget.USER);
let connectionStore = new ConnectionStore(stateService, configurationService,
credentialsService, capabilitiesService);
// If I look up the parent group using its ID, then I get back the correct group
let actualGroup = connectionStore.getGroupFromId(parentGroupId);
assert.equal(actualGroup.id, parentGroupId, 'Did not get the parent group when looking it up with its ID');
// If I look up the child group using its ID, then I get back the correct group
actualGroup = connectionStore.getGroupFromId(childGroupId);
assert.equal(actualGroup.id, childGroupId, 'Did not get the child group when looking it up with its ID');
});
test('getProfileWithoutPassword can return the profile without credentials in the password property or options dictionary', () => {
let stateService = new TestStateService();
let configurationService = new TestConfigurationService();
let credentialsService = new TestCredentialsService();
let connectionStore = new ConnectionStore(stateService, configurationService,
credentialsService, capabilitiesService);
let profile = deepClone(defaultNamedProfile);
profile.options['password'] = profile.password;
profile.id = 'testId';
let expectedProfile = Object.assign({}, profile);
expectedProfile.password = '';
expectedProfile.options['password'] = '';
expectedProfile = ConnectionProfile.fromIConnectionProfile(capabilitiesService, expectedProfile).toIConnectionProfile();
let profileWithoutCredentials = connectionStore.getProfileWithoutPassword(profile);
assert.deepEqual(profileWithoutCredentials.toIConnectionProfile(), expectedProfile);
});
test('addPassword gets the password from the credentials service', async () => {
let stateService = new TestStateService();
let configurationService = new TestConfigurationService();
let credentialsService = new TestCredentialsService();
let profile = ConnectionProfile.fromIConnectionProfile(capabilitiesService, Object.assign({}, defaultNamedProfile, { password: undefined }));
let credId = `Microsoft.SqlTools|itemtype:Profile|id:${profile.getConnectionInfoId()}`;
let password: string = 'asdf!@#$';
await credentialsService.saveCredential(credId, password);
let connectionStore = new ConnectionStore(stateService, configurationService,
credentialsService, capabilitiesService);
let passwordProfile = (await connectionStore.addSavedPassword(profile)).profile;
assert.equal(passwordProfile.password, password);
});
test('getConnectionProfileGroups', async () => {
let stateService = new TestStateService();
let configurationService = new TestConfigurationService();
let credentialsService = new TestCredentialsService();
let parentGroupId = 'parentGroup';
let childGroupId = 'childGroup';
let groups: IConnectionProfileGroup[] = [
{
id: parentGroupId,
name: parentGroupId,
color: undefined,
description: '',
parentId: ''
},
{
id: childGroupId,
name: childGroupId,
color: undefined,
description: '',
parentId: parentGroupId
}
];
configurationService.updateValue('datasource.connectionGroups', groups, ConfigurationTarget.USER);
let connectionStore = new ConnectionStore(stateService, configurationService,
credentialsService, capabilitiesService);
let connectionGroups = connectionStore.getConnectionProfileGroups();
for (let group of connectionGroups) {
let foundGroup = groups.find(g => g.id === group.id);
assert.ok(foundGroup);
}
});
test('removing connection correctly removes', async () => {
let stateService = new TestStateService();
let configurationService = new TestConfigurationService();
let credentialsService = new TestCredentialsService();
let connectionStore = new ConnectionStore(stateService, configurationService,
credentialsService, capabilitiesService);
for (let i = 0; i < 5; i++) {
let cred = Object.assign({}, defaultNamedProfile, { serverName: defaultNamedProfile.serverName + i });
let connectionProfile = new ConnectionProfile(capabilitiesService, cred);
await connectionStore.addRecentConnection(connectionProfile);
let current = connectionStore.getRecentlyUsedConnections();
assert.equal(current.length, i + 1);
}
for (let i = 0; i < 5; i++) {
let cred = Object.assign({}, defaultNamedProfile, { serverName: defaultNamedProfile.serverName + i });
let connectionProfile = new ConnectionProfile(capabilitiesService, cred);
await connectionStore.removeRecentConnection(connectionProfile);
let current = connectionStore.getRecentlyUsedConnections();
assert.equal(current.length, 4 - i);
}
});
test('getRecentlyUsedConnections correctly fills in group names', async () => {
let stateService = new TestStateService();
let configurationService = new TestConfigurationService();
let credentialsService = new TestCredentialsService();
let connectionStore = new ConnectionStore(stateService, configurationService,
credentialsService, capabilitiesService);
let parentGroupId = 'parentGroup';
let parentGroupName = 'parentGroupName';
let group: IConnectionProfileGroup = {
id: parentGroupId,
name: parentGroupName,
color: undefined,
description: '',
parentId: ''
};
let connection: azdata.IConnectionProfile = {
options: [],
connectionName: '',
serverName: 'server1',
databaseName: 'database',
userName: 'user',
password: 'password',
authenticationType: '',
providerName: 'MSSQL',
groupId: parentGroupId,
groupFullName: '',
savePassword: true,
saveProfile: true,
id: 'server1'
};
configurationService.updateValue('datasource.connectionGroups', [group], ConfigurationTarget.USER);
configurationService.updateValue('datasource.connections', [connection], ConfigurationTarget.USER);
connectionStore.addRecentConnection(ConnectionProfile.fromIConnectionProfile(capabilitiesService, connection));
let connections = connectionStore.getRecentlyUsedConnections();
assert.equal(connections[0].groupFullName, parentGroupName);
});
});

View File

@@ -17,8 +17,13 @@ export class TestConfigurationService implements IConfigurationService {
return Promise.resolve(this.getValue());
}
public getValue(arg1?: any): any {
return getConfigurationValue(this.configuration.user, arg1);
public getValue(arg1?: any, arg2?: any): any {
let configuration;
configuration = configuration ? configuration : this.configuration;
if (arg1 && typeof arg1 === 'string') {
return getConfigurationValue(configuration, arg1);
}
return configuration;
}
public updateValue(key: string, value: any, target?: any): Promise<void> {

View File

@@ -1,30 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { IStateService } from 'vs/platform/state/common/state';
export class TestStateService implements IStateService {
_serviceBrand: any;
private storage = {};
constructor() { }
getItem<T>(key: string, defaultValue: T): T;
getItem<T>(key: string, defaultValue: T | undefined): T | undefined;
getItem<T>(key: string, defaultValue?: T): T | undefined {
return this.storage[key] || defaultValue;
}
setItem(key: string, data: any): void {
this.storage[key] = data;
}
removeItem(key: string): void {
delete this.storage[key];
}
}

View File

@@ -25,11 +25,11 @@ export const ICredentialsService = createDecorator<ICredentialsService>(SERVICE_
export interface ICredentialsService {
_serviceBrand: any;
saveCredential(credentialId: string, password: string): Promise<boolean>;
saveCredential(credentialId: string, password: string): Thenable<boolean>;
readCredential(credentialId: string): Promise<azdata.Credential>;
readCredential(credentialId: string): Thenable<azdata.Credential>;
deleteCredential(credentialId: string): Promise<boolean>;
deleteCredential(credentialId: string): Thenable<boolean>;
addEventListener(handle: number, events: CredentialManagementEvents): IDisposable;
}
@@ -62,15 +62,15 @@ export class CredentialsService implements ICredentialsService {
};
}
public saveCredential(credentialId: string, password: string): Promise<boolean> {
public saveCredential(credentialId: string, password: string): Thenable<boolean> {
return this._onServerEventsReady.promise.then(() => this._serverEvents[this._lastHandle].onSaveCredential(credentialId, password));
}
public readCredential(credentialId: string): Promise<azdata.Credential> {
public readCredential(credentialId: string): Thenable<azdata.Credential> {
return this._onServerEventsReady.promise.then(() => this._serverEvents[this._lastHandle].onReadCredential(credentialId));
}
public deleteCredential(credentialId: string): Promise<boolean> {
public deleteCredential(credentialId: string): Thenable<boolean> {
return this._onServerEventsReady.promise.then(() => this._serverEvents[this._lastHandle].onDeleteCredential(credentialId));
}

View File

@@ -1,44 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { ICredentialsService, CredentialManagementEvents } from 'sql/platform/credentials/common/credentialsService';
import { Credential } from 'azdata';
import { IDisposable } from 'vs/base/common/lifecycle';
import { Emitter } from 'vs/base/common/event';
export class TestCredentialsService implements ICredentialsService {
_serviceBrand: any;
public credentials = new Map<string, Credential>();
private _onCredential = new Emitter<Credential>();
public readonly onCredential = this._onCredential.event;
saveCredential(credentialId: string, password: string): Promise<boolean> {
let credential = { credentialId, password };
this.credentials.set(credentialId, credential);
this._onCredential.fire(credential);
return Promise.resolve(true);
}
readCredential(credentialId: string): Promise<Credential> {
let cred = this.credentials.get(credentialId);
if (cred) {
return Promise.resolve<Credential>(cred);
} else {
return Promise.reject('');
}
}
deleteCredential(credentialId: string): Promise<boolean> {
return Promise.resolve(this.credentials.delete(credentialId));
}
addEventListener(handle: number, events: CredentialManagementEvents): IDisposable {
throw new Error('Method not implemented.');
}
}

View File

@@ -99,7 +99,7 @@ suite('SQL ConnectionManagementService tests', () => {
connectionDialogService.setup(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(none));
connectionDialogService.setup(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), undefined, TypeMoq.It.isAny())).returns(() => Promise.resolve(none));
connectionStore.setup(x => x.addRecentConnection(TypeMoq.It.isAny())).returns(() => Promise.resolve());
connectionStore.setup(x => x.addActiveConnection(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve());
connectionStore.setup(x => x.saveProfile(TypeMoq.It.isAny())).returns(() => Promise.resolve(connectionProfile));
workbenchEditorService.setup(x => x.openEditor(undefined, TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(undefined));
connectionStore.setup(x => x.addSavedPassword(TypeMoq.It.is<IConnectionProfile>(
@@ -147,13 +147,16 @@ suite('SQL ConnectionManagementService tests', () => {
function createConnectionManagementService(): ConnectionManagementService {
let connectionManagementService = new ConnectionManagementService(
undefined,
connectionStore.object,
new TestStorageService(),
connectionDialogService.object,
undefined,
undefined,
workbenchEditorService.object,
undefined,
workspaceConfigurationServiceMock.object,
undefined,
capabilitiesService,
undefined,
editorGroupService.object,

View File

@@ -0,0 +1,508 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as TypeMoq from 'typemoq';
import { ConnectionConfig } from 'sql/platform/connection/common/connectionConfig';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { WorkspaceConfigurationTestService } from 'sqltest/stubs/workspaceConfigurationTestService';
import * as Constants from 'sql/platform/connection/common/constants';
import { StorageTestService } from 'sqltest/stubs/storageTestService';
import { ConnectionStore } from 'sql/platform/connection/common/connectionStore';
import { CredentialsService } from 'sql/platform/credentials/common/credentialsService';
import * as assert from 'assert';
import { Memento } from 'vs/workbench/common/memento';
import * as azdata from 'azdata';
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
import { ConnectionProfileGroup, IConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup';
import { ConnectionOptionSpecialType, ServiceOptionType } from 'sql/workbench/api/common/sqlExtHostTypes';
import { CapabilitiesTestService } from '../../stubs/capabilitiesTestService';
import { ConnectionProviderProperties } from 'sql/workbench/parts/connection/common/connectionProviderExtension';
suite('SQL ConnectionStore tests', () => {
let defaultNamedProfile: IConnectionProfile;
let context: TypeMoq.Mock<Memento>;
let credentialStore: TypeMoq.Mock<CredentialsService>;
let connectionConfig: TypeMoq.Mock<ConnectionConfig>;
let workspaceConfigurationServiceMock: TypeMoq.Mock<WorkspaceConfigurationTestService>;
let storageServiceMock: TypeMoq.Mock<StorageTestService>;
let capabilitiesService: CapabilitiesTestService;
let mementoArray: any = [];
let maxRecent = 5;
let msSQLCapabilities: ConnectionProviderProperties;
let provider2Capabilities: ConnectionProviderProperties;
let defaultNamedConnectionProfile: ConnectionProfile;
setup(() => {
defaultNamedProfile = Object.assign({}, {
connectionName: 'new name',
serverName: 'namedServer',
databaseName: 'bcd',
authenticationType: 'SqlLogin',
userName: 'cde',
password: 'asdf!@#$',
savePassword: true,
groupId: '',
groupFullName: '',
getOptionsKey: undefined,
matches: undefined,
providerName: 'MSSQL',
options: {},
saveProfile: true,
id: undefined
});
storageServiceMock = TypeMoq.Mock.ofType(StorageTestService);
let momento = new Memento('ConnectionManagement', storageServiceMock.object);
context = TypeMoq.Mock.ofInstance(momento);
context.setup(x => x.getMemento(TypeMoq.It.isAny())).returns(() => mementoArray);
credentialStore = TypeMoq.Mock.ofType(CredentialsService);
connectionConfig = TypeMoq.Mock.ofType(ConnectionConfig);
// setup configuration to return maxRecent for the #MRU items
let configResult: { [key: string]: any } = {};
configResult[Constants.configMaxRecentConnections] = maxRecent;
workspaceConfigurationServiceMock = TypeMoq.Mock.ofType(WorkspaceConfigurationTestService);
workspaceConfigurationServiceMock.setup(x => x.getValue(Constants.sqlConfigSectionName))
.returns(() => configResult);
capabilitiesService = new CapabilitiesTestService();
let connectionProvider: azdata.ConnectionOption[] = [
{
name: 'connectionName',
displayName: undefined,
description: undefined,
groupName: undefined,
categoryValues: undefined,
defaultValue: undefined,
isIdentity: true,
isRequired: true,
specialValueType: ConnectionOptionSpecialType.connectionName,
valueType: ServiceOptionType.string
},
{
name: 'serverName',
displayName: undefined,
description: undefined,
groupName: undefined,
categoryValues: undefined,
defaultValue: undefined,
isIdentity: true,
isRequired: true,
specialValueType: ConnectionOptionSpecialType.serverName,
valueType: ServiceOptionType.string
},
{
name: 'databaseName',
displayName: undefined,
description: undefined,
groupName: undefined,
categoryValues: undefined,
defaultValue: undefined,
isIdentity: true,
isRequired: true,
specialValueType: ConnectionOptionSpecialType.databaseName,
valueType: ServiceOptionType.string
},
{
name: 'userName',
displayName: undefined,
description: undefined,
groupName: undefined,
categoryValues: undefined,
defaultValue: undefined,
isIdentity: true,
isRequired: true,
specialValueType: ConnectionOptionSpecialType.userName,
valueType: ServiceOptionType.string
},
{
name: 'authenticationType',
displayName: undefined,
description: undefined,
groupName: undefined,
categoryValues: undefined,
defaultValue: undefined,
isIdentity: true,
isRequired: true,
specialValueType: ConnectionOptionSpecialType.authType,
valueType: ServiceOptionType.string
},
{
name: 'password',
displayName: undefined,
description: undefined,
groupName: undefined,
categoryValues: undefined,
defaultValue: undefined,
isIdentity: true,
isRequired: true,
specialValueType: ConnectionOptionSpecialType.password,
valueType: ServiceOptionType.string
}
];
msSQLCapabilities = {
providerId: 'MSSQL',
displayName: 'MSSQL',
connectionOptions: connectionProvider
};
provider2Capabilities = {
providerId: 'MSSQL',
displayName: 'MSSQL',
connectionOptions: connectionProvider
};
capabilitiesService.capabilities['MSSQL'] = { connection: msSQLCapabilities };
capabilitiesService.capabilities['Provider2'] = { connection: provider2Capabilities };
let groups: IConnectionProfileGroup[] = [
{
id: 'root',
name: 'root',
parentId: '',
color: '',
description: ''
},
{
id: 'g1',
name: 'g1',
parentId: 'root',
color: 'blue',
description: 'g1'
}
];
connectionConfig.setup(x => x.getAllGroups()).returns(() => groups);
defaultNamedConnectionProfile = new ConnectionProfile(capabilitiesService, defaultNamedProfile);
});
test('addActiveConnection should limit recent connection saves to the MaxRecentConnections amount', async () => {
// Given 5 is the max # creds
let numCreds = 6;
// setup memento for MRU to return a list we have access to
credentialStore.setup(x => x.saveCredential(TypeMoq.It.isAny(), TypeMoq.It.isAny()))
.returns(() => Promise.resolve(true));
// When saving 4 connections
// Expect all of them to be saved even if size is limited to 3
let connectionStore = new ConnectionStore(context.object, workspaceConfigurationServiceMock.object,
credentialStore.object, capabilitiesService, connectionConfig.object);
for (let i = 0; i < numCreds; i++) {
let cred = Object.assign({}, defaultNamedProfile, { serverName: defaultNamedProfile.serverName + i });
let connectionProfile = new ConnectionProfile(capabilitiesService, cred);
await connectionStore.addActiveConnection(connectionProfile, true);
let current = connectionStore.getRecentlyUsedConnections();
if (i >= maxRecent) {
assert.equal(current.length, maxRecent, `expect only top ${maxRecent} creds to be saved to MRU`);
} else {
assert.equal(current.length, i + 1, `expect all credentials to be saved to MRU ${current.length}|${i + 1} `);
}
assert.equal(current[0].serverName, cred.serverName, 'Expect most recently saved item to be first in list');
assert.ok(!current[0].password);
}
credentialStore.verify(x => x.saveCredential(TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.exactly(numCreds));
let recentConnections = connectionStore.getActiveConnections();
assert.equal(numCreds, recentConnections.length, `expect number of active connection ${numCreds}|${recentConnections.length} `);
});
test('addActiveConnection with addToMru as false should not add any recent connections', async () => {
// setup memento for MRU to return a list we have access to
credentialStore.setup(x => x.saveCredential(TypeMoq.It.isAny(), TypeMoq.It.isAny()))
.returns(() => Promise.resolve(true));
const numCreds = 3;
let connectionStore = new ConnectionStore(context.object, workspaceConfigurationServiceMock.object,
credentialStore.object, capabilitiesService, connectionConfig.object);
connectionStore.clearActiveConnections();
connectionStore.clearRecentlyUsed();
for (let i = 0; i < 3; i++) {
let cred = Object.assign({}, defaultNamedProfile, { serverName: defaultNamedProfile.serverName + i });
let connectionProfile = new ConnectionProfile(capabilitiesService, cred);
await connectionStore.addActiveConnection(connectionProfile, false);
let recentConnections = connectionStore.getRecentlyUsedConnections();
let activeConnections = connectionStore.getActiveConnections();
assert.equal(recentConnections.length, 0, `expect no entries to be saved to MRU`);
assert.equal(activeConnections.length, i + 1, `expect all credentials to be saved to activeConnections ${activeConnections.length}|${i + 1} `);
}
credentialStore.verify(x => x.saveCredential(TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.exactly(numCreds));
});
test('getRecentlyUsedConnections should return connection for given provider', () => {
let connectionStore = new ConnectionStore(context.object, workspaceConfigurationServiceMock.object,
credentialStore.object, capabilitiesService, connectionConfig.object);
let connections = connectionStore.getRecentlyUsedConnections(['Provider2']);
assert.notEqual(connections, undefined);
assert.equal(connections.every(c => c.providerName === 'Provider2'), true);
});
test('addActiveConnection should add same connection exactly once', async () => {
// setup memento for MRU to return a list we have access to
credentialStore.setup(x => x.saveCredential(TypeMoq.It.isAny(), TypeMoq.It.isAny()))
.returns(() => Promise.resolve(true));
// Given we save the same connection twice
// Then expect the only 1 instance of that connection to be listed in the MRU
let connectionStore = new ConnectionStore(context.object, workspaceConfigurationServiceMock.object,
credentialStore.object, capabilitiesService, connectionConfig.object);
connectionStore.clearActiveConnections();
connectionStore.clearRecentlyUsed();
let cred = Object.assign({}, defaultNamedProfile, { serverName: defaultNamedProfile.serverName + 1 });
let connectionProfile = new ConnectionProfile(capabilitiesService, cred);
await connectionStore.addActiveConnection(defaultNamedConnectionProfile, true);
await connectionStore.addActiveConnection(connectionProfile, true);
await connectionStore.addActiveConnection(connectionProfile, true);
let recentConnections = connectionStore.getRecentlyUsedConnections();
assert.equal(recentConnections.length, 2, 'expect 2 unique credentials to have been added');
assert.equal(recentConnections[0].serverName, cred.serverName, 'Expect most recently saved item to be first in list');
assert.ok(!recentConnections[0].password);
});
test('addActiveConnection should save password to credential store', async () => {
// Setup credential store to capture credentials sent to it
let capturedCreds: any;
credentialStore.setup(x => x.saveCredential(TypeMoq.It.isAny(), TypeMoq.It.isAny()))
.callback((cred: string, pass: any) => {
capturedCreds = {
'credentialId': cred,
'password': pass
};
})
.returns(() => Promise.resolve(true));
// Given we save 1 connection with password and multiple other connections without
let connectionStore = new ConnectionStore(context.object, workspaceConfigurationServiceMock.object,
credentialStore.object, capabilitiesService, connectionConfig.object);
connectionStore.clearActiveConnections();
connectionStore.clearRecentlyUsed();
let integratedCred = Object.assign({}, defaultNamedProfile, {
serverName: defaultNamedProfile.serverName + 'Integrated',
authenticationType: 'Integrated',
userName: '',
password: ''
});
let noPwdCred = Object.assign({}, defaultNamedProfile, {
serverName: defaultNamedProfile.serverName + 'NoPwd',
password: ''
});
let connectionProfile = new ConnectionProfile(capabilitiesService, defaultNamedProfile);
let expectedCredCount = 0;
expectedCredCount++;
// Connection with stored password
await connectionStore.addActiveConnection(connectionProfile, true);
let recentConnections = connectionStore.getActiveConnections();
// Then verify that saveCredential was called and correctly stored the password
credentialStore.verify(x => x.saveCredential(TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
assert.strictEqual(capturedCreds.password, defaultNamedProfile.password);
let credId: string = capturedCreds.credentialId;
assert.ok(credId.includes(ConnectionStore.CRED_PROFILE_USER), 'Expect credential to be marked as an Profile cred');
assert.ok(!recentConnections[0].password);
// Integrated auth
expectedCredCount++;
let integratedCredConnectionProfile = new ConnectionProfile(capabilitiesService, integratedCred);
await connectionStore.addActiveConnection(integratedCredConnectionProfile, true);
recentConnections = connectionStore.getActiveConnections();
// We shouldn't see an increase in the calls to saveCredential, but the MRU should be increased
credentialStore.verify(x => x.saveCredential(TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
assert.equal(recentConnections.length, expectedCredCount, `expect ${expectedCredCount} unique credentials to have been added`);
// Connection with blank (no) password
expectedCredCount++;
let noPwdCredConnectionProfile = new ConnectionProfile(capabilitiesService, noPwdCred);
await connectionStore.addActiveConnection(noPwdCredConnectionProfile, true);
recentConnections = connectionStore.getActiveConnections();
// We shouldn't see an increase in the calls to saveCredential, but the MRU should be increased
credentialStore.verify(x => x.saveCredential(TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
assert.equal(recentConnections.length, expectedCredCount, `expect ${expectedCredCount} unique credentials to have been added`);
});
test('can clear connections list', (done) => {
connectionConfig.setup(x => x.getConnections(TypeMoq.It.isAny())).returns(() => []);
let connectionStore = new ConnectionStore(context.object, workspaceConfigurationServiceMock.object,
credentialStore.object, capabilitiesService, connectionConfig.object);
// When we clear the connections list and get the list of available connection items
connectionStore.clearActiveConnections();
connectionStore.clearRecentlyUsed();
// Expect no connection items
let result = connectionStore.getActiveConnections();
let expectedCount = 0; // 1 for create connection profile
assert.equal(result.length, expectedCount);
result = connectionStore.getRecentlyUsedConnections();
assert.equal(result.length, expectedCount);
// Then test is complete
done();
});
test('isPasswordRequired should return true for MSSQL SqlLogin', () => {
let connectionStore = new ConnectionStore(context.object, workspaceConfigurationServiceMock.object,
credentialStore.object, capabilitiesService, connectionConfig.object);
let expected: boolean = true;
let actual = connectionStore.isPasswordRequired(defaultNamedProfile);
assert.equal(expected, actual);
});
test('isPasswordRequired should return true for MSSQL SqlLogin for connection profile object', () => {
let connectionStore = new ConnectionStore(context.object, workspaceConfigurationServiceMock.object,
credentialStore.object, capabilitiesService, connectionConfig.object);
let connectionProfile = new ConnectionProfile(capabilitiesService, defaultNamedProfile);
let expected: boolean = true;
let actual = connectionStore.isPasswordRequired(connectionProfile);
assert.equal(expected, actual);
});
test('isPasswordRequired should return false if the password is not required in capabilities', () => {
let providerName: string = 'providername';
let connectionProvider = msSQLCapabilities.connectionOptions.map(o => {
if (o.name === 'password') {
o.isRequired = false;
}
return o;
});
let providerCapabilities = {
providerId: providerName,
displayName: providerName,
connectionOptions: connectionProvider
};
capabilitiesService.capabilities[providerName] = { connection: providerCapabilities };
let connectionStore = new ConnectionStore(context.object, workspaceConfigurationServiceMock.object,
credentialStore.object, capabilitiesService, connectionConfig.object);
let connectionProfile: IConnectionProfile = Object.assign({}, defaultNamedProfile, { providerName: providerName });
let expected: boolean = false;
let actual = connectionStore.isPasswordRequired(connectionProfile);
assert.equal(expected, actual);
});
test('saveProfile should save the password after the profile is saved', done => {
let password: string = 'asdf!@#$';
let groupId: string = 'group id';
let connectionProfile: IConnectionProfile = Object.assign({}, defaultNamedProfile, { password: password });
let savedConnection: IConnectionProfile = Object.assign({}, connectionProfile, { groupId: groupId, password: '' });
connectionConfig.setup(x => x.addConnection(TypeMoq.It.isAny())).returns(() => Promise.resolve(savedConnection));
credentialStore.setup(x => x.saveCredential(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(true));
let connectionStore = new ConnectionStore(context.object, workspaceConfigurationServiceMock.object,
credentialStore.object, capabilitiesService, connectionConfig.object);
connectionStore.saveProfile(connectionProfile).then(profile => {
// add connection should be called with a profile without password
connectionConfig.verify(x => x.addConnection(TypeMoq.It.is<IConnectionProfile>(c => c.password === '')), TypeMoq.Times.once());
credentialStore.verify(x => x.saveCredential(TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
assert.equal(profile.password, password, 'The returned profile should still keep the password');
assert.equal(profile.groupId, groupId, 'Group id should be set in the profile');
done();
}).catch(err => {
assert.fail(err);
done(err);
});
});
test('addConnectionToMemento should not add duplicate items', () => {
let connectionStore = new ConnectionStore(context.object, workspaceConfigurationServiceMock.object,
credentialStore.object, capabilitiesService, connectionConfig.object);
let mementoKey = 'RECENT_CONNECTIONS2';
connectionStore.clearFromMemento(mementoKey);
let connectionProfile: IConnectionProfile = Object.assign({}, defaultNamedProfile);
connectionStore.addConnectionToMemento(connectionProfile, mementoKey);
connectionProfile = Object.assign({}, defaultNamedProfile, { authenticationType: 'Integrated', userName: '' });
connectionStore.addConnectionToMemento(connectionProfile, mementoKey);
let currentList = connectionStore.getConnectionsFromMemento(mementoKey);
assert.equal(currentList.length, 2, 'Adding same connection with different auth');
connectionProfile = Object.assign({}, defaultNamedProfile, { groupFullName: 'new group' });
connectionStore.addConnectionToMemento(connectionProfile, mementoKey);
currentList = connectionStore.getConnectionsFromMemento(mementoKey);
assert.equal(currentList.length, 3, 'Adding same connection with different group name');
connectionProfile = Object.assign({}, defaultNamedProfile,
{ groupFullName: defaultNamedProfile.groupFullName.toUpperCase() });
connectionStore.addConnectionToMemento(connectionProfile, mementoKey);
currentList = connectionStore.getConnectionsFromMemento(mementoKey);
assert.equal(currentList.length, 3, 'Adding same connection with same group name but uppercase');
connectionProfile = Object.assign({}, defaultNamedProfile,
{ groupFullName: '' });
connectionStore.addConnectionToMemento(connectionProfile, mementoKey);
currentList = connectionStore.getConnectionsFromMemento(mementoKey);
assert.equal(currentList.length, 3, 'Adding same connection with group empty string');
connectionProfile = Object.assign({}, defaultNamedProfile,
{ groupFullName: '/' });
connectionStore.addConnectionToMemento(connectionProfile, mementoKey);
currentList = connectionStore.getConnectionsFromMemento(mementoKey);
assert.equal(currentList.length, 3, 'Adding same connection with group /');
});
test('getGroupFromId returns undefined when there is no group with the given ID', () => {
let connectionStore = new ConnectionStore(context.object, workspaceConfigurationServiceMock.object,
credentialStore.object, capabilitiesService, connectionConfig.object);
let group = connectionStore.getGroupFromId('invalidId');
assert.equal(group, undefined, 'Returned group was not undefined when there was no group with the given ID');
});
test('getGroupFromId returns the group that has the given ID', () => {
// Set up the server groups with an additional group that contains a child group
let groups: IConnectionProfileGroup[] = connectionConfig.object.getAllGroups();
let parentGroupId = 'parentGroup';
let childGroupId = 'childGroup';
let parentGroup = new ConnectionProfileGroup(parentGroupId, undefined, parentGroupId, '', '');
let childGroup = new ConnectionProfileGroup(childGroupId, parentGroup, childGroupId, '', '');
groups.push(parentGroup, childGroup);
let newConnectionConfig = TypeMoq.Mock.ofType(ConnectionConfig);
newConnectionConfig.setup(x => x.getAllGroups()).returns(() => groups);
let connectionStore = new ConnectionStore(context.object, workspaceConfigurationServiceMock.object,
credentialStore.object, capabilitiesService, newConnectionConfig.object);
// If I look up the parent group using its ID, then I get back the correct group
let actualGroup = connectionStore.getGroupFromId(parentGroupId);
assert.equal(actualGroup.id, parentGroupId, 'Did not get the parent group when looking it up with its ID');
// If I look up the child group using its ID, then I get back the correct group
actualGroup = connectionStore.getGroupFromId(childGroupId);
assert.equal(actualGroup.id, childGroupId, 'Did not get the child group when looking it up with its ID');
});
test('getProfileWithoutPassword can return the profile without credentials in the password property or options dictionary', () => {
let connectionStore = new ConnectionStore(context.object, workspaceConfigurationServiceMock.object,
credentialStore.object, capabilitiesService, connectionConfig.object);
let profile = Object.assign({}, defaultNamedProfile);
profile.options['password'] = profile.password;
profile.id = 'testId';
let expectedProfile = Object.assign({}, profile);
expectedProfile.password = '';
expectedProfile.options['password'] = '';
expectedProfile = ConnectionProfile.fromIConnectionProfile(capabilitiesService, expectedProfile).toIConnectionProfile();
let profileWithoutCredentials = connectionStore.getProfileWithoutPassword(profile);
assert.deepEqual(profileWithoutCredentials.toIConnectionProfile(), expectedProfile);
});
});

View File

@@ -18,6 +18,7 @@ import { TestConnectionManagementService } from 'sqltest/stubs/connectionManagem
import { ErrorMessageServiceStub } from 'sqltest/stubs/errorMessageServiceStub';
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
import { ServerTreeView } from 'sql/parts/objectExplorer/viewlet/serverTreeView';
import * as Constants from 'sql/platform/connection/common/constants';
import * as LocalizedConstants from 'sql/parts/connection/common/localizedConstants';
import { ObjectExplorerService, ObjectExplorerNodeEventArgs } from 'sql/workbench/services/objectExplorer/common/objectExplorerService';
import { TreeNode } from 'sql/parts/objectExplorer/common/treeNode';
@@ -31,7 +32,6 @@ import { ObjectExplorerActionsContext, ManageConnectionAction } from 'sql/parts/
import { IConnectionResult, IConnectionParams } from 'sql/platform/connection/common/connectionManagement';
import { TreeSelectionHandler } from 'sql/parts/objectExplorer/viewlet/treeSelectionHandler';
import { CapabilitiesTestService } from 'sqltest/stubs/capabilitiesTestService';
import { UNSAVED_GROUP_ID } from 'sql/platform/connection/common/constants';
suite('SQL Connection Tree Action tests', () => {
let errorMessageService: TypeMoq.Mock<ErrorMessageServiceStub>;
@@ -328,7 +328,7 @@ suite('SQL Connection Tree Action tests', () => {
saveProfile: true,
id: 'testId'
});
connection.parent = new ConnectionProfileGroup(LocalizedConstants.unsavedGroupLabel, undefined, UNSAVED_GROUP_ID, undefined, undefined);
connection.parent = new ConnectionProfileGroup(LocalizedConstants.unsavedGroupLabel, undefined, Constants.unsavedGroupId, undefined, undefined);
let connectionAction: DeleteConnectionAction = new DeleteConnectionAction(DeleteConnectionAction.ID,
DeleteConnectionAction.DELETE_CONNECTION_LABEL,
connection,
@@ -513,4 +513,4 @@ suite('SQL Connection Tree Action tests', () => {
}).then(() => done(), (err) => done(err));
});
});
});

View File

@@ -124,7 +124,7 @@ suite('SQL QueryAction Tests', () => {
.returns(() => Promise.resolve(none));
// ... Mock "isConnected" in ConnectionManagementService
let connectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, {}, connectionDialogService.object);
let connectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, {}, {}, new TestStorageService(), connectionDialogService.object);
connectionManagementService.callBase = true;
connectionManagementService.setup(x => x.isConnected(TypeMoq.It.isAnyString())).returns(() => isConnected);
@@ -182,7 +182,7 @@ suite('SQL QueryAction Tests', () => {
queryEditor.setup(x => x.isSelectionEmpty()).returns(() => isSelectionEmpty);
// ... Mock "isConnected" in ConnectionManagementService
let connectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, {});
let connectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, {}, {}, new TestStorageService());
connectionManagementService.callBase = true;
connectionManagementService.setup(x => x.isConnected(TypeMoq.It.isAnyString())).returns(() => true);
@@ -249,7 +249,7 @@ suite('SQL QueryAction Tests', () => {
});
// ... Mock "isConnected" in ConnectionManagementService
let connectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, {}, connectionDialogService.object);
let connectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, {}, {}, new TestStorageService(), connectionDialogService.object);
connectionManagementService.callBase = true;
connectionManagementService.setup(x => x.isConnected(TypeMoq.It.isAnyString())).returns(() => isConnected);
@@ -315,7 +315,7 @@ suite('SQL QueryAction Tests', () => {
let calledCancelQuery: boolean = false;
// ... Mock "isConnected" in ConnectionManagementService
let connectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, {});
let connectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, {}, {}, new TestStorageService());
connectionManagementService.setup(x => x.isConnected(TypeMoq.It.isAnyString())).returns(() => isConnected);
// ... Mock QueryModelService
@@ -348,7 +348,7 @@ suite('SQL QueryAction Tests', () => {
let countCalledDisconnectEditor: number = 0;
// ... Mock "isConnected" and "disconnectEditor" in ConnectionManagementService
let connectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, {});
let connectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, {}, {}, new TestStorageService());
connectionManagementService.setup(x => x.isConnected(TypeMoq.It.isAnyString())).returns(() => isConnected);
connectionManagementService.setup(x => x.disconnectEditor(TypeMoq.It.isAny())).callback(() => {
countCalledDisconnectEditor++;
@@ -387,7 +387,7 @@ suite('SQL QueryAction Tests', () => {
.returns(() => Promise.resolve(none));
// ... Mock "isConnected" in ConnectionManagementService
let connectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, {}, connectionDialogService.object);
let connectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, {}, {}, new TestStorageService(), connectionDialogService.object);
connectionManagementService.callBase = true;
connectionManagementService.setup(x => x.isConnected(TypeMoq.It.isAnyString())).returns(() => isConnected);
@@ -433,7 +433,7 @@ suite('SQL QueryAction Tests', () => {
.returns(() => Promise.resolve(none));
// ... Mock "isConnected" in ConnectionManagementService
let connectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, {}, connectionDialogService.object);
let connectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, {}, {}, new TestStorageService(), connectionDialogService.object);
connectionManagementService.callBase = true;
connectionManagementService.setup(x => x.isConnected(TypeMoq.It.isAnyString())).returns(() => isConnected);
@@ -468,7 +468,7 @@ suite('SQL QueryAction Tests', () => {
let databaseName: string = undefined;
// ... Mock "isConnected" in ConnectionManagementService
let connectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, {});
let connectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, {}, {}, new TestStorageService());
connectionManagementService.callBase = true;
connectionManagementService.setup(x => x.isConnected(TypeMoq.It.isAnyString())).returns(() => isConnected);
connectionManagementService.setup(x => x.getConnectionProfile(TypeMoq.It.isAny())).returns(() => <IConnectionProfile>{
@@ -505,7 +505,7 @@ suite('SQL QueryAction Tests', () => {
// ... Create mock connection management service
let databaseName = 'foobar';
let cms = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, {});
let cms = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, {}, {}, new TestStorageService());
cms.callBase = true;
cms.setup(x => x.onConnectionChanged).returns(() => dbChangedEmitter.event);
cms.setup(x => x.getConnectionProfile(TypeMoq.It.isAny())).returns(() => <IConnectionProfile>{ databaseName: databaseName });

View File

@@ -36,15 +36,15 @@ export class CredentialsTestProvider implements azdata.CredentialProvider {
export class CredentialsTestService implements ICredentialsService {
_serviceBrand: any;
saveCredential(credentialId: string, password: string): Promise<boolean> {
saveCredential(credentialId: string, password: string): Thenable<boolean> {
return undefined;
}
readCredential(credentialId: string): Promise<azdata.Credential> {
readCredential(credentialId: string): Thenable<azdata.Credential> {
return undefined;
}
deleteCredential(credentialId: string): Promise<boolean> {
deleteCredential(credentialId: string): Thenable<boolean> {
return undefined;
}
@@ -52,4 +52,4 @@ export class CredentialsTestService implements ICredentialsService {
return undefined;
}
}
}