diff --git a/src/sql/base/common/map.ts b/src/sql/base/common/map.ts index 59a7531f02..4adc7f793e 100644 --- a/src/sql/base/common/map.ts +++ b/src/sql/base/common/map.ts @@ -111,4 +111,3 @@ export class TrieMap { return result; } } - diff --git a/src/sql/parts/admin/common/adminService.ts b/src/sql/parts/admin/common/adminService.ts index 926172ae81..25ef6a9220 100644 --- a/src/sql/parts/admin/common/adminService.ts +++ b/src/sql/parts/admin/common/adminService.ts @@ -43,19 +43,12 @@ export class AdminService implements IAdminService { private _providers: { [handle: string]: sqlops.AdminServicesProvider; } = Object.create(null); - private _providerOptions: { [handle: string]: sqlops.AdminServicesOptions; } = Object.create(null); - constructor( @IInstantiationService private _instantiationService: IInstantiationService, @IWorkbenchEditorService private _editorService: IWorkbenchEditorService, @IConnectionManagementService private _connectionService: IConnectionManagementService, @ICapabilitiesService private _capabilitiesService: ICapabilitiesService ) { - if (_capabilitiesService && _capabilitiesService.onProviderRegisteredEvent) { - _capabilitiesService.onProviderRegisteredEvent((capabilities => { - this._providerOptions[capabilities.providerName] = capabilities.adminServicesProvider; - })); - } } private _runAction(uri: string, action: (handler: sqlops.AdminServicesProvider) => Thenable): Thenable { diff --git a/src/sql/parts/connection/common/connectionConfig.ts b/src/sql/parts/connection/common/connectionConfig.ts index db3638ca6f..83a858c86e 100644 --- a/src/sql/parts/connection/common/connectionConfig.ts +++ b/src/sql/parts/connection/common/connectionConfig.ts @@ -29,32 +29,14 @@ export interface ISaveGroupResult { */ export class ConnectionConfig implements IConnectionConfig { - private _providerCapabilitiesMap: { [providerName: string]: sqlops.DataProtocolServerCapabilities }; /** * Constructor. */ public constructor( private _configurationEditService: ConfigurationEditingService, private _workspaceConfigurationService: IWorkspaceConfigurationService, - private _capabilitiesService: ICapabilitiesService, - cachedMetadata?: sqlops.DataProtocolServerCapabilities[] - ) { - this._providerCapabilitiesMap = {}; - this.setCachedMetadata(cachedMetadata); - if (this._capabilitiesService && this._capabilitiesService.onCapabilitiesReady()) { - this._capabilitiesService.onCapabilitiesReady().then(() => { - this.setCachedMetadata(this._capabilitiesService.getCapabilities()); - }); - } - } - - public setCachedMetadata(cachedMetadata: sqlops.DataProtocolServerCapabilities[]): void { - if (cachedMetadata) { - cachedMetadata.forEach(item => { - this.updateCapabilitiesCache(item.providerName, item); - }); - } - } + private _capabilitiesService: ICapabilitiesService + ) { } /** * Returns connection groups from user and workspace settings. @@ -81,44 +63,6 @@ export class ConnectionConfig implements IConnectionConfig { return allGroups; } - private updateCapabilitiesCache(providerName: string, providerCapabilities: sqlops.DataProtocolServerCapabilities): void { - if (providerName && providerCapabilities) { - this._providerCapabilitiesMap[providerName] = providerCapabilities; - } - } - - private getCapabilitiesFromCache(providerName: string): sqlops.DataProtocolServerCapabilities { - if (providerName in this._providerCapabilitiesMap) { - return this._providerCapabilitiesMap[providerName]; - } - return undefined; - } - - /** - * Returns the capabilities for given provider name. First tries to get it from capabilitiesService and if it's not registered yet, - * Gets the data from the metadata stored in the config - * @param providerName Provider Name - */ - public getCapabilities(providerName: string): sqlops.DataProtocolServerCapabilities { - let result: sqlops.DataProtocolServerCapabilities = this.getCapabilitiesFromCache(providerName); - if (result) { - return result; - } else { - let capabilities = this._capabilitiesService.getCapabilities(); - if (capabilities) { - let providerCapabilities = capabilities.find(c => c.providerName === providerName); - if (providerCapabilities) { - this.updateCapabilitiesCache(providerName, providerCapabilities); - return providerCapabilities; - } else { - return undefined; - } - } else { - return undefined; - } - } - } - /** * Add a new connection to the connection config. */ @@ -131,14 +75,12 @@ export class ConnectionConfig implements IConnectionConfig { profiles = []; } - let providerCapabilities = this.getCapabilities(profile.providerName); - let connectionProfile = this.getConnectionProfileInstance(profile, groupId, providerCapabilities); - let newProfile = ConnectionProfile.convertToProfileStore(providerCapabilities, connectionProfile); + let connectionProfile = this.getConnectionProfileInstance(profile, groupId); + let newProfile = ConnectionProfile.convertToProfileStore(this._capabilitiesService, connectionProfile); // Remove the profile if already set var sameProfileInList = profiles.find(value => { - let providerCapabilities = this.getCapabilities(value.providerName); - let providerConnectionProfile = ConnectionProfile.createFromStoredProfile(value, providerCapabilities); + let providerConnectionProfile = ConnectionProfile.createFromStoredProfile(value, this._capabilitiesService); return providerConnectionProfile.matches(connectionProfile); }); if (sameProfileInList) { @@ -159,10 +101,10 @@ export class ConnectionConfig implements IConnectionConfig { }); } - private getConnectionProfileInstance(profile: IConnectionProfile, groupId: string, providerCapabilities: sqlops.DataProtocolServerCapabilities): ConnectionProfile { + private getConnectionProfileInstance(profile: IConnectionProfile, groupId: string): ConnectionProfile { let connectionProfile = profile as ConnectionProfile; if (connectionProfile === undefined) { - connectionProfile = new ConnectionProfile(providerCapabilities, profile); + connectionProfile = new ConnectionProfile(this._capabilitiesService, profile); } connectionProfile.groupId = groupId; return connectionProfile; @@ -286,15 +228,7 @@ export class ConnectionConfig implements IConnectionConfig { } let connectionProfiles = profiles.map(p => { - let capabilitiesForProvider = this.getCapabilities(p.providerName); - - let providerConnectionProfile = ConnectionProfile.createFromStoredProfile(p, capabilitiesForProvider); - providerConnectionProfile.setServerCapabilities(capabilitiesForProvider); - this._capabilitiesService.onProviderRegisteredEvent((serverCapabilities) => { - providerConnectionProfile.onProviderRegistered(serverCapabilities); - }); - - return providerConnectionProfile; + return ConnectionProfile.createFromStoredProfile(p, this._capabilitiesService); }); return connectionProfiles; @@ -308,8 +242,7 @@ export class ConnectionConfig implements IConnectionConfig { let profiles = this._workspaceConfigurationService.inspect(Constants.connectionsArrayName).user; // Remove the profile from the connections profiles = profiles.filter(value => { - let providerCapabilities = this.getCapabilities(value.providerName); - let providerConnectionProfile = ConnectionProfile.createFromStoredProfile(value, providerCapabilities); + let providerConnectionProfile = ConnectionProfile.createFromStoredProfile(value, this._capabilitiesService); return providerConnectionProfile.getOptionsKey() !== profile.getOptionsKey(); }); @@ -330,8 +263,7 @@ export class ConnectionConfig implements IConnectionConfig { let profiles = this._workspaceConfigurationService.inspect(Constants.connectionsArrayName).user; // Remove the profiles from the connections profiles = profiles.filter(value => { - let providerCapabilities = this.getCapabilities(value.providerName); - let providerConnectionProfile = ConnectionProfile.createFromStoredProfile(value, providerCapabilities); + let providerConnectionProfile = ConnectionProfile.createFromStoredProfile(value, this._capabilitiesService); return !connections.some((val) => val.getOptionsKey() === providerConnectionProfile.getOptionsKey()); }); @@ -382,13 +314,12 @@ export class ConnectionConfig implements IConnectionConfig { let profiles = target === ConfigurationTarget.USER ? this._workspaceConfigurationService.inspect(Constants.connectionsArrayName).user : this._workspaceConfigurationService.inspect(Constants.connectionsArrayName).workspace; if (profiles) { - let providerCapabilities = this.getCapabilities(profile.providerName); if (profile.parent && profile.parent.id === Constants.unsavedGroupId) { profile.groupId = newGroupID; - profiles.push(ConnectionProfile.convertToProfileStore(providerCapabilities, profile)); + profiles.push(ConnectionProfile.convertToProfileStore(this._capabilitiesService, profile)); } else { profiles.forEach((value) => { - let configProf = ConnectionProfile.createFromStoredProfile(value, providerCapabilities); + let configProf = ConnectionProfile.createFromStoredProfile(value, this._capabilitiesService); if (configProf.getOptionsKey() === profile.getOptionsKey()) { value.groupId = newGroupID; } diff --git a/src/sql/parts/connection/common/connectionManagement.ts b/src/sql/parts/connection/common/connectionManagement.ts index c31580f5c0..3ddc717af1 100644 --- a/src/sql/parts/connection/common/connectionManagement.ts +++ b/src/sql/parts/connection/common/connectionManagement.ts @@ -216,8 +216,6 @@ export interface IConnectionManagementService { hasRegisteredServers(): boolean; - getCapabilities(providerName: string): sqlops.DataProtocolServerCapabilities; - canChangeConnectionConfig(profile: ConnectionProfile, newGroupID: string): boolean; getTabColorForUri(uri: string): string; diff --git a/src/sql/parts/connection/common/connectionManagementService.ts b/src/sql/parts/connection/common/connectionManagementService.ts index 5b1cd5082a..e60c18685b 100644 --- a/src/sql/parts/connection/common/connectionManagementService.ts +++ b/src/sql/parts/connection/common/connectionManagementService.ts @@ -137,9 +137,9 @@ export class ConnectionManagementService implements IConnectionManagementService 100 /* High Priority */ )); - if (_capabilitiesService && _capabilitiesService.onProviderRegisteredEvent) { - _capabilitiesService.onProviderRegisteredEvent((capabilities => { - if (capabilities.providerName === 'MSSQL') { + if (_capabilitiesService) { + _capabilitiesService.onCapabilitiesRegistered((p => { + if (p === 'MSSQL') { if (!this.hasRegisteredServers()) { // prompt the user for a new connection on startup if no profiles are registered this.showConnectionDialog(); @@ -679,21 +679,13 @@ export class ConnectionManagementService implements IConnectionManagementService return Object.keys(this._providers); } - public getCapabilities(providerName: string): sqlops.DataProtocolServerCapabilities { - let capabilities = this._capabilitiesService.getCapabilities(); - if (capabilities !== undefined && capabilities.length > 0) { - return capabilities.find(c => c.providerName === providerName); - } - return undefined; - } - public getAdvancedProperties(): sqlops.ConnectionOption[] { - let capabilities = this._capabilitiesService.getCapabilities(); - if (capabilities !== undefined && capabilities.length > 0) { + let providers = this._capabilitiesService.providers; + if (providers !== undefined && providers.length > 0) { // just grab the first registered provider for now, this needs to change // to lookup based on currently select provider - let providerCapabilities = capabilities[0]; + let providerCapabilities = this._capabilitiesService.getCapabilities(providers[0]); if (!!providerCapabilities.connectionProvider) { return providerCapabilities.connectionProvider.options; } @@ -1362,7 +1354,7 @@ export class ConnectionManagementService implements IConnectionManagementService } // Find the password option for the connection provider - let passwordOption = this._capabilitiesService.getCapabilities().find(capability => capability.providerName === profile.providerName).connectionProvider.options.find( + let passwordOption = this._capabilitiesService.getCapabilities(profile.providerName).connectionProvider.options.find( option => option.specialValueType === ConnectionOptionSpecialType.password); if (!passwordOption) { return undefined; diff --git a/src/sql/parts/connection/common/connectionProfile.ts b/src/sql/parts/connection/common/connectionProfile.ts index 6c49589a5b..a7c618da97 100644 --- a/src/sql/parts/connection/common/connectionProfile.ts +++ b/src/sql/parts/connection/common/connectionProfile.ts @@ -12,6 +12,8 @@ import * as interfaces from 'sql/parts/connection/common/interfaces'; import { equalsIgnoreCase } from 'vs/base/common/strings'; import { generateUuid } from 'vs/base/common/uuid'; import * as objects from 'sql/base/common/objects'; +import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService'; +import { isString } from 'vs/base/common/types'; // Concrete implementation of the IConnectionProfile interface @@ -28,9 +30,13 @@ export class ConnectionProfile extends ProviderConnectionInfo implements interfa public saveProfile: boolean; public isDisconnecting: boolean = false; - public constructor(serverCapabilities?: sqlops.DataProtocolServerCapabilities, model?: interfaces.IConnectionProfile) { - super(serverCapabilities, model); - if (model) { + + public constructor( + capabilitiesService: ICapabilitiesService, + model: string | interfaces.IConnectionProfile + ) { + super(capabilitiesService, model); + if (model && !isString(model)) { this.groupId = model.groupId; this.groupFullName = model.groupFullName; this.savePassword = model.savePassword; @@ -91,7 +97,7 @@ export class ConnectionProfile extends ProviderConnectionInfo implements interfa } public clone(): ConnectionProfile { - let instance = new ConnectionProfile(this._serverCapabilities, this); + let instance = new ConnectionProfile(this.capabilitiesService, this); return instance; } @@ -137,12 +143,6 @@ export class ConnectionProfile extends ProviderConnectionInfo implements interfa return super.getOptionsKey(); } - public onProviderRegistered(serverCapabilities: sqlops.DataProtocolServerCapabilities): void { - if (serverCapabilities.providerName === this.providerName) { - this.setServerCapabilities(serverCapabilities); - } - } - public toIConnectionProfile(): interfaces.IConnectionProfile { let result: interfaces.IConnectionProfile = { serverName: this.serverName, @@ -170,8 +170,19 @@ export class ConnectionProfile extends ProviderConnectionInfo implements interfa }; } - public static createFromStoredProfile(profile: interfaces.IConnectionProfileStore, serverCapabilities: sqlops.DataProtocolServerCapabilities): ConnectionProfile { - let connectionInfo = new ConnectionProfile(serverCapabilities, undefined); + public static fromIConnectionProfile(capabilitiesService: ICapabilitiesService, profile: interfaces.IConnectionProfile) { + if (profile) { + if (profile instanceof ConnectionProfile) { + return profile; + } else { + return new ConnectionProfile(capabilitiesService, profile); + } + } + return undefined; + } + + public static createFromStoredProfile(profile: interfaces.IConnectionProfileStore, capabilitiesService: ICapabilitiesService): ConnectionProfile { + let connectionInfo = new ConnectionProfile(capabilitiesService, profile.providerName); connectionInfo.options = profile.options; // append group ID and original display name to build unique OE session ID @@ -187,28 +198,11 @@ export class ConnectionProfile extends ProviderConnectionInfo implements interfa return connectionInfo; } - public static convertToConnectionProfile(serverCapabilities: sqlops.DataProtocolServerCapabilities, conn: interfaces.IConnectionProfile): ConnectionProfile { - if (conn) { - let connectionProfile: ConnectionProfile = undefined; - let connectionProfileInstance = conn as ConnectionProfile; - if (connectionProfileInstance && conn instanceof ConnectionProfile) { - connectionProfile = connectionProfileInstance; - connectionProfile.setServerCapabilities(serverCapabilities); - } else { - connectionProfile = new ConnectionProfile(serverCapabilities, conn); - } - - return connectionProfile; - } else { - return undefined; - } - } - public static convertToProfileStore( - serverCapabilities: sqlops.DataProtocolServerCapabilities, + capabilitiesService: ICapabilitiesService, connectionProfile: interfaces.IConnectionProfile): interfaces.IConnectionProfileStore { if (connectionProfile) { - let connectionInfo = ConnectionProfile.convertToConnectionProfile(serverCapabilities, connectionProfile); + let connectionInfo = ConnectionProfile.fromIConnectionProfile(capabilitiesService, connectionProfile); let profile: interfaces.IConnectionProfileStore = { options: {}, groupId: connectionProfile.groupId, @@ -224,5 +218,4 @@ export class ConnectionProfile extends ProviderConnectionInfo implements interfa return undefined; } } - } diff --git a/src/sql/parts/connection/common/connectionStatusManager.ts b/src/sql/parts/connection/common/connectionStatusManager.ts index 2b87998c13..b0bc190f81 100644 --- a/src/sql/parts/connection/common/connectionStatusManager.ts +++ b/src/sql/parts/connection/common/connectionStatusManager.ts @@ -22,25 +22,6 @@ export class ConnectionStatusManager { this._providerCapabilitiesMap = {}; } - public getCapabilities(providerName: string): sqlops.DataProtocolServerCapabilities { - let result: sqlops.DataProtocolServerCapabilities; - - if (providerName in this._providerCapabilitiesMap) { - result = this._providerCapabilitiesMap[providerName]; - } else { - let capabilities = this._capabilitiesService.getCapabilities(); - if (capabilities) { - let providerCapabilities = capabilities.find(c => c.providerName === providerName); - if (providerCapabilities) { - this._providerCapabilitiesMap[providerName] = providerCapabilities; - result = providerCapabilities; - } - } - } - - return result; - } - public findConnection(id: string): ConnectionManagementInfo { if (id in this._connections) { return this._connections[id]; @@ -80,15 +61,14 @@ export class ConnectionStatusManager { public addConnection(connection: IConnectionProfile, id: string): ConnectionManagementInfo { // Always create a copy and save that in the list - let connectionProfile = new ConnectionProfile(this.getCapabilities(connection.providerName), connection); - const self = this; + let connectionProfile = new ConnectionProfile(this._capabilitiesService, connection); let connectionInfo: ConnectionManagementInfo = new ConnectionManagementInfo(); connectionInfo.providerId = connection.providerName; connectionInfo.extensionTimer = StopWatch.create(); connectionInfo.intelliSenseTimer = StopWatch.create(); connectionInfo.connectionProfile = connectionProfile; connectionInfo.connecting = true; - self._connections[id] = connectionInfo; + this._connections[id] = connectionInfo; connectionInfo.serviceTimer = StopWatch.create(); connectionInfo.ownerUri = id; diff --git a/src/sql/parts/connection/common/connectionStore.ts b/src/sql/parts/connection/common/connectionStore.ts index c66b39d723..a8afeabc22 100644 --- a/src/sql/parts/connection/common/connectionStore.ts +++ b/src/sql/parts/connection/common/connectionStore.ts @@ -50,14 +50,8 @@ export class ConnectionStore { this._groupIdToFullNameMap = {}; this._groupFullNameToIdMap = {}; if (!this._connectionConfig) { - let cachedServerCapabilities = this.getCachedServerCapabilities(); this._connectionConfig = new ConnectionConfig(this._configurationEditService, - this._workspaceConfigurationService, this._capabilitiesService, cachedServerCapabilities); - } - if (_capabilitiesService) { - _capabilitiesService.onProviderRegisteredEvent(e => { - this.saveCachedServerCapabilities(); - }); + this._workspaceConfigurationService, this._capabilitiesService); } } @@ -84,8 +78,8 @@ export class ConnectionStore { * @returns {string} formatted string with server, DB and username */ public formatCredentialId(connectionProfile: IConnectionProfile, itemType?: string): string { - let connectionProfileInstance: ConnectionProfile = ConnectionProfile.convertToConnectionProfile( - this._connectionConfig.getCapabilities(connectionProfile.providerName), connectionProfile); + let connectionProfileInstance: ConnectionProfile = ConnectionProfile.fromIConnectionProfile( + this._capabilitiesService, connectionProfile); if (!connectionProfileInstance.getConnectionInfoId()) { throw new Error('Missing Id, which is required'); } @@ -111,7 +105,7 @@ export class ConnectionStore { */ public isPasswordRequired(connection: IConnectionProfile): boolean { if (connection) { - let connectionProfile = ConnectionProfile.convertToConnectionProfile(this._connectionConfig.getCapabilities(connection.providerName), connection); + let connectionProfile = ConnectionProfile.fromIConnectionProfile(this._capabilitiesService, connection); return connectionProfile.isPasswordRequired(); } else { return false; @@ -174,7 +168,6 @@ export class ConnectionStore { // Add necessary default properties before returning // this is needed to support immediate connections ConnInfo.fixupConnectionCredentials(profile); - this.saveCachedServerCapabilities(); resolve(profile); }, err => { reject(err); @@ -214,23 +207,6 @@ export class ConnectionStore { }); } - private getCachedServerCapabilities(): sqlops.DataProtocolServerCapabilities[] { - if (this._memento) { - let metadata: sqlops.DataProtocolServerCapabilities[] = this._memento[Constants.capabilitiesOptions]; - return metadata; - } else { - return undefined; - } - - } - - private saveCachedServerCapabilities(): void { - if (this._memento) { - let capabilities = this._capabilitiesService.getCapabilities(); - this._memento[Constants.capabilitiesOptions] = capabilities; - } - } - /** * Gets the list of recently used connections. These will not include the password - a separate call to * {addSavedPassword} is needed to fill that before connecting @@ -250,11 +226,7 @@ export class ConnectionStore { private convertConfigValuesToConnectionProfiles(configValues: IConnectionProfile[]): ConnectionProfile[] { return configValues.map(c => { if (c) { - let capabilities = this._connectionConfig.getCapabilities(c.providerName); - let connectionProfile = new ConnectionProfile(capabilities, c); - this._capabilitiesService.onProviderRegisteredEvent((serverCapabilities) => { - connectionProfile.onProviderRegistered(serverCapabilities); - }); + let connectionProfile = new ConnectionProfile(this._capabilitiesService, c); if (connectionProfile.saveProfile) { if (!connectionProfile.groupFullName && connectionProfile.groupId) { connectionProfile.groupFullName = this.getGroupFullName(connectionProfile.groupId); @@ -289,7 +261,7 @@ export class ConnectionStore { public getProfileWithoutPassword(conn: IConnectionProfile): ConnectionProfile { if (conn) { - let savedConn: ConnectionProfile = ConnectionProfile.convertToConnectionProfile(this._connectionConfig.getCapabilities(conn.providerName), conn); + let savedConn: ConnectionProfile = ConnectionProfile.fromIConnectionProfile(this._capabilitiesService, conn); savedConn = savedConn.withoutPassword(); return savedConn; diff --git a/src/sql/parts/connection/common/iconnectionConfig.ts b/src/sql/parts/connection/common/iconnectionConfig.ts index c4aa95162d..b5f07861d0 100644 --- a/src/sql/parts/connection/common/iconnectionConfig.ts +++ b/src/sql/parts/connection/common/iconnectionConfig.ts @@ -23,8 +23,6 @@ export interface IConnectionConfig { getAllGroups(): IConnectionProfileGroup[]; changeGroupIdForConnectionGroup(source: ConnectionProfileGroup, target: ConnectionProfileGroup): Promise; changeGroupIdForConnection(source: ConnectionProfile, targetGroupId: string): Promise; - setCachedMetadata(cachedMetaData: sqlops.DataProtocolServerCapabilities[]): void; - getCapabilities(providerName: string): sqlops.DataProtocolServerCapabilities; editGroup(group: ConnectionProfileGroup): Promise; deleteConnection(profile: ConnectionProfile): Promise; deleteGroup(group: ConnectionProfileGroup): Promise; diff --git a/src/sql/parts/connection/common/providerConnectionInfo.ts b/src/sql/parts/connection/common/providerConnectionInfo.ts index 45e6bd7e16..1b9e7d87a4 100644 --- a/src/sql/parts/connection/common/providerConnectionInfo.ts +++ b/src/sql/parts/connection/common/providerConnectionInfo.ts @@ -5,45 +5,68 @@ 'use strict'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { isString } from 'vs/base/common/types'; + import * as sqlops from 'sqlops'; import * as interfaces from 'sql/parts/connection/common/interfaces'; import { ConnectionOptionSpecialType, ServiceOptionType } from 'sql/workbench/api/common/sqlExtHostTypes'; import * as Constants from 'sql/parts/connection/common/constants'; +import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService'; -export class ProviderConnectionInfo implements sqlops.ConnectionInfo { +export class ProviderConnectionInfo extends Disposable implements sqlops.ConnectionInfo { - options: { [name: string]: any }; + options: { [name: string]: any } = {}; - public providerName: string; + private _providerName: string; protected _serverCapabilities: sqlops.DataProtocolServerCapabilities; private static readonly SqlAuthentication = 'SqlLogin'; public static readonly ProviderPropertyName = 'providerName'; - public constructor(serverCapabilities?: sqlops.DataProtocolServerCapabilities, model?: interfaces.IConnectionProfile) { - this.options = {}; - if (serverCapabilities) { - this._serverCapabilities = serverCapabilities; - this.providerName = serverCapabilities.providerName; - } - if (model) { - if (model.options && this._serverCapabilities) { - this._serverCapabilities.connectionProvider.options.forEach(option => { - let value = model.options[option.name]; - this.options[option.name] = value; - }); + public constructor( + protected capabilitiesService: ICapabilitiesService, + model: string | interfaces.IConnectionProfile + ) { + super(); + // we can't really do a whole lot if we don't have a provider + if (isString(model) || (model && model.providerName)) { + this.providerName = isString(model) ? model : model.providerName; + + if (!isString(model)) { + if (model.options && this._serverCapabilities) { + this._serverCapabilities.connectionProvider.options.forEach(option => { + let value = model.options[option.name]; + this.options[option.name] = value; + }); + } + this.serverName = model.serverName; + this.authenticationType = model.authenticationType; + this.databaseName = model.databaseName; + this.password = model.password; + this.userName = model.userName; } - this.serverName = model.serverName; - this.authenticationType = model.authenticationType; - this.databaseName = model.databaseName; - this.password = model.password; - this.userName = model.userName; + } + } + + public get providerName(): string { + return this._providerName; + } + + public set providerName(name: string) { + this._providerName = name; + if (!this._serverCapabilities) { + this._serverCapabilities = this.capabilitiesService.getCapabilities(this.providerName); + this._register(this.capabilitiesService.onCapabilitiesRegistered(e => { + if (e === this.providerName) { + this._serverCapabilities = this.capabilitiesService.getCapabilities(e); + } + })); } } public clone(): ProviderConnectionInfo { - let instance = new ProviderConnectionInfo(this._serverCapabilities); + let instance = new ProviderConnectionInfo(this.capabilitiesService, this.providerName); instance.options = Object.assign({}, this.options); - instance.providerName = this.providerName; return instance; } @@ -51,10 +74,6 @@ export class ProviderConnectionInfo implements sqlops.ConnectionInfo { return this._serverCapabilities; } - public setServerCapabilities(value: sqlops.DataProtocolServerCapabilities) { - this._serverCapabilities = value; - } - public get serverName(): string { return this.getSpecialTypeOptionValue(ConnectionOptionSpecialType.serverName); } diff --git a/src/sql/parts/connection/connectionDialog/connectionDialogService.ts b/src/sql/parts/connection/connectionDialog/connectionDialogService.ts index 81c197d262..68c6ea3425 100644 --- a/src/sql/parts/connection/connectionDialog/connectionDialogService.ts +++ b/src/sql/parts/connection/connectionDialog/connectionDialogService.ts @@ -65,13 +65,12 @@ export class ConnectionDialogService implements IConnectionDialogService { private _connectionManagementService: IConnectionManagementService; private _container: HTMLElement; private _connectionDialog: ConnectionDialogWidget; - private _connectionControllerMap: { [providerDisplayName: string]: IConnectionComponentController }; + private _connectionControllerMap: { [providerDisplayName: string]: IConnectionComponentController } = {}; private _model: ConnectionProfile; private _params: INewConnectionParams; private _inputModel: IConnectionProfile; - private _capabilitiesMaps: { [providerDisplayName: string]: sqlops.DataProtocolServerCapabilities }; - private _providerNameToDisplayNameMap: { [providerDisplayName: string]: string }; - private _providerTypes: string[]; + private _providerNameToDisplayNameMap: { [providerDisplayName: string]: string } = {}; + private _providerTypes: string[] = []; private _currentProviderType: string = 'Microsoft SQL Server'; private _connecting: boolean = false; private _connectionErrorTitle = localize('connectionError', 'Connection error'); @@ -85,29 +84,11 @@ export class ConnectionDialogService implements IConnectionDialogService { @IWindowsService private _windowsService: IWindowsService, @IClipboardService private _clipboardService: IClipboardService, @ICommandService private _commandService: ICommandService - ) { - this._capabilitiesMaps = {}; - this._providerNameToDisplayNameMap = {}; - this._connectionControllerMap = {}; - this._providerTypes = []; - if (_capabilitiesService) { - _capabilitiesService.onProviderRegisteredEvent((capabilities => { - let defaultProvider = this.getDefaultProviderName(); - if (capabilities.providerName === defaultProvider) { - this.showDialogWithModel(); - } - })); - } - } + ) { } private getDefaultProviderName() { if (this._workspaceConfigurationService) { let defaultProvider = WorkbenchUtils.getSqlConfigValue(this._workspaceConfigurationService, Constants.defaultEngine); - if (defaultProvider - && this._capabilitiesMaps - && defaultProvider in this._capabilitiesMaps) { - return defaultProvider; - } } // as a fallback, default to MSSQL if the value from settings is not available return Constants.mssqlProviderName; @@ -209,7 +190,7 @@ export class ConnectionDialogService implements IConnectionDialogService { // Set the model name, initialize the controller if needed, and return the controller this._model.providerName = providerName; if (!this._connectionControllerMap[providerName]) { - this._connectionControllerMap[providerName] = this._instantiationService.createInstance(ConnectionController, this._container, this._connectionManagementService, this._capabilitiesMaps[providerName], { + this._connectionControllerMap[providerName] = this._instantiationService.createInstance(ConnectionController, this._container, this._connectionManagementService, this._capabilitiesService.getCapabilities(providerName), { onSetConnectButton: (enable: boolean) => this.handleSetConnectButtonEnable(enable) }, providerName); } @@ -222,7 +203,7 @@ export class ConnectionDialogService implements IConnectionDialogService { private handleShowUiComponent(input: OnShowUIResponse) { this._currentProviderType = input.selectedProviderType; - this._model = new ConnectionProfile(this._capabilitiesMaps[this.getCurrentProviderName()], this._model); + this._model = new ConnectionProfile(this._capabilitiesService, this._model); this.uiController.showUiComponent(input.container); } @@ -249,9 +230,11 @@ export class ConnectionDialogService implements IConnectionDialogService { private updateModelServerCapabilities(model: IConnectionProfile) { this._model = this.createModel(model); - this._currentProviderType = this._providerNameToDisplayNameMap[this._model.providerName]; - if (this._connectionDialog) { - this._connectionDialog.updateProvider(this._currentProviderType); + if (this._model.providerName) { + this._currentProviderType = this._providerNameToDisplayNameMap[this._model.providerName]; + if (this._connectionDialog) { + this._connectionDialog.updateProvider(this._currentProviderType); + } } } @@ -259,8 +242,7 @@ export class ConnectionDialogService implements IConnectionDialogService { let defaultProvider = this.getDefaultProviderName(); let providerName = model ? model.providerName : defaultProvider; providerName = providerName ? providerName : defaultProvider; - let serverCapabilities = this._capabilitiesMaps[providerName]; - let newProfile = new ConnectionProfile(serverCapabilities, model); + let newProfile = new ConnectionProfile(this._capabilitiesService, model || providerName); newProfile.saveProfile = true; newProfile.generateNewId(); // If connecting from a query editor set "save connection" to false @@ -270,23 +252,11 @@ export class ConnectionDialogService implements IConnectionDialogService { return newProfile; } - private cacheCapabilities(capabilities: sqlops.DataProtocolServerCapabilities) { - if (capabilities) { - this._providerTypes.push(capabilities.providerDisplayName); - this._capabilitiesMaps[capabilities.providerName] = capabilities; - this._providerNameToDisplayNameMap[capabilities.providerName] = capabilities.providerDisplayName; - } - } - private showDialogWithModel(): TPromise { return new TPromise((resolve, reject) => { - if (this.getDefaultProviderName() in this._capabilitiesMaps) { - this.updateModelServerCapabilities(this._inputModel); - - this.doShowDialog(this._params); - } - let none: void; - resolve(none); + this.updateModelServerCapabilities(this._inputModel); + this.doShowDialog(this._params); + resolve(null); }); } @@ -305,14 +275,14 @@ export class ConnectionDialogService implements IConnectionDialogService { let capabilitiesPromise: Promise = Promise.resolve(); if (this._providerTypes.length === 0) { capabilitiesPromise = this._capabilitiesService.onCapabilitiesReady().then(() => { - let capabilities = this._capabilitiesService.getCapabilities(); - capabilities.forEach(c => { - this.cacheCapabilities(c); + this._capabilitiesService.providers.map(p => { + let capabilities = this._capabilitiesService.getCapabilities(p); + this._providerTypes.push(capabilities.providerDisplayName); + this._providerNameToDisplayNameMap[capabilities.providerName] = capabilities.providerDisplayName; }); }); } - - capabilitiesPromise.then(success => { + capabilitiesPromise.then(s => { this.updateModelServerCapabilities(model); // If connecting from a query editor set "save connection" to false if (params && params.input && params.connectionType === ConnectionType.editor) { @@ -324,7 +294,7 @@ export class ConnectionDialogService implements IConnectionDialogService { this.showErrorDialog(Severity.Error, this._connectionErrorTitle, connectionResult.errorMessage, connectionResult.callStack); } })); - }, err => reject(err)); + }, e => reject(e)); }); } diff --git a/src/sql/parts/connection/connectionDialog/connectionDialogWidget.ts b/src/sql/parts/connection/connectionDialog/connectionDialogWidget.ts index d73956ab4a..2ca2012aca 100644 --- a/src/sql/parts/connection/connectionDialog/connectionDialogWidget.ts +++ b/src/sql/parts/connection/connectionDialog/connectionDialogWidget.ts @@ -274,7 +274,7 @@ export class ConnectionDialogWidget extends Modal { resolve(confirmed); }); - //this._messageService.confirm(confirm).then(confirmation => { + //this._messageService.confirm(confirm).then(confirmation => { // if (!confirmation.confirmed) { // return TPromise.as(false); // } else { @@ -453,7 +453,6 @@ export class ConnectionDialogWidget extends Modal { public updateProvider(displayName: string) { this._providerTypeSelectBox.selectWithOptionName(displayName); - this.onProviderTypeSelected(displayName); } public set databaseDropdownExpanded(val: boolean) { diff --git a/src/sql/parts/dashboard/widgets/explorer/explorerWidget.component.ts b/src/sql/parts/dashboard/widgets/explorer/explorerWidget.component.ts index f4cf153eee..6a3d9e80a2 100644 --- a/src/sql/parts/dashboard/widgets/explorer/explorerWidget.component.ts +++ b/src/sql/parts/dashboard/widgets/explorer/explorerWidget.component.ts @@ -105,7 +105,7 @@ export class ExplorerWidget extends DashboardWidget implements IDashboardWidget, this._register(toDisposableSubscription(this._bootstrap.metadataService.databaseNames.subscribe( data => { let profileData = data.map(d => { - let profile = new ConnectionProfile(currentProfile.serverCapabilities, currentProfile); + let profile = new ConnectionProfile(this._bootstrap.capabilitiesService, currentProfile); profile.databaseName = d; return profile; }); diff --git a/src/sql/parts/disasterRecovery/backup/common/backupServiceImp.ts b/src/sql/parts/disasterRecovery/backup/common/backupServiceImp.ts index a00586b6c0..28741bc634 100644 --- a/src/sql/parts/disasterRecovery/backup/common/backupServiceImp.ts +++ b/src/sql/parts/disasterRecovery/backup/common/backupServiceImp.ts @@ -22,6 +22,7 @@ import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; import { TPromise } from 'vs/base/common/winjs.base'; import * as ConnectionUtils from 'sql/parts/connection/common/utils'; import { ProviderConnectionInfo } from 'sql/parts/connection/common/providerConnectionInfo'; +import { ServiceOption } from 'sqlops'; export class BackupService implements IBackupService { @@ -88,7 +89,6 @@ export class BackupUiService implements IBackupUiService { public _serviceBrand: any; private _backupDialogs: { [providerName: string]: BackupDialog | OptionsDialog } = {}; private _currentProvider: string; - private _optionsMap: { [providerName: string]: sqlops.ServiceOption[] } = {}; private _optionValues: { [optionName: string]: any } = {}; private _connectionUri: string; private static _connectionUniqueId: number = 0; @@ -115,20 +115,22 @@ export class BackupUiService implements IBackupUiService { }); } + private getOptions(provider: string): ServiceOption[] { + let feature = this._capabilitiesService.getCapabilities(this._currentProvider).features.find(f => f.featureName === 'backup'); + if (feature) { + return feature.optionsMetadata; + } else { + return undefined; + } + } + public showBackupDialog(connection: IConnectionProfile): TPromise { let self = this; self._connectionUri = ConnectionUtils.generateUri(connection); self._currentProvider = connection.providerName; let backupDialog = self._backupDialogs[self._currentProvider]; if (!backupDialog) { - let capabilitiesList = this._capabilitiesService.getCapabilities(); - capabilitiesList.forEach(providerCapabilities => { - let backupFeature = providerCapabilities.features.find(feature => feature.featureName === 'backup'); - if (backupFeature && backupFeature.optionsMetadata) { - this._optionsMap[providerCapabilities.providerName] = backupFeature.optionsMetadata; - } - }); - let backupOptions = self._optionsMap[self._currentProvider]; + let backupOptions = this.getOptions(this._currentProvider); if (backupOptions) { backupDialog = self._instantiationService ? self._instantiationService.createInstance( OptionsDialog, 'Backup database - ' + connection.serverName + ':' + connection.databaseName, 'BackupOptions', undefined) : undefined; @@ -141,7 +143,7 @@ export class BackupUiService implements IBackupUiService { self._backupDialogs[self._currentProvider] = backupDialog; } - let backupOptions = this._optionsMap[self._currentProvider]; + let backupOptions = this.getOptions(this._currentProvider); return new TPromise(() => { if (backupOptions) { (backupDialog as OptionsDialog).open(backupOptions, self._optionValues); diff --git a/src/sql/parts/disasterRecovery/restore/common/restoreServiceImpl.ts b/src/sql/parts/disasterRecovery/restore/common/restoreServiceImpl.ts index f27ca7df00..5d40b1ef78 100644 --- a/src/sql/parts/disasterRecovery/restore/common/restoreServiceImpl.ts +++ b/src/sql/parts/disasterRecovery/restore/common/restoreServiceImpl.ts @@ -139,7 +139,7 @@ export class RestoreDialogController implements IRestoreDialogController { private _sessionId: string; private readonly _restoreFeature = 'Restore'; private readonly _restoreTaskName: string = 'Restore Database'; - private readonly _restoreCompleted : string = 'Completed'; + private readonly _restoreCompleted: string = 'Completed'; private _optionValues: { [optionName: string]: any } = {}; constructor( @@ -178,11 +178,11 @@ export class RestoreDialogController implements IRestoreDialogController { private isSuccessfulRestore(response: TaskNode): boolean { return (response.taskName === this._restoreTaskName && - response.message === this._restoreCompleted && - (response.status === TaskStatus.succeeded || - response.status === TaskStatus.succeededWithWarning) && - (response.taskExecutionMode === TaskExecutionMode.execute || - response.taskExecutionMode === TaskExecutionMode.executeAndScript)); + response.message === this._restoreCompleted && + (response.status === TaskStatus.succeeded || + response.status === TaskStatus.succeededWithWarning) && + (response.taskExecutionMode === TaskExecutionMode.execute || + response.taskExecutionMode === TaskExecutionMode.executeAndScript)); } private handleMssqlOnValidateFile(overwriteTargetDatabase: boolean = false): void { @@ -266,7 +266,7 @@ export class RestoreDialogController implements IRestoreDialogController { private getRestoreOption(): sqlops.ServiceOption[] { let options: sqlops.ServiceOption[] = []; let providerId: string = this.getCurrentProviderId(); - let providerCapabilities = this._capabilitiesService.getCapabilities().find(c => c.providerName === providerId); + let providerCapabilities = this._capabilitiesService.getCapabilities(providerId); if (providerCapabilities) { let restoreMetadataProvider = providerCapabilities.features.find(f => f.featureName === this._restoreFeature); diff --git a/src/sql/parts/insights/browser/insightsDialogView.ts b/src/sql/parts/insights/browser/insightsDialogView.ts index b43eee98e9..59c35ccdc5 100644 --- a/src/sql/parts/insights/browser/insightsDialogView.ts +++ b/src/sql/parts/insights/browser/insightsDialogView.ts @@ -38,6 +38,7 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { MenuRegistry, ExecuteCommandAction } from 'vs/platform/actions/common/actions'; +import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService'; const labelDisplay = nls.localize("insights.item", "Item"); const valueDisplay = nls.localize("insights.value", "Value"); @@ -129,7 +130,8 @@ export class InsightsDialogView extends Modal { @IContextMenuService private _contextMenuService: IContextMenuService, @ITelemetryService telemetryService: ITelemetryService, @IContextKeyService contextKeyService: IContextKeyService, - @ICommandService private _commandService: ICommandService + @ICommandService private _commandService: ICommandService, + @ICapabilitiesService private _capabilitiesService: ICapabilitiesService ) { super(nls.localize("InsightsDialogTitle", "Insights"), TelemetryKeys.Insights, partService, telemetryService, contextKeyService); this._model.onDataChange(e => this.build()); @@ -388,7 +390,7 @@ export class InsightsDialogView extends Modal { } let currentProfile = this._connectionProfile as ConnectionProfile; - let profile = new ConnectionProfile(currentProfile.serverCapabilities, currentProfile); + let profile = new ConnectionProfile(this._capabilitiesService, currentProfile); profile.databaseName = database; profile.serverName = server; profile.userName = user; diff --git a/src/sql/parts/registeredServer/common/objectExplorerService.ts b/src/sql/parts/registeredServer/common/objectExplorerService.ts index d0278f45c1..80c1252310 100644 --- a/src/sql/parts/registeredServer/common/objectExplorerService.ts +++ b/src/sql/parts/registeredServer/common/objectExplorerService.ts @@ -19,6 +19,7 @@ import * as TelemetryUtils from 'sql/common/telemetryUtilities'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { warn, error } from 'sql/base/common/log'; import { ServerTreeView } from 'sql/parts/registeredServer/viewlet/serverTreeView'; +import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService'; export const SERVICE_ID = 'ObjectExplorerService'; @@ -102,7 +103,8 @@ export class ObjectExplorerService implements IObjectExplorerService { constructor( @IConnectionManagementService private _connectionManagementService: IConnectionManagementService, - @ITelemetryService private _telemetryService: ITelemetryService + @ITelemetryService private _telemetryService: ITelemetryService, + @ICapabilitiesService private _capabilitiesService: ICapabilitiesService ) { this._onUpdateObjectExplorerNodes = new Emitter(); this._activeObjectExplorerNodes = {}; @@ -124,8 +126,7 @@ export class ObjectExplorerService implements IObjectExplorerService { public updateObjectExplorerNodes(connection: IConnectionProfile): Promise { return this._connectionManagementService.addSavedPassword(connection).then(withPassword => { - let connectionProfile = ConnectionProfile.convertToConnectionProfile( - this._connectionManagementService.getCapabilities(connection.providerName), withPassword); + let connectionProfile = ConnectionProfile.fromIConnectionProfile(this._capabilitiesService, withPassword); return this.updateNewObjectExplorerNode(connectionProfile); }); } diff --git a/src/sql/services/capabilities/capabilitiesService.ts b/src/sql/services/capabilities/capabilitiesService.ts index c1ca995c3e..19cba0718d 100644 --- a/src/sql/services/capabilities/capabilitiesService.ts +++ b/src/sql/services/capabilities/capabilitiesService.ts @@ -7,19 +7,32 @@ import { ConnectionManagementInfo } from 'sql/parts/connection/common/connectionManagementInfo'; import * as Constants from 'sql/common/constants'; -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { Deferred } from 'sql/base/common/promise'; + import * as sqlops from 'sqlops'; + import Event, { Emitter } from 'vs/base/common/event'; import { IAction } from 'vs/base/common/actions'; -import { Deferred } from 'sql/base/common/promise'; import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IExtensionManagementService, ILocalExtension, IExtensionEnablementService, LocalExtensionType } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { Memento } from 'vs/workbench/common/memento'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; +import { IStorageService } from 'vs/platform/storage/common/storage'; export const SERVICE_ID = 'capabilitiesService'; export const HOST_NAME = 'sqlops'; export const HOST_VERSION = '1.0'; +interface IProtocolMomento { + [id: string]: sqlops.DataProtocolServerCapabilities; +} + +export const clientCapabilities = { + hostName: HOST_NAME, + hostVersion: HOST_VERSION +}; + export const ICapabilitiesService = createDecorator(SERVICE_ID); /** @@ -31,7 +44,7 @@ export interface ICapabilitiesService { /** * Retrieve a list of registered capabilities providers */ - getCapabilities(): sqlops.DataProtocolServerCapabilities[]; + getCapabilities(provider: string): sqlops.DataProtocolServerCapabilities; /** * Register a capabilities provider @@ -43,43 +56,38 @@ export interface ICapabilitiesService { */ isFeatureAvailable(action: IAction, connectionManagementInfo: ConnectionManagementInfo): boolean; - /** - * Event raised when a provider is registered - */ - onProviderRegisteredEvent: Event; - /** * Promise fulfilled when Capabilities are ready */ onCapabilitiesReady(): Promise; + /** + * When a new capabilities is registered, it emits the provider name, be to use to get the new capabilities + */ + readonly onCapabilitiesRegistered: Event; + + /** + * Get an array of all known providers + */ + readonly providers: string[]; + } /** * Capabilities service implementation class. This class provides the ability * to discover the DMP capabilties that a DMP provider offers. */ -export class CapabilitiesService implements ICapabilitiesService { +export class CapabilitiesService extends Disposable implements ICapabilitiesService { public _serviceBrand: any; + private _momento = new Memento('capabilitiesCache'); + private static DATA_PROVIDER_CATEGORY: string = 'Data Provider'; - private _providers: sqlops.CapabilitiesProvider[] = []; - - private _capabilities: sqlops.DataProtocolServerCapabilities[] = []; - - private _onProviderRegistered: Emitter; - - private _clientCapabilties: sqlops.DataProtocolClientCapabilities = { - - hostName: HOST_NAME, - hostVersion: HOST_VERSION - }; - private disposables: IDisposable[] = []; - private _onCapabilitiesReady: Deferred; + private _onCapabilitiesReady = new Deferred(); // Setting this to 1 by default as we have MS SQL provider by default and then we increament // this number based on extensions installed. @@ -89,12 +97,15 @@ export class CapabilitiesService implements ICapabilitiesService { private _registeredCapabilities: number = 0; - constructor( @IExtensionManagementService private extensionManagementService: IExtensionManagementService, - @IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService) { + private _onCapabilitiesRegistered = this._register(new Emitter()); + public readonly onCapabilitiesRegistered = this._onCapabilitiesRegistered.event; - this._onProviderRegistered = new Emitter(); - this.disposables.push(this._onProviderRegistered); - this._onCapabilitiesReady = new Deferred(); + constructor( + @IExtensionManagementService private extensionManagementService: IExtensionManagementService, + @IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService, + @IStorageService private _storageService: IStorageService + ) { + super(); // Get extensions and filter where the category has 'Data Provider' in it this.extensionManagementService.getInstalled(LocalExtensionType.User).then((extensions: ILocalExtension[]) => { @@ -140,8 +151,16 @@ export class CapabilitiesService implements ICapabilitiesService { /** * Retrieve a list of registered server capabilities */ - public getCapabilities(): sqlops.DataProtocolServerCapabilities[] { - return this._capabilities; + public getCapabilities(provider: string): sqlops.DataProtocolServerCapabilities { + return this.capabilities[provider]; + } + + public get providers(): string[] { + return Object.keys(this.capabilities); + } + + private get capabilities(): IProtocolMomento { + return this._momento.getMemento(this._storageService) as IProtocolMomento; } /** @@ -149,12 +168,11 @@ export class CapabilitiesService implements ICapabilitiesService { * @param provider */ public registerProvider(provider: sqlops.CapabilitiesProvider): void { - this._providers.push(provider); - // request the capabilities from server - provider.getServerCapabilities(this._clientCapabilties).then(serverCapabilities => { - this._capabilities.push(serverCapabilities); - this._onProviderRegistered.fire(serverCapabilities); + provider.getServerCapabilities(clientCapabilities).then(serverCapabilities => { + this.capabilities[serverCapabilities.providerName] = serverCapabilities; + this._momento.saveMemento(); + this._onCapabilitiesRegistered.fire(serverCapabilities.providerName); this._registeredCapabilities++; this.resolveCapabilitiesIfReady(); }); @@ -198,13 +216,4 @@ export class CapabilitiesService implements ICapabilitiesService { } } - - // Event Emitters - public get onProviderRegisteredEvent(): Event { - return this._onProviderRegistered.event; - } - - public dispose(): void { - this.disposables = dispose(this.disposables); - } } diff --git a/src/sql/services/serialization/serializationService.ts b/src/sql/services/serialization/serializationService.ts index b041814226..742699eb1a 100644 --- a/src/sql/services/serialization/serializationService.ts +++ b/src/sql/services/serialization/serializationService.ts @@ -74,7 +74,7 @@ export class SerializationService implements ISerializationService { public getSerializationFeatureMetadataProvider(ownerUri: string): sqlops.FeatureMetadataProvider { let providerId: string = this._connectionService.getProviderIdFromUri(ownerUri); - let providerCapabilities = this._capabilitiesService.getCapabilities().find(c => c.providerName === providerId); + let providerCapabilities = this._capabilitiesService.getCapabilities(providerId); if (providerCapabilities) { return providerCapabilities.features.find(f => f.featureName === SERVICE_ID); diff --git a/src/sqltest/parts/connection/connectionProfile.test.ts b/src/sqltest/parts/connection/connectionProfile.test.ts index 56f172e14e..046f96942f 100644 --- a/src/sqltest/parts/connection/connectionProfile.test.ts +++ b/src/sqltest/parts/connection/connectionProfile.test.ts @@ -11,9 +11,11 @@ import { IConnectionProfile, IConnectionProfileStore } from 'sql/parts/connectio import * as sqlops from 'sqlops'; import * as assert from 'assert'; import { ConnectionOptionSpecialType } from 'sql/workbench/api/common/sqlExtHostTypes'; +import { CapabilitiesTestService } from 'sqltest/stubs/capabilitiesTestService'; suite('SQL ConnectionProfileInfo tests', () => { let msSQLCapabilities: sqlops.DataProtocolServerCapabilities; + let capabilitiesService: CapabilitiesTestService; let connectionProfile: IConnectionProfile = { serverName: 'new server', @@ -121,10 +123,12 @@ suite('SQL ConnectionProfileInfo tests', () => { features: undefined }; capabilities.push(msSQLCapabilities); + capabilitiesService = new CapabilitiesTestService(); + capabilitiesService.capabilities['MSSQL'] = msSQLCapabilities; }); test('set properties should set the values correctly', () => { - let conn = new ConnectionProfile(msSQLCapabilities, undefined); + let conn = new ConnectionProfile(capabilitiesService, undefined); assert.equal(conn.serverName, undefined); conn.serverName = connectionProfile.serverName; conn.databaseName = connectionProfile.databaseName; @@ -145,7 +149,7 @@ suite('SQL ConnectionProfileInfo tests', () => { }); test('constructor should initialize the options given a valid model', () => { - let conn = new ConnectionProfile(msSQLCapabilities, connectionProfile); + let conn = new ConnectionProfile(capabilitiesService, connectionProfile); assert.equal(conn.serverName, connectionProfile.serverName); assert.equal(conn.databaseName, connectionProfile.databaseName); @@ -158,7 +162,7 @@ suite('SQL ConnectionProfileInfo tests', () => { }); test('getOptionsKey should create a valid unique id', () => { - let conn = new ConnectionProfile(msSQLCapabilities, connectionProfile); + let conn = new ConnectionProfile(capabilitiesService, connectionProfile); let expectedId = 'providerName:MSSQL|authenticationType:|databaseName:database|serverName:new server|userName:user|databaseDisplayName:database|group:group id'; let id = conn.getOptionsKey(); assert.equal(id, expectedId); @@ -166,7 +170,7 @@ suite('SQL ConnectionProfileInfo tests', () => { test('createFromStoredProfile should create connection profile from stored profile', () => { let savedProfile = storedProfile; - let connectionProfile = ConnectionProfile.createFromStoredProfile(savedProfile, msSQLCapabilities); + let connectionProfile = ConnectionProfile.createFromStoredProfile(savedProfile, capabilitiesService); assert.equal(savedProfile.groupId, connectionProfile.groupId); assert.deepEqual(savedProfile.providerName, connectionProfile.providerName); assert.deepEqual(savedProfile.savePassword, connectionProfile.savePassword); @@ -175,7 +179,7 @@ suite('SQL ConnectionProfileInfo tests', () => { test('createFromStoredProfile should set the id to new guid if not set in stored profile', () => { let savedProfile = Object.assign({}, storedProfile, { id: undefined }); - let connectionProfile = ConnectionProfile.createFromStoredProfile(savedProfile, msSQLCapabilities); + let connectionProfile = ConnectionProfile.createFromStoredProfile(savedProfile, capabilitiesService); assert.equal(savedProfile.groupId, connectionProfile.groupId); assert.deepEqual(savedProfile.providerName, connectionProfile.providerName); assert.equal(savedProfile.savePassword, connectionProfile.savePassword); @@ -184,20 +188,20 @@ suite('SQL ConnectionProfileInfo tests', () => { }); test('withoutPassword should create a new instance without password', () => { - let conn = new ConnectionProfile(msSQLCapabilities, connectionProfile); + let conn = new ConnectionProfile(capabilitiesService, connectionProfile); assert.notEqual(conn.password, ''); let withoutPassword = conn.withoutPassword(); assert.equal(withoutPassword.password, ''); }); test('unique id should not include password', () => { - let conn = new ConnectionProfile(msSQLCapabilities, connectionProfile); + let conn = new ConnectionProfile(capabilitiesService, connectionProfile); let withoutPassword = conn.withoutPassword(); assert.equal(withoutPassword.getOptionsKey(), conn.getOptionsKey()); }); test('cloneWithDatabase should create new profile with new id', () => { - let conn = new ConnectionProfile(msSQLCapabilities, connectionProfile); + let conn = new ConnectionProfile(capabilitiesService, connectionProfile); let newProfile = conn.cloneWithDatabase('new db'); assert.notEqual(newProfile.id, conn.id); assert.equal(newProfile.databaseName, 'new db'); diff --git a/src/sqltest/parts/connection/connectionStatusManager.test.ts b/src/sqltest/parts/connection/connectionStatusManager.test.ts index 2885ad4aa8..60b1ff4b44 100644 --- a/src/sqltest/parts/connection/connectionStatusManager.test.ts +++ b/src/sqltest/parts/connection/connectionStatusManager.test.ts @@ -72,8 +72,7 @@ let connection3Id: string; suite('SQL ConnectionStatusManager tests', () => { setup(() => { capabilitiesService = new CapabilitiesTestService(); - connectionProfileObject = new ConnectionProfile(capabilitiesService.getCapabilities().find(x => x.providerName === 'MSSQL') - , connectionProfile); + connectionProfileObject = new ConnectionProfile(capabilitiesService, connectionProfile); connections = new ConnectionStatusManager(capabilitiesService); connection1Id = Utils.generateUri(connectionProfile); connection2Id = 'connection2Id'; @@ -94,7 +93,7 @@ suite('SQL ConnectionStatusManager tests', () => { let id: string = connection1Id; let expected = connectionProfileObject; let actual = connections.findConnection(id); - assert.deepEqual(actual.connectionProfile, expected); + assert.equal(connectionProfileObject.matches(actual.connectionProfile), true); }); test('getConnectionProfile should return undefined given invalid id', () => { @@ -108,7 +107,7 @@ suite('SQL ConnectionStatusManager tests', () => { let id: string = connection1Id; let expected = connectionProfileObject; let actual = connections.getConnectionProfile(id); - assert.deepEqual(actual, expected); + assert.equal(connectionProfileObject.matches(actual), true); }); test('hasConnection should return false given invalid id', () => { diff --git a/src/sqltest/parts/connection/connectionStore.test.ts b/src/sqltest/parts/connection/connectionStore.test.ts index e63bdda722..aca98d51b2 100644 --- a/src/sqltest/parts/connection/connectionStore.test.ts +++ b/src/sqltest/parts/connection/connectionStore.test.ts @@ -20,6 +20,7 @@ import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile import { Emitter } from 'vs/base/common/event'; import { ConnectionProfileGroup, IConnectionProfileGroup } from 'sql/parts/connection/common/connectionProfileGroup'; import { ConnectionOptionSpecialType } from 'sql/workbench/api/common/sqlExtHostTypes'; +import { CapabilitiesTestService } from '../../stubs/capabilitiesTestService'; suite('SQL ConnectionStore tests', () => { let defaultNamedProfile: IConnectionProfile; @@ -29,13 +30,11 @@ suite('SQL ConnectionStore tests', () => { let connectionConfig: TypeMoq.Mock; let workspaceConfigurationServiceMock: TypeMoq.Mock; let storageServiceMock: TypeMoq.Mock; - let capabilitiesService: TypeMoq.Mock; + let capabilitiesService: CapabilitiesTestService; let mementoArray: any = []; let maxRecent = 5; let msSQLCapabilities: sqlops.DataProtocolServerCapabilities; let defaultNamedConnectionProfile: ConnectionProfile; - let onProviderRegistered = new Emitter(); - setup(() => { defaultNamedProfile = Object.assign({}, { @@ -96,8 +95,8 @@ suite('SQL ConnectionStore tests', () => { } }; - capabilitiesService = TypeMoq.Mock.ofType(CapabilitiesService, TypeMoq.MockBehavior.Loose, extensionManagementServiceMock, {}); - let capabilities: sqlops.DataProtocolServerCapabilities[] = []; + capabilitiesService = new CapabilitiesTestService(); + let capabilities: { [id: string]: sqlops.DataProtocolServerCapabilities } = {}; let connectionProvider: sqlops.ConnectionProviderOptions = { options: [ { @@ -170,10 +169,8 @@ suite('SQL ConnectionStore tests', () => { adminServicesProvider: undefined, features: undefined }; - capabilities.push(msSQLCapabilities); - capabilitiesService.setup(x => x.getCapabilities()).returns(() => capabilities); - capabilitiesService.setup(x => x.onProviderRegisteredEvent).returns(() => onProviderRegistered.event); - connectionConfig.setup(x => x.getCapabilities('MSSQL')).returns(() => msSQLCapabilities); + capabilities['MSSQL'] = msSQLCapabilities; + capabilitiesService.capabilities['MSSQL'] = msSQLCapabilities; let groups: IConnectionProfileGroup[] = [ { id: 'root', @@ -192,7 +189,7 @@ suite('SQL ConnectionStore tests', () => { ]; connectionConfig.setup(x => x.getAllGroups()).returns(() => groups); - defaultNamedConnectionProfile = new ConnectionProfile(msSQLCapabilities, defaultNamedProfile); + defaultNamedConnectionProfile = new ConnectionProfile(capabilitiesService, defaultNamedProfile); }); test('addActiveConnection should limit recent connection saves to the MaxRecentConnections amount', (done) => { @@ -206,11 +203,11 @@ suite('SQL ConnectionStore tests', () => { // When saving 4 connections // Expect all of them to be saved even if size is limited to 3 let connectionStore = new ConnectionStore(storageServiceMock.object, context.object, undefined, workspaceConfigurationServiceMock.object, - credentialStore.object, capabilitiesService.object, connectionConfig.object); + credentialStore.object, capabilitiesService, connectionConfig.object); let promise = Promise.resolve(); for (let i = 0; i < numCreds; i++) { let cred = Object.assign({}, defaultNamedProfile, { serverName: defaultNamedProfile.serverName + i }); - let connectionProfile = new ConnectionProfile(msSQLCapabilities, cred); + let connectionProfile = new ConnectionProfile(capabilitiesService, cred); promise = promise.then(() => { return connectionStore.addActiveConnection(connectionProfile); }).then(() => { @@ -243,12 +240,12 @@ suite('SQL ConnectionStore tests', () => { // 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(storageServiceMock.object, context.object, undefined, workspaceConfigurationServiceMock.object, - credentialStore.object, capabilitiesService.object, connectionConfig.object); + credentialStore.object, capabilitiesService, connectionConfig.object); connectionStore.clearActiveConnections(); connectionStore.clearRecentlyUsed(); let promise = Promise.resolve(); let cred = Object.assign({}, defaultNamedProfile, { serverName: defaultNamedProfile.serverName + 1 }); - let connectionProfile = new ConnectionProfile(msSQLCapabilities, cred); + let connectionProfile = new ConnectionProfile(capabilitiesService, cred); promise = promise.then(() => { return connectionStore.addActiveConnection(defaultNamedConnectionProfile); }).then(() => { @@ -278,7 +275,7 @@ suite('SQL ConnectionStore tests', () => { // Given we save 1 connection with password and multiple other connections without let connectionStore = new ConnectionStore(storageServiceMock.object, context.object, undefined, workspaceConfigurationServiceMock.object, - credentialStore.object, capabilitiesService.object, connectionConfig.object); + credentialStore.object, capabilitiesService, connectionConfig.object); connectionStore.clearActiveConnections(); connectionStore.clearRecentlyUsed(); let integratedCred = Object.assign({}, defaultNamedProfile, { @@ -291,7 +288,7 @@ suite('SQL ConnectionStore tests', () => { serverName: defaultNamedProfile.serverName + 'NoPwd', password: '' }); - let connectionProfile = new ConnectionProfile(msSQLCapabilities, defaultNamedProfile); + let connectionProfile = new ConnectionProfile(capabilitiesService, defaultNamedProfile); let expectedCredCount = 0; let promise = Promise.resolve(); @@ -309,7 +306,7 @@ suite('SQL ConnectionStore tests', () => { }).then(() => { // When add integrated auth connection expectedCredCount++; - let integratedCredConnectionProfile = new ConnectionProfile(msSQLCapabilities, integratedCred); + let integratedCredConnectionProfile = new ConnectionProfile(capabilitiesService, integratedCred); return connectionStore.addActiveConnection(integratedCredConnectionProfile); }).then(() => { let current = connectionStore.getRecentlyUsedConnections(); @@ -319,7 +316,7 @@ suite('SQL ConnectionStore tests', () => { }).then(() => { // When add connection without password expectedCredCount++; - let noPwdCredConnectionProfile = new ConnectionProfile(msSQLCapabilities, noPwdCred); + let noPwdCredConnectionProfile = new ConnectionProfile(capabilitiesService, noPwdCred); return connectionStore.addActiveConnection(noPwdCredConnectionProfile); }).then(() => { let current = connectionStore.getRecentlyUsedConnections(); @@ -333,7 +330,7 @@ suite('SQL ConnectionStore tests', () => { connectionConfig.setup(x => x.getConnections(TypeMoq.It.isAny())).returns(() => []); let connectionStore = new ConnectionStore(storageServiceMock.object, context.object, undefined, workspaceConfigurationServiceMock.object, - credentialStore.object, capabilitiesService.object, connectionConfig.object); + credentialStore.object, capabilitiesService, connectionConfig.object); // When we clear the connections list and get the list of available connection items connectionStore.clearActiveConnections(); @@ -351,7 +348,7 @@ suite('SQL ConnectionStore tests', () => { test('isPasswordRequired should return true for MSSQL SqlLogin', () => { let connectionStore = new ConnectionStore(storageServiceMock.object, context.object, undefined, workspaceConfigurationServiceMock.object, - credentialStore.object, capabilitiesService.object, connectionConfig.object); + credentialStore.object, capabilitiesService, connectionConfig.object); let expected: boolean = true; let actual = connectionStore.isPasswordRequired(defaultNamedProfile); @@ -361,8 +358,8 @@ suite('SQL ConnectionStore tests', () => { test('isPasswordRequired should return true for MSSQL SqlLogin for connection profile object', () => { let connectionStore = new ConnectionStore(storageServiceMock.object, context.object, undefined, workspaceConfigurationServiceMock.object, - credentialStore.object, capabilitiesService.object, connectionConfig.object); - let connectionProfile = new ConnectionProfile(msSQLCapabilities, defaultNamedProfile); + credentialStore.object, capabilitiesService, connectionConfig.object); + let connectionProfile = new ConnectionProfile(capabilitiesService, defaultNamedProfile); let expected: boolean = true; let actual = connectionStore.isPasswordRequired(connectionProfile); @@ -387,10 +384,11 @@ suite('SQL ConnectionStore tests', () => { adminServicesProvider: undefined, features: undefined }; - connectionConfig.setup(x => x.getCapabilities(providerName)).returns(() => providerCapabilities); + + capabilitiesService.capabilities[providerName] = providerCapabilities; let connectionStore = new ConnectionStore(storageServiceMock.object, context.object, undefined, workspaceConfigurationServiceMock.object, - credentialStore.object, capabilitiesService.object, connectionConfig.object); + credentialStore.object, capabilitiesService, connectionConfig.object); let connectionProfile: IConnectionProfile = Object.assign({}, defaultNamedProfile, { providerName: providerName }); let expected: boolean = false; let actual = connectionStore.isPasswordRequired(connectionProfile); @@ -407,7 +405,7 @@ suite('SQL ConnectionStore tests', () => { credentialStore.setup(x => x.saveCredential(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(true)); let connectionStore = new ConnectionStore(storageServiceMock.object, context.object, undefined, workspaceConfigurationServiceMock.object, - credentialStore.object, capabilitiesService.object, connectionConfig.object); + credentialStore.object, capabilitiesService, connectionConfig.object); connectionStore.saveProfile(connectionProfile).then(profile => { // add connection should be called with a profile without password @@ -424,7 +422,7 @@ suite('SQL ConnectionStore tests', () => { test('addConnectionToMemento should not add duplicate items', () => { let connectionStore = new ConnectionStore(storageServiceMock.object, context.object, undefined, workspaceConfigurationServiceMock.object, - credentialStore.object, capabilitiesService.object, connectionConfig.object); + credentialStore.object, capabilitiesService, connectionConfig.object); let mementoKey = 'RECENT_CONNECTIONS2'; connectionStore.clearFromMemento(mementoKey); let connectionProfile: IConnectionProfile = Object.assign({}, defaultNamedProfile); @@ -466,7 +464,7 @@ suite('SQL ConnectionStore tests', () => { test('getGroupFromId returns undefined when there is no group with the given ID', () => { let connectionStore = new ConnectionStore(storageServiceMock.object, context.object, undefined, workspaceConfigurationServiceMock.object, - credentialStore.object, capabilitiesService.object, connectionConfig.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'); }); @@ -482,7 +480,7 @@ suite('SQL ConnectionStore tests', () => { let newConnectionConfig = TypeMoq.Mock.ofType(ConnectionConfig); newConnectionConfig.setup(x => x.getAllGroups()).returns(() => groups); let connectionStore = new ConnectionStore(storageServiceMock.object, context.object, undefined, workspaceConfigurationServiceMock.object, - credentialStore.object, capabilitiesService.object, newConnectionConfig.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); @@ -495,14 +493,14 @@ suite('SQL ConnectionStore tests', () => { test('getProfileWithoutPassword can return the profile without credentials in the password property or options dictionary', () => { let connectionStore = new ConnectionStore(storageServiceMock.object, context.object, undefined, workspaceConfigurationServiceMock.object, - credentialStore.object, capabilitiesService.object, connectionConfig.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.convertToConnectionProfile(msSQLCapabilities, expectedProfile).toIConnectionProfile(); + expectedProfile = ConnectionProfile.fromIConnectionProfile(capabilitiesService, expectedProfile).toIConnectionProfile(); let profileWithoutCredentials = connectionStore.getProfileWithoutPassword(profile); assert.deepEqual(profileWithoutCredentials.toIConnectionProfile(), expectedProfile); }); diff --git a/src/sqltest/parts/connection/connectionTreeActions.test.ts b/src/sqltest/parts/connection/connectionTreeActions.test.ts index 4114fb3d85..2c096bc4e1 100644 --- a/src/sqltest/parts/connection/connectionTreeActions.test.ts +++ b/src/sqltest/parts/connection/connectionTreeActions.test.ts @@ -33,6 +33,7 @@ import Severity from 'vs/base/common/severity'; import { ObjectExplorerActionsContext, ManageConnectionAction } from 'sql/parts/registeredServer/viewlet/objectExplorerActions'; import { IConnectionResult, IConnectionParams } from 'sql/parts/connection/common/connectionManagement'; import { TreeSelectionHandler } from 'sql/parts/registeredServer/viewlet/treeSelectionHandler'; +import { CapabilitiesTestService } from 'sqltest/stubs/capabilitiesTestService'; suite('SQL Connection Tree Action tests', () => { let errorMessageService: TypeMoq.Mock; @@ -42,6 +43,7 @@ suite('SQL Connection Tree Action tests', () => { errorCode: undefined, callStack: undefined }; + let capabilitiesService = new CapabilitiesTestService(); setup(() => { errorMessageService = TypeMoq.Mock.ofType(ErrorMessageServiceStub, TypeMoq.MockBehavior.Loose); let nothing: void; @@ -91,7 +93,7 @@ suite('SQL Connection Tree Action tests', () => { let manageConnectionAction: ManageConnectionAction = new ManageConnectionAction(ManageConnectionAction.ID, ManageConnectionAction.LABEL, connectionManagementService.object, instantiationService.object, objectExplorerService.object); - let connection: ConnectionProfile = new ConnectionProfile(undefined, { + let connection: ConnectionProfile = new ConnectionProfile(capabilitiesService, { savePassword: false, groupFullName: 'testGroup', serverName: 'testServerName', @@ -126,7 +128,7 @@ suite('SQL Connection Tree Action tests', () => { let manageConnectionAction: ManageConnectionAction = new ManageConnectionAction(ManageConnectionAction.ID, ManageConnectionAction.LABEL, connectionManagementService.object, instantiationService.object, objectExplorerService.object); - let connection: ConnectionProfile = new ConnectionProfile(undefined, { + let connection: ConnectionProfile = new ConnectionProfile(capabilitiesService, { savePassword: false, groupFullName: 'testGroup', serverName: 'testServerName', @@ -158,7 +160,7 @@ suite('SQL Connection Tree Action tests', () => { let objectExplorerService = createObjectExplorerService(connectionManagementService.object); let changeConnectionAction: DisconnectConnectionAction = new DisconnectConnectionAction(DisconnectConnectionAction.ID, DisconnectConnectionAction.LABEL, connectionManagementService.object, objectExplorerService.object, errorMessageService.object); - let connection: ConnectionProfile = new ConnectionProfile(undefined, { + let connection: ConnectionProfile = new ConnectionProfile(capabilitiesService, { savePassword: false, groupFullName: 'testGroup', serverName: 'testServerName', @@ -265,7 +267,7 @@ suite('SQL Connection Tree Action tests', () => { test('DeleteConnectionAction - test delete connection', (done) => { let connectionManagementService = createConnectionManagementService(true); - let connection: ConnectionProfile = new ConnectionProfile(undefined, { + let connection: ConnectionProfile = new ConnectionProfile(capabilitiesService, { savePassword: false, groupFullName: 'testGroup', serverName: 'testServerName', @@ -311,7 +313,7 @@ suite('SQL Connection Tree Action tests', () => { let isConnectedReturnValue: boolean = false; let connectionManagementService = createConnectionManagementService(isConnectedReturnValue); - let connection: ConnectionProfile = new ConnectionProfile(undefined, { + let connection: ConnectionProfile = new ConnectionProfile(capabilitiesService, { savePassword: false, groupFullName: 'testGroup', serverName: 'testServerName', @@ -348,7 +350,9 @@ suite('SQL Connection Tree Action tests', () => { features: undefined }; - var connection = new ConnectionProfile(sqlProvider, { + capabilitiesService.capabilities['MSSQL'] = sqlProvider; + + var connection = new ConnectionProfile(capabilitiesService, { savePassword: false, groupFullName: 'testGroup', serverName: 'testServerName', @@ -437,7 +441,9 @@ suite('SQL Connection Tree Action tests', () => { features: undefined }; - var connection = new ConnectionProfile(sqlProvider, { + capabilitiesService.capabilities['MSSQL'] = sqlProvider; + + var connection = new ConnectionProfile(capabilitiesService, { savePassword: false, groupFullName: 'testGroup', serverName: 'testServerName', diff --git a/src/sqltest/parts/connection/objectExplorerService.test.ts b/src/sqltest/parts/connection/objectExplorerService.test.ts index 33c36a6ab7..5d924fa9ef 100644 --- a/src/sqltest/parts/connection/objectExplorerService.test.ts +++ b/src/sqltest/parts/connection/objectExplorerService.test.ts @@ -18,7 +18,9 @@ import * as TypeMoq from 'typemoq'; import * as assert from 'assert'; import { ServerTreeView } from 'sql/parts/registeredServer/viewlet/serverTreeView'; import { ConnectionOptionSpecialType } from 'sql/workbench/api/common/sqlExtHostTypes'; -import Event from 'vs/base/common/event'; +import Event, { Emitter } from 'vs/base/common/event'; +import { CapabilitiesService } from 'sql/services/capabilities/capabilitiesService'; +import { CapabilitiesTestService } from 'sqltest/stubs/capabilitiesTestService'; suite('SQL Object Explorer Service tests', () => { var sqlOEProvider: TypeMoq.Mock; @@ -122,6 +124,7 @@ suite('SQL Object Explorer Service tests', () => { sqlOEProvider = TypeMoq.Mock.ofType(ObjectExplorerProviderTestService, TypeMoq.MockBehavior.Loose); sqlOEProvider.callBase = true; + let onCapabilitiesRegistered = new Emitter(); let sqlProvider = { protocolVersion: '1', @@ -206,7 +209,10 @@ suite('SQL Object Explorer Service tests', () => { features: undefined }; - connection = new ConnectionProfile(sqlProvider, { + let capabilitiesService = new CapabilitiesTestService(); + capabilitiesService.capabilities['MSSQL'] = sqlProvider; + + connection = new ConnectionProfile(capabilitiesService, { savePassword: false, groupFullName: 'testGroup', serverName: 'testServerName', @@ -224,7 +230,7 @@ suite('SQL Object Explorer Service tests', () => { }); conProfGroup = new ConnectionProfileGroup('testGroup', undefined, 'testGroup', undefined, undefined); - connectionToFail = new ConnectionProfile(sqlProvider, { + connectionToFail = new ConnectionProfile(capabilitiesService, { savePassword: false, groupFullName: 'testGroup', serverName: 'testServerName2', @@ -251,7 +257,13 @@ suite('SQL Object Explorer Service tests', () => { connectionManagementService.setup(x => x.getCapabilities('MSSQL')).returns(() => undefined); - objectExplorerService = new ObjectExplorerService(connectionManagementService.object, undefined); + let extensionManagementServiceMock = { + getInstalled: () => { + return Promise.resolve([]); + } + }; + + objectExplorerService = new ObjectExplorerService(connectionManagementService.object, undefined, capabilitiesService); objectExplorerService.registerProvider('MSSQL', sqlOEProvider.object); sqlOEProvider.setup(x => x.createNewSession(TypeMoq.It.is(x => x.options['serverName'] === connection.serverName))).returns(() => new Promise((resolve) => { resolve(response); diff --git a/src/sqltest/parts/connection/providerConnectionInfo.test.ts b/src/sqltest/parts/connection/providerConnectionInfo.test.ts index dbf4a8b53a..6f1568f2c8 100644 --- a/src/sqltest/parts/connection/providerConnectionInfo.test.ts +++ b/src/sqltest/parts/connection/providerConnectionInfo.test.ts @@ -11,9 +11,12 @@ import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; import * as sqlops from 'sqlops'; import * as assert from 'assert'; import { ConnectionOptionSpecialType } from 'sql/workbench/api/common/sqlExtHostTypes'; +import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService'; +import { CapabilitiesTestService } from 'sqltest/stubs/capabilitiesTestService'; suite('SQL ProviderConnectionInfo tests', () => { let msSQLCapabilities: sqlops.DataProtocolServerCapabilities; + let capabilitiesService: CapabilitiesTestService; let connectionProfile: IConnectionProfile = { serverName: 'new server', @@ -119,6 +122,8 @@ suite('SQL ProviderConnectionInfo tests', () => { features: undefined }; capabilities.push(msSQLCapabilities); + capabilitiesService = new CapabilitiesTestService(); + capabilitiesService.capabilities['MSSQL'] = msSQLCapabilities; }); test('constructor should accept undefined parameters', () => { @@ -127,7 +132,7 @@ suite('SQL ProviderConnectionInfo tests', () => { }); test('set properties should set the values correctly', () => { - let conn = new ProviderConnectionInfo(msSQLCapabilities, undefined); + let conn = new ProviderConnectionInfo(capabilitiesService, 'MSSQL'); assert.equal(conn.serverName, undefined); conn.serverName = connectionProfile.serverName; conn.databaseName = connectionProfile.databaseName; @@ -142,7 +147,7 @@ suite('SQL ProviderConnectionInfo tests', () => { }); test('set properties should store the values in the options', () => { - let conn = new ProviderConnectionInfo(msSQLCapabilities, undefined); + let conn = new ProviderConnectionInfo(capabilitiesService, 'MSSQL'); assert.equal(conn.serverName, undefined); conn.serverName = connectionProfile.serverName; conn.databaseName = connectionProfile.databaseName; @@ -157,7 +162,7 @@ suite('SQL ProviderConnectionInfo tests', () => { }); test('constructor should initialize the options given a valid model', () => { - let conn = new ProviderConnectionInfo(msSQLCapabilities, connectionProfile); + let conn = new ProviderConnectionInfo(capabilitiesService, connectionProfile); assert.equal(conn.serverName, connectionProfile.serverName); assert.equal(conn.databaseName, connectionProfile.databaseName); @@ -167,7 +172,7 @@ suite('SQL ProviderConnectionInfo tests', () => { }); test('clone should create a new instance that equals the old one', () => { - let conn = new ProviderConnectionInfo(msSQLCapabilities, connectionProfile); + let conn = new ProviderConnectionInfo(capabilitiesService, connectionProfile); let conn2 = conn.clone(); assert.equal(conn.serverName, conn2.serverName); @@ -178,7 +183,7 @@ suite('SQL ProviderConnectionInfo tests', () => { }); test('Changing the cloned object should not change the original one', () => { - let conn = new ProviderConnectionInfo(msSQLCapabilities, connectionProfile); + let conn = new ProviderConnectionInfo(capabilitiesService, connectionProfile); let conn2 = conn.clone(); conn2.serverName = conn.serverName + '1'; @@ -189,7 +194,7 @@ suite('SQL ProviderConnectionInfo tests', () => { let options = {}; options['encrypt'] = 'test value'; let conn2 = Object.assign({}, connectionProfile, { options: options }); - let conn = new ProviderConnectionInfo(msSQLCapabilities, conn2); + let conn = new ProviderConnectionInfo(capabilitiesService, conn2); assert.equal(conn.serverName, conn2.serverName); assert.equal(conn.databaseName, conn2.databaseName); @@ -200,21 +205,21 @@ suite('SQL ProviderConnectionInfo tests', () => { }); test('getOptionsKey should create a valid unique id', () => { - let conn = new ProviderConnectionInfo(msSQLCapabilities, connectionProfile); + let conn = new ProviderConnectionInfo(capabilitiesService, connectionProfile); let expectedId = 'providerName:MSSQL|authenticationType:|databaseName:database|serverName:new server|userName:user'; let id = conn.getOptionsKey(); assert.equal(id, expectedId); }); test('getOptionsKey should create different id for different server names', () => { - let conn = new ProviderConnectionInfo(msSQLCapabilities, connectionProfile); - let conn2 = new ProviderConnectionInfo(msSQLCapabilities, Object.assign({}, connectionProfile, { serverName: connectionProfile.serverName + '1' })); + let conn = new ProviderConnectionInfo(capabilitiesService, connectionProfile); + let conn2 = new ProviderConnectionInfo(capabilitiesService, Object.assign({}, connectionProfile, { serverName: connectionProfile.serverName + '1' })); assert.notEqual(conn.getOptionsKey(), conn2.getOptionsKey()); }); test('titleParts should return server, database and auth type as first items', () => { - let conn = new ProviderConnectionInfo(msSQLCapabilities, connectionProfile); + let conn = new ProviderConnectionInfo(capabilitiesService, connectionProfile); let titleParts = conn.titleParts; assert.equal(titleParts.length, 4); assert.equal(titleParts[0], connectionProfile.serverName); diff --git a/src/sqltest/stubs/capabilitiesTestService.ts b/src/sqltest/stubs/capabilitiesTestService.ts index 6437e073ee..2a4f13ffbb 100644 --- a/src/sqltest/stubs/capabilitiesTestService.ts +++ b/src/sqltest/stubs/capabilitiesTestService.ts @@ -6,11 +6,11 @@ 'use strict'; import * as sqlops from 'sqlops'; import { ConnectionManagementInfo } from 'sql/parts/connection/common/connectionManagementInfo'; -import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService'; -import Event from 'vs/base/common/event'; -import { Action } from 'vs/base/common/actions'; +import { ICapabilitiesService, clientCapabilities } from 'sql/services/capabilities/capabilitiesService'; import { ConnectionOptionSpecialType } from 'sql/workbench/api/common/sqlExtHostTypes'; +import Event, { Emitter } from 'vs/base/common/event'; +import { Action } from 'vs/base/common/actions'; export class CapabilitiesTestService implements ICapabilitiesService { @@ -18,8 +18,7 @@ export class CapabilitiesTestService implements ICapabilitiesService { private _providers: sqlops.CapabilitiesProvider[] = []; - private _capabilities: sqlops.DataProtocolServerCapabilities[] = []; - + public capabilities: { [id: string]: sqlops.DataProtocolServerCapabilities } = {}; constructor() { @@ -95,15 +94,19 @@ export class CapabilitiesTestService implements ICapabilitiesService { adminServicesProvider: undefined, features: undefined }; - this._capabilities.push(msSQLCapabilities); + this.capabilities['MSSQL'] = msSQLCapabilities; } /** * Retrieve a list of registered server capabilities */ - public getCapabilities(): sqlops.DataProtocolServerCapabilities[] { - return this._capabilities; + public getCapabilities(provider: string): sqlops.DataProtocolServerCapabilities { + return this.capabilities[provider]; + } + + public get providers(): string[] { + return Object.keys(this.capabilities); } /** @@ -125,5 +128,8 @@ export class CapabilitiesTestService implements ICapabilitiesService { public onCapabilitiesReady(): Promise { return Promise.resolve(null); } + + private _onCapabilitiesRegistered = new Emitter(); + public readonly onCapabilitiesRegistered = this._onCapabilitiesRegistered.event; }