Object Explorer API (#783)

See https://github.com/Microsoft/sqlopsstudio/wiki/Extensibility-API#object-explorer for usage details
This commit is contained in:
Matt Irvine
2018-03-09 15:02:20 -08:00
committed by GitHub
parent fab4185c1f
commit c06ab27d08
13 changed files with 707 additions and 63 deletions

View File

@@ -5,7 +5,7 @@
'use strict';
import { NodeType } from 'sql/parts/registeredServer/common/nodeType';
import { TreeNode } from 'sql/parts/registeredServer/common/treeNode';
import { TreeNode, TreeItemCollapsibleState, ObjectExplorerCallbacks } from 'sql/parts/registeredServer/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';
@@ -20,6 +20,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { warn, error } from 'sql/base/common/log';
import { ServerTreeView } from 'sql/parts/registeredServer/viewlet/serverTreeView';
import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService';
import * as vscode from 'vscode';
export const SERVICE_ID = 'ObjectExplorerService';
@@ -36,7 +37,7 @@ export interface IObjectExplorerService {
refreshNode(providerId: string, session: sqlops.ObjectExplorerSession, nodePath: string): Thenable<sqlops.ObjectExplorerExpandInfo>;
expandTreeNode(session: sqlops.ObjectExplorerSession, parentTree: TreeNode): Thenable<TreeNode[]>;
resolveTreeNodeChildren(session: sqlops.ObjectExplorerSession, parentTree: TreeNode): Thenable<TreeNode[]>;
refreshTreeNode(session: sqlops.ObjectExplorerSession, parentTree: TreeNode): Thenable<TreeNode[]>;
@@ -66,16 +67,19 @@ export interface IObjectExplorerService {
onSelectionOrFocusChange: Event<void>;
getServerTreeView(): ServerTreeView;
getActiveConnectionNodes(): TreeNode[];
getTreeNode(connectionId: string, nodePath: string): Thenable<TreeNode>;
}
interface SessionStatus {
nodes: { [nodePath: string]: NodeStatus };
connection: ConnectionProfile;
}
interface NodeStatus {
expandHandler: (result: sqlops.ObjectExplorerExpandInfo) => void;
expandEmitter: Emitter<sqlops.ObjectExplorerExpandInfo>;
}
export interface ObjectExplorerNodeEventArgs {
@@ -83,6 +87,10 @@ export interface ObjectExplorerNodeEventArgs {
errorMessage: string;
}
export interface NodeInfoWithConnection {
connectionId: string;
nodeInfo: sqlops.NodeInfo;
}
export class ObjectExplorerService implements IObjectExplorerService {
@@ -153,8 +161,8 @@ export class ObjectExplorerService implements IObjectExplorerService {
}
let nodeStatus = this._sessions[expandResponse.sessionId].nodes[expandResponse.nodePath];
if (nodeStatus && nodeStatus.expandHandler) {
nodeStatus.expandHandler(expandResponse);
if (nodeStatus && nodeStatus.expandEmitter) {
nodeStatus.expandEmitter.fire(expandResponse);
} else {
warn(`Cannot find node status for session: ${expandResponse.sessionId} and node path: ${expandResponse.nodePath}`);
}
@@ -268,24 +276,33 @@ export class ObjectExplorerService implements IObjectExplorerService {
let self = this;
return new Promise<sqlops.ObjectExplorerExpandInfo>((resolve, reject) => {
if (session.sessionId in self._sessions && self._sessions[session.sessionId]) {
self._sessions[session.sessionId].nodes[nodePath] = {
expandHandler: ((expandResult) => {
if (expandResult && !expandResult.errorMessage) {
resolve(expandResult);
}
else {
reject(expandResult ? expandResult.errorMessage : undefined);
}
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];
})
};
self.callExpandOrRefreshFromProvider(provider, {
sessionId: session ? session.sessionId : undefined,
nodePath: nodePath
}, refresh).then(result => {
}, error => {
reject(error);
});
}
}));
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}`);
}
@@ -323,7 +340,7 @@ export class ObjectExplorerService implements IObjectExplorerService {
this._disposables = dispose(this._disposables);
}
public expandTreeNode(session: sqlops.ObjectExplorerSession, parentTree: TreeNode): Thenable<TreeNode[]> {
public resolveTreeNodeChildren(session: sqlops.ObjectExplorerSession, parentTree: TreeNode): Thenable<TreeNode[]> {
return this.expandOrRefreshTreeNode(session, parentTree);
}
@@ -377,7 +394,12 @@ export class ObjectExplorerService implements IObjectExplorerService {
}
return new TreeNode(nodeInfo.nodeType, nodeInfo.label, isLeaf, nodeInfo.nodePath,
nodeInfo.nodeSubType, nodeInfo.nodeStatus, parent, nodeInfo.metadata);
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 {
@@ -424,4 +446,96 @@ export class ObjectExplorerService implements IObjectExplorerService {
public getServerTreeView() {
return this._serverTreeView;
}
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;
}
}

View File

@@ -11,6 +11,19 @@ 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
@@ -59,8 +72,8 @@ export class TreeNode {
public nodeStatus: string;
/**
* Children of this node
*/
* Children of this node
*/
public children: TreeNode[];
@@ -108,8 +121,38 @@ export class TreeNode {
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) {
nodeSubType: string, nodeStatus: string, parent: TreeNode, metadata: sqlops.ObjectMetadata,
private _objectExplorerCallbacks: ObjectExplorerCallbacks) {
this.nodeTypeId = nodeTypeId;
this.label = label;
this.isAlwaysLeaf = isAlwaysLeaf;

View File

@@ -74,7 +74,7 @@ export class ServerTreeDataSource implements IDataSource {
if (node.children) {
resolve(node.children);
} else {
this._objectExplorerService.expandTreeNode(node.getSession(), node).then(() => {
this._objectExplorerService.resolveTreeNodeChildren(node.getSession(), node).then(() => {
resolve(node.children);
}, expandError => {
this.showError(expandError);

View File

@@ -28,6 +28,7 @@ import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesServ
import { Button } from 'sql/base/browser/ui/button/button';
import { attachButtonStyler } from 'sql/common/theme/styler';
import Event, { Emitter } from 'vs/base/common/event';
import { TreeNode, TreeItemCollapsibleState } from 'sql/parts/registeredServer/common/treeNode';
const $ = builder.$;
@@ -428,6 +429,48 @@ export class ServerTreeView {
return this._tree.isDOMFocused();
}
/**
* Set whether the given element is expanded or collapsed
*/
public setExpandedState(element: TreeNode | ConnectionProfile, expandedState: TreeItemCollapsibleState): Thenable<void> {
if (expandedState === TreeItemCollapsibleState.Collapsed) {
return this._tree.collapse(element);
} else if (expandedState === TreeItemCollapsibleState.Expanded) {
return this._tree.expand(element);
}
return Promise.resolve();
}
/**
* Reveal the given element in the tree
*/
public reveal(element: TreeNode | ConnectionProfile): Thenable<void> {
return this._tree.reveal(element);
}
/**
* Select the given element in the tree and clear any other selections
*/
public setSelected(element: TreeNode | ConnectionProfile, selected: boolean, clearOtherSelections: boolean): Thenable<void> {
if (clearOtherSelections || (selected && clearOtherSelections !== false)) {
this._tree.clearSelection();
}
if (selected) {
this._tree.select(element);
return this._tree.reveal(element);
} else {
this._tree.deselect(element);
return Promise.resolve();
}
}
/**
* Check if the given element in the tree is expanded
*/
public isExpanded(element: TreeNode | ConnectionProfile): boolean {
return this._tree.isExpanded(element);
}
/**
* dispose the server tree view
*/

View File

@@ -209,7 +209,7 @@ export class TreeUpdateUtils {
} else {
var rootNode = objectExplorerService.getObjectExplorerNode(connection);
if (rootNode) {
objectExplorerService.expandTreeNode(rootNode.getSession(), rootNode).then(() => {
objectExplorerService.resolveTreeNodeChildren(rootNode.getSession(), rootNode).then(() => {
resolve(rootNode.children);
}, expandError => {
resolve([]);

61
src/sql/sqlops.d.ts vendored
View File

@@ -108,6 +108,67 @@ declare module 'sqlops' {
}
}
/**
* Namespace for interacting with Object Explorer
*/
export namespace objectexplorer {
/**
* Get an Object Explorer node corresponding to the given connection and path. If no path
* is given, it returns the top-level node for the given connection. If there is no node at
* the given path, it returns undefined.
* @param {string} connectionId The id of the connection that the node exists on
* @param {string?} nodePath The path of the node to get
* @returns {ObjectExplorerNode} The node corresponding to the given connection and path,
* or undefined if no such node exists.
*/
export function getNode(connectionId: string, nodePath?: string): Thenable<ObjectExplorerNode>;
/**
* Get all active Object Explorer connection nodes
* @returns {ObjectExplorerNode[]} The Object Explorer nodes for each saved connection
*/
export function getActiveConnectionNodes(): Thenable<ObjectExplorerNode[]>;
/**
* Interface for representing and interacting with items in Object Explorer
*/
export interface ObjectExplorerNode extends NodeInfo {
/**
* The id of the connection that the node exists under
*/
connectionId: string;
/**
* Whether the node is currently expanded in Object Explorer
*/
isExpanded(): Thenable<boolean>;
/**
* Set whether the node is expanded or collapsed
* @param expandedState The new state of the node. If 'None', the node will not be changed
*/
setExpandedState(expandedState: vscode.TreeItemCollapsibleState): Thenable<void>;
/**
* Set whether the node is selected
* @param selected Whether the node should be selected
* @param clearOtherSelections If true, clear any other selections. If false, leave any existing selections.
* Defaults to true when selected is true and false when selected is false.
*/
setSelected(selected: boolean, clearOtherSelections?: boolean): Thenable<void>;
/**
* Get all the child nodes. Returns an empty list if there are no children.
*/
getChildren(): Thenable<ObjectExplorerNode[]>;
/**
* Get the parent node. Returns undefined if there is none.
*/
getParent(): Thenable<ObjectExplorerNode>;
}
}
// EXPORTED INTERFACES /////////////////////////////////////////////////
export interface ConnectionInfo {

View File

@@ -0,0 +1,70 @@
/*---------------------------------------------------------------------------------------------
* 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 { IThreadService } from 'vs/workbench/services/thread/common/threadService';
import { ExtHostObjectExplorerShape, SqlMainContext, MainThreadObjectExplorerShape } from 'sql/workbench/api/node/sqlExtHost.protocol';
import * as sqlops from 'sqlops';
import * as vscode from 'vscode';
export class ExtHostObjectExplorer implements ExtHostObjectExplorerShape {
private _proxy: MainThreadObjectExplorerShape;
constructor(
threadService: IThreadService
) {
this._proxy = threadService.get(SqlMainContext.MainThreadObjectExplorer);
}
public $getNode(connectionId: string, nodePath?: string): Thenable<sqlops.objectexplorer.ObjectExplorerNode> {
return this._proxy.$getNode(connectionId, nodePath).then(nodeInfo => nodeInfo === undefined ? undefined : new ExtHostObjectExplorerNode(nodeInfo, connectionId, this._proxy));
}
public $getActiveConnectionNodes(): Thenable<sqlops.objectexplorer.ObjectExplorerNode[]> {
return this._proxy.$getActiveConnectionNodes().then(results => results.map(result => new ExtHostObjectExplorerNode(result.nodeInfo, result.connectionId, this._proxy)));
}
}
class ExtHostObjectExplorerNode implements sqlops.objectexplorer.ObjectExplorerNode {
public connectionId: string;
public nodePath: string;
public nodeType: string;
public nodeSubType: string;
public nodeStatus: string;
public label: string;
public isLeaf: boolean;
public metadata: sqlops.ObjectMetadata;
public errorMessage: string;
constructor(nodeInfo: sqlops.NodeInfo, connectionId: string, private _proxy: MainThreadObjectExplorerShape) {
Object.entries(nodeInfo).forEach(([key, value]) => this[key] = value);
this.connectionId = connectionId;
}
isExpanded(): Thenable<boolean> {
return this._proxy.$isExpanded(this.connectionId, this.nodePath);
}
setExpandedState(expandedState: vscode.TreeItemCollapsibleState): Thenable<void> {
return this._proxy.$setExpandedState(this.connectionId, this.nodePath, expandedState);
}
setSelected(selected: boolean, clearOtherSelections: boolean = undefined): Thenable<void> {
return this._proxy.$setSelected(this.connectionId, this.nodePath, selected, clearOtherSelections);
}
getChildren(): Thenable<sqlops.objectexplorer.ObjectExplorerNode[]> {
return this._proxy.$getChildren(this.connectionId, this.nodePath).then(children => children.map(nodeInfo => new ExtHostObjectExplorerNode(nodeInfo, this.connectionId, this._proxy)));
}
getParent(): Thenable<sqlops.objectexplorer.ObjectExplorerNode> {
let parentPathEndIndex = this.nodePath.lastIndexOf('/');
if (parentPathEndIndex === -1) {
return Promise.resolve(undefined);
}
return this._proxy.$getNode(this.connectionId, this.nodePath.slice(0, parentPathEndIndex)).then(nodeInfo => nodeInfo ? new ExtHostObjectExplorerNode(nodeInfo, this.connectionId, this._proxy) : undefined);
}
}

View File

@@ -0,0 +1,72 @@
/*---------------------------------------------------------------------------------------------
* 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 { SqlExtHostContext, SqlMainContext, ExtHostObjectExplorerShape, MainThreadObjectExplorerShape } from 'sql/workbench/api/node/sqlExtHost.protocol';
import * as sqlops from 'sqlops';
import * as vscode from 'vscode';
import { IExtHostContext } from 'vs/workbench/api/node/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
import { IObjectExplorerService, NodeInfoWithConnection } from 'sql/parts/registeredServer/common/objectExplorerService';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import * as TaskUtilities from 'sql/workbench/common/taskUtilities';
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
@extHostNamedCustomer(SqlMainContext.MainThreadObjectExplorer)
export class MainThreadObjectExplorer implements MainThreadObjectExplorerShape {
private _proxy: ExtHostObjectExplorerShape;
private _toDispose: IDisposable[];
constructor(
extHostContext: IExtHostContext,
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService,
@IObjectExplorerService private _objectExplorerService: IObjectExplorerService,
@IWorkbenchEditorService private _workbenchEditorService: IWorkbenchEditorService
) {
if (extHostContext) {
this._proxy = extHostContext.get(SqlExtHostContext.ExtHostObjectExplorer);
}
this._toDispose = [];
}
public dispose(): void {
this._toDispose = dispose(this._toDispose);
}
public $getNode(connectionId: string, nodePath?: string): Thenable<sqlops.NodeInfo> {
return this._objectExplorerService.getTreeNode(connectionId, nodePath).then(treeNode => {
if (!treeNode) {
return undefined;
}
return treeNode.toNodeInfo();
});
}
public $getActiveConnectionNodes(): Thenable<NodeInfoWithConnection[]> {
let connectionNodes = this._objectExplorerService.getActiveConnectionNodes();
return Promise.resolve(connectionNodes.map(node => {
return {connectionId: node.connection.id, nodeInfo: node.toNodeInfo()};
}));
}
public $setExpandedState(connectionId: string, nodePath: string, expandedState: vscode.TreeItemCollapsibleState): Thenable<void> {
return this._objectExplorerService.getTreeNode(connectionId, nodePath).then(treeNode => treeNode.setExpandedState(expandedState));
}
public $setSelected(connectionId: string, nodePath: string, selected: boolean, clearOtherSelections: boolean = undefined): Thenable<void> {
return this._objectExplorerService.getTreeNode(connectionId, nodePath).then(treeNode => treeNode.setSelected(selected, clearOtherSelections));
}
public $getChildren(connectionId: string, nodePath: string): Thenable<sqlops.NodeInfo[]> {
return this._objectExplorerService.getTreeNode(connectionId, nodePath).then(treeNode => treeNode.getChildren().then(children => children.map(node => node.toNodeInfo())));
}
public $isExpanded(connectionId: string, nodePath: string): Thenable<boolean> {
return this._objectExplorerService.getTreeNode(connectionId, nodePath).then(treeNode => treeNode.isExpanded());
}
}

View File

@@ -32,6 +32,7 @@ import { IExtensionApiFactory } from 'vs/workbench/api/node/extHost.api.impl';
import { ExtHostDashboardWebviews } from 'sql/workbench/api/node/extHostDashboardWebview';
import { ExtHostConnectionManagement } from 'sql/workbench/api/node/extHostConnectionManagement';
import { ExtHostDashboard } from 'sql/workbench/api/node/extHostDashboard';
import { ExtHostObjectExplorer } from 'sql/workbench/api/node/extHostObjectExplorer';
export interface ISqlExtensionApiFactory {
vsCodeFactory(extension: IExtensionDescription): typeof vscode;
@@ -56,6 +57,7 @@ export function createApiFactory(
const extHostConnectionManagement = threadService.set(SqlExtHostContext.ExtHostConnectionManagement, new ExtHostConnectionManagement(threadService));
const extHostCredentialManagement = threadService.set(SqlExtHostContext.ExtHostCredentialManagement, new ExtHostCredentialManagement(threadService));
const extHostDataProvider = threadService.set(SqlExtHostContext.ExtHostDataProtocol, new ExtHostDataProtocol(threadService));
const extHostObjectExplorer = threadService.set(SqlExtHostContext.ExtHostObjectExplorer, new ExtHostObjectExplorer(threadService));
const extHostSerializationProvider = threadService.set(SqlExtHostContext.ExtHostSerializationProvider, new ExtHostSerializationProvider(threadService));
const extHostResourceProvider = threadService.set(SqlExtHostContext.ExtHostResourceProvider, new ExtHostResourceProvider(threadService));
const extHostModalDialogs = threadService.set(SqlExtHostContext.ExtHostModalDialogs, new ExtHostModalDialogs(threadService));
@@ -105,6 +107,16 @@ export function createApiFactory(
}
};
// namespace: objectexplorer
const objectExplorer: typeof sqlops.objectexplorer = {
getNode(connectionId: string, nodePath?: string): Thenable<sqlops.objectexplorer.ObjectExplorerNode> {
return extHostObjectExplorer.$getNode(connectionId, nodePath);
},
getActiveConnectionNodes(): Thenable<sqlops.objectexplorer.ObjectExplorerNode[]> {
return extHostObjectExplorer.$getActiveConnectionNodes();
}
};
// namespace: serialization
const serialization: typeof sqlops.serialization = {
registerProvider(provider: sqlops.SerializationProvider): vscode.Disposable {
@@ -286,6 +298,7 @@ export function createApiFactory(
accounts,
connection,
credentials,
objectexplorer: objectExplorer,
resources,
serialization,
dataprotocol,

View File

@@ -13,6 +13,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import 'sql/workbench/api/node/mainThreadConnectionManagement';
import 'sql/workbench/api/node/mainThreadCredentialManagement';
import 'sql/workbench/api/node/mainThreadDataProtocol';
import 'sql/workbench/api/node/mainThreadObjectExplorer';
import 'sql/workbench/api/node/mainThreadSerializationProvider';
import 'sql/workbench/api/node/mainThreadResourceProvider';
import 'sql/workbench/api/electron-browser/mainThreadTasks';

View File

@@ -13,6 +13,7 @@ import { TPromise } from 'vs/base/common/winjs.base';
import { IDisposable } from 'vs/base/common/lifecycle';
import * as sqlops from 'sqlops';
import * as vscode from 'vscode';
import { ITaskHandlerDescription } from 'sql/platform/tasks/common/tasks';
@@ -305,7 +306,6 @@ export abstract class ExtHostDataProtocolShape {
$stopSession(handle: number, sessionId: string): Thenable<boolean> { throw ni(); }
}
/**
* ResourceProvider extension host class.
*/
@@ -419,6 +419,7 @@ export const SqlMainContext = {
MainThreadConnectionManagement: createMainId<MainThreadConnectionManagementShape>('MainThreadConnectionManagement'),
MainThreadCredentialManagement: createMainId<MainThreadCredentialManagementShape>('MainThreadCredentialManagement'),
MainThreadDataProtocol: createMainId<MainThreadDataProtocolShape>('MainThreadDataProtocol'),
MainThreadObjectExplorer: createMainId<MainThreadObjectExplorerShape>('MainThreadObjectExplorer'),
MainThreadSerializationProvider: createMainId<MainThreadSerializationProviderShape>('MainThreadSerializationProvider'),
MainThreadResourceProvider: createMainId<MainThreadResourceProviderShape>('MainThreadResourceProvider'),
MainThreadModalDialog: createMainId<MainThreadModalDialogShape>('MainThreadModalDialog'),
@@ -432,6 +433,7 @@ export const SqlExtHostContext = {
ExtHostConnectionManagement: createExtId<ExtHostConnectionManagementShape>('ExtHostConnectionManagement'),
ExtHostCredentialManagement: createExtId<ExtHostCredentialManagementShape>('ExtHostCredentialManagement'),
ExtHostDataProtocol: createExtId<ExtHostDataProtocolShape>('ExtHostDataProtocol'),
ExtHostObjectExplorer: createExtId<ExtHostObjectExplorerShape>('ExtHostObjectExplorer'),
ExtHostSerializationProvider: createExtId<ExtHostSerializationProviderShape>('ExtHostSerializationProvider'),
ExtHostResourceProvider: createExtId<ExtHostResourceProviderShape>('ExtHostResourceProvider'),
ExtHostModalDialogs: createExtId<ExtHostModalDialogsShape>('ExtHostModalDialogs'),
@@ -485,3 +487,15 @@ export interface MainThreadDashboardWebviewShape extends IDisposable {
$registerProvider(widgetId: string);
$setHtml(handle: number, value: string);
}
export interface ExtHostObjectExplorerShape {
}
export interface MainThreadObjectExplorerShape extends IDisposable {
$getNode(connectionId: string, nodePath?: string): Thenable<sqlops.NodeInfo>;
$getActiveConnectionNodes(): Thenable<{ nodeInfo: sqlops.NodeInfo, connectionId: string}[]>;
$setExpandedState(connectionId: string, nodePath: string, expandedState: vscode.TreeItemCollapsibleState): Thenable<void>;
$setSelected(connectionId: string, nodePath: string, selected: boolean, clearOtherSelections?: boolean): Thenable<void>;
$getChildren(connectionId: string, nodePath: string): Thenable<sqlops.NodeInfo[]>;
$isExpanded(connectionId: string, nodePath: string): Thenable<boolean>;
}

View File

@@ -72,8 +72,8 @@ suite('SQL Connection Tree Action tests', () => {
function createObjectExplorerService(connectionManagementService: TestConnectionManagementService): TypeMoq.Mock<ObjectExplorerService> {
let objectExplorerService = TypeMoq.Mock.ofType(ObjectExplorerService, TypeMoq.MockBehavior.Strict, connectionManagementService);
objectExplorerService.callBase = true;
objectExplorerService.setup(x => x.getObjectExplorerNode(TypeMoq.It.isAny())).returns(() => new TreeNode('', '', false, '', '', '', undefined, undefined));
objectExplorerService.setup(x => x.getObjectExplorerNode(undefined)).returns(() => new TreeNode('', '', false, '', '', '', undefined, undefined));
objectExplorerService.setup(x => x.getObjectExplorerNode(TypeMoq.It.isAny())).returns(() => new TreeNode('', '', false, '', '', '', undefined, undefined, undefined));
objectExplorerService.setup(x => x.getObjectExplorerNode(undefined)).returns(() => new TreeNode('', '', false, '', '', '', undefined, undefined, undefined));
objectExplorerService.setup(x => x.onUpdateObjectExplorerNodes).returns(() => new Emitter<ObjectExplorerNodeEventArgs>().event);
objectExplorerService.setup(x => x.onUpdateObjectExplorerNodes).returns(() => new Emitter<ObjectExplorerNodeEventArgs>().event);
@@ -144,7 +144,7 @@ suite('SQL Connection Tree Action tests', () => {
saveProfile: true,
id: 'testId'
});
let treeNode = new TreeNode(NodeType.Database, 'db node', false, '', '', '', undefined, undefined);
let treeNode = new TreeNode(NodeType.Database, 'db node', false, '', '', '', undefined, undefined, undefined);
treeNode.connection = connection;
var actionContext = new ObjectExplorerActionsContext();
actionContext.treeNode = treeNode;
@@ -395,11 +395,11 @@ suite('SQL Connection Tree Action tests', () => {
errorMessage: ''
};
var tablesNode = new TreeNode(NodeType.Folder, 'Tables', false, 'testServerName\Db1\tables', '', '', null, null);
var tablesNode = new TreeNode(NodeType.Folder, 'Tables', false, 'testServerName\Db1\tables', '', '', null, null, undefined);
tablesNode.connection = connection;
tablesNode.session = objectExplorerSession;
var table1Node = new TreeNode(NodeType.Table, 'dbo.Table1', false, 'testServerName\tables\dbo.Table1', '', '', tablesNode, null);
var table2Node = new TreeNode(NodeType.Table, 'dbo.Table1', false, 'testServerName\tables\dbo.Table1', '', '', tablesNode, null);
var table1Node = new TreeNode(NodeType.Table, 'dbo.Table1', false, 'testServerName\tables\dbo.Table1', '', '', tablesNode, null, undefined);
var table2Node = new TreeNode(NodeType.Table, 'dbo.Table1', false, 'testServerName\tables\dbo.Table1', '', '', tablesNode, null, undefined);
tablesNode.children = [table1Node, table2Node];
let objectExplorerService = TypeMoq.Mock.ofType(ObjectExplorerService, TypeMoq.MockBehavior.Loose, connectionManagementService.object);
objectExplorerService.callBase = true;
@@ -486,11 +486,11 @@ suite('SQL Connection Tree Action tests', () => {
errorMessage: ''
};
var tablesNode = new TreeNode(NodeType.Folder, 'Tables', false, 'testServerName\Db1\tables', '', '', null, null);
var tablesNode = new TreeNode(NodeType.Folder, 'Tables', false, 'testServerName\Db1\tables', '', '', null, null, undefined);
tablesNode.connection = connection;
tablesNode.session = objectExplorerSession;
var table1Node = new TreeNode(NodeType.Table, 'dbo.Table1', false, 'testServerName\tables\dbo.Table1', '', '', tablesNode, null);
var table2Node = new TreeNode(NodeType.Table, 'dbo.Table1', false, 'testServerName\tables\dbo.Table1', '', '', tablesNode, null);
var table1Node = new TreeNode(NodeType.Table, 'dbo.Table1', false, 'testServerName\tables\dbo.Table1', '', '', tablesNode, null, undefined);
var table2Node = new TreeNode(NodeType.Table, 'dbo.Table1', false, 'testServerName\tables\dbo.Table1', '', '', tablesNode, null, undefined);
tablesNode.children = [table1Node, table2Node];
let objectExplorerService = TypeMoq.Mock.ofType(ObjectExplorerService, TypeMoq.MockBehavior.Loose, connectionManagementService.object);
objectExplorerService.callBase = true;

View File

@@ -10,7 +10,7 @@ import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile
import { ConnectionProfileGroup } from 'sql/parts/connection/common/connectionProfileGroup';
import { ObjectExplorerService } from 'sql/parts/registeredServer/common/objectExplorerService';
import { NodeType } from 'sql/parts/registeredServer/common/nodeType';
import { TreeNode } from 'sql/parts/registeredServer/common/treeNode';
import { TreeNode, TreeItemCollapsibleState, ObjectExplorerCallbacks } from 'sql/parts/registeredServer/common/treeNode';
import { TPromise } from 'vs/base/common/winjs.base';
import * as sqlops from 'sqlops';
@@ -37,11 +37,12 @@ suite('SQL Object Explorer Service tests', () => {
let sessionId = '1234';
let failedSessionId = '12345';
let numberOfFailedSession: number = 0;
let serverTreeView: TypeMoq.Mock<ServerTreeView>;
setup(() => {
let NodeInfoTable1 = {
nodePath: 'testServerName\tables\dbo.Table1',
nodePath: 'testServerName/tables/dbo.Table1',
nodeType: NodeType.Table,
label: 'dbo.Table1',
isLeaf: false,
@@ -51,7 +52,7 @@ suite('SQL Object Explorer Service tests', () => {
errorMessage: ''
};
let NodeInfoTable2 = {
nodePath: 'testServerName\tables\dbo.Table2',
nodePath: 'testServerName/tables/dbo.Table2',
nodeType: NodeType.Table,
label: 'dbo.Table2',
isLeaf: false,
@@ -62,7 +63,7 @@ suite('SQL Object Explorer Service tests', () => {
};
let NodeInfoTable3 = {
nodePath: 'testServerName\tables\dbo.Table3',
nodePath: 'testServerName/tables/dbo.Table3',
nodeType: NodeType.Table,
label: 'dbo.Table3',
isLeaf: false,
@@ -76,7 +77,7 @@ suite('SQL Object Explorer Service tests', () => {
success: true,
sessionId: sessionId,
rootNode: {
nodePath: 'testServerName\tables',
nodePath: 'testServerName/tables',
nodeType: NodeType.Folder,
label: 'Tables',
isLeaf: false,
@@ -284,6 +285,14 @@ suite('SQL Object Explorer Service tests', () => {
numberOfFailedSession++;
}
});
serverTreeView = TypeMoq.Mock.ofInstance({
setExpandedState: (element, expandedState) => Promise.resolve() as Thenable<void>,
reveal: element => Promise.resolve() as Thenable<void>,
setSelected: (element, selected, clearOtherSelections) => undefined,
isExpanded: element => undefined,
onSelectionOrFocusChange: Event.None
} as ServerTreeView);
});
test('create new session should create session successfully', (done) => {
@@ -332,7 +341,7 @@ suite('SQL Object Explorer Service tests', () => {
test('expand node should expand node correctly', (done) => {
objectExplorerService.createNewSession('MSSQL', connection).then(result => {
objectExplorerService.onSessionCreated(1, objectExplorerSession);
objectExplorerService.expandNode('MSSQL', objectExplorerSession, 'testServerName\tables').then(expandInfo => {
objectExplorerService.expandNode('MSSQL', objectExplorerSession, 'testServerName/tables').then(expandInfo => {
assert.equal(expandInfo !== null || expandInfo !== undefined, true);
assert.equal(expandInfo.sessionId, '1234');
assert.equal(expandInfo.nodes.length, 2);
@@ -350,7 +359,7 @@ suite('SQL Object Explorer Service tests', () => {
test('refresh node should refresh node correctly', (done) => {
objectExplorerService.createNewSession('MSSQL', connection).then(result => {
objectExplorerService.onSessionCreated(1, objectExplorerSession);
objectExplorerService.refreshNode('MSSQL', objectExplorerSession, 'testServerName\tables').then(expandInfo => {
objectExplorerService.refreshNode('MSSQL', objectExplorerSession, 'testServerName/tables').then(expandInfo => {
assert.equal(expandInfo !== null || expandInfo !== undefined, true);
assert.equal(expandInfo.sessionId, '1234');
assert.equal(expandInfo.nodes.length, 2);
@@ -365,19 +374,19 @@ suite('SQL Object Explorer Service tests', () => {
});
});
test('expand tree node should children correctly', (done) => {
var tablesNode = new TreeNode(NodeType.Folder, 'Tables', false, 'testServerName\tables', '', '', null, null);
test('expand tree node should get correct children', (done) => {
var tablesNode = new TreeNode(NodeType.Folder, 'Tables', false, 'testServerName/tables', '', '', null, null, undefined);
tablesNode.connection = connection;
objectExplorerService.createNewSession('MSSQL', connection).then(result => {
objectExplorerService.onSessionCreated(1, objectExplorerSession);
objectExplorerService.expandTreeNode(objectExplorerSession, tablesNode).then(children => {
objectExplorerService.resolveTreeNodeChildren(objectExplorerSession, tablesNode).then(children => {
assert.equal(children !== null || children !== undefined, true);
assert.equal(children[0].label, 'dbo.Table1');
assert.equal(children[0].parent, tablesNode);
assert.equal(children[0].nodePath, 'testServerName\tables\dbo.Table1');
assert.equal(children[0].nodePath, 'testServerName/tables/dbo.Table1');
assert.equal(children[1].label, 'dbo.Table2');
assert.equal(children[1].parent, tablesNode);
assert.equal(children[1].nodePath, 'testServerName\tables\dbo.Table2');
assert.equal(children[1].nodePath, 'testServerName/tables/dbo.Table2');
done();
}, err => {
// Must call done here so test indicates it's finished if errors occur
@@ -387,7 +396,7 @@ suite('SQL Object Explorer Service tests', () => {
});
test('refresh tree node should children correctly', (done) => {
var tablesNode = new TreeNode(NodeType.Folder, 'Tables', false, 'testServerName\tables', '', '', null, null);
var tablesNode = new TreeNode(NodeType.Folder, 'Tables', false, 'testServerName/tables', '', '', null, null, undefined);
tablesNode.connection = connection;
objectExplorerService.createNewSession('MSSQL', connection).then(result => {
objectExplorerService.onSessionCreated(1, objectExplorerSession);
@@ -395,10 +404,10 @@ suite('SQL Object Explorer Service tests', () => {
assert.equal(children !== null || children !== undefined, true);
assert.equal(children[0].label, 'dbo.Table1');
assert.equal(children[0].parent, tablesNode);
assert.equal(children[0].nodePath, 'testServerName\tables\dbo.Table1');
assert.equal(children[0].nodePath, 'testServerName/tables/dbo.Table1');
assert.equal(children[1].label, 'dbo.Table3');
assert.equal(children[1].parent, tablesNode);
assert.equal(children[1].nodePath, 'testServerName\tables\dbo.Table3');
assert.equal(children[1].nodePath, 'testServerName/tables/dbo.Table3');
done();
}, err => {
// Must call done here so test indicates it's finished if errors occur
@@ -416,7 +425,7 @@ suite('SQL Object Explorer Service tests', () => {
assert.equal(treeNode.getSession(), objectExplorerSession);
assert.equal(treeNode.getConnectionProfile(), connection);
assert.equal(treeNode.label, 'Tables');
assert.equal(treeNode.nodePath, 'testServerName\tables');
assert.equal(treeNode.nodePath, 'testServerName/tables');
done();
}, err => {
// Must call done here so test indicates it's finished if errors occur
@@ -450,13 +459,13 @@ suite('SQL Object Explorer Service tests', () => {
name: 'Db1',
schema: null
};
var databaseNode = new TreeNode(NodeType.Database, 'Db1', false, 'testServerName\Db1', '', '', null, databaseMetaData);
var databaseNode = new TreeNode(NodeType.Database, 'Db1', false, 'testServerName\\Db1', '', '', null, databaseMetaData, undefined);
databaseNode.connection = connection;
databaseNode.session = objectExplorerSession;
var tablesNode = new TreeNode(NodeType.Folder, 'Tables', false, 'testServerName\Db1\tables', '', '', databaseNode, null);
var tablesNode = new TreeNode(NodeType.Folder, 'Tables', false, 'testServerName\\Db1\\tables', '', '', databaseNode, null, undefined);
databaseNode.children = [tablesNode];
var table1Node = new TreeNode(NodeType.Table, 'dbo.Table1', false, 'testServerName\Db1\tables\dbo.Table1', '', '', tablesNode, null);
var table2Node = new TreeNode(NodeType.Table, 'dbo.Table2', false, 'testServerName\Db1\tables\dbo.Table2', '', '', tablesNode, null);
var table1Node = new TreeNode(NodeType.Table, 'dbo.Table1', false, 'testServerName\\Db1\\tables\\dbo.Table1', '', '', tablesNode, null, undefined);
var table2Node = new TreeNode(NodeType.Table, 'dbo.Table2', false, 'testServerName\\Db1\\tables\\dbo.Table2', '', '', tablesNode, null, undefined);
tablesNode.children = [table1Node, table2Node];
assert.equal(table1Node.getSession(), objectExplorerSession);
assert.equal(table1Node.getConnectionProfile(), connection);
@@ -475,7 +484,7 @@ suite('SQL Object Explorer Service tests', () => {
test('getSelectedProfileAndDatabase returns the profile but no database if children of a server are selected', () => {
let serverTreeView = TypeMoq.Mock.ofInstance({ getSelection: () => undefined, onSelectionOrFocusChange: Event.None } as ServerTreeView);
let databaseNode = new TreeNode(NodeType.Folder, 'Folder1', false, 'testServerName\\Folder1', '', '', undefined, undefined);
let databaseNode = new TreeNode(NodeType.Folder, 'Folder1', false, 'testServerName\\Folder1', '', '', undefined, undefined, undefined);
databaseNode.connection = connection;
serverTreeView.setup(x => x.getSelection()).returns(() => [databaseNode]);
objectExplorerService.registerServerTreeView(serverTreeView.object);
@@ -495,8 +504,8 @@ suite('SQL Object Explorer Service tests', () => {
schema: undefined
};
let databaseName = 'Db1';
let databaseNode = new TreeNode(NodeType.Database, databaseName, false, 'testServerName\\Db1', '', '', undefined, databaseMetadata);
let tablesNode = new TreeNode(NodeType.Folder, 'Tables', false, 'testServerName\\Db1\\tables', '', '', databaseNode, undefined);
let databaseNode = new TreeNode(NodeType.Database, databaseName, false, 'testServerName\\Db1', '', '', undefined, databaseMetadata, undefined);
let tablesNode = new TreeNode(NodeType.Folder, 'Tables', false, 'testServerName\\Db1\\tables', '', '', databaseNode, undefined, undefined);
databaseNode.connection = connection;
databaseNode.children = [tablesNode];
serverTreeView.setup(x => x.getSelection()).returns(() => [tablesNode]);
@@ -515,4 +524,208 @@ suite('SQL Object Explorer Service tests', () => {
let selectedProfileAndDatabase = objectExplorerService.getSelectedProfileAndDatabase();
assert.equal(selectedProfileAndDatabase, undefined);
});
test('isExpanded returns true when the node and its parents are expanded', (done) => {
let table1NodePath = objectExplorerExpandInfo.nodes[0].nodePath;
let tableExpandInfo = {
sessionId: sessionId,
nodes: [],
errorMessage: '',
nodePath: table1NodePath
};
serverTreeView.setup(x => x.isExpanded(TypeMoq.It.isAny())).returns(treeNode => {
return treeNode === connection || treeNode.nodePath === table1NodePath;
});
objectExplorerService.registerServerTreeView(serverTreeView.object);
objectExplorerService.createNewSession('MSSQL', connection).then(result => {
objectExplorerService.onSessionCreated(1, objectExplorerSession);
objectExplorerService.resolveTreeNodeChildren(objectExplorerSession, objectExplorerService.getObjectExplorerNode(connection)).then(childNodes => {
sqlOEProvider.setup(x => x.expandNode(TypeMoq.It.isAny())).callback(() => {
objectExplorerService.onNodeExpanded(1, tableExpandInfo);
}).returns(() => TPromise.as(true));
let tableNode = childNodes.find(node => node.nodePath === table1NodePath);
objectExplorerService.resolveTreeNodeChildren(objectExplorerSession, tableNode).then(() => {
// If I check whether the table is expanded, the answer should be yes
tableNode.isExpanded().then(isExpanded => {
try {
assert.equal(isExpanded, true);
done();
} catch (err) {
done(err);
}
}, err => done(err));
}, err => done(err));
}, err => done(err));
}, err => done(err));
});
test('isExpanded returns false when the node is not expanded', (done) => {
let table1NodePath = objectExplorerExpandInfo.nodes[0].nodePath;
serverTreeView.setup(x => x.isExpanded(TypeMoq.It.isAny())).returns(treeNode => {
return treeNode === connection;
});
objectExplorerService.registerServerTreeView(serverTreeView.object);
objectExplorerService.createNewSession('MSSQL', connection).then(result => {
objectExplorerService.onSessionCreated(1, objectExplorerSession);
objectExplorerService.resolveTreeNodeChildren(objectExplorerSession, objectExplorerService.getObjectExplorerNode(connection)).then(childNodes => {
// If I check whether the table is expanded, the answer should be no because only its parent node is expanded
let tableNode = childNodes.find(node => node.nodePath === table1NodePath);
tableNode.isExpanded().then(isExpanded => {
try {
assert.equal(isExpanded, false);
done();
} catch (err) {
done(err);
}
}, err => done(err));
}, err => done(err));
}, err => done(err));
});
test('isExpanded returns false when the parent of the requested node is not expanded', (done) => {
let table1NodePath = objectExplorerExpandInfo.nodes[0].nodePath;
let tableExpandInfo = {
sessionId: sessionId,
nodes: [],
errorMessage: '',
nodePath: table1NodePath
};
serverTreeView.setup(x => x.isExpanded(TypeMoq.It.isAny())).returns(treeNode => {
return treeNode.nodePath === table1NodePath;
});
objectExplorerService.registerServerTreeView(serverTreeView.object);
objectExplorerService.createNewSession('MSSQL', connection).then(result => {
objectExplorerService.onSessionCreated(1, objectExplorerSession);
objectExplorerService.resolveTreeNodeChildren(objectExplorerSession, objectExplorerService.getObjectExplorerNode(connection)).then(childNodes => {
sqlOEProvider.setup(x => x.expandNode(TypeMoq.It.isAny())).callback(() => {
objectExplorerService.onNodeExpanded(1, tableExpandInfo);
}).returns(() => TPromise.as(true));
objectExplorerService.resolveTreeNodeChildren(objectExplorerSession, childNodes.find(node => node.nodePath === table1NodePath)).then(() => {
// If I check whether the table is expanded, the answer should be yes
let tableNode = childNodes.find(node => node.nodePath === table1NodePath);
tableNode.isExpanded().then(isExpanded => {
try {
assert.equal(isExpanded, false);
done();
} catch (err) {
done(err);
}
}, err => done(err));
}, err => done(err));
}, err => done(err));
}, err => done(err));
});
test('setting a node to expanded calls expand on the requested tree node', (done) => {
let table1NodePath = objectExplorerExpandInfo.nodes[0].nodePath;
let tableExpandInfo = {
sessionId: sessionId,
nodes: [],
errorMessage: '',
nodePath: table1NodePath
};
// Set up the OE provider so that the second expand call expands the table
sqlOEProvider.setup(x => x.expandNode(TypeMoq.It.is(nodeInfo => nodeInfo.nodePath === table1NodePath))).callback(() => {
objectExplorerService.onNodeExpanded(1, tableExpandInfo);
}).returns(() => TPromise.as(true));
serverTreeView.setup(x => x.setExpandedState(TypeMoq.It.isAny(), TypeMoq.It.is(state => state === TreeItemCollapsibleState.Expanded))).returns(treeNode => {
if (treeNode instanceof ConnectionProfile) {
treeNode = objectExplorerService.getObjectExplorerNode(treeNode);
}
return objectExplorerService.resolveTreeNodeChildren(objectExplorerSession, treeNode).then(() => undefined);
});
serverTreeView.setup(x => x.reveal(TypeMoq.It.isAny())).returns(() => Promise.resolve());
objectExplorerService.registerServerTreeView(serverTreeView.object);
objectExplorerService.createNewSession('MSSQL', connection).then(result => {
objectExplorerService.onSessionCreated(1, objectExplorerSession);
// If I expand the node, then it should get revealed and expanded
objectExplorerService.getTreeNode(connection.id, table1NodePath).then(tableNode => {
tableNode.setExpandedState(TreeItemCollapsibleState.Expanded).then(() => {
try {
serverTreeView.verify(x => x.setExpandedState(TypeMoq.It.isValue(tableNode), TypeMoq.It.is(state => state === TreeItemCollapsibleState.Expanded)), TypeMoq.Times.once());
serverTreeView.verify(x => x.reveal(TypeMoq.It.isValue(tableNode)), TypeMoq.Times.once());
done();
} catch (err) {
done(err);
}
}, err => done(err));
}, err => done(err));
});
});
test('setting a node to collapsed calls collapse on the requested tree node', (done) => {
serverTreeView.setup(x => x.isExpanded(TypeMoq.It.isAny())).returns(treeNode => {
return treeNode === connection;
});
serverTreeView.setup(x => x.setExpandedState(TypeMoq.It.is(treeNode => treeNode === connection), TypeMoq.It.is(state => state === TreeItemCollapsibleState.Collapsed))).returns(() => Promise.resolve());
objectExplorerService.registerServerTreeView(serverTreeView.object);
objectExplorerService.createNewSession('MSSQL', connection).then(result => {
objectExplorerService.onSessionCreated(1, objectExplorerSession);
objectExplorerService.resolveTreeNodeChildren(objectExplorerSession, objectExplorerService.getObjectExplorerNode(connection)).then(childNodes => {
// If I collapse the connection node, then the tree's collapse method should get called
objectExplorerService.getTreeNode(connection.id, undefined).then(treeNode => treeNode.setExpandedState(TreeItemCollapsibleState.Collapsed).then(() => {
try {
serverTreeView.verify(x => x.setExpandedState(TypeMoq.It.is(treeNode => treeNode === connection), TypeMoq.It.is(state => state === TreeItemCollapsibleState.Collapsed)), TypeMoq.Times.once());
done();
} catch (err) {
done(err);
}
}, err => done(err)));
}, err => done(err));
}, err => done(err));
});
test('setNodeSelected sets the tree selection to the requested tree node', (done) => {
let table1NodePath = objectExplorerExpandInfo.nodes[0].nodePath;
serverTreeView.setup(x => x.setSelected(TypeMoq.It.is((treeNode: TreeNode) => treeNode.nodePath === table1NodePath), TypeMoq.It.isAny(), undefined)).returns(() => Promise.resolve());
serverTreeView.setup(x => x.reveal(TypeMoq.It.isAny())).returns(() => Promise.resolve());
objectExplorerService.registerServerTreeView(serverTreeView.object);
objectExplorerService.createNewSession('MSSQL', connection).then(result => {
objectExplorerService.onSessionCreated(1, objectExplorerSession);
// If I select the table node, then it should be selected and revealed
objectExplorerService.getTreeNode(connection.id, table1NodePath).then(tableNode => {
tableNode.setSelected(true).then(() => {
try {
serverTreeView.verify(x => x.setSelected(TypeMoq.It.isValue(tableNode), TypeMoq.It.isValue(true), undefined), TypeMoq.Times.once());
serverTreeView.verify(x => x.reveal(TypeMoq.It.isValue(tableNode)), TypeMoq.Times.once());
done();
} catch (err) {
done(err);
}
}, err => done(err));
}, err => done(err));
}, err => done(err));
});
test('findTreeNode returns the tree node for the relevant node', (done) => {
let table1NodePath = objectExplorerExpandInfo.nodes[0].nodePath;
objectExplorerService.createNewSession('MSSQL', connection).then(result => {
objectExplorerService.onSessionCreated(1, objectExplorerSession);
objectExplorerService.getTreeNode(connection.id, table1NodePath).then(treeNode => {
try {
assert.equal(treeNode.nodePath, objectExplorerExpandInfo.nodes[0].nodePath);
assert.equal(treeNode.nodeTypeId, objectExplorerExpandInfo.nodes[0].nodeType);
assert.equal(treeNode.label, objectExplorerExpandInfo.nodes[0].label);
done();
} catch (err) {
done(err);
}
}, err => done(err));
});
});
test('findTreeNode returns undefined if the requested node does not exist', (done) => {
let invalidNodePath = objectExplorerSession.rootNode.nodePath + '/invalidNode';
objectExplorerService.createNewSession('MSSQL', connection).then(result => {
objectExplorerService.onSessionCreated(1, objectExplorerSession);
objectExplorerService.getTreeNode(connection.id, invalidNodePath).then(nodeInfo => {
try {
assert.equal(nodeInfo, undefined);
done();
} catch (err) {
done(err);
}
}, err => done(err));
});
});
});