mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2025-02-25 18:55:31 -06:00
Added copy server support, allowing the duplication of existing servers with the option to make certain modifications. #6085 (#7106)
Added copy server support, allowing the duplication of existing servers with the option to make certain modifications. #6085
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 20 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 112 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 65 KiB |
@@ -42,6 +42,12 @@ following options (in alphabetical order):
|
|||||||
+-----------------------------+--------------------------------------------------------------------------------------------------------------------------+
|
+-----------------------------+--------------------------------------------------------------------------------------------------------------------------+
|
||||||
| Option | Action |
|
| Option | Action |
|
||||||
+=============================+==========================================================================================================================+
|
+=============================+==========================================================================================================================+
|
||||||
|
| *Register* | |
|
||||||
|
| | |
|
||||||
|
| 1) *Server* | Click to open the :ref:`Server <server_dialog>` dialog to register a server. |
|
||||||
|
| | |
|
||||||
|
| 2) *Deploy Cloud Instance*| Click to open the :ref:`Cloud Deployment <cloud_deployment>` dialog to deploy an cloud instance. |
|
||||||
|
+-----------------------------+--------------------------------------------------------------------------------------------------------------------------+
|
||||||
| *Change Password...* | Click to open the :ref:`Change Password... <change_password_dialog>` dialog to change your password. |
|
| *Change Password...* | Click to open the :ref:`Change Password... <change_password_dialog>` dialog to change your password. |
|
||||||
+-----------------------------+--------------------------------------------------------------------------------------------------------------------------+
|
+-----------------------------+--------------------------------------------------------------------------------------------------------------------------+
|
||||||
| *Clear Saved Password* | If you have saved the database server password, click to clear the saved password. |
|
| *Clear Saved Password* | If you have saved the database server password, click to clear the saved password. |
|
||||||
@@ -52,6 +58,8 @@ following options (in alphabetical order):
|
|||||||
+-----------------------------+--------------------------------------------------------------------------------------------------------------------------+
|
+-----------------------------+--------------------------------------------------------------------------------------------------------------------------+
|
||||||
| *Connect Server* | Click to open the :ref:`Connect to Server <connect_to_server>` dialog to establish a connection with a server. |
|
| *Connect Server* | Click to open the :ref:`Connect to Server <connect_to_server>` dialog to establish a connection with a server. |
|
||||||
+-----------------------------+--------------------------------------------------------------------------------------------------------------------------+
|
+-----------------------------+--------------------------------------------------------------------------------------------------------------------------+
|
||||||
|
| *Copy Server...* | Click to copy the currently selected server. |
|
||||||
|
+-----------------------------+--------------------------------------------------------------------------------------------------------------------------+
|
||||||
| *Create* | Click *Create* to access a context menu that provides context-sensitive selections. |
|
| *Create* | Click *Create* to access a context menu that provides context-sensitive selections. |
|
||||||
| | Your selection opens a *Create* dialog for creating a new object. |
|
| | Your selection opens a *Create* dialog for creating a new object. |
|
||||||
+-----------------------------+--------------------------------------------------------------------------------------------------------------------------+
|
+-----------------------------+--------------------------------------------------------------------------------------------------------------------------+
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ New features
|
|||||||
|
|
||||||
| `Issue #2483 <https://github.com/pgadmin-org/pgadmin4/issues/2483>`_ - Administer pgAdmin Users and Preferences Using the Command Line Interface (CLI).
|
| `Issue #2483 <https://github.com/pgadmin-org/pgadmin4/issues/2483>`_ - Administer pgAdmin Users and Preferences Using the Command Line Interface (CLI).
|
||||||
| `Issue #5908 <https://github.com/pgadmin-org/pgadmin4/issues/5908>`_ - Allow users to convert View/Edit table into a Query tool to enable editing the SQL generated.
|
| `Issue #5908 <https://github.com/pgadmin-org/pgadmin4/issues/5908>`_ - Allow users to convert View/Edit table into a Query tool to enable editing the SQL generated.
|
||||||
|
| `Issue #6085 <https://github.com/pgadmin-org/pgadmin4/issues/6085>`_ - Added copy server support, allowing the duplication of existing servers with the option to make certain modifications.
|
||||||
| `Issue #7016 <https://github.com/pgadmin-org/pgadmin4/issues/7016>`_ - Added keep-alive support for SSH sessions when connecting to a PostgreSQL server via an SSH tunnel.
|
| `Issue #7016 <https://github.com/pgadmin-org/pgadmin4/issues/7016>`_ - Added keep-alive support for SSH sessions when connecting to a PostgreSQL server via an SSH tunnel.
|
||||||
|
|
||||||
Housekeeping
|
Housekeeping
|
||||||
|
|||||||
@@ -39,8 +39,9 @@ def get_icon_css_class(group_id, group_user_id,
|
|||||||
group_user_id != current_user.id and
|
group_user_id != current_user.id and
|
||||||
ServerGroupModule.has_shared_server(group_id)):
|
ServerGroupModule.has_shared_server(group_id)):
|
||||||
default_val = 'icon-server_group_shared'
|
default_val = 'icon-server_group_shared'
|
||||||
|
return default_val, True
|
||||||
|
|
||||||
return default_val
|
return default_val, False
|
||||||
|
|
||||||
|
|
||||||
SG_NOT_FOUND_ERROR = 'The specified server group could not be found.'
|
SG_NOT_FOUND_ERROR = 'The specified server group could not be found.'
|
||||||
@@ -86,14 +87,16 @@ class ServerGroupModule(BrowserPluginModule):
|
|||||||
).order_by("id")
|
).order_by("id")
|
||||||
|
|
||||||
for idx, group in enumerate(groups):
|
for idx, group in enumerate(groups):
|
||||||
|
icon_class, is_shared = get_icon_css_class(group.id, group.user_id)
|
||||||
yield self.generate_browser_node(
|
yield self.generate_browser_node(
|
||||||
"%d" % (group.id), None,
|
"%d" % (group.id), None,
|
||||||
group.name,
|
group.name,
|
||||||
get_icon_css_class(group.id, group.user_id),
|
icon_class,
|
||||||
True,
|
True,
|
||||||
self.node_type,
|
self.node_type,
|
||||||
can_delete=True if idx > 0 else False,
|
can_delete=True if idx > 0 else False,
|
||||||
user_id=group.user_id
|
user_id=group.user_id,
|
||||||
|
is_shared=is_shared
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -264,15 +267,17 @@ class ServerGroupView(NodeView):
|
|||||||
status=410, success=0, errormsg=e.message
|
status=410, success=0, errormsg=e.message
|
||||||
)
|
)
|
||||||
|
|
||||||
|
icon_class, is_shared = get_icon_css_class(gid, servergroup.user_id)
|
||||||
return jsonify(
|
return jsonify(
|
||||||
node=self.blueprint.generate_browser_node(
|
node=self.blueprint.generate_browser_node(
|
||||||
gid,
|
gid,
|
||||||
None,
|
None,
|
||||||
servergroup.name,
|
servergroup.name,
|
||||||
get_icon_css_class(gid, servergroup.user_id),
|
icon_class,
|
||||||
True,
|
True,
|
||||||
self.node_type,
|
self.node_type,
|
||||||
can_delete=True # This is user created hence can deleted
|
can_delete=True, # This is user created hence can delete
|
||||||
|
is_shared=is_shared
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -311,16 +316,18 @@ class ServerGroupView(NodeView):
|
|||||||
data['id'] = sg.id
|
data['id'] = sg.id
|
||||||
data['name'] = sg.name
|
data['name'] = sg.name
|
||||||
|
|
||||||
|
icon_class, is_shared = get_icon_css_class(sg.id, sg.user_id)
|
||||||
return jsonify(
|
return jsonify(
|
||||||
node=self.blueprint.generate_browser_node(
|
node=self.blueprint.generate_browser_node(
|
||||||
"%d" % sg.id,
|
"%d" % sg.id,
|
||||||
None,
|
None,
|
||||||
sg.name,
|
sg.name,
|
||||||
get_icon_css_class(sg.id, sg.user_id),
|
icon_class,
|
||||||
True,
|
True,
|
||||||
self.node_type,
|
self.node_type,
|
||||||
# This is user created hence can deleted
|
# This is user created hence can deleted
|
||||||
can_delete=True
|
can_delete=True,
|
||||||
|
is_shared=is_shared
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
except exc.IntegrityError:
|
except exc.IntegrityError:
|
||||||
@@ -399,14 +406,17 @@ class ServerGroupView(NodeView):
|
|||||||
groups = ServerGroup.query.filter_by(user_id=current_user.id)
|
groups = ServerGroup.query.filter_by(user_id=current_user.id)
|
||||||
|
|
||||||
for group in groups:
|
for group in groups:
|
||||||
|
icon_class, is_shared = get_icon_css_class(group.id,
|
||||||
|
group.user_id)
|
||||||
nodes.append(
|
nodes.append(
|
||||||
self.blueprint.generate_browser_node(
|
self.blueprint.generate_browser_node(
|
||||||
"%d" % group.id,
|
"%d" % group.id,
|
||||||
None,
|
None,
|
||||||
group.name,
|
group.name,
|
||||||
get_icon_css_class(group.id, group.user_id),
|
icon_class,
|
||||||
True,
|
True,
|
||||||
self.node_type
|
self.node_type,
|
||||||
|
is_shared=is_shared
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
@@ -417,12 +427,15 @@ class ServerGroupView(NodeView):
|
|||||||
errormsg=gettext("Could not find the server group.")
|
errormsg=gettext("Could not find the server group.")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
icon_class, is_shared = get_icon_css_class(group.id,
|
||||||
|
group.user_id)
|
||||||
nodes = self.blueprint.generate_browser_node(
|
nodes = self.blueprint.generate_browser_node(
|
||||||
"%d" % (group.id), None,
|
"%d" % (group.id), None,
|
||||||
group.name,
|
group.name,
|
||||||
get_icon_css_class(group.id, group.user_id),
|
icon_class,
|
||||||
True,
|
True,
|
||||||
self.node_type
|
self.node_type,
|
||||||
|
is_shared=is_shared
|
||||||
)
|
)
|
||||||
|
|
||||||
return make_json_response(data=nodes)
|
return make_json_response(data=nodes)
|
||||||
|
|||||||
@@ -1201,7 +1201,7 @@ class ServerNode(PGChildNodeView):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# To check ssl configuration
|
# To check ssl configuration
|
||||||
is_ssl, connection_params = self.check_ssl_fields(connection_params)
|
_, connection_params = self.check_ssl_fields(connection_params)
|
||||||
# set the connection params again in the data
|
# set the connection params again in the data
|
||||||
if 'connection_params' in data:
|
if 'connection_params' in data:
|
||||||
data['connection_params'] = connection_params
|
data['connection_params'] = connection_params
|
||||||
@@ -1221,8 +1221,8 @@ class ServerNode(PGChildNodeView):
|
|||||||
config.ALLOW_SAVE_PASSWORD else 0,
|
config.ALLOW_SAVE_PASSWORD else 0,
|
||||||
comment=data.get('comment', None),
|
comment=data.get('comment', None),
|
||||||
role=data.get('role', None),
|
role=data.get('role', None),
|
||||||
db_res=','.join(data['db_res'])
|
db_res=','.join(data['db_res']) if 'db_res' in data and
|
||||||
if 'db_res' in data else None,
|
isinstance(data['db_res'], list) else None,
|
||||||
bgcolor=data.get('bgcolor', None),
|
bgcolor=data.get('bgcolor', None),
|
||||||
fgcolor=data.get('fgcolor', None),
|
fgcolor=data.get('fgcolor', None),
|
||||||
service=data.get('service', None),
|
service=data.get('service', None),
|
||||||
@@ -1763,7 +1763,7 @@ class ServerNode(PGChildNodeView):
|
|||||||
|
|
||||||
if conn.connected():
|
if conn.connected():
|
||||||
# Execute the command for reload configuration for the server
|
# Execute the command for reload configuration for the server
|
||||||
status, rid = conn.execute_scalar("SELECT pg_reload_conf();")
|
status, _ = conn.execute_scalar("SELECT pg_reload_conf();")
|
||||||
|
|
||||||
if not status:
|
if not status:
|
||||||
return internal_server_error(
|
return internal_server_error(
|
||||||
@@ -1782,7 +1782,7 @@ class ServerNode(PGChildNodeView):
|
|||||||
|
|
||||||
def create_restore_point(self, gid, sid):
|
def create_restore_point(self, gid, sid):
|
||||||
"""
|
"""
|
||||||
This method will creates named restore point
|
This method will create named restore point
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
gid: Server group ID
|
gid: Server group ID
|
||||||
|
|||||||
@@ -44,9 +44,28 @@ define('pgadmin.node.server', [
|
|||||||
title: function(d, action) {
|
title: function(d, action) {
|
||||||
if(action == 'create') {
|
if(action == 'create') {
|
||||||
return gettext('Register - %s', this.label);
|
return gettext('Register - %s', this.label);
|
||||||
|
} else if (action == 'copy') {
|
||||||
|
return gettext('Copy Server - %s', d.label);
|
||||||
}
|
}
|
||||||
return d._label??'';
|
return d._label??'';
|
||||||
},
|
},
|
||||||
|
copy: function(d) {
|
||||||
|
// This function serves the purpose of facilitating modifications
|
||||||
|
// during the server copying process.
|
||||||
|
|
||||||
|
// Changing the name of the server to "Copy of <existing name>"
|
||||||
|
d.name = gettext('Copy of %s', d.name);
|
||||||
|
// If existing server is a shared server from another user then
|
||||||
|
// copy this server as a local server for the current user.
|
||||||
|
if (d?.shared && d.user_id != current_user?.id) {
|
||||||
|
d.gid = null;
|
||||||
|
d.user_id = current_user?.id;
|
||||||
|
d.shared = false;
|
||||||
|
d.server_owner = null;
|
||||||
|
d.shared_username = null;
|
||||||
|
}
|
||||||
|
return d;
|
||||||
|
},
|
||||||
Init: function() {
|
Init: function() {
|
||||||
/* Avoid multiple registration of same menus */
|
/* Avoid multiple registration of same menus */
|
||||||
if (this.initialized)
|
if (this.initialized)
|
||||||
@@ -135,6 +154,11 @@ define('pgadmin.node.server', [
|
|||||||
data: {
|
data: {
|
||||||
data_disabled: gettext('SSH Tunnel password is not saved for selected server.'),
|
data_disabled: gettext('SSH Tunnel password is not saved for selected server.'),
|
||||||
},
|
},
|
||||||
|
}, {
|
||||||
|
name: 'copy_server', node: 'server', module: this,
|
||||||
|
applies: ['object', 'context'], callback: 'show_obj_properties',
|
||||||
|
label: gettext('Copy Server...'), data: {action: 'copy'},
|
||||||
|
priority: 4,
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
_.bindAll(this, 'connection_lost');
|
_.bindAll(this, 'connection_lost');
|
||||||
@@ -501,7 +525,9 @@ define('pgadmin.node.server', [
|
|||||||
},
|
},
|
||||||
getSchema: (treeNodeInfo, itemNodeData)=>{
|
getSchema: (treeNodeInfo, itemNodeData)=>{
|
||||||
return new ServerSchema(
|
return new ServerSchema(
|
||||||
getNodeListById(pgBrowser.Nodes['server_group'], treeNodeInfo, itemNodeData),
|
getNodeListById(pgBrowser.Nodes['server_group'], treeNodeInfo, itemNodeData, {},
|
||||||
|
// Filter out shared servers group, it should not be visible.
|
||||||
|
(server)=> !server.is_shared),
|
||||||
itemNodeData.user_id,
|
itemNodeData.user_id,
|
||||||
{
|
{
|
||||||
gid: treeNodeInfo['server_group']._id,
|
gid: treeNodeInfo['server_group']._id,
|
||||||
|
|||||||
@@ -370,6 +370,14 @@ export default class ServerSchema extends BaseUISchema {
|
|||||||
validate(state, setError) {
|
validate(state, setError) {
|
||||||
let errmsg = null;
|
let errmsg = null;
|
||||||
|
|
||||||
|
if(isEmptyString(state.gid)) {
|
||||||
|
errmsg = gettext('Server group must be specified.');
|
||||||
|
setError('gid', errmsg);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
setError('gid', null);
|
||||||
|
}
|
||||||
|
|
||||||
if (isEmptyString(state.service)) {
|
if (isEmptyString(state.service)) {
|
||||||
errmsg = gettext('Either Host name or Service must be specified.');
|
errmsg = gettext('Either Host name or Service must be specified.');
|
||||||
if(isEmptyString(state.host)) {
|
if(isEmptyString(state.host)) {
|
||||||
|
|||||||
@@ -95,6 +95,11 @@ define('pgadmin.browser.node', [
|
|||||||
}
|
}
|
||||||
return d._label??'';
|
return d._label??'';
|
||||||
},
|
},
|
||||||
|
copy: function(d) {
|
||||||
|
// This function serves the purpose of facilitating modifications
|
||||||
|
// during the copying process of any node.
|
||||||
|
return d;
|
||||||
|
},
|
||||||
hasId: true,
|
hasId: true,
|
||||||
///////
|
///////
|
||||||
// Initialization function
|
// Initialization function
|
||||||
@@ -407,6 +412,36 @@ define('pgadmin.browser.node', [
|
|||||||
onSave: onSave,
|
onSave: onSave,
|
||||||
onClose: onClose,
|
onClose: onClose,
|
||||||
});
|
});
|
||||||
|
} else if (args.action == 'copy') {
|
||||||
|
// This else-if block is used to copy the existing object and
|
||||||
|
// open the respective dialog. Add the copied object into the object
|
||||||
|
// browser tree upon the 'Save' button click.
|
||||||
|
treeNodeInfo = pgBrowser.tree.getTreeNodeHierarchy(nodeItem);
|
||||||
|
const panelId = _.uniqueId(BROWSER_PANELS.EDIT_PROPERTIES);
|
||||||
|
const onClose = (force=false)=>pgBrowser.docker.close(panelId, force);
|
||||||
|
const onSave = (newNodeData)=>{
|
||||||
|
// Clear the cache for this node now.
|
||||||
|
setTimeout(()=>{
|
||||||
|
this.clear_cache.apply(this, item);
|
||||||
|
}, 0);
|
||||||
|
try {
|
||||||
|
pgBrowser.Events.trigger(
|
||||||
|
'pgadmin:browser:tree:add', _.clone(newNodeData.node),
|
||||||
|
{'server_group': treeNodeInfo['server_group']}
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
console.warn(e.stack || e);
|
||||||
|
}
|
||||||
|
onClose();
|
||||||
|
};
|
||||||
|
this.showPropertiesDialog(panelId, panelTitle, {
|
||||||
|
treeNodeInfo: treeNodeInfo,
|
||||||
|
item: nodeItem,
|
||||||
|
nodeData: nodeData,
|
||||||
|
actionType: 'copy',
|
||||||
|
onSave: onSave,
|
||||||
|
onClose: onClose,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
const panelId = BROWSER_PANELS.EDIT_PROPERTIES+nodeData.id;
|
const panelId = BROWSER_PANELS.EDIT_PROPERTIES+nodeData.id;
|
||||||
const onClose = (force=false)=>pgBrowser.docker.close(panelId, force);
|
const onClose = (force=false)=>pgBrowser.docker.close(panelId, force);
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import CloudWizard from './CloudWizard';
|
|||||||
import getApiInstance from '../../../../static/js/api_instance';
|
import getApiInstance from '../../../../static/js/api_instance';
|
||||||
import { BROWSER_PANELS } from '../../../../browser/static/js/constants';
|
import { BROWSER_PANELS } from '../../../../browser/static/js/constants';
|
||||||
import pgAdmin from 'sources/pgadmin';
|
import pgAdmin from 'sources/pgadmin';
|
||||||
|
import current_user from 'pgadmin.user_management.current_user';
|
||||||
|
|
||||||
// Cloud Wizard
|
// Cloud Wizard
|
||||||
define('pgadmin.misc.cloud', [
|
define('pgadmin.misc.cloud', [
|
||||||
@@ -43,7 +44,7 @@ define('pgadmin.misc.cloud', [
|
|||||||
priority: 15,
|
priority: 15,
|
||||||
label: gettext('Deploy Cloud Instance...'),
|
label: gettext('Deploy Cloud Instance...'),
|
||||||
icon: 'wcTabIcon icon-server',
|
icon: 'wcTabIcon icon-server',
|
||||||
enable: true,
|
enable: 'canCreate',
|
||||||
data: {action: 'create'},
|
data: {action: 'create'},
|
||||||
category: 'register',
|
category: 'register',
|
||||||
node: 'server_group',
|
node: 'server_group',
|
||||||
@@ -55,7 +56,7 @@ define('pgadmin.misc.cloud', [
|
|||||||
priority: 15,
|
priority: 15,
|
||||||
label: gettext('Deploy Cloud Instance...'),
|
label: gettext('Deploy Cloud Instance...'),
|
||||||
icon: 'wcTabIcon icon-server',
|
icon: 'wcTabIcon icon-server',
|
||||||
enable: true,
|
enable: 'canCreate',
|
||||||
data: {action: 'create'},
|
data: {action: 'create'},
|
||||||
category: 'register',
|
category: 'register',
|
||||||
node: 'server',
|
node: 'server',
|
||||||
@@ -64,6 +65,10 @@ define('pgadmin.misc.cloud', [
|
|||||||
pgBrowser.add_menus(menus);
|
pgBrowser.add_menus(menus);
|
||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
|
canCreate: function(node){
|
||||||
|
let serverOwner = node.user_id;
|
||||||
|
return (serverOwner == current_user.id || _.isUndefined(serverOwner));
|
||||||
|
},
|
||||||
|
|
||||||
// Callback to draw Wizard Dialog
|
// Callback to draw Wizard Dialog
|
||||||
start_cloud_wizard: function() {
|
start_cloud_wizard: function() {
|
||||||
|
|||||||
@@ -27,17 +27,29 @@ export default function ObjectNodeProperties({panelId, node, treeNodeInfo, nodeD
|
|||||||
let serverInfo = treeNodeInfo && ('server' in treeNodeInfo) &&
|
let serverInfo = treeNodeInfo && ('server' in treeNodeInfo) &&
|
||||||
pgAdmin.Browser.serverInfo && pgAdmin.Browser.serverInfo[treeNodeInfo.server._id];
|
pgAdmin.Browser.serverInfo && pgAdmin.Browser.serverInfo[treeNodeInfo.server._id];
|
||||||
let inCatalog = treeNodeInfo && ('catalog' in treeNodeInfo);
|
let inCatalog = treeNodeInfo && ('catalog' in treeNodeInfo);
|
||||||
let urlBase = generateNodeUrl.call(node, treeNodeInfo, actionType, nodeData, false, node.url_jump_after_node);
|
let isActionTypeCopy = actionType == 'copy';
|
||||||
|
// If the actionType is set to 'copy' it is necessary to retrieve the details
|
||||||
|
// of the existing node. Therefore, specify the actionType as 'edit' to
|
||||||
|
// facilitate this process.
|
||||||
|
let urlBase = generateNodeUrl.call(node, treeNodeInfo, isActionTypeCopy ? 'edit' : actionType, nodeData, false, node.url_jump_after_node);
|
||||||
const api = getApiInstance();
|
const api = getApiInstance();
|
||||||
// To check node data is updated or not
|
// To check node data is updated or not
|
||||||
const staleCounter = useRef(0);
|
const staleCounter = useRef(0);
|
||||||
const url = (isNew)=>{
|
const url = (isNew)=>{
|
||||||
return urlBase + (isNew ? '' : nodeData._id);
|
return urlBase + (isNew ? '' : nodeData._id);
|
||||||
};
|
};
|
||||||
const isDirty = useRef(false); // usefull for warnings
|
const isDirty = useRef(false); // useful for warnings
|
||||||
let warnOnCloseFlag = true;
|
let warnOnCloseFlag = true;
|
||||||
const confirmOnCloseReset = usePreferences().getPreferencesForModule('browser').confirm_on_properties_close;
|
const confirmOnCloseReset = usePreferences().getPreferencesForModule('browser').confirm_on_properties_close;
|
||||||
let updatedData = ['table', 'partition'].includes(nodeType) && !_.isEmpty(nodeData.rows_cnt) ? {rows_cnt: nodeData.rows_cnt} : undefined;
|
let updatedData = ['table', 'partition'].includes(nodeType) && !_.isEmpty(nodeData.rows_cnt) ? {rows_cnt: nodeData.rows_cnt} : undefined;
|
||||||
|
let schema = node.getSchema.call(node, treeNodeInfo, nodeData);
|
||||||
|
|
||||||
|
// We only have two actionTypes, 'create' and 'edit' to initiate the dialog,
|
||||||
|
// so if isActionTypeCopy is true, we should revert back to "create" since
|
||||||
|
// we are duplicating the node.
|
||||||
|
if (isActionTypeCopy) {
|
||||||
|
actionType = 'create';
|
||||||
|
}
|
||||||
|
|
||||||
let onError = (err)=> {
|
let onError = (err)=> {
|
||||||
if(err.response){
|
if(err.response){
|
||||||
@@ -51,7 +63,7 @@ export default function ObjectNodeProperties({panelId, node, treeNodeInfo, nodeD
|
|||||||
|
|
||||||
/* Called when dialog is opened in edit mode, promise required */
|
/* Called when dialog is opened in edit mode, promise required */
|
||||||
let initData = ()=>new Promise((resolve, reject)=>{
|
let initData = ()=>new Promise((resolve, reject)=>{
|
||||||
if(actionType === 'create') {
|
if(actionType === 'create' && !isActionTypeCopy) {
|
||||||
resolve({});
|
resolve({});
|
||||||
} else {
|
} else {
|
||||||
// Do not call the API if tab is not active.
|
// Do not call the API if tab is not active.
|
||||||
@@ -60,7 +72,13 @@ export default function ObjectNodeProperties({panelId, node, treeNodeInfo, nodeD
|
|||||||
}
|
}
|
||||||
api.get(url(false))
|
api.get(url(false))
|
||||||
.then((res)=>{
|
.then((res)=>{
|
||||||
resolve(res.data);
|
let data = res.data;
|
||||||
|
if (isActionTypeCopy) {
|
||||||
|
// Delete the idAttribute while copying the node.
|
||||||
|
delete data[schema.idAttribute];
|
||||||
|
data = node.copy(data);
|
||||||
|
}
|
||||||
|
resolve(data);
|
||||||
})
|
})
|
||||||
.catch((err)=>{
|
.catch((err)=>{
|
||||||
pgAdmin.Browser.notifier.pgNotifier('error', err, gettext('Failed to fetch data'), function(msg) {
|
pgAdmin.Browser.notifier.pgNotifier('error', err, gettext('Failed to fetch data'), function(msg) {
|
||||||
@@ -192,7 +210,6 @@ export default function ObjectNodeProperties({panelId, node, treeNodeInfo, nodeD
|
|||||||
inCatalog: inCatalog,
|
inCatalog: inCatalog,
|
||||||
};
|
};
|
||||||
|
|
||||||
let schema = node.getSchema.call(node, treeNodeInfo, nodeData);
|
|
||||||
// Show/Hide security group for nodes under the catalog
|
// Show/Hide security group for nodes under the catalog
|
||||||
if('catalog' in treeNodeInfo
|
if('catalog' in treeNodeInfo
|
||||||
&& formType !== 'tab') {
|
&& formType !== 'tab') {
|
||||||
|
|||||||
@@ -191,7 +191,7 @@ class Server(db.Model):
|
|||||||
tunnel_port = db.Column(
|
tunnel_port = db.Column(
|
||||||
db.Integer(),
|
db.Integer(),
|
||||||
db.CheckConstraint('port <= 65534'),
|
db.CheckConstraint('port <= 65534'),
|
||||||
nullable=True)
|
nullable=True, default=22)
|
||||||
tunnel_username = db.Column(db.String(64), nullable=True)
|
tunnel_username = db.Column(db.String(64), nullable=True)
|
||||||
tunnel_authentication = db.Column(
|
tunnel_authentication = db.Column(
|
||||||
db.Integer(),
|
db.Integer(),
|
||||||
@@ -201,7 +201,7 @@ class Server(db.Model):
|
|||||||
)
|
)
|
||||||
tunnel_identity_file = db.Column(db.String(64), nullable=True)
|
tunnel_identity_file = db.Column(db.String(64), nullable=True)
|
||||||
tunnel_password = db.Column(PgAdminDbBinaryString())
|
tunnel_password = db.Column(PgAdminDbBinaryString())
|
||||||
tunnel_keep_alive = db.Column(db.Integer(), nullable=True)
|
tunnel_keep_alive = db.Column(db.Integer(), nullable=True, default=0)
|
||||||
shared = db.Column(db.Boolean(), nullable=False)
|
shared = db.Column(db.Boolean(), nullable=False)
|
||||||
shared_username = db.Column(db.String(64), nullable=True)
|
shared_username = db.Column(db.String(64), nullable=True)
|
||||||
kerberos_conn = db.Column(db.Boolean(), nullable=False, default=0)
|
kerberos_conn = db.Column(db.Boolean(), nullable=False, default=0)
|
||||||
|
|||||||
@@ -42,6 +42,10 @@ describe('ServerSchema', ()=>{
|
|||||||
let state = {};
|
let state = {};
|
||||||
let setError = jest.fn();
|
let setError = jest.fn();
|
||||||
|
|
||||||
|
schemaObj.validate(state, setError);
|
||||||
|
expect(setError).toHaveBeenCalledWith('gid', 'Server group must be specified.');
|
||||||
|
|
||||||
|
state.gid = 1;
|
||||||
schemaObj.validate(state, setError);
|
schemaObj.validate(state, setError);
|
||||||
expect(setError).toHaveBeenCalledWith('host', 'Either Host name or Service must be specified.');
|
expect(setError).toHaveBeenCalledWith('host', 'Either Host name or Service must be specified.');
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user