codekingpro/portable-devtools
114k
1/////////////////////////////////////////////////////////////2//3// pgAdmin 4 - PostgreSQL Tools4//5// Copyright (C) 2013 - 2024, The pgAdmin Development Team6// This software is released under the PostgreSQL Licence7//8//////////////////////////////////////////////////////////////9 10import * as BrowserFS from 'browserfs';11import url_for from 'sources/url_for';12import pgAdmin from 'sources/pgadmin';13import _ from 'lodash';14import { FileType } from 'react-aspen';15import { findInTree } from './tree';16import gettext from 'sources/gettext';17 18import { unix } from 'path-fx';19import getApiInstance, { parseApiError } from '../api_instance';20 21export class ManageTreeNodes {22 constructor() {23 this.tree = {};24 this.tempTree = new TreeNode(undefined, {});25 }26 27 public init = (_root: string) => new Promise((res) => {28 const node = {parent: null, children: [], data: null};29 this.tree = {};30 this.tree[_root] = {name: 'root', type: FileType.Directory, metadata: node};31 res();32 });33 34 public updateNode = (_path, _data) => new Promise((res) => {35 const item = this.findNode(_path);36 if (item) {37 item.data = {...item.data, ..._data};38 item.name = _data.label;39 item.metadata.data = _data;40 }41 res(true);42 });43 44 public removeNode = async (_path) => {45 const item = this.findNode(_path);46 47 if (item && item.parentNode) {48 item.children = [];49 item.parentNode.children.splice(item.parentNode.children.indexOf(item), 1);50 }51 return true;52 };53 54 findNode(path) {55 if (path === null || path === undefined || path.length === 0 || path == '/browser') {56 return this.tempTree;57 }58 return findInTree(this.tempTree, path);59 }60 61 public addNode = (_parent: string, _path: string, _data: []) => new Promise((res) => {62 _data.type = _data.inode ? FileType.Directory : FileType.File;63 _data._label = _data.label;64 _data.label = _.escape(_data.label);65 66 _data.is_collection = isCollectionNode(_data._type);67 const nodeData = {parent: _parent, children: [], data: _data};68 69 const tmpParentNode = this.findNode(_parent);70 const treeNode = new TreeNode(_data.id, _data, {}, tmpParentNode, nodeData, _data.type);71 72 if (tmpParentNode !== null && tmpParentNode !== undefined) tmpParentNode.children.push(treeNode);73 74 res(treeNode);75 });76 77 public readNode = async (_path: string) => {78 let temp_tree_path = _path;79 const node = this.findNode(_path);80 const base_url = pgAdmin.Browser.URL;81 const api = getApiInstance();82 83 if (node && node.children.length > 0) {84 if (node.type !== FileType.File) {85 console.error(node, 'It\'s a leaf node');86 return [];87 }88 else {89 if (node.children.length != 0) return node.children;90 }91 }92 93 const self = this;94 let url = '';95 if (_path == '/browser') {96 url = url_for('browser.nodes');97 } else {98 const _parent_url = self.generate_url(_path);99 if (node.metadata.data._pid == null ) {100 url = node.metadata.data._type + '/children/' + node.metadata.data._id;101 }102 else {103 if (node.metadata.data._type.includes('coll-')) {104 const _type = node.metadata.data._type.replace('coll-', '');105 url = _type + '/nodes/' + _parent_url + '/';106 }107 else {108 url = node.metadata.data._type + '/children/' + _parent_url + '/' + node.metadata.data._id;109 }110 }111 112 url = base_url + url;113 114 temp_tree_path = node.path;115 116 if (node.metadata.data._type == 'server' && !node.metadata.data.connected) {117 url = null;118 }119 }120 121 let treeData = [];122 if (url) {123 try {124 const res = await api.get(url);125 treeData = res.data.data;126 } catch (error) {127 /* react-aspen does not handle reject case */128 console.error(error);129 pgAdmin.Browser.notifier.error(parseApiError(error)||'Node Load Error...');130 return [];131 }132 }133 134 const Path = BrowserFS.BFSRequire('path');135 for (const idx in treeData) {136 const _node: any = treeData[idx];137 const _pathl = Path.join(_path, _node.id);138 await self.addNode(temp_tree_path, _pathl, _node);139 }140 if (node.children.length > 0) return node.children;141 else {142 if (node.data && node.data._type == 'server' && node.data.connected) {143 pgAdmin.Browser.notifier.info(gettext('Server children are not available.'144 +' Please check these nodes are not hidden through the preferences setting `Browser > Nodes`.'), null);145 }146 return [];147 }148 };149 150 public generate_url = (path: string) => {151 let _path = path;152 const _parent_path = [];153 let _partitions = [];154 while(_path != '/') {155 const node = this.findNode(_path);156 const _parent = unix.dirname(_path);157 if(node.parentNode && node.parentNode.path == _parent) {158 if (node.parentNode.metadata.data !== null && !node.parentNode.metadata.data._type.includes('coll-'))159 if(node.parentNode.metadata.data._type.includes('partition')) {160 _partitions.push(node.parentNode.metadata.data._id);161 } else {162 _parent_path.push(node.parentNode.metadata.data._id);163 }164 }165 _path = _parent;166 }167 _partitions = _partitions.reverse();168 // Replace the table with the last partition as in reality partition node is not child of the table169 if(_partitions.length > 0) _parent_path[0] = _partitions[_partitions.length-1];170 171 return _parent_path.reverse().join('/');172 };173}174 175 176 177export class TreeNode {178 constructor(id, data, domNode, parent, metadata, type) {179 this.id = id;180 this.data = data;181 this.setParent(parent);182 this.children = [];183 this.domNode = domNode;184 this.metadata = metadata;185 this.name = metadata ? metadata.data.label : '';186 this.type = type ? type : undefined;187 }188 189 hasParent() {190 return this.parentNode !== null && this.parentNode !== undefined;191 }192 193 parent() {194 return this.parentNode;195 }196 197 setParent(parent) {198 this.parentNode = parent;199 this.path = this.id;200 if (this.id)201 if (parent !== null && parent !== undefined && parent.path !== undefined) {202 this.path = parent.path + '/' + this.id;203 } else {204 this.path = '/browser/' + this.id;205 }206 }207 208 getData() {209 if (this.data === undefined) {210 return undefined;211 } else if (this.data === null) {212 return null;213 }214 return Object.assign({}, this.data);215 }216 217 getHtmlIdentifier() {218 return this.domNode;219 }220 221 /*222 * Find the ancestor with matches this condition223 */224 ancestorNode(condition) {225 let node = this;226 227 while (node.hasParent()) {228 node = node.parent();229 if (condition(node)) {230 return node;231 }232 }233 234 return null;235 }236 237 /**238 * Given a condition returns true if the current node239 * or any of the parent nodes condition result is true240 */241 anyFamilyMember(condition) {242 if(condition(this)) {243 return true;244 }245 246 return this.ancestorNode(condition) !== null;247 }248 anyParent(condition) {249 return this.ancestorNode(condition) !== null;250 }251 252 reload(tree) {253 return new Promise((resolve)=>{254 this.unload(tree)255 .then(()=>{256 tree.setInode(this.domNode);257 tree.deselect(this.domNode);258 setTimeout(() => {259 tree.selectNode(this.domNode);260 }, 0);261 resolve();262 });263 });264 }265 266 unload(tree) {267 return new Promise((resolve, reject)=>{268 this.children = [];269 tree.unload(this.domNode)270 .then(271 ()=>{272 resolve(true);273 },274 ()=>{275 reject();276 });277 });278 }279 280 281 open(tree, suppressNoDom) {282 return new Promise((resolve, reject)=>{283 if(suppressNoDom && (this.domNode == null || typeof(this.domNode) === 'undefined')) {284 resolve(true);285 } else if(tree.isOpen(this.domNode)) {286 resolve(true);287 } else {288 tree.open(this.domNode).then(() => resolve(true), () => reject(true));289 }290 });291 }292 293}294 295export function isCollectionNode(node) {296 if (pgAdmin.Browser.Nodes && node in pgAdmin.Browser.Nodes) {297 if (pgAdmin.Browser.Nodes[node].is_collection !== undefined) return pgAdmin.Browser.Nodes[node].is_collection;298 else return false;299 }300 return false;301}302 