2021-09-27 06:14:26 -05:00
|
|
|
/////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// pgAdmin 4 - PostgreSQL Tools
|
|
|
|
//
|
|
|
|
// Copyright (C) 2013 - 2021, The pgAdmin Development Team
|
|
|
|
// This software is released under the PostgreSQL Licence
|
|
|
|
//
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
import * as BrowserFS from 'browserfs'
|
|
|
|
import url_for from 'sources/url_for';
|
|
|
|
import pgAdmin from 'sources/pgadmin';
|
|
|
|
import _ from 'underscore';
|
|
|
|
import { FileType } from 'react-aspen'
|
|
|
|
import { findInTree } from './tree';
|
|
|
|
|
2021-09-29 03:05:54 -05:00
|
|
|
import { dirname, unix } from 'path-fx';
|
2021-09-27 06:14:26 -05:00
|
|
|
|
|
|
|
export class ManageTreeNodes {
|
|
|
|
constructor(fs) {
|
|
|
|
this.tree = {}
|
|
|
|
this.tempTree = new TreeNode(undefined, {});
|
|
|
|
}
|
|
|
|
|
|
|
|
public init = (_root: string) => new Promise((res, rej) => {
|
|
|
|
let node = {parent: null, children: [], data: null};
|
|
|
|
this.tree = {};
|
|
|
|
this.tree[_root] = {name: 'root', type: FileType.Directory, metadata: node};
|
|
|
|
res();
|
|
|
|
})
|
|
|
|
|
|
|
|
public updateNode = (_path, _data) => new Promise((res, rej) => {
|
|
|
|
if (_path in this.tree) {
|
|
|
|
const item = this.tree[_path];
|
|
|
|
item.name = _data.label;
|
|
|
|
item.metadata.data = _data;
|
|
|
|
}
|
|
|
|
res(true);
|
|
|
|
})
|
|
|
|
|
|
|
|
public removeNode = async (_path, _removeOnlyChild) => {
|
|
|
|
const item = this.findNode(_path);
|
|
|
|
|
|
|
|
if (item && item.parentNode) {
|
|
|
|
item.children = [];
|
|
|
|
item.parentNode.children.splice(item.parentNode.children.indexOf(item), 1);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
};
|
|
|
|
|
|
|
|
findNode(path) {
|
|
|
|
if (path === null || path === undefined || path.length === 0 || path == '/browser') {
|
|
|
|
return this.tempTree;
|
|
|
|
}
|
|
|
|
return findInTree(this.tempTree, path);
|
|
|
|
}
|
|
|
|
|
|
|
|
public addNode = (_parent: string, _path: string, _data: []) => new Promise((res, rej) => {
|
|
|
|
_data.type = _data.inode ? FileType.Directory : FileType.File;
|
|
|
|
_data._label = _data.label;
|
|
|
|
_data.label = _.escape(_data.label);
|
|
|
|
|
|
|
|
_data.is_collection = isCollectionNode(_data._type);
|
|
|
|
let nodeData = {parent: _parent, children: [], data: _data};
|
|
|
|
|
|
|
|
let tmpParentNode = this.findNode(_parent);
|
|
|
|
let treeNode = new TreeNode(_data.id, _data, {}, tmpParentNode, nodeData, _data.type);
|
|
|
|
|
|
|
|
if (tmpParentNode !== null && tmpParentNode !== undefined) tmpParentNode.children.push(treeNode);
|
|
|
|
|
|
|
|
res(treeNode);
|
|
|
|
})
|
|
|
|
|
|
|
|
public readNode = (_path: string) => new Promise<string[]>((res, rej) => {
|
|
|
|
let temp_tree_path = _path;
|
|
|
|
let node = this.findNode(_path);
|
|
|
|
|
|
|
|
if (node && node.children.length > 0) {
|
|
|
|
if (!node.type === FileType.File) {
|
|
|
|
rej("It's a leaf node")
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (node.children.length != 0) res(node.children)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var self = this;
|
|
|
|
|
|
|
|
async function loadData() {
|
|
|
|
let url = '';
|
|
|
|
if (_path == '/browser') {
|
|
|
|
url = url_for('browser.nodes');
|
|
|
|
} else {
|
|
|
|
let _parent_url = self.generate_url(_path);
|
|
|
|
if (node.metadata.data._pid == null ) {
|
|
|
|
url = node.metadata.data._type + '/children/' + node.metadata.data._id;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (node.metadata.data._type.includes("coll-")) {
|
|
|
|
let _type = node.metadata.data._type.replace("coll-", "")
|
|
|
|
url = _type + '/nodes/' + _parent_url;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
url = node.metadata.data._type + '/children/' + _parent_url + '/' + node.metadata.data._id;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
temp_tree_path = node.path;
|
|
|
|
|
|
|
|
if (node.metadata.data._type == 'server' && !node.metadata.data.connected) {
|
|
|
|
url = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async function jsonData(url) {
|
|
|
|
let res = await fetch(url, {
|
|
|
|
headers: {
|
|
|
|
'X-Requested-With': 'XMLHttpRequest',
|
|
|
|
'X-pgA-CSRFToken': pgAdmin.csrf_token
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
if (res.status == 200) {
|
|
|
|
try {
|
|
|
|
let json = await res.json();
|
|
|
|
return json.data;
|
|
|
|
} catch (e) {
|
|
|
|
console.warn(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
throw new Error("Node Load Error...");
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
|
|
|
|
let treeData = null;
|
|
|
|
if (url) treeData = await jsonData(url);
|
|
|
|
|
|
|
|
const Path = BrowserFS.BFSRequire('path')
|
|
|
|
const fill = async (tree) => {
|
|
|
|
for (let idx in tree) {
|
|
|
|
const _node = tree[idx]
|
|
|
|
const _pathl = Path.join(_path, _node.id)
|
|
|
|
await self.addNode(temp_tree_path, _pathl, _node);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let d = await fill(treeData);
|
|
|
|
if (node.children.length > 0) res(node.children);
|
|
|
|
else res(null);
|
|
|
|
|
|
|
|
}
|
|
|
|
loadData();
|
|
|
|
})
|
|
|
|
|
|
|
|
public generate_url = (path: string) => {
|
|
|
|
let _path = path;
|
|
|
|
let _parent_path = [];
|
2021-10-04 05:45:49 -05:00
|
|
|
let _partitions = [];
|
2021-09-27 06:14:26 -05:00
|
|
|
while(_path != '/') {
|
|
|
|
let node = this.findNode(_path);
|
2021-09-29 03:05:54 -05:00
|
|
|
let _parent = unix.dirname(_path);
|
2021-09-27 06:14:26 -05:00
|
|
|
if(node.parentNode && node.parentNode.path == _parent) {
|
|
|
|
if (node.parentNode.metadata.data !== null && !node.parentNode.metadata.data._type.includes('coll-'))
|
2021-10-04 05:45:49 -05:00
|
|
|
if(node.parentNode.metadata.data._type.includes('partition')) {
|
|
|
|
_partitions.push(node.parentNode.metadata.data._id);
|
|
|
|
} else {
|
|
|
|
_parent_path.push(node.parentNode.metadata.data._id);
|
|
|
|
}
|
2021-09-27 06:14:26 -05:00
|
|
|
}
|
|
|
|
_path = _parent;
|
|
|
|
}
|
2021-10-04 05:45:49 -05:00
|
|
|
// Replace the table with the last partition as in reality partition node is not child of the table
|
|
|
|
if(_partitions.length > 0) _parent_path[0] = _partitions[_partitions.length-1];
|
|
|
|
|
2021-09-27 06:14:26 -05:00
|
|
|
let _rev_arr = _parent_path.reverse();
|
|
|
|
return _rev_arr.join("/");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export class TreeNode {
|
|
|
|
constructor(id, data, domNode, parent, metadata, type) {
|
|
|
|
this.id = id;
|
|
|
|
this.data = data;
|
|
|
|
this.setParent(parent);
|
|
|
|
this.children = [];
|
|
|
|
this.domNode = domNode;
|
|
|
|
this.metadata = metadata;
|
|
|
|
this.name = metadata ? metadata.data.label : "";
|
|
|
|
this.type = type ? type : undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
hasParent() {
|
|
|
|
return this.parentNode !== null && this.parentNode !== undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
parent() {
|
|
|
|
return this.parentNode;
|
|
|
|
}
|
|
|
|
|
|
|
|
setParent(parent) {
|
|
|
|
this.parentNode = parent;
|
|
|
|
this.path = this.id;
|
|
|
|
if (this.id)
|
|
|
|
if (parent !== null && parent !== undefined && parent.path !== undefined) {
|
|
|
|
this.path = parent.path + '/' + this.id;
|
|
|
|
} else {
|
|
|
|
this.path = '/browser/' + this.id;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
getData() {
|
|
|
|
if (this.data === undefined) {
|
|
|
|
return undefined;
|
|
|
|
} else if (this.data === null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return Object.assign({}, this.data);
|
|
|
|
}
|
|
|
|
|
|
|
|
getHtmlIdentifier() {
|
|
|
|
return this.domNode;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find the ancestor with matches this condition
|
|
|
|
*/
|
|
|
|
ancestorNode(condition) {
|
|
|
|
let node = this;
|
|
|
|
|
|
|
|
while (node.hasParent()) {
|
|
|
|
node = node.parent();
|
|
|
|
if (condition(node)) {
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Given a condition returns true if the current node
|
|
|
|
* or any of the parent nodes condition result is true
|
|
|
|
*/
|
|
|
|
anyFamilyMember(condition) {
|
|
|
|
if(condition(this)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.ancestorNode(condition) !== null;
|
|
|
|
}
|
|
|
|
anyParent(condition) {
|
|
|
|
return this.ancestorNode(condition) !== null;
|
|
|
|
}
|
|
|
|
|
|
|
|
reload(tree) {
|
|
|
|
return new Promise((resolve)=>{
|
|
|
|
this.unload(tree)
|
|
|
|
.then(()=>{
|
|
|
|
tree.setInode(this.domNode);
|
|
|
|
tree.deselect(this.domNode);
|
|
|
|
setTimeout(() => {
|
|
|
|
tree.selectNode(this.domNode);
|
|
|
|
}, 0);
|
|
|
|
resolve();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
unload(tree) {
|
|
|
|
return new Promise((resolve, reject)=>{
|
|
|
|
this.children = [];
|
|
|
|
tree.unload(this.domNode)
|
|
|
|
.then(
|
|
|
|
()=>{
|
|
|
|
resolve(true);
|
|
|
|
},
|
|
|
|
()=>{
|
|
|
|
reject();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
open(tree, suppressNoDom) {
|
|
|
|
return new Promise((resolve, reject)=>{
|
|
|
|
if(suppressNoDom && (this.domNode == null || typeof(this.domNode) === 'undefined')) {
|
|
|
|
resolve(true);
|
|
|
|
} else if(tree.isOpen(this.domNode)) {
|
|
|
|
resolve(true);
|
|
|
|
} else {
|
|
|
|
tree.open(this.domNode).then(val => resolve(true), err => reject(true));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
export function isCollectionNode(node) {
|
|
|
|
if (pgAdmin.Browser.Nodes && node in pgAdmin.Browser.Nodes) {
|
|
|
|
if (pgAdmin.Browser.Nodes[node].is_collection !== undefined) return pgAdmin.Browser.Nodes[node].is_collection;
|
|
|
|
else return false;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|