diff --git a/extensions/mssql/package.json b/extensions/mssql/package.json index f7dfffb656..d2e1549f7b 100644 --- a/extensions/mssql/package.json +++ b/extensions/mssql/package.json @@ -189,6 +189,473 @@ ] } ] + }, + "connectionProvider": { + "providerId": "MSSQL", + "displayName": "Microsoft SQL Server", + "connectionOptions": [ + { + "specialValueType": "serverName", + "isIdentity": true, + "name": "server", + "displayName": "Server", + "description": "Name of the SQL Server instance", + "groupName": "Source", + "valueType": "string", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": true, + "isArray": false + }, + { + "specialValueType": "databaseName", + "isIdentity": true, + "name": "database", + "displayName": "Database", + "description": "The name of the initial catalog or database int the data source", + "groupName": "Source", + "valueType": "string", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "specialValueType": "authType", + "isIdentity": true, + "name": "authenticationType", + "displayName": "Authentication type", + "description": "Specifies the method of authenticating with SQL Server", + "groupName": "Security", + "valueType": "category", + "defaultValue": null, + "objectType": null, + "categoryValues": [ + { + "displayName": "SQL Login", + "name": "SqlLogin" + }, + { + "displayName": "Windows Authentication", + "name": "Integrated" + } + ], + "isRequired": true, + "isArray": false + }, + { + "specialValueType": "userName", + "isIdentity": true, + "name": "user", + "displayName": "User name", + "description": "Indicates the user ID to be used when connecting to the data source", + "groupName": "Security", + "valueType": "string", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": true, + "isArray": false + }, + { + "specialValueType": "password", + "isIdentity": true, + "name": "password", + "displayName": "Password", + "description": "Indicates the password to be used when connecting to the data source", + "groupName": "Security", + "valueType": "password", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": true, + "isArray": false + }, + { + "specialValueType": null, + "isIdentity": false, + "name": "applicationIntent", + "displayName": "Application intent", + "description": "Declares the application workload type when connecting to a server", + "groupName": "Initialization", + "valueType": "category", + "defaultValue": null, + "objectType": null, + "categoryValues": [ + { + "displayName": "ReadWrite", + "name": "ReadWrite" + }, + { + "displayName": "ReadOnly", + "name": "ReadOnly" + } + ], + "isRequired": false, + "isArray": false + }, + { + "specialValueType": null, + "isIdentity": false, + "name": "asynchronousProcessing", + "displayName": "Asynchronous processing enabled", + "description": "When true, enables usage of the Asynchronous functionality in the .Net Framework Data Provider", + "groupName": "Initialization", + "valueType": "boolean", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "specialValueType": null, + "isIdentity": false, + "name": "connectTimeout", + "displayName": "Connect timeout", + "description": "The length of time (in seconds) to wait for a connection to the server before terminating the attempt and generating an error", + "groupName": "Initialization", + "valueType": "number", + "defaultValue": "15", + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "specialValueType": null, + "isIdentity": false, + "name": "currentLanguage", + "displayName": "Current language", + "description": "The SQL Server language record name", + "groupName": "Initialization", + "valueType": "string", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "specialValueType": null, + "isIdentity": false, + "name": "columnEncryptionSetting", + "displayName": "Column encryption setting", + "description": "Default column encryption setting for all the commands on the connection", + "groupName": "Security", + "valueType": "category", + "defaultValue": null, + "objectType": null, + "categoryValues": [ + { + "displayName": null, + "name": "Disabled" + }, + { + "displayName": null, + "name": "Enabled" + } + ], + "isRequired": false, + "isArray": false + }, + { + "specialValueType": null, + "isIdentity": false, + "name": "encrypt", + "displayName": "Encrypt", + "description": "When true, SQL Server uses SSL encryption for all data sent between the client and server if the servers has a certificate installed", + "groupName": "Security", + "valueType": "boolean", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "specialValueType": null, + "isIdentity": false, + "name": "persistSecurityInfo", + "displayName": "Persist security info", + "description": "When false, security-sensitive information, such as the password, is not returned as part of the connection", + "groupName": "Security", + "valueType": "boolean", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "specialValueType": null, + "isIdentity": false, + "name": "trustServerCertificate", + "displayName": "Trust server certificate", + "description": "When true (and encrypt=true), SQL Server uses SSL encryption for all data sent between the client and server without validating the server certificate", + "groupName": "Security", + "valueType": "boolean", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "specialValueType": null, + "isIdentity": false, + "name": "attachedDBFileName", + "displayName": "Attached DB file name", + "description": "The name of the primary file, including the full path name, of an attachable database", + "groupName": "Source", + "valueType": "string", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "specialValueType": null, + "isIdentity": false, + "name": "contextConnection", + "displayName": "Context connection", + "description": "When true, indicates the connection should be from the SQL server context. Available only when running in the SQL Server process", + "groupName": "Source", + "valueType": "boolean", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "specialValueType": null, + "isIdentity": false, + "name": "port", + "displayName": "Port", + "description": null, + "groupName": null, + "valueType": "number", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "specialValueType": null, + "isIdentity": false, + "name": "connectRetryCount", + "displayName": "Connect retry count", + "description": "Number of attempts to restore connection", + "groupName": "Connection Resiliency", + "valueType": "number", + "defaultValue": "1", + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "specialValueType": null, + "isIdentity": false, + "name": "connectRetryInterval", + "displayName": "Connect retry interval", + "description": "Delay between attempts to restore connection", + "groupName": "Connection Resiliency", + "valueType": "number", + "defaultValue": "10", + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "specialValueType": "appName", + "isIdentity": false, + "name": "applicationName", + "displayName": "Application name", + "description": "The name of the application", + "groupName": "Context", + "valueType": "string", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "specialValueType": null, + "isIdentity": false, + "name": "workstationId", + "displayName": "Workstation Id", + "description": "The name of the workstation connecting to SQL Server", + "groupName": "Context", + "valueType": "string", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "specialValueType": null, + "isIdentity": false, + "name": "pooling", + "displayName": "Pooling", + "description": "When true, the connection object is drawn from the appropriate pool, or if necessary, is created and added to the appropriate pool", + "groupName": "Pooling", + "valueType": "boolean", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "specialValueType": null, + "isIdentity": false, + "name": "maxPoolSize", + "displayName": "Max pool size", + "description": "The maximum number of connections allowed in the pool", + "groupName": "Pooling", + "valueType": "number", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "specialValueType": null, + "isIdentity": false, + "name": "minPoolSize", + "displayName": "Min pool size", + "description": "The minimum number of connections allowed in the pool", + "groupName": "Pooling", + "valueType": "number", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "specialValueType": null, + "isIdentity": false, + "name": "loadBalanceTimeout", + "displayName": "Load balance timeout", + "description": "The minimum amount of time (in seconds) for this connection to live in the pool before being destroyed", + "groupName": "Pooling", + "valueType": "number", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "specialValueType": null, + "isIdentity": false, + "name": "replication", + "displayName": "Replication", + "description": "Used by SQL Server in Replication", + "groupName": "Replication", + "valueType": "boolean", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "specialValueType": null, + "isIdentity": false, + "name": "attachDbFilename", + "displayName": "Attach DB filename", + "description": null, + "groupName": null, + "valueType": "string", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "specialValueType": null, + "isIdentity": false, + "name": "failoverPartner", + "displayName": "Failover partner", + "description": "The name or network address of the instance of SQL Server that acts as a failover partner", + "groupName": " Source", + "valueType": "string", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "specialValueType": null, + "isIdentity": false, + "name": "multiSubnetFailover", + "displayName": "Multi subnet failover", + "description": null, + "groupName": null, + "valueType": "boolean", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "specialValueType": null, + "isIdentity": false, + "name": "multipleActiveResultSets", + "displayName": "Multiple active result sets", + "description": "When true, multiple result sets can be returned and read from one connection", + "groupName": "Advanced", + "valueType": "boolean", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "specialValueType": null, + "isIdentity": false, + "name": "packetSize", + "displayName": "Packet size", + "description": "Size in bytes of the network packets used to communicate with an instance of SQL Server", + "groupName": "Advanced", + "valueType": "number", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + }, + { + "specialValueType": null, + "isIdentity": false, + "name": "typeSystemVersion", + "displayName": "Type system version", + "description": "Indicates which server type system then provider will expose through the DataReader", + "groupName": "Advanced", + "valueType": "string", + "defaultValue": null, + "objectType": null, + "categoryValues": null, + "isRequired": false, + "isArray": false + } + ] } }, "dependencies": { diff --git a/src/sql/base/common/map.ts b/src/sql/base/common/map.ts index 4adc7f793e..fd2135faa6 100644 --- a/src/sql/base/common/map.ts +++ b/src/sql/base/common/map.ts @@ -111,3 +111,14 @@ export class TrieMap { return result; } } + +export function toObject(map: Map): { [key: string]: V } { + if (map) { + let rt: { [key: string]: V } = Object.create(null); + map.forEach((v, k) => { + rt[k] = v; + }); + return rt; + } + return {}; +} diff --git a/src/sql/base/common/objects.ts b/src/sql/base/common/objects.ts index 8bf1c4f137..f0c1cb5593 100644 --- a/src/sql/base/common/objects.ts +++ b/src/sql/base/common/objects.ts @@ -38,7 +38,7 @@ export function mixin(destination: any, source: any, overwrite: boolean = true, if (overwrite) { if (Types.isObject(destination[key]) && Types.isObject(source[key])) { mixin(destination[key], source[key], overwrite, fn); - } else if(fn) { + } else if (fn) { destination[key] = fn(destination[key], source[key], overwrite); } else { destination[key] = source[key]; @@ -50,4 +50,12 @@ export function mixin(destination: any, source: any, overwrite: boolean = true, }); } return destination; -} \ No newline at end of file +} + +export function entries(o: { [key: string]: T }): [string, T][] { + return Object.entries(o); +} + +export function values(o: { [key: string]: T }): T[] { + return Object.values(o); +} diff --git a/src/sql/base/common/promise.ts b/src/sql/base/common/promise.ts index dbad3a307d..a336b8c127 100644 --- a/src/sql/base/common/promise.ts +++ b/src/sql/base/common/promise.ts @@ -17,4 +17,10 @@ export class Deferred { this.reject = reject; }); } + + then(onfulfilled?: (value: T) => TResult | Thenable, onrejected?: (reason: any) => TResult | Thenable): Thenable; + then(onfulfilled?: (value: T) => TResult | Thenable, onrejected?: (reason: any) => void): Thenable; + then(onfulfilled?: (value: T) => TResult | Thenable, onrejected?: (reason: any) => TResult | Thenable | void): Thenable { + return this.promise.then(onfulfilled, onrejected); + } } \ No newline at end of file diff --git a/src/sql/parts/connection/common/connectionManagement.ts b/src/sql/parts/connection/common/connectionManagement.ts index 3ddc717af1..b6997f6ab4 100644 --- a/src/sql/parts/connection/common/connectionManagement.ts +++ b/src/sql/parts/connection/common/connectionManagement.ts @@ -239,14 +239,6 @@ export interface IConnectionManagementService { */ ensureDefaultLanguageFlavor(uri: string): void; - /** - * Gets an array of all known providers. - * - * @returns {string[]} An array of provider names - * @memberof IConnectionManagementService - */ - getProviderNames(): string[]; - /** * Refresh the IntelliSense cache for the connection with the given URI */ diff --git a/src/sql/parts/connection/common/connectionManagementService.ts b/src/sql/parts/connection/common/connectionManagementService.ts index e60c18685b..e6259b4e57 100644 --- a/src/sql/parts/connection/common/connectionManagementService.ts +++ b/src/sql/parts/connection/common/connectionManagementService.ts @@ -30,12 +30,16 @@ import { warn } from 'sql/base/common/log'; import { IResourceProviderService } from 'sql/parts/accountManagement/common/interfaces'; import { IAngularEventingService, AngularEventType } from 'sql/services/angularEventing/angularEventingService'; import * as QueryConstants from 'sql/parts/query/common/constants'; +import { Deferred } from 'sql/base/common/promise'; +import { ConnectionOptionSpecialType } from 'sql/workbench/api/common/sqlExtHostTypes'; +import { values } from 'sql/base/common/objects'; +import { ConnectionProviderProperties, IConnectionProviderRegistry, Extensions as ConnectionProviderExtensions } from 'sql/workbench/parts/connection/common/connectionProviderExtension'; import * as sqlops from 'sqlops'; import * as nls from 'vs/nls'; import * as errors from 'vs/base/common/errors'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import * as platform from 'vs/platform/registry/common/platform'; @@ -55,30 +59,26 @@ import * as statusbar from 'vs/workbench/browser/parts/statusbar/statusbar'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IStatusbarService } from 'vs/platform/statusbar/common/statusbar'; import { ICommandService } from 'vs/platform/commands/common/commands'; -import { Deferred } from 'sql/base/common/promise'; -import { ConnectionOptionSpecialType } from 'sql/workbench/api/common/sqlExtHostTypes'; -export class ConnectionManagementService implements IConnectionManagementService { +export class ConnectionManagementService extends Disposable implements IConnectionManagementService { _serviceBrand: any; - private disposables: IDisposable[] = []; - - private _providers: { [handle: string]: sqlops.ConnectionProvider; } = Object.create(null); + private _providers = new Map, properties: ConnectionProviderProperties }>(); private _uriToProvider: { [uri: string]: string; } = Object.create(null); - private _connectionStatusManager: ConnectionStatusManager; + private _connectionStatusManager = new ConnectionStatusManager(this._capabilitiesService); - private _onAddConnectionProfile: Emitter; - private _onDeleteConnectionProfile: Emitter; - private _onConnect: Emitter; - private _onDisconnect: Emitter; - private _onConnectRequestSent: Emitter; - private _onConnectionChanged: Emitter; - private _onLanguageFlavorChanged: Emitter; + private _onAddConnectionProfile = new Emitter(); + private _onDeleteConnectionProfile = new Emitter(); + private _onConnect = new Emitter(); + private _onDisconnect = new Emitter(); + private _onConnectRequestSent = new Emitter(); + private _onConnectionChanged = new Emitter(); + private _onLanguageFlavorChanged = new Emitter(); - private _connectionGlobalStatus: ConnectionGlobalStatus; + private _connectionGlobalStatus = new ConnectionGlobalStatus(this._statusBarService); private _configurationEditService: ConfigurationEditingService; @@ -103,6 +103,7 @@ export class ConnectionManagementService implements IConnectionManagementService @IViewletService private _viewletService: IViewletService, @IAngularEventingService private _angularEventing: IAngularEventingService ) { + super(); if (this._instantiationService) { this._configurationEditService = this._instantiationService.createInstance(ConfigurationEditingService); } @@ -116,20 +117,6 @@ export class ConnectionManagementService implements IConnectionManagementService this._configurationEditService, this._workspaceConfigurationService, this._credentialsService, this._capabilitiesService); } - this._connectionStatusManager = new ConnectionStatusManager(this._capabilitiesService); - this._connectionGlobalStatus = new ConnectionGlobalStatus(this._statusBarService); - - // Setting up our event emitters - this._onAddConnectionProfile = new Emitter(); - this._onDeleteConnectionProfile = new Emitter(); - this._onConnect = new Emitter(); - this._onDisconnect = new Emitter(); - this._onConnectionChanged = new Emitter(); - this._onConnectRequestSent = new Emitter(); - this._onLanguageFlavorChanged = new Emitter(); - - this._onProvidersReady = new Deferred(); - // Register Statusbar item (platform.Registry.as(statusbar.Extensions.Statusbar)).registerStatusbarItem(new statusbar.StatusbarItemDescriptor( ConnectionStatusbarItem, @@ -137,19 +124,33 @@ export class ConnectionManagementService implements IConnectionManagementService 100 /* High Priority */ )); - 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(); - } - } - })); + if (_capabilitiesService && Object.keys(_capabilitiesService.providers).length > 0 && !this.hasRegisteredServers()) { + // prompt the user for a new connection on startup if no profiles are registered + this.showConnectionDialog(); + } else if (_capabilitiesService && !this.hasRegisteredServers()) { + _capabilitiesService.onCapabilitiesRegistered(e => { + // prompt the user for a new connection on startup if no profiles are registered + this.showConnectionDialog(); + }); } - this.disposables.push(this._onAddConnectionProfile); - this.disposables.push(this._onDeleteConnectionProfile); + const registry = platform.Registry.as(ConnectionProviderExtensions.ConnectionProviderContributions); + + let providerRegistration = (p: { id: string, properties: ConnectionProviderProperties }) => { + let provider = { + onReady: new Deferred(), + properties: p.properties + }; + this._providers.set(p.id, provider); + }; + + registry.onNewProvider(providerRegistration, this); + Object.entries(registry.providers).map(v => { + providerRegistration({ id: v[0], properties: v[1] }); + }); + + this._register(this._onAddConnectionProfile); + this._register(this._onDeleteConnectionProfile); // Refresh editor titles when connections start/end/change to ensure tabs are colored correctly this.onConnectionChanged(() => this.refreshEditorTitles()); @@ -186,37 +187,21 @@ export class ConnectionManagementService implements IConnectionManagementService return this._onLanguageFlavorChanged.event; } - private _onProvidersReady: Deferred; - - private onProvidersReady(): Promise { - return this._onProvidersReady.promise; - } - private _providerCount: number = 0; // Connection Provider Registration public registerProvider(providerId: string, provider: sqlops.ConnectionProvider): void { - this._providers[providerId] = provider; - - // temporarily close splash screen when a connection provider has been registered - // @todo remove this code once a proper initialization event is available (karlb 4/1/2017) - ++this._providerCount; - - this._onProvidersReady.resolve(); - - if (this._providerCount === 1) { - // show the Registered Server viewlet - let startupConfig = this._workspaceConfigurationService.getValue('startup'); - if (startupConfig) { - let showServerViewlet = startupConfig['alwaysShowServersView']; - if (showServerViewlet) { - // only show the Servers viewlet if there isn't another active viewlet - if (!this._viewletService.getActiveViewlet()) { - this._commandService.executeCommand('workbench.view.connections', {}); - } - } - } + if (!this._providers.has(providerId)) { + console.error('Provider', providerId, 'attempted to register but has no metadata'); + let providerType = { + onReady: new Deferred(), + properties: undefined + }; + this._providers.set(providerId, providerType); } + + // we know this is a deferred promise because we made it + (this._providers.get(providerId).onReady as Deferred).resolve(provider); } /** @@ -675,19 +660,15 @@ export class ConnectionManagementService implements IConnectionManagementService }); } - public getProviderNames(): string[] { - return Object.keys(this._providers); - } - public getAdvancedProperties(): sqlops.ConnectionOption[] { let providers = this._capabilitiesService.providers; - if (providers !== undefined && providers.length > 0) { + if (providers) { // just grab the first registered provider for now, this needs to change // to lookup based on currently select provider - let providerCapabilities = this._capabilitiesService.getCapabilities(providers[0]); - if (!!providerCapabilities.connectionProvider) { - return providerCapabilities.connectionProvider.options; + let providerCapabilities = values(providers)[0]; + if (!!providerCapabilities.connection) { + return providerCapabilities.connection.connectionOptions; } } @@ -752,7 +733,7 @@ export class ConnectionManagementService implements IConnectionManagementService * @memberof ConnectionManagementService */ public doChangeLanguageFlavor(uri: string, language: string, provider: string): void { - if (provider in this._providers) { + if (this._providers.has(provider)) { this._onLanguageFlavorChanged.fire({ uri: uri, language: language, @@ -772,7 +753,7 @@ export class ConnectionManagementService implements IConnectionManagementService if (!this.getProviderIdFromUri(uri)) { // Lookup the default settings and use this let defaultProvider = WorkbenchUtils.getSqlConfigValue(this._workspaceConfigurationService, Constants.defaultEngine); - if (defaultProvider && defaultProvider in this._providers) { + if (defaultProvider && this._providers.has(defaultProvider)) { // Only set a default if it's in the list of registered providers this.doChangeLanguageFlavor(uri, 'sql', defaultProvider); } @@ -788,15 +769,13 @@ export class ConnectionManagementService implements IConnectionManagementService // setup URI to provider ID map for connection this._uriToProvider[uri] = connection.providerName; - return new Promise((resolve, reject) => { - this.onProvidersReady().then(() => { - this._providers[connection.providerName].connect(uri, connectionInfo); - this._onConnectRequestSent.fire(); + return this._providers.get(connection.providerName).onReady.then((provider) => { + provider.connect(uri, connectionInfo); + this._onConnectRequestSent.fire(); - // TODO make this generic enough to handle non-SQL languages too - this.doChangeLanguageFlavor(uri, 'sql', connection.providerName); - resolve(true); - }); + // TODO make this generic enough to handle non-SQL languages too + this.doChangeLanguageFlavor(uri, 'sql', connection.providerName); + return true; }); } @@ -806,9 +785,9 @@ export class ConnectionManagementService implements IConnectionManagementService return Promise.resolve(false); } - return new Promise((resolve, reject) => { - this._providers[providerId].disconnect(uri); - resolve(true); + return this._providers.get(providerId).onReady.then(provider => { + provider.disconnect(uri); + return true; }); } @@ -818,9 +797,9 @@ export class ConnectionManagementService implements IConnectionManagementService return Promise.resolve(false); } - return new Promise((resolve, reject) => { - this._providers[providerId].cancelConnect(uri); - resolve(true); + return this._providers.get(providerId).onReady.then(provider => { + provider.cancelConnect(uri); + return true; }); } @@ -830,15 +809,12 @@ export class ConnectionManagementService implements IConnectionManagementService return Promise.resolve(undefined); } - return new Promise((resolve, reject) => { - let provider = this._providers[providerId]; - provider.listDatabases(uri).then(result => { + return this._providers.get(providerId).onReady.then(provider => { + return provider.listDatabases(uri).then(result => { if (result && result.databaseNames) { result.databaseNames.sort(); } - resolve(result); - }, error => { - reject(error); + return result; }); }); } @@ -932,10 +908,6 @@ export class ConnectionManagementService implements IConnectionManagementService public onIntelliSenseCacheComplete(handle: number, connectionUri: string): void { } - public dispose(): void { - this.disposables = dispose(this.disposables); - } - public shutdown(): void { this._connectionStore.clearActiveConnections(); this._connectionMemento.saveMemento(); @@ -1202,12 +1174,13 @@ export class ConnectionManagementService implements IConnectionManagementService return Promise.resolve(false); } - let provider = this._providers[providerId]; - return provider.changeDatabase(connectionUri, databaseName).then(result => { - if (result) { - this.getConnectionProfile(connectionUri).databaseName = databaseName; - } - return result; + return this._providers.get(providerId).onReady.then(provider => { + return provider.changeDatabase(connectionUri, databaseName).then(result => { + if (result) { + this.getConnectionProfile(connectionUri).databaseName = databaseName; + } + return result; + }); }); } return Promise.resolve(false); @@ -1316,8 +1289,7 @@ export class ConnectionManagementService implements IConnectionManagementService return Promise.reject('No provider corresponding to the given URI'); } - let provider = this._providers[providerId]; - return provider.rebuildIntelliSenseCache(connectionUri); + return this._providers.get(providerId).onReady.then(provider => provider.rebuildIntelliSenseCache(connectionUri)); } return Promise.reject('The given URI is not currently connected'); } @@ -1354,7 +1326,7 @@ export class ConnectionManagementService implements IConnectionManagementService } // Find the password option for the connection provider - let passwordOption = this._capabilitiesService.getCapabilities(profile.providerName).connectionProvider.options.find( + let passwordOption = this._capabilitiesService.getCapabilities(profile.providerName).connection.connectionOptions.find( option => option.specialValueType === ConnectionOptionSpecialType.password); if (!passwordOption) { return undefined; diff --git a/src/sql/parts/connection/common/providerConnectionInfo.ts b/src/sql/parts/connection/common/providerConnectionInfo.ts index 1b9e7d87a4..1b08eb206e 100644 --- a/src/sql/parts/connection/common/providerConnectionInfo.ts +++ b/src/sql/parts/connection/common/providerConnectionInfo.ts @@ -13,13 +13,14 @@ 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'; +import { ConnectionProviderProperties } from 'sql/workbench/parts/connection/common/connectionProviderExtension'; export class ProviderConnectionInfo extends Disposable implements sqlops.ConnectionInfo { options: { [name: string]: any } = {}; private _providerName: string; - protected _serverCapabilities: sqlops.DataProtocolServerCapabilities; + protected _serverCapabilities: ConnectionProviderProperties; private static readonly SqlAuthentication = 'SqlLogin'; public static readonly ProviderPropertyName = 'providerName'; @@ -34,7 +35,7 @@ export class ProviderConnectionInfo extends Disposable implements sqlops.Connect if (!isString(model)) { if (model.options && this._serverCapabilities) { - this._serverCapabilities.connectionProvider.options.forEach(option => { + this._serverCapabilities.connectionOptions.forEach(option => { let value = model.options[option.name]; this.options[option.name] = value; }); @@ -55,10 +56,13 @@ export class ProviderConnectionInfo extends Disposable implements sqlops.Connect public set providerName(name: string) { this._providerName = name; if (!this._serverCapabilities) { - this._serverCapabilities = this.capabilitiesService.getCapabilities(this.providerName); + let capabilities = this.capabilitiesService.getCapabilities(this.providerName); + if (capabilities) { + this._serverCapabilities = capabilities.connection; + } this._register(this.capabilitiesService.onCapabilitiesRegistered(e => { - if (e === this.providerName) { - this._serverCapabilities = this.capabilitiesService.getCapabilities(e); + if (e.connection.providerId === this.providerName) { + this._serverCapabilities = e.connection; } })); } @@ -70,7 +74,7 @@ export class ProviderConnectionInfo extends Disposable implements sqlops.Connect return instance; } - public get serverCapabilities(): sqlops.DataProtocolServerCapabilities { + public get serverCapabilities(): ConnectionProviderProperties { return this._serverCapabilities; } @@ -141,7 +145,7 @@ export class ProviderConnectionInfo extends Disposable implements sqlops.Connect } public isPasswordRequired(): boolean { - let optionMetadata = this._serverCapabilities.connectionProvider.options.find( + let optionMetadata = this._serverCapabilities.connectionOptions.find( option => option.specialValueType === ConnectionOptionSpecialType.password); let isPasswordRequired: boolean = optionMetadata.isRequired; if (this.providerName === Constants.mssqlProviderName) { @@ -166,7 +170,7 @@ export class ProviderConnectionInfo extends Disposable implements sqlops.Connect public getOptionsKey(): string { let idNames = []; if (this._serverCapabilities) { - idNames = this._serverCapabilities.connectionProvider.options.map(o => { + idNames = this._serverCapabilities.connectionOptions.map(o => { if ((o.specialValueType || o.isIdentity) && o.specialValueType !== ConnectionOptionSpecialType.password) { return o.name; } else { @@ -210,7 +214,7 @@ export class ProviderConnectionInfo extends Disposable implements sqlops.Connect public getSpecialTypeOptionName(type: string): string { if (this._serverCapabilities) { - let optionMetadata = this._serverCapabilities.connectionProvider.options.find(o => o.specialValueType === type); + let optionMetadata = this._serverCapabilities.connectionOptions.find(o => o.specialValueType === type); return !!optionMetadata ? optionMetadata.name : undefined; } else { return type.toString(); @@ -225,7 +229,7 @@ export class ProviderConnectionInfo extends Disposable implements sqlops.Connect } public get authenticationTypeDisplayName(): string { - let optionMetadata = this._serverCapabilities.connectionProvider.options.find(o => o.specialValueType === ConnectionOptionSpecialType.authType); + let optionMetadata = this._serverCapabilities.connectionOptions.find(o => o.specialValueType === ConnectionOptionSpecialType.authType); let authType = this.authenticationType; let displayName: string = authType; @@ -240,7 +244,7 @@ export class ProviderConnectionInfo extends Disposable implements sqlops.Connect } public getProviderOptions(): sqlops.ConnectionOption[] { - return this._serverCapabilities.connectionProvider.options; + return this._serverCapabilities.connectionOptions; } public static get idSeparator(): string { @@ -258,7 +262,7 @@ export class ProviderConnectionInfo extends Disposable implements sqlops.Connect parts.push(this.databaseName); parts.push(this.authenticationTypeDisplayName); - this._serverCapabilities.connectionProvider.options.forEach(element => { + this._serverCapabilities.connectionOptions.forEach(element => { if (element.specialValueType !== ConnectionOptionSpecialType.serverName && element.specialValueType !== ConnectionOptionSpecialType.databaseName && element.specialValueType !== ConnectionOptionSpecialType.authType && diff --git a/src/sql/parts/connection/connectionDialog/connectionController.ts b/src/sql/parts/connection/connectionDialog/connectionController.ts index 691721ed68..373f6b1237 100644 --- a/src/sql/parts/connection/connectionDialog/connectionController.ts +++ b/src/sql/parts/connection/connectionDialog/connectionController.ts @@ -16,6 +16,7 @@ import * as sqlops from 'sqlops'; import * as Utils from 'sql/parts/connection/common/utils'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ConnectionOptionSpecialType } from 'sql/workbench/api/common/sqlExtHostTypes'; +import { ConnectionProviderProperties } from 'sql/workbench/parts/connection/common/connectionProviderExtension'; export class ConnectionController implements IConnectionComponentController { private _container: HTMLElement; @@ -31,14 +32,14 @@ export class ConnectionController implements IConnectionComponentController { constructor(container: HTMLElement, connectionManagementService: IConnectionManagementService, - sqlCapabilities: sqlops.DataProtocolServerCapabilities, + connectionProperties: ConnectionProviderProperties, callback: IConnectionComponentCallbacks, providerName: string, - @IInstantiationService private _instantiationService: IInstantiationService ) { + @IInstantiationService private _instantiationService: IInstantiationService) { this._container = container; this._connectionManagementService = connectionManagementService; this._callback = callback; - this._providerOptions = sqlCapabilities.connectionProvider.options; + this._providerOptions = connectionProperties.connectionOptions; var specialOptions = this._providerOptions.filter( (property) => (property.specialValueType !== null && property.specialValueType !== undefined)); this._connectionWidget = this._instantiationService.createInstance(ConnectionWidget, specialOptions, { @@ -48,8 +49,8 @@ export class ConnectionController implements IConnectionComponentController { onSetAzureTimeOut: () => this.handleonSetAzureTimeOut(), onFetchDatabases: (serverName: string, authenticationType: string, userName?: string, password?: string) => this.onFetchDatabases( serverName, authenticationType, userName, password).then(result => { - return result; - }) + return result; + }) }, providerName); this._providerName = providerName; } @@ -65,7 +66,7 @@ export class ConnectionController implements IConnectionComponentController { let uri = this._connectionManagementService.getConnectionId(tempProfile); return new Promise((resolve, reject) => { if (this._databaseCache.has(uri)) { - let cachedDatabases : string[] = this._databaseCache.get(uri); + let cachedDatabases: string[] = this._databaseCache.get(uri); if (cachedDatabases !== null) { resolve(cachedDatabases); } else { diff --git a/src/sql/parts/connection/connectionDialog/connectionDialogService.ts b/src/sql/parts/connection/connectionDialog/connectionDialogService.ts index 68c6ea3425..29ac5a96d6 100644 --- a/src/sql/parts/connection/connectionDialog/connectionDialogService.ts +++ b/src/sql/parts/connection/connectionDialog/connectionDialogService.ts @@ -32,6 +32,7 @@ import { IWindowsService } from 'vs/platform/windows/common/windows'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { ICommandService } from 'vs/platform/commands/common/commands'; import * as types from 'vs/base/common/types'; +import { entries } from 'sql/base/common/objects'; export interface IConnectionValidateResult { isValid: boolean; @@ -190,7 +191,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._capabilitiesService.getCapabilities(providerName), { + this._connectionControllerMap[providerName] = this._instantiationService.createInstance(ConnectionController, this._container, this._connectionManagementService, this._capabilitiesService.getCapabilities(providerName).connection, { onSetConnectButton: (enable: boolean) => this.handleSetConnectButtonEnable(enable) }, providerName); } @@ -274,12 +275,9 @@ export class ConnectionDialogService implements IConnectionDialogService { // only create the provider maps first time the dialog gets called let capabilitiesPromise: Promise = Promise.resolve(); if (this._providerTypes.length === 0) { - capabilitiesPromise = this._capabilitiesService.onCapabilitiesReady().then(() => { - this._capabilitiesService.providers.map(p => { - let capabilities = this._capabilitiesService.getCapabilities(p); - this._providerTypes.push(capabilities.providerDisplayName); - this._providerNameToDisplayNameMap[capabilities.providerName] = capabilities.providerDisplayName; - }); + entries(this._capabilitiesService.providers).forEach(p => { + this._providerTypes.push(p[1].connection.displayName); + this._providerNameToDisplayNameMap[p[0]] = p[1].connection.displayName; }); } capabilitiesPromise.then(s => { diff --git a/src/sql/parts/disasterRecovery/backup/common/backupServiceImp.ts b/src/sql/parts/disasterRecovery/backup/common/backupServiceImp.ts index 28741bc634..a95bd8e361 100644 --- a/src/sql/parts/disasterRecovery/backup/common/backupServiceImp.ts +++ b/src/sql/parts/disasterRecovery/backup/common/backupServiceImp.ts @@ -116,7 +116,7 @@ export class BackupUiService implements IBackupUiService { } private getOptions(provider: string): ServiceOption[] { - let feature = this._capabilitiesService.getCapabilities(this._currentProvider).features.find(f => f.featureName === 'backup'); + let feature = this._capabilitiesService.getLegacyCapabilities(this._currentProvider).features.find(f => f.featureName === 'backup'); if (feature) { return feature.optionsMetadata; } else { diff --git a/src/sql/parts/disasterRecovery/restore/common/restoreServiceImpl.ts b/src/sql/parts/disasterRecovery/restore/common/restoreServiceImpl.ts index 5d40b1ef78..9f037b5b30 100644 --- a/src/sql/parts/disasterRecovery/restore/common/restoreServiceImpl.ts +++ b/src/sql/parts/disasterRecovery/restore/common/restoreServiceImpl.ts @@ -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(providerId); + let providerCapabilities = this._capabilitiesService.getLegacyCapabilities(providerId); if (providerCapabilities) { let restoreMetadataProvider = providerCapabilities.features.find(f => f.featureName === this._restoreFeature); diff --git a/src/sql/parts/registeredServer/viewlet/serverTreeView.ts b/src/sql/parts/registeredServer/viewlet/serverTreeView.ts index d0a4c1d08e..37392469b8 100644 --- a/src/sql/parts/registeredServer/viewlet/serverTreeView.ts +++ b/src/sql/parts/registeredServer/viewlet/serverTreeView.ts @@ -60,14 +60,6 @@ export class ServerTreeView { this); this._treeSelectionHandler = this._instantiationService.createInstance(TreeSelectionHandler); this._onSelectionOrFocusChange = new Emitter(); - if (this._capabilitiesService) { - this._capabilitiesService.onCapabilitiesReady().then(() => { - if (this._connectionManagementService.hasRegisteredServers()) { - this.refreshTree(); - this._treeSelectionHandler.onTreeActionStateChange(false); - } - }); - } } /** diff --git a/src/sql/services/capabilities/capabilitiesService.ts b/src/sql/services/capabilities/capabilitiesService.ts index 19cba0718d..21db808cbf 100644 --- a/src/sql/services/capabilities/capabilitiesService.ts +++ b/src/sql/services/capabilities/capabilitiesService.ts @@ -8,24 +8,31 @@ import { ConnectionManagementInfo } from 'sql/parts/connection/common/connectionManagementInfo'; import * as Constants from 'sql/common/constants'; import { Deferred } from 'sql/base/common/promise'; +import { ConnectionProviderProperties, IConnectionProviderRegistry, Extensions as ConnectionExtensions } from 'sql/workbench/parts/connection/common/connectionProviderExtension'; +import { toObject } from 'sql/base/common/map'; import * as sqlops from 'sqlops'; import Event, { Emitter } from 'vs/base/common/event'; import { IAction } from 'vs/base/common/actions'; -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'; +import { Registry } from 'vs/platform/registry/common/platform'; export const SERVICE_ID = 'capabilitiesService'; export const HOST_NAME = 'sqlops'; export const HOST_VERSION = '1.0'; -interface IProtocolMomento { - [id: string]: sqlops.DataProtocolServerCapabilities; +const connectionRegistry = Registry.as(ConnectionExtensions.ConnectionProviderContributions); + +interface ConnectionCache { + [id: string]: ConnectionProviderProperties; +} + +interface CapabilitiesMomento { + connectionProviderCache: ConnectionCache; } export const clientCapabilities = { @@ -33,6 +40,11 @@ export const clientCapabilities = { hostVersion: HOST_VERSION }; +export interface ProviderFeatures { + connection: ConnectionProviderProperties; +} + + export const ICapabilitiesService = createDecorator(SERVICE_ID); /** @@ -44,7 +56,12 @@ export interface ICapabilitiesService { /** * Retrieve a list of registered capabilities providers */ - getCapabilities(provider: string): sqlops.DataProtocolServerCapabilities; + getCapabilities(provider: string): ProviderFeatures; + + /** + * get the old version of provider information + */ + getLegacyCapabilities(provider: string): sqlops.DataProtocolServerCapabilities; /** * Register a capabilities provider @@ -56,20 +73,15 @@ export interface ICapabilitiesService { */ isFeatureAvailable(action: IAction, connectionManagementInfo: ConnectionManagementInfo): boolean; - /** - * 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; + readonly onCapabilitiesRegistered: Event; /** * Get an array of all known providers */ - readonly providers: string[]; + readonly providers: { [id: string]: ProviderFeatures }; } @@ -78,89 +90,76 @@ export interface ICapabilitiesService { * to discover the DMP capabilties that a DMP provider offers. */ export class CapabilitiesService extends Disposable implements ICapabilitiesService { + _serviceBrand: any; - public _serviceBrand: any; + private _momento = new Memento('capabilities'); + private _providers = new Map(); + private _featureUpdateEvents = new Map>(); + private _legacyProviders = new Map(); - private _momento = new Memento('capabilitiesCache'); - - private static DATA_PROVIDER_CATEGORY: string = 'Data Provider'; - - private disposables: IDisposable[] = []; - - 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. - // TODO once we have a complete extension story this might change and will have to be looked into - - private _expectedCapabilitiesCount: number = 1; - - private _registeredCapabilities: number = 0; - - private _onCapabilitiesRegistered = this._register(new Emitter()); + private _onCapabilitiesRegistered = this._register(new Emitter()); public readonly onCapabilitiesRegistered = this._onCapabilitiesRegistered.event; 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[]) => { - let dataProviderExtensions = extensions.filter(extension => - extension.manifest.categories && extension.manifest.categories.indexOf(CapabilitiesService.DATA_PROVIDER_CATEGORY) > -1); + if (!this.capabilities.connectionProviderCache) { + this.capabilities.connectionProviderCache = {}; + } - if (dataProviderExtensions.length > 0) { - // Scrape out disabled extensions + // handle in case some extensions have already registered (unlikley) + Object.entries(connectionRegistry.providers).map(v => { + this.handleConnectionProvider({ id: v[0], properties: v[1] }); + }); + // register for when new extensions are added + connectionRegistry.onNewProvider(this.handleConnectionProvider, this); - // @SQLTODO reenable this code - // this.extensionEnablementService.getDisabledExtensions() - // .then(disabledExtensions => { - - // let disabledExtensionsId = disabledExtensions.map(disabledExtension => disabledExtension.id); - // dataProviderExtensions = dataProviderExtensions.filter(extension => - // disabledExtensions.indexOf(getGalleryExtensionId(extension.manifest.publisher, extension.manifest.name)) < 0); - - - // // return extensions.map(extension => { - // // return { - // // identifier: { id: adoptToGalleryExtensionId(stripVersion(extension.identifier.id)), uuid: extension.identifier.uuid }, - // // local: extension, - // // globallyEnabled: disabledExtensions.every(disabled => !areSameExtensions(disabled, extension.identifier)) - // // }; - // // }); - // }); - - - // const disabledExtensions = this.extensionEnablementService.getGloballyDisabledExtensions() - // .map(disabledExtension => disabledExtension.id); - // dataProviderExtensions = dataProviderExtensions.filter(extension => - // disabledExtensions.indexOf(getGalleryExtensionId(extension.manifest.publisher, extension.manifest.name)) < 0); - } - - this._expectedCapabilitiesCount += dataProviderExtensions.length; + // handle adding already known capabilities (could have caching problems) + Object.entries(this.capabilities.connectionProviderCache).map(v => { + this.handleConnectionProvider({ id: v[0], properties: v[1] }, false); }); } - public onCapabilitiesReady(): Promise { - return this._onCapabilitiesReady.promise; + private handleConnectionProvider(e: { id: string, properties: ConnectionProviderProperties }, isNew = true): void { + + let provider = this._providers.get(e.id); + if (provider) { + provider.connection = e.properties; + } else { + provider = { + connection: e.properties + }; + this._providers.set(e.id, provider); + } + if (!this._featureUpdateEvents.has(e.id)) { + this._featureUpdateEvents.set(e.id, new Emitter()); + } + + if (isNew) { + this.capabilities.connectionProviderCache[e.id] = e.properties; + this._onCapabilitiesRegistered.fire(provider); + } } /** * Retrieve a list of registered server capabilities */ - public getCapabilities(provider: string): sqlops.DataProtocolServerCapabilities { - return this.capabilities[provider]; + public getCapabilities(provider: string): ProviderFeatures { + return this._providers.get(provider); } - public get providers(): string[] { - return Object.keys(this.capabilities); + public getLegacyCapabilities(provider: string): sqlops.DataProtocolServerCapabilities { + return this._legacyProviders.get(provider); } - private get capabilities(): IProtocolMomento { - return this._momento.getMemento(this._storageService) as IProtocolMomento; + public get providers(): { [id: string]: ProviderFeatures } { + return toObject(this._providers); + } + + private get capabilities(): CapabilitiesMomento { + return this._momento.getMemento(this._storageService) as CapabilitiesMomento; } /** @@ -170,20 +169,10 @@ export class CapabilitiesService extends Disposable implements ICapabilitiesServ public registerProvider(provider: sqlops.CapabilitiesProvider): void { // request the capabilities from server provider.getServerCapabilities(clientCapabilities).then(serverCapabilities => { - this.capabilities[serverCapabilities.providerName] = serverCapabilities; - this._momento.saveMemento(); - this._onCapabilitiesRegistered.fire(serverCapabilities.providerName); - this._registeredCapabilities++; - this.resolveCapabilitiesIfReady(); + this._legacyProviders.set(serverCapabilities.providerName, serverCapabilities); }); } - private resolveCapabilitiesIfReady(): void { - if (this._registeredCapabilities === this._expectedCapabilitiesCount) { - this._onCapabilitiesReady.resolve(); - } - } - /** * Returns true if the feature is available for given connection * @param featureComponent a component which should have the feature name @@ -214,6 +203,9 @@ export class CapabilitiesService extends Disposable implements ICapabilitiesServ } else { return true; } + } + public shutdown(): void { + this._momento.saveMemento(); } } diff --git a/src/sql/services/serialization/serializationService.ts b/src/sql/services/serialization/serializationService.ts index 742699eb1a..f48b3a062e 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(providerId); + let providerCapabilities = this._capabilitiesService.getLegacyCapabilities(providerId); if (providerCapabilities) { return providerCapabilities.features.find(f => f.featureName === SERVICE_ID); diff --git a/src/sql/workbench/parts/connection/common/connectionProviderExtension.ts b/src/sql/workbench/parts/connection/common/connectionProviderExtension.ts new file mode 100644 index 0000000000..14508c00f9 --- /dev/null +++ b/src/sql/workbench/parts/connection/common/connectionProviderExtension.ts @@ -0,0 +1,134 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Registry } from 'vs/platform/registry/common/platform'; +import { IExtensionPointUser, ExtensionsRegistry } from 'vs/platform/extensions/common/extensionsRegistry'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import { localize } from 'vs/nls'; +import Event, { Emitter } from 'vs/base/common/event'; +import { deepClone } from 'vs/base/common/objects'; + +import * as sqlops from 'sqlops'; + +export interface ConnectionProviderProperties { + providerId: string; + displayName: string; + connectionOptions: sqlops.ConnectionOption[]; +} + +export const Extensions = { + ConnectionProviderContributions: 'connection.providers' +}; + +export interface IConnectionProviderRegistry { + registerConnectionProvider(id: string, properties: ConnectionProviderProperties): void; + getProperties(id: string): ConnectionProviderProperties; + readonly onNewProvider: Event<{ id: string, properties: ConnectionProviderProperties }>; + readonly providers: { [id: string]: ConnectionProviderProperties }; +} + +class ConnectionProviderRegistryImpl implements IConnectionProviderRegistry { + private _providers = new Map(); + private _onNewProvider = new Emitter<{ id: string, properties: ConnectionProviderProperties }>(); + public readonly onNewProvider: Event<{ id: string, properties: ConnectionProviderProperties }> = this._onNewProvider.event; + + public registerConnectionProvider(id: string, properties: ConnectionProviderProperties): void { + this._providers.set(id, properties); + this._onNewProvider.fire({ id, properties }); + } + + public getProperties(id: string): ConnectionProviderProperties { + return this._providers.get(id); + } + + public get providers(): { [id: string]: ConnectionProviderProperties } { + let rt: { [id: string]: ConnectionProviderProperties } = {}; + this._providers.forEach((v, k) => { + rt[k] = deepClone(v); + }); + return rt; + } +} + +const connectionRegistry = new ConnectionProviderRegistryImpl(); +Registry.add(Extensions.ConnectionProviderContributions, connectionRegistry); + +const ConnectionProviderContrib: IJSONSchema = { + type: 'object', + properties: { + providerId: { + type: 'string', + description: localize('schema.providerId', "Common id for the provider") + }, + displayName: { + type: 'string', + description: localize('schema.displayName', "Display Name for the provider") + }, + connectionOptions: { + type: 'array', + description: localize('schema.connectionOptions', "Options for connection"), + items: { + type: 'object', + properties: { + specialValueType: { + type: 'string' + }, + isIdentity: { + type: 'boolean' + }, + name: { + type: 'string' + }, + displayName: { + type: 'string' + }, + description: { + type: 'string' + }, + groupName: { + type: 'string' + }, + valueType: { + type: 'string' + }, + defaultValue: { + type: 'any' + }, + objectType: { + type: 'any' + }, + categoryValues: { + type: 'any' + }, + isRequired: { + type: 'boolean' + }, + isArray: { + type: 'bolean' + } + } + } + } + }, + required: ['providerId'] +}; + +ExtensionsRegistry.registerExtensionPoint('connectionProvider', [], ConnectionProviderContrib).setHandler(extensions => { + + function handleCommand(contrib: ConnectionProviderProperties, extension: IExtensionPointUser) { + connectionRegistry.registerConnectionProvider(contrib.providerId, contrib); + } + + for (let extension of extensions) { + const { value } = extension; + if (Array.isArray(value)) { + for (let command of value) { + handleCommand(command, extension); + } + } else { + handleCommand(value, extension); + } + } +}); diff --git a/src/sqltest/parts/connection/connectionManagementService.test.ts b/src/sqltest/parts/connection/connectionManagementService.test.ts index 7bc8118f11..3815406417 100644 --- a/src/sqltest/parts/connection/connectionManagementService.test.ts +++ b/src/sqltest/parts/connection/connectionManagementService.test.ts @@ -32,7 +32,8 @@ import { WorkspaceConfigurationTestService } from 'sqltest/stubs/workspaceConfig import * as assert from 'assert'; import * as TypeMoq from 'typemoq'; -import { IConnectionProfileGroup } from 'sql/parts/connection/common/connectionProfileGroup'; +import { IConnectionProfileGroup, ConnectionProfileGroup } from 'sql/parts/connection/common/connectionProfileGroup'; +import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile'; suite('SQL ConnectionManagementService tests', () => { @@ -86,6 +87,8 @@ suite('SQL ConnectionManagementService tests', () => { mssqlConnectionProvider = TypeMoq.Mock.ofType(ConnectionProviderStub); let resourceProviderStub = new ResourceProviderStub(); resourceProviderStubMock = TypeMoq.Mock.ofInstance(resourceProviderStub); + let root = new ConnectionProfileGroup(ConnectionProfileGroup.RootGroupName, undefined, ConnectionProfileGroup.RootGroupName, undefined, undefined); + root.connections = [ConnectionProfile.fromIConnectionProfile(capabilitiesService, connectionProfile)]; connectionDialogService.setup(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), undefined)).returns(() => TPromise.as(none)); connectionDialogService.setup(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), undefined, undefined)).returns(() => TPromise.as(none)); @@ -105,6 +108,7 @@ suite('SQL ConnectionManagementService tests', () => { c => c.serverName === connectionProfileWithEmptyUnsavedPassword.serverName))).returns( () => Promise.resolve({ profile: connectionProfileWithEmptyUnsavedPassword, savedCred: false })); connectionStore.setup(x => x.isPasswordRequired(TypeMoq.It.isAny())).returns(() => true); + connectionStore.setup(x => x.getConnectionProfileGroups()).returns(() => [root]); mssqlConnectionProvider.setup(x => x.connect(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => undefined); diff --git a/src/sqltest/parts/connection/connectionProfile.test.ts b/src/sqltest/parts/connection/connectionProfile.test.ts index 046f96942f..4ab6fdfc95 100644 --- a/src/sqltest/parts/connection/connectionProfile.test.ts +++ b/src/sqltest/parts/connection/connectionProfile.test.ts @@ -12,9 +12,10 @@ import * as sqlops from 'sqlops'; import * as assert from 'assert'; import { ConnectionOptionSpecialType } from 'sql/workbench/api/common/sqlExtHostTypes'; import { CapabilitiesTestService } from 'sqltest/stubs/capabilitiesTestService'; +import { ConnectionProviderProperties } from 'sql/workbench/parts/connection/common/connectionProviderExtension'; suite('SQL ConnectionProfileInfo tests', () => { - let msSQLCapabilities: sqlops.DataProtocolServerCapabilities; + let msSQLCapabilities: ConnectionProviderProperties; let capabilitiesService: CapabilitiesTestService; let connectionProfile: IConnectionProfile = { @@ -49,82 +50,75 @@ suite('SQL ConnectionProfileInfo tests', () => { }; setup(() => { - let capabilities: sqlops.DataProtocolServerCapabilities[] = []; - let connectionProvider: sqlops.ConnectionProviderOptions = { - options: [ - { - name: 'serverName', - displayName: undefined, - description: undefined, - groupName: undefined, - categoryValues: undefined, - defaultValue: undefined, - isIdentity: true, - isRequired: true, - specialValueType: ConnectionOptionSpecialType.serverName, - valueType: 0 - }, - { - name: 'databaseName', - displayName: undefined, - description: undefined, - groupName: undefined, - categoryValues: undefined, - defaultValue: undefined, - isIdentity: true, - isRequired: true, - specialValueType: ConnectionOptionSpecialType.databaseName, - valueType: 0 - }, - { - name: 'userName', - displayName: undefined, - description: undefined, - groupName: undefined, - categoryValues: undefined, - defaultValue: undefined, - isIdentity: true, - isRequired: true, - specialValueType: ConnectionOptionSpecialType.userName, - valueType: 0 - }, - { - name: 'authenticationType', - displayName: undefined, - description: undefined, - groupName: undefined, - categoryValues: undefined, - defaultValue: undefined, - isIdentity: true, - isRequired: true, - specialValueType: ConnectionOptionSpecialType.authType, - valueType: 0 - }, - { - name: 'password', - displayName: undefined, - description: undefined, - groupName: undefined, - categoryValues: undefined, - defaultValue: undefined, - isIdentity: true, - isRequired: true, - specialValueType: ConnectionOptionSpecialType.password, - valueType: 0 - } - ] - }; + let connectionProvider: sqlops.ConnectionOption[] = [ + { + name: 'serverName', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: ConnectionOptionSpecialType.serverName, + valueType: 0 + }, + { + name: 'databaseName', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: ConnectionOptionSpecialType.databaseName, + valueType: 0 + }, + { + name: 'userName', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: ConnectionOptionSpecialType.userName, + valueType: 0 + }, + { + name: 'authenticationType', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: ConnectionOptionSpecialType.authType, + valueType: 0 + }, + { + name: 'password', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: ConnectionOptionSpecialType.password, + valueType: 0 + } + ]; msSQLCapabilities = { - protocolVersion: '1', - providerName: 'MSSQL', - providerDisplayName: 'MSSQL', - connectionProvider: connectionProvider, - adminServicesProvider: undefined, - features: undefined + providerId: 'MSSQL', + displayName: 'MSSQL', + connectionOptions: connectionProvider }; - capabilities.push(msSQLCapabilities); capabilitiesService = new CapabilitiesTestService(); - capabilitiesService.capabilities['MSSQL'] = msSQLCapabilities; + capabilitiesService.capabilities['MSSQL'] = { connection: msSQLCapabilities }; }); test('set properties should set the values correctly', () => { diff --git a/src/sqltest/parts/connection/connectionStore.test.ts b/src/sqltest/parts/connection/connectionStore.test.ts index aca98d51b2..17224a6299 100644 --- a/src/sqltest/parts/connection/connectionStore.test.ts +++ b/src/sqltest/parts/connection/connectionStore.test.ts @@ -21,6 +21,7 @@ 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'; +import { ConnectionProviderProperties } from 'sql/workbench/parts/connection/common/connectionProviderExtension'; suite('SQL ConnectionStore tests', () => { let defaultNamedProfile: IConnectionProfile; @@ -33,7 +34,7 @@ suite('SQL ConnectionStore tests', () => { let capabilitiesService: CapabilitiesTestService; let mementoArray: any = []; let maxRecent = 5; - let msSQLCapabilities: sqlops.DataProtocolServerCapabilities; + let msSQLCapabilities: ConnectionProviderProperties; let defaultNamedConnectionProfile: ConnectionProfile; setup(() => { @@ -96,81 +97,74 @@ suite('SQL ConnectionStore tests', () => { }; capabilitiesService = new CapabilitiesTestService(); - let capabilities: { [id: string]: sqlops.DataProtocolServerCapabilities } = {}; - let connectionProvider: sqlops.ConnectionProviderOptions = { - options: [ - { - name: 'serverName', - displayName: undefined, - description: undefined, - groupName: undefined, - categoryValues: undefined, - defaultValue: undefined, - isIdentity: true, - isRequired: true, - specialValueType: ConnectionOptionSpecialType.serverName, - valueType: 0 - }, - { - name: 'databaseName', - displayName: undefined, - description: undefined, - groupName: undefined, - categoryValues: undefined, - defaultValue: undefined, - isIdentity: true, - isRequired: true, - specialValueType: ConnectionOptionSpecialType.databaseName, - valueType: 0 - }, - { - name: 'userName', - displayName: undefined, - description: undefined, - groupName: undefined, - categoryValues: undefined, - defaultValue: undefined, - isIdentity: true, - isRequired: true, - specialValueType: ConnectionOptionSpecialType.userName, - valueType: 0 - }, - { - name: 'authenticationType', - displayName: undefined, - description: undefined, - groupName: undefined, - categoryValues: undefined, - defaultValue: undefined, - isIdentity: true, - isRequired: true, - specialValueType: ConnectionOptionSpecialType.authType, - valueType: 0 - }, - { - name: 'password', - displayName: undefined, - description: undefined, - groupName: undefined, - categoryValues: undefined, - defaultValue: undefined, - isIdentity: true, - isRequired: true, - specialValueType: ConnectionOptionSpecialType.password, - valueType: 0 - } - ] - }; + let connectionProvider: sqlops.ConnectionOption[] = [ + { + name: 'serverName', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: ConnectionOptionSpecialType.serverName, + valueType: 0 + }, + { + name: 'databaseName', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: ConnectionOptionSpecialType.databaseName, + valueType: 0 + }, + { + name: 'userName', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: ConnectionOptionSpecialType.userName, + valueType: 0 + }, + { + name: 'authenticationType', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: ConnectionOptionSpecialType.authType, + valueType: 0 + }, + { + name: 'password', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: ConnectionOptionSpecialType.password, + valueType: 0 + } + ]; msSQLCapabilities = { - protocolVersion: '1', - providerName: 'MSSQL', - providerDisplayName: 'MSSQL', - connectionProvider: connectionProvider, - adminServicesProvider: undefined, - features: undefined + providerId: 'MSSQL', + displayName: 'MSSQL', + connectionOptions: connectionProvider }; - capabilities['MSSQL'] = msSQLCapabilities; - capabilitiesService.capabilities['MSSQL'] = msSQLCapabilities; + capabilitiesService.capabilities['MSSQL'] = { connection: msSQLCapabilities }; let groups: IConnectionProfileGroup[] = [ { id: 'root', @@ -368,24 +362,19 @@ suite('SQL ConnectionStore tests', () => { test('isPasswordRequired should return false if the password is not required in capabilities', () => { let providerName: string = 'providername'; - let connectionProvider: sqlops.ConnectionProviderOptions = { - options: msSQLCapabilities.connectionProvider.options.map(o => { - if (o.name === 'password') { - o.isRequired = false; - } - return o; - }) - }; + let connectionProvider = msSQLCapabilities.connectionOptions.map(o => { + if (o.name === 'password') { + o.isRequired = false; + } + return o; + }); let providerCapabilities = { - protocolVersion: '1', - providerName: providerName, - providerDisplayName: providerName, - connectionProvider: connectionProvider, - adminServicesProvider: undefined, - features: undefined + providerId: providerName, + displayName: providerName, + connectionOptions: connectionProvider }; - capabilitiesService.capabilities[providerName] = providerCapabilities; + capabilitiesService.capabilities[providerName] = { connection: providerCapabilities }; let connectionStore = new ConnectionStore(storageServiceMock.object, context.object, undefined, workspaceConfigurationServiceMock.object, credentialStore.object, capabilitiesService, connectionConfig.object); diff --git a/src/sqltest/parts/connection/connectionTreeActions.test.ts b/src/sqltest/parts/connection/connectionTreeActions.test.ts index 5f4bb5713f..623aae6f2f 100644 --- a/src/sqltest/parts/connection/connectionTreeActions.test.ts +++ b/src/sqltest/parts/connection/connectionTreeActions.test.ts @@ -342,15 +342,12 @@ suite('SQL Connection Tree Action tests', () => { test('RefreshConnectionAction - refresh should be called if connection status is connect', (done) => { let isConnectedReturnValue: boolean = true; let sqlProvider = { - protocolVersion: '1', - providerName: 'MSSQL', - providerDisplayName: 'MSSQL', - connectionProvider: { options: [] }, - adminServicesProvider: { databaseInfoOptions: [], databaseFileInfoOptions: [], fileGroupInfoOptions: [] }, - features: undefined + providerId: 'MSSQL', + displayName: 'MSSQL', + connectionOptions: [], }; - capabilitiesService.capabilities['MSSQL'] = sqlProvider; + capabilitiesService.capabilities['MSSQL'] = { connection: sqlProvider }; var connection = new ConnectionProfile(capabilitiesService, { savePassword: false, @@ -433,15 +430,12 @@ suite('SQL Connection Tree Action tests', () => { test('RefreshConnectionAction - refresh should not be called if connection status is not connect', (done) => { let isConnectedReturnValue: boolean = false; let sqlProvider = { - protocolVersion: '1', - providerName: 'MSSQL', - providerDisplayName: 'MSSQL', - connectionProvider: { options: [] }, - adminServicesProvider: { databaseInfoOptions: [], databaseFileInfoOptions: [], fileGroupInfoOptions: [] }, - features: undefined + providerId: 'MSSQL', + displayName: 'MSSQL', + connectionOptions: [] }; - capabilitiesService.capabilities['MSSQL'] = sqlProvider; + capabilitiesService.capabilities['MSSQL'] = { connection: sqlProvider }; var connection = new ConnectionProfile(capabilitiesService, { savePassword: false, diff --git a/src/sqltest/parts/connection/objectExplorerService.test.ts b/src/sqltest/parts/connection/objectExplorerService.test.ts index ed8ae21380..bb351a7c8b 100644 --- a/src/sqltest/parts/connection/objectExplorerService.test.ts +++ b/src/sqltest/parts/connection/objectExplorerService.test.ts @@ -128,90 +128,86 @@ suite('SQL Object Explorer Service tests', () => { let onCapabilitiesRegistered = new Emitter(); let sqlProvider = { - protocolVersion: '1', - providerName: 'MSSQL', - providerDisplayName: 'MSSQL', - connectionProvider: { - options: [ - { - name: 'serverName', - displayName: undefined, - description: undefined, - groupName: undefined, - categoryValues: undefined, - defaultValue: undefined, - isIdentity: true, - isRequired: true, - specialValueType: ConnectionOptionSpecialType.serverName, - valueType: 0 - }, - { - name: 'databaseName', - displayName: undefined, - description: undefined, - groupName: undefined, - categoryValues: undefined, - defaultValue: undefined, - isIdentity: true, - isRequired: true, - specialValueType: ConnectionOptionSpecialType.databaseName, - valueType: 0 - }, - { - name: 'userName', - displayName: undefined, - description: undefined, - groupName: undefined, - categoryValues: undefined, - defaultValue: undefined, - isIdentity: true, - isRequired: true, - specialValueType: ConnectionOptionSpecialType.userName, - valueType: 0 - }, - { - name: 'authenticationType', - displayName: undefined, - description: undefined, - groupName: undefined, - categoryValues: undefined, - defaultValue: undefined, - isIdentity: true, - isRequired: true, - specialValueType: ConnectionOptionSpecialType.authType, - valueType: 0 - }, - { - name: 'password', - displayName: undefined, - description: undefined, - groupName: undefined, - categoryValues: undefined, - defaultValue: undefined, - isIdentity: true, - isRequired: true, - specialValueType: ConnectionOptionSpecialType.password, - valueType: 0 - }, - { - name: 'encrypt', - displayName: undefined, - description: undefined, - groupName: undefined, - categoryValues: undefined, - defaultValue: undefined, - isIdentity: false, - isRequired: false, - specialValueType: undefined, - valueType: 0 - }] - }, - adminServicesProvider: { databaseInfoOptions: [], databaseFileInfoOptions: [], fileGroupInfoOptions: [] }, - features: undefined + providerId: 'MSSQL', + displayName: 'MSSQL', + connectionOptions: [ + { + name: 'serverName', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: ConnectionOptionSpecialType.serverName, + valueType: 0 + }, + { + name: 'databaseName', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: ConnectionOptionSpecialType.databaseName, + valueType: 0 + }, + { + name: 'userName', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: ConnectionOptionSpecialType.userName, + valueType: 0 + }, + { + name: 'authenticationType', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: ConnectionOptionSpecialType.authType, + valueType: 0 + }, + { + name: 'password', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: ConnectionOptionSpecialType.password, + valueType: 0 + }, + { + name: 'encrypt', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: false, + isRequired: false, + specialValueType: undefined, + valueType: 0 + } + ] }; let capabilitiesService = new CapabilitiesTestService(); - capabilitiesService.capabilities['MSSQL'] = sqlProvider; + capabilitiesService.capabilities['MSSQL'] = { connection: sqlProvider }; connection = new ConnectionProfile(capabilitiesService, { savePassword: false, diff --git a/src/sqltest/parts/connection/providerConnectionInfo.test.ts b/src/sqltest/parts/connection/providerConnectionInfo.test.ts index 6f1568f2c8..5b57f38ab5 100644 --- a/src/sqltest/parts/connection/providerConnectionInfo.test.ts +++ b/src/sqltest/parts/connection/providerConnectionInfo.test.ts @@ -15,7 +15,7 @@ import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesServ import { CapabilitiesTestService } from 'sqltest/stubs/capabilitiesTestService'; suite('SQL ProviderConnectionInfo tests', () => { - let msSQLCapabilities: sqlops.DataProtocolServerCapabilities; + let msSQLCapabilities: any; let capabilitiesService: CapabilitiesTestService; let connectionProfile: IConnectionProfile = { @@ -37,93 +37,88 @@ suite('SQL ProviderConnectionInfo tests', () => { setup(() => { let capabilities: sqlops.DataProtocolServerCapabilities[] = []; - let connectionProvider: sqlops.ConnectionProviderOptions = { - options: [ - { - name: 'serverName', - displayName: undefined, - description: undefined, - groupName: undefined, - categoryValues: undefined, - defaultValue: undefined, - isIdentity: true, - isRequired: true, - specialValueType: ConnectionOptionSpecialType.serverName, - valueType: 0 - }, - { - name: 'databaseName', - displayName: undefined, - description: undefined, - groupName: undefined, - categoryValues: undefined, - defaultValue: undefined, - isIdentity: true, - isRequired: true, - specialValueType: ConnectionOptionSpecialType.databaseName, - valueType: 0 - }, - { - name: 'userName', - displayName: undefined, - description: undefined, - groupName: undefined, - categoryValues: undefined, - defaultValue: undefined, - isIdentity: true, - isRequired: true, - specialValueType: ConnectionOptionSpecialType.userName, - valueType: 0 - }, - { - name: 'authenticationType', - displayName: undefined, - description: undefined, - groupName: undefined, - categoryValues: undefined, - defaultValue: undefined, - isIdentity: true, - isRequired: true, - specialValueType: ConnectionOptionSpecialType.authType, - valueType: 0 - }, - { - name: 'password', - displayName: undefined, - description: undefined, - groupName: undefined, - categoryValues: undefined, - defaultValue: undefined, - isIdentity: true, - isRequired: true, - specialValueType: ConnectionOptionSpecialType.password, - valueType: 0 - }, - { - name: 'encrypt', - displayName: undefined, - description: undefined, - groupName: undefined, - categoryValues: undefined, - defaultValue: undefined, - isIdentity: false, - isRequired: false, - specialValueType: undefined, - valueType: 0 - } - ] - }; + let connectionProvider: sqlops.ConnectionOption[] = [ + { + name: 'serverName', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: ConnectionOptionSpecialType.serverName, + valueType: 0 + }, + { + name: 'databaseName', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: ConnectionOptionSpecialType.databaseName, + valueType: 0 + }, + { + name: 'userName', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: ConnectionOptionSpecialType.userName, + valueType: 0 + }, + { + name: 'authenticationType', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: ConnectionOptionSpecialType.authType, + valueType: 0 + }, + { + name: 'password', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: ConnectionOptionSpecialType.password, + valueType: 0 + }, + { + name: 'encrypt', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: false, + isRequired: false, + specialValueType: undefined, + valueType: 0 + } + ]; msSQLCapabilities = { - protocolVersion: '1', - providerName: 'MSSQL', - providerDisplayName: 'MSSQL', - connectionProvider: connectionProvider, - adminServicesProvider: undefined, - features: undefined + providerId: 'MSSQL', + displayName: 'MSSQL', + connectionOptions: connectionProvider, }; capabilities.push(msSQLCapabilities); capabilitiesService = new CapabilitiesTestService(); - capabilitiesService.capabilities['MSSQL'] = msSQLCapabilities; + capabilitiesService.capabilities['MSSQL'] = { connection: msSQLCapabilities }; }); test('constructor should accept undefined parameters', () => { diff --git a/src/sqltest/stubs/capabilitiesTestService.ts b/src/sqltest/stubs/capabilitiesTestService.ts index 2a4f13ffbb..161478e0d7 100644 --- a/src/sqltest/stubs/capabilitiesTestService.ts +++ b/src/sqltest/stubs/capabilitiesTestService.ts @@ -6,7 +6,7 @@ 'use strict'; import * as sqlops from 'sqlops'; import { ConnectionManagementInfo } from 'sql/parts/connection/common/connectionManagementInfo'; -import { ICapabilitiesService, clientCapabilities } from 'sql/services/capabilities/capabilitiesService'; +import { ICapabilitiesService, clientCapabilities, ProviderFeatures } from 'sql/services/capabilities/capabilitiesService'; import { ConnectionOptionSpecialType } from 'sql/workbench/api/common/sqlExtHostTypes'; import Event, { Emitter } from 'vs/base/common/event'; @@ -18,95 +18,94 @@ export class CapabilitiesTestService implements ICapabilitiesService { private _providers: sqlops.CapabilitiesProvider[] = []; - public capabilities: { [id: string]: sqlops.DataProtocolServerCapabilities } = {}; + public capabilities: { [id: string]: ProviderFeatures } = {}; constructor() { - let connectionProvider: sqlops.ConnectionProviderOptions = { - options: [ - { - name: 'serverName', - displayName: undefined, - description: undefined, - groupName: undefined, - categoryValues: undefined, - defaultValue: undefined, - isIdentity: true, - isRequired: true, - specialValueType: ConnectionOptionSpecialType.serverName, - valueType: 0 - }, - { - name: 'databaseName', - displayName: undefined, - description: undefined, - groupName: undefined, - categoryValues: undefined, - defaultValue: undefined, - isIdentity: true, - isRequired: true, - specialValueType: ConnectionOptionSpecialType.databaseName, - valueType: 0 - }, - { - name: 'userName', - displayName: undefined, - description: undefined, - groupName: undefined, - categoryValues: undefined, - defaultValue: undefined, - isIdentity: true, - isRequired: true, - specialValueType: ConnectionOptionSpecialType.userName, - valueType: 0 - }, - { - name: 'authenticationType', - displayName: undefined, - description: undefined, - groupName: undefined, - categoryValues: undefined, - defaultValue: undefined, - isIdentity: true, - isRequired: true, - specialValueType: ConnectionOptionSpecialType.authType, - valueType: 0 - }, - { - name: 'password', - displayName: undefined, - description: undefined, - groupName: undefined, - categoryValues: undefined, - defaultValue: undefined, - isIdentity: true, - isRequired: true, - specialValueType: ConnectionOptionSpecialType.password, - valueType: 0 - } - ] - }; + let connectionProvider: sqlops.ConnectionOption[] = [ + { + name: 'serverName', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: ConnectionOptionSpecialType.serverName, + valueType: 0 + }, + { + name: 'databaseName', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: ConnectionOptionSpecialType.databaseName, + valueType: 0 + }, + { + name: 'userName', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: ConnectionOptionSpecialType.userName, + valueType: 0 + }, + { + name: 'authenticationType', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: ConnectionOptionSpecialType.authType, + valueType: 0 + }, + { + name: 'password', + displayName: undefined, + description: undefined, + groupName: undefined, + categoryValues: undefined, + defaultValue: undefined, + isIdentity: true, + isRequired: true, + specialValueType: ConnectionOptionSpecialType.password, + valueType: 0 + } + ]; let msSQLCapabilities = { - protocolVersion: '1', - providerName: 'MSSQL', - providerDisplayName: 'MSSQL', - connectionProvider: connectionProvider, - adminServicesProvider: undefined, - features: undefined + providerId: 'MSSQL', + displayName: 'MSSQL', + connectionOptions: connectionProvider, }; - this.capabilities['MSSQL'] = msSQLCapabilities; + this.capabilities['MSSQL'] = { connection: msSQLCapabilities }; } /** * Retrieve a list of registered server capabilities */ - public getCapabilities(provider: string): sqlops.DataProtocolServerCapabilities { + public getCapabilities(provider: string): ProviderFeatures { return this.capabilities[provider]; } - public get providers(): string[] { - return Object.keys(this.capabilities); + public getLegacyCapabilities(provider: string): sqlops.DataProtocolServerCapabilities { + throw new Error("Method not implemented."); + } + + public get providers(): { [id: string]: ProviderFeatures } { + return this.capabilities; } /** @@ -129,7 +128,7 @@ export class CapabilitiesTestService implements ICapabilitiesService { return Promise.resolve(null); } - private _onCapabilitiesRegistered = new Emitter(); + private _onCapabilitiesRegistered = new Emitter(); public readonly onCapabilitiesRegistered = this._onCapabilitiesRegistered.event; } diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts index 440ca23a8c..4c5cdd26bf 100644 --- a/src/vs/workbench/electron-browser/workbench.ts +++ b/src/vs/workbench/electron-browser/workbench.ts @@ -684,7 +684,8 @@ export class Workbench implements IPartService { serviceCollection.set(INewDashboardTabDialogService, this.instantiationService.createInstance(NewDashboardTabDialogService)); serviceCollection.set(ISqlOAuthService, this.instantiationService.createInstance(SqlOAuthService)); serviceCollection.set(sqlIClipboardService, this.instantiationService.createInstance(sqlClipboardService)); - serviceCollection.set(ICapabilitiesService, this.instantiationService.createInstance(CapabilitiesService)); + let capabilitiesService = this.instantiationService.createInstance(CapabilitiesService); + serviceCollection.set(ICapabilitiesService, capabilitiesService); serviceCollection.set(IErrorMessageService, this.instantiationService.createInstance(ErrorMessageService)); serviceCollection.set(IConnectionDialogService, this.instantiationService.createInstance(ConnectionDialogService)); serviceCollection.set(IServerGroupController, this.instantiationService.createInstance(ServerGroupController)); @@ -719,6 +720,7 @@ export class Workbench implements IPartService { this.toDispose.push(connectionManagementService); this.toShutdown.push(connectionManagementService); this.toShutdown.push(accountManagementService); + this.toShutdown.push(capabilitiesService); // Contributed services const contributedServices = getServices();