mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-17 01:25:36 -05:00
* Merge from vscode 37cb23d3dd4f9433d56d4ba5ea3203580719a0bd * fix issues with merges * bump node version in azpipe * replace license headers * remove duplicate launch task * fix build errors * fix build errors * fix tslint issues * working through package and linux build issues * more work * wip * fix packaged builds * working through linux build errors * wip * wip * wip * fix mac and linux file limits * iterate linux pipeline * disable editor typing * revert series to parallel * remove optimize vscode from linux * fix linting issues * revert testing change * add work round for new node * readd packaging for extensions * fix issue with angular not resolving decorator dependencies
247 lines
8.4 KiB
TypeScript
247 lines
8.4 KiB
TypeScript
/*---------------------------------------------------------------------------------------------
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
*--------------------------------------------------------------------------------------------*/
|
|
|
|
'use strict';
|
|
|
|
import { ConnectionManagementInfo } from 'sql/platform/connection/common/connectionManagementInfo';
|
|
import * as Constants from 'sql/common/constants';
|
|
import { ConnectionProviderProperties, IConnectionProviderRegistry, Extensions as ConnectionExtensions } from 'sql/workbench/parts/connection/common/connectionProviderExtension';
|
|
import { toObject } from 'sql/base/common/map';
|
|
|
|
import * as azdata from 'sqlops';
|
|
|
|
import { Event, Emitter } from 'vs/base/common/event';
|
|
import { IAction } from 'vs/base/common/actions';
|
|
import { Memento } from 'vs/workbench/common/memento';
|
|
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
|
import { Disposable } from 'vs/base/common/lifecycle';
|
|
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
|
import { Registry } from 'vs/platform/registry/common/platform';
|
|
import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement';
|
|
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
|
import { entries } from 'sql/base/common/objects';
|
|
|
|
export const SERVICE_ID = 'capabilitiesService';
|
|
export const HOST_NAME = 'azdata';
|
|
export const HOST_VERSION = '1.0';
|
|
|
|
const connectionRegistry = Registry.as<IConnectionProviderRegistry>(ConnectionExtensions.ConnectionProviderContributions);
|
|
|
|
interface ConnectionCache {
|
|
[id: string]: ConnectionProviderProperties;
|
|
}
|
|
|
|
interface CapabilitiesMomento {
|
|
connectionProviderCache: ConnectionCache;
|
|
}
|
|
|
|
export const clientCapabilities = {
|
|
hostName: HOST_NAME,
|
|
hostVersion: HOST_VERSION
|
|
};
|
|
|
|
export interface ProviderFeatures {
|
|
connection: ConnectionProviderProperties;
|
|
}
|
|
|
|
|
|
export const ICapabilitiesService = createDecorator<ICapabilitiesService>(SERVICE_ID);
|
|
|
|
/**
|
|
* Interface for managing provider capabilities
|
|
*/
|
|
export interface ICapabilitiesService {
|
|
_serviceBrand: any;
|
|
|
|
/**
|
|
* Retrieve a list of registered capabilities providers
|
|
*/
|
|
getCapabilities(provider: string): ProviderFeatures;
|
|
|
|
/**
|
|
* get the old version of provider information
|
|
*/
|
|
getLegacyCapabilities(provider: string): azdata.DataProtocolServerCapabilities;
|
|
|
|
/**
|
|
* Register a capabilities provider
|
|
*/
|
|
registerProvider(provider: azdata.CapabilitiesProvider): void;
|
|
|
|
/**
|
|
* Returns true if the feature is available for given connection
|
|
*/
|
|
isFeatureAvailable(action: IAction, connectionManagementInfo: ConnectionManagementInfo): boolean;
|
|
|
|
/**
|
|
* When a new capabilities is registered, it emits the provider name, be to use to get the new capabilities
|
|
*/
|
|
readonly onCapabilitiesRegistered: Event<ProviderFeatures>;
|
|
|
|
/**
|
|
* Get an array of all known providers
|
|
*/
|
|
readonly providers: { [id: string]: ProviderFeatures };
|
|
|
|
}
|
|
|
|
/**
|
|
* Capabilities service implementation class. This class provides the ability
|
|
* to discover the DMP capabilties that a DMP provider offers.
|
|
*/
|
|
export class CapabilitiesService extends Disposable implements ICapabilitiesService {
|
|
_serviceBrand: any;
|
|
|
|
private _momento: Memento;
|
|
private _providers = new Map<string, ProviderFeatures>();
|
|
private _featureUpdateEvents = new Map<string, Emitter<ProviderFeatures>>();
|
|
private _legacyProviders = new Map<string, azdata.DataProtocolServerCapabilities>();
|
|
|
|
private _onCapabilitiesRegistered = this._register(new Emitter<ProviderFeatures>());
|
|
public readonly onCapabilitiesRegistered = this._onCapabilitiesRegistered.event;
|
|
|
|
constructor(
|
|
@IStorageService private _storageService: IStorageService,
|
|
@IExtensionService extensionService: IExtensionService,
|
|
@IExtensionManagementService extentionManagementService: IExtensionManagementService
|
|
) {
|
|
super();
|
|
|
|
this._momento = new Memento('capabilities', this._storageService);
|
|
|
|
if (!this.capabilities.connectionProviderCache) {
|
|
this.capabilities.connectionProviderCache = {};
|
|
}
|
|
|
|
// handle in case some extensions have already registered (unlikley)
|
|
entries(connectionRegistry.providers).map(v => {
|
|
this.handleConnectionProvider({ id: v[0], properties: v[1] });
|
|
});
|
|
// register for when new extensions are added
|
|
this._register(connectionRegistry.onNewProvider(this.handleConnectionProvider, this));
|
|
|
|
// handle adding already known capabilities (could have caching problems)
|
|
entries(this.capabilities.connectionProviderCache).map(v => {
|
|
this.handleConnectionProvider({ id: v[0], properties: v[1] }, false);
|
|
});
|
|
|
|
extensionService.whenInstalledExtensionsRegistered().then(() => {
|
|
this.cleanupProviders();
|
|
});
|
|
|
|
_storageService.onWillSaveState(() => this.shutdown());
|
|
|
|
this._register(extentionManagementService.onDidUninstallExtension(({ identifier }) => {
|
|
const connectionProvider = 'connectionProvider';
|
|
extensionService.getExtensions().then(i => {
|
|
let extension = i.find(c => c.identifier.value.toLowerCase() === identifier.id.toLowerCase());
|
|
if (extension && extension.contributes
|
|
&& extension.contributes[connectionProvider]
|
|
&& extension.contributes[connectionProvider].providerId) {
|
|
let id = extension.contributes[connectionProvider].providerId;
|
|
delete this.capabilities.connectionProviderCache[id];
|
|
}
|
|
});
|
|
}));
|
|
}
|
|
|
|
private cleanupProviders(): void {
|
|
let knownProviders = Object.keys(connectionRegistry.providers);
|
|
for (let key in this.capabilities.connectionProviderCache) {
|
|
if (!knownProviders.includes(key)) {
|
|
this._providers.delete(key);
|
|
delete this.capabilities.connectionProviderCache[key];
|
|
}
|
|
}
|
|
}
|
|
|
|
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<ProviderFeatures>());
|
|
}
|
|
|
|
if (isNew) {
|
|
this.capabilities.connectionProviderCache[e.id] = e.properties;
|
|
this._onCapabilitiesRegistered.fire(provider);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Retrieve a list of registered server capabilities
|
|
*/
|
|
public getCapabilities(provider: string): ProviderFeatures {
|
|
return this._providers.get(provider);
|
|
}
|
|
|
|
public getLegacyCapabilities(provider: string): azdata.DataProtocolServerCapabilities {
|
|
return this._legacyProviders.get(provider);
|
|
}
|
|
|
|
public get providers(): { [id: string]: ProviderFeatures } {
|
|
return toObject(this._providers);
|
|
}
|
|
|
|
private get capabilities(): CapabilitiesMomento {
|
|
return this._momento.getMemento(StorageScope.GLOBAL) as CapabilitiesMomento;
|
|
}
|
|
|
|
/**
|
|
* Register the capabilities provider and query the provider for its capabilities
|
|
* @param provider
|
|
*/
|
|
public registerProvider(provider: azdata.CapabilitiesProvider): void {
|
|
// request the capabilities from server
|
|
provider.getServerCapabilities(clientCapabilities).then(serverCapabilities => {
|
|
this._legacyProviders.set(serverCapabilities.providerName, serverCapabilities);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Returns true if the feature is available for given connection
|
|
* @param featureComponent a component which should have the feature name
|
|
* @param connectionManagementInfo connectionManagementInfo
|
|
*/
|
|
public isFeatureAvailable(action: IAction, connectionManagementInfo: ConnectionManagementInfo): boolean {
|
|
let isCloud = connectionManagementInfo && connectionManagementInfo.serverInfo && connectionManagementInfo.serverInfo.isCloud;
|
|
let isMssql = connectionManagementInfo.connectionProfile.providerName === 'MSSQL';
|
|
// TODO: The logic should from capabilities service.
|
|
if (action) {
|
|
let featureName: string = action.id;
|
|
switch (featureName) {
|
|
case Constants.BackupFeatureName:
|
|
if (isMssql) {
|
|
return connectionManagementInfo.connectionProfile.databaseName && !isCloud;
|
|
} else {
|
|
return !!connectionManagementInfo.connectionProfile.databaseName;
|
|
}
|
|
case Constants.RestoreFeatureName:
|
|
if (isMssql) {
|
|
return !isCloud;
|
|
} else {
|
|
return !!connectionManagementInfo.connectionProfile.databaseName;
|
|
}
|
|
default:
|
|
return true;
|
|
}
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
private shutdown(): void {
|
|
this._momento.saveMemento();
|
|
}
|
|
}
|