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

View File

@@ -6,11 +6,12 @@
'use strict'; 'use strict';
import { ConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup'; import { ConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup';
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile'; 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 { 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 { 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 { DragMouseEvent } from 'vs/base/browser/mouseEvent';
import { TreeUpdateUtils } from 'sql/parts/objectExplorer/viewlet/treeUpdateUtils'; 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'; import { IDragAndDropData } from 'vs/base/browser/dnd';
/** /**
@@ -18,8 +19,8 @@ import { IDragAndDropData } from 'vs/base/browser/dnd';
*/ */
export class ServerTreeDragAndDrop implements IDragAndDrop { export class ServerTreeDragAndDrop implements IDragAndDrop {
constructor( constructor(@IConnectionManagementService private _connectionManagementService: IConnectionManagementService,
@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 isDropToItself = source && targetConnectionProfileGroup && (source instanceof ConnectionProfileGroup) && source.name === targetConnectionProfileGroup.name;
let isDropToSameLevel = oldParent && oldParent.equals(targetConnectionProfileGroup); 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); return (!isDropToSameLevel && !isDropToItself && !isUnsavedDrag);
} }
} }
@@ -152,6 +153,11 @@ export class ServerTreeDragAndDrop implements IDragAndDrop {
*/ */
export class RecentConnectionsDragAndDrop 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 a uri if the given element should be allowed to drag.
* Returns null, otherwise. * Returns null, otherwise.

View File

@@ -7,16 +7,14 @@
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService'; import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile'; import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
import { ConnectionProfileGroup, IConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup'; 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 { IConnectionProfile, IConnectionProfileStore } from 'sql/platform/connection/common/interfaces';
import * as Utils from 'sql/platform/connection/common/utils'; import * as Utils from 'sql/platform/connection/common/utils';
import { generateUuid } from 'vs/base/common/uuid'; import { generateUuid } from 'vs/base/common/uuid';
import * as nls from 'vs/nls'; import * as nls from 'vs/nls';
import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration';
const GROUPS_CONFIG_KEY = 'datasource.connectionGroups';
const CONNECTIONS_CONFIG_KEY = 'datasource.connections';
export interface ISaveGroupResult { export interface ISaveGroupResult {
groups: IConnectionProfileGroup[]; groups: IConnectionProfileGroup[];
newGroupId: string; newGroupId: string;
@@ -25,7 +23,7 @@ export interface ISaveGroupResult {
/** /**
* Implements connection profile file storage. * Implements connection profile file storage.
*/ */
export class ConnectionConfig { export class ConnectionConfig implements IConnectionConfig {
public constructor( public constructor(
private configurationService: IConfigurationService, private configurationService: IConfigurationService,
@@ -38,7 +36,7 @@ export class ConnectionConfig {
public getAllGroups(): IConnectionProfileGroup[] { public getAllGroups(): IConnectionProfileGroup[] {
let allGroups: 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 (user) {
if (workspace) { if (workspace) {
@@ -47,12 +45,13 @@ export class ConnectionConfig {
} }
allGroups = allGroups.concat(user); allGroups = allGroups.concat(user);
} }
return allGroups.map(g => { allGroups = allGroups.map(g => {
if (g.parentId === '' || !g.parentId) { if (g.parentId === '' || !g.parentId) {
g.parentId = undefined; g.parentId = undefined;
} }
return g; return g;
}); });
return allGroups;
} }
/** /**
@@ -61,7 +60,7 @@ export class ConnectionConfig {
public addConnection(profile: IConnectionProfile): Promise<IConnectionProfile> { public addConnection(profile: IConnectionProfile): Promise<IConnectionProfile> {
if (profile.saveProfile) { if (profile.saveProfile) {
return this.addGroupFromProfile(profile).then(groupId => { 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) { if (!profiles) {
profiles = []; profiles = [];
} }
@@ -70,7 +69,7 @@ export class ConnectionConfig {
let newProfile = ConnectionProfile.convertToProfileStore(this._capabilitiesService, connectionProfile); let newProfile = ConnectionProfile.convertToProfileStore(this._capabilitiesService, connectionProfile);
// Remove the profile if already set // Remove the profile if already set
let sameProfileInList = profiles.find(value => { var sameProfileInList = profiles.find(value => {
let providerConnectionProfile = ConnectionProfile.createFromStoredProfile(value, this._capabilitiesService); let providerConnectionProfile = ConnectionProfile.createFromStoredProfile(value, this._capabilitiesService);
return providerConnectionProfile.matches(connectionProfile); return providerConnectionProfile.matches(connectionProfile);
}); });
@@ -83,7 +82,7 @@ export class ConnectionConfig {
profiles.push(newProfile); 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 { } else {
return Promise.resolve(profile); return Promise.resolve(profile);
@@ -107,11 +106,11 @@ export class ConnectionConfig {
if (profile.groupId && profile.groupId !== Utils.defaultGroupId) { if (profile.groupId && profile.groupId !== Utils.defaultGroupId) {
return Promise.resolve(profile.groupId); return Promise.resolve(profile.groupId);
} else { } 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); let result = this.saveGroup(groups, profile.groupFullName, undefined, undefined);
groups = result.groups; 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) { if (profileGroup.id) {
return Promise.resolve(profileGroup.id); return Promise.resolve(profileGroup.id);
} else { } 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; let sameNameGroup = groups ? groups.find(group => group.name === profileGroup.name) : undefined;
if (sameNameGroup) { if (sameNameGroup) {
let errMessage: string = nls.localize('invalidServerName', "A server group with the same name already exists."); 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); let result = this.saveGroup(groups, profileGroup.name, profileGroup.color, profileGroup.description);
groups = result.groups; 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[] { 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[]; let profiles: IConnectionProfileStore[];
if (configs) { if (configs) {
if (configTarget === ConfigurationTarget.USER) { if (configTarget === ConfigurationTarget.USER) {
@@ -148,7 +147,7 @@ export class ConnectionConfig {
} }
if (profiles) { if (profiles) {
if (this.fixConnectionIds(profiles)) { if (this.fixConnectionIds(profiles)) {
this.configurationService.updateValue(CONNECTIONS_CONFIG_KEY, profiles, configTarget); this.configurationService.updateValue(Constants.connectionsArrayName, profiles, configTarget);
} }
} else { } else {
profiles = []; profiles = [];
@@ -165,7 +164,8 @@ export class ConnectionConfig {
private fixConnectionIds(profiles: IConnectionProfileStore[]): boolean { private fixConnectionIds(profiles: IConnectionProfileStore[]): boolean {
let idsCache: { [label: string]: boolean } = {}; let idsCache: { [label: string]: boolean } = {};
let changed: boolean = false; let changed: boolean = false;
for (let profile of profiles) { for (var index = 0; index < profiles.length; index++) {
var profile = profiles[index];
if (!profile.id) { if (!profile.id) {
profile.id = generateUuid(); profile.id = generateUuid();
changed = true; changed = true;
@@ -215,7 +215,7 @@ export class ConnectionConfig {
*/ */
public deleteConnection(profile: ConnectionProfile): Promise<void> { public deleteConnection(profile: ConnectionProfile): Promise<void> {
// Get all connections in the settings // 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 // Remove the profile from the connections
profiles = profiles.filter(value => { profiles = profiles.filter(value => {
let providerConnectionProfile = ConnectionProfile.createFromStoredProfile(value, this._capabilitiesService); let providerConnectionProfile = ConnectionProfile.createFromStoredProfile(value, this._capabilitiesService);
@@ -223,7 +223,7 @@ export class ConnectionConfig {
}); });
// Write connections back to settings // 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 // Add selected group to subgroups list
subgroups.push(group); subgroups.push(group);
// Get all connections in the settings // 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 // Remove the profiles from the connections
profiles = profiles.filter(value => { profiles = profiles.filter(value => {
let providerConnectionProfile = ConnectionProfile.createFromStoredProfile(value, this._capabilitiesService); let providerConnectionProfile = ConnectionProfile.createFromStoredProfile(value, this._capabilitiesService);
@@ -244,14 +244,14 @@ export class ConnectionConfig {
}); });
// Get all groups in the settings // 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 // Remove subgroups in the settings
groups = groups.filter((grp) => { groups = groups.filter((grp) => {
return !subgroups.some((item) => item.id === grp.id); return !subgroups.some((item) => item.id === grp.id);
}); });
return Promise.all([ return Promise.all([
this.configurationService.updateValue(CONNECTIONS_CONFIG_KEY, profiles, ConfigurationTarget.USER), this.configurationService.updateValue(Constants.connectionsArrayName, profiles, ConfigurationTarget.USER),
this.configurationService.updateValue(GROUPS_CONFIG_KEY, groups, ConfigurationTarget.USER) this.configurationService.updateValue(Constants.connectionGroupsArrayName, groups, ConfigurationTarget.USER)
]).then(() => Promise.resolve()); ]).then(() => Promise.resolve());
} }
@@ -259,14 +259,14 @@ export class ConnectionConfig {
* Moves the source group under the target group. * Moves the source group under the target group.
*/ */
public changeGroupIdForConnectionGroup(source: ConnectionProfileGroup, target: ConnectionProfileGroup): Promise<void> { 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 => { groups = groups.map(g => {
if (g.id === source.id) { if (g.id === source.id) {
g.parentId = target.id; g.parentId = target.id;
} }
return g; 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. * Moves the connection under the target group with the new ID.
*/ */
private changeGroupIdForConnectionInSettings(profile: ConnectionProfile, newGroupID: string, target: ConfigurationTarget = ConfigurationTarget.USER): Promise<void> { 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 : let profiles = target === ConfigurationTarget.USER ? this.configurationService.inspect<IConnectionProfileStore[]>(Constants.connectionsArrayName).user :
this.configurationService.inspect<IConnectionProfileStore[]>(CONNECTIONS_CONFIG_KEY).workspace; this.configurationService.inspect<IConnectionProfileStore[]>(Constants.connectionsArrayName).workspace;
if (profiles) { if (profiles) {
if (profile.parent && profile.parent.id === UNSAVED_GROUP_ID) { if (profile.parent && profile.parent.id === Constants.unsavedGroupId) {
profile.groupId = newGroupID; profile.groupId = newGroupID;
profiles.push(ConnectionProfile.convertToProfileStore(this._capabilitiesService, profile)); profiles.push(ConnectionProfile.convertToProfileStore(this._capabilitiesService, profile));
} else { } 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 { } else {
return Promise.resolve(); return Promise.resolve();
} }
@@ -320,12 +320,14 @@ export class ConnectionConfig {
} }
public saveGroup(groups: IConnectionProfileGroup[], groupFullName: string, color: string, description: string): ISaveGroupResult { public saveGroup(groups: IConnectionProfileGroup[], groupFullName: string, color: string, description: string): ISaveGroupResult {
let result: ISaveGroupResult;
let groupNames = ConnectionProfileGroup.getGroupFullNameParts(groupFullName); 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> { 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; let sameNameGroup = groups ? groups.find(group => group.name === source.name && group.id !== source.id) : undefined;
if (sameNameGroup) { if (sameNameGroup) {
let errMessage: string = nls.localize('invalidServerName', "A server group with the same name already exists."); 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 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 { 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 Utils from 'sql/platform/connection/common/utils';
import * as Constants from 'sql/platform/connection/common/constants'; import * as Constants from 'sql/platform/connection/common/constants';
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService'; 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 * as ConnectionContracts from 'sql/parts/connection/common/connection';
import { ConnectionStatusManager } from 'sql/platform/connection/common/connectionStatusManager'; import { ConnectionStatusManager } from 'sql/platform/connection/common/connectionStatusManager';
import { DashboardInput } from 'sql/parts/dashboard/dashboardInput'; 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 { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IEditorService, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { IEditorService, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService';
import * as platform from 'vs/platform/registry/common/platform'; 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 { 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 { ConnectionProfileGroup, IConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup';
import { Event, Emitter } from 'vs/base/common/event'; import { Event, Emitter } from 'vs/base/common/event';
import { EditorPart } from 'vs/workbench/browser/parts/editor/editorPart'; 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); private _connectionGlobalStatus = new ConnectionGlobalStatus(this._statusBarService);
constructor( constructor(
private _connectionMemento: Memento,
private _connectionStore: ConnectionStore, private _connectionStore: ConnectionStore,
@IStorageService _storageService: IStorageService,
@IConnectionDialogService private _connectionDialogService: IConnectionDialogService, @IConnectionDialogService private _connectionDialogService: IConnectionDialogService,
@IServerGroupController private _serverGroupController: IServerGroupController, @IServerGroupController private _serverGroupController: IServerGroupController,
@IInstantiationService private _instantiationService: IInstantiationService, @IInstantiationService private _instantiationService: IInstantiationService,
@IEditorService private _editorService: IEditorService, @IEditorService private _editorService: IEditorService,
@ITelemetryService private _telemetryService: ITelemetryService, @ITelemetryService private _telemetryService: ITelemetryService,
@IConfigurationService private _configurationService: IConfigurationService, @IConfigurationService private _configurationService: IConfigurationService,
@ICredentialsService private _credentialsService: ICredentialsService,
@ICapabilitiesService private _capabilitiesService: ICapabilitiesService, @ICapabilitiesService private _capabilitiesService: ICapabilitiesService,
@IQuickInputService private _quickInputService: IQuickInputService, @IQuickInputService private _quickInputService: IQuickInputService,
@IEditorGroupsService private _editorGroupService: IEditorGroupsService, @IEditorGroupsService private _editorGroupService: IEditorGroupsService,
@@ -91,8 +97,13 @@ export class ConnectionManagementService extends Disposable implements IConnecti
) { ) {
super(); 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) { if (!this._connectionStore) {
this._connectionStore = _instantiationService.createInstance(ConnectionStore); this._connectionStore = new ConnectionStore(this._connectionMemento,
this._configurationService, this._credentialsService, this._capabilitiesService);
} }
// Register Statusbar item // Register Statusbar item
@@ -124,6 +135,7 @@ export class ConnectionManagementService extends Disposable implements IConnecti
this.onConnectionChanged(() => this.refreshEditorTitles()); this.onConnectionChanged(() => this.refreshEditorTitles());
this.onConnect(() => this.refreshEditorTitles()); this.onConnect(() => this.refreshEditorTitles());
this.onDisconnect(() => this.refreshEditorTitles()); this.onDisconnect(() => this.refreshEditorTitles());
_storageService.onWillSaveState(() => this.shutdown());
} }
public providerRegistered(providerId: string): boolean { public providerRegistered(providerId: string): boolean {
@@ -612,11 +624,11 @@ export class ConnectionManagementService extends Disposable implements IConnecti
} }
public clearRecentConnection(connectionProfile: IConnectionProfile): void { public clearRecentConnection(connectionProfile: IConnectionProfile): void {
this._connectionStore.removeRecentConnection(connectionProfile); this._connectionStore.removeConnectionToMemento(connectionProfile, Constants.recentConnections);
} }
public getActiveConnections(providers?: string[]): ConnectionProfile[] { public getActiveConnections(providers?: string[]): ConnectionProfile[] {
return this._connectionStatusManager.getActiveConnectionProfiles(providers); return this._connectionStatusManager.getActiveConnectionProfiles();
} }
public getConnectionUriFromId(connectionId: string): string { public getConnectionUriFromId(connectionId: string): string {
@@ -849,8 +861,8 @@ export class ConnectionManagementService extends Disposable implements IConnecti
* Add a connection to the active connections list. * Add a connection to the active connections list.
*/ */
private tryAddActiveConnection(connectionManagementInfo: ConnectionManagementInfo, newConnection: IConnectionProfile, addToMru: boolean): void { private tryAddActiveConnection(connectionManagementInfo: ConnectionManagementInfo, newConnection: IConnectionProfile, addToMru: boolean): void {
if (newConnection && addToMru) { if (newConnection) {
this._connectionStore.addRecentConnection(newConnection) this._connectionStore.addActiveConnection(newConnection, addToMru)
.then(() => { .then(() => {
connectionManagementInfo.connectHandler(true); connectionManagementInfo.connectHandler(true);
}, err => { }, err => {
@@ -918,6 +930,11 @@ export class ConnectionManagementService extends Disposable implements IConnecti
public onIntelliSenseCacheComplete(handle: number, connectionUri: string): void { public onIntelliSenseCacheComplete(handle: number, connectionUri: string): void {
} }
private shutdown(): void {
this._connectionStore.clearActiveConnections();
this._connectionMemento.saveMemento();
}
public changeGroupIdForConnectionGroup(source: ConnectionProfileGroup, target: ConnectionProfileGroup): Promise<void> { public changeGroupIdForConnectionGroup(source: ConnectionProfileGroup, target: ConnectionProfileGroup): Promise<void> {
TelemetryUtils.addTelemetry(this._telemetryService, TelemetryKeys.MoveServerConnection); TelemetryUtils.addTelemetry(this._telemetryService, TelemetryKeys.MoveServerConnection);
return this._connectionStore.changeGroupIdForConnectionGroup(source, target); return this._connectionStore.changeGroupIdForConnectionGroup(source, target);
@@ -1079,6 +1096,7 @@ export class ConnectionManagementService extends Disposable implements IConnecti
this.doDisconnect(uri, profile).then(result => { this.doDisconnect(uri, profile).then(result => {
if (result) { if (result) {
this.addTelemetryForConnectionDisconnected(input); this.addTelemetryForConnectionDisconnected(input);
this._connectionStore.removeActiveConnection(input);
this._connectionStatusManager.removeConnection(uri); this._connectionStatusManager.removeConnection(uri);
resolve(); resolve();
} else { } else {

View File

@@ -5,26 +5,21 @@
'use strict'; 'use strict';
import { ReverseLookUpMap } from 'sql/base/common/map'; import * as Constants from 'sql/platform/connection/common/constants';
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService'; import * as ConnInfo from 'sql/platform/connection/common/connectionInfo';
import { ConnectionConfig } from 'sql/platform/connection/common/connectionConfig';
import { fixupConnectionCredentials } from 'sql/platform/connection/common/connectionInfo';
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile'; 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 { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { ICredentialsService } from 'sql/platform/credentials/common/credentialsService'; 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 { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IStateService } from 'vs/platform/state/common/state';
const MAX_CONNECTIONS_DEFAULT = 25; 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 * Manages the connections list including saved profiles and the most recently used connections
* *
@@ -32,15 +27,39 @@ const CRED_PROFILE_USER = 'Profile';
* @class ConnectionStore * @class ConnectionStore
*/ */
export class ConnectionStore { export class ConnectionStore {
private groupIdMap = new ReverseLookUpMap<string, string>(); private _memento: any;
private connectionConfig = new ConnectionConfig(this.configurationService, this.capabilitiesService); private _groupIdToFullNameMap: { [groupId: string]: string };
private _groupFullNameToIdMap: { [groupId: string]: string };
constructor( constructor(
@IStateService private stateService: IStateService, private _context: Memento,
@IConfigurationService private configurationService: IConfigurationService, private _configurationService: IConfigurationService,
@ICredentialsService private credentialService: ICredentialsService, private _credentialService: ICredentialsService,
@ICapabilitiesService private capabilitiesService: ICapabilitiesService 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 * @param {string} itemType type of the item (MRU or Profile) - optional
* @returns {string} formatted string with server, DB and username * @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( let connectionProfileInstance: ConnectionProfile = ConnectionProfile.fromIConnectionProfile(
this.capabilitiesService, connectionProfile); this._capabilitiesService, connectionProfile);
let cred: string[] = [CRED_PREFIX]; if (!connectionProfileInstance.getConnectionInfoId()) {
throw new Error('Missing Id, which is required');
}
let cred: string[] = [ConnectionStore.CRED_PREFIX];
if (!itemType) { if (!itemType) {
itemType = CRED_PROFILE_USER; itemType = ConnectionStore.CRED_PROFILE_USER;
} }
cred.push(CRED_ITEMTYPE_PREFIX.concat(itemType)); ConnectionStore.pushIfNonEmpty(itemType, ConnectionStore.CRED_ITEMTYPE_PREFIX, cred);
cred.push(CRED_ID_PREFIX.concat(connectionProfileInstance.getConnectionInfoId())); ConnectionStore.pushIfNonEmpty(connectionProfileInstance.getConnectionInfoId(), ConnectionStore.CRED_ID_PREFIX, cred);
return cred.join(CRED_SEPARATOR); 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 { public isPasswordRequired(connection: IConnectionProfile): boolean {
if (connection) { if (connection) {
let connectionProfile = ConnectionProfile.fromIConnectionProfile(this.capabilitiesService, connection); let connectionProfile = ConnectionProfile.fromIConnectionProfile(this._capabilitiesService, connection);
return connectionProfile.isPasswordRequired(); return connectionProfile.isPasswordRequired();
} else { } else {
return false; return false;
@@ -78,20 +106,28 @@ export class ConnectionStore {
} }
public addSavedPassword(credentialsItem: IConnectionProfile): Promise<{ profile: IConnectionProfile, savedCred: boolean }> { public addSavedPassword(credentialsItem: IConnectionProfile): Promise<{ profile: IConnectionProfile, savedCred: boolean }> {
if (credentialsItem.savePassword && this.isPasswordRequired(credentialsItem) && !credentialsItem.password) { let self = this;
let credentialId = this.formatCredentialId(credentialsItem, CRED_PROFILE_USER); return new Promise<{ profile: IConnectionProfile, savedCred: boolean }>((resolve, reject) => {
return this.credentialService.readCredential(credentialId) if (credentialsItem.savePassword && this.isPasswordRequired(credentialsItem)
.then(savedCred => { && !credentialsItem.password) {
if (savedCred) {
credentialsItem.password = savedCred.password; let credentialId = this.formatCredentialIdForCred(credentialsItem);
credentialsItem.options['password'] = savedCred.password; self._credentialService.readCredential(credentialId)
} .then(savedCred => {
return { profile: credentialsItem, savedCred: !!savedCred }; if (savedCred) {
}); credentialsItem.password = savedCred.password;
} else { credentialsItem.options['password'] = savedCred.password;
// No need to look up the password }
return Promise.resolve({ profile: credentialsItem, savedCred: credentialsItem.savePassword }); 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 * @returns {Promise<IConnectionProfile>} a Promise that returns the original profile, for help in chaining calls
*/ */
public saveProfile(profile: IConnectionProfile, forceWritePlaintextPassword?: boolean): Promise<IConnectionProfile> { 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 const self = this;
let savedProfile = forceWritePlaintextPassword ? profile : this.getProfileWithoutPassword(profile); return new Promise<IConnectionProfile>((resolve, reject) => {
return this.saveProfileToConfig(savedProfile) // Add the profile to the saved list, taking care to clear out the password field if necessary
.then(savedConnectionProfile => { let savedProfile: IConnectionProfile;
profile.groupId = savedConnectionProfile.groupId; if (forceWritePlaintextPassword) {
profile.id = savedConnectionProfile.id; savedProfile = profile;
// Only save if we successfully added the profile } else {
return this.saveProfilePasswordIfNeeded(profile);
}).then(() => { savedProfile = this.getProfileWithoutPassword(profile);
// Add necessary default properties before returning }
// this is needed to support immediate connections self.saveProfileToConfig(savedProfile)
fixupConnectionCredentials(profile); .then(savedConnectionProfile => {
return profile; 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 * @returns {Promise<string>} a Promise that returns the id of connection group
*/ */
public saveProfileGroup(profile: IConnectionProfileGroup): Promise<string> { 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> { private saveProfileToConfig(profile: IConnectionProfile): Promise<IConnectionProfile> {
if (profile.saveProfile) { const self = this;
return this.connectionConfig.addConnection(profile); return new Promise<IConnectionProfile>((resolve, reject) => {
} else { if (profile.saveProfile) {
return Promise.resolve(profile); 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 * @returns {azdata.ConnectionInfo} the array of connections, empty if none are found
*/ */
public getRecentlyUsedConnections(providers?: string[]): ConnectionProfile[] { 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) { if (providers && providers.length > 0) {
configValues = configValues.filter(c => providers.includes(c.providerName)); configValues = configValues.filter(c => providers.includes(c.providerName));
} }
@@ -155,7 +223,7 @@ export class ConnectionStore {
private convertConfigValuesToConnectionProfiles(configValues: IConnectionProfile[]): ConnectionProfile[] { private convertConfigValuesToConnectionProfiles(configValues: IConnectionProfile[]): ConnectionProfile[] {
return configValues.map(c => { return configValues.map(c => {
if (c) { if (c) {
let connectionProfile = new ConnectionProfile(this.capabilitiesService, c); let connectionProfile = new ConnectionProfile(this._capabilitiesService, c);
if (connectionProfile.saveProfile) { if (connectionProfile.saveProfile) {
if (!connectionProfile.groupFullName && connectionProfile.groupId) { if (!connectionProfile.groupFullName && connectionProfile.groupId) {
connectionProfile.groupFullName = this.getGroupFullName(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 { public getProfileWithoutPassword(conn: IConnectionProfile): ConnectionProfile {
if (conn) { if (conn) {
let savedConn: ConnectionProfile = ConnectionProfile.fromIConnectionProfile(this.capabilitiesService, conn); let savedConn: ConnectionProfile = ConnectionProfile.fromIConnectionProfile(this._capabilitiesService, conn);
savedConn = savedConn.withoutPassword(); savedConn = savedConn.withoutPassword();
return savedConn; return savedConn;
@@ -193,35 +276,71 @@ export class ConnectionStore {
* @param {boolean} addToMru Whether to add this connection to the MRU * @param {boolean} addToMru Whether to add this connection to the MRU
* @returns {Promise<void>} a Promise that returns when the connection was saved * @returns {Promise<void>} a Promise that returns when the connection was saved
*/ */
public addRecentConnection(conn: IConnectionProfile): Promise<void> { public async addActiveConnection(conn: IConnectionProfile, addToMru: boolean): Promise<void> {
let maxConnections = this.getMaxRecentConnectionsCount(); if (addToMru) {
return this.addConnectionToState(conn, RECENT_CONNECTIONS_STATE_KEY, maxConnections, conn.savePassword); await this.addConnectionToMru(conn);
} }
private addConnectionToState(conn: IConnectionProfile, key: string, maxConnections?: number, savePassword?: boolean): Promise<void> { // Only add connections we don't already know about
// Get all profiles if (!this.getActiveConnections().some(existingConn => existingConn.id === conn.id)) {
let configValues = this.getConnectionsFromState(key); await this.addConnectionToMemento(conn, Constants.activeConnections, undefined, conn.savePassword);
let configToSave = this.addToConnectionList(conn, configValues);
if (maxConnections) {
// Remove last element if needed
if (configToSave.length > maxConnections) {
configToSave = configToSave.slice(0, maxConnections);
}
} }
this.stateService.setItem(key, configToSave);
return savePassword ? this.doSavePassword(conn).then() : Promise.resolve();
} }
private removeConnectionFromState(conn: IConnectionProfile, key: string): void { /**
// Get all profiles * Adds the specified connection to the MRU list
let configValues = this.getConnectionsFromState(key); * @param conn The connection to add
let configToSave = this.removeFromConnectionList(conn, configValues); */
private async addConnectionToMru(conn: IConnectionProfile): Promise<void> {
this.stateService.setItem(key, configToSave); let maxConnections = this.getMaxRecentConnectionsCount();
if (ConnectionProfile.isConnectionToDefaultDb(conn)) {
conn.databaseName = '';
}
await this.addConnectionToMemento(conn, Constants.recentConnections, maxConnections);
} }
private getConnectionsFromState(mementoKey: string): ConnectionProfile[] { public addConnectionToMemento(conn: IConnectionProfile, mementoKey: string, maxConnections?: number, savePassword?: boolean): Promise<void> {
return this.convertConfigValuesToConnectionProfiles(this.stateService.getItem<IConnectionProfile[]>(mementoKey, [])); 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[] { private addToConnectionList(conn: IConnectionProfile, list: ConnectionProfile[]): IConnectionProfile[] {
@@ -239,7 +358,11 @@ export class ConnectionStore {
list.unshift(savedProfile); 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[] { private removeFromConnectionList(conn: IConnectionProfile, list: ConnectionProfile[]): IConnectionProfile[] {
@@ -255,18 +378,37 @@ export class ConnectionStore {
return !equal; 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. * Clear all recently used connections from the MRU list.
*/ */
public clearRecentlyUsed(): void { public clearRecentlyUsed(): void {
this.stateService.setItem(RECENT_CONNECTIONS_STATE_KEY, []); this._memento[Constants.recentConnections] = [];
} }
public removeRecentConnection(conn: IConnectionProfile): void { public clearFromMemento(name: string): void {
this.removeConnectionFromState(conn, RECENT_CONNECTIONS_STATE_KEY); 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> { private saveProfilePasswordIfNeeded(profile: IConnectionProfile): Promise<boolean> {
@@ -277,23 +419,32 @@ export class ConnectionStore {
} }
private doSavePassword(conn: IConnectionProfile): Promise<boolean> { private doSavePassword(conn: IConnectionProfile): Promise<boolean> {
if (conn.password) { let self = this;
let credentialId = this.formatCredentialId(conn); return new Promise<boolean>((resolve, reject) => {
return this.credentialService.saveCredential(credentialId, conn.password); if (conn.password) {
} else { let credentialId = this.formatCredentialId(conn);
return Promise.resolve(true); 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[] { public getConnectionProfileGroups(withoutConnections?: boolean, providers?: string[]): ConnectionProfileGroup[] {
let profilesInConfiguration: ConnectionProfile[]; let profilesInConfiguration: ConnectionProfile[];
if (!withoutConnections) { if (!withoutConnections) {
profilesInConfiguration = this.connectionConfig.getConnections(true); profilesInConfiguration = this._connectionConfig.getConnections(true);
if (providers && providers.length > 0) { if (providers && providers.length > 0) {
profilesInConfiguration = profilesInConfiguration.filter(x => providers.includes(x.providerName)); 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); let connectionProfileGroups = this.convertToConnectionGroup(groups, profilesInConfiguration, undefined);
return connectionProfileGroups; return connectionProfileGroups;
@@ -328,53 +479,74 @@ export class ConnectionStore {
} }
public getGroupFromId(groupId: string): IConnectionProfileGroup { public getGroupFromId(groupId: string): IConnectionProfileGroup {
let groups = this.connectionConfig.getAllGroups(); let groups = this._connectionConfig.getAllGroups();
return groups.find(group => group.id === groupId); return groups.find(group => group.id === groupId);
} }
private getMaxRecentConnectionsCount(): number { 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> { public editGroup(group: ConnectionProfileGroup): Promise<any> {
return this.connectionConfig.editGroup(group).then(); 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> { public deleteConnectionFromConfiguration(connection: ConnectionProfile): Promise<void> {
return this.connectionConfig.deleteConnection(connection); return this._connectionConfig.deleteConnection(connection);
} }
public deleteGroupFromConfiguration(group: ConnectionProfileGroup): Promise<void> { public deleteGroupFromConfiguration(group: ConnectionProfileGroup): Promise<void> {
return this.connectionConfig.deleteGroup(group); return this._connectionConfig.deleteGroup(group);
} }
public changeGroupIdForConnectionGroup(source: ConnectionProfileGroup, target: ConnectionProfileGroup): Promise<void> { 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 { 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> { 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 { private addGroupFullNameToMap(groupId: string, groupFullName: string): void {
if (groupId) { if (groupId) {
this.groupIdMap.set(groupId, groupFullName); this._groupIdToFullNameMap[groupId] = groupFullName;
} }
if (groupFullName !== undefined) { if (groupFullName !== undefined) {
this.groupIdMap.set(groupFullName.toUpperCase(), groupId); this._groupFullNameToIdMap[groupFullName.toUpperCase()] = groupId;
} }
} }
private getGroupFullName(groupId: string): string { private getGroupFullName(groupId: string): string {
if (!this.groupIdMap.has(groupId)) { if (groupId in this._groupIdToFullNameMap) {
return this._groupIdToFullNameMap[groupId];
} else {
// Load the cache // Load the cache
this.getConnectionProfileGroups(true); this.getConnectionProfileGroups(true);
} }
return this.groupIdMap.get(groupId); return this._groupIdToFullNameMap[groupId];
} }
private getGroupId(groupFullName: string): string { private getGroupId(groupFullName: string): string {
@@ -382,10 +554,14 @@ export class ConnectionStore {
groupFullName = ''; groupFullName = '';
} }
let key = groupFullName.toUpperCase(); 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 // Load the cache
this.getConnectionProfileGroups(true); 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 sqlConfigSectionName = 'sql';
export const outputChannelName = 'MSSQL'; export const outputChannelName = 'MSSQL';
export const connectionsArrayName = 'datasource.connections';
export const connectionGroupsArrayName = 'datasource.connectionGroups';
/**Unsaved connections Id */
export const unsavedGroupId = 'unsaved';
/* Memento constants */ /* Memento constants */
export const activeConnections = 'ACTIVE_CONNECTIONS';
export const recentConnections = 'RECENT_CONNECTIONS';
export const capabilitiesOptions = 'OPTIONS_METADATA'; export const capabilitiesOptions = 'OPTIONS_METADATA';
export const configMaxRecentConnections = 'maxRecentConnections';
export const mssqlProviderName = 'MSSQL'; export const mssqlProviderName = 'MSSQL';
export const anyProviderName = '*'; export const anyProviderName = '*';
export const connectionProviderContextKey = 'connectionProvider'; export const connectionProviderContextKey = 'connectionProvider';
@@ -24,5 +34,3 @@ export const passwordChars = '***************';
export const sqlLogin = 'SqlLogin'; export const sqlLogin = 'SqlLogin';
export const integrated = 'Integrated'; export const integrated = 'Integrated';
export const azureMFA = 'AzureMFA'; 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'; 'use strict';
import * as assert from 'assert'; import * as TypeMoq from 'typemoq';
import * as azdata from 'azdata';
import { ICapabilitiesService, ProviderFeatures } from 'sql/platform/capabilities/common/capabilitiesService';
import { ConnectionConfig, ISaveGroupResult } from 'sql/platform/connection/common/connectionConfig'; 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 { 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 { ConnectionOptionSpecialType, ServiceOptionType } from 'sql/workbench/api/common/sqlExtHostTypes';
import { CapabilitiesTestService } from 'sqltest/stubs/capabilitiesTestService'; import { CapabilitiesTestService } from 'sqltest/stubs/capabilitiesTestService';
import * as TypeMoq from 'typemoq'; import { TestConfigurationService } from 'sql/platform/connection/test/common/testConfigurationService';
import { Emitter } from 'vs/base/common/event'; import { deepFreeze, deepClone } from 'vs/base/common/objects';
import { deepClone, deepFreeze } from 'vs/base/common/objects'; import { isUndefinedOrNull } from 'vs/base/common/types';
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { ConnectionManagementInfo } from 'sql/platform/connection/common/connectionManagementInfo';
suite('ConnectionConfig', () => { suite('ConnectionConfig', () => {
let capabilitiesService: TypeMoq.Mock<ICapabilitiesService>; let capabilitiesService: TypeMoq.Mock<ICapabilitiesService>;
@@ -231,9 +234,9 @@ suite('ConnectionConfig', () => {
test('getAllGroups should merge user and workspace settings correctly', () => { test('getAllGroups should merge user and workspace settings correctly', () => {
let configurationService = new TestConfigurationService(); 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 // 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 config = new ConnectionConfig(configurationService, capabilitiesService.object);
let allGroups = config.getAllGroups(); let allGroups = config.getAllGroups();
@@ -242,7 +245,7 @@ suite('ConnectionConfig', () => {
assert.ok(groupsAreEqual(allGroups, testGroups), 'the groups returned did not match expectation'); 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 = { let newProfile: IConnectionProfile = {
serverName: 'new server', serverName: 'new server',
databaseName: 'database', databaseName: 'database',
@@ -262,15 +265,15 @@ suite('ConnectionConfig', () => {
}; };
let configurationService = new TestConfigurationService(); let configurationService = new TestConfigurationService();
configurationService.updateValue('datasource.connectionGroups', deepClone(testGroups), ConfigurationTarget.USER); configurationService.updateValue(Constants.connectionGroupsArrayName, deepClone(testGroups), ConfigurationTarget.USER);
configurationService.updateValue('datasource.connections', deepClone(testConnections), ConfigurationTarget.USER); configurationService.updateValue(Constants.connectionsArrayName, deepClone(testConnections), ConfigurationTarget.USER);
let connectionProfile = new ConnectionProfile(capabilitiesService.object, newProfile); let connectionProfile = new ConnectionProfile(capabilitiesService.object, newProfile);
connectionProfile.options['databaseDisplayName'] = 'database'; connectionProfile.options['databaseDisplayName'] = 'database';
let config = new ConnectionConfig(configurationService, capabilitiesService.object); let config = new ConnectionConfig(configurationService, capabilitiesService.object);
let savedConnectionProfile = await config.addConnection(connectionProfile); let savedConnectionProfile = await config.addConnection(connectionProfile);
assert.ok(!!savedConnectionProfile.id); assert.ok(!isUndefinedOrNull(savedConnectionProfile.id));
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('addConnection should not add the new profile to user settings if already exists', async () => { 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(); let configurationService = new TestConfigurationService();
configurationService.updateValue('datasource.connectionGroups', deepClone(testGroups), ConfigurationTarget.USER); configurationService.updateValue(Constants.connectionGroupsArrayName, deepClone(testGroups), ConfigurationTarget.USER);
configurationService.updateValue('datasource.connections', deepClone(testConnections), ConfigurationTarget.USER); configurationService.updateValue(Constants.connectionsArrayName, deepClone(testConnections), ConfigurationTarget.USER);
let connectionProfile = new ConnectionProfile(capabilitiesService.object, newProfile); let connectionProfile = new ConnectionProfile(capabilitiesService.object, newProfile);
connectionProfile.options['databaseDisplayName'] = existingConnection.options['databaseName']; connectionProfile.options['databaseDisplayName'] = existingConnection.options['databaseName'];
@@ -304,7 +307,7 @@ suite('ConnectionConfig', () => {
let savedConnectionProfile = await config.addConnection(connectionProfile); let savedConnectionProfile = await config.addConnection(connectionProfile);
assert.equal(savedConnectionProfile.id, existingConnection.id); 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 () => { 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(); let configurationService = new TestConfigurationService();
configurationService.updateValue('datasource.connectionGroups', deepClone(testGroups), ConfigurationTarget.USER); configurationService.updateValue(Constants.connectionGroupsArrayName, deepClone(testGroups), ConfigurationTarget.USER);
configurationService.updateValue('datasource.connections', deepClone(testConnections), ConfigurationTarget.USER); configurationService.updateValue(Constants.connectionsArrayName, deepClone(testConnections), ConfigurationTarget.USER);
let connectionProfile = new ConnectionProfile(capabilitiesService.object, newProfile); let connectionProfile = new ConnectionProfile(capabilitiesService.object, newProfile);
let config = new ConnectionConfig(configurationService, capabilitiesService.object); let config = new ConnectionConfig(configurationService, capabilitiesService.object);
await config.addConnection(connectionProfile); await config.addConnection(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);
assert.equal(configurationService.inspect<IConnectionProfileStore[]>('datasource.connectionGroups').user.length, testGroups.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', () => { test('getConnections should return connections from user and workspace settings given getWorkspaceConnections set to true', () => {
let configurationService = new TestConfigurationService(); let configurationService = new TestConfigurationService();
configurationService.updateValue('datasource.connections', deepClone(testConnections).slice(0, 1), ConfigurationTarget.USER); configurationService.updateValue(Constants.connectionsArrayName, 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(1, testConnections.length), ConfigurationTarget.WORKSPACE);
let config = new ConnectionConfig(configurationService, capabilitiesService.object); let config = new ConnectionConfig(configurationService, capabilitiesService.object);
let allConnections = config.getConnections(true); let allConnections = config.getConnections(true);
@@ -350,8 +353,8 @@ suite('ConnectionConfig', () => {
test('getConnections should return connections from user settings given getWorkspaceConnections set to false', () => { test('getConnections should return connections from user settings given getWorkspaceConnections set to false', () => {
let configurationService = new TestConfigurationService(); let configurationService = new TestConfigurationService();
configurationService.updateValue('datasource.connections', deepClone(testConnections).slice(0, 2), ConfigurationTarget.USER); configurationService.updateValue(Constants.connectionsArrayName, 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(2, testConnections.length), ConfigurationTarget.WORKSPACE);
let config = new ConnectionConfig(configurationService, capabilitiesService.object); let config = new ConnectionConfig(configurationService, capabilitiesService.object);
let allConnections = config.getConnections(false); let allConnections = config.getConnections(false);
@@ -368,8 +371,8 @@ suite('ConnectionConfig', () => {
return c; return c;
}); });
let configurationService = new TestConfigurationService(); let configurationService = new TestConfigurationService();
configurationService.updateValue('datasource.connections', userConnections, ConfigurationTarget.USER); configurationService.updateValue(Constants.connectionsArrayName, userConnections, ConfigurationTarget.USER);
configurationService.updateValue('datasource.connections', workspaceConnections, ConfigurationTarget.WORKSPACE); configurationService.updateValue(Constants.connectionsArrayName, workspaceConnections, ConfigurationTarget.WORKSPACE);
let config = new ConnectionConfig(configurationService, capabilitiesService.object); let config = new ConnectionConfig(configurationService, capabilitiesService.object);
let allConnections = config.getConnections(false); let allConnections = config.getConnections(false);
@@ -378,7 +381,7 @@ suite('ConnectionConfig', () => {
let userConnection = testConnections.find(u => u.options['serverName'] === connection.serverName); let userConnection = testConnections.find(u => u.options['serverName'] === connection.serverName);
if (userConnection !== undefined) { if (userConnection !== undefined) {
assert.notEqual(connection.id, connection.getOptionsKey()); assert.notEqual(connection.id, connection.getOptionsKey());
assert.ok(!!connection.id); assert.ok(!isUndefinedOrNull(connection.id));
} else { } else {
let workspaceConnection = workspaceConnections.find(u => u.options['serverName'] === connection.serverName); let workspaceConnection = workspaceConnections.find(u => u.options['serverName'] === connection.serverName);
assert.notEqual(connection.id, connection.getOptionsKey()); assert.notEqual(connection.id, connection.getOptionsKey());
@@ -394,7 +397,7 @@ suite('ConnectionConfig', () => {
let color: string = 'red'; let color: string = 'red';
let result: ISaveGroupResult = config.saveGroup(groups, newGroups, color, newGroups); 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'); assert.equal(result.groups.length, testGroups.length + 2, 'The result groups length is invalid');
let newGroup = result.groups.find(g => g.name === 'new-group2'); let newGroup = result.groups.find(g => g.name === 'new-group2');
assert.equal(result.newGroupId, newGroup.id, 'The groups id is invalid'); assert.equal(result.newGroupId, newGroup.id, 'The groups id is invalid');
@@ -407,7 +410,7 @@ suite('ConnectionConfig', () => {
let color: string = 'red'; let color: string = 'red';
let result: ISaveGroupResult = config.saveGroup(groups, newGroups, color, newGroups); 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'); assert.equal(result.groups.length, testGroups.length + 1, 'The result groups length is invalid');
let newGroup = result.groups.find(g => g.name === 'g2-5'); let newGroup = result.groups.find(g => g.name === 'g2-5');
assert.equal(result.newGroupId, newGroup.id, 'The groups id is invalid'); assert.equal(result.newGroupId, newGroup.id, 'The groups id is invalid');
@@ -420,7 +423,7 @@ suite('ConnectionConfig', () => {
let color: string = 'red'; let color: string = 'red';
let result: ISaveGroupResult = config.saveGroup(groups, newGroups, color, newGroups); 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'); assert.equal(result.groups.length, testGroups.length, 'The result groups length is invalid');
let newGroup = result.groups.find(g => g.name === 'g2-1'); let newGroup = result.groups.find(g => g.name === 'g2-1');
assert.equal(result.newGroupId, newGroup.id, 'The groups id is invalid'); assert.equal(result.newGroupId, newGroup.id, 'The groups id is invalid');
@@ -445,7 +448,7 @@ suite('ConnectionConfig', () => {
connectionName: undefined connectionName: undefined
}; };
let configurationService = new TestConfigurationService(); 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 connectionProfile = new ConnectionProfile(capabilitiesService.object, newProfile);
connectionProfile.options['databaseDisplayName'] = 'database'; connectionProfile.options['databaseDisplayName'] = 'database';
@@ -453,7 +456,7 @@ suite('ConnectionConfig', () => {
let config = new ConnectionConfig(configurationService, capabilitiesService.object); let config = new ConnectionConfig(configurationService, capabilitiesService.object);
await config.deleteConnection(connectionProfile); 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 () => { test('deleteConnectionGroup should remove the children connections and subgroups from config', async () => {
@@ -475,8 +478,8 @@ suite('ConnectionConfig', () => {
connectionName: undefined connectionName: undefined
}; };
let configurationService = new TestConfigurationService(); let configurationService = new TestConfigurationService();
configurationService.updateValue('datasource.connections', deepClone(testConnections), ConfigurationTarget.USER); configurationService.updateValue(Constants.connectionsArrayName, deepClone(testConnections), ConfigurationTarget.USER);
configurationService.updateValue('datasource.connectionGroups', deepClone(testGroups), ConfigurationTarget.USER); configurationService.updateValue(Constants.connectionGroupsArrayName, deepClone(testGroups), ConfigurationTarget.USER);
let connectionProfile = new ConnectionProfile(capabilitiesService.object, newProfile); let connectionProfile = new ConnectionProfile(capabilitiesService.object, newProfile);
connectionProfile.options['databaseDisplayName'] = 'database'; connectionProfile.options['databaseDisplayName'] = 'database';
@@ -489,8 +492,8 @@ suite('ConnectionConfig', () => {
let config = new ConnectionConfig(configurationService, capabilitiesService.object); let config = new ConnectionConfig(configurationService, capabilitiesService.object);
await config.deleteGroup(connectionProfileGroup); await config.deleteGroup(connectionProfileGroup);
assert.equal(configurationService.inspect<IConnectionProfileStore[]>('datasource.connections').user.length, testConnections.length - 1); assert.equal(configurationService.inspect<IConnectionProfileStore[]>(Constants.connectionsArrayName).user.length, testConnections.length - 1);
assert.equal(configurationService.inspect<IConnectionProfileGroup[]>('datasource.connectionGroups').user.length, testGroups.length - 2); assert.equal(configurationService.inspect<IConnectionProfileGroup[]>(Constants.connectionGroupsArrayName).user.length, testGroups.length - 2);
}); });
test('deleteConnection should not throw error for connection not in config', async () => { test('deleteConnection should not throw error for connection not in config', async () => {
@@ -512,34 +515,34 @@ suite('ConnectionConfig', () => {
connectionName: undefined connectionName: undefined
}; };
let configurationService = new TestConfigurationService(); 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 connectionProfile = new ConnectionProfile(capabilitiesService.object, newProfile);
let config = new ConnectionConfig(configurationService, capabilitiesService.object); let config = new ConnectionConfig(configurationService, capabilitiesService.object);
await config.deleteConnection(connectionProfile); 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 () => { test('renameGroup should change group name', async () => {
let configurationService = new TestConfigurationService(); 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 connectionProfileGroup = new ConnectionProfileGroup('g-renamed', undefined, 'g2', undefined, undefined);
let config = new ConnectionConfig(configurationService, capabilitiesService.object); let config = new ConnectionConfig(configurationService, capabilitiesService.object);
await config.editGroup(connectionProfileGroup); 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); assert.equal(editedGroups.length, testGroups.length);
let editedGroup = editedGroups.find(group => group.id === 'g2'); let editedGroup = editedGroups.find(group => group.id === 'g2');
assert.ok(!!editedGroup); assert.ok(!isUndefinedOrNull(editedGroup));
assert.equal(editedGroup.name, 'g-renamed'); assert.equal(editedGroup.name, 'g-renamed');
}); });
test('edit group should throw if there is a confliction', async () => { test('edit group should throw if there is a confliction', async () => {
let configurationService = new TestConfigurationService(); 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 sameNameGroup = new ConnectionProfileGroup('g3', undefined, 'g2', undefined, undefined);
let config = new ConnectionConfig(configurationService, capabilitiesService.object); let config = new ConnectionConfig(configurationService, capabilitiesService.object);
@@ -548,27 +551,27 @@ suite('ConnectionConfig', () => {
await config.editGroup(sameNameGroup); await config.editGroup(sameNameGroup);
assert.fail(); assert.fail();
} catch (e) { } 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'); let originalGroup = groups.find(g => g.id === 'g2');
assert.ok(!!originalGroup); assert.ok(!isUndefinedOrNull(originalGroup));
assert.equal(originalGroup.name, 'g2'); assert.equal(originalGroup.name, 'g2');
} }
}); });
test('change group(parent) for connection group', async () => { test('change group(parent) for connection group', async () => {
let configurationService = new TestConfigurationService(); 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 sourceProfileGroup = new ConnectionProfileGroup('g2', undefined, 'g2', undefined, undefined);
let targetProfileGroup = new ConnectionProfileGroup('g3', undefined, 'g3', undefined, undefined); let targetProfileGroup = new ConnectionProfileGroup('g3', undefined, 'g3', undefined, undefined);
let config = new ConnectionConfig(configurationService, capabilitiesService.object); let config = new ConnectionConfig(configurationService, capabilitiesService.object);
await config.changeGroupIdForConnectionGroup(sourceProfileGroup, targetProfileGroup); 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); assert.equal(editedGroups.length, testGroups.length);
let editedGroup = editedGroups.find(group => group.id === 'g2'); let editedGroup = editedGroups.find(group => group.id === 'g2');
assert.ok(!!editedGroup); assert.ok(!isUndefinedOrNull(editedGroup));
assert.equal(editedGroup.parentId, 'g3'); assert.equal(editedGroup.parentId, 'g3');
}); });
@@ -612,7 +615,7 @@ suite('ConnectionConfig', () => {
let _testConnections = deepClone(testConnections).concat([existingProfile, changingProfile]); let _testConnections = deepClone(testConnections).concat([existingProfile, changingProfile]);
let configurationService = new TestConfigurationService(); 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); let connectionProfile = new ConnectionProfile(capabilitiesService.object, changingProfile);
@@ -621,11 +624,11 @@ suite('ConnectionConfig', () => {
await config.changeGroupIdForConnection(connectionProfile, 'test'); await config.changeGroupIdForConnection(connectionProfile, 'test');
assert.fail(); assert.fail();
} catch (e) { } catch (e) {
let editedConnections = configurationService.inspect<IConnectionProfileStore[]>('datasource.connections').user; let editedConnections = configurationService.inspect<IConnectionProfileStore[]>(Constants.connectionsArrayName).user;
// two // two
assert.equal(editedConnections.length, _testConnections.length); assert.equal(editedConnections.length, _testConnections.length);
let editedConnection = editedConnections.find(con => con.id === 'server3-2'); let editedConnection = editedConnections.find(con => con.id === 'server3-2');
assert.ok(!!editedConnection); assert.ok(!isUndefinedOrNull(editedConnection));
assert.equal(editedConnection.groupId, 'g3'); assert.equal(editedConnection.groupId, 'g3');
} }
}); });
@@ -650,7 +653,7 @@ suite('ConnectionConfig', () => {
}; };
let configurationService = new TestConfigurationService(); 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 connectionProfile = new ConnectionProfile(capabilitiesService.object, newProfile);
let newId = 'newid'; let newId = 'newid';
@@ -658,18 +661,18 @@ suite('ConnectionConfig', () => {
let config = new ConnectionConfig(configurationService, capabilitiesService.object); let config = new ConnectionConfig(configurationService, capabilitiesService.object);
await config.changeGroupIdForConnection(connectionProfile, newId); 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); assert.equal(editedConnections.length, testConnections.length);
let editedConnection = editedConnections.find(con => con.id === 'server3'); let editedConnection = editedConnections.find(con => con.id === 'server3');
assert.ok(!!editedConnection); assert.ok(!isUndefinedOrNull(editedConnection));
assert.equal(editedConnection.groupId, 'newid'); assert.equal(editedConnection.groupId, 'newid');
}); });
test('addConnection should not move the connection when editing', async () => { test('addConnection should not move the connection when editing', async () => {
// Set up the connection config // Set up the connection config
let configurationService = new TestConfigurationService(); let configurationService = new TestConfigurationService();
configurationService.updateValue('datasource.connections', deepClone(testConnections), ConfigurationTarget.USER); configurationService.updateValue(Constants.connectionsArrayName, deepClone(testConnections), ConfigurationTarget.USER);
configurationService.updateValue('datasource.connectionGroups', deepClone(testGroups), ConfigurationTarget.USER); configurationService.updateValue(Constants.connectionGroupsArrayName, deepClone(testGroups), ConfigurationTarget.USER);
let config = new ConnectionConfig(configurationService, capabilitiesService.object); let config = new ConnectionConfig(configurationService, capabilitiesService.object);
// Clone a connection and modify an option // Clone a connection and modify an option
@@ -699,13 +702,13 @@ suite('ConnectionConfig', () => {
description: 'new group' description: 'new group'
}; };
let configurationService = new TestConfigurationService(); 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); let config = new ConnectionConfig(configurationService, capabilitiesService.object);
await config.addGroup(newGroup); 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); assert.equal(editGroups.length, testGroups.length + 1);
}); });
@@ -719,14 +722,14 @@ suite('ConnectionConfig', () => {
description: 'new group' description: 'new group'
}; };
let configurationService = new TestConfigurationService(); 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); const config = new ConnectionConfig(configurationService, capabilitiesService.object);
try { try {
await config.addGroup(existingGroupName); await config.addGroup(existingGroupName);
assert.fail(); assert.fail();
} catch (e) { } catch (e) {
let editGroups = configurationService.inspect<IConnectionProfileGroup[]>('datasource.connectionGroups').user; let editGroups = configurationService.inspect<IConnectionProfileGroup[]>(Constants.connectionGroupsArrayName).user;
assert.equal(editGroups.length, testGroups.length); 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()); return Promise.resolve(this.getValue());
} }
public getValue(arg1?: any): any { public getValue(arg1?: any, arg2?: any): any {
return getConfigurationValue(this.configuration.user, arg1); 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> { 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 { export interface ICredentialsService {
_serviceBrand: any; _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; 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)); 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)); 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)); 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(), 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)); 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)); 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)); 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>( connectionStore.setup(x => x.addSavedPassword(TypeMoq.It.is<IConnectionProfile>(
@@ -147,13 +147,16 @@ suite('SQL ConnectionManagementService tests', () => {
function createConnectionManagementService(): ConnectionManagementService { function createConnectionManagementService(): ConnectionManagementService {
let connectionManagementService = new ConnectionManagementService( let connectionManagementService = new ConnectionManagementService(
undefined,
connectionStore.object, connectionStore.object,
new TestStorageService(),
connectionDialogService.object, connectionDialogService.object,
undefined, undefined,
undefined, undefined,
workbenchEditorService.object, workbenchEditorService.object,
undefined, undefined,
workspaceConfigurationServiceMock.object, workspaceConfigurationServiceMock.object,
undefined,
capabilitiesService, capabilitiesService,
undefined, undefined,
editorGroupService.object, 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 { ErrorMessageServiceStub } from 'sqltest/stubs/errorMessageServiceStub';
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
import { ServerTreeView } from 'sql/parts/objectExplorer/viewlet/serverTreeView'; 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 * as LocalizedConstants from 'sql/parts/connection/common/localizedConstants';
import { ObjectExplorerService, ObjectExplorerNodeEventArgs } from 'sql/workbench/services/objectExplorer/common/objectExplorerService'; import { ObjectExplorerService, ObjectExplorerNodeEventArgs } from 'sql/workbench/services/objectExplorer/common/objectExplorerService';
import { TreeNode } from 'sql/parts/objectExplorer/common/treeNode'; 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 { IConnectionResult, IConnectionParams } from 'sql/platform/connection/common/connectionManagement';
import { TreeSelectionHandler } from 'sql/parts/objectExplorer/viewlet/treeSelectionHandler'; import { TreeSelectionHandler } from 'sql/parts/objectExplorer/viewlet/treeSelectionHandler';
import { CapabilitiesTestService } from 'sqltest/stubs/capabilitiesTestService'; import { CapabilitiesTestService } from 'sqltest/stubs/capabilitiesTestService';
import { UNSAVED_GROUP_ID } from 'sql/platform/connection/common/constants';
suite('SQL Connection Tree Action tests', () => { suite('SQL Connection Tree Action tests', () => {
let errorMessageService: TypeMoq.Mock<ErrorMessageServiceStub>; let errorMessageService: TypeMoq.Mock<ErrorMessageServiceStub>;
@@ -328,7 +328,7 @@ suite('SQL Connection Tree Action tests', () => {
saveProfile: true, saveProfile: true,
id: 'testId' 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, let connectionAction: DeleteConnectionAction = new DeleteConnectionAction(DeleteConnectionAction.ID,
DeleteConnectionAction.DELETE_CONNECTION_LABEL, DeleteConnectionAction.DELETE_CONNECTION_LABEL,
connection, connection,
@@ -513,4 +513,4 @@ suite('SQL Connection Tree Action tests', () => {
}).then(() => done(), (err) => done(err)); }).then(() => done(), (err) => done(err));
}); });
}); });

View File

@@ -124,7 +124,7 @@ suite('SQL QueryAction Tests', () => {
.returns(() => Promise.resolve(none)); .returns(() => Promise.resolve(none));
// ... Mock "isConnected" in ConnectionManagementService // ... 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.callBase = true;
connectionManagementService.setup(x => x.isConnected(TypeMoq.It.isAnyString())).returns(() => isConnected); 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); queryEditor.setup(x => x.isSelectionEmpty()).returns(() => isSelectionEmpty);
// ... Mock "isConnected" in ConnectionManagementService // ... 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.callBase = true;
connectionManagementService.setup(x => x.isConnected(TypeMoq.It.isAnyString())).returns(() => true); connectionManagementService.setup(x => x.isConnected(TypeMoq.It.isAnyString())).returns(() => true);
@@ -249,7 +249,7 @@ suite('SQL QueryAction Tests', () => {
}); });
// ... Mock "isConnected" in ConnectionManagementService // ... 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.callBase = true;
connectionManagementService.setup(x => x.isConnected(TypeMoq.It.isAnyString())).returns(() => isConnected); connectionManagementService.setup(x => x.isConnected(TypeMoq.It.isAnyString())).returns(() => isConnected);
@@ -315,7 +315,7 @@ suite('SQL QueryAction Tests', () => {
let calledCancelQuery: boolean = false; let calledCancelQuery: boolean = false;
// ... Mock "isConnected" in ConnectionManagementService // ... 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); connectionManagementService.setup(x => x.isConnected(TypeMoq.It.isAnyString())).returns(() => isConnected);
// ... Mock QueryModelService // ... Mock QueryModelService
@@ -348,7 +348,7 @@ suite('SQL QueryAction Tests', () => {
let countCalledDisconnectEditor: number = 0; let countCalledDisconnectEditor: number = 0;
// ... Mock "isConnected" and "disconnectEditor" in ConnectionManagementService // ... 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.isConnected(TypeMoq.It.isAnyString())).returns(() => isConnected);
connectionManagementService.setup(x => x.disconnectEditor(TypeMoq.It.isAny())).callback(() => { connectionManagementService.setup(x => x.disconnectEditor(TypeMoq.It.isAny())).callback(() => {
countCalledDisconnectEditor++; countCalledDisconnectEditor++;
@@ -387,7 +387,7 @@ suite('SQL QueryAction Tests', () => {
.returns(() => Promise.resolve(none)); .returns(() => Promise.resolve(none));
// ... Mock "isConnected" in ConnectionManagementService // ... 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.callBase = true;
connectionManagementService.setup(x => x.isConnected(TypeMoq.It.isAnyString())).returns(() => isConnected); connectionManagementService.setup(x => x.isConnected(TypeMoq.It.isAnyString())).returns(() => isConnected);
@@ -433,7 +433,7 @@ suite('SQL QueryAction Tests', () => {
.returns(() => Promise.resolve(none)); .returns(() => Promise.resolve(none));
// ... Mock "isConnected" in ConnectionManagementService // ... 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.callBase = true;
connectionManagementService.setup(x => x.isConnected(TypeMoq.It.isAnyString())).returns(() => isConnected); connectionManagementService.setup(x => x.isConnected(TypeMoq.It.isAnyString())).returns(() => isConnected);
@@ -468,7 +468,7 @@ suite('SQL QueryAction Tests', () => {
let databaseName: string = undefined; let databaseName: string = undefined;
// ... Mock "isConnected" in ConnectionManagementService // ... 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.callBase = true;
connectionManagementService.setup(x => x.isConnected(TypeMoq.It.isAnyString())).returns(() => isConnected); connectionManagementService.setup(x => x.isConnected(TypeMoq.It.isAnyString())).returns(() => isConnected);
connectionManagementService.setup(x => x.getConnectionProfile(TypeMoq.It.isAny())).returns(() => <IConnectionProfile>{ connectionManagementService.setup(x => x.getConnectionProfile(TypeMoq.It.isAny())).returns(() => <IConnectionProfile>{
@@ -505,7 +505,7 @@ suite('SQL QueryAction Tests', () => {
// ... Create mock connection management service // ... Create mock connection management service
let databaseName = 'foobar'; 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.callBase = true;
cms.setup(x => x.onConnectionChanged).returns(() => dbChangedEmitter.event); cms.setup(x => x.onConnectionChanged).returns(() => dbChangedEmitter.event);
cms.setup(x => x.getConnectionProfile(TypeMoq.It.isAny())).returns(() => <IConnectionProfile>{ databaseName: databaseName }); 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 { export class CredentialsTestService implements ICredentialsService {
_serviceBrand: any; _serviceBrand: any;
saveCredential(credentialId: string, password: string): Promise<boolean> { saveCredential(credentialId: string, password: string): Thenable<boolean> {
return undefined; return undefined;
} }
readCredential(credentialId: string): Promise<azdata.Credential> { readCredential(credentialId: string): Thenable<azdata.Credential> {
return undefined; return undefined;
} }
deleteCredential(credentialId: string): Promise<boolean> { deleteCredential(credentialId: string): Thenable<boolean> {
return undefined; return undefined;
} }
@@ -52,4 +52,4 @@ export class CredentialsTestService implements ICredentialsService {
return undefined; return undefined;
} }
} }