mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-11 02:32:35 -05:00
Rename registeredServers to objectExplorer (#1093)
This commit is contained in:
97
src/sql/parts/objectExplorer/common/nodeType.ts
Normal file
97
src/sql/parts/objectExplorer/common/nodeType.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
export class NodeType {
|
||||
public static Folder = 'Folder';
|
||||
public static Root = 'root';
|
||||
public static Database = 'Database';
|
||||
public static Server = 'Server';
|
||||
public static ScalarValuedFunction = 'ScalarValuedFunction';
|
||||
public static TableValuedFunction = 'TableValuedFunction';
|
||||
public static AggregateFunction = 'AggregateFunction';
|
||||
public static FileGroup = 'FileGroup';
|
||||
public static StoredProcedure = 'StoredProcedure';
|
||||
public static UserDefinedTableType = 'UserDefinedTableType';
|
||||
public static View = 'View';
|
||||
public static Table = 'Table';
|
||||
public static HistoryTable = 'HistoryTable';
|
||||
public static ServerLevelLinkedServerLogin = 'ServerLevelLinkedServerLogin';
|
||||
public static ServerLevelServerAudit = 'ServerLevelServerAudit';
|
||||
public static ServerLevelCryptographicProvider = 'ServerLevelCryptographicProvider';
|
||||
public static ServerLevelCredential = 'ServerLevelCredential';
|
||||
public static ServerLevelServerRole = 'ServerLevelServerRole';
|
||||
public static ServerLevelLogin = 'ServerLevelLogin';
|
||||
public static ServerLevelServerAuditSpecification = 'ServerLevelServerAuditSpecification';
|
||||
public static ServerLevelServerTrigger = 'ServerLevelServerTrigger';
|
||||
public static ServerLevelLinkedServer = 'ServerLevelLinkedServer';
|
||||
public static ServerLevelEndpoint = 'ServerLevelEndpoint';
|
||||
public static Synonym = 'Synonym';
|
||||
public static DatabaseTrigger = 'DatabaseTrigger';
|
||||
public static Assembly = 'Assembly';
|
||||
public static MessageType = 'MessageType';
|
||||
public static Contract = 'Contract';
|
||||
public static Queue = 'Queue';
|
||||
public static Service = 'Service';
|
||||
public static Route = 'Route';
|
||||
public static DatabaseAndQueueEventNotification = 'DatabaseAndQueueEventNotification';
|
||||
public static RemoteServiceBinding = 'RemoteServiceBinding';
|
||||
public static BrokerPriority = 'BrokerPriority';
|
||||
public static FullTextCatalog = 'FullTextCatalog';
|
||||
public static FullTextStopList = 'FullTextStopList';
|
||||
public static SqlLogFile = 'SqlLogFile';
|
||||
public static PartitionFunction = 'PartitionFunction';
|
||||
public static PartitionScheme = 'PartitionScheme';
|
||||
public static SearchPropertyList = 'SearchPropertyList';
|
||||
public static User = 'User';
|
||||
public static Schema = 'Schema';
|
||||
public static AsymmetricKey = 'AsymmetricKey';
|
||||
public static Certificate = 'Certificate';
|
||||
public static SymmetricKey = 'SymmetricKey';
|
||||
public static DatabaseEncryptionKey = 'DatabaseEncryptionKey';
|
||||
public static MasterKey = 'MasterKey';
|
||||
public static DatabaseAuditSpecification = 'DatabaseAuditSpecification';
|
||||
public static Column = 'Column';
|
||||
public static Key = 'Key';
|
||||
public static Constraint = 'Constraint';
|
||||
public static Trigger = 'Trigger';
|
||||
public static Index = 'Index';
|
||||
public static Statistic = 'Statistic';
|
||||
public static UserDefinedDataType = 'UserDefinedDataType';
|
||||
public static UserDefinedType = 'UserDefinedType';
|
||||
public static XmlSchemaCollection = 'XmlSchemaCollection';
|
||||
public static SystemExactNumeric = 'SystemExactNumeric';
|
||||
public static SystemApproximateNumeric = 'SystemApproximateNumeric';
|
||||
public static SystemDateAndTime = 'SystemDateAndTime';
|
||||
public static SystemCharacterString = 'SystemCharacterString';
|
||||
public static SystemUnicodeCharacterString = 'SystemUnicodeCharacterString';
|
||||
public static SystemBinaryString = 'SystemBinaryString';
|
||||
public static SystemOtherDataType = 'SystemOtherDataType';
|
||||
public static SystemClrDataType = 'SystemClrDataType';
|
||||
public static SystemSpatialDataType = 'SystemSpatialDataType';
|
||||
public static UserDefinedTableTypeColumn = 'UserDefinedTableTypeColumn';
|
||||
public static UserDefinedTableTypeKey = 'UserDefinedTableTypeKey';
|
||||
public static UserDefinedTableTypeConstraint = 'UserDefinedTableTypeConstraint';
|
||||
public static StoredProcedureParameter = 'StoredProcedureParameter';
|
||||
public static TableValuedFunctionParameter = 'TableValuedFunctionParameter';
|
||||
public static ScalarValuedFunctionParameter = 'ScalarValuedFunctionParameter';
|
||||
public static AggregateFunctionParameter = 'AggregateFunctionParameter';
|
||||
public static DatabaseRole = 'DatabaseRole';
|
||||
public static ApplicationRole = 'ApplicationRole';
|
||||
public static FileGroupFile = 'FileGroupFile';
|
||||
public static SystemMessageType = 'SystemMessageType';
|
||||
public static SystemContract = 'SystemContract';
|
||||
public static SystemService = 'SystemService';
|
||||
public static SystemQueue = 'SystemQueue';
|
||||
public static Sequence = 'Sequence';
|
||||
public static SecurityPolicy = 'SecurityPolicy';
|
||||
public static DatabaseScopedCredential = 'DatabaseScopedCredential';
|
||||
public static ExternalResource = 'ExternalResource';
|
||||
public static ExternalDataSource = 'ExternalDataSource';
|
||||
public static ExternalFileFormat = 'ExternalFileFormat';
|
||||
public static ExternalTable = 'ExternalTable';
|
||||
public static ColumnMasterKey = 'ColumnMasterKey';
|
||||
public static ColumnEncryptionKey = 'ColumnEncryptionKey';
|
||||
}
|
||||
561
src/sql/parts/objectExplorer/common/objectExplorerService.ts
Normal file
561
src/sql/parts/objectExplorer/common/objectExplorerService.ts
Normal file
@@ -0,0 +1,561 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { NodeType } from 'sql/parts/objectExplorer/common/nodeType';
|
||||
import { TreeNode, TreeItemCollapsibleState, ObjectExplorerCallbacks } from 'sql/parts/objectExplorer/common/treeNode';
|
||||
import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
|
||||
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import * as sqlops from 'sqlops';
|
||||
import * as nls from 'vs/nls';
|
||||
import * as TelemetryKeys from 'sql/common/telemetryKeys';
|
||||
import * as TelemetryUtils from 'sql/common/telemetryUtilities';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { warn, error } from 'sql/base/common/log';
|
||||
import { ServerTreeView } from 'sql/parts/objectExplorer/viewlet/serverTreeView';
|
||||
import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
export const SERVICE_ID = 'ObjectExplorerService';
|
||||
|
||||
export const IObjectExplorerService = createDecorator<IObjectExplorerService>(SERVICE_ID);
|
||||
|
||||
export interface IObjectExplorerService {
|
||||
_serviceBrand: any;
|
||||
|
||||
createNewSession(providerId: string, connection: ConnectionProfile): Thenable<sqlops.ObjectExplorerSessionResponse>;
|
||||
|
||||
closeSession(providerId: string, session: sqlops.ObjectExplorerSession): Thenable<sqlops.ObjectExplorerCloseSessionResponse>;
|
||||
|
||||
expandNode(providerId: string, session: sqlops.ObjectExplorerSession, nodePath: string): Thenable<sqlops.ObjectExplorerExpandInfo>;
|
||||
|
||||
refreshNode(providerId: string, session: sqlops.ObjectExplorerSession, nodePath: string): Thenable<sqlops.ObjectExplorerExpandInfo>;
|
||||
|
||||
resolveTreeNodeChildren(session: sqlops.ObjectExplorerSession, parentTree: TreeNode): Thenable<TreeNode[]>;
|
||||
|
||||
refreshTreeNode(session: sqlops.ObjectExplorerSession, parentTree: TreeNode): Thenable<TreeNode[]>;
|
||||
|
||||
onSessionCreated(handle: number, sessionResponse: sqlops.ObjectExplorerSession);
|
||||
|
||||
onNodeExpanded(handle: number, sessionResponse: sqlops.ObjectExplorerExpandInfo);
|
||||
|
||||
/**
|
||||
* Register a ObjectExplorer provider
|
||||
*/
|
||||
registerProvider(providerId: string, provider: sqlops.ObjectExplorerProvider): void;
|
||||
|
||||
getObjectExplorerNode(connection: IConnectionProfile): TreeNode;
|
||||
|
||||
updateObjectExplorerNodes(connectionProfile: IConnectionProfile): Promise<void>;
|
||||
|
||||
deleteObjectExplorerNode(connection: IConnectionProfile): void;
|
||||
|
||||
onUpdateObjectExplorerNodes: Event<ObjectExplorerNodeEventArgs>;
|
||||
|
||||
registerServerTreeView(view: ServerTreeView): void;
|
||||
|
||||
getSelectedProfileAndDatabase(): { profile: ConnectionProfile, databaseName: string };
|
||||
|
||||
isFocused(): boolean;
|
||||
|
||||
onSelectionOrFocusChange: Event<void>;
|
||||
|
||||
getServerTreeView(): ServerTreeView;
|
||||
|
||||
findNodes(connectionId: string, type: string, schema: string, name: string, database: string, parentObjectNames?: string[]): Thenable<sqlops.NodeInfo[]>;
|
||||
|
||||
getActiveConnectionNodes(): TreeNode[];
|
||||
|
||||
getTreeNode(connectionId: string, nodePath: string): Thenable<TreeNode>;
|
||||
}
|
||||
|
||||
interface SessionStatus {
|
||||
nodes: { [nodePath: string]: NodeStatus };
|
||||
connection: ConnectionProfile;
|
||||
}
|
||||
|
||||
interface NodeStatus {
|
||||
expandEmitter: Emitter<sqlops.ObjectExplorerExpandInfo>;
|
||||
}
|
||||
|
||||
export interface ObjectExplorerNodeEventArgs {
|
||||
connection: IConnectionProfile;
|
||||
errorMessage: string;
|
||||
}
|
||||
|
||||
export interface NodeInfoWithConnection {
|
||||
connectionId: string;
|
||||
nodeInfo: sqlops.NodeInfo;
|
||||
}
|
||||
|
||||
export class ObjectExplorerService implements IObjectExplorerService {
|
||||
|
||||
public _serviceBrand: any;
|
||||
|
||||
private _disposables: IDisposable[] = [];
|
||||
|
||||
private _providers: { [handle: string]: sqlops.ObjectExplorerProvider; } = Object.create(null);
|
||||
|
||||
private _activeObjectExplorerNodes: { [id: string]: TreeNode };
|
||||
private _sessions: { [sessionId: string]: SessionStatus };
|
||||
|
||||
private _onUpdateObjectExplorerNodes: Emitter<ObjectExplorerNodeEventArgs>;
|
||||
|
||||
private _serverTreeView: ServerTreeView;
|
||||
|
||||
private _onSelectionOrFocusChange: Emitter<void>;
|
||||
|
||||
constructor(
|
||||
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService,
|
||||
@ITelemetryService private _telemetryService: ITelemetryService,
|
||||
@ICapabilitiesService private _capabilitiesService: ICapabilitiesService
|
||||
) {
|
||||
this._onUpdateObjectExplorerNodes = new Emitter<ObjectExplorerNodeEventArgs>();
|
||||
this._activeObjectExplorerNodes = {};
|
||||
this._sessions = {};
|
||||
this._providers = {};
|
||||
this._onSelectionOrFocusChange = new Emitter<void>();
|
||||
}
|
||||
|
||||
public get onUpdateObjectExplorerNodes(): Event<ObjectExplorerNodeEventArgs> {
|
||||
return this._onUpdateObjectExplorerNodes.event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event fired when the selection or focus of Object Explorer changes
|
||||
*/
|
||||
public get onSelectionOrFocusChange(): Event<void> {
|
||||
return this._onSelectionOrFocusChange.event;
|
||||
}
|
||||
|
||||
public updateObjectExplorerNodes(connection: IConnectionProfile): Promise<void> {
|
||||
return this._connectionManagementService.addSavedPassword(connection).then(withPassword => {
|
||||
let connectionProfile = ConnectionProfile.fromIConnectionProfile(this._capabilitiesService, withPassword);
|
||||
return this.updateNewObjectExplorerNode(connectionProfile);
|
||||
});
|
||||
}
|
||||
|
||||
public deleteObjectExplorerNode(connection: IConnectionProfile): void {
|
||||
let self = this;
|
||||
var connectionUri = connection.id;
|
||||
var nodeTree = this._activeObjectExplorerNodes[connectionUri];
|
||||
if (nodeTree) {
|
||||
self.closeSession(connection.providerName, nodeTree.getSession()).then(() => {
|
||||
delete self._activeObjectExplorerNodes[connectionUri];
|
||||
delete self._sessions[nodeTree.getSession().sessionId];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets called when expanded node response is ready
|
||||
*/
|
||||
public onNodeExpanded(handle: number, expandResponse: sqlops.ObjectExplorerExpandInfo) {
|
||||
|
||||
if (expandResponse.errorMessage) {
|
||||
error(expandResponse.errorMessage);
|
||||
}
|
||||
|
||||
let nodeStatus = this._sessions[expandResponse.sessionId].nodes[expandResponse.nodePath];
|
||||
if (nodeStatus && nodeStatus.expandEmitter) {
|
||||
nodeStatus.expandEmitter.fire(expandResponse);
|
||||
} else {
|
||||
warn(`Cannot find node status for session: ${expandResponse.sessionId} and node path: ${expandResponse.nodePath}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets called when session is created
|
||||
*/
|
||||
public onSessionCreated(handle: number, session: sqlops.ObjectExplorerSession) {
|
||||
let connection: ConnectionProfile = undefined;
|
||||
let errorMessage: string = undefined;
|
||||
if (this._sessions[session.sessionId]) {
|
||||
connection = this._sessions[session.sessionId].connection;
|
||||
|
||||
if (session && session.success && session.rootNode) {
|
||||
let server = this.toTreeNode(session.rootNode, null);
|
||||
server.connection = connection;
|
||||
server.session = session;
|
||||
this._activeObjectExplorerNodes[connection.id] = server;
|
||||
} else {
|
||||
errorMessage = session && session.errorMessage ? session.errorMessage :
|
||||
nls.localize('OeSessionFailedError', 'Failed to create Object Explorer session');
|
||||
error(errorMessage);
|
||||
}
|
||||
|
||||
} else {
|
||||
warn(`cannot find session ${session.sessionId}`);
|
||||
}
|
||||
|
||||
this.sendUpdateNodeEvent(connection, errorMessage);
|
||||
}
|
||||
|
||||
private sendUpdateNodeEvent(connection: ConnectionProfile, errorMessage: string = undefined) {
|
||||
let eventArgs: ObjectExplorerNodeEventArgs = {
|
||||
connection: <IConnectionProfile>connection,
|
||||
errorMessage: errorMessage
|
||||
};
|
||||
this._onUpdateObjectExplorerNodes.fire(eventArgs);
|
||||
}
|
||||
|
||||
private updateNewObjectExplorerNode(connection: ConnectionProfile): Promise<void> {
|
||||
let self = this;
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
if (self._activeObjectExplorerNodes[connection.id]) {
|
||||
this.sendUpdateNodeEvent(connection);
|
||||
resolve();
|
||||
} else {
|
||||
// Create session will send the event or reject the promise
|
||||
this.createNewSession(connection.providerName, connection).then(response => {
|
||||
resolve();
|
||||
}, error => {
|
||||
this.sendUpdateNodeEvent(connection, error);
|
||||
reject(error);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public getObjectExplorerNode(connection: IConnectionProfile): TreeNode {
|
||||
return this._activeObjectExplorerNodes[connection.id];
|
||||
}
|
||||
|
||||
public createNewSession(providerId: string, connection: ConnectionProfile): Thenable<sqlops.ObjectExplorerSessionResponse> {
|
||||
let self = this;
|
||||
return new Promise<sqlops.ObjectExplorerSessionResponse>((resolve, reject) => {
|
||||
let provider = this._providers[providerId];
|
||||
if (provider) {
|
||||
provider.createNewSession(connection.toConnectionInfo()).then(result => {
|
||||
self._sessions[result.sessionId] = {
|
||||
connection: connection,
|
||||
nodes: {}
|
||||
};
|
||||
resolve(result);
|
||||
}, error => {
|
||||
reject(error);
|
||||
});
|
||||
} else {
|
||||
reject(`Provider doesn't exist. id: ${providerId}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public expandNode(providerId: string, session: sqlops.ObjectExplorerSession, nodePath: string): Thenable<sqlops.ObjectExplorerExpandInfo> {
|
||||
return new Promise<sqlops.ObjectExplorerExpandInfo>((resolve, reject) => {
|
||||
let provider = this._providers[providerId];
|
||||
if (provider) {
|
||||
TelemetryUtils.addTelemetry(this._telemetryService, TelemetryKeys.ObjectExplorerExpand, { refresh: 0, provider: providerId });
|
||||
this.expandOrRefreshNode(provider, session, nodePath).then(result => {
|
||||
resolve(result);
|
||||
}, error => {
|
||||
reject(error);
|
||||
});
|
||||
} else {
|
||||
reject(`Provider doesn't exist. id: ${providerId}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
private callExpandOrRefreshFromProvider(provider: sqlops.ObjectExplorerProvider, nodeInfo: sqlops.ExpandNodeInfo, refresh: boolean = false) {
|
||||
if (refresh) {
|
||||
return provider.refreshNode(nodeInfo);
|
||||
} else {
|
||||
return provider.expandNode(nodeInfo);
|
||||
}
|
||||
}
|
||||
|
||||
private expandOrRefreshNode(
|
||||
provider: sqlops.ObjectExplorerProvider,
|
||||
session: sqlops.ObjectExplorerSession,
|
||||
nodePath: string,
|
||||
refresh: boolean = false): Thenable<sqlops.ObjectExplorerExpandInfo> {
|
||||
let self = this;
|
||||
return new Promise<sqlops.ObjectExplorerExpandInfo>((resolve, reject) => {
|
||||
if (session.sessionId in self._sessions && self._sessions[session.sessionId]) {
|
||||
let newRequest = false;
|
||||
if (!self._sessions[session.sessionId].nodes[nodePath]) {
|
||||
self._sessions[session.sessionId].nodes[nodePath] = {
|
||||
expandEmitter: new Emitter<sqlops.ObjectExplorerExpandInfo>()
|
||||
};
|
||||
newRequest = true;
|
||||
}
|
||||
self._sessions[session.sessionId].nodes[nodePath].expandEmitter.event(((expandResult) => {
|
||||
if (expandResult && !expandResult.errorMessage) {
|
||||
resolve(expandResult);
|
||||
}
|
||||
else {
|
||||
reject(expandResult ? expandResult.errorMessage : undefined);
|
||||
}
|
||||
if (newRequest) {
|
||||
delete self._sessions[session.sessionId].nodes[nodePath];
|
||||
}
|
||||
}));
|
||||
if (newRequest) {
|
||||
self.callExpandOrRefreshFromProvider(provider, {
|
||||
sessionId: session.sessionId,
|
||||
nodePath: nodePath
|
||||
}, refresh).then(result => {
|
||||
}, error => {
|
||||
reject(error);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
reject(`session cannot find to expand node. id: ${session.sessionId} nodePath: ${nodePath}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public refreshNode(providerId: string, session: sqlops.ObjectExplorerSession, nodePath: string): Thenable<sqlops.ObjectExplorerExpandInfo> {
|
||||
let provider = this._providers[providerId];
|
||||
if (provider) {
|
||||
TelemetryUtils.addTelemetry(this._telemetryService, TelemetryKeys.ObjectExplorerExpand, { refresh: 1, provider: providerId });
|
||||
return this.expandOrRefreshNode(provider, session, nodePath, true);
|
||||
}
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
public closeSession(providerId: string, session: sqlops.ObjectExplorerSession): Thenable<sqlops.ObjectExplorerCloseSessionResponse> {
|
||||
let provider = this._providers[providerId];
|
||||
if (provider) {
|
||||
return provider.closeSession({
|
||||
sessionId: session ? session.sessionId : undefined
|
||||
});
|
||||
}
|
||||
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a ObjectExplorer provider
|
||||
*/
|
||||
public registerProvider(providerId: string, provider: sqlops.ObjectExplorerProvider): void {
|
||||
this._providers[providerId] = provider;
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this._disposables = dispose(this._disposables);
|
||||
}
|
||||
|
||||
public resolveTreeNodeChildren(session: sqlops.ObjectExplorerSession, parentTree: TreeNode): Thenable<TreeNode[]> {
|
||||
return this.expandOrRefreshTreeNode(session, parentTree);
|
||||
}
|
||||
|
||||
public refreshTreeNode(session: sqlops.ObjectExplorerSession, parentTree: TreeNode): Thenable<TreeNode[]> {
|
||||
return this.expandOrRefreshTreeNode(session, parentTree, true);
|
||||
}
|
||||
|
||||
private callExpandOrRefreshFromService(providerId: string, session: sqlops.ObjectExplorerSession, nodePath: string, refresh: boolean = false): Thenable<sqlops.ObjectExplorerExpandInfo> {
|
||||
if (refresh) {
|
||||
return this.refreshNode(providerId, session, nodePath);
|
||||
} else {
|
||||
return this.expandNode(providerId, session, nodePath);
|
||||
}
|
||||
}
|
||||
|
||||
private expandOrRefreshTreeNode(
|
||||
session: sqlops.ObjectExplorerSession,
|
||||
parentTree: TreeNode,
|
||||
refresh: boolean = false): Thenable<TreeNode[]> {
|
||||
return new Promise<TreeNode[]>((resolve, reject) => {
|
||||
this.callExpandOrRefreshFromService(parentTree.getConnectionProfile().providerName, session, parentTree.nodePath, refresh).then(expandResult => {
|
||||
let children: TreeNode[] = [];
|
||||
if (expandResult && expandResult.nodes) {
|
||||
children = expandResult.nodes.map(node => {
|
||||
return this.toTreeNode(node, parentTree);
|
||||
});
|
||||
parentTree.children = children.filter(c => c !== undefined);
|
||||
resolve(children);
|
||||
} else {
|
||||
reject(expandResult && expandResult.errorMessage ? expandResult.errorMessage : 'Failed to expand node');
|
||||
}
|
||||
}, error => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private toTreeNode(nodeInfo: sqlops.NodeInfo, parent: TreeNode): TreeNode {
|
||||
// Show the status for database nodes with a status field
|
||||
let isLeaf: boolean = nodeInfo.isLeaf;
|
||||
if (nodeInfo.nodeType === NodeType.Database) {
|
||||
if (nodeInfo.nodeStatus) {
|
||||
nodeInfo.label = nodeInfo.label + ' (' + nodeInfo.nodeStatus + ')';
|
||||
}
|
||||
if (isLeaf) {
|
||||
// set to common status so we can have a single 'Unavailable' db icon
|
||||
nodeInfo.nodeStatus = 'Unavailable';
|
||||
} else {
|
||||
nodeInfo.nodeStatus = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
return new TreeNode(nodeInfo.nodeType, nodeInfo.label, isLeaf, nodeInfo.nodePath,
|
||||
nodeInfo.nodeSubType, nodeInfo.nodeStatus, parent, nodeInfo.metadata, {
|
||||
getChildren: treeNode => this.getChildren(treeNode),
|
||||
isExpanded: treeNode => this.isExpanded(treeNode),
|
||||
setNodeExpandedState: (treeNode, expandedState) => this.setNodeExpandedState(treeNode, expandedState),
|
||||
setNodeSelected: (treeNode, selected, clearOtherSelections: boolean = undefined) => this.setNodeSelected(treeNode, selected, clearOtherSelections)
|
||||
});
|
||||
}
|
||||
|
||||
public registerServerTreeView(view: ServerTreeView): void {
|
||||
if (this._serverTreeView) {
|
||||
throw new Error('The object explorer server tree view is already registered');
|
||||
}
|
||||
this._serverTreeView = view;
|
||||
this._serverTreeView.onSelectionOrFocusChange(() => this._onSelectionOrFocusChange.fire());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the connection profile corresponding to the current Object Explorer selection,
|
||||
* or undefined if there are multiple selections or no such connection
|
||||
*/
|
||||
public getSelectedProfileAndDatabase(): { profile: ConnectionProfile, databaseName: string } {
|
||||
if (!this._serverTreeView) {
|
||||
return undefined;
|
||||
}
|
||||
let selection = this._serverTreeView.getSelection();
|
||||
if (selection.length === 1) {
|
||||
let selectedNode = selection[0];
|
||||
if (selectedNode instanceof ConnectionProfile) {
|
||||
return { profile: selectedNode, databaseName: undefined };
|
||||
} else if (selectedNode instanceof TreeNode) {
|
||||
let profile = selectedNode.getConnectionProfile();
|
||||
let database = selectedNode.getDatabaseName();
|
||||
// If the database is unavailable, use the server connection
|
||||
if (selectedNode.nodeTypeId === 'Database' && selectedNode.isAlwaysLeaf) {
|
||||
database = undefined;
|
||||
}
|
||||
return { profile: profile, databaseName: database };
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a boolean indicating whether the Object Explorer tree has focus
|
||||
*/
|
||||
public isFocused(): boolean {
|
||||
return this._serverTreeView.isFocused();
|
||||
}
|
||||
|
||||
public getServerTreeView() {
|
||||
return this._serverTreeView;
|
||||
}
|
||||
|
||||
public findNodes(connectionId: string, type: string, schema: string, name: string, database: string, parentObjectNames?: string[]): Thenable<sqlops.NodeInfo[]> {
|
||||
let rootNode = this._activeObjectExplorerNodes[connectionId];
|
||||
if (!rootNode) {
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
let sessionId = rootNode.session.sessionId;
|
||||
return this._providers[this._sessions[sessionId].connection.providerName].findNodes({
|
||||
type: type,
|
||||
name: name,
|
||||
schema: schema,
|
||||
database: database,
|
||||
parentObjectNames: parentObjectNames,
|
||||
sessionId: sessionId
|
||||
}).then(response => {
|
||||
return response.nodes;
|
||||
});
|
||||
}
|
||||
|
||||
public getActiveConnectionNodes(): TreeNode[] {
|
||||
return Object.values(this._activeObjectExplorerNodes);
|
||||
}
|
||||
|
||||
private async setNodeExpandedState(treeNode: TreeNode, expandedState: TreeItemCollapsibleState): Promise<void> {
|
||||
treeNode = await this.getUpdatedTreeNode(treeNode);
|
||||
let expandNode = this.getTreeItem(treeNode);
|
||||
if (expandedState === TreeItemCollapsibleState.Expanded) {
|
||||
await this._serverTreeView.reveal(expandNode);
|
||||
}
|
||||
return this._serverTreeView.setExpandedState(expandNode, expandedState);
|
||||
}
|
||||
|
||||
private async setNodeSelected(treeNode: TreeNode, selected: boolean, clearOtherSelections: boolean = undefined): Promise<void> {
|
||||
treeNode = await this.getUpdatedTreeNode(treeNode);
|
||||
let selectNode = this.getTreeItem(treeNode);
|
||||
if (selected) {
|
||||
await this._serverTreeView.reveal(selectNode);
|
||||
}
|
||||
return this._serverTreeView.setSelected(selectNode, selected, clearOtherSelections);
|
||||
}
|
||||
|
||||
private async getChildren(treeNode: TreeNode): Promise<TreeNode[]> {
|
||||
treeNode = await this.getUpdatedTreeNode(treeNode);
|
||||
if (treeNode.isAlwaysLeaf) {
|
||||
return [];
|
||||
}
|
||||
if (!treeNode.children) {
|
||||
await this.resolveTreeNodeChildren(treeNode.getSession(), treeNode);
|
||||
}
|
||||
return treeNode.children;
|
||||
}
|
||||
|
||||
private async isExpanded(treeNode: TreeNode): Promise<boolean> {
|
||||
treeNode = await this.getUpdatedTreeNode(treeNode);
|
||||
do {
|
||||
let expandNode = this.getTreeItem(treeNode);
|
||||
if (!this._serverTreeView.isExpanded(expandNode)) {
|
||||
return false;
|
||||
}
|
||||
treeNode = treeNode.parent;
|
||||
} while (treeNode);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private getTreeItem(treeNode: TreeNode): TreeNode | ConnectionProfile {
|
||||
let rootNode = this._activeObjectExplorerNodes[treeNode.getConnectionProfile().id];
|
||||
if (treeNode === rootNode) {
|
||||
return treeNode.connection;
|
||||
}
|
||||
return treeNode;
|
||||
}
|
||||
|
||||
private getUpdatedTreeNode(treeNode: TreeNode): Promise<TreeNode> {
|
||||
return this.getTreeNode(treeNode.getConnectionProfile().id, treeNode.nodePath).then(treeNode => {
|
||||
if (!treeNode) {
|
||||
throw new Error(nls.localize('treeNodeNoLongerExists', 'The given tree node no longer exists'));
|
||||
}
|
||||
return treeNode;
|
||||
});
|
||||
}
|
||||
|
||||
public async getTreeNode(connectionId: string, nodePath: string): Promise<TreeNode> {
|
||||
let parentNode = this._activeObjectExplorerNodes[connectionId];
|
||||
if (!parentNode) {
|
||||
return undefined;
|
||||
}
|
||||
if (!nodePath) {
|
||||
return parentNode;
|
||||
}
|
||||
let currentNode = parentNode;
|
||||
while (currentNode.nodePath !== nodePath) {
|
||||
let nextNode = undefined;
|
||||
if (!currentNode.isAlwaysLeaf && !currentNode.children) {
|
||||
await this.resolveTreeNodeChildren(currentNode.getSession(), currentNode);
|
||||
}
|
||||
if (currentNode.children) {
|
||||
// Look at the next node in the path, which is the child object with the longest path where the desired path starts with the child path
|
||||
let children = currentNode.children.filter(child => nodePath.startsWith(child.nodePath));
|
||||
if (children.length > 0) {
|
||||
nextNode = children.reduce((currentMax, candidate) => currentMax.nodePath.length < candidate.nodePath.length ? candidate : currentMax);
|
||||
}
|
||||
}
|
||||
if (!nextNode) {
|
||||
return undefined;
|
||||
}
|
||||
currentNode = nextNode;
|
||||
}
|
||||
return currentNode;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!sql/media/actionBarLabel';
|
||||
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { localize } from 'vs/nls';
|
||||
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
|
||||
import { ViewletRegistry, Extensions as ViewletExtensions, ViewletDescriptor, ToggleViewletAction } from 'vs/workbench/browser/viewlet';
|
||||
import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
|
||||
import { VIEWLET_ID } from 'sql/parts/connection/common/connectionManagement';
|
||||
import { ConnectionViewlet } from 'sql/parts/objectExplorer/viewlet/connectionViewlet';
|
||||
|
||||
// Viewlet Action
|
||||
export class OpenConnectionsViewletAction extends ToggleViewletAction {
|
||||
public static ID = VIEWLET_ID;
|
||||
public static LABEL = 'Show Servers';
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IViewletService viewletService: IViewletService,
|
||||
@IWorkbenchEditorService editorService: IWorkbenchEditorService
|
||||
) {
|
||||
super(id, label, VIEWLET_ID, viewletService, editorService);
|
||||
}
|
||||
}
|
||||
|
||||
// Viewlet
|
||||
const viewletDescriptor = new ViewletDescriptor(
|
||||
ConnectionViewlet,
|
||||
VIEWLET_ID,
|
||||
'Servers',
|
||||
'connectionViewlet',
|
||||
-100
|
||||
);
|
||||
|
||||
Registry.as<ViewletRegistry>(ViewletExtensions.Viewlets).registerViewlet(viewletDescriptor);
|
||||
|
||||
Registry.as<ViewletRegistry>(ViewletExtensions.Viewlets).setDefaultViewletId(VIEWLET_ID);
|
||||
|
||||
const registry = Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions);
|
||||
registry.registerWorkbenchAction(
|
||||
new SyncActionDescriptor(
|
||||
OpenConnectionsViewletAction,
|
||||
OpenConnectionsViewletAction.ID,
|
||||
OpenConnectionsViewletAction.LABEL,
|
||||
{ primary: KeyMod.CtrlCmd | KeyCode.Shift | KeyCode.KEY_C }),
|
||||
'View: Show Servers',
|
||||
localize('registeredServers.view', "View")
|
||||
);
|
||||
|
||||
let configurationRegistry = <IConfigurationRegistry>Registry.as(Extensions.Configuration);
|
||||
configurationRegistry.registerConfiguration({
|
||||
'id': 'databaseConnections',
|
||||
'title': localize('databaseConnections', 'Database Connections'),
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'datasource.connections': {
|
||||
'description': localize('datasource.connections', 'data source connections'),
|
||||
'type': 'array'
|
||||
},
|
||||
'datasource.connectionGroups': {
|
||||
'description': localize('datasource.connectionGroups', 'data source groups'),
|
||||
'type': 'array'
|
||||
}
|
||||
}
|
||||
});
|
||||
configurationRegistry.registerConfiguration({
|
||||
'id': 'startupConfig',
|
||||
'title': localize('startupConfig', 'Startup Configuration'),
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'startup.alwaysShowServersView': {
|
||||
'type': 'boolean',
|
||||
'description': localize('startup.alwaysShowServersView', 'True for the Servers view to be shown on launch of SQL Operations Studio default; false if the last opened view should be shown'),
|
||||
'default': true
|
||||
}
|
||||
}
|
||||
});
|
||||
166
src/sql/parts/objectExplorer/common/treeNode.ts
Normal file
166
src/sql/parts/objectExplorer/common/treeNode.ts
Normal file
@@ -0,0 +1,166 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile';
|
||||
import { NodeType } from 'sql/parts/objectExplorer/common/nodeType';
|
||||
import * as sqlops from 'sqlops';
|
||||
|
||||
import * as UUID from 'vs/base/common/uuid';
|
||||
|
||||
export enum TreeItemCollapsibleState {
|
||||
None = 0,
|
||||
Collapsed = 1,
|
||||
Expanded = 2
|
||||
}
|
||||
|
||||
export interface ObjectExplorerCallbacks {
|
||||
getChildren(treeNode: TreeNode): Thenable<TreeNode[]>;
|
||||
isExpanded(treeNode: TreeNode): Thenable<boolean>;
|
||||
setNodeExpandedState(TreeNode: TreeNode, expandedState: TreeItemCollapsibleState): Thenable<void>;
|
||||
setNodeSelected(TreeNode: TreeNode, selected: boolean, clearOtherSelections?: boolean): Thenable<void>;
|
||||
}
|
||||
|
||||
export class TreeNode {
|
||||
/**
|
||||
* id for TreeNode
|
||||
*/
|
||||
public id: string;
|
||||
|
||||
/**
|
||||
* string defining the type of the node - for example Server, Database, Folder, Table
|
||||
*/
|
||||
public nodeTypeId: string;
|
||||
|
||||
/**
|
||||
* Label to display to the user, describing this node
|
||||
*/
|
||||
public label: string;
|
||||
|
||||
/**
|
||||
* Is this a leaf node (in which case no children can be generated) or is it expandable?
|
||||
*/
|
||||
public isAlwaysLeaf: boolean;
|
||||
|
||||
/**
|
||||
* Message to show if this Node is in an error state. This indicates
|
||||
* that children could be retrieved
|
||||
*/
|
||||
public errorStateMessage: string;
|
||||
|
||||
/**
|
||||
* Parent of this node
|
||||
*/
|
||||
public parent: TreeNode;
|
||||
|
||||
/**
|
||||
* Path identifying this node
|
||||
*/
|
||||
public nodePath: string;
|
||||
|
||||
/**
|
||||
* Node sub type
|
||||
*/
|
||||
public nodeSubType: string;
|
||||
|
||||
/**
|
||||
* Node Status
|
||||
*/
|
||||
public nodeStatus: string;
|
||||
|
||||
/**
|
||||
* Children of this node
|
||||
*/
|
||||
public children: TreeNode[];
|
||||
|
||||
|
||||
public connection: ConnectionProfile;
|
||||
|
||||
public session: sqlops.ObjectExplorerSession;
|
||||
|
||||
public metadata: sqlops.ObjectMetadata;
|
||||
|
||||
public getConnectionProfile(): ConnectionProfile {
|
||||
var currentNode: TreeNode = this;
|
||||
while (!currentNode.connection && currentNode.parent) {
|
||||
currentNode = currentNode.parent;
|
||||
}
|
||||
return currentNode.connection;
|
||||
}
|
||||
|
||||
public getDatabaseName(): string {
|
||||
if (this.connection) {
|
||||
return undefined;
|
||||
}
|
||||
var currentNode: TreeNode = this;
|
||||
while (currentNode.nodeTypeId !== NodeType.Database && currentNode.nodeTypeId !== NodeType.Server) {
|
||||
currentNode = currentNode.parent;
|
||||
}
|
||||
|
||||
if (currentNode.nodeTypeId === NodeType.Database) {
|
||||
return currentNode.metadata ? currentNode.metadata.name : null;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public getSession(): sqlops.ObjectExplorerSession {
|
||||
var currentNode: TreeNode = this;
|
||||
while (!currentNode.session && currentNode.parent) {
|
||||
currentNode = currentNode.parent;
|
||||
}
|
||||
return currentNode.session;
|
||||
}
|
||||
|
||||
public isTopLevel(): boolean {
|
||||
if (this.parent && this.parent.nodeTypeId === NodeType.Root) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public toNodeInfo(): sqlops.NodeInfo {
|
||||
return <sqlops.NodeInfo> {
|
||||
nodePath: this.nodePath,
|
||||
nodeType: this.nodeTypeId,
|
||||
nodeSubType: this.nodeSubType,
|
||||
nodeStatus: this.nodeStatus,
|
||||
label: this.label,
|
||||
isLeaf: this.isAlwaysLeaf,
|
||||
metadata: this.metadata,
|
||||
errorMessage: this.errorStateMessage
|
||||
};
|
||||
}
|
||||
|
||||
public getChildren(): Thenable<TreeNode[]> {
|
||||
return this._objectExplorerCallbacks.getChildren(this);
|
||||
}
|
||||
|
||||
public isExpanded(): Thenable<boolean> {
|
||||
return this._objectExplorerCallbacks.isExpanded(this);
|
||||
}
|
||||
|
||||
public setExpandedState(expandedState: TreeItemCollapsibleState): Thenable<void> {
|
||||
return this._objectExplorerCallbacks.setNodeExpandedState(this, expandedState);
|
||||
}
|
||||
|
||||
public setSelected(selected: boolean, clearOtherSelections?: boolean): Thenable<void> {
|
||||
return this._objectExplorerCallbacks.setNodeSelected(this, selected, clearOtherSelections);
|
||||
}
|
||||
|
||||
constructor(nodeTypeId: string, label: string, isAlwaysLeaf: boolean, nodePath: string,
|
||||
nodeSubType: string, nodeStatus: string, parent: TreeNode, metadata: sqlops.ObjectMetadata,
|
||||
private _objectExplorerCallbacks: ObjectExplorerCallbacks) {
|
||||
this.nodeTypeId = nodeTypeId;
|
||||
this.label = label;
|
||||
this.isAlwaysLeaf = isAlwaysLeaf;
|
||||
this.nodePath = nodePath;
|
||||
this.parent = parent;
|
||||
this.metadata = metadata;
|
||||
this.id = UUID.generateUuid();
|
||||
this.nodeSubType = nodeSubType;
|
||||
this.nodeStatus = nodeStatus;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user