mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-14 01:25:37 -05:00
This reverts commit 756f77063a.
This commit is contained in:
@@ -15,48 +15,3 @@ export function toObject<V>(map: Map<string, V>): { [key: string]: V } {
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
export class ReverseLookUpMap<K, V> {
|
||||
private forward = new Map<K, V>();
|
||||
private reverse = new Map<V, K>();
|
||||
|
||||
public clear(): void {
|
||||
this.forward.clear();
|
||||
this.reverse.clear();
|
||||
}
|
||||
|
||||
public delete(key: K): boolean {
|
||||
let reverseKey = this.forward.get(key);
|
||||
return this.forward.delete(key) && this.reverse.delete(reverseKey);
|
||||
}
|
||||
|
||||
public forEach(callbackfn: (value: V, index: K, map: Map<K, V>) => void, thisArg?: any): void {
|
||||
this.forward.forEach(callbackfn, thisArg);
|
||||
}
|
||||
|
||||
public get(key: K): V {
|
||||
return this.forward.get(key);
|
||||
}
|
||||
|
||||
public reverseGet(key: V): K {
|
||||
return this.reverse.get(key);
|
||||
}
|
||||
|
||||
public has(key: K): boolean {
|
||||
return this.forward.has(key);
|
||||
}
|
||||
|
||||
public reverseHas(key: V): boolean {
|
||||
return this.reverse.has(key);
|
||||
}
|
||||
|
||||
public set(key: K, value: V): ReverseLookUpMap<K, V> {
|
||||
this.forward.set(key, value);
|
||||
this.reverse.set(value, key);
|
||||
return this;
|
||||
}
|
||||
|
||||
public get size(): number {
|
||||
return this.forward.size;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
|
||||
import { ConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup';
|
||||
import * as TaskUtilities from 'sql/workbench/common/taskUtilities';
|
||||
import { ITree } from 'vs/base/parts/tree/browser/tree';
|
||||
import * as Constants from 'sql/platform/connection/common/constants';
|
||||
import { IObjectExplorerService } from 'sql/workbench/services/objectExplorer/common/objectExplorerService';
|
||||
import { TreeNode } from 'sql/parts/objectExplorer/common/treeNode';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
@@ -22,7 +23,6 @@ import { ObjectExplorerActionsContext } from 'sql/parts/objectExplorer/viewlet/o
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IErrorMessageService } from 'sql/platform/errorMessage/common/errorMessageService';
|
||||
import { ConnectionViewletPanel } from 'sql/workbench/parts/dataExplorer/browser/connectionViewletPanel';
|
||||
import { UNSAVED_GROUP_ID } from 'sql/platform/connection/common/constants';
|
||||
|
||||
export class RefreshAction extends Action {
|
||||
|
||||
@@ -356,13 +356,13 @@ export class DeleteConnectionAction extends Action {
|
||||
) {
|
||||
super(id, label);
|
||||
this.class = 'delete-connection-action';
|
||||
if (element instanceof ConnectionProfileGroup && element.id === UNSAVED_GROUP_ID) {
|
||||
if (element instanceof ConnectionProfileGroup && element.id === Constants.unsavedGroupId) {
|
||||
this.enabled = false;
|
||||
}
|
||||
|
||||
if (element instanceof ConnectionProfile) {
|
||||
let parent: ConnectionProfileGroup = element.parent;
|
||||
if (parent && parent.id === UNSAVED_GROUP_ID) {
|
||||
if (parent && parent.id === Constants.unsavedGroupId) {
|
||||
this.enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,11 +6,12 @@
|
||||
'use strict';
|
||||
import { ConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup';
|
||||
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
|
||||
import { ITree, IDragAndDrop, IDragOverReaction, DRAG_OVER_ACCEPT_BUBBLE_DOWN, DRAG_OVER_REJECT } from 'vs/base/parts/tree/browser/tree';
|
||||
import * as Constants from 'sql/platform/connection/common/constants';
|
||||
import { DragMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { TreeUpdateUtils } from 'sql/parts/objectExplorer/viewlet/treeUpdateUtils';
|
||||
import { UNSAVED_GROUP_ID } from 'sql/platform/connection/common/constants';
|
||||
import { IDragAndDropData } from 'vs/base/browser/dnd';
|
||||
|
||||
/**
|
||||
@@ -18,8 +19,8 @@ import { IDragAndDropData } from 'vs/base/browser/dnd';
|
||||
*/
|
||||
export class ServerTreeDragAndDrop implements IDragAndDrop {
|
||||
|
||||
constructor(
|
||||
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService
|
||||
constructor(@IConnectionManagementService private _connectionManagementService: IConnectionManagementService,
|
||||
@IInstantiationService private _instantiationService: IInstantiationService
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -142,7 +143,7 @@ export class ServerTreeDragAndDrop implements IDragAndDrop {
|
||||
|
||||
let isDropToItself = source && targetConnectionProfileGroup && (source instanceof ConnectionProfileGroup) && source.name === targetConnectionProfileGroup.name;
|
||||
let isDropToSameLevel = oldParent && oldParent.equals(targetConnectionProfileGroup);
|
||||
let isUnsavedDrag = source && (source instanceof ConnectionProfileGroup) && (source.id === UNSAVED_GROUP_ID);
|
||||
let isUnsavedDrag = source && (source instanceof ConnectionProfileGroup) && (source.id === Constants.unsavedGroupId);
|
||||
return (!isDropToSameLevel && !isDropToItself && !isUnsavedDrag);
|
||||
}
|
||||
}
|
||||
@@ -152,6 +153,11 @@ export class ServerTreeDragAndDrop implements IDragAndDrop {
|
||||
*/
|
||||
export class RecentConnectionsDragAndDrop implements IDragAndDrop {
|
||||
|
||||
constructor(@IConnectionManagementService private connectionManagementService: IConnectionManagementService,
|
||||
@IInstantiationService private instantiationService: IInstantiationService
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a uri if the given element should be allowed to drag.
|
||||
* Returns null, otherwise.
|
||||
|
||||
@@ -7,16 +7,14 @@
|
||||
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
|
||||
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
|
||||
import { ConnectionProfileGroup, IConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup';
|
||||
import { UNSAVED_GROUP_ID } from 'sql/platform/connection/common/constants';
|
||||
import * as Constants from 'sql/platform/connection/common/constants';
|
||||
import { IConnectionConfig } from 'sql/platform/connection/common/iconnectionConfig';
|
||||
import { IConnectionProfile, IConnectionProfileStore } from 'sql/platform/connection/common/interfaces';
|
||||
import * as Utils from 'sql/platform/connection/common/utils';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import * as nls from 'vs/nls';
|
||||
import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
|
||||
const GROUPS_CONFIG_KEY = 'datasource.connectionGroups';
|
||||
const CONNECTIONS_CONFIG_KEY = 'datasource.connections';
|
||||
|
||||
export interface ISaveGroupResult {
|
||||
groups: IConnectionProfileGroup[];
|
||||
newGroupId: string;
|
||||
@@ -25,7 +23,7 @@ export interface ISaveGroupResult {
|
||||
/**
|
||||
* Implements connection profile file storage.
|
||||
*/
|
||||
export class ConnectionConfig {
|
||||
export class ConnectionConfig implements IConnectionConfig {
|
||||
|
||||
public constructor(
|
||||
private configurationService: IConfigurationService,
|
||||
@@ -38,7 +36,7 @@ export class ConnectionConfig {
|
||||
public getAllGroups(): IConnectionProfileGroup[] {
|
||||
|
||||
let allGroups: IConnectionProfileGroup[] = [];
|
||||
let { user, workspace } = this.configurationService.inspect<IConnectionProfileGroup[]>(GROUPS_CONFIG_KEY);
|
||||
let { user, workspace } = this.configurationService.inspect<IConnectionProfileGroup[]>(Constants.connectionGroupsArrayName);
|
||||
|
||||
if (user) {
|
||||
if (workspace) {
|
||||
@@ -47,12 +45,13 @@ export class ConnectionConfig {
|
||||
}
|
||||
allGroups = allGroups.concat(user);
|
||||
}
|
||||
return allGroups.map(g => {
|
||||
allGroups = allGroups.map(g => {
|
||||
if (g.parentId === '' || !g.parentId) {
|
||||
g.parentId = undefined;
|
||||
}
|
||||
return g;
|
||||
});
|
||||
return allGroups;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -61,7 +60,7 @@ export class ConnectionConfig {
|
||||
public addConnection(profile: IConnectionProfile): Promise<IConnectionProfile> {
|
||||
if (profile.saveProfile) {
|
||||
return this.addGroupFromProfile(profile).then(groupId => {
|
||||
let profiles = this.configurationService.inspect<IConnectionProfileStore[]>(CONNECTIONS_CONFIG_KEY).user;
|
||||
let profiles = this.configurationService.inspect<IConnectionProfileStore[]>(Constants.connectionsArrayName).user;
|
||||
if (!profiles) {
|
||||
profiles = [];
|
||||
}
|
||||
@@ -70,7 +69,7 @@ export class ConnectionConfig {
|
||||
let newProfile = ConnectionProfile.convertToProfileStore(this._capabilitiesService, connectionProfile);
|
||||
|
||||
// Remove the profile if already set
|
||||
let sameProfileInList = profiles.find(value => {
|
||||
var sameProfileInList = profiles.find(value => {
|
||||
let providerConnectionProfile = ConnectionProfile.createFromStoredProfile(value, this._capabilitiesService);
|
||||
return providerConnectionProfile.matches(connectionProfile);
|
||||
});
|
||||
@@ -83,7 +82,7 @@ export class ConnectionConfig {
|
||||
profiles.push(newProfile);
|
||||
}
|
||||
|
||||
return this.configurationService.updateValue(CONNECTIONS_CONFIG_KEY, profiles, ConfigurationTarget.USER).then(() => connectionProfile);
|
||||
return this.configurationService.updateValue(Constants.connectionsArrayName, profiles, ConfigurationTarget.USER).then(() => connectionProfile);
|
||||
});
|
||||
} else {
|
||||
return Promise.resolve(profile);
|
||||
@@ -107,11 +106,11 @@ export class ConnectionConfig {
|
||||
if (profile.groupId && profile.groupId !== Utils.defaultGroupId) {
|
||||
return Promise.resolve(profile.groupId);
|
||||
} else {
|
||||
let groups = this.configurationService.inspect<IConnectionProfileGroup[]>(GROUPS_CONFIG_KEY).user;
|
||||
let groups = this.configurationService.inspect<IConnectionProfileGroup[]>(Constants.connectionGroupsArrayName).user;
|
||||
let result = this.saveGroup(groups, profile.groupFullName, undefined, undefined);
|
||||
groups = result.groups;
|
||||
|
||||
return this.configurationService.updateValue(GROUPS_CONFIG_KEY, groups, ConfigurationTarget.USER).then(() => result.newGroupId);
|
||||
return this.configurationService.updateValue(Constants.connectionGroupsArrayName, groups, ConfigurationTarget.USER).then(() => result.newGroupId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,7 +122,7 @@ export class ConnectionConfig {
|
||||
if (profileGroup.id) {
|
||||
return Promise.resolve(profileGroup.id);
|
||||
} else {
|
||||
let groups = this.configurationService.inspect<IConnectionProfileGroup[]>(GROUPS_CONFIG_KEY).user;
|
||||
let groups = this.configurationService.inspect<IConnectionProfileGroup[]>(Constants.connectionGroupsArrayName).user;
|
||||
let sameNameGroup = groups ? groups.find(group => group.name === profileGroup.name) : undefined;
|
||||
if (sameNameGroup) {
|
||||
let errMessage: string = nls.localize('invalidServerName', "A server group with the same name already exists.");
|
||||
@@ -132,13 +131,13 @@ export class ConnectionConfig {
|
||||
let result = this.saveGroup(groups, profileGroup.name, profileGroup.color, profileGroup.description);
|
||||
groups = result.groups;
|
||||
|
||||
return this.configurationService.updateValue(GROUPS_CONFIG_KEY, groups, ConfigurationTarget.USER).then(() => result.newGroupId);
|
||||
return this.configurationService.updateValue(Constants.connectionGroupsArrayName, groups, ConfigurationTarget.USER).then(() => result.newGroupId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private getConnectionProfilesForTarget(configTarget: ConfigurationTarget): IConnectionProfileStore[] {
|
||||
let configs = this.configurationService.inspect<IConnectionProfileStore[]>(CONNECTIONS_CONFIG_KEY);
|
||||
let configs = this.configurationService.inspect<IConnectionProfileStore[]>(Constants.connectionsArrayName);
|
||||
let profiles: IConnectionProfileStore[];
|
||||
if (configs) {
|
||||
if (configTarget === ConfigurationTarget.USER) {
|
||||
@@ -148,7 +147,7 @@ export class ConnectionConfig {
|
||||
}
|
||||
if (profiles) {
|
||||
if (this.fixConnectionIds(profiles)) {
|
||||
this.configurationService.updateValue(CONNECTIONS_CONFIG_KEY, profiles, configTarget);
|
||||
this.configurationService.updateValue(Constants.connectionsArrayName, profiles, configTarget);
|
||||
}
|
||||
} else {
|
||||
profiles = [];
|
||||
@@ -165,7 +164,8 @@ export class ConnectionConfig {
|
||||
private fixConnectionIds(profiles: IConnectionProfileStore[]): boolean {
|
||||
let idsCache: { [label: string]: boolean } = {};
|
||||
let changed: boolean = false;
|
||||
for (let profile of profiles) {
|
||||
for (var index = 0; index < profiles.length; index++) {
|
||||
var profile = profiles[index];
|
||||
if (!profile.id) {
|
||||
profile.id = generateUuid();
|
||||
changed = true;
|
||||
@@ -215,7 +215,7 @@ export class ConnectionConfig {
|
||||
*/
|
||||
public deleteConnection(profile: ConnectionProfile): Promise<void> {
|
||||
// Get all connections in the settings
|
||||
let profiles = this.configurationService.inspect<IConnectionProfileStore[]>(CONNECTIONS_CONFIG_KEY).user;
|
||||
let profiles = this.configurationService.inspect<IConnectionProfileStore[]>(Constants.connectionsArrayName).user;
|
||||
// Remove the profile from the connections
|
||||
profiles = profiles.filter(value => {
|
||||
let providerConnectionProfile = ConnectionProfile.createFromStoredProfile(value, this._capabilitiesService);
|
||||
@@ -223,7 +223,7 @@ export class ConnectionConfig {
|
||||
});
|
||||
|
||||
// Write connections back to settings
|
||||
return this.configurationService.updateValue(CONNECTIONS_CONFIG_KEY, profiles, ConfigurationTarget.USER);
|
||||
return this.configurationService.updateValue(Constants.connectionsArrayName, profiles, ConfigurationTarget.USER);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -236,7 +236,7 @@ export class ConnectionConfig {
|
||||
// Add selected group to subgroups list
|
||||
subgroups.push(group);
|
||||
// Get all connections in the settings
|
||||
let profiles = this.configurationService.inspect<IConnectionProfileStore[]>(CONNECTIONS_CONFIG_KEY).user;
|
||||
let profiles = this.configurationService.inspect<IConnectionProfileStore[]>(Constants.connectionsArrayName).user;
|
||||
// Remove the profiles from the connections
|
||||
profiles = profiles.filter(value => {
|
||||
let providerConnectionProfile = ConnectionProfile.createFromStoredProfile(value, this._capabilitiesService);
|
||||
@@ -244,14 +244,14 @@ export class ConnectionConfig {
|
||||
});
|
||||
|
||||
// Get all groups in the settings
|
||||
let groups = this.configurationService.inspect<IConnectionProfileGroup[]>(GROUPS_CONFIG_KEY).user;
|
||||
let groups = this.configurationService.inspect<IConnectionProfileGroup[]>(Constants.connectionGroupsArrayName).user;
|
||||
// Remove subgroups in the settings
|
||||
groups = groups.filter((grp) => {
|
||||
return !subgroups.some((item) => item.id === grp.id);
|
||||
});
|
||||
return Promise.all([
|
||||
this.configurationService.updateValue(CONNECTIONS_CONFIG_KEY, profiles, ConfigurationTarget.USER),
|
||||
this.configurationService.updateValue(GROUPS_CONFIG_KEY, groups, ConfigurationTarget.USER)
|
||||
this.configurationService.updateValue(Constants.connectionsArrayName, profiles, ConfigurationTarget.USER),
|
||||
this.configurationService.updateValue(Constants.connectionGroupsArrayName, groups, ConfigurationTarget.USER)
|
||||
]).then(() => Promise.resolve());
|
||||
}
|
||||
|
||||
@@ -259,14 +259,14 @@ export class ConnectionConfig {
|
||||
* Moves the source group under the target group.
|
||||
*/
|
||||
public changeGroupIdForConnectionGroup(source: ConnectionProfileGroup, target: ConnectionProfileGroup): Promise<void> {
|
||||
let groups = this.configurationService.inspect<IConnectionProfileGroup[]>(GROUPS_CONFIG_KEY).user;
|
||||
let groups = this.configurationService.inspect<IConnectionProfileGroup[]>(Constants.connectionGroupsArrayName).user;
|
||||
groups = groups.map(g => {
|
||||
if (g.id === source.id) {
|
||||
g.parentId = target.id;
|
||||
}
|
||||
return g;
|
||||
});
|
||||
return this.configurationService.updateValue(GROUPS_CONFIG_KEY, groups, ConfigurationTarget.USER);
|
||||
return this.configurationService.updateValue(Constants.connectionGroupsArrayName, groups, ConfigurationTarget.USER);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -283,10 +283,10 @@ export class ConnectionConfig {
|
||||
* Moves the connection under the target group with the new ID.
|
||||
*/
|
||||
private changeGroupIdForConnectionInSettings(profile: ConnectionProfile, newGroupID: string, target: ConfigurationTarget = ConfigurationTarget.USER): Promise<void> {
|
||||
let profiles = target === ConfigurationTarget.USER ? this.configurationService.inspect<IConnectionProfileStore[]>(CONNECTIONS_CONFIG_KEY).user :
|
||||
this.configurationService.inspect<IConnectionProfileStore[]>(CONNECTIONS_CONFIG_KEY).workspace;
|
||||
let profiles = target === ConfigurationTarget.USER ? this.configurationService.inspect<IConnectionProfileStore[]>(Constants.connectionsArrayName).user :
|
||||
this.configurationService.inspect<IConnectionProfileStore[]>(Constants.connectionsArrayName).workspace;
|
||||
if (profiles) {
|
||||
if (profile.parent && profile.parent.id === UNSAVED_GROUP_ID) {
|
||||
if (profile.parent && profile.parent.id === Constants.unsavedGroupId) {
|
||||
profile.groupId = newGroupID;
|
||||
profiles.push(ConnectionProfile.convertToProfileStore(this._capabilitiesService, profile));
|
||||
} else {
|
||||
@@ -298,7 +298,7 @@ export class ConnectionConfig {
|
||||
});
|
||||
}
|
||||
|
||||
return this.configurationService.updateValue(CONNECTIONS_CONFIG_KEY, profiles, target);
|
||||
return this.configurationService.updateValue(Constants.connectionsArrayName, profiles, target);
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
@@ -320,12 +320,14 @@ export class ConnectionConfig {
|
||||
}
|
||||
|
||||
public saveGroup(groups: IConnectionProfileGroup[], groupFullName: string, color: string, description: string): ISaveGroupResult {
|
||||
let result: ISaveGroupResult;
|
||||
let groupNames = ConnectionProfileGroup.getGroupFullNameParts(groupFullName);
|
||||
return this.saveGroupInTree(groups, undefined, groupNames, color, description, 0);
|
||||
result = this.saveGroupInTree(groups, undefined, groupNames, color, description, 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
public editGroup(source: ConnectionProfileGroup): Promise<void> {
|
||||
let groups = this.configurationService.inspect<IConnectionProfileGroup[]>(GROUPS_CONFIG_KEY).user;
|
||||
let groups = this.configurationService.inspect<IConnectionProfileGroup[]>(Constants.connectionGroupsArrayName).user;
|
||||
let sameNameGroup = groups ? groups.find(group => group.name === source.name && group.id !== source.id) : undefined;
|
||||
if (sameNameGroup) {
|
||||
let errMessage: string = nls.localize('invalidServerName', "A server group with the same name already exists.");
|
||||
@@ -340,7 +342,7 @@ export class ConnectionConfig {
|
||||
}
|
||||
return g;
|
||||
});
|
||||
return this.configurationService.updateValue(GROUPS_CONFIG_KEY, groups, ConfigurationTarget.USER);
|
||||
return this.configurationService.updateValue(Constants.connectionGroupsArrayName, groups, ConfigurationTarget.USER);
|
||||
}
|
||||
|
||||
private isSameGroupName(group1: IConnectionProfileGroup, group2: IConnectionProfileGroup): boolean {
|
||||
|
||||
@@ -17,6 +17,7 @@ import { ConnectionManagementInfo } from 'sql/platform/connection/common/connect
|
||||
import * as Utils from 'sql/platform/connection/common/utils';
|
||||
import * as Constants from 'sql/platform/connection/common/constants';
|
||||
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
|
||||
import { ICredentialsService } from 'sql/platform/credentials/common/credentialsService';
|
||||
import * as ConnectionContracts from 'sql/parts/connection/common/connection';
|
||||
import { ConnectionStatusManager } from 'sql/platform/connection/common/connectionStatusManager';
|
||||
import { DashboardInput } from 'sql/parts/dashboard/dashboardInput';
|
||||
@@ -43,7 +44,9 @@ import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IEditorService, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService';
|
||||
import * as platform from 'vs/platform/registry/common/platform';
|
||||
import { Memento } from 'vs/workbench/common/memento';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { ConnectionProfileGroup, IConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { EditorPart } from 'vs/workbench/browser/parts/editor/editorPart';
|
||||
@@ -74,13 +77,16 @@ export class ConnectionManagementService extends Disposable implements IConnecti
|
||||
private _connectionGlobalStatus = new ConnectionGlobalStatus(this._statusBarService);
|
||||
|
||||
constructor(
|
||||
private _connectionMemento: Memento,
|
||||
private _connectionStore: ConnectionStore,
|
||||
@IStorageService _storageService: IStorageService,
|
||||
@IConnectionDialogService private _connectionDialogService: IConnectionDialogService,
|
||||
@IServerGroupController private _serverGroupController: IServerGroupController,
|
||||
@IInstantiationService private _instantiationService: IInstantiationService,
|
||||
@IEditorService private _editorService: IEditorService,
|
||||
@ITelemetryService private _telemetryService: ITelemetryService,
|
||||
@IConfigurationService private _configurationService: IConfigurationService,
|
||||
@ICredentialsService private _credentialsService: ICredentialsService,
|
||||
@ICapabilitiesService private _capabilitiesService: ICapabilitiesService,
|
||||
@IQuickInputService private _quickInputService: IQuickInputService,
|
||||
@IEditorGroupsService private _editorGroupService: IEditorGroupsService,
|
||||
@@ -91,8 +97,13 @@ export class ConnectionManagementService extends Disposable implements IConnecti
|
||||
) {
|
||||
super();
|
||||
|
||||
// _connectionMemento and _connectionStore are in constructor to enable this class to be more testable
|
||||
if (!this._connectionMemento) {
|
||||
this._connectionMemento = new Memento('ConnectionManagement', _storageService);
|
||||
}
|
||||
if (!this._connectionStore) {
|
||||
this._connectionStore = _instantiationService.createInstance(ConnectionStore);
|
||||
this._connectionStore = new ConnectionStore(this._connectionMemento,
|
||||
this._configurationService, this._credentialsService, this._capabilitiesService);
|
||||
}
|
||||
|
||||
// Register Statusbar item
|
||||
@@ -124,6 +135,7 @@ export class ConnectionManagementService extends Disposable implements IConnecti
|
||||
this.onConnectionChanged(() => this.refreshEditorTitles());
|
||||
this.onConnect(() => this.refreshEditorTitles());
|
||||
this.onDisconnect(() => this.refreshEditorTitles());
|
||||
_storageService.onWillSaveState(() => this.shutdown());
|
||||
}
|
||||
|
||||
public providerRegistered(providerId: string): boolean {
|
||||
@@ -612,11 +624,11 @@ export class ConnectionManagementService extends Disposable implements IConnecti
|
||||
}
|
||||
|
||||
public clearRecentConnection(connectionProfile: IConnectionProfile): void {
|
||||
this._connectionStore.removeRecentConnection(connectionProfile);
|
||||
this._connectionStore.removeConnectionToMemento(connectionProfile, Constants.recentConnections);
|
||||
}
|
||||
|
||||
public getActiveConnections(providers?: string[]): ConnectionProfile[] {
|
||||
return this._connectionStatusManager.getActiveConnectionProfiles(providers);
|
||||
return this._connectionStatusManager.getActiveConnectionProfiles();
|
||||
}
|
||||
|
||||
public getConnectionUriFromId(connectionId: string): string {
|
||||
@@ -849,8 +861,8 @@ export class ConnectionManagementService extends Disposable implements IConnecti
|
||||
* Add a connection to the active connections list.
|
||||
*/
|
||||
private tryAddActiveConnection(connectionManagementInfo: ConnectionManagementInfo, newConnection: IConnectionProfile, addToMru: boolean): void {
|
||||
if (newConnection && addToMru) {
|
||||
this._connectionStore.addRecentConnection(newConnection)
|
||||
if (newConnection) {
|
||||
this._connectionStore.addActiveConnection(newConnection, addToMru)
|
||||
.then(() => {
|
||||
connectionManagementInfo.connectHandler(true);
|
||||
}, err => {
|
||||
@@ -918,6 +930,11 @@ export class ConnectionManagementService extends Disposable implements IConnecti
|
||||
public onIntelliSenseCacheComplete(handle: number, connectionUri: string): void {
|
||||
}
|
||||
|
||||
private shutdown(): void {
|
||||
this._connectionStore.clearActiveConnections();
|
||||
this._connectionMemento.saveMemento();
|
||||
}
|
||||
|
||||
public changeGroupIdForConnectionGroup(source: ConnectionProfileGroup, target: ConnectionProfileGroup): Promise<void> {
|
||||
TelemetryUtils.addTelemetry(this._telemetryService, TelemetryKeys.MoveServerConnection);
|
||||
return this._connectionStore.changeGroupIdForConnectionGroup(source, target);
|
||||
@@ -1079,6 +1096,7 @@ export class ConnectionManagementService extends Disposable implements IConnecti
|
||||
this.doDisconnect(uri, profile).then(result => {
|
||||
if (result) {
|
||||
this.addTelemetryForConnectionDisconnected(input);
|
||||
this._connectionStore.removeActiveConnection(input);
|
||||
this._connectionStatusManager.removeConnection(uri);
|
||||
resolve();
|
||||
} else {
|
||||
|
||||
@@ -5,26 +5,21 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
import { ReverseLookUpMap } from 'sql/base/common/map';
|
||||
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
|
||||
import { ConnectionConfig } from 'sql/platform/connection/common/connectionConfig';
|
||||
import { fixupConnectionCredentials } from 'sql/platform/connection/common/connectionInfo';
|
||||
import * as Constants from 'sql/platform/connection/common/constants';
|
||||
import * as ConnInfo from 'sql/platform/connection/common/connectionInfo';
|
||||
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
|
||||
import { ConnectionProfileGroup, IConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup';
|
||||
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
|
||||
import { ICredentialsService } from 'sql/platform/credentials/common/credentialsService';
|
||||
import { IConnectionConfig } from 'sql/platform/connection/common/iconnectionConfig';
|
||||
import { ConnectionConfig } from 'sql/platform/connection/common/connectionConfig';
|
||||
import { Memento } from 'vs/workbench/common/memento';
|
||||
import { StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { ConnectionProfileGroup, IConnectionProfileGroup } from './connectionProfileGroup';
|
||||
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IStateService } from 'vs/platform/state/common/state';
|
||||
|
||||
const MAX_CONNECTIONS_DEFAULT = 25;
|
||||
|
||||
const RECENT_CONNECTIONS_STATE_KEY = 'recentConnections';
|
||||
const CRED_PREFIX = 'Microsoft.SqlTools';
|
||||
const CRED_SEPARATOR = '|';
|
||||
const CRED_ID_PREFIX = 'id:';
|
||||
const CRED_ITEMTYPE_PREFIX = 'itemtype:';
|
||||
const CRED_PROFILE_USER = 'Profile';
|
||||
|
||||
/**
|
||||
* Manages the connections list including saved profiles and the most recently used connections
|
||||
*
|
||||
@@ -32,15 +27,39 @@ const CRED_PROFILE_USER = 'Profile';
|
||||
* @class ConnectionStore
|
||||
*/
|
||||
export class ConnectionStore {
|
||||
private groupIdMap = new ReverseLookUpMap<string, string>();
|
||||
private connectionConfig = new ConnectionConfig(this.configurationService, this.capabilitiesService);
|
||||
private _memento: any;
|
||||
private _groupIdToFullNameMap: { [groupId: string]: string };
|
||||
private _groupFullNameToIdMap: { [groupId: string]: string };
|
||||
|
||||
constructor(
|
||||
@IStateService private stateService: IStateService,
|
||||
@IConfigurationService private configurationService: IConfigurationService,
|
||||
@ICredentialsService private credentialService: ICredentialsService,
|
||||
@ICapabilitiesService private capabilitiesService: ICapabilitiesService
|
||||
private _context: Memento,
|
||||
private _configurationService: IConfigurationService,
|
||||
private _credentialService: ICredentialsService,
|
||||
private _capabilitiesService: ICapabilitiesService,
|
||||
private _connectionConfig?: IConnectionConfig
|
||||
) {
|
||||
if (_context) {
|
||||
this._memento = this._context.getMemento(StorageScope.GLOBAL);
|
||||
}
|
||||
this._groupIdToFullNameMap = {};
|
||||
this._groupFullNameToIdMap = {};
|
||||
if (!this._connectionConfig) {
|
||||
this._connectionConfig = new ConnectionConfig(this._configurationService, this._capabilitiesService);
|
||||
}
|
||||
}
|
||||
|
||||
public static get CRED_PREFIX(): string { return 'Microsoft.SqlTools'; }
|
||||
public static get CRED_SEPARATOR(): string { return '|'; }
|
||||
public static get CRED_ID_PREFIX(): string { return 'id:'; }
|
||||
public static get CRED_ITEMTYPE_PREFIX(): string { return 'itemtype:'; }
|
||||
public static get CRED_PROFILE_USER(): string { return 'Profile'; }
|
||||
|
||||
public formatCredentialIdForCred(connectionProfile: IConnectionProfile): string {
|
||||
if (!connectionProfile) {
|
||||
throw new Error('Missing Connection which is required');
|
||||
}
|
||||
let itemTypeString: string = ConnectionStore.CRED_PROFILE_USER;
|
||||
return this.formatCredentialId(connectionProfile, itemTypeString);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -51,17 +70,26 @@ export class ConnectionStore {
|
||||
* @param {string} itemType type of the item (MRU or Profile) - optional
|
||||
* @returns {string} formatted string with server, DB and username
|
||||
*/
|
||||
private formatCredentialId(connectionProfile: IConnectionProfile, itemType?: string): string {
|
||||
public formatCredentialId(connectionProfile: IConnectionProfile, itemType?: string): string {
|
||||
let connectionProfileInstance: ConnectionProfile = ConnectionProfile.fromIConnectionProfile(
|
||||
this.capabilitiesService, connectionProfile);
|
||||
let cred: string[] = [CRED_PREFIX];
|
||||
this._capabilitiesService, connectionProfile);
|
||||
if (!connectionProfileInstance.getConnectionInfoId()) {
|
||||
throw new Error('Missing Id, which is required');
|
||||
}
|
||||
let cred: string[] = [ConnectionStore.CRED_PREFIX];
|
||||
if (!itemType) {
|
||||
itemType = CRED_PROFILE_USER;
|
||||
itemType = ConnectionStore.CRED_PROFILE_USER;
|
||||
}
|
||||
|
||||
cred.push(CRED_ITEMTYPE_PREFIX.concat(itemType));
|
||||
cred.push(CRED_ID_PREFIX.concat(connectionProfileInstance.getConnectionInfoId()));
|
||||
return cred.join(CRED_SEPARATOR);
|
||||
ConnectionStore.pushIfNonEmpty(itemType, ConnectionStore.CRED_ITEMTYPE_PREFIX, cred);
|
||||
ConnectionStore.pushIfNonEmpty(connectionProfileInstance.getConnectionInfoId(), ConnectionStore.CRED_ID_PREFIX, cred);
|
||||
return cred.join(ConnectionStore.CRED_SEPARATOR);
|
||||
}
|
||||
|
||||
private static pushIfNonEmpty(value: string, prefix: string, arr: string[]): void {
|
||||
if (value) {
|
||||
arr.push(prefix.concat(value));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,7 +98,7 @@ export class ConnectionStore {
|
||||
*/
|
||||
public isPasswordRequired(connection: IConnectionProfile): boolean {
|
||||
if (connection) {
|
||||
let connectionProfile = ConnectionProfile.fromIConnectionProfile(this.capabilitiesService, connection);
|
||||
let connectionProfile = ConnectionProfile.fromIConnectionProfile(this._capabilitiesService, connection);
|
||||
return connectionProfile.isPasswordRequired();
|
||||
} else {
|
||||
return false;
|
||||
@@ -78,20 +106,28 @@ export class ConnectionStore {
|
||||
}
|
||||
|
||||
public addSavedPassword(credentialsItem: IConnectionProfile): Promise<{ profile: IConnectionProfile, savedCred: boolean }> {
|
||||
if (credentialsItem.savePassword && this.isPasswordRequired(credentialsItem) && !credentialsItem.password) {
|
||||
let credentialId = this.formatCredentialId(credentialsItem, CRED_PROFILE_USER);
|
||||
return this.credentialService.readCredential(credentialId)
|
||||
.then(savedCred => {
|
||||
if (savedCred) {
|
||||
credentialsItem.password = savedCred.password;
|
||||
credentialsItem.options['password'] = savedCred.password;
|
||||
}
|
||||
return { profile: credentialsItem, savedCred: !!savedCred };
|
||||
});
|
||||
} else {
|
||||
// No need to look up the password
|
||||
return Promise.resolve({ profile: credentialsItem, savedCred: credentialsItem.savePassword });
|
||||
}
|
||||
let self = this;
|
||||
return new Promise<{ profile: IConnectionProfile, savedCred: boolean }>((resolve, reject) => {
|
||||
if (credentialsItem.savePassword && this.isPasswordRequired(credentialsItem)
|
||||
&& !credentialsItem.password) {
|
||||
|
||||
let credentialId = this.formatCredentialIdForCred(credentialsItem);
|
||||
self._credentialService.readCredential(credentialId)
|
||||
.then(savedCred => {
|
||||
if (savedCred) {
|
||||
credentialsItem.password = savedCred.password;
|
||||
credentialsItem.options['password'] = savedCred.password;
|
||||
}
|
||||
resolve({ profile: credentialsItem, savedCred: !!savedCred });
|
||||
},
|
||||
reason => {
|
||||
reject(reason);
|
||||
});
|
||||
} else {
|
||||
// No need to look up the password
|
||||
resolve({ profile: credentialsItem, savedCred: credentialsItem.savePassword });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -103,20 +139,34 @@ export class ConnectionStore {
|
||||
* @returns {Promise<IConnectionProfile>} a Promise that returns the original profile, for help in chaining calls
|
||||
*/
|
||||
public saveProfile(profile: IConnectionProfile, forceWritePlaintextPassword?: boolean): Promise<IConnectionProfile> {
|
||||
// Add the profile to the saved list, taking care to clear out the password field if necessary
|
||||
let savedProfile = forceWritePlaintextPassword ? profile : this.getProfileWithoutPassword(profile);
|
||||
return this.saveProfileToConfig(savedProfile)
|
||||
.then(savedConnectionProfile => {
|
||||
profile.groupId = savedConnectionProfile.groupId;
|
||||
profile.id = savedConnectionProfile.id;
|
||||
// Only save if we successfully added the profile
|
||||
return this.saveProfilePasswordIfNeeded(profile);
|
||||
}).then(() => {
|
||||
// Add necessary default properties before returning
|
||||
// this is needed to support immediate connections
|
||||
fixupConnectionCredentials(profile);
|
||||
return profile;
|
||||
});
|
||||
const self = this;
|
||||
return new Promise<IConnectionProfile>((resolve, reject) => {
|
||||
// Add the profile to the saved list, taking care to clear out the password field if necessary
|
||||
let savedProfile: IConnectionProfile;
|
||||
if (forceWritePlaintextPassword) {
|
||||
savedProfile = profile;
|
||||
} else {
|
||||
|
||||
savedProfile = this.getProfileWithoutPassword(profile);
|
||||
}
|
||||
self.saveProfileToConfig(savedProfile)
|
||||
.then(savedConnectionProfile => {
|
||||
profile.groupId = savedConnectionProfile.groupId;
|
||||
profile.id = savedConnectionProfile.id;
|
||||
// Only save if we successfully added the profile
|
||||
return self.saveProfilePasswordIfNeeded(profile);
|
||||
// And resolve / reject at the end of the process
|
||||
}, err => {
|
||||
reject(err);
|
||||
}).then(resolved => {
|
||||
// Add necessary default properties before returning
|
||||
// this is needed to support immediate connections
|
||||
ConnInfo.fixupConnectionCredentials(profile);
|
||||
resolve(profile);
|
||||
}, err => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -126,15 +176,29 @@ export class ConnectionStore {
|
||||
* @returns {Promise<string>} a Promise that returns the id of connection group
|
||||
*/
|
||||
public saveProfileGroup(profile: IConnectionProfileGroup): Promise<string> {
|
||||
return this.connectionConfig.addGroup(profile);
|
||||
const self = this;
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
self._connectionConfig.addGroup(profile).then(groupId => {
|
||||
resolve(groupId);
|
||||
}).catch(error => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private saveProfileToConfig(profile: IConnectionProfile): Promise<IConnectionProfile> {
|
||||
if (profile.saveProfile) {
|
||||
return this.connectionConfig.addConnection(profile);
|
||||
} else {
|
||||
return Promise.resolve(profile);
|
||||
}
|
||||
const self = this;
|
||||
return new Promise<IConnectionProfile>((resolve, reject) => {
|
||||
if (profile.saveProfile) {
|
||||
self._connectionConfig.addConnection(profile).then(savedProfile => {
|
||||
resolve(savedProfile);
|
||||
}).catch(error => {
|
||||
reject(error);
|
||||
});
|
||||
} else {
|
||||
resolve(profile);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -144,8 +208,12 @@ export class ConnectionStore {
|
||||
* @returns {azdata.ConnectionInfo} the array of connections, empty if none are found
|
||||
*/
|
||||
public getRecentlyUsedConnections(providers?: string[]): ConnectionProfile[] {
|
||||
let configValues = this.stateService.getItem<IConnectionProfile[]>(RECENT_CONNECTIONS_STATE_KEY, []).filter(c => !!c);
|
||||
let configValues: IConnectionProfile[] = this._memento[Constants.recentConnections];
|
||||
if (!configValues) {
|
||||
configValues = [];
|
||||
}
|
||||
|
||||
configValues = configValues.filter(c => !!(c));
|
||||
if (providers && providers.length > 0) {
|
||||
configValues = configValues.filter(c => providers.includes(c.providerName));
|
||||
}
|
||||
@@ -155,7 +223,7 @@ export class ConnectionStore {
|
||||
private convertConfigValuesToConnectionProfiles(configValues: IConnectionProfile[]): ConnectionProfile[] {
|
||||
return configValues.map(c => {
|
||||
if (c) {
|
||||
let connectionProfile = new ConnectionProfile(this.capabilitiesService, c);
|
||||
let connectionProfile = new ConnectionProfile(this._capabilitiesService, c);
|
||||
if (connectionProfile.saveProfile) {
|
||||
if (!connectionProfile.groupFullName && connectionProfile.groupId) {
|
||||
connectionProfile.groupFullName = this.getGroupFullName(connectionProfile.groupId);
|
||||
@@ -173,9 +241,24 @@ export class ConnectionStore {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of active connections. These will not include the password - a separate call to
|
||||
* {addSavedPassword} is needed to fill that before connecting
|
||||
*
|
||||
* @returns {azdata.ConnectionInfo} the array of connections, empty if none are found
|
||||
*/
|
||||
public getActiveConnections(): ConnectionProfile[] {
|
||||
let configValues: IConnectionProfile[] = this._memento[Constants.activeConnections];
|
||||
if (!configValues) {
|
||||
configValues = [];
|
||||
}
|
||||
|
||||
return this.convertConfigValuesToConnectionProfiles(configValues);
|
||||
}
|
||||
|
||||
public getProfileWithoutPassword(conn: IConnectionProfile): ConnectionProfile {
|
||||
if (conn) {
|
||||
let savedConn: ConnectionProfile = ConnectionProfile.fromIConnectionProfile(this.capabilitiesService, conn);
|
||||
let savedConn: ConnectionProfile = ConnectionProfile.fromIConnectionProfile(this._capabilitiesService, conn);
|
||||
savedConn = savedConn.withoutPassword();
|
||||
|
||||
return savedConn;
|
||||
@@ -193,35 +276,71 @@ export class ConnectionStore {
|
||||
* @param {boolean} addToMru Whether to add this connection to the MRU
|
||||
* @returns {Promise<void>} a Promise that returns when the connection was saved
|
||||
*/
|
||||
public addRecentConnection(conn: IConnectionProfile): Promise<void> {
|
||||
let maxConnections = this.getMaxRecentConnectionsCount();
|
||||
return this.addConnectionToState(conn, RECENT_CONNECTIONS_STATE_KEY, maxConnections, conn.savePassword);
|
||||
}
|
||||
|
||||
private addConnectionToState(conn: IConnectionProfile, key: string, maxConnections?: number, savePassword?: boolean): Promise<void> {
|
||||
// Get all profiles
|
||||
let configValues = this.getConnectionsFromState(key);
|
||||
let configToSave = this.addToConnectionList(conn, configValues);
|
||||
if (maxConnections) {
|
||||
// Remove last element if needed
|
||||
if (configToSave.length > maxConnections) {
|
||||
configToSave = configToSave.slice(0, maxConnections);
|
||||
}
|
||||
public async addActiveConnection(conn: IConnectionProfile, addToMru: boolean): Promise<void> {
|
||||
if (addToMru) {
|
||||
await this.addConnectionToMru(conn);
|
||||
}
|
||||
|
||||
// Only add connections we don't already know about
|
||||
if (!this.getActiveConnections().some(existingConn => existingConn.id === conn.id)) {
|
||||
await this.addConnectionToMemento(conn, Constants.activeConnections, undefined, conn.savePassword);
|
||||
}
|
||||
this.stateService.setItem(key, configToSave);
|
||||
return savePassword ? this.doSavePassword(conn).then() : Promise.resolve();
|
||||
}
|
||||
|
||||
private removeConnectionFromState(conn: IConnectionProfile, key: string): void {
|
||||
// Get all profiles
|
||||
let configValues = this.getConnectionsFromState(key);
|
||||
let configToSave = this.removeFromConnectionList(conn, configValues);
|
||||
|
||||
this.stateService.setItem(key, configToSave);
|
||||
/**
|
||||
* Adds the specified connection to the MRU list
|
||||
* @param conn The connection to add
|
||||
*/
|
||||
private async addConnectionToMru(conn: IConnectionProfile): Promise<void> {
|
||||
let maxConnections = this.getMaxRecentConnectionsCount();
|
||||
if (ConnectionProfile.isConnectionToDefaultDb(conn)) {
|
||||
conn.databaseName = '';
|
||||
}
|
||||
await this.addConnectionToMemento(conn, Constants.recentConnections, maxConnections);
|
||||
}
|
||||
|
||||
private getConnectionsFromState(mementoKey: string): ConnectionProfile[] {
|
||||
return this.convertConfigValuesToConnectionProfiles(this.stateService.getItem<IConnectionProfile[]>(mementoKey, []));
|
||||
public addConnectionToMemento(conn: IConnectionProfile, mementoKey: string, maxConnections?: number, savePassword?: boolean): Promise<void> {
|
||||
const self = this;
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
// Get all profiles
|
||||
let configValues = self.getConnectionsFromMemento(mementoKey);
|
||||
let configToSave = this.addToConnectionList(conn, configValues);
|
||||
if (maxConnections) {
|
||||
// Remove last element if needed
|
||||
if (configToSave.length > maxConnections) {
|
||||
configToSave = configToSave.slice(0, maxConnections);
|
||||
}
|
||||
}
|
||||
self._memento[mementoKey] = configToSave;
|
||||
if (savePassword) {
|
||||
self.doSavePassword(conn).then(result => {
|
||||
resolve(undefined);
|
||||
});
|
||||
} else {
|
||||
resolve(undefined);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public removeConnectionToMemento(conn: IConnectionProfile, mementoKey: string): Promise<void> {
|
||||
const self = this;
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
// Get all profiles
|
||||
let configValues = self.getConnectionsFromMemento(mementoKey);
|
||||
let configToSave = this.removeFromConnectionList(conn, configValues);
|
||||
|
||||
self._memento[mementoKey] = configToSave;
|
||||
resolve(undefined);
|
||||
});
|
||||
}
|
||||
|
||||
public getConnectionsFromMemento(mementoKey: string): ConnectionProfile[] {
|
||||
let configValues: IConnectionProfile[] = this._memento[mementoKey];
|
||||
if (!configValues) {
|
||||
configValues = [];
|
||||
}
|
||||
|
||||
return this.convertConfigValuesToConnectionProfiles(configValues);
|
||||
}
|
||||
|
||||
private addToConnectionList(conn: IConnectionProfile, list: ConnectionProfile[]): IConnectionProfile[] {
|
||||
@@ -239,7 +358,11 @@ export class ConnectionStore {
|
||||
|
||||
list.unshift(savedProfile);
|
||||
|
||||
return list.filter(n => n !== undefined).map(c => c.toIConnectionProfile());
|
||||
let newList = list.map(c => {
|
||||
let connectionProfile = c ? c.toIConnectionProfile() : undefined;
|
||||
return connectionProfile;
|
||||
});
|
||||
return newList.filter(n => n !== undefined);
|
||||
}
|
||||
|
||||
private removeFromConnectionList(conn: IConnectionProfile, list: ConnectionProfile[]): IConnectionProfile[] {
|
||||
@@ -255,18 +378,37 @@ export class ConnectionStore {
|
||||
return !equal;
|
||||
});
|
||||
|
||||
return list.filter(n => n !== undefined).map(c => c.toIConnectionProfile());
|
||||
let newList = list.map(c => {
|
||||
let connectionProfile = c ? c.toIConnectionProfile() : undefined;
|
||||
return connectionProfile;
|
||||
});
|
||||
return newList.filter(n => n !== undefined);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all recently used connections from the MRU list.
|
||||
*/
|
||||
public clearRecentlyUsed(): void {
|
||||
this.stateService.setItem(RECENT_CONNECTIONS_STATE_KEY, []);
|
||||
this._memento[Constants.recentConnections] = [];
|
||||
}
|
||||
|
||||
public removeRecentConnection(conn: IConnectionProfile): void {
|
||||
this.removeConnectionFromState(conn, RECENT_CONNECTIONS_STATE_KEY);
|
||||
public clearFromMemento(name: string): void {
|
||||
this._memento[name] = [];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Clear all active connections from the MRU list.
|
||||
*/
|
||||
public clearActiveConnections(): void {
|
||||
this._memento[Constants.activeConnections] = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a connection profile from the active connections list.
|
||||
*/
|
||||
public removeActiveConnection(conn: IConnectionProfile): Promise<void> {
|
||||
return this.removeConnectionToMemento(conn, Constants.activeConnections);
|
||||
}
|
||||
|
||||
private saveProfilePasswordIfNeeded(profile: IConnectionProfile): Promise<boolean> {
|
||||
@@ -277,23 +419,32 @@ export class ConnectionStore {
|
||||
}
|
||||
|
||||
private doSavePassword(conn: IConnectionProfile): Promise<boolean> {
|
||||
if (conn.password) {
|
||||
let credentialId = this.formatCredentialId(conn);
|
||||
return this.credentialService.saveCredential(credentialId, conn.password);
|
||||
} else {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
let self = this;
|
||||
return new Promise<boolean>((resolve, reject) => {
|
||||
if (conn.password) {
|
||||
let credentialId = this.formatCredentialId(conn);
|
||||
self._credentialService.saveCredential(credentialId, conn.password)
|
||||
.then((result) => {
|
||||
resolve(result);
|
||||
}, reason => {
|
||||
// Bubble up error if there was a problem executing the set command
|
||||
reject(reason);
|
||||
});
|
||||
} else {
|
||||
resolve(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public getConnectionProfileGroups(withoutConnections?: boolean, providers?: string[]): ConnectionProfileGroup[] {
|
||||
let profilesInConfiguration: ConnectionProfile[];
|
||||
if (!withoutConnections) {
|
||||
profilesInConfiguration = this.connectionConfig.getConnections(true);
|
||||
profilesInConfiguration = this._connectionConfig.getConnections(true);
|
||||
if (providers && providers.length > 0) {
|
||||
profilesInConfiguration = profilesInConfiguration.filter(x => providers.includes(x.providerName));
|
||||
}
|
||||
}
|
||||
let groups = this.connectionConfig.getAllGroups();
|
||||
let groups = this._connectionConfig.getAllGroups();
|
||||
|
||||
let connectionProfileGroups = this.convertToConnectionGroup(groups, profilesInConfiguration, undefined);
|
||||
return connectionProfileGroups;
|
||||
@@ -328,53 +479,74 @@ export class ConnectionStore {
|
||||
}
|
||||
|
||||
public getGroupFromId(groupId: string): IConnectionProfileGroup {
|
||||
let groups = this.connectionConfig.getAllGroups();
|
||||
let groups = this._connectionConfig.getAllGroups();
|
||||
return groups.find(group => group.id === groupId);
|
||||
}
|
||||
|
||||
private getMaxRecentConnectionsCount(): number {
|
||||
return this.configurationService.getValue('sql.maxRecentConnections') || MAX_CONNECTIONS_DEFAULT;
|
||||
let config = this._configurationService.getValue(Constants.sqlConfigSectionName);
|
||||
|
||||
let maxConnections: number = config[Constants.configMaxRecentConnections];
|
||||
if (typeof (maxConnections) !== 'number' || maxConnections <= 0) {
|
||||
maxConnections = MAX_CONNECTIONS_DEFAULT;
|
||||
}
|
||||
return maxConnections;
|
||||
}
|
||||
|
||||
public editGroup(group: ConnectionProfileGroup): Promise<void> {
|
||||
return this.connectionConfig.editGroup(group).then();
|
||||
public editGroup(group: ConnectionProfileGroup): Promise<any> {
|
||||
const self = this;
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
self._connectionConfig.editGroup(group).then(() => {
|
||||
resolve(null);
|
||||
}).catch(error => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public deleteConnectionFromConfiguration(connection: ConnectionProfile): Promise<void> {
|
||||
return this.connectionConfig.deleteConnection(connection);
|
||||
return this._connectionConfig.deleteConnection(connection);
|
||||
}
|
||||
|
||||
public deleteGroupFromConfiguration(group: ConnectionProfileGroup): Promise<void> {
|
||||
return this.connectionConfig.deleteGroup(group);
|
||||
return this._connectionConfig.deleteGroup(group);
|
||||
}
|
||||
|
||||
public changeGroupIdForConnectionGroup(source: ConnectionProfileGroup, target: ConnectionProfileGroup): Promise<void> {
|
||||
return this.connectionConfig.changeGroupIdForConnectionGroup(source, target);
|
||||
return this._connectionConfig.changeGroupIdForConnectionGroup(source, target);
|
||||
}
|
||||
|
||||
public canChangeConnectionConfig(profile: ConnectionProfile, newGroupID: string): boolean {
|
||||
return this.connectionConfig.canChangeConnectionConfig(profile, newGroupID);
|
||||
return this._connectionConfig.canChangeConnectionConfig(profile, newGroupID);
|
||||
}
|
||||
|
||||
public changeGroupIdForConnection(source: ConnectionProfile, targetGroupId: string): Promise<void> {
|
||||
return this.connectionConfig.changeGroupIdForConnection(source, targetGroupId).then();
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
this._connectionConfig.changeGroupIdForConnection(source, targetGroupId).then(() => {
|
||||
resolve();
|
||||
}, (error => {
|
||||
reject(error);
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
private addGroupFullNameToMap(groupId: string, groupFullName: string): void {
|
||||
if (groupId) {
|
||||
this.groupIdMap.set(groupId, groupFullName);
|
||||
this._groupIdToFullNameMap[groupId] = groupFullName;
|
||||
}
|
||||
if (groupFullName !== undefined) {
|
||||
this.groupIdMap.set(groupFullName.toUpperCase(), groupId);
|
||||
this._groupFullNameToIdMap[groupFullName.toUpperCase()] = groupId;
|
||||
}
|
||||
}
|
||||
|
||||
private getGroupFullName(groupId: string): string {
|
||||
if (!this.groupIdMap.has(groupId)) {
|
||||
if (groupId in this._groupIdToFullNameMap) {
|
||||
return this._groupIdToFullNameMap[groupId];
|
||||
} else {
|
||||
// Load the cache
|
||||
this.getConnectionProfileGroups(true);
|
||||
}
|
||||
return this.groupIdMap.get(groupId);
|
||||
return this._groupIdToFullNameMap[groupId];
|
||||
}
|
||||
|
||||
private getGroupId(groupFullName: string): string {
|
||||
@@ -382,10 +554,14 @@ export class ConnectionStore {
|
||||
groupFullName = '';
|
||||
}
|
||||
let key = groupFullName.toUpperCase();
|
||||
if (!this.groupIdMap.reverseHas(key)) {
|
||||
let result: string = '';
|
||||
if (key in this._groupFullNameToIdMap) {
|
||||
result = this._groupFullNameToIdMap[key];
|
||||
} else {
|
||||
// Load the cache
|
||||
this.getConnectionProfileGroups(true);
|
||||
result = this._groupFullNameToIdMap[key];
|
||||
}
|
||||
return this.groupIdMap.reverseGet(key);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,9 +7,19 @@
|
||||
export const sqlConfigSectionName = 'sql';
|
||||
export const outputChannelName = 'MSSQL';
|
||||
|
||||
export const connectionsArrayName = 'datasource.connections';
|
||||
export const connectionGroupsArrayName = 'datasource.connectionGroups';
|
||||
|
||||
/**Unsaved connections Id */
|
||||
export const unsavedGroupId = 'unsaved';
|
||||
|
||||
/* Memento constants */
|
||||
export const activeConnections = 'ACTIVE_CONNECTIONS';
|
||||
export const recentConnections = 'RECENT_CONNECTIONS';
|
||||
export const capabilitiesOptions = 'OPTIONS_METADATA';
|
||||
|
||||
export const configMaxRecentConnections = 'maxRecentConnections';
|
||||
|
||||
export const mssqlProviderName = 'MSSQL';
|
||||
export const anyProviderName = '*';
|
||||
export const connectionProviderContextKey = 'connectionProvider';
|
||||
@@ -24,5 +34,3 @@ export const passwordChars = '***************';
|
||||
export const sqlLogin = 'SqlLogin';
|
||||
export const integrated = 'Integrated';
|
||||
export const azureMFA = 'AzureMFA';
|
||||
|
||||
export const UNSAVED_GROUP_ID = 'unsaved';
|
||||
|
||||
29
src/sql/platform/connection/common/iconnectionConfig.ts
Normal file
29
src/sql/platform/connection/common/iconnectionConfig.ts
Normal 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;
|
||||
}
|
||||
@@ -5,20 +5,23 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as assert from 'assert';
|
||||
import * as azdata from 'azdata';
|
||||
import { ICapabilitiesService, ProviderFeatures } from 'sql/platform/capabilities/common/capabilitiesService';
|
||||
import * as TypeMoq from 'typemoq';
|
||||
import { ConnectionConfig, ISaveGroupResult } from 'sql/platform/connection/common/connectionConfig';
|
||||
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
|
||||
import { ConnectionProfileGroup, IConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup';
|
||||
import { IConnectionProfile, IConnectionProfileStore } from 'sql/platform/connection/common/interfaces';
|
||||
import { TestConfigurationService } from 'sql/platform/connection/test/common/testConfigurationService';
|
||||
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
|
||||
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
|
||||
import * as Constants from 'sql/platform/connection/common/constants';
|
||||
import { IConnectionProfileGroup, ConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup';
|
||||
import * as assert from 'assert';
|
||||
import { ProviderFeatures, ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
|
||||
import * as azdata from 'azdata';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { ConnectionOptionSpecialType, ServiceOptionType } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
import { CapabilitiesTestService } from 'sqltest/stubs/capabilitiesTestService';
|
||||
import * as TypeMoq from 'typemoq';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { deepClone, deepFreeze } from 'vs/base/common/objects';
|
||||
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
|
||||
import { TestConfigurationService } from 'sql/platform/connection/test/common/testConfigurationService';
|
||||
import { deepFreeze, deepClone } from 'vs/base/common/objects';
|
||||
import { isUndefinedOrNull } from 'vs/base/common/types';
|
||||
import { ConnectionManagementInfo } from 'sql/platform/connection/common/connectionManagementInfo';
|
||||
|
||||
suite('ConnectionConfig', () => {
|
||||
let capabilitiesService: TypeMoq.Mock<ICapabilitiesService>;
|
||||
@@ -231,9 +234,9 @@ suite('ConnectionConfig', () => {
|
||||
|
||||
test('getAllGroups should merge user and workspace settings correctly', () => {
|
||||
let configurationService = new TestConfigurationService();
|
||||
configurationService.updateValue('datasource.connectionGroups', deepClone(testGroups).slice(0, 3), ConfigurationTarget.USER);
|
||||
configurationService.updateValue(Constants.connectionGroupsArrayName, deepClone(testGroups).slice(0, 3), ConfigurationTarget.USER);
|
||||
// we intentionally overlap these values with the expectation that the function to should return each group once
|
||||
configurationService.updateValue('datasource.connectionGroups', deepClone(testGroups).slice(2, testGroups.length), ConfigurationTarget.WORKSPACE);
|
||||
configurationService.updateValue(Constants.connectionGroupsArrayName, deepClone(testGroups).slice(2, testGroups.length), ConfigurationTarget.WORKSPACE);
|
||||
|
||||
let config = new ConnectionConfig(configurationService, capabilitiesService.object);
|
||||
let allGroups = config.getAllGroups();
|
||||
@@ -242,7 +245,7 @@ suite('ConnectionConfig', () => {
|
||||
assert.ok(groupsAreEqual(allGroups, testGroups), 'the groups returned did not match expectation');
|
||||
});
|
||||
|
||||
test('addConnection should add the new profile to user settings', async () => {
|
||||
test('addConnection should add the new profile to user settings if does not exist', async () => {
|
||||
let newProfile: IConnectionProfile = {
|
||||
serverName: 'new server',
|
||||
databaseName: 'database',
|
||||
@@ -262,15 +265,15 @@ suite('ConnectionConfig', () => {
|
||||
};
|
||||
|
||||
let configurationService = new TestConfigurationService();
|
||||
configurationService.updateValue('datasource.connectionGroups', deepClone(testGroups), ConfigurationTarget.USER);
|
||||
configurationService.updateValue('datasource.connections', deepClone(testConnections), ConfigurationTarget.USER);
|
||||
configurationService.updateValue(Constants.connectionGroupsArrayName, deepClone(testGroups), ConfigurationTarget.USER);
|
||||
configurationService.updateValue(Constants.connectionsArrayName, deepClone(testConnections), ConfigurationTarget.USER);
|
||||
let connectionProfile = new ConnectionProfile(capabilitiesService.object, newProfile);
|
||||
connectionProfile.options['databaseDisplayName'] = 'database';
|
||||
let config = new ConnectionConfig(configurationService, capabilitiesService.object);
|
||||
let savedConnectionProfile = await config.addConnection(connectionProfile);
|
||||
|
||||
assert.ok(!!savedConnectionProfile.id);
|
||||
assert.equal(configurationService.inspect<IConnectionProfileStore[]>('datasource.connections').user.length, testConnections.length + 1);
|
||||
assert.ok(!isUndefinedOrNull(savedConnectionProfile.id));
|
||||
assert.equal(configurationService.inspect<IConnectionProfileStore[]>(Constants.connectionsArrayName).user.length, testConnections.length + 1);
|
||||
});
|
||||
|
||||
test('addConnection should not add the new profile to user settings if already exists', async () => {
|
||||
@@ -294,8 +297,8 @@ suite('ConnectionConfig', () => {
|
||||
};
|
||||
|
||||
let configurationService = new TestConfigurationService();
|
||||
configurationService.updateValue('datasource.connectionGroups', deepClone(testGroups), ConfigurationTarget.USER);
|
||||
configurationService.updateValue('datasource.connections', deepClone(testConnections), ConfigurationTarget.USER);
|
||||
configurationService.updateValue(Constants.connectionGroupsArrayName, deepClone(testGroups), ConfigurationTarget.USER);
|
||||
configurationService.updateValue(Constants.connectionsArrayName, deepClone(testConnections), ConfigurationTarget.USER);
|
||||
|
||||
let connectionProfile = new ConnectionProfile(capabilitiesService.object, newProfile);
|
||||
connectionProfile.options['databaseDisplayName'] = existingConnection.options['databaseName'];
|
||||
@@ -304,7 +307,7 @@ suite('ConnectionConfig', () => {
|
||||
let savedConnectionProfile = await config.addConnection(connectionProfile);
|
||||
|
||||
assert.equal(savedConnectionProfile.id, existingConnection.id);
|
||||
assert.equal(configurationService.inspect<IConnectionProfileStore[]>('datasource.connections').user.length, testConnections.length);
|
||||
assert.equal(configurationService.inspect<IConnectionProfileStore[]>(Constants.connectionsArrayName).user.length, testConnections.length);
|
||||
});
|
||||
|
||||
test('addConnection should add the new group to user settings if does not exist', async () => {
|
||||
@@ -327,21 +330,21 @@ suite('ConnectionConfig', () => {
|
||||
};
|
||||
|
||||
let configurationService = new TestConfigurationService();
|
||||
configurationService.updateValue('datasource.connectionGroups', deepClone(testGroups), ConfigurationTarget.USER);
|
||||
configurationService.updateValue('datasource.connections', deepClone(testConnections), ConfigurationTarget.USER);
|
||||
configurationService.updateValue(Constants.connectionGroupsArrayName, deepClone(testGroups), ConfigurationTarget.USER);
|
||||
configurationService.updateValue(Constants.connectionsArrayName, deepClone(testConnections), ConfigurationTarget.USER);
|
||||
|
||||
let connectionProfile = new ConnectionProfile(capabilitiesService.object, newProfile);
|
||||
let config = new ConnectionConfig(configurationService, capabilitiesService.object);
|
||||
await config.addConnection(connectionProfile);
|
||||
|
||||
assert.equal(configurationService.inspect<IConnectionProfileStore[]>('datasource.connections').user.length, testConnections.length + 1);
|
||||
assert.equal(configurationService.inspect<IConnectionProfileStore[]>('datasource.connectionGroups').user.length, testGroups.length + 1);
|
||||
assert.equal(configurationService.inspect<IConnectionProfileStore[]>(Constants.connectionsArrayName).user.length, testConnections.length + 1);
|
||||
assert.equal(configurationService.inspect<IConnectionProfileStore[]>(Constants.connectionGroupsArrayName).user.length, testGroups.length + 1);
|
||||
});
|
||||
|
||||
test('getConnections should return connections from user and workspace settings given getWorkspaceConnections set to true', () => {
|
||||
let configurationService = new TestConfigurationService();
|
||||
configurationService.updateValue('datasource.connections', deepClone(testConnections).slice(0, 1), ConfigurationTarget.USER);
|
||||
configurationService.updateValue('datasource.connections', deepClone(testConnections).slice(1, testConnections.length), ConfigurationTarget.WORKSPACE);
|
||||
configurationService.updateValue(Constants.connectionsArrayName, deepClone(testConnections).slice(0, 1), ConfigurationTarget.USER);
|
||||
configurationService.updateValue(Constants.connectionsArrayName, deepClone(testConnections).slice(1, testConnections.length), ConfigurationTarget.WORKSPACE);
|
||||
|
||||
let config = new ConnectionConfig(configurationService, capabilitiesService.object);
|
||||
let allConnections = config.getConnections(true);
|
||||
@@ -350,8 +353,8 @@ suite('ConnectionConfig', () => {
|
||||
|
||||
test('getConnections should return connections from user settings given getWorkspaceConnections set to false', () => {
|
||||
let configurationService = new TestConfigurationService();
|
||||
configurationService.updateValue('datasource.connections', deepClone(testConnections).slice(0, 2), ConfigurationTarget.USER);
|
||||
configurationService.updateValue('datasource.connections', deepClone(testConnections).slice(2, testConnections.length), ConfigurationTarget.WORKSPACE);
|
||||
configurationService.updateValue(Constants.connectionsArrayName, deepClone(testConnections).slice(0, 2), ConfigurationTarget.USER);
|
||||
configurationService.updateValue(Constants.connectionsArrayName, deepClone(testConnections).slice(2, testConnections.length), ConfigurationTarget.WORKSPACE);
|
||||
|
||||
let config = new ConnectionConfig(configurationService, capabilitiesService.object);
|
||||
let allConnections = config.getConnections(false);
|
||||
@@ -368,8 +371,8 @@ suite('ConnectionConfig', () => {
|
||||
return c;
|
||||
});
|
||||
let configurationService = new TestConfigurationService();
|
||||
configurationService.updateValue('datasource.connections', userConnections, ConfigurationTarget.USER);
|
||||
configurationService.updateValue('datasource.connections', workspaceConnections, ConfigurationTarget.WORKSPACE);
|
||||
configurationService.updateValue(Constants.connectionsArrayName, userConnections, ConfigurationTarget.USER);
|
||||
configurationService.updateValue(Constants.connectionsArrayName, workspaceConnections, ConfigurationTarget.WORKSPACE);
|
||||
|
||||
let config = new ConnectionConfig(configurationService, capabilitiesService.object);
|
||||
let allConnections = config.getConnections(false);
|
||||
@@ -378,7 +381,7 @@ suite('ConnectionConfig', () => {
|
||||
let userConnection = testConnections.find(u => u.options['serverName'] === connection.serverName);
|
||||
if (userConnection !== undefined) {
|
||||
assert.notEqual(connection.id, connection.getOptionsKey());
|
||||
assert.ok(!!connection.id);
|
||||
assert.ok(!isUndefinedOrNull(connection.id));
|
||||
} else {
|
||||
let workspaceConnection = workspaceConnections.find(u => u.options['serverName'] === connection.serverName);
|
||||
assert.notEqual(connection.id, connection.getOptionsKey());
|
||||
@@ -394,7 +397,7 @@ suite('ConnectionConfig', () => {
|
||||
let color: string = 'red';
|
||||
|
||||
let result: ISaveGroupResult = config.saveGroup(groups, newGroups, color, newGroups);
|
||||
assert.ok(!!result);
|
||||
assert.ok(!isUndefinedOrNull(result));
|
||||
assert.equal(result.groups.length, testGroups.length + 2, 'The result groups length is invalid');
|
||||
let newGroup = result.groups.find(g => g.name === 'new-group2');
|
||||
assert.equal(result.newGroupId, newGroup.id, 'The groups id is invalid');
|
||||
@@ -407,7 +410,7 @@ suite('ConnectionConfig', () => {
|
||||
let color: string = 'red';
|
||||
|
||||
let result: ISaveGroupResult = config.saveGroup(groups, newGroups, color, newGroups);
|
||||
assert.ok(!!result);
|
||||
assert.ok(!isUndefinedOrNull(result));
|
||||
assert.equal(result.groups.length, testGroups.length + 1, 'The result groups length is invalid');
|
||||
let newGroup = result.groups.find(g => g.name === 'g2-5');
|
||||
assert.equal(result.newGroupId, newGroup.id, 'The groups id is invalid');
|
||||
@@ -420,7 +423,7 @@ suite('ConnectionConfig', () => {
|
||||
let color: string = 'red';
|
||||
|
||||
let result: ISaveGroupResult = config.saveGroup(groups, newGroups, color, newGroups);
|
||||
assert.ok(!!result);
|
||||
assert.ok(!isUndefinedOrNull(result));
|
||||
assert.equal(result.groups.length, testGroups.length, 'The result groups length is invalid');
|
||||
let newGroup = result.groups.find(g => g.name === 'g2-1');
|
||||
assert.equal(result.newGroupId, newGroup.id, 'The groups id is invalid');
|
||||
@@ -445,7 +448,7 @@ suite('ConnectionConfig', () => {
|
||||
connectionName: undefined
|
||||
};
|
||||
let configurationService = new TestConfigurationService();
|
||||
configurationService.updateValue('datasource.connections', deepClone(testConnections), ConfigurationTarget.USER);
|
||||
configurationService.updateValue(Constants.connectionsArrayName, deepClone(testConnections), ConfigurationTarget.USER);
|
||||
|
||||
let connectionProfile = new ConnectionProfile(capabilitiesService.object, newProfile);
|
||||
connectionProfile.options['databaseDisplayName'] = 'database';
|
||||
@@ -453,7 +456,7 @@ suite('ConnectionConfig', () => {
|
||||
let config = new ConnectionConfig(configurationService, capabilitiesService.object);
|
||||
await config.deleteConnection(connectionProfile);
|
||||
|
||||
assert.equal(configurationService.inspect<IConnectionProfileStore[]>('datasource.connections').user.length, testConnections.length - 1);
|
||||
assert.equal(configurationService.inspect<IConnectionProfileStore[]>(Constants.connectionsArrayName).user.length, testConnections.length - 1);
|
||||
});
|
||||
|
||||
test('deleteConnectionGroup should remove the children connections and subgroups from config', async () => {
|
||||
@@ -475,8 +478,8 @@ suite('ConnectionConfig', () => {
|
||||
connectionName: undefined
|
||||
};
|
||||
let configurationService = new TestConfigurationService();
|
||||
configurationService.updateValue('datasource.connections', deepClone(testConnections), ConfigurationTarget.USER);
|
||||
configurationService.updateValue('datasource.connectionGroups', deepClone(testGroups), ConfigurationTarget.USER);
|
||||
configurationService.updateValue(Constants.connectionsArrayName, deepClone(testConnections), ConfigurationTarget.USER);
|
||||
configurationService.updateValue(Constants.connectionGroupsArrayName, deepClone(testGroups), ConfigurationTarget.USER);
|
||||
|
||||
let connectionProfile = new ConnectionProfile(capabilitiesService.object, newProfile);
|
||||
connectionProfile.options['databaseDisplayName'] = 'database';
|
||||
@@ -489,8 +492,8 @@ suite('ConnectionConfig', () => {
|
||||
let config = new ConnectionConfig(configurationService, capabilitiesService.object);
|
||||
await config.deleteGroup(connectionProfileGroup);
|
||||
|
||||
assert.equal(configurationService.inspect<IConnectionProfileStore[]>('datasource.connections').user.length, testConnections.length - 1);
|
||||
assert.equal(configurationService.inspect<IConnectionProfileGroup[]>('datasource.connectionGroups').user.length, testGroups.length - 2);
|
||||
assert.equal(configurationService.inspect<IConnectionProfileStore[]>(Constants.connectionsArrayName).user.length, testConnections.length - 1);
|
||||
assert.equal(configurationService.inspect<IConnectionProfileGroup[]>(Constants.connectionGroupsArrayName).user.length, testGroups.length - 2);
|
||||
});
|
||||
|
||||
test('deleteConnection should not throw error for connection not in config', async () => {
|
||||
@@ -512,34 +515,34 @@ suite('ConnectionConfig', () => {
|
||||
connectionName: undefined
|
||||
};
|
||||
let configurationService = new TestConfigurationService();
|
||||
configurationService.updateValue('datasource.connections', deepClone(testConnections), ConfigurationTarget.USER);
|
||||
configurationService.updateValue(Constants.connectionsArrayName, deepClone(testConnections), ConfigurationTarget.USER);
|
||||
|
||||
let connectionProfile = new ConnectionProfile(capabilitiesService.object, newProfile);
|
||||
let config = new ConnectionConfig(configurationService, capabilitiesService.object);
|
||||
await config.deleteConnection(connectionProfile);
|
||||
|
||||
assert.equal(configurationService.inspect<IConnectionProfileStore[]>('datasource.connections').user.length, testConnections.length);
|
||||
assert.equal(configurationService.inspect<IConnectionProfileStore[]>(Constants.connectionsArrayName).user.length, testConnections.length);
|
||||
});
|
||||
|
||||
test('renameGroup should change group name', async () => {
|
||||
let configurationService = new TestConfigurationService();
|
||||
configurationService.updateValue('datasource.connectionGroups', deepClone(testGroups), ConfigurationTarget.USER);
|
||||
configurationService.updateValue(Constants.connectionGroupsArrayName, deepClone(testGroups), ConfigurationTarget.USER);
|
||||
|
||||
let connectionProfileGroup = new ConnectionProfileGroup('g-renamed', undefined, 'g2', undefined, undefined);
|
||||
let config = new ConnectionConfig(configurationService, capabilitiesService.object);
|
||||
await config.editGroup(connectionProfileGroup);
|
||||
|
||||
let editedGroups = configurationService.inspect<IConnectionProfileGroup[]>('datasource.connectionGroups').user;
|
||||
let editedGroups = configurationService.inspect<IConnectionProfileGroup[]>(Constants.connectionGroupsArrayName).user;
|
||||
|
||||
assert.equal(editedGroups.length, testGroups.length);
|
||||
let editedGroup = editedGroups.find(group => group.id === 'g2');
|
||||
assert.ok(!!editedGroup);
|
||||
assert.ok(!isUndefinedOrNull(editedGroup));
|
||||
assert.equal(editedGroup.name, 'g-renamed');
|
||||
});
|
||||
|
||||
test('edit group should throw if there is a confliction', async () => {
|
||||
let configurationService = new TestConfigurationService();
|
||||
configurationService.updateValue('datasource.connectionGroups', deepClone(testGroups), ConfigurationTarget.USER);
|
||||
configurationService.updateValue(Constants.connectionGroupsArrayName, deepClone(testGroups), ConfigurationTarget.USER);
|
||||
|
||||
let sameNameGroup = new ConnectionProfileGroup('g3', undefined, 'g2', undefined, undefined);
|
||||
let config = new ConnectionConfig(configurationService, capabilitiesService.object);
|
||||
@@ -548,27 +551,27 @@ suite('ConnectionConfig', () => {
|
||||
await config.editGroup(sameNameGroup);
|
||||
assert.fail();
|
||||
} catch (e) {
|
||||
let groups = configurationService.inspect<IConnectionProfileGroup[]>('datasource.connectionGroups').user;
|
||||
let groups = configurationService.inspect<IConnectionProfileGroup[]>(Constants.connectionGroupsArrayName).user;
|
||||
let originalGroup = groups.find(g => g.id === 'g2');
|
||||
assert.ok(!!originalGroup);
|
||||
assert.ok(!isUndefinedOrNull(originalGroup));
|
||||
assert.equal(originalGroup.name, 'g2');
|
||||
}
|
||||
});
|
||||
|
||||
test('change group(parent) for connection group', async () => {
|
||||
let configurationService = new TestConfigurationService();
|
||||
configurationService.updateValue('datasource.connectionGroups', deepClone(testGroups), ConfigurationTarget.USER);
|
||||
configurationService.updateValue(Constants.connectionGroupsArrayName, deepClone(testGroups), ConfigurationTarget.USER);
|
||||
|
||||
let sourceProfileGroup = new ConnectionProfileGroup('g2', undefined, 'g2', undefined, undefined);
|
||||
let targetProfileGroup = new ConnectionProfileGroup('g3', undefined, 'g3', undefined, undefined);
|
||||
let config = new ConnectionConfig(configurationService, capabilitiesService.object);
|
||||
await config.changeGroupIdForConnectionGroup(sourceProfileGroup, targetProfileGroup);
|
||||
|
||||
let editedGroups = configurationService.inspect<IConnectionProfileGroup[]>('datasource.connectionGroups').user;
|
||||
let editedGroups = configurationService.inspect<IConnectionProfileGroup[]>(Constants.connectionGroupsArrayName).user;
|
||||
|
||||
assert.equal(editedGroups.length, testGroups.length);
|
||||
let editedGroup = editedGroups.find(group => group.id === 'g2');
|
||||
assert.ok(!!editedGroup);
|
||||
assert.ok(!isUndefinedOrNull(editedGroup));
|
||||
assert.equal(editedGroup.parentId, 'g3');
|
||||
});
|
||||
|
||||
@@ -612,7 +615,7 @@ suite('ConnectionConfig', () => {
|
||||
let _testConnections = deepClone(testConnections).concat([existingProfile, changingProfile]);
|
||||
|
||||
let configurationService = new TestConfigurationService();
|
||||
configurationService.updateValue('datasource.connections', _testConnections, ConfigurationTarget.USER);
|
||||
configurationService.updateValue(Constants.connectionsArrayName, _testConnections, ConfigurationTarget.USER);
|
||||
|
||||
let connectionProfile = new ConnectionProfile(capabilitiesService.object, changingProfile);
|
||||
|
||||
@@ -621,11 +624,11 @@ suite('ConnectionConfig', () => {
|
||||
await config.changeGroupIdForConnection(connectionProfile, 'test');
|
||||
assert.fail();
|
||||
} catch (e) {
|
||||
let editedConnections = configurationService.inspect<IConnectionProfileStore[]>('datasource.connections').user;
|
||||
let editedConnections = configurationService.inspect<IConnectionProfileStore[]>(Constants.connectionsArrayName).user;
|
||||
// two
|
||||
assert.equal(editedConnections.length, _testConnections.length);
|
||||
let editedConnection = editedConnections.find(con => con.id === 'server3-2');
|
||||
assert.ok(!!editedConnection);
|
||||
assert.ok(!isUndefinedOrNull(editedConnection));
|
||||
assert.equal(editedConnection.groupId, 'g3');
|
||||
}
|
||||
});
|
||||
@@ -650,7 +653,7 @@ suite('ConnectionConfig', () => {
|
||||
};
|
||||
|
||||
let configurationService = new TestConfigurationService();
|
||||
configurationService.updateValue('datasource.connections', deepClone(testConnections), ConfigurationTarget.USER);
|
||||
configurationService.updateValue(Constants.connectionsArrayName, deepClone(testConnections), ConfigurationTarget.USER);
|
||||
|
||||
let connectionProfile = new ConnectionProfile(capabilitiesService.object, newProfile);
|
||||
let newId = 'newid';
|
||||
@@ -658,18 +661,18 @@ suite('ConnectionConfig', () => {
|
||||
let config = new ConnectionConfig(configurationService, capabilitiesService.object);
|
||||
await config.changeGroupIdForConnection(connectionProfile, newId);
|
||||
|
||||
let editedConnections = configurationService.inspect<IConnectionProfileStore[]>('datasource.connections').user;
|
||||
let editedConnections = configurationService.inspect<IConnectionProfileStore[]>(Constants.connectionsArrayName).user;
|
||||
assert.equal(editedConnections.length, testConnections.length);
|
||||
let editedConnection = editedConnections.find(con => con.id === 'server3');
|
||||
assert.ok(!!editedConnection);
|
||||
assert.ok(!isUndefinedOrNull(editedConnection));
|
||||
assert.equal(editedConnection.groupId, 'newid');
|
||||
});
|
||||
|
||||
test('addConnection should not move the connection when editing', async () => {
|
||||
// Set up the connection config
|
||||
let configurationService = new TestConfigurationService();
|
||||
configurationService.updateValue('datasource.connections', deepClone(testConnections), ConfigurationTarget.USER);
|
||||
configurationService.updateValue('datasource.connectionGroups', deepClone(testGroups), ConfigurationTarget.USER);
|
||||
configurationService.updateValue(Constants.connectionsArrayName, deepClone(testConnections), ConfigurationTarget.USER);
|
||||
configurationService.updateValue(Constants.connectionGroupsArrayName, deepClone(testGroups), ConfigurationTarget.USER);
|
||||
let config = new ConnectionConfig(configurationService, capabilitiesService.object);
|
||||
|
||||
// Clone a connection and modify an option
|
||||
@@ -699,13 +702,13 @@ suite('ConnectionConfig', () => {
|
||||
description: 'new group'
|
||||
};
|
||||
let configurationService = new TestConfigurationService();
|
||||
configurationService.updateValue('datasource.connectionGroups', deepClone(testGroups), ConfigurationTarget.USER);
|
||||
configurationService.updateValue(Constants.connectionGroupsArrayName, deepClone(testGroups), ConfigurationTarget.USER);
|
||||
|
||||
let config = new ConnectionConfig(configurationService, capabilitiesService.object);
|
||||
|
||||
await config.addGroup(newGroup);
|
||||
|
||||
let editGroups = configurationService.inspect<IConnectionProfileGroup[]>('datasource.connectionGroups').user;
|
||||
let editGroups = configurationService.inspect<IConnectionProfileGroup[]>(Constants.connectionGroupsArrayName).user;
|
||||
|
||||
assert.equal(editGroups.length, testGroups.length + 1);
|
||||
});
|
||||
@@ -719,14 +722,14 @@ suite('ConnectionConfig', () => {
|
||||
description: 'new group'
|
||||
};
|
||||
let configurationService = new TestConfigurationService();
|
||||
configurationService.updateValue('datasource.connectionGroups', deepClone(testGroups), ConfigurationTarget.USER);
|
||||
configurationService.updateValue(Constants.connectionGroupsArrayName, deepClone(testGroups), ConfigurationTarget.USER);
|
||||
|
||||
const config = new ConnectionConfig(configurationService, capabilitiesService.object);
|
||||
try {
|
||||
await config.addGroup(existingGroupName);
|
||||
assert.fail();
|
||||
} catch (e) {
|
||||
let editGroups = configurationService.inspect<IConnectionProfileGroup[]>('datasource.connectionGroups').user;
|
||||
let editGroups = configurationService.inspect<IConnectionProfileGroup[]>(Constants.connectionGroupsArrayName).user;
|
||||
|
||||
assert.equal(editGroups.length, testGroups.length);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
@@ -17,8 +17,13 @@ export class TestConfigurationService implements IConfigurationService {
|
||||
return Promise.resolve(this.getValue());
|
||||
}
|
||||
|
||||
public getValue(arg1?: any): any {
|
||||
return getConfigurationValue(this.configuration.user, arg1);
|
||||
public getValue(arg1?: any, arg2?: any): any {
|
||||
let configuration;
|
||||
configuration = configuration ? configuration : this.configuration;
|
||||
if (arg1 && typeof arg1 === 'string') {
|
||||
return getConfigurationValue(configuration, arg1);
|
||||
}
|
||||
return configuration;
|
||||
}
|
||||
|
||||
public updateValue(key: string, value: any, target?: any): Promise<void> {
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
@@ -25,11 +25,11 @@ export const ICredentialsService = createDecorator<ICredentialsService>(SERVICE_
|
||||
export interface ICredentialsService {
|
||||
_serviceBrand: any;
|
||||
|
||||
saveCredential(credentialId: string, password: string): Promise<boolean>;
|
||||
saveCredential(credentialId: string, password: string): Thenable<boolean>;
|
||||
|
||||
readCredential(credentialId: string): Promise<azdata.Credential>;
|
||||
readCredential(credentialId: string): Thenable<azdata.Credential>;
|
||||
|
||||
deleteCredential(credentialId: string): Promise<boolean>;
|
||||
deleteCredential(credentialId: string): Thenable<boolean>;
|
||||
|
||||
addEventListener(handle: number, events: CredentialManagementEvents): IDisposable;
|
||||
}
|
||||
@@ -62,15 +62,15 @@ export class CredentialsService implements ICredentialsService {
|
||||
};
|
||||
}
|
||||
|
||||
public saveCredential(credentialId: string, password: string): Promise<boolean> {
|
||||
public saveCredential(credentialId: string, password: string): Thenable<boolean> {
|
||||
return this._onServerEventsReady.promise.then(() => this._serverEvents[this._lastHandle].onSaveCredential(credentialId, password));
|
||||
}
|
||||
|
||||
public readCredential(credentialId: string): Promise<azdata.Credential> {
|
||||
public readCredential(credentialId: string): Thenable<azdata.Credential> {
|
||||
return this._onServerEventsReady.promise.then(() => this._serverEvents[this._lastHandle].onReadCredential(credentialId));
|
||||
}
|
||||
|
||||
public deleteCredential(credentialId: string): Promise<boolean> {
|
||||
public deleteCredential(credentialId: string): Thenable<boolean> {
|
||||
return this._onServerEventsReady.promise.then(() => this._serverEvents[this._lastHandle].onDeleteCredential(credentialId));
|
||||
}
|
||||
|
||||
|
||||
@@ -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.');
|
||||
}
|
||||
}
|
||||
@@ -99,7 +99,7 @@ suite('SQL ConnectionManagementService tests', () => {
|
||||
connectionDialogService.setup(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(none));
|
||||
connectionDialogService.setup(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), undefined, TypeMoq.It.isAny())).returns(() => Promise.resolve(none));
|
||||
|
||||
connectionStore.setup(x => x.addRecentConnection(TypeMoq.It.isAny())).returns(() => Promise.resolve());
|
||||
connectionStore.setup(x => x.addActiveConnection(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve());
|
||||
connectionStore.setup(x => x.saveProfile(TypeMoq.It.isAny())).returns(() => Promise.resolve(connectionProfile));
|
||||
workbenchEditorService.setup(x => x.openEditor(undefined, TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(undefined));
|
||||
connectionStore.setup(x => x.addSavedPassword(TypeMoq.It.is<IConnectionProfile>(
|
||||
@@ -147,13 +147,16 @@ suite('SQL ConnectionManagementService tests', () => {
|
||||
|
||||
function createConnectionManagementService(): ConnectionManagementService {
|
||||
let connectionManagementService = new ConnectionManagementService(
|
||||
undefined,
|
||||
connectionStore.object,
|
||||
new TestStorageService(),
|
||||
connectionDialogService.object,
|
||||
undefined,
|
||||
undefined,
|
||||
workbenchEditorService.object,
|
||||
undefined,
|
||||
workspaceConfigurationServiceMock.object,
|
||||
undefined,
|
||||
capabilitiesService,
|
||||
undefined,
|
||||
editorGroupService.object,
|
||||
|
||||
508
src/sqltest/parts/connection/connectionStore.test.ts
Normal file
508
src/sqltest/parts/connection/connectionStore.test.ts
Normal 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);
|
||||
});
|
||||
});
|
||||
@@ -18,6 +18,7 @@ import { TestConnectionManagementService } from 'sqltest/stubs/connectionManagem
|
||||
import { ErrorMessageServiceStub } from 'sqltest/stubs/errorMessageServiceStub';
|
||||
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
|
||||
import { ServerTreeView } from 'sql/parts/objectExplorer/viewlet/serverTreeView';
|
||||
import * as Constants from 'sql/platform/connection/common/constants';
|
||||
import * as LocalizedConstants from 'sql/parts/connection/common/localizedConstants';
|
||||
import { ObjectExplorerService, ObjectExplorerNodeEventArgs } from 'sql/workbench/services/objectExplorer/common/objectExplorerService';
|
||||
import { TreeNode } from 'sql/parts/objectExplorer/common/treeNode';
|
||||
@@ -31,7 +32,6 @@ import { ObjectExplorerActionsContext, ManageConnectionAction } from 'sql/parts/
|
||||
import { IConnectionResult, IConnectionParams } from 'sql/platform/connection/common/connectionManagement';
|
||||
import { TreeSelectionHandler } from 'sql/parts/objectExplorer/viewlet/treeSelectionHandler';
|
||||
import { CapabilitiesTestService } from 'sqltest/stubs/capabilitiesTestService';
|
||||
import { UNSAVED_GROUP_ID } from 'sql/platform/connection/common/constants';
|
||||
|
||||
suite('SQL Connection Tree Action tests', () => {
|
||||
let errorMessageService: TypeMoq.Mock<ErrorMessageServiceStub>;
|
||||
@@ -328,7 +328,7 @@ suite('SQL Connection Tree Action tests', () => {
|
||||
saveProfile: true,
|
||||
id: 'testId'
|
||||
});
|
||||
connection.parent = new ConnectionProfileGroup(LocalizedConstants.unsavedGroupLabel, undefined, UNSAVED_GROUP_ID, undefined, undefined);
|
||||
connection.parent = new ConnectionProfileGroup(LocalizedConstants.unsavedGroupLabel, undefined, Constants.unsavedGroupId, undefined, undefined);
|
||||
let connectionAction: DeleteConnectionAction = new DeleteConnectionAction(DeleteConnectionAction.ID,
|
||||
DeleteConnectionAction.DELETE_CONNECTION_LABEL,
|
||||
connection,
|
||||
@@ -513,4 +513,4 @@ suite('SQL Connection Tree Action tests', () => {
|
||||
}).then(() => done(), (err) => done(err));
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
@@ -124,7 +124,7 @@ suite('SQL QueryAction Tests', () => {
|
||||
.returns(() => Promise.resolve(none));
|
||||
|
||||
// ... Mock "isConnected" in ConnectionManagementService
|
||||
let connectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, {}, connectionDialogService.object);
|
||||
let connectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, {}, {}, new TestStorageService(), connectionDialogService.object);
|
||||
connectionManagementService.callBase = true;
|
||||
connectionManagementService.setup(x => x.isConnected(TypeMoq.It.isAnyString())).returns(() => isConnected);
|
||||
|
||||
@@ -182,7 +182,7 @@ suite('SQL QueryAction Tests', () => {
|
||||
queryEditor.setup(x => x.isSelectionEmpty()).returns(() => isSelectionEmpty);
|
||||
|
||||
// ... Mock "isConnected" in ConnectionManagementService
|
||||
let connectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, {});
|
||||
let connectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, {}, {}, new TestStorageService());
|
||||
connectionManagementService.callBase = true;
|
||||
connectionManagementService.setup(x => x.isConnected(TypeMoq.It.isAnyString())).returns(() => true);
|
||||
|
||||
@@ -249,7 +249,7 @@ suite('SQL QueryAction Tests', () => {
|
||||
});
|
||||
|
||||
// ... Mock "isConnected" in ConnectionManagementService
|
||||
let connectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, {}, connectionDialogService.object);
|
||||
let connectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, {}, {}, new TestStorageService(), connectionDialogService.object);
|
||||
connectionManagementService.callBase = true;
|
||||
connectionManagementService.setup(x => x.isConnected(TypeMoq.It.isAnyString())).returns(() => isConnected);
|
||||
|
||||
@@ -315,7 +315,7 @@ suite('SQL QueryAction Tests', () => {
|
||||
let calledCancelQuery: boolean = false;
|
||||
|
||||
// ... Mock "isConnected" in ConnectionManagementService
|
||||
let connectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, {});
|
||||
let connectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, {}, {}, new TestStorageService());
|
||||
connectionManagementService.setup(x => x.isConnected(TypeMoq.It.isAnyString())).returns(() => isConnected);
|
||||
|
||||
// ... Mock QueryModelService
|
||||
@@ -348,7 +348,7 @@ suite('SQL QueryAction Tests', () => {
|
||||
let countCalledDisconnectEditor: number = 0;
|
||||
|
||||
// ... Mock "isConnected" and "disconnectEditor" in ConnectionManagementService
|
||||
let connectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, {});
|
||||
let connectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, {}, {}, new TestStorageService());
|
||||
connectionManagementService.setup(x => x.isConnected(TypeMoq.It.isAnyString())).returns(() => isConnected);
|
||||
connectionManagementService.setup(x => x.disconnectEditor(TypeMoq.It.isAny())).callback(() => {
|
||||
countCalledDisconnectEditor++;
|
||||
@@ -387,7 +387,7 @@ suite('SQL QueryAction Tests', () => {
|
||||
.returns(() => Promise.resolve(none));
|
||||
|
||||
// ... Mock "isConnected" in ConnectionManagementService
|
||||
let connectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, {}, connectionDialogService.object);
|
||||
let connectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, {}, {}, new TestStorageService(), connectionDialogService.object);
|
||||
connectionManagementService.callBase = true;
|
||||
connectionManagementService.setup(x => x.isConnected(TypeMoq.It.isAnyString())).returns(() => isConnected);
|
||||
|
||||
@@ -433,7 +433,7 @@ suite('SQL QueryAction Tests', () => {
|
||||
.returns(() => Promise.resolve(none));
|
||||
|
||||
// ... Mock "isConnected" in ConnectionManagementService
|
||||
let connectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, {}, connectionDialogService.object);
|
||||
let connectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, {}, {}, new TestStorageService(), connectionDialogService.object);
|
||||
connectionManagementService.callBase = true;
|
||||
connectionManagementService.setup(x => x.isConnected(TypeMoq.It.isAnyString())).returns(() => isConnected);
|
||||
|
||||
@@ -468,7 +468,7 @@ suite('SQL QueryAction Tests', () => {
|
||||
let databaseName: string = undefined;
|
||||
|
||||
// ... Mock "isConnected" in ConnectionManagementService
|
||||
let connectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, {});
|
||||
let connectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, {}, {}, new TestStorageService());
|
||||
connectionManagementService.callBase = true;
|
||||
connectionManagementService.setup(x => x.isConnected(TypeMoq.It.isAnyString())).returns(() => isConnected);
|
||||
connectionManagementService.setup(x => x.getConnectionProfile(TypeMoq.It.isAny())).returns(() => <IConnectionProfile>{
|
||||
@@ -505,7 +505,7 @@ suite('SQL QueryAction Tests', () => {
|
||||
|
||||
// ... Create mock connection management service
|
||||
let databaseName = 'foobar';
|
||||
let cms = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, {});
|
||||
let cms = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, {}, {}, new TestStorageService());
|
||||
cms.callBase = true;
|
||||
cms.setup(x => x.onConnectionChanged).returns(() => dbChangedEmitter.event);
|
||||
cms.setup(x => x.getConnectionProfile(TypeMoq.It.isAny())).returns(() => <IConnectionProfile>{ databaseName: databaseName });
|
||||
|
||||
@@ -36,15 +36,15 @@ export class CredentialsTestProvider implements azdata.CredentialProvider {
|
||||
export class CredentialsTestService implements ICredentialsService {
|
||||
_serviceBrand: any;
|
||||
|
||||
saveCredential(credentialId: string, password: string): Promise<boolean> {
|
||||
saveCredential(credentialId: string, password: string): Thenable<boolean> {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
readCredential(credentialId: string): Promise<azdata.Credential> {
|
||||
readCredential(credentialId: string): Thenable<azdata.Credential> {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
deleteCredential(credentialId: string): Promise<boolean> {
|
||||
deleteCredential(credentialId: string): Thenable<boolean> {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -52,4 +52,4 @@ export class CredentialsTestService implements ICredentialsService {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user