1) Fixed an issue where the process watcher dialog throws an error for
the database server which is already removed. Fixes #5985 2) Fixed cognitive complexity reported by SonarQube.pull/37/head
parent
a026f339c3
commit
be386e77f2
|
@ -11,6 +11,7 @@ notes for it.
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 1
|
:maxdepth: 1
|
||||||
|
|
||||||
|
release_notes_4_29
|
||||||
release_notes_4_28
|
release_notes_4_28
|
||||||
release_notes_4_27
|
release_notes_4_27
|
||||||
release_notes_4_26
|
release_notes_4_26
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
************
|
||||||
|
Version 4.29
|
||||||
|
************
|
||||||
|
|
||||||
|
Release date: 2020-12-10
|
||||||
|
|
||||||
|
This release contains a number of bug fixes and new features since the release of pgAdmin4 4.28.
|
||||||
|
|
||||||
|
New features
|
||||||
|
************
|
||||||
|
|
||||||
|
|
||||||
|
Housekeeping
|
||||||
|
************
|
||||||
|
|
||||||
|
|
||||||
|
Bug fixes
|
||||||
|
*********
|
||||||
|
|
||||||
|
| `Issue #5985 <https://redmine.postgresql.org/issues/5985>`_ - Fixed an issue where the process watcher dialog throws an error for the database server which is already removed.
|
|
@ -262,3 +262,21 @@ def remove_saved_passwords(user_id):
|
||||||
except Exception as _:
|
except Exception as _:
|
||||||
db.session.rollback()
|
db.session.rollback()
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
def does_server_exists(sid, user_id):
|
||||||
|
|
||||||
|
"""
|
||||||
|
This function will return True if server is existing for a user
|
||||||
|
:param sid: server id
|
||||||
|
:param user_id: user id
|
||||||
|
:return: Boolean
|
||||||
|
"""
|
||||||
|
# **kwargs parameter can be added to function to filter with more
|
||||||
|
# parameters.
|
||||||
|
try:
|
||||||
|
return True if Server.query.filter_by(
|
||||||
|
id=sid, user_id=user_id
|
||||||
|
).first() is not None else False
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
|
|
@ -23,6 +23,7 @@ import logging
|
||||||
|
|
||||||
from pgadmin.utils import u_encode, file_quote, fs_encoding, \
|
from pgadmin.utils import u_encode, file_quote, fs_encoding, \
|
||||||
get_complete_file_path, get_storage_directory, IS_WIN
|
get_complete_file_path, get_storage_directory, IS_WIN
|
||||||
|
from pgadmin.browser.server_groups.servers.utils import does_server_exists
|
||||||
|
|
||||||
import pytz
|
import pytz
|
||||||
from dateutil import parser
|
from dateutil import parser
|
||||||
|
@ -68,9 +69,9 @@ class IProcessDesc(object, metaclass=ABCMeta):
|
||||||
try:
|
try:
|
||||||
# check if file name is encoded with UTF-8
|
# check if file name is encoded with UTF-8
|
||||||
file = self.bfile.decode('utf-8')
|
file = self.bfile.decode('utf-8')
|
||||||
except Exception as e:
|
except Exception:
|
||||||
str(e)
|
|
||||||
# do nothing if bfile is not encoded.
|
# do nothing if bfile is not encoded.
|
||||||
|
pass
|
||||||
|
|
||||||
path = get_complete_file_path(file)
|
path = get_complete_file_path(file)
|
||||||
path = file if path is None else path
|
path = file if path is None else path
|
||||||
|
@ -87,14 +88,7 @@ class IProcessDesc(object, metaclass=ABCMeta):
|
||||||
else:
|
else:
|
||||||
last_dir = file
|
last_dir = file
|
||||||
|
|
||||||
if IS_WIN:
|
last_dir = replace_path_for_win(last_dir)
|
||||||
if '\\' in last_dir:
|
|
||||||
if len(last_dir) == 1:
|
|
||||||
last_dir = last_dir.replace('\\', '\\\\')
|
|
||||||
else:
|
|
||||||
last_dir = last_dir.replace('\\', '/')
|
|
||||||
else:
|
|
||||||
last_dir = last_dir.replace('\\', '/')
|
|
||||||
|
|
||||||
return None if hasattr(self, 'is_import') and self.is_import \
|
return None if hasattr(self, 'is_import') and self.is_import \
|
||||||
else last_dir
|
else last_dir
|
||||||
|
@ -102,6 +96,16 @@ class IProcessDesc(object, metaclass=ABCMeta):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def replace_path_for_win(last_dir=None):
|
||||||
|
if IS_WIN:
|
||||||
|
if '\\' in last_dir and len(last_dir) == 1:
|
||||||
|
last_dir = last_dir.replace('\\', '\\\\')
|
||||||
|
else:
|
||||||
|
last_dir = last_dir.replace('\\', '/')
|
||||||
|
|
||||||
|
return last_dir
|
||||||
|
|
||||||
|
|
||||||
class BatchProcess(object):
|
class BatchProcess(object):
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
|
|
||||||
|
@ -625,6 +629,9 @@ class BatchProcess(object):
|
||||||
):
|
):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
if BatchProcess._operate_orphan_process(p):
|
||||||
|
continue
|
||||||
|
|
||||||
execution_time = None
|
execution_time = None
|
||||||
|
|
||||||
stime = parser.parse(p.start_time)
|
stime = parser.parse(p.start_time)
|
||||||
|
@ -654,6 +661,29 @@ class BatchProcess(object):
|
||||||
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _operate_orphan_process(p):
|
||||||
|
|
||||||
|
if p and p.desc:
|
||||||
|
desc = loads(p.desc)
|
||||||
|
if does_server_exists(desc.sid, current_user.id) is False:
|
||||||
|
current_app.logger.warning(
|
||||||
|
_("Server with id '{0}' is either removed or does "
|
||||||
|
"not exists for the background process "
|
||||||
|
"'{1}'").format(desc.sid, p.pid)
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
BatchProcess.acknowledge(p.pid)
|
||||||
|
except LookupError as lerr:
|
||||||
|
current_app.logger.warning(
|
||||||
|
_("Status for the background process '{0}' could "
|
||||||
|
"not be loaded.").format(p.pid)
|
||||||
|
)
|
||||||
|
current_app.logger.exception(lerr)
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def total_seconds(dt):
|
def total_seconds(dt):
|
||||||
return round(dt.total_seconds(), 2)
|
return round(dt.total_seconds(), 2)
|
||||||
|
|
|
@ -141,11 +141,6 @@ class BackupMessage(IProcessDesc):
|
||||||
# It should never reach here.
|
# It should never reach here.
|
||||||
return _("Unknown Backup")
|
return _("Unknown Backup")
|
||||||
|
|
||||||
# @property
|
|
||||||
# def current_storage_dir(self):
|
|
||||||
# return self.bfile if os.path.isdir(self.bfile) \
|
|
||||||
# else os.path.dirname(self.bfile)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def message(self):
|
def message(self):
|
||||||
name, host, port = self.get_server_details()
|
name, host, port = self.get_server_details()
|
||||||
|
|
|
@ -199,11 +199,18 @@ class BatchProcessTest(BaseTestGenerator):
|
||||||
|
|
||||||
self.assertTrue(popen_mock.called)
|
self.assertTrue(popen_mock.called)
|
||||||
|
|
||||||
|
@patch('os.path.realpath')
|
||||||
|
@patch('pgadmin.misc.bgprocess.processes.get_storage_directory')
|
||||||
|
@patch('pgadmin.misc.bgprocess.processes.get_complete_file_path')
|
||||||
@patch('pgadmin.misc.bgprocess.processes.Process')
|
@patch('pgadmin.misc.bgprocess.processes.Process')
|
||||||
@patch('pgadmin.misc.bgprocess.processes.BatchProcess.'
|
@patch('pgadmin.misc.bgprocess.processes.BatchProcess.'
|
||||||
'update_process_info')
|
'update_process_info')
|
||||||
def _check_list(self, p, backup_obj, update_process_info_mock,
|
@patch('pgadmin.misc.bgprocess.processes.BatchProcess.'
|
||||||
process_mock):
|
'_operate_orphan_process')
|
||||||
|
def _check_list(self, p, backup_obj, _operate_orphan_process_mock,
|
||||||
|
update_process_info_mock, process_mock,
|
||||||
|
get_complete_file_path_mock, get_storage_directory_mock,
|
||||||
|
realpath_mock):
|
||||||
class TestMockProcess():
|
class TestMockProcess():
|
||||||
def __init__(self, desc, args, cmd):
|
def __init__(self, desc, args, cmd):
|
||||||
self.pid = 1
|
self.pid = 1
|
||||||
|
@ -222,6 +229,10 @@ class BatchProcessTest(BaseTestGenerator):
|
||||||
self.class_params['cmd'])]
|
self.class_params['cmd'])]
|
||||||
|
|
||||||
update_process_info_mock.return_value = [True, True]
|
update_process_info_mock.return_value = [True, True]
|
||||||
|
get_complete_file_path_mock.return_value = self.class_params['bfile']
|
||||||
|
realpath_mock.return_value = self.class_params['bfile']
|
||||||
|
get_storage_directory_mock.return_value = '//'
|
||||||
|
_operate_orphan_process_mock.return_value = False
|
||||||
|
|
||||||
ret_value = p.list()
|
ret_value = p.list()
|
||||||
self.assertEqual(1, len(ret_value))
|
self.assertEqual(1, len(ret_value))
|
||||||
|
|
|
@ -212,9 +212,11 @@ class BatchProcessTest(BaseTestGenerator):
|
||||||
@patch('pgadmin.misc.bgprocess.processes.Process')
|
@patch('pgadmin.misc.bgprocess.processes.Process')
|
||||||
@patch('pgadmin.misc.bgprocess.processes.BatchProcess.'
|
@patch('pgadmin.misc.bgprocess.processes.BatchProcess.'
|
||||||
'update_process_info')
|
'update_process_info')
|
||||||
def _check_list(self, p, import_export_obj, update_process_info_mock,
|
@patch('pgadmin.misc.bgprocess.processes.BatchProcess.'
|
||||||
process_mock,
|
'_operate_orphan_process')
|
||||||
get_storage_directory_mock, get_complete_file_path_mock,
|
def _check_list(self, p, import_export_obj, _operate_orphan_process_mock,
|
||||||
|
update_process_info_mock, process_mock,
|
||||||
|
get_complete_file_path_mock, get_storage_directory_mock,
|
||||||
realpath_mock):
|
realpath_mock):
|
||||||
class TestMockProcess():
|
class TestMockProcess():
|
||||||
def __init__(self, desc, args, cmd):
|
def __init__(self, desc, args, cmd):
|
||||||
|
@ -237,6 +239,7 @@ class BatchProcessTest(BaseTestGenerator):
|
||||||
get_complete_file_path_mock.return_value = self.params['filename']
|
get_complete_file_path_mock.return_value = self.params['filename']
|
||||||
realpath_mock.return_value = self.params['filename']
|
realpath_mock.return_value = self.params['filename']
|
||||||
get_storage_directory_mock.return_value = '//'
|
get_storage_directory_mock.return_value = '//'
|
||||||
|
_operate_orphan_process_mock.return_value = False
|
||||||
|
|
||||||
ret_value = p.list()
|
ret_value = p.list()
|
||||||
self.assertEqual(1, len(ret_value))
|
self.assertEqual(1, len(ret_value))
|
||||||
|
|
|
@ -65,7 +65,7 @@ define([
|
||||||
var params = {
|
var params = {
|
||||||
supported_types: ['sql', 'csv', '*'],
|
supported_types: ['sql', 'csv', '*'],
|
||||||
dialog_type: 'storage_dialog',
|
dialog_type: 'storage_dialog',
|
||||||
dialog_title: 'Storage Manager...',
|
dialog_title: 'Storage Manager',
|
||||||
btn_primary: undefined,
|
btn_primary: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue