Skip to content

Commit 0874eb6

Browse files
Fixed an issue where pgAdmin failed to update the server connection status when the server was disconnected in the background and a refresh was performed on that server. #8149
1 parent 22fd919 commit 0874eb6

File tree

5 files changed

+57
-419
lines changed

5 files changed

+57
-419
lines changed

web/pgadmin/browser/server_groups/servers/__init__.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
from pgadmin.model import db, Server, ServerGroup, User, SharedServer
3232
from pgadmin.utils.driver import get_driver
3333
from pgadmin.utils.master_password import get_crypt_key
34-
from pgadmin.utils.exception import CryptKeyMissing
34+
from pgadmin.utils.exception import CryptKeyMissing, ConnectionLost
3535
from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry
3636
from pgadmin.browser.server_groups.servers.utils import \
3737
(is_valid_ipaddress, get_replication_type, convert_connection_parameter,
@@ -627,12 +627,18 @@ def node(self, gid, sid):
627627
in_recovery = None
628628
wal_paused = None
629629
if connected:
630-
status, result, in_recovery, wal_paused =\
631-
recovery_state(conn, manager.version)
632-
if not status:
630+
try:
631+
status, result, in_recovery, wal_paused =\
632+
recovery_state(conn, manager.version)
633+
634+
if not status:
635+
connected = False
636+
manager.release()
637+
errmsg = "{0} : {1}".format(server.name, result)
638+
639+
except ConnectionLost:
633640
connected = False
634641
manager.release()
635-
errmsg = "{0} : {1}".format(server.name, result)
636642

637643
return make_json_response(
638644
result=self.blueprint.generate_browser_node(

web/pgadmin/browser/static/js/browser.js

Lines changed: 30 additions & 165 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
import MainMenuFactory from './MainMenuFactory';
1111
import _ from 'lodash';
1212
import { checkMasterPassword, showQuickSearch } from '../../../static/js/Dialogs/index';
13-
import { pgHandleItemError } from '../../../static/js/utils';
1413
import { send_heartbeat, stop_heartbeat } from './heartbeat';
1514
import getApiInstance, {parseApiError} from '../../../static/js/api_instance';
1615
import usePreferences, { setupPreferenceBroadcast } from '../../../preferences/static/js/store';
@@ -196,7 +195,7 @@ define('pgadmin.browser', [
196195
obj.check_corrupted_db_file();
197196
obj.Events.on('pgadmin:browser:tree:add', obj.onAddTreeNode.bind(obj));
198197
obj.Events.on('pgadmin:browser:tree:update', obj.onUpdateTreeNode.bind(obj));
199-
obj.Events.on('pgadmin:browser:tree:refresh', obj.onRefreshTreeNodeReact.bind(obj));
198+
obj.Events.on('pgadmin:browser:tree:refresh', obj.onRefreshTreeNode.bind(obj));
200199
obj.Events.on('pgadmin-browser:tree:loadfail', obj.onLoadFailNode.bind(obj));
201200
obj.bind_beforeunload();
202201

@@ -1239,173 +1238,39 @@ define('pgadmin.browser', [
12391238
}
12401239
},
12411240

1242-
onRefreshTreeNodeReact: function(_i, _opts) {
1243-
this.tree.refresh(_i).then(() =>{
1244-
if (_opts?.success) _opts.success();
1245-
});
1246-
},
1247-
1248-
onRefreshTreeNode: function(_i, _opts) {
1249-
let _d = _i && this.tree.itemData(_i),
1250-
n = this.Nodes[_d?._type],
1251-
ctx = {
1252-
b: this, // Browser
1253-
d: _d, // current parent
1254-
i: _i, // current item
1255-
p: null, // path of the old object
1256-
pathOfTreeItems: [], // path items
1257-
t: this.tree, // Tree Api
1258-
o: _opts,
1259-
},
1260-
isOpen,
1261-
idx = -1;
1262-
1263-
this.Events.trigger('pgadmin-browser:tree:refreshing', _i, _d, n);
1264-
1265-
if (!n) {
1266-
_i = null;
1267-
ctx.i = null;
1268-
ctx.d = null;
1269-
} else {
1270-
isOpen = (this.tree.isInode(_i) && this.tree.isOpen(_i));
1271-
}
1272-
1273-
ctx.branch = ctx.t.serialize(
1274-
_i, {}, function(i, el, d) {
1275-
idx++;
1276-
if (!idx || (d.inode && d.open)) {
1277-
return {
1278-
_id: d._id, _type: d._type, branch: d.branch, open: d.open,
1279-
};
1280-
}
1281-
});
1282-
1283-
if (!n) {
1284-
ctx.t.destroy({
1285-
success: function() {
1286-
ctx.t = ctx.b.tree;
1287-
ctx.i = null;
1288-
ctx.b._refreshNode(ctx, ctx.branch);
1289-
},
1290-
error: function() {
1291-
let fail = _opts.o?.fail || _opts?.fail;
1292-
1293-
if (typeof(fail) == 'function') {
1294-
fail();
1295-
}
1296-
},
1297-
});
1298-
return;
1299-
}
1300-
1301-
let api = getApiInstance();
1302-
let fetchNodeInfo = function(__i, __d, __n) {
1303-
let info = __n.getTreeNodeHierarchy(__i),
1304-
url = __n.generate_url(__i, 'nodes', __d, true);
1305-
1306-
api.get(
1307-
url
1308-
).then(({data: res})=> {
1309-
// Node information can come as result/data
1310-
let newData = res.result || res.data;
1311-
1312-
newData._label = newData.label;
1313-
newData.label = _.escape(newData.label);
1314-
1315-
ctx.t.setLabel(ctx.i, {label: newData.label});
1316-
ctx.t.addIcon(ctx.i, {icon: newData.icon});
1317-
ctx.t.setId(ctx.i, {id: newData.id});
1318-
if (newData.inode)
1319-
ctx.t.setInode(ctx.i, {inode: true});
1320-
1321-
// This will update the tree item data.
1322-
let itemData = ctx.t.itemData(ctx.i);
1323-
_.extend(itemData, newData);
1324-
1325-
if (
1326-
__n.can_expand && typeof(__n.can_expand) == 'function'
1327-
) {
1328-
if (!__n.can_expand(itemData)) {
1329-
ctx.t.unload(ctx.i);
1330-
return;
1331-
}
1332-
}
1333-
ctx.b._refreshNode(ctx, ctx.branch);
1334-
let success = (ctx?.o?.success) || ctx.success;
1335-
if (success && typeof(success) == 'function') {
1336-
success();
1337-
}
1338-
}).catch(function(error) {
1339-
if (!pgHandleItemError(
1340-
error, {item: __i, info: info}
1341-
)) {
1342-
if(error.response.headers['content-type'] == 'application/json') {
1343-
let jsonResp = error.response.data ?? {};
1344-
if(error.response.status == 410 && jsonResp.success == 0) {
1345-
let parent = ctx.t.parent(ctx.i);
1346-
1347-
ctx.t.remove(ctx.i, {
1348-
success: function() {
1349-
if (parent) {
1350-
// Try to refresh the parent on error
1351-
try {
1352-
pgBrowser.Events.trigger(
1353-
'pgadmin:browser:tree:refresh', parent
1354-
);
1355-
} catch (e) { console.warn(e.stack || e); }
1356-
}
1357-
},
1358-
});
1359-
}
1360-
}
1361-
1362-
pgAdmin.Browser.notifier.pgNotifier('error', error, gettext('Error retrieving details for the node.'), function (msg) {
1363-
if (msg == 'CRYPTKEY_SET') {
1364-
fetchNodeInfo(__i, __d, __n);
1365-
} else {
1366-
console.warn(arguments);
1367-
}
1241+
onRefreshTreeNode: async function(nodeItem, opts) {
1242+
this.tree.toggleItemLoader(nodeItem, true);
1243+
1244+
const itemNodeData = nodeItem && this.tree.itemData(nodeItem);
1245+
let nodeObj = this.Nodes[itemNodeData?._type];
1246+
1247+
// If the node is a collection node, we can directly refresh it.
1248+
if(!nodeObj?.collection_node) {
1249+
// If the node is not a collection node, we need to fetch its data
1250+
// from the server and update the tree node.
1251+
try {
1252+
const url = nodeObj.generate_url(nodeItem, 'nodes', itemNodeData, true);
1253+
const api = getApiInstance();
1254+
const resp = await api.get(url);
1255+
// server response data comes in result
1256+
const respData = resp.data.data || resp.data.result;
1257+
1258+
if(respData) {
1259+
this.tree.update(nodeItem, {
1260+
...itemNodeData, ...respData
13681261
});
1262+
this.tree.setLabel(nodeItem, {label: respData.label});
1263+
this.tree.addIcon(nodeItem, {icon: respData.icon});
13691264
}
1370-
});
1371-
};
1372-
1373-
if (n?.collection_node) {
1374-
let p = ctx.i = this.tree.parent(_i),
1375-
unloadNode = function() {
1376-
this.tree.unload(_i, {
1377-
success: function() {
1378-
_i = p;
1379-
_d = ctx.d = ctx.t.itemData(ctx.i);
1380-
n = ctx.b.Nodes[_d._type];
1381-
_i = p;
1382-
fetchNodeInfo(_i, _d, n);
1383-
},
1384-
fail: function() { console.warn(arguments); },
1385-
});
1386-
}.bind(this);
1387-
if (!this.tree.isInode(_i)) {
1388-
this.tree.setInode(_i, { success: unloadNode });
1389-
} else {
1390-
unloadNode();
1265+
} catch (error) {
1266+
console.error('Failed to refresh tree node:', error);
1267+
return;
13911268
}
1392-
} else if (isOpen) {
1393-
this.tree.unload(_i, {
1394-
success: fetchNodeInfo.bind(this, _i, _d, n),
1395-
fail: function() {
1396-
console.warn(arguments);
1397-
},
1398-
});
1399-
} else if (!this.tree.isInode(_i) && _d.inode) {
1400-
this.tree.setInode(_i, {
1401-
success: fetchNodeInfo.bind(this, _i, _d, n),
1402-
fail: function() {
1403-
console.warn(arguments);
1404-
},
1405-
});
1406-
} else {
1407-
fetchNodeInfo(_i, _d, n);
14081269
}
1270+
1271+
await this.tree.refresh(nodeItem);
1272+
this.tree.toggleItemLoader(nodeItem, false);
1273+
opts?.success?.();
14091274
},
14101275

14111276
onLoadFailNode: function(_nodeData) {

web/pgadmin/static/js/components/PgTree/FileTreeX/index.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ export class FileTreeX extends React.Component<IFileTreeXProps> {
152152
resize: this.resize,
153153
showLoader: this.showLoader,
154154
hideLoader: this.hideLoader,
155+
toggleItemLoader: this.toggleItemLoader,
155156
};
156157

157158
model.decorations.addDecoration(this.activeFileDec);
@@ -556,6 +557,17 @@ export class FileTreeX extends React.Component<IFileTreeXProps> {
556557

557558
};
558559

560+
private readonly toggleItemLoader = (item: FileOrDir, show=false) => {
561+
const ref = FileTreeItem.itemIdToRefMap.get(item.id);
562+
if (ref) {
563+
if (show) {
564+
this.showLoader(ref);
565+
} else {
566+
this.hideLoader(ref);
567+
}
568+
}
569+
};
570+
559571
private readonly showLoader = (ref: HTMLDivElement) => {
560572
// get label ref and add loading class
561573
ref.style.background = 'none';

0 commit comments

Comments
 (0)