diff --git a/src/sql/sqlops.proposed.d.ts b/src/sql/sqlops.proposed.d.ts index 6c8897d603..689f584165 100644 --- a/src/sql/sqlops.proposed.d.ts +++ b/src/sql/sqlops.proposed.d.ts @@ -697,4 +697,35 @@ declare module 'sqlops' { */ readonly retainContextWhenHidden?: boolean; } + + export enum DataProviderType { + ConnectionProvider = 'ConnectionProvider', + BackupProvider = 'BackupProvider', + RestoreProvider = 'RestoreProvider', + ScriptingProvider = 'ScriptingProvider', + ObjectExplorerProvider = 'ObjectExplorerProvider', + TaskServicesProvider = 'TaskServicesProvider', + FileBrowserProvider = 'FileBrowserProvider', + ProfilerProvider = 'ProfilerProvider', + MetadataProvider = 'MetadataProvider', + QueryProvider = 'QueryProvider', + AdminServicesProvider = 'AdminServicesProvider', + AgentServicesProvider = 'AgentServicesProvider', + CapabilitiesProvider = 'CapabilitiesProvider' + } + + export namespace dataprotocol { + /** + * Get the provider corresponding to the given provider ID and type + * @param providerId The ID that the provider was registered with + * @param providerType The type of the provider + */ + export function getProvider(providerId: string, providerType: DataProviderType): T; + + /** + * Get all registered providers of the given type + * @param providerType The type of the providers + */ + export function getProvidersByType(providerType: DataProviderType): T[]; + } } diff --git a/src/sql/workbench/api/common/sqlExtHostTypes.ts b/src/sql/workbench/api/common/sqlExtHostTypes.ts index 4bb345bdd1..ca6fa2aa60 100644 --- a/src/sql/workbench/api/common/sqlExtHostTypes.ts +++ b/src/sql/workbench/api/common/sqlExtHostTypes.ts @@ -166,3 +166,18 @@ export interface ActionDescriptor { callbackData?: any; } +export enum DataProviderType { + ConnectionProvider = 'ConnectionProvider', + BackupProvider = 'BackupProvider', + RestoreProvider = 'RestoreProvider', + ScriptingProvider = 'ScriptingProvider', + ObjectExplorerProvider = 'ObjectExplorerProvider', + TaskServicesProvider = 'TaskServicesProvider', + FileBrowserProvider = 'FileBrowserProvider', + ProfilerProvider = 'ProfilerProvider', + MetadataProvider = 'MetadataProvider', + QueryProvider = 'QueryProvider', + AdminServicesProvider = 'AdminServicesProvider', + AgentServicesProvider = 'AgentServicesProvider', + CapabilitiesProvider = 'CapabilitiesProvider' +} diff --git a/src/sql/workbench/api/node/extHostDataProtocol.ts b/src/sql/workbench/api/node/extHostDataProtocol.ts index 4031d415c2..7c7e9527ac 100644 --- a/src/sql/workbench/api/node/extHostDataProtocol.ts +++ b/src/sql/workbench/api/node/extHostDataProtocol.ts @@ -4,12 +4,13 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import Event, { Emitter } from 'vs/base/common/event'; -import { IMainContext } from 'vs/workbench/api/node/extHost.protocol'; -import { SqlMainContext, MainThreadDataProtocolShape, ExtHostDataProtocolShape } from 'sql/workbench/api/node/sqlExtHost.protocol'; import * as vscode from 'vscode'; import * as sqlops from 'sqlops'; +import Event, { Emitter } from 'vs/base/common/event'; +import { IMainContext } from 'vs/workbench/api/node/extHost.protocol'; import { Disposable } from 'vs/workbench/api/node/extHostTypes'; +import { SqlMainContext, MainThreadDataProtocolShape, ExtHostDataProtocolShape } from 'sql/workbench/api/node/sqlExtHost.protocol'; +import { DataProviderType } from 'sql/workbench/api/common/sqlExtHostTypes'; export class ExtHostDataProtocol extends ExtHostDataProtocolShape { @@ -21,6 +22,7 @@ export class ExtHostDataProtocol extends ExtHostDataProtocolShape { private static _handlePool: number = 0; private _adapter = new Map(); + private _providersByType = new Map(); constructor( mainContext: IMainContext @@ -49,86 +51,106 @@ export class ExtHostDataProtocol extends ExtHostDataProtocolShape { } } - private registerProvider(provider: sqlops.DataProvider): vscode.Disposable { + private registerProvider(provider: sqlops.DataProvider, providerType: DataProviderType): vscode.Disposable { provider.handle = this._nextHandle(); this._adapter.set(provider.handle, provider); + let providersForType = this._providersByType.get(providerType); + if (!providersForType) { + providersForType = [provider]; + } else { + providersForType.push(provider); + } + this._providersByType.set(providerType, providersForType); return this._createDisposable(provider.handle); } + public getProvider(providerId: string, providerType: sqlops.DataProviderType): T { + let providersForType = this._providersByType.get(providerType); + if (!providersForType) { + return undefined; + } + return providersForType.find(provider => provider.providerId === providerId) as T; + } + + public getProvidersByType(providerType: sqlops.DataProviderType): T[] { + let providersForType = this._providersByType.get(providerType); + return (providersForType || []) as T[]; + } + $registerConnectionProvider(provider: sqlops.ConnectionProvider): vscode.Disposable { - let rt = this.registerProvider(provider); + let rt = this.registerProvider(provider, DataProviderType.ConnectionProvider); this._proxy.$registerConnectionProvider(provider.providerId, provider.handle); return rt; } $registerBackupProvider(provider: sqlops.BackupProvider): vscode.Disposable { - let rt = this.registerProvider(provider); + let rt = this.registerProvider(provider, DataProviderType.BackupProvider); this._proxy.$registerBackupProvider(provider.providerId, provider.handle); return rt; } $registerRestoreProvider(provider: sqlops.RestoreProvider): vscode.Disposable { - let rt = this.registerProvider(provider); + let rt = this.registerProvider(provider, DataProviderType.RestoreProvider); this._proxy.$registerRestoreProvider(provider.providerId, provider.handle); return rt; } $registerScriptingProvider(provider: sqlops.ScriptingProvider): vscode.Disposable { - let rt = this.registerProvider(provider); + let rt = this.registerProvider(provider, DataProviderType.ScriptingProvider); this._proxy.$registerScriptingProvider(provider.providerId, provider.handle); return rt; } $registerQueryProvider(provider: sqlops.QueryProvider): vscode.Disposable { - let rt = this.registerProvider(provider); + let rt = this.registerProvider(provider, DataProviderType.QueryProvider); this._proxy.$registerQueryProvider(provider.providerId, provider.handle); return rt; } $registerMetadataProvider(provider: sqlops.MetadataProvider): vscode.Disposable { - let rt = this.registerProvider(provider); + let rt = this.registerProvider(provider, DataProviderType.MetadataProvider); this._proxy.$registerMetadataProvider(provider.providerId, provider.handle); return rt; } $registerTaskServicesProvider(provider: sqlops.TaskServicesProvider): vscode.Disposable { - let rt = this.registerProvider(provider); + let rt = this.registerProvider(provider, DataProviderType.TaskServicesProvider); this._proxy.$registerTaskServicesProvider(provider.providerId, provider.handle); return rt; } $registerFileBrowserProvider(provider: sqlops.FileBrowserProvider): vscode.Disposable { - let rt = this.registerProvider(provider); + let rt = this.registerProvider(provider, DataProviderType.FileBrowserProvider); this._proxy.$registerFileBrowserProvider(provider.providerId, provider.handle); return rt; } $registerObjectExplorerProvider(provider: sqlops.ObjectExplorerProvider): vscode.Disposable { - let rt = this.registerProvider(provider); + let rt = this.registerProvider(provider, DataProviderType.ObjectExplorerProvider); this._proxy.$registerObjectExplorerProvider(provider.providerId, provider.handle); return rt; } $registerProfilerProvider(provider: sqlops.ProfilerProvider): vscode.Disposable { - let rt = this.registerProvider(provider); + let rt = this.registerProvider(provider, DataProviderType.ProfilerProvider); this._proxy.$registerProfilerProvider(provider.providerId, provider.handle); return rt; } $registerAdminServicesProvider(provider: sqlops.AdminServicesProvider): vscode.Disposable { - let rt = this.registerProvider(provider); + let rt = this.registerProvider(provider, DataProviderType.AdminServicesProvider); this._proxy.$registerAdminServicesProvider(provider.providerId, provider.handle); return rt; } $registerAgentServiceProvider(provider: sqlops.AgentServicesProvider): vscode.Disposable { - let rt = this.registerProvider(provider); + let rt = this.registerProvider(provider, DataProviderType.AgentServicesProvider); this._proxy.$registerAgentServicesProvider(provider.providerId, provider.handle); return rt; } $registerCapabilitiesServiceProvider(provider: sqlops.CapabilitiesProvider): vscode.Disposable { - let rt = this.registerProvider(provider); + let rt = this.registerProvider(provider, DataProviderType.CapabilitiesProvider); this._proxy.$registerCapabilitiesServiceProvider(provider.providerId, provider.handle); return rt; } diff --git a/src/sql/workbench/api/node/sqlExtHost.api.impl.ts b/src/sql/workbench/api/node/sqlExtHost.api.impl.ts index 23565e4585..8cb0329797 100644 --- a/src/sql/workbench/api/node/sqlExtHost.api.impl.ts +++ b/src/sql/workbench/api/node/sqlExtHost.api.impl.ts @@ -281,6 +281,12 @@ export function createApiFactory( registerCapabilitiesServiceProvider, onDidChangeLanguageFlavor(listener: (e: sqlops.DidChangeLanguageFlavorParams) => any, thisArgs?: any, disposables?: extHostTypes.Disposable[]) { return extHostDataProvider.onDidChangeLanguageFlavor(listener, thisArgs, disposables); + }, + getProvider(providerId: string, providerType: sqlops.DataProviderType) { + return extHostDataProvider.getProvider(providerId, providerType); + }, + getProvidersByType(providerType: sqlops.DataProviderType) { + return extHostDataProvider.getProvidersByType(providerType); } }; @@ -362,6 +368,7 @@ export function createApiFactory( resources, serialization, dataprotocol, + DataProviderType: sqlExtHostTypes.DataProviderType, ServiceOptionType: sqlExtHostTypes.ServiceOptionType, ConnectionOptionSpecialType: sqlExtHostTypes.ConnectionOptionSpecialType, EditRowState: sqlExtHostTypes.EditRowState, diff --git a/src/sqltest/workbench/api/extHostDataProtocol.test.ts b/src/sqltest/workbench/api/extHostDataProtocol.test.ts index 253c580c77..9962cb1cd5 100644 --- a/src/sqltest/workbench/api/extHostDataProtocol.test.ts +++ b/src/sqltest/workbench/api/extHostDataProtocol.test.ts @@ -6,105 +6,60 @@ 'use strict'; import * as assert from 'assert'; -import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; -import { setUnexpectedErrorHandler, errorHandler } from 'vs/base/common/errors'; -import URI from 'vs/base/common/uri'; -import * as EditorCommon from 'vs/editor/common/editorCommon'; -import { TextModel as EditorModel } from 'vs/editor/common/model/textModel'; -import { TestRPCProtocol } from 'vs/workbench/test/electron-browser/api/testRPCProtocol'; -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { IMarkerService } from 'vs/platform/markers/common/markers'; -import { MarkerService } from 'vs/platform/markers/common/markerService'; -import { IRPCProtocol } from 'vs/workbench/services/extensions/node/proxyIdentifier'; -import { ExtHostCommands } from 'vs/workbench/api/node/extHostCommands'; -import { MainThreadCommands } from 'vs/workbench/api/electron-browser/mainThreadCommands'; -import { IHeapService } from 'vs/workbench/api/electron-browser/mainThreadHeapService'; -import { ExtHostDocuments } from 'vs/workbench/api/node/extHostDocuments'; -import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/node/extHostDocumentsAndEditors'; -import { DocumentSymbolProviderRegistry } from 'vs/editor/common/modes'; -import { MainContext, ExtHostContext } from 'vs/workbench/api/node/extHost.protocol'; -import { ExtHostDiagnostics } from 'vs/workbench/api/node/extHostDiagnostics'; -import { ExtHostHeapService } from 'vs/workbench/api/node/extHostHeapService'; -import * as vscode from 'vscode'; - +import { Mock } from 'typemoq'; import * as sqlops from 'sqlops'; import { ExtHostDataProtocol } from 'sql/workbench/api/node/extHostDataProtocol'; -import { SqlExtHostContext } from 'sql/workbench/api/node/sqlExtHost.protocol'; +import { DataProviderType } from 'sql/workbench/api/common/sqlExtHostTypes'; +import { ProxyAuthHandler } from 'vs/code/electron-main/auth'; +import { MainThreadDataProtocolShape } from 'sql/workbench/api/node/sqlExtHost.protocol'; -const IRPCProtocol = createDecorator('rpcProtocol'); +suite('ExtHostDataProtocol', () => { -const model = EditorModel.createFromString( - [ - 'This is the first line', - 'This is the second line', - 'This is the third line', - ].join('\n'), - undefined, - undefined, - URI.parse('far://testing/file.a')); + let extHostDataProtocol: ExtHostDataProtocol; -let extHost: ExtHostDataProtocol; -let disposables: vscode.Disposable[] = []; -let threadService: TestRPCProtocol; -let originalErrorHandler: (e: any) => any; - -suite('ExtHostDataProtocol', function () { - - suiteSetup(() => { - - // threadService = new TestThreadService(); - // let instantiationService = new TestInstantiationService(); - // instantiationService.stub(IThreadService, threadService); - // instantiationService.stub(IMarkerService, MarkerService); - // instantiationService.stub(IHeapService, { - // _serviceBrand: undefined, - // trackRecursive(args) { - // // nothing - // return args; - // } - // }); - - // originalErrorHandler = errorHandler.getUnexpectedErrorHandler(); - // setUnexpectedErrorHandler(() => { }); - - // const extHostDocumentsAndEditors = new ExtHostDocumentsAndEditors(threadService); - // extHostDocumentsAndEditors.$acceptDocumentsAndEditorsDelta({ - // addedDocuments: [{ - // isDirty: false, - // versionId: model.getVersionId(), - // modeId: model.getLanguageIdentifier().language, - // url: model.uri, - // lines: model.getValue().split(model.getEOL()), - // EOL: model.getEOL(), - // }] - // }); - // const extHostDocuments = new ExtHostDocuments(threadService, extHostDocumentsAndEditors); - // threadService.set(ExtHostContext.ExtHostDocuments, extHostDocuments); - - // const heapService = new ExtHostHeapService(); - - // const commands = new ExtHostCommands(threadService, heapService); - // threadService.set(ExtHostContext.ExtHostCommands, commands); - // threadService.setTestInstance(MainContext.MainThreadCommands, instantiationService.createInstance(MainThreadCommands)); - - // const diagnostics = new ExtHostDiagnostics(threadService); - // threadService.set(ExtHostContext.ExtHostDiagnostics, diagnostics); - - // extHost = new ExtHostDataProtocol(threadService); - // threadService.set(SqlExtHostContext.ExtHostDataProtocol, extHost); + setup(() => { + extHostDataProtocol = new ExtHostDataProtocol({ + getProxy: identifier => { + return { + $registerMetadataProvider: (providerId, handle) => Promise.resolve(), + $registerConnectionProvider: (providerId, handle) => Promise.resolve() + } as any; + } + } as any); }); - suiteTeardown(() => { - // setUnexpectedErrorHandler(originalErrorHandler); - // model.dispose(); - }); + test('Providers are exposed to other extensions', () => { + let extension1Id = 'provider1'; + let extension1MetadataMock = Mock.ofInstance({ + getMetadata: () => undefined, + getDatabases: () => undefined, + getTableInfo: () => undefined, + getViewInfo: () => undefined, + providerId: extension1Id + } as sqlops.MetadataProvider); - teardown(function () { - // while (disposables.length) { - // disposables.pop().dispose(); - // } - // return threadService.sync(); - }); + let extension2Id = 'provider2'; + let extension2MetadataMock = Mock.ofInstance({ + getMetadata: () => undefined, + getDatabases: () => undefined, + getTableInfo: () => undefined, + getViewInfo: () => undefined, + providerId: extension2Id + } as sqlops.MetadataProvider); - // --- outline + // If I register both providers and then get them using the getProvider API + extHostDataProtocol.$registerMetadataProvider(extension1MetadataMock.object); + extHostDataProtocol.$registerMetadataProvider(extension2MetadataMock.object); + extHostDataProtocol.$registerConnectionProvider({} as sqlops.ConnectionProvider); + let retrievedProvider1 = extHostDataProtocol.getProvider(extension1Id, DataProviderType.MetadataProvider); + let retrievedProvider2 = extHostDataProtocol.getProvider(extension2Id, DataProviderType.MetadataProvider); + let allProviders = extHostDataProtocol.getProvidersByType(DataProviderType.MetadataProvider); + + // Then each provider was retrieved successfully + assert.equal(retrievedProvider1, extension1MetadataMock.object, 'Expected metadata provider was not retrieved for extension 1'); + assert.equal(retrievedProvider2, extension2MetadataMock.object, 'Expected metadata provider was not retrieved for extension 2'); + assert.equal(allProviders.length, 2, 'All metadata providers had unexpected length'); + assert.equal(allProviders.some(provider => provider === extension1MetadataMock.object), true, 'All metadata providers did not include extension 1 metadata provider'); + assert.equal(allProviders.some(provider => provider === extension2MetadataMock.object), true, 'All metadata providers did not include extension 2 metadata provider'); + }); });