mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 18:46:40 -05:00
Added Big Data Cluster Viewlet to ADS (#6204)
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 * as nls from 'vscode-nls';
|
||||
import * as azdata from 'azdata';
|
||||
import * as vscode from 'vscode';
|
||||
import { TreeNode } from './treeNode';
|
||||
import { BdcItemType } from '../constants';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
export class AddControllerNode extends TreeNode {
|
||||
private readonly nodeType: string;
|
||||
|
||||
constructor() {
|
||||
super(localize('textBigDataClusterControllerWithDots', 'Add Big Data Cluster Controller...'));
|
||||
this.nodeType = BdcItemType.addController;
|
||||
}
|
||||
|
||||
public async getChildren(): Promise<TreeNode[]> {
|
||||
return [];
|
||||
}
|
||||
|
||||
public getTreeItem(): vscode.TreeItem {
|
||||
let item = new vscode.TreeItem(this.label, vscode.TreeItemCollapsibleState.None);
|
||||
item.command = {
|
||||
title: localize('textConnectToController', 'Connect to Controller'),
|
||||
command: 'bigDataClusters.command.addController',
|
||||
arguments: [this]
|
||||
};
|
||||
item.contextValue = this.nodeType;
|
||||
return item;
|
||||
}
|
||||
|
||||
public getNodeInfo(): azdata.NodeInfo {
|
||||
return {
|
||||
label: this.label,
|
||||
isLeaf: this.isLeaf,
|
||||
errorMessage: undefined,
|
||||
metadata: undefined,
|
||||
nodePath: this.nodePath,
|
||||
nodeStatus: undefined,
|
||||
nodeType: this.nodeType,
|
||||
iconType: this.nodeType,
|
||||
nodeSubType: undefined
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { TreeNode } from './treeNode';
|
||||
|
||||
export interface IControllerTreeChangeHandler {
|
||||
notifyNodeChanged(node?: TreeNode): void;
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 * as vscode from 'vscode';
|
||||
import * as azdata from 'azdata';
|
||||
import { TreeNode } from './treeNode';
|
||||
import { IControllerTreeChangeHandler } from './controllerTreeChangeHandler';
|
||||
import { AddControllerNode } from './addControllerTreeNode';
|
||||
import { ControllerRootNode, ControllerNode } from './controllerTreeNode';
|
||||
import { IEndPoint } from '../controller/clusterControllerApi';
|
||||
import { showErrorMessage } from '../utils';
|
||||
|
||||
const ConfigNamespace = 'clusterControllers';
|
||||
const CredentialNamespace = 'clusterControllerCredentials';
|
||||
|
||||
export class ControllerTreeDataProvider implements vscode.TreeDataProvider<TreeNode>, IControllerTreeChangeHandler {
|
||||
|
||||
private _onDidChangeTreeData: vscode.EventEmitter<TreeNode> = new vscode.EventEmitter<TreeNode>();
|
||||
public readonly onDidChangeTreeData: vscode.Event<TreeNode> = this._onDidChangeTreeData.event;
|
||||
private root: ControllerRootNode;
|
||||
private credentialProvider: azdata.CredentialProvider;
|
||||
|
||||
constructor() {
|
||||
this.root = new ControllerRootNode(this);
|
||||
this.loadSavedControllers();
|
||||
}
|
||||
|
||||
public async getChildren(element?: TreeNode): Promise<TreeNode[]> {
|
||||
if (element) {
|
||||
return element.getChildren();
|
||||
}
|
||||
|
||||
if (this.root.hasChildren) {
|
||||
return this.root.getChildren();
|
||||
} else {
|
||||
return [new AddControllerNode()];
|
||||
}
|
||||
}
|
||||
|
||||
public getTreeItem(element: TreeNode): vscode.TreeItem | Thenable<vscode.TreeItem> {
|
||||
return element.getTreeItem();
|
||||
}
|
||||
|
||||
public addController(
|
||||
url: string,
|
||||
username: string,
|
||||
password: string,
|
||||
rememberPassword: boolean,
|
||||
masterInstance?: IEndPoint
|
||||
): void {
|
||||
this.root.addControllerNode(url, username, password, rememberPassword, masterInstance);
|
||||
this.notifyNodeChanged();
|
||||
}
|
||||
|
||||
public deleteController(url: string, username: string): ControllerNode {
|
||||
let deleted = this.root.deleteControllerNode(url, username);
|
||||
if (deleted) {
|
||||
this.notifyNodeChanged();
|
||||
}
|
||||
return deleted;
|
||||
}
|
||||
|
||||
public notifyNodeChanged(node?: TreeNode): void {
|
||||
this._onDidChangeTreeData.fire(node);
|
||||
}
|
||||
|
||||
public async loadSavedControllers(): Promise<void> {
|
||||
let config = vscode.workspace.getConfiguration(ConfigNamespace);
|
||||
if (config && config.controllers) {
|
||||
let controllers = config.controllers;
|
||||
this.root.clearChildren();
|
||||
for (let c of controllers) {
|
||||
let password = undefined;
|
||||
if (c.rememberPassword) {
|
||||
password = await this.getPassword(c.url, c.username);
|
||||
}
|
||||
this.root.addChild(new ControllerNode(
|
||||
c.url, c.username, password, c.rememberPassword,
|
||||
undefined, this.root, this, undefined
|
||||
));
|
||||
}
|
||||
this.notifyNodeChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public async saveControllers(): Promise<void> {
|
||||
let controllers = this.root.children.map(e => {
|
||||
let controller = e as ControllerNode;
|
||||
return {
|
||||
url: controller.url,
|
||||
username: controller.username,
|
||||
password: controller.password,
|
||||
rememberPassword: !!controller.rememberPassword
|
||||
};
|
||||
});
|
||||
|
||||
let controllersWithoutPassword = controllers.map(e => {
|
||||
return {
|
||||
url: e.url,
|
||||
username: e.username,
|
||||
rememberPassword: e.rememberPassword
|
||||
};
|
||||
});
|
||||
|
||||
try {
|
||||
await vscode.workspace.getConfiguration(ConfigNamespace).update('controllers', controllersWithoutPassword, true);
|
||||
} catch (error) {
|
||||
showErrorMessage(error);
|
||||
}
|
||||
|
||||
for (let e of controllers) {
|
||||
if (e.rememberPassword) {
|
||||
await this.savePassword(e.url, e.username, e.password);
|
||||
} else {
|
||||
await this.deletePassword(e.url, e.username);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async savePassword(url: string, username: string, password: string): Promise<boolean> {
|
||||
let provider = await this.getCredentialProvider();
|
||||
let id = this.createId(url, username);
|
||||
let result = await provider.saveCredential(id, password);
|
||||
return result;
|
||||
}
|
||||
|
||||
private async deletePassword(url: string, username: string): Promise<boolean> {
|
||||
let provider = await this.getCredentialProvider();
|
||||
let id = this.createId(url, username);
|
||||
let result = await provider.deleteCredential(id);
|
||||
return result;
|
||||
}
|
||||
|
||||
private async getPassword(url: string, username: string): Promise<string> {
|
||||
let provider = await this.getCredentialProvider();
|
||||
let id = this.createId(url, username);
|
||||
let credential = await provider.readCredential(id);
|
||||
return credential ? credential.password : undefined;
|
||||
}
|
||||
|
||||
private async getCredentialProvider(): Promise<azdata.CredentialProvider> {
|
||||
if (!this.credentialProvider) {
|
||||
this.credentialProvider = await azdata.credentials.getProvider(CredentialNamespace);
|
||||
}
|
||||
return this.credentialProvider;
|
||||
}
|
||||
|
||||
private createId(url: string, username: string): string {
|
||||
return `${url}::${username}`;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,392 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 * as vscode from 'vscode';
|
||||
import * as azdata from 'azdata';
|
||||
import * as nls from 'vscode-nls';
|
||||
import { IControllerTreeChangeHandler } from './controllerTreeChangeHandler';
|
||||
import { TreeNode } from './treeNode';
|
||||
import { IconPath, BdcItemType } from '../constants';
|
||||
import { IEndPoint, IControllerError, getEndPoints } from '../controller/clusterControllerApi';
|
||||
import { showErrorMessage } from '../utils';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
export abstract class ControllerTreeNode extends TreeNode {
|
||||
|
||||
constructor(
|
||||
label: string,
|
||||
parent: ControllerTreeNode,
|
||||
private _treeChangeHandler: IControllerTreeChangeHandler,
|
||||
private _description?: string,
|
||||
private _nodeType?: string,
|
||||
private _iconPath?: { dark: string, light: string }
|
||||
) {
|
||||
super(label, parent);
|
||||
this._description = this._description || this.label;
|
||||
}
|
||||
|
||||
public async getChildren(): Promise<ControllerTreeNode[]> {
|
||||
return this.children as ControllerTreeNode[];
|
||||
}
|
||||
|
||||
public refresh(): void {
|
||||
super.refresh();
|
||||
this.treeChangeHandler.notifyNodeChanged(this);
|
||||
}
|
||||
|
||||
public getTreeItem(): vscode.TreeItem {
|
||||
let item: vscode.TreeItem = {};
|
||||
item.id = this.id;
|
||||
item.label = this.label;
|
||||
item.collapsibleState = this.isLeaf ?
|
||||
vscode.TreeItemCollapsibleState.None :
|
||||
vscode.TreeItemCollapsibleState.Collapsed;
|
||||
item.iconPath = this._iconPath;
|
||||
item.contextValue = this._nodeType;
|
||||
item.tooltip = this._description;
|
||||
item.iconPath = this._iconPath;
|
||||
return item;
|
||||
}
|
||||
|
||||
public getNodeInfo(): azdata.NodeInfo {
|
||||
return {
|
||||
label: this.label,
|
||||
isLeaf: this.isLeaf,
|
||||
errorMessage: undefined,
|
||||
metadata: undefined,
|
||||
nodePath: this.nodePath,
|
||||
nodeStatus: undefined,
|
||||
nodeType: this._nodeType,
|
||||
iconType: this._nodeType,
|
||||
nodeSubType: undefined
|
||||
};
|
||||
}
|
||||
|
||||
public get description(): string {
|
||||
return this._description;
|
||||
}
|
||||
|
||||
public set description(description: string) {
|
||||
this._description = description;
|
||||
}
|
||||
|
||||
public get nodeType(): string {
|
||||
return this._nodeType;
|
||||
}
|
||||
|
||||
public set nodeType(nodeType: string) {
|
||||
this._nodeType = nodeType;
|
||||
}
|
||||
|
||||
public set iconPath(iconPath: { dark: string, light: string }) {
|
||||
this._iconPath = iconPath;
|
||||
}
|
||||
|
||||
public get iconPath(): { dark: string, light: string } {
|
||||
return this._iconPath;
|
||||
}
|
||||
|
||||
public set treeChangeHandler(treeChangeHandler: IControllerTreeChangeHandler) {
|
||||
this._treeChangeHandler = treeChangeHandler;
|
||||
}
|
||||
|
||||
public get treeChangeHandler(): IControllerTreeChangeHandler {
|
||||
return this._treeChangeHandler;
|
||||
}
|
||||
}
|
||||
|
||||
export class ControllerRootNode extends ControllerTreeNode {
|
||||
|
||||
private _masterNodeFactory: SqlMasterNodeFactory;
|
||||
|
||||
constructor(treeChangeHandler: IControllerTreeChangeHandler) {
|
||||
super('root', undefined, treeChangeHandler, undefined, BdcItemType.controllerRoot);
|
||||
this._masterNodeFactory = new SqlMasterNodeFactory();
|
||||
}
|
||||
|
||||
public async getChildren(): Promise<ControllerNode[]> {
|
||||
return this.children as ControllerNode[];
|
||||
}
|
||||
|
||||
public addControllerNode(url: string, username: string, password: string, rememberPassword: boolean, masterInstance?: IEndPoint): void {
|
||||
let controllerNode = this.getExistingControllerNode(url, username);
|
||||
if (controllerNode) {
|
||||
controllerNode.password = password;
|
||||
controllerNode.rememberPassword = rememberPassword;
|
||||
controllerNode.clearChildren();
|
||||
} else {
|
||||
controllerNode = new ControllerNode(url, username, password, rememberPassword, undefined, this, this.treeChangeHandler, undefined);
|
||||
this.addChild(controllerNode);
|
||||
}
|
||||
|
||||
if (masterInstance) {
|
||||
controllerNode.addSqlMasterNode(masterInstance.endpoint, masterInstance.description);
|
||||
}
|
||||
}
|
||||
|
||||
public deleteControllerNode(url: string, username: string): ControllerNode {
|
||||
if (!url || !username) {
|
||||
return undefined;
|
||||
}
|
||||
let nodes = this.children as ControllerNode[];
|
||||
let index = nodes.findIndex(e => e.url === url && e.username === username);
|
||||
let deleted = undefined;
|
||||
if (index >= 0) {
|
||||
deleted = nodes.splice(index, 1);
|
||||
}
|
||||
return deleted;
|
||||
}
|
||||
|
||||
private getExistingControllerNode(url: string, username: string): ControllerNode {
|
||||
if (!url || !username) {
|
||||
return undefined;
|
||||
}
|
||||
let nodes = this.children as ControllerNode[];
|
||||
return nodes.find(e => e.url === url && e.username === username);
|
||||
}
|
||||
|
||||
public get sqlMasterNodeFactory(): SqlMasterNodeFactory {
|
||||
return this._masterNodeFactory;
|
||||
}
|
||||
}
|
||||
|
||||
export class ControllerNode extends ControllerTreeNode {
|
||||
|
||||
constructor(
|
||||
private _url: string,
|
||||
private _username: string,
|
||||
private _password: string,
|
||||
private _rememberPassword: boolean,
|
||||
label: string,
|
||||
parent: ControllerTreeNode,
|
||||
treeChangeHandler: IControllerTreeChangeHandler,
|
||||
description?: string,
|
||||
) {
|
||||
super(label, parent, treeChangeHandler, description, BdcItemType.controller, IconPath.controllerNode);
|
||||
this.label = label;
|
||||
this.description = description;
|
||||
|
||||
}
|
||||
|
||||
public async getChildren(): Promise<ControllerTreeNode[]> {
|
||||
if (this.children && this.children.length > 0) {
|
||||
this.clearChildren();
|
||||
}
|
||||
|
||||
if (!this._password) {
|
||||
vscode.commands.executeCommand('bigDataClusters.command.addController', this);
|
||||
return this.children as ControllerTreeNode[];
|
||||
}
|
||||
|
||||
try {
|
||||
let response = await getEndPoints(this._url, this._username, this._password, true);
|
||||
if (response && response.endPoints) {
|
||||
let master = response.endPoints.find(e => e.name && e.name === 'sql-server-master');
|
||||
this.addSqlMasterNode(master.endpoint, master.description);
|
||||
}
|
||||
return this.children as ControllerTreeNode[];
|
||||
} catch (error) {
|
||||
showErrorMessage(error);
|
||||
return this.children as ControllerTreeNode[];
|
||||
}
|
||||
}
|
||||
|
||||
private static toIpAndPort(url: string): string {
|
||||
if (!url) {
|
||||
return;
|
||||
}
|
||||
return url.trim().replace(/ /g, '').replace(/^.+\:\/\//, '').replace(/:(\d+)$/, ',$1');
|
||||
}
|
||||
|
||||
public addSqlMasterNode(endPointAddress: string, description: string): void {
|
||||
let epFolder = this.getEndPointFolderNode();
|
||||
let node = (this.root as ControllerRootNode).sqlMasterNodeFactory
|
||||
.getSqlMasterNode(endPointAddress, epFolder, undefined, this.treeChangeHandler, description);
|
||||
epFolder.addChild(node);
|
||||
}
|
||||
|
||||
private getEndPointFolderNode(): FolderNode {
|
||||
let label = localize('textSqlServers', 'SQL Servers');
|
||||
let epFolderNode = this.children.find(e => e instanceof FolderNode && e.label === label);
|
||||
if (!epFolderNode) {
|
||||
epFolderNode = new FolderNode(label, this, this.treeChangeHandler);
|
||||
this.addChild(epFolderNode);
|
||||
}
|
||||
return epFolderNode as FolderNode;
|
||||
}
|
||||
|
||||
public getTreeItem(): vscode.TreeItem {
|
||||
let item: vscode.TreeItem = super.getTreeItem();
|
||||
item.collapsibleState = vscode.TreeItemCollapsibleState.Collapsed;
|
||||
return item;
|
||||
}
|
||||
|
||||
public get url() {
|
||||
return this._url;
|
||||
}
|
||||
|
||||
public set url(url: string) {
|
||||
this._url = url;
|
||||
}
|
||||
|
||||
public get username() {
|
||||
return this._username;
|
||||
}
|
||||
|
||||
public set username(username: string) {
|
||||
this._username = username;
|
||||
}
|
||||
|
||||
public get password() {
|
||||
return this._password;
|
||||
}
|
||||
|
||||
public set password(pw: string) {
|
||||
this._password = pw;
|
||||
}
|
||||
|
||||
public get rememberPassword() {
|
||||
return this._rememberPassword;
|
||||
}
|
||||
|
||||
public set rememberPassword(rememberPassword: boolean) {
|
||||
this._rememberPassword = rememberPassword;
|
||||
}
|
||||
|
||||
public set label(label: string) {
|
||||
super.label = label || `controller: ${ControllerNode.toIpAndPort(this._url)} (${this._username})`;
|
||||
}
|
||||
|
||||
public get label(): string {
|
||||
return super.label;
|
||||
}
|
||||
|
||||
public set description(description: string) {
|
||||
super.description = description || super.label;
|
||||
}
|
||||
|
||||
public get description(): string {
|
||||
return super.description;
|
||||
}
|
||||
}
|
||||
|
||||
export class FolderNode extends ControllerTreeNode {
|
||||
constructor(
|
||||
label: string,
|
||||
parent: ControllerTreeNode,
|
||||
treeChangeHandler: IControllerTreeChangeHandler
|
||||
) {
|
||||
super(label, parent, treeChangeHandler, label, BdcItemType.folder, IconPath.folderNode);
|
||||
}
|
||||
}
|
||||
|
||||
export class SqlMasterNode extends ControllerTreeNode {
|
||||
private static readonly _role: string = 'sql-server-master';
|
||||
private _username: string;
|
||||
private _password: string;
|
||||
|
||||
constructor(
|
||||
private _endPointAddress: string,
|
||||
parent: ControllerTreeNode,
|
||||
label: string,
|
||||
treeChangeHandler: IControllerTreeChangeHandler,
|
||||
description?: string,
|
||||
) {
|
||||
super(label, parent, treeChangeHandler, description, BdcItemType.sqlMaster, IconPath.sqlMasterNode);
|
||||
this._username = 'sa';
|
||||
this.label = label;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
private getControllerPassword(): string {
|
||||
if (!this._password) {
|
||||
let current: TreeNode = this;
|
||||
while (current && !(current instanceof ControllerNode)) {
|
||||
current = current.parent;
|
||||
}
|
||||
this._password = current && current instanceof ControllerNode ? current.password : undefined;
|
||||
}
|
||||
return this._password;
|
||||
}
|
||||
|
||||
public getTreeItem(): vscode.TreeItem {
|
||||
let item = super.getTreeItem();
|
||||
let connectionProfile: azdata.IConnectionProfile = {
|
||||
id: this.id,
|
||||
connectionName: this.id,
|
||||
serverName: this._endPointAddress,
|
||||
databaseName: '',
|
||||
userName: this._username,
|
||||
password: this.getControllerPassword(),
|
||||
authenticationType: 'SqlLogin',
|
||||
savePassword: false,
|
||||
groupFullName: '',
|
||||
groupId: '',
|
||||
providerName: 'MSSQL',
|
||||
saveProfile: false,
|
||||
options: {}
|
||||
};
|
||||
return Object.assign(item, { payload: connectionProfile, childProvider: 'MSSQL' });
|
||||
}
|
||||
|
||||
public get role() {
|
||||
return SqlMasterNode._role;
|
||||
}
|
||||
|
||||
public get endPointAddress() {
|
||||
return this._endPointAddress;
|
||||
}
|
||||
|
||||
public set endPointAddress(endPointAddress: string) {
|
||||
this._endPointAddress = endPointAddress;
|
||||
}
|
||||
|
||||
public set label(label: string) {
|
||||
super.label = label || `master: ${this._endPointAddress} (${this._username})`;
|
||||
}
|
||||
|
||||
public get label(): string {
|
||||
return super.label;
|
||||
}
|
||||
|
||||
public set description(description: string) {
|
||||
super.description = description || super.label;
|
||||
}
|
||||
|
||||
public get description(): string {
|
||||
return super.description;
|
||||
}
|
||||
}
|
||||
|
||||
export class SqlMasterNodeFactory {
|
||||
private registry: {} = {};
|
||||
|
||||
public getSqlMasterNode(
|
||||
endPointAddress: string,
|
||||
parent: ControllerTreeNode,
|
||||
label: string,
|
||||
treeChangeHandler: IControllerTreeChangeHandler,
|
||||
description?: string
|
||||
): SqlMasterNode {
|
||||
let id = this.createRegistryId(endPointAddress, 'sa');
|
||||
if (!this.registry[id]) {
|
||||
this.registry[id] = new SqlMasterNode(endPointAddress, parent, label, treeChangeHandler, description);
|
||||
} else {
|
||||
let node = this.registry[id] as SqlMasterNode;
|
||||
node.parent = parent;
|
||||
node.label = label;
|
||||
node.treeChangeHandler = treeChangeHandler;
|
||||
description = description;
|
||||
}
|
||||
return this.registry[id] as SqlMasterNode;
|
||||
}
|
||||
|
||||
private createRegistryId(endPointAddress: string, username: string): string {
|
||||
return `${endPointAddress}::${username}`;
|
||||
}
|
||||
}
|
||||
195
extensions/big-data-cluster/src/bigDataCluster/tree/treeNode.ts
Normal file
195
extensions/big-data-cluster/src/bigDataCluster/tree/treeNode.ts
Normal file
@@ -0,0 +1,195 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 * as azdata from 'azdata';
|
||||
import * as vscode from 'vscode';
|
||||
import { generateGuid } from '../utils';
|
||||
|
||||
export abstract class TreeNode {
|
||||
|
||||
private _id: string;
|
||||
private _children: TreeNode[];
|
||||
private _isLeaf: boolean;
|
||||
|
||||
constructor(private _label: string, private _parent?: TreeNode) {
|
||||
this.resetId();
|
||||
}
|
||||
|
||||
public resetId(): void {
|
||||
this._id = (this._label || '_') + `::${generateGuid()}`;
|
||||
}
|
||||
|
||||
public get id(): string {
|
||||
return this._id;
|
||||
}
|
||||
|
||||
public set label(label: string) {
|
||||
if (!this._label) {
|
||||
this._label = label;
|
||||
this.resetId();
|
||||
} else {
|
||||
this._label = label;
|
||||
}
|
||||
}
|
||||
|
||||
public get label(): string {
|
||||
return this._label;
|
||||
}
|
||||
|
||||
public set parent(parent: TreeNode) {
|
||||
this._parent = parent;
|
||||
}
|
||||
|
||||
public get parent(): TreeNode {
|
||||
return this._parent;
|
||||
}
|
||||
|
||||
public get children(): TreeNode[] {
|
||||
if (!this._children) {
|
||||
this._children = [];
|
||||
}
|
||||
return this._children;
|
||||
}
|
||||
|
||||
public get hasChildren(): boolean {
|
||||
return this.children && this.children.length > 0;
|
||||
}
|
||||
|
||||
public set isLeaf(isLeaf: boolean) {
|
||||
this._isLeaf = isLeaf;
|
||||
}
|
||||
|
||||
public get isLeaf(): boolean {
|
||||
return this._isLeaf;
|
||||
}
|
||||
|
||||
public get root(): TreeNode {
|
||||
return TreeNode.getRoot(this);
|
||||
}
|
||||
|
||||
public equals(node: TreeNode): boolean {
|
||||
if (!node) {
|
||||
return undefined;
|
||||
}
|
||||
return this.nodePath === node.nodePath;
|
||||
}
|
||||
|
||||
public refresh(): void {
|
||||
this.resetId();
|
||||
}
|
||||
|
||||
public static getRoot(node: TreeNode): TreeNode {
|
||||
if (!node) {
|
||||
return undefined;
|
||||
}
|
||||
let current: TreeNode = node;
|
||||
while (current.parent) {
|
||||
current = current.parent;
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
public get nodePath(): string {
|
||||
return TreeNode.getNodePath(this);
|
||||
}
|
||||
|
||||
public static getNodePath(node: TreeNode): string {
|
||||
if (!node) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let current: TreeNode = node;
|
||||
let path = current._id;
|
||||
while (current.parent) {
|
||||
current = current.parent;
|
||||
path = `${current._id}/${path}`;
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
public async findNode(condition: (node: TreeNode) => boolean, expandIfNeeded?: boolean): Promise<TreeNode> {
|
||||
return TreeNode.findNode(this, condition, expandIfNeeded);
|
||||
}
|
||||
|
||||
public static async findNode(node: TreeNode, condition: (node: TreeNode) => boolean, expandIfNeeded?: boolean): Promise<TreeNode> {
|
||||
if (!node || !condition) {
|
||||
return undefined;
|
||||
}
|
||||
let result: TreeNode = undefined;
|
||||
let nodesToCheck: TreeNode[] = [node];
|
||||
while (nodesToCheck.length > 0) {
|
||||
let current = nodesToCheck.shift();
|
||||
if (condition(current)) {
|
||||
result = current;
|
||||
break;
|
||||
}
|
||||
if (current.hasChildren) {
|
||||
nodesToCheck = nodesToCheck.concat(current.children);
|
||||
} else if (expandIfNeeded) {
|
||||
let children = await current.getChildren();
|
||||
if (children && children.length > 0) {
|
||||
nodesToCheck = nodesToCheck.concat(children);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public async filterNode(condition: (node: TreeNode) => boolean, expandIfNeeded?: boolean): Promise<TreeNode[]> {
|
||||
return TreeNode.filterNode(this, condition, expandIfNeeded);
|
||||
}
|
||||
|
||||
public static async filterNode(node: TreeNode, condition: (node: TreeNode) => boolean, expandIfNeeded?: boolean): Promise<TreeNode[]> {
|
||||
if (!node || !condition) {
|
||||
return undefined;
|
||||
}
|
||||
let result: TreeNode[] = [];
|
||||
let nodesToCheck: TreeNode[] = [node];
|
||||
while (nodesToCheck.length > 0) {
|
||||
let current = nodesToCheck.shift();
|
||||
if (condition(current)) {
|
||||
result.push(current);
|
||||
}
|
||||
if (current.hasChildren) {
|
||||
nodesToCheck = nodesToCheck.concat(current.children);
|
||||
} else if (expandIfNeeded) {
|
||||
let children = await current.getChildren();
|
||||
if (children && children.length > 0) {
|
||||
nodesToCheck = nodesToCheck.concat(children);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public async findNodeByPath(path: string, expandIfNeeded?: boolean): Promise<TreeNode> {
|
||||
return TreeNode.findNodeByPath(this, path, expandIfNeeded);
|
||||
}
|
||||
|
||||
public static async findNodeByPath(node: TreeNode, path: string, expandIfNeeded?: boolean): Promise<TreeNode> {
|
||||
return TreeNode.findNode(node, node => {
|
||||
return node.nodePath && (node.nodePath === path || node.nodePath.startsWith(path));
|
||||
}, expandIfNeeded);
|
||||
}
|
||||
|
||||
public addChild(node: TreeNode): void {
|
||||
if (!this._children) {
|
||||
this._children = [];
|
||||
}
|
||||
this._children.push(node);
|
||||
}
|
||||
|
||||
public clearChildren(): void {
|
||||
if (this._children) {
|
||||
this._children = [];
|
||||
}
|
||||
}
|
||||
|
||||
public abstract async getChildren(): Promise<TreeNode[]>;
|
||||
public abstract getTreeItem(): vscode.TreeItem;
|
||||
public abstract getNodeInfo(): azdata.NodeInfo;
|
||||
}
|
||||
Reference in New Issue
Block a user