public inbox for [email protected]
help / color / mirror / Atom feed[pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab
18+ messages / 4 participants
[nested] [flat]
* [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab
@ 2020-09-17 10:45 Nikhil Mohite <[email protected]>
2020-09-21 11:54 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
0 siblings, 1 reply; 18+ messages in thread
From: Nikhil Mohite @ 2020-09-17 10:45 UTC (permalink / raw)
To: pgadmin-hackers
Hi Team,
Regarding RM-3794 <https://redmine.postgresql.org/issues/3794; allow
the user to change the database connection from an open query tool:
I have implemented the feature and also added documentation for it.
PFA patch.
--
*Thanks & Regards,*
*Nikhil Mohite*
*Software Engineer.*
*EDB Postgres* <https://www.enterprisedb.com/;
*Mob.No: +91-7798364578.*
Attachments:
[application/octet-stream] RM_3794_v1.patch (197.4K, 3-RM_3794_v1.patch)
download | inline diff:
diff --git a/docs/en_US/images/new_connection_dialog.png b/docs/en_US/images/new_connection_dialog.png
new file mode 100644
index 0000000000000000000000000000000000000000..c2f877dc9771b8b697c925438e4abb4f7436db35
GIT binary patch
literal 44942
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
F{{cYfFA@L%
literal 0
HcmV?d00001
diff --git a/docs/en_US/images/new_connection_options.png b/docs/en_US/images/new_connection_options.png
new file mode 100644
index 0000000000000000000000000000000000000000..3de8fc4ee9a50d1c93c35b274cc21fec784c1138
GIT binary patch
literal 47015
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
O-}%!Hr)q!oyY_$PkK4om
literal 0
HcmV?d00001
diff --git a/docs/en_US/query_tool.rst b/docs/en_US/query_tool.rst
index 83933f0..545fb95 100644
--- a/docs/en_US/query_tool.rst
+++ b/docs/en_US/query_tool.rst
@@ -300,3 +300,24 @@ transaction status by clicking on the status icon in the Query Tool:
.. image:: images/query_tool_connection_status.png
:alt: Query tool connection and transaction statuses
:align: center
+
+Change connection
+*****************
+
+User can connect to another server or database from existing open session of query tool.
+
+* Click on the connection link next to connection status.
+* Now click on the *<New Connection>* option from the dropdown.
+
+.. image:: images/new_connection_options.png
+ :alt: Query tool connection options
+ :align: center
+
+* Now select server, database, user, and password to connect and click OK.
+
+.. image:: images/new_connection_dialog.png
+ :alt: Query tool connection dialog
+ :align: center
+
+* A newly created connection will now get listed in the options.
+* To connect, select the newly created connection from the dropdown list.
diff --git a/web/pgadmin/browser/server_groups/servers/roles/tests/utils.py b/web/pgadmin/browser/server_groups/servers/roles/tests/utils.py
index 3a7ee58..028ee64 100644
--- a/web/pgadmin/browser/server_groups/servers/roles/tests/utils.py
+++ b/web/pgadmin/browser/server_groups/servers/roles/tests/utils.py
@@ -152,3 +152,38 @@ def delete_role(connection, role_names):
exception = "Error while deleting role: %s: line:%s %s" % (
file_name, sys.exc_traceback.tb_lineno, exception)
print(exception, file=sys.stderr)
+
+
+def create_role_with_password(server, role_name, role_password):
+ """
+ This function create the role.
+ :param server:
+ :param role_name:
+ :param role_password:
+ :return:
+ """
+ try:
+ connection = utils.get_db_connection(server['db'],
+ server['username'],
+ server['db_password'],
+ server['host'],
+ server['port'],
+ server['sslmode'])
+ pg_cursor = connection.cursor()
+ pg_cursor.execute(
+ "CREATE ROLE %s LOGIN PASSWORD '%s'" % (role_name, role_password))
+ connection.commit()
+ # Get 'oid' from newly created tablespace
+ pg_cursor.execute(
+ "SELECT pr.oid from pg_catalog.pg_roles pr WHERE pr.rolname='%s'" %
+ role_name)
+ oid = pg_cursor.fetchone()
+ role_id = ''
+ if oid:
+ role_id = oid[0]
+ connection.close()
+ return role_id
+ except Exception as exception:
+ exception = "Error while deleting role: %s: line:%s %s" % (
+ file_name, sys.exc_traceback.tb_lineno, exception)
+ print(exception, file=sys.stderr)
diff --git a/web/pgadmin/model/__init__.py b/web/pgadmin/model/__init__.py
index b33adc0..d24a350 100644
--- a/web/pgadmin/model/__init__.py
+++ b/web/pgadmin/model/__init__.py
@@ -94,6 +94,15 @@ class ServerGroup(db.Model):
name = db.Column(db.String(128), nullable=False)
__table_args__ = (db.UniqueConstraint('user_id', 'name'),)
+ @property
+ def serialize(self):
+ """Return object data in easily serializable format"""
+ return {
+ 'id': self.id,
+ 'user_id': self.user_id,
+ 'name': self.name,
+ }
+
class Server(db.Model):
"""Define a registered Postgres server"""
@@ -175,6 +184,44 @@ class Server(db.Model):
tunnel_password = db.Column(db.String(64), nullable=True)
shared = db.Column(db.Boolean(), nullable=False)
+ @property
+ def serialize(self):
+ """Return object data in easily serializable format"""
+ return {
+ "id": self.id,
+ "user_id": self.user_id,
+ "servergroup_id": self.servergroup_id,
+ "name": self.name,
+ "host": self.host,
+ "hostaddr": self.hostaddr,
+ "port": self.port,
+ "maintenance_db": self.maintenance_db,
+ "username": self.username,
+ "password": self.password,
+ "save_password": self.save_password,
+ "role": self.role,
+ "ssl_mode": self.ssl_mode,
+ "comment": self.comment,
+ "discovery_id": self.discovery_id,
+ "db_res": self.db_res,
+ "passfile": self.passfile,
+ "sslcert": self.sslcert,
+ "sslkey": self.sslkey,
+ "sslrootcert": self.sslrootcert,
+ "sslcrl": self.sslcrl,
+ "sslcompression": self.sslcompression,
+ "bgcolor": self.bgcolor,
+ "fgcolor": self.fgcolor,
+ "service": self.service,
+ "connect_timeout": self.connect_timeout,
+ "use_ssh_tunnel": self.use_ssh_tunnel,
+ "tunnel_host": self.tunnel_host,
+ "tunnel_port": self.tunnel_port,
+ "tunnel_authentication": self.tunnel_authentication,
+ "tunnel_identity_file": self.tunnel_identity_file,
+ "tunnel_password": self.tunnel_password
+ }
+
class ModulePreference(db.Model):
"""Define a preferences table for any modules."""
diff --git a/web/pgadmin/static/js/sqleditor/new_connection_dialog.js b/web/pgadmin/static/js/sqleditor/new_connection_dialog.js
new file mode 100644
index 0000000..a56d736
--- /dev/null
+++ b/web/pgadmin/static/js/sqleditor/new_connection_dialog.js
@@ -0,0 +1,252 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2020, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import gettext from 'sources/gettext';
+import url_for from 'sources/url_for';
+import $ from 'jquery';
+import Alertify from 'pgadmin.alertifyjs';
+import pgAdmin from 'sources/pgadmin';
+import Backform from 'pgadmin.backform';
+import newConnectionDialogModel from 'sources/sqleditor/new_connection_dialog_model';
+
+
+let NewConnectionDialog = {
+ 'dialog': function(handler, reconnect) {
+ let url = url_for('sqleditor.get_new_connection_data', {
+ 'sid': handler.url_params.sid,
+ 'sgid': handler.url_params.sgid,
+ });
+
+ if(reconnect) {
+ url += '?connect=1';
+ }
+
+ let title = gettext('Connect to server');
+
+ $.ajax({
+ url: url,
+ headers: {
+ 'Cache-Control' : 'no-cache',
+ },
+ }).done(function (res) {
+ let response = res.data.result;
+ response.database_list = [];
+ response.user_list = [];
+ if (Alertify.newConnectionDialog) {
+ delete Alertify.newConnectionDialog;
+ }
+
+ // Create Dialog
+ Alertify.dialog('newConnectionDialog', function factory() {
+ let $container = $('<div class=\'new-connection-dialog\'></div>');
+ return {
+ main: function(message) {
+ this.msg = message;
+ },
+ build: function() {
+ this.elements.content.appendChild($container.get(0));
+ Alertify.pgDialogBuild.apply(this);
+ },
+ setup: function(){
+ return {
+ buttons: [
+ {
+ text: '',
+ key: 112,
+ className: 'btn btn-primary-icon pull-left fa fa-question pg-alertify-icon-button',
+ attrs: {
+ name: 'dialog_help',
+ type: 'button',
+ label: gettext('Help'),
+ 'aria-label': gettext('Help'),
+ url: url_for('help.static', {
+ 'filename': 'query_tool.html',
+ }),
+ },
+ },
+ {
+ text: gettext('Cancel'),
+ key: 27,
+ className: 'btn btn-secondary fa fa-times pg-alertify-button',
+ 'data-btn-name': 'cancel',
+ }, {
+ text: gettext('OK'),
+ key: 13,
+ className: 'btn btn-primary fa fa-check pg-alertify-button',
+ 'data-btn-name': 'ok',
+ },
+ ],
+ // Set options for dialog
+ options: {
+ title: title,
+ //disable both padding and overflow control.
+ padding: !1,
+ overflow: !1,
+ model: 0,
+ resizable: true,
+ maximizable: false,
+ pinnable: false,
+ closableByDimmer: false,
+ modal: false,
+ autoReset: false,
+ closable: true,
+ },
+ };
+ },
+ prepare: function() {
+ let self = this;
+ $container.html('');
+ // Disable Ok button
+ this.__internal.buttons[2].element.disabled = true;
+
+ // Status bar
+ this.statusBar = $(
+ '<div class=\'pg-prop-status-bar pg-el-xs-12 d-none\'>' +
+ ' <div class="error-in-footer"> ' +
+ ' <div class="d-flex px-2 py-1"> ' +
+ ' <div class="pr-2"> ' +
+ ' <i class="fa fa-exclamation-triangle text-danger" aria-hidden="true"></i> ' +
+ ' </div> ' +
+ ' <div class="alert-text" role="alert"></div> ' +
+ ' </div> ' +
+ ' </div> ' +
+ '</div>').appendTo($container);
+
+ // To show progress on filter Saving/Updating on AJAX
+ this.showNewConnectionProgress = $(
+ `<div id="show_filter_progress" class="pg-sp-container sql-editor-busy-fetching d-none">
+ <div class="pg-sp-content">
+ <div class="row"><div class="col-12 pg-sp-icon sql-editor-busy-icon"></div></div>
+ <div class="row"><div class="col-12 pg-sp-text sql-editor-busy-text">` + gettext('Loading data...') + `</div></div>
+ </div>
+ </div>`
+ ).appendTo($container);
+ $(
+ self.showNewConnectionProgress[0]
+ ).removeClass('d-none');
+
+ self.newConnCollectionModel = newConnectionDialogModel(response, handler.url_params.sgid, handler.url_params.sid);
+ let fields = Backform.generateViewSchema(null, self.newConnCollectionModel, 'create', null, null, true);
+
+ let view = this.view = new Backform.Dialog({
+ el: '<div></div>',
+ model: self.newConnCollectionModel,
+ schema: fields,
+ });
+
+ $(this.elements.body.childNodes[0]).addClass(
+ 'alertify_tools_dialog_properties obj_properties'
+ );
+
+ $container.append(view.render().$el);
+
+ // Enable/disable save button and show/hide statusbar based on session
+ view.listenTo(view.model, 'pgadmin-session:start', function() {
+ view.listenTo(view.model, 'pgadmin-session:invalid', function(msg) {
+ self.statusBar.removeClass('d-none');
+ $(self.statusBar.find('.alert-text')).html(msg);
+ // Disable Okay button
+ self.__internal.buttons[2].element.disabled = true;
+ });
+
+ view.listenTo(view.model, 'pgadmin-session:valid', function() {
+ self.statusBar.addClass('d-none');
+ $(self.statusBar.find('.alert-text')).html('');
+ // Enable Okay button
+ self.__internal.buttons[2].element.disabled = false;
+ });
+ });
+
+ view.listenTo(view.model, 'pgadmin-session:stop', function() {
+ view.stopListening(view.model, 'pgadmin-session:invalid');
+ view.stopListening(view.model, 'pgadmin-session:valid');
+ });
+
+ // Starts monitoring changes to model
+ view.model.startNewSession();
+
+ // Hide Progress ...
+ $(
+ self.showNewConnectionProgress[0]
+ ).addClass('d-none');
+ },
+ callback: function(e) {
+ let self = this;
+ if (e.button.element.name == 'dialog_help') {
+ e.cancel = true;
+ pgAdmin.Browser.showHelp(e.button.element.name, e.button.element.getAttribute('url'),
+ null, null);
+ return;
+ } else if (e.button['data-btn-name'] === 'ok') {
+ e.cancel = true; // Do not close dialog
+ let newConnCollectionModel = this.newConnCollectionModel.toJSON();
+ let selected_database_name = null;
+ response.database_list.forEach(function(data){
+ if(newConnCollectionModel['database'] == data['value']) {
+ selected_database_name = data['label'];
+ return false;
+ }
+ });
+ let is_create_connection = true;
+ let title = selected_database_name + '/' + newConnCollectionModel['user'] + '@' + response.server_name;
+ handler.gridView.connection_list.forEach(function(connection_data){
+ if(parseInt(connection_data['server']) == newConnCollectionModel['server']
+ && parseInt(connection_data['database']) == newConnCollectionModel['database']
+ && connection_data['user'] == newConnCollectionModel['user']) {
+ is_create_connection = false;
+ // break for loop by return false.
+ return false;
+ }
+
+ if(title == connection_data['title']) {
+ is_create_connection = false;
+ return false;
+ }
+ });
+ if(!is_create_connection) {
+ let errmsg = 'Connection with this configuration already present.';
+ Alertify.error(errmsg);
+ }else {
+ let connection_details = {
+ 'server_group': handler.gridView.handler.url_params.sgid,
+ 'server': newConnCollectionModel['server'],
+ 'database': newConnCollectionModel['database'],
+ 'title': title,
+ 'user': newConnCollectionModel['user'],
+ 'password': newConnCollectionModel['password'],
+ };
+ handler.gridView.on_change_connection(connection_details, self);
+ }
+ } else {
+ self.close();
+ }
+ },
+ };
+ });
+ setTimeout(function(){
+ Alertify.newConnectionDialog('Connect to server.').resizeTo(pgAdmin.Browser.stdW.md,pgAdmin.Browser.stdH.md);
+ }, 500);
+ }).fail(function(error) {
+ Alertify.alert().setting({
+ 'title': gettext('Connection lost'),
+ 'label':gettext('Ok'),
+ 'message': gettext('Connection to the server has been lost.'),
+ 'onok': function(){
+ alert(error);
+ //Close the window after connection is lost
+ window.close();
+ },
+ }).show();
+ });
+
+ },
+
+};
+
+module.exports = NewConnectionDialog;
diff --git a/web/pgadmin/static/js/sqleditor/new_connection_dialog_model.js b/web/pgadmin/static/js/sqleditor/new_connection_dialog_model.js
new file mode 100644
index 0000000..7f309cd
--- /dev/null
+++ b/web/pgadmin/static/js/sqleditor/new_connection_dialog_model.js
@@ -0,0 +1,228 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2020, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import gettext from 'sources/gettext';
+import _ from 'underscore';
+import $ from 'jquery';
+import pgAdmin from 'sources/pgadmin';
+import Backform from 'pgadmin.backform';
+import url_for from 'sources/url_for';
+import alertify from 'pgadmin.alertifyjs';
+
+export default function newConnectionDialogModel(response, sgid, sid) {
+
+ let server_name = '';
+ let database_name = '';
+
+ let NewConnectionSelect2Control = Backform.Select2Control.extend({
+ fetchData: function(){
+ let self = this;
+ url = self.field.get('url');
+
+ let url = url_for(url, {
+ 'sid': self.model.attributes.server,
+ 'sgid': sgid,
+ });
+
+ $.ajax({
+ async: false,
+ url: url,
+ headers: {
+ 'Cache-Control' : 'no-cache',
+ },
+ }).done(function (res) {
+ var transform = self.field.get('transform');
+ if(res.data.status){
+ let data = res.data.result.data;
+
+ if (transform && _.isFunction(transform)) {
+ self.field.set('options', transform.bind(self, data));
+ } else {
+ self.field.set('options', data);
+ }
+ } else {
+ if (transform && _.isFunction(transform)) {
+ self.field.set('options', transform.bind(self, []));
+ } else {
+ self.field.set('options', []);
+ }
+ alertify.error(res.data.msg);
+ }
+ }).fail(function(e){
+ let msg = '';
+ if(e.status == 404) {
+ msg = 'Unable to find url.';
+ } else {
+ msg = e.responseJSON.errormsg;
+ }
+ alertify.error(msg);
+ });
+ },
+ render: function() {
+ this.fetchData();
+ return Backform.Select2Control.prototype.render.apply(this, arguments);
+ },
+ onChange: function() {
+ Backform.Select2Control.prototype.onChange.apply(this, arguments);
+ },
+ });
+
+ let newConnectionModel = pgAdmin.Browser.DataModel.extend({
+ idAttribute: 'name',
+ defaults: {
+ server: parseInt(sid),
+ database: null,
+ user: null,
+ password: null,
+ server_name: server_name,
+ database_name: database_name,
+ },
+ schema: [{
+ id: 'server',
+ name: 'server',
+ label: gettext('Server'),
+ type: 'text',
+ editable: true,
+ disabled: false,
+ select2: {
+ allowClear: false,
+ },
+ control: Backform.Select2Control.extend({
+ onChange: function() {
+ this.model.attributes.database = null;
+ this.model.attributes.user = null;
+ let self = this;
+ Backform.Select2Control.prototype.onChange.apply(this, arguments);
+
+ response.server_list.forEach(function(obj){
+ if(obj.id==self.model.changed.server) {
+ response.server_name = obj.name;
+ }
+ });
+ },
+ }),
+ options: function() {
+ return _.map(response.server_list, (obj) => {
+ if (obj.id == parseInt(sid))
+ response.server_name = obj.name;
+
+ return {
+ value: obj.id,
+ label: obj.name,
+ };
+ });
+ },
+ },
+ {
+ id: 'database',
+ name: 'database',
+ label: gettext('Database'),
+ type: 'text',
+ editable: true,
+ disabled: function(m) {
+ let self_local = this;
+ if (!_.isUndefined(m.get('server')) && !_.isNull(m.get('server'))
+ && m.get('server') !== '') {
+ setTimeout(function() {
+ if(self_local.options.length) {
+ m.set('database', self_local.options[0].value);
+ }
+ }, 10);
+ return false;
+ }
+
+ return true;
+ },
+ deps: ['server'],
+ url: 'sqleditor.get_new_connection_database',
+ select2: {
+ allowClear: false,
+ width: '100%',
+ first_empty: true,
+ select_first: false,
+ },
+ extraClasses:['new-connection-dialog-style'],
+ control: NewConnectionSelect2Control,
+ transform: function(data) {
+ response.database_list = data;
+ return data;
+ },
+ },
+ {
+ id: 'user',
+ name: 'user',
+ label: gettext('User'),
+ type: 'text',
+ editable: true,
+ deps: ['server'],
+ select2: {
+ allowClear: false,
+ width: '100%',
+ },
+ control: NewConnectionSelect2Control,
+ url: 'sqleditor.get_new_connection_user',
+ disabled: function(m) {
+ let self_local = this;
+ if (!_.isUndefined(m.get('server')) && !_.isNull(m.get('server'))
+ && m.get('server') !== '') {
+ setTimeout(function() {
+ if(self_local.options.length) {
+ m.set('user', self_local.options[0].value);
+ }
+ }, 10);
+ return false;
+ }
+ return true;
+ },
+ },
+ {
+ id: 'password',
+ name: 'password',
+ label: gettext('Password'),
+ type: 'password',
+ editable: true,
+ disabled: false,
+ deps: ['user'],
+ control: Backform.InputControl.extend({
+ render: function() {
+ let self = this;
+ self.model.attributes.password = null;
+ Backform.InputControl.prototype.render.apply(self, arguments);
+ return self;
+ },
+ onChange: function() {
+ let self = this;
+ Backform.InputControl.prototype.onChange.apply(self, arguments);
+ },
+ }),
+ },
+ ],
+ validate: function() {
+ let msg = null;
+ this.errorModel.clear();
+ if(_.isUndefined(this.get('database')) || _.isNull(this.get('database'))){
+ msg = gettext('Please select database');
+ this.errorModel.set('database', msg);
+ return msg;
+ } else if(_.isUndefined(this.get('database')) || _.isUndefined(this.get('user'))|| _.isNull(this.get('user'))) {
+ msg = gettext('Please select user');
+ this.errorModel.set('user', msg);
+ return msg;
+ } else if((this.attributes.password == '' || _.isUndefined(this.get('password')) || _.isNull(this.get('password')))) {
+ msg = gettext('Please enter password');
+ this.errorModel.set('password', msg);
+ return msg;
+ }
+ return null;
+ },
+ });
+
+ let model = new newConnectionModel();
+ return model;
+}
diff --git a/web/pgadmin/static/scss/_alert.scss b/web/pgadmin/static/scss/_alert.scss
index dac552b..836f0af 100644
--- a/web/pgadmin/static/scss/_alert.scss
+++ b/web/pgadmin/static/scss/_alert.scss
@@ -92,6 +92,7 @@
right: 0;
left: 0;
bottom: 0;
+ z-index: 1;
}
.pg-prop-status-bar {
diff --git a/web/pgadmin/tools/datagrid/__init__.py b/web/pgadmin/tools/datagrid/__init__.py
index f5fc78c..5f32e74 100644
--- a/web/pgadmin/tools/datagrid/__init__.py
+++ b/web/pgadmin/tools/datagrid/__init__.py
@@ -18,21 +18,22 @@ from flask import Response, url_for, session, request, make_response
from werkzeug.useragents import UserAgent
from flask import current_app as app, render_template
from flask_babelex import gettext
-from flask_security import login_required
+from flask_security import login_required, current_user
from pgadmin.tools.sqleditor.command import ObjectRegistry, SQLFilter
+from pgadmin.tools.sqleditor import check_transaction_status
from pgadmin.utils import PgAdminModule
from pgadmin.utils.ajax import make_json_response, bad_request, \
- internal_server_error
+ internal_server_error, unauthorized
from config import PG_DEFAULT_DRIVER
-from pgadmin.model import Server
+from pgadmin.model import Server, User
from pgadmin.utils.driver import get_driver
from pgadmin.utils.exception import ConnectionLost, SSHTunnelConnectionLost
from pgadmin.utils.preferences import Preferences
from pgadmin.settings import get_setting
from pgadmin.browser.utils import underscore_unescape
from pgadmin.utils.exception import ObjectGone
-from pgadmin.utils.constants import MIMETYPE_APP_JS
+from pgadmin.utils.constants import MIMETYPE_APP_JS, UNAUTH_REQ
MODULE_NAME = 'datagrid'
@@ -73,7 +74,8 @@ class DataGridModule(PgAdminModule):
'datagrid.filter_validate',
'datagrid.filter',
'datagrid.panel',
- 'datagrid.close'
+ 'datagrid.close',
+ 'datagrid.update_query_tool_connection'
]
def on_logout(self, user):
@@ -320,10 +322,23 @@ def initialize_query_tool(trans_id, sgid, sid, did=None):
req_args['recreate'] == '1'):
connect = False
+ is_error, errmsg, conn_id, version = _init_query_tool(trans_id, connect,
+ sgid, sid, did)
+ if is_error:
+ return errmsg
+
+ return make_json_response(
+ data={
+ 'connId': str(conn_id),
+ 'serverVersion': version,
+ }
+ )
+
+
+def _init_query_tool(trans_id, connect, sgid, sid, did, **kwargs):
# Create asynchronous connection using random connection id.
conn_id = str(random.randint(1, 9999999))
- # Use Maintenance database OID
manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)
if did is None:
@@ -334,7 +349,7 @@ def initialize_query_tool(trans_id, sgid, sid, did=None):
)
except Exception as e:
app.logger.error(e)
- return internal_server_error(errormsg=str(e))
+ return True, internal_server_error(errormsg=str(e)), '', ''
try:
conn = manager.connection(did=did, conn_id=conn_id,
@@ -342,16 +357,26 @@ def initialize_query_tool(trans_id, sgid, sid, did=None):
use_binary_placeholder=True,
array_to_string=True)
if connect:
- status, msg = conn.connect()
+ user = None
+ password = None
+
+ if 'user' in kwargs and 'password' in kwargs:
+ user = kwargs['user']
+ password = kwargs['password']
+
+ if user:
+ status, msg = conn.connect(user=user, password=password)
+ else:
+ status, msg = conn.connect()
if not status:
app.logger.error(msg)
- return internal_server_error(errormsg=str(msg))
+ return True, internal_server_error(errormsg=str(msg)), '', ''
except (ConnectionLost, SSHTunnelConnectionLost) as e:
app.logger.error(e)
raise
except Exception as e:
app.logger.error(e)
- return internal_server_error(errormsg=str(e))
+ return True, internal_server_error(errormsg=str(e)), '', ''
if 'gridData' not in session:
sql_grid_data = dict()
@@ -373,10 +398,79 @@ def initialize_query_tool(trans_id, sgid, sid, did=None):
# Store the grid dictionary into the session variable
session['gridData'] = sql_grid_data
+ return False, '', conn_id, manager.version
+
+
[email protected](
+ '/initialize/query_tool/update_connection/<int:trans_id>/'
+ '<int:sgid>/<int:sid>/<int:did>',
+ methods=["POST"], endpoint='update_query_tool_connection'
+)
+def update_query_tool_connection(trans_id, sgid, sid, did):
+ # Remove transaction Id.
+ with query_tool_close_session_lock:
+ data = dict()
+ if request.data:
+ data = json.loads(request.data, encoding='utf-8')
+
+ if 'gridData' not in session:
+ return make_json_response(data={'status': True})
+
+ grid_data = session['gridData']
+
+ # Return from the function if transaction id not found
+ if str(trans_id) not in grid_data:
+ return make_json_response(data={'status': True})
+
+ connect = True
+
+ req_args = request.args
+ if ('recreate' in req_args and
+ req_args['recreate'] == '1'):
+ connect = False
+
+ new_trans_id = str(random.randint(1, 9999999))
+ kwargs = {
+ 'user': data['user'],
+ 'password': data['password']
+ }
+
+ is_error, errmsg, conn_id, version = _init_query_tool(
+ new_trans_id, connect, sgid, sid, did, **kwargs)
+
+ if is_error:
+ return errmsg
+ else:
+ try:
+ # Check the transaction and connection status
+ status, error_msg, conn, trans_obj, session_obj = \
+ check_transaction_status(trans_id)
+
+ status, error_msg, new_conn, new_trans_obj, new_session_obj = \
+ check_transaction_status(new_trans_id)
+
+ new_session_obj['primary_keys'] = session_obj[
+ 'primary_keys'] if 'primary_keys' in session_obj else None
+ new_session_obj['columns_info'] = session_obj[
+ 'columns_info'] if 'columns_info' in session_obj else None
+ new_session_obj['client_primary_key'] = session_obj[
+ 'client_primary_key'] if 'client_primary_key'\
+ in session_obj else None
+
+ close_query_tool_session(trans_id)
+ # Remove the information of unique transaction id from the
+ # session variable.
+ grid_data.pop(str(trans_id), None)
+ session['gridData'] = grid_data
+ except Exception as e:
+ app.logger.error(e)
+ # return internal_server_error(errormsg=str(e))
+
return make_json_response(
data={
'connId': str(conn_id),
- 'serverVersion': manager.version,
+ 'serverVersion': version,
+ 'tran_id': new_trans_id
}
)
diff --git a/web/pgadmin/tools/datagrid/templates/datagrid/index.html b/web/pgadmin/tools/datagrid/templates/datagrid/index.html
index e81a42a..fb2526f 100644
--- a/web/pgadmin/tools/datagrid/templates/datagrid/index.html
+++ b/web/pgadmin/tools/datagrid/templates/datagrid/index.html
@@ -391,8 +391,17 @@
title="" role="img">
</i>
</div>
- <div class="editor-title"
- style="background-color: {% if fgcolor %}{{ bgcolor or '#FFFFFF' }}{% endif %}; color: {% if fgcolor %}{{ fgcolor }}{% endif %};"> </div>
+ <div class="connection-info btn-group mr-1" role="group" aria-label="">
+ <div class="editor-title" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"
+ style="background-color: {% if fgcolor %}{{ bgcolor or '#FFFFFF' }}{% endif %}; color: {% if fgcolor %}{{ fgcolor }}{% endif %};">
+ </div>
+ <span class="conn-info-dd dropdown-toggle dropdown-toggle-split"
+ data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></span>
+ <ul class="dropdown-menu" id="connections-list">
+ </ul>
+ </div>
+
+
</div>
<div id="editor-panel" tabindex="0">
<div id="fetching_data" class="pg-sp-container sql-editor-busy-fetching">
@@ -455,6 +464,7 @@ require(['sources/generated/browser_nodes', 'sources/generated/codemirror', 'sou
var script_type_url = '';
{% endif %}
// Start the query tool.
+
sqlEditorController.start(
{{ uniqueId }},
{{ url_params|safe}},
diff --git a/web/pgadmin/tools/datagrid/tests/__init__.py b/web/pgadmin/tools/datagrid/tests/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/web/pgadmin/tools/datagrid/tests/datagrid_test_data.json b/web/pgadmin/tools/datagrid/tests/datagrid_test_data.json
new file mode 100644
index 0000000..a50992f
--- /dev/null
+++ b/web/pgadmin/tools/datagrid/tests/datagrid_test_data.json
@@ -0,0 +1,134 @@
+{
+ "data_grid_init_query_tool": [
+ {
+ "name": "Datagrid init query tool",
+ "url": "/datagrid/initialize/query_tool/",
+ "is_positive_test": true,
+ "mocking_required": false,
+ "test_data": {},
+ "mock_data": {},
+ "expected_data": {
+ "status_code": 200
+ }
+ }
+ ],
+ "data_grid_query_tool_close": [
+ {
+ "name": "Datagrid query tool close",
+ "url": "/datagrid/close/",
+ "is_positive_test": true,
+ "mocking_required": false,
+ "test_data": {},
+ "mock_data": {},
+ "expected_data": {
+ "status_code": 200
+ }
+ }
+ ],
+ "data_grid_validate_filter": [
+ {
+ "name": "Datagrid validate filter",
+ "url": "/datagrid/filter/validate/",
+ "is_positive_test": true,
+ "mocking_required": false,
+ "test_data": "id = 1",
+ "mock_data": {},
+ "expected_data": {
+ "status_code": 200
+ }
+ },
+ {
+ "name": "Datagrid validate filter",
+ "url": "/datagrid/filter/validate/",
+ "is_positive_test": false,
+ "mocking_required": true,
+ "test_data": "id = 1",
+ "mock_data": {
+ "function_name": "pgadmin.utils.driver.psycopg2.connection.Connection.execute_scalar",
+ "return_value": "(False, 'Mocked Internal Server Error while validate filter')"
+ },
+ "expected_data": {
+ "status_code": 200
+ }
+ }
+ ],
+ "data_grid_update_connection": [
+ {
+ "name": "Datagrid update connection positive",
+ "url": "/datagrid/initialize/query_tool/update_connection/",
+ "is_positive_test": true,
+ "mocking_required": false,
+ "is_create_role": false,
+ "test_data": {},
+ "mock_data": {},
+ "expected_data": {
+ "status_code": 200
+ }
+ },
+ {
+ "name": "Datagrid update connection with new user",
+ "url": "/datagrid/initialize/query_tool/update_connection/",
+ "is_positive_test": true,
+ "mocking_required": false,
+ "is_create_role": true,
+ "test_data": {},
+ "mock_data": {},
+ "expected_data": {
+ "status_code": 200
+ }
+ }
+ ],
+ "data_grid_panel": [
+ {
+ "name": "Datagrid Panel",
+ "url": "/datagrid/panel/",
+ "is_positive_test": true,
+ "mocking_required": false,
+ "test_data": {},
+ "mock_data": {
+ },
+ "expected_data": {
+ "status_code": 500
+ }
+ }
+ ],
+ "data_grid_initialize": [
+ {
+ "name": "Datagrid Initialize",
+ "url": "/datagrid/initialize/datagrid/",
+ "is_positive_test": true,
+ "mocking_required": false,
+ "test_data": "id=1",
+ "mock_data": {
+
+ },
+ "expected_data": {
+ "status_code": 200
+ }
+ },{
+ "name": "Datagrid Initialize",
+ "url": "/datagrid/initialize/datagrid/",
+ "is_positive_test": true,
+ "mocking_required": false,
+ "test_data": null,
+ "mock_data": {},
+ "expected_data": {
+ "status_code": 200
+ }
+ },
+ {
+ "name": "Datagrid Initialize",
+ "url": "/datagrid/initialize/datagrid/",
+ "is_positive_test": false,
+ "mocking_required": true,
+ "test_data": "id=1",
+ "mock_data": {
+ "function_name": "pgadmin.utils.driver.psycopg2.connection.Connection.execute_dict",
+ "return_value": "(False, 'Mocked Internal Server Error while initialize datagrid.')"
+ },
+ "expected_data": {
+ "status_code": 500
+ }
+ }
+ ]
+}
diff --git a/web/pgadmin/tools/datagrid/tests/test_data_grid_init_query_tool.py b/web/pgadmin/tools/datagrid/tests/test_data_grid_init_query_tool.py
new file mode 100644
index 0000000..6ecf5de
--- /dev/null
+++ b/web/pgadmin/tools/datagrid/tests/test_data_grid_init_query_tool.py
@@ -0,0 +1,72 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+
+import json
+import uuid
+import random
+
+from unittest.mock import patch
+from pgadmin.browser.server_groups.servers.databases.tests import utils as \
+ database_utils
+
+from pgadmin.utils.route import BaseTestGenerator
+from regression import parent_node_dict
+from regression.python_test_utils import test_utils as utils
+from regression.test_setup import config_data
+from . import utils as data_grid_utils
+
+
+class DatagridInitQueryToolTestCase(BaseTestGenerator):
+ """
+ This will init query-tool connection.
+ """
+
+ scenarios = utils.generate_scenarios(
+ 'data_grid_init_query_tool',
+ data_grid_utils.test_cases
+ )
+
+ def setUp(self):
+ self.database_info = parent_node_dict["database"][-1]
+ self.db_name = self.database_info["db_name"]
+ self.did = self.database_info["db_id"]
+ self.sid = parent_node_dict["server"][-1]["server_id"]
+ self.sgid = config_data['server_group']
+
+ db_con = database_utils.connect_database(self, utils.SERVER_GROUP,
+ self.sid, self.did)
+
+ self.trans_id = str(random.randint(1, 9999999))
+
+ if not db_con['data']["connected"]:
+ raise Exception("Could not connect to database to add a table.")
+
+ def init_query_tool(self):
+ response = self.tester.post(
+ self.url + str(self.trans_id) + '/' + str(self.sgid) + '/' + str(
+ self.sid) + '/' + str(self.did),
+ content_type='html/json'
+ )
+ return response
+
+ def runTest(self):
+ """ This function will init query tool connection."""
+
+ if self.is_positive_test:
+ response = self.init_query_tool()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+
+ self.assertEqual(actual_response_code, expected_response_code)
+
+ def tearDown(self):
+ """This function disconnect database."""
+ database_utils.disconnect_database(self, self.sid,
+ self.did)
diff --git a/web/pgadmin/tools/datagrid/tests/test_data_grid_panel.py b/web/pgadmin/tools/datagrid/tests/test_data_grid_panel.py
new file mode 100644
index 0000000..5ab6a6b
--- /dev/null
+++ b/web/pgadmin/tools/datagrid/tests/test_data_grid_panel.py
@@ -0,0 +1,92 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+
+import json
+import uuid
+import random
+
+from unittest.mock import patch
+from pgadmin.browser.server_groups.servers.databases.tests import utils as \
+ database_utils
+
+from pgadmin.utils.route import BaseTestGenerator
+from regression import parent_node_dict
+from regression.python_test_utils import test_utils as utils
+from regression.test_setup import config_data
+from pgadmin.browser.server_groups.servers.databases.schemas.tests import \
+ utils as schema_utils
+from pgadmin.browser.server_groups.servers.databases.schemas.tables.tests \
+ import utils as tables_utils
+from . import utils as data_grid_utils
+
+
+class DatagridPanelTestCase(BaseTestGenerator):
+ """
+ This will data grid panel.
+ """
+
+ scenarios = utils.generate_scenarios(
+ 'data_grid_panel',
+ data_grid_utils.test_cases
+ )
+
+ def setUp(self):
+ self.database_info = parent_node_dict["database"][-1]
+ self.db_name = self.database_info["db_name"]
+ self.did = self.database_info["db_id"]
+ self.sid = parent_node_dict["server"][-1]["server_id"]
+ self.sgid = config_data['server_group']
+
+ db_con = database_utils.connect_database(self, utils.SERVER_GROUP,
+ self.sid, self.did)
+ if not db_con['data']["connected"]:
+ raise Exception("Could not connect to database to add a table.")
+
+ self.trans_id = str(random.randint(1, 9999999))
+ qt_init = data_grid_utils._init_query_tool(self, self.trans_id,
+ self.sgid, self.sid,
+ self.did)
+
+ if not qt_init['success']:
+ raise Exception("Could not initialize querty tool.")
+
+ def panel(self):
+ query_param = \
+ '?is_query_tool={0}&sgid={1}&sid={2}&server_type={3}' \
+ '&did={4}'.format(True, self.sgid, self.sid,
+ self.server_information['type'], self.did)
+
+ response = self.tester.post(
+ self.url + str(self.trans_id) + query_param,
+ data=json.dumps(self.test_data),
+ content_type='html/json'
+ )
+ return response
+
+ def runTest(self):
+ """ This function will update query tool connection."""
+
+ if self.is_positive_test:
+ response = self.panel()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+ else:
+ with patch(self.mock_data["function_name"],
+ return_value=eval(self.mock_data["return_value"])):
+ response = self.panel()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+
+ self.assertEqual(actual_response_code, expected_response_code)
+
+ def tearDown(self):
+ """This function disconnect database."""
+ database_utils.disconnect_database(self, self.sid,
+ self.did)
diff --git a/web/pgadmin/tools/datagrid/tests/test_data_grid_query_tool_close.py b/web/pgadmin/tools/datagrid/tests/test_data_grid_query_tool_close.py
new file mode 100644
index 0000000..d1a0a97
--- /dev/null
+++ b/web/pgadmin/tools/datagrid/tests/test_data_grid_query_tool_close.py
@@ -0,0 +1,90 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+
+import json
+import uuid
+import random
+
+from unittest.mock import patch
+from pgadmin.browser.server_groups.servers.databases.tests import utils as \
+ database_utils
+
+from pgadmin.utils.route import BaseTestGenerator
+from regression import parent_node_dict
+from regression.python_test_utils import test_utils as utils
+from regression.test_setup import config_data
+from . import utils as data_grid_utils
+
+
+class DatagridQueryToolCloseTestCase(BaseTestGenerator):
+ """
+ This will close query-tool connection.
+ """
+
+ scenarios = utils.generate_scenarios(
+ 'data_grid_query_tool_close',
+ data_grid_utils.test_cases
+ )
+
+ def setUp(self):
+ self.database_info = parent_node_dict["database"][-1]
+ self.db_name = self.database_info["db_name"]
+ self.did = self.database_info["db_id"]
+ self.sid = parent_node_dict["server"][-1]["server_id"]
+ self.sgid = config_data['server_group']
+
+ db_con = database_utils.connect_database(self, utils.SERVER_GROUP,
+ self.sid, self.did)
+
+ if not db_con['data']["connected"]:
+ raise Exception("Could not connect to database to add a table.")
+
+ self.trans_id = str(random.randint(1, 9999999))
+ qt_init = data_grid_utils._init_query_tool(self, self.trans_id,
+ self.sgid, self.sid,
+ self.did)
+
+ if not qt_init['success']:
+ raise Exception("Could not initialize querty tool.")
+
+ self.test_data = {
+ "database": self.did,
+ "server": self.sid
+ }
+
+ if self.server_information['type'] == 'ppas':
+ self.test_data['password'] = 'enterprisedb'
+ self.test_data['user'] = 'enterprisedb'
+ else:
+ self.test_data['password'] = 'root'
+ self.test_data['user'] = 'postgres'
+
+ def close_connection(self):
+ response = self.tester.delete(
+ self.url + str(self.trans_id),
+
+ content_type='html/json'
+ )
+ return response
+
+ def runTest(self):
+ """ This function will update query tool connection."""
+
+ if self.is_positive_test:
+ response = self.close_connection()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+
+ self.assertEqual(actual_response_code, expected_response_code)
+
+ def tearDown(self):
+ """This function disconnect database."""
+ database_utils.disconnect_database(self, self.sid,
+ self.did)
diff --git a/web/pgadmin/tools/datagrid/tests/test_data_grid_update_connection.py b/web/pgadmin/tools/datagrid/tests/test_data_grid_update_connection.py
new file mode 100644
index 0000000..a21c38e
--- /dev/null
+++ b/web/pgadmin/tools/datagrid/tests/test_data_grid_update_connection.py
@@ -0,0 +1,119 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+
+import json
+import uuid
+import random
+
+from unittest.mock import patch
+from pgadmin.browser.server_groups.servers.databases.tests import utils as \
+ database_utils
+
+from pgadmin.utils.route import BaseTestGenerator
+from regression import parent_node_dict
+from regression.python_test_utils import test_utils as utils
+from regression.test_setup import config_data
+from pgadmin.browser.server_groups.servers.roles.tests import \
+ utils as roles_utils
+from . import utils as data_grid_utils
+
+
+class DatagridUpdateConnectionTestCase(BaseTestGenerator):
+ """
+ This will update query-tool connection.
+ """
+
+ scenarios = utils.generate_scenarios(
+ 'data_grid_update_connection',
+ data_grid_utils.test_cases
+ )
+
+ def setUp(self):
+ self.database_info = parent_node_dict["database"][-1]
+ self.db_name = self.database_info["db_name"]
+ self.did = self.database_info["db_id"]
+ self.sid = parent_node_dict["server"][-1]["server_id"]
+ self.sgid = config_data['server_group']
+
+ db_con = database_utils.connect_database(self, utils.SERVER_GROUP,
+ self.sid, self.did)
+
+ self.trans_id = str(random.randint(1, 9999999))
+ self.roles = None
+
+ if self.is_create_role:
+ data = roles_utils.get_role_data(self.server['db_password'])
+ self.role_name = data['rolname']
+ self.role_password = data['rolpassword']
+ roles_utils.create_role_with_password(
+ self.server, self.role_name, self.role_password)
+
+ if not self.is_positive_test or self.is_create_role:
+ qt_init = data_grid_utils._init_query_tool(self, self.trans_id,
+ self.sgid, self.sid,
+ self.did)
+
+ if not qt_init['success']:
+ raise Exception("Could not initialize querty tool.")
+
+ self.test_data = {
+ "database": self.did,
+ "server": self.sid
+ }
+
+ if self.server_information['type'] == 'ppas':
+ self.test_data['password'] = 'enterprisedb'
+ self.test_data['user'] = 'enterprisedb'
+ else:
+ self.test_data['password'] = 'root'
+ self.test_data['user'] = 'postgres'
+
+ if not db_con['data']["connected"]:
+ raise Exception("Could not connect to database to add a table.")
+
+ def update_connection(self, user_data=None):
+ if user_data:
+ response = self.tester.post(
+ self.url + str(self.trans_id) + '/' + str(self.sgid) +
+ '/' + str(self.sid) + '/' + str(self.did),
+ data=json.dumps(user_data),
+ content_type='html/json'
+ )
+ else:
+ response = self.tester.post(
+ self.url + str(self.trans_id) + '/' + str(self.sgid) + '/' +
+ str(self.sid) + '/' + str(self.did),
+ data=json.dumps(self.test_data),
+ content_type='html/json'
+ )
+ return response
+
+ def runTest(self):
+ """ This function will update query tool connection."""
+
+ if self.is_positive_test:
+ user_data = dict()
+ if self.is_create_role:
+ user_data['user'] = self.role_name
+ user_data['password'] = self.role_password
+ response = self.update_connection(user_data=user_data)
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+ else:
+ response = self.update_connection()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+
+ self.assertEqual(actual_response_code, expected_response_code)
+
+ def tearDown(self):
+ """This function disconnect database."""
+ database_utils.disconnect_database(self, self.sid,
+ self.did)
diff --git a/web/pgadmin/tools/datagrid/tests/test_data_grid_validate_filter.py b/web/pgadmin/tools/datagrid/tests/test_data_grid_validate_filter.py
new file mode 100644
index 0000000..0aba5d8
--- /dev/null
+++ b/web/pgadmin/tools/datagrid/tests/test_data_grid_validate_filter.py
@@ -0,0 +1,91 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+
+import json
+import uuid
+import random
+
+from unittest.mock import patch
+from pgadmin.browser.server_groups.servers.databases.tests import utils as \
+ database_utils
+
+from pgadmin.utils.route import BaseTestGenerator
+from regression import parent_node_dict
+from regression.python_test_utils import test_utils as utils
+from regression.test_setup import config_data
+from pgadmin.browser.server_groups.servers.databases.schemas.tests import \
+ utils as schema_utils
+from pgadmin.browser.server_groups.servers.databases.schemas.tables.tests \
+ import utils as tables_utils
+from . import utils as data_grid_utils
+
+
+class DatagridValidateFilterTestCase(BaseTestGenerator):
+ """
+ This will validate filter connection.
+ """
+
+ scenarios = utils.generate_scenarios(
+ 'data_grid_validate_filter',
+ data_grid_utils.test_cases
+ )
+
+ def setUp(self):
+ self.database_info = parent_node_dict["database"][-1]
+ self.db_name = self.database_info["db_name"]
+ self.did = self.database_info["db_id"]
+ self.sid = parent_node_dict["server"][-1]["server_id"]
+ self.sgid = config_data['server_group']
+
+ db_con = database_utils.connect_database(self, utils.SERVER_GROUP,
+ self.sid, self.did)
+ if not db_con['data']["connected"]:
+ raise Exception("Could not connect to database to add a table.")
+ self.schema_id = parent_node_dict['schema'][-1]["schema_id"]
+ self.schema_name = parent_node_dict['schema'][-1]["schema_name"]
+ schema_response = schema_utils.verify_schemas(self.server,
+ self.db_name,
+ self.schema_name)
+ if not schema_response:
+ raise Exception("Could not find the schema to add a table.")
+ self.table_name = "table_for_wizard%s" % (str(uuid.uuid4())[1:8])
+ self.table_id = tables_utils.create_table(self.server, self.db_name,
+ self.schema_name,
+ self.table_name)
+
+ def validate_filter(self):
+ response = self.tester.post(
+ self.url + str(self.sid) + '/' + str(self.did) + '/' +
+ str(self.table_id),
+ data=json.dumps(self.test_data),
+ content_type='html/json'
+ )
+ return response
+
+ def runTest(self):
+ """ This function will update query tool connection."""
+
+ if self.is_positive_test:
+ response = self.validate_filter()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+ else:
+ with patch(self.mock_data["function_name"],
+ return_value=eval(self.mock_data["return_value"])):
+ response = self.validate_filter()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+
+ self.assertEqual(actual_response_code, expected_response_code)
+
+ def tearDown(self):
+ """This function disconnect database."""
+ database_utils.disconnect_database(self, self.sid,
+ self.did)
diff --git a/web/pgadmin/tools/datagrid/tests/test_initialize_data_grid.py b/web/pgadmin/tools/datagrid/tests/test_initialize_data_grid.py
new file mode 100644
index 0000000..130a1d6
--- /dev/null
+++ b/web/pgadmin/tools/datagrid/tests/test_initialize_data_grid.py
@@ -0,0 +1,108 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+
+import json
+import uuid
+import random
+
+from unittest.mock import patch
+from pgadmin.browser.server_groups.servers.databases.tests import utils as \
+ database_utils
+
+from pgadmin.utils.route import BaseTestGenerator
+from regression import parent_node_dict
+from regression.python_test_utils import test_utils as utils
+from regression.test_setup import config_data
+from pgadmin.browser.server_groups.servers.databases.schemas.tests import \
+ utils as schema_utils
+from pgadmin.browser.server_groups.servers.databases.schemas.tables.tests \
+ import utils as tables_utils
+from . import utils as data_grid_utils
+
+
+class DatagridInitializeTestCase(BaseTestGenerator):
+ """
+ This will Initialize datagrid
+ """
+
+ scenarios = utils.generate_scenarios(
+ 'data_grid_initialize',
+ data_grid_utils.test_cases
+ )
+
+ def setUp(self):
+ self.database_info = parent_node_dict["database"][-1]
+ self.db_name = self.database_info["db_name"]
+ self.did = self.database_info["db_id"]
+ self.sid = parent_node_dict["server"][-1]["server_id"]
+ self.sgid = config_data['server_group']
+
+ db_con = database_utils.connect_database(self, utils.SERVER_GROUP,
+ self.sid, self.did)
+ if not db_con['data']["connected"]:
+ raise Exception("Could not connect to database to add a table.")
+
+ self.schema_id = parent_node_dict['schema'][-1]["schema_id"]
+ self.schema_name = parent_node_dict['schema'][-1]["schema_name"]
+ schema_response = schema_utils.verify_schemas(self.server,
+ self.db_name,
+ self.schema_name)
+ if not schema_response:
+ raise Exception("Could not find the schema to add a table.")
+ self.table_name = "table_for_wizard%s" % (str(uuid.uuid4())[1:8])
+ self.table_id = tables_utils.create_table(self.server, self.db_name,
+ self.schema_name,
+ self.table_name)
+ self.trans_id = str(random.randint(1, 9999999))
+ qt_init = data_grid_utils._init_query_tool(self, self.trans_id,
+ self.sgid, self.sid,
+ self.did)
+
+ if not qt_init['success']:
+ raise Exception("Could not initialize query tool.")
+
+ def initialize_datagrid(self):
+ if self.test_data:
+ response = self.tester.post(
+ self.url + str(self.trans_id) + '/4/table/' +
+ str(self.sgid) + '/' + str(self.sid) + '/' +
+ str(self.did) + '/' + str(self.table_id),
+ data=json.dumps(self.test_data),
+ content_type='html/json'
+ )
+ else:
+ response = self.tester.post(
+ self.url + str(self.trans_id) + '/4/table/' +
+ str(self.sgid) + '/' + str(self.sid) + '/' +
+ str(self.did) + '/' + str(self.table_id),
+ content_type='html/json'
+ )
+ return response
+
+ def runTest(self):
+ """ This function will update query tool connection."""
+
+ if self.is_positive_test:
+ response = self.initialize_datagrid()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+ else:
+ with patch(self.mock_data["function_name"],
+ return_value=eval(self.mock_data["return_value"])):
+ response = self.initialize_datagrid()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+
+ self.assertEqual(actual_response_code, expected_response_code)
+
+ def tearDown(self):
+ """This function disconnect database."""
+ database_utils.disconnect_database(self, self.sid,
+ self.did)
diff --git a/web/pgadmin/tools/datagrid/tests/utils.py b/web/pgadmin/tools/datagrid/tests/utils.py
new file mode 100644
index 0000000..82f9427
--- /dev/null
+++ b/web/pgadmin/tools/datagrid/tests/utils.py
@@ -0,0 +1,33 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+import os
+import json
+
+file_name = os.path.basename(__file__)
+CURRENT_PATH = os.path.dirname(os.path.realpath(__file__))
+with open(CURRENT_PATH + "/datagrid_test_data.json") as data_file:
+ test_cases = json.load(data_file)
+
+
+def _init_query_tool(self, trans_id, server_group, server_id, db_id):
+ QUERY_TOOL_INIT_URL = '/datagrid/initialize/query_tool/'
+
+ qt_init = self.tester.post(
+ '{0}/{1}/{2}/{3}/{4}'.format(
+ QUERY_TOOL_INIT_URL,
+ trans_id,
+ server_group,
+ server_id,
+ db_id
+ ),
+ follow_redirects=True
+ )
+ assert qt_init.status_code == 200
+ qt_init = json.loads(qt_init.data.decode('utf-8'))
+ return qt_init
diff --git a/web/pgadmin/tools/sqleditor/__init__.py b/web/pgadmin/tools/sqleditor/__init__.py
index c62bc7c..434ba88 100644
--- a/web/pgadmin/tools/sqleditor/__init__.py
+++ b/web/pgadmin/tools/sqleditor/__init__.py
@@ -10,33 +10,38 @@
"""A blueprint module implementing the sqleditor frame."""
import os
import pickle
-import sys
import re
+from urllib.parse import unquote
import simplejson as json
-from flask import Response, url_for, render_template, session, request, \
- current_app
+from config import PG_DEFAULT_DRIVER, ON_DEMAND_RECORD_COUNT
+from flask import Response, url_for, render_template, session, current_app
+from flask import request, jsonify
from flask_babelex import gettext
from flask_security import login_required, current_user
-from urllib.parse import unquote
-
-from config import PG_DEFAULT_DRIVER, ON_DEMAND_RECORD_COUNT
from pgadmin.misc.file_manager import Filemanager
from pgadmin.tools.sqleditor.command import QueryToolCommand
from pgadmin.tools.sqleditor.utils.constant_definition import ASYNC_OK, \
ASYNC_EXECUTION_ABORTED, \
CONNECTION_STATUS_MESSAGE_MAPPING, TX_STATUS_INERROR
+from pgadmin.tools.sqleditor.utils.filter_dialog import FilterDialog
+from pgadmin.tools.sqleditor.utils.query_history import QueryHistory
+from pgadmin.tools.sqleditor.utils.query_tool_fs_utils import \
+ read_file_generator
+from pgadmin.tools.sqleditor.utils.query_tool_preferences import \
+ register_query_tool_preferences
from pgadmin.tools.sqleditor.utils.start_running_query import StartRunningQuery
from pgadmin.tools.sqleditor.utils.update_session_grid_transaction import \
update_session_grid_transaction
from pgadmin.utils import PgAdminModule
from pgadmin.utils import get_storage_directory
from pgadmin.utils.ajax import make_json_response, bad_request, \
- success_return, internal_server_error
+ internal_server_error
+from pgadmin.utils.constants import MIMETYPE_APP_JS
from pgadmin.utils.driver import get_driver
-from pgadmin.utils.menu import MenuItem
-from pgadmin.utils.exception import ConnectionLost, SSHTunnelConnectionLost,\
+from pgadmin.utils.exception import ConnectionLost, SSHTunnelConnectionLost, \
CryptKeyMissing
+from pgadmin.utils.menu import MenuItem
from pgadmin.utils.sqlautocomplete.autocomplete import SQLAutoComplete
from pgadmin.tools.sqleditor.utils.query_tool_preferences import \
register_query_tool_preferences
@@ -46,6 +51,8 @@ from pgadmin.tools.sqleditor.utils.filter_dialog import FilterDialog
from pgadmin.tools.sqleditor.utils.query_history import QueryHistory
from pgadmin.utils.constants import MIMETYPE_APP_JS, SERVER_CONNECTION_CLOSED,\
ERROR_MSG_TRANS_ID_NOT_FOUND
+from pgadmin.model import Server
+
MODULE_NAME = 'sqleditor'
@@ -109,6 +116,9 @@ class SqlEditorModule(PgAdminModule):
'sqleditor.get_query_history',
'sqleditor.add_query_history',
'sqleditor.clear_query_history',
+ 'sqleditor.get_new_connection_data',
+ 'sqleditor.get_new_connection_database',
+ 'sqleditor.get_new_connection_user',
]
def register_preferences(self):
@@ -1465,6 +1475,188 @@ def get_filter_data(trans_id):
return FilterDialog.get(status, error_msg, conn, trans_obj, session_ob)
[email protected](
+ '/new_connection_dialog/<int:sgid>/<int:sid>',
+ methods=["GET"], endpoint='get_new_connection_data'
+)
+@login_required
+def get_new_connection_data(sgid, sid=None):
+ """
+ This method is used to get required data for get new connection.
+ :extract_sql_from_network_parameters,
+ """
+ try:
+ # if sid and not did:
+ servers = Server.query.all()
+ server_list = [
+ {'name': server.serialize['name'], "id": server.serialize['id']}
+ for server in servers]
+
+ msg = "Success"
+ return make_json_response(
+ data={
+ 'status': True,
+ 'msg': msg,
+ 'result': {
+ 'server_list': server_list
+ }
+ }
+ )
+
+ except Exception as e:
+ return make_json_response(
+ data={
+ 'status': False,
+ 'msg': 'Unable to fetch data.',
+ 'result': {
+ 'server_list': []
+ }
+ }
+ )
+
+
[email protected](
+ '/new_connection_database/<int:sgid>/<int:sid>',
+ methods=["GET"], endpoint='get_new_connection_database'
+)
+@login_required
+def get_new_connection_database(sgid, sid=None):
+ """
+ This method is used to get required data for get new connection.
+ :extract_sql_from_network_parameters,
+ """
+ try:
+ database_list = []
+ from pgadmin.utils.driver import get_driver
+ manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)
+ conn = manager.connection()
+ if conn.connected():
+ is_connected = True
+ else:
+ # connection = conn.connect()
+ is_connected = False
+ if is_connected:
+ if sid:
+ template_path = 'databases/sql/#{0}#'.format(manager.version)
+ last_system_oid = 0
+ server_node_res = manager
+
+ db_disp_res = None
+ params = None
+ if server_node_res and server_node_res.db_res:
+ db_disp_res = ", ".join(
+ ['%s'] * len(server_node_res.db_res.split(','))
+ )
+ params = tuple(server_node_res.db_res.split(','))
+ sql = render_template(
+ "/".join([template_path, 'nodes.sql']),
+ last_system_oid=last_system_oid,
+ db_restrictions=db_disp_res
+ )
+ status, databases = conn.execute_dict(sql, params)
+ database_list = [
+ {'label': database['name'], 'value': database['did']} for
+ database in databases['rows']]
+ else:
+ status = False
+
+ msg = "Success"
+ return make_json_response(
+ data={
+ 'status': status,
+ 'msg': msg,
+ 'result': {
+ 'data': database_list,
+ }
+ }
+ )
+ else:
+ return make_json_response(
+ data={
+ 'status': False,
+ 'msg': 'Server not connected, '
+ 'Please connect with server and try again.',
+ 'result': {
+ 'database_list': [],
+ }
+ }
+ )
+ except Exception as e:
+ return make_json_response(
+ data={
+ 'status': False,
+ 'msg': 'Unable to fetch data.',
+ 'result': {
+ 'database_list': [],
+ }
+ }
+ )
+
+
[email protected](
+ '/new_connection_user/<int:sgid>/<int:sid>',
+ methods=["GET"], endpoint='get_new_connection_user'
+)
+@login_required
+def get_new_connection_user(sgid, sid=None):
+ """
+ This method is used to get required data for get new connection.
+ :extract_sql_from_network_parameters,
+ """
+ try:
+ from pgadmin.utils.driver import get_driver
+ manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)
+ conn = manager.connection()
+ user_list = []
+ if conn.connected():
+ is_connected = True
+ else:
+ is_connected = False
+ if is_connected:
+ if sid:
+ sql_path = 'roles/sql/#{0}#'.format(manager.version)
+ status, users = conn.execute_2darray(
+ render_template(sql_path + 'nodes.sql')
+ )
+ user_list = [
+ {'value': user['rolname'], 'label': user['rolname']} for
+ user in users['rows'] if user['rolcanlogin']]
+ else:
+ status = False
+
+ msg = "Success"
+ return make_json_response(
+ data={
+ 'status': status,
+ 'msg': msg,
+ 'result': {
+ 'data': user_list,
+ }
+ }
+ )
+ else:
+ return make_json_response(
+ data={
+ 'status': False,
+ 'msg': 'Server not connected, '
+ 'Please connect with server and try again.',
+ 'result': {
+ 'user_list': [],
+ }
+ }
+ )
+ except Exception as e:
+ return make_json_response(
+ data={
+ 'status': False,
+ 'msg': 'Unable to fetch data.',
+ 'result': {
+ 'user_list': [],
+ }
+ }
+ )
+
+
@blueprint.route(
'/filter_dialog/<int:trans_id>',
methods=["PUT"], endpoint='set_filter_data'
diff --git a/web/pgadmin/tools/sqleditor/static/css/sqleditor.css b/web/pgadmin/tools/sqleditor/static/css/sqleditor.css
index 00543ff..b8c2d2e 100644
--- a/web/pgadmin/tools/sqleditor/static/css/sqleditor.css
+++ b/web/pgadmin/tools/sqleditor/static/css/sqleditor.css
@@ -315,10 +315,6 @@ input.editor-checkbox:focus {
padding: 10px 0px;
}
-.editor-title {
- width:100%;
-}
-
.connection-status-hide {
display: none !important;
}
@@ -395,3 +391,7 @@ input.editor-checkbox:focus {
.hide-vertical-scrollbar {
overflow-y: hidden;
}
+
+.new-connection-dialog-style {
+ width: 100% !important;
+}
diff --git a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
index ce02a53..9720d36 100644
--- a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
+++ b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
@@ -14,6 +14,7 @@ define('tools.querytool', [
'jqueryui.position', 'underscore', 'pgadmin.alertifyjs',
'sources/pgadmin', 'backbone', 'bundled_codemirror', 'sources/utils',
'pgadmin.misc.explain',
+ 'pgadmin.user_management.current_user',
'sources/selection/grid_selector',
'sources/selection/active_cell_capture',
'sources/selection/clipboard',
@@ -26,6 +27,7 @@ define('tools.querytool', [
'sources/sqleditor/execute_query',
'sources/sqleditor/query_tool_http_error_handler',
'sources/sqleditor/filter_dialog',
+ 'sources/sqleditor/new_connection_dialog',
'sources/sqleditor/geometry_viewer',
'sources/sqleditor/history/history_collection.js',
'sources/sqleditor/history/query_history',
@@ -52,8 +54,8 @@ define('tools.querytool', [
'pgadmin.tools.user_management',
], function(
gettext, url_for, $, jqueryui, jqueryui_position, _, alertify, pgAdmin, Backbone, codemirror, pgadminUtils,
- pgExplain, GridSelector, ActiveCellCapture, clipboard, copyData, RangeSelectionHelper, handleQueryOutputKeyboardEvent,
- XCellSelectionModel, setStagedRows, SqlEditorUtils, ExecuteQuery, httpErrorHandler, FilterHandler,
+ pgExplain, current_user, GridSelector, ActiveCellCapture, clipboard, copyData, RangeSelectionHelper, handleQueryOutputKeyboardEvent,
+ XCellSelectionModel, setStagedRows, SqlEditorUtils, ExecuteQuery, httpErrorHandler, FilterHandler, newConnectionHandler,
GeometryViewer, historyColl, queryHist, querySources,
keyboardShortcuts, queryToolActions, queryToolNotifications, Datagrid,
modifyAnimation, calculateQueryRunTime, callRenderAfterPoll, queryToolPref, queryTxnStatus, csrfToken, panelTitleFunc,
@@ -89,6 +91,9 @@ define('tools.querytool', [
this.layout = opts.layout;
this.set_server_version(opts.server_ver);
this.trigger('pgadmin-sqleditor:view:initialised');
+ this.connection_list = [
+ {'server_group': null,'server': null, 'database': null, 'user': null, 'password': null, 'title': '<New Connection>'},
+ ];
},
// Bind all the events
@@ -151,6 +156,30 @@ define('tools.querytool', [
'click #btn-rollback': 'on_rollback_transaction',
},
+ render_connection: function(data_list) {
+ var dropdownElement = document.getElementById('connections-list');
+ dropdownElement.innerHTML = '';
+ data_list.forEach((option, index) => {
+ $('#connections-list').append('<li class="connection-list-item" data-index='+ index +'><a class="dropdown-item" href="#" tabindex="0">'+ option.title +'</a></li>');
+
+ });
+ var self = this;
+ $('.connection-list-item').click(function() {
+ self.get_connection_data(this);
+ });
+ },
+
+ get_connection_data: function(event){
+ var index = $(event).attr('data-index');
+ var connection_details = this.connection_list[index];
+ if(connection_details.server_group) {
+ this.on_change_connection(connection_details);
+ } else {
+ this.on_new_connection();
+ }
+
+ },
+
reflectPreferences: function() {
let self = this,
browser = pgWindow.default.pgAdmin.Browser,
@@ -199,8 +228,15 @@ define('tools.querytool', [
});
},
- set_editor_title: function(title) {
- this.$el.find('.editor-title').text(title);
+ set_editor_title: function(title, is_connected) {
+ if(is_connected) {
+ this.$el.find('.editor-title').text(title);
+ this.render_connection(this.connection_list);
+ } else {
+ this.$el.find('.editor-title').text(title);
+ this.render_connection(this.connection_list);
+ }
+
},
// This function is used to render the template.
@@ -684,6 +720,8 @@ define('tools.querytool', [
pgBrowser.register_to_activity_listener(document, ()=>{
alertify.alert(gettext('Timeout'), gettext('Your session has timed out due to inactivity. Please close the window and login again.'));
});
+
+ self.render_connection(self.connection_list);
},
/* Regarding SlickGrid usage in render_grid function.
@@ -1595,6 +1633,17 @@ define('tools.querytool', [
);
},
+ on_new_connection: function() {
+ var self = this;
+
+ // Trigger the show_filter signal to the SqlEditorController class
+ self.handler.trigger(
+ 'pgadmin-sqleditor:button:show_new_connection',
+ self,
+ self.handler
+ );
+ },
+
// Callback function for include filter button click.
on_include_filter: function(ev) {
var self = this;
@@ -2038,6 +2087,55 @@ define('tools.querytool', [
queryToolActions.executeRollback(this.handler);
},
+ on_change_connection: function(connection_details, ref) {
+ var self = this;
+ $.ajax({
+ url: url_for('datagrid.update_query_tool_connection', {
+ 'trans_id': self.transId,
+ 'sgid': connection_details['server_group'],
+ 'sid': connection_details['server'],
+ 'did': connection_details['database'],
+ }),
+ method: 'POST',
+ contentType: 'application/json',
+ data: JSON.stringify(connection_details),
+ })
+ .done(function(res) {
+ if(res.success) {
+ self.transId = res.data.tran_id;
+ self.handler.transId = res.data.tran_id;
+ self.handler.url_params = {
+ 'did': connection_details['database'],
+ 'is_query_tool': self.handler.url_params.is_query_tool,
+ 'server_type': self.handler.url_params.server_type,
+ 'sgid': connection_details['server_group'],
+ 'sid': connection_details['server'],
+ 'title': connection_details['title'],
+ };
+ self.set_editor_title(self.handler.url_params.title);
+ self.handler.setTitle(self.handler.url_params.title);
+ alertify.success('connected successfully');
+ if(ref){
+ let connection_data = {
+ 'server_group': self.handler.url_params.sgid,
+ 'server': connection_details['server'],
+ 'database': connection_details['database'],
+ 'user': connection_details['user'],
+ 'password': connection_details['password'],
+ 'title': connection_details['title'],
+ 'is_allow_new_connection': true,
+ };
+ self.connection_list.unshift(connection_data);
+ self.render_connection(self.connection_list);
+ ref.close();
+ }
+ }
+
+ })
+ .fail(function(xhr) {
+ alertify.error(xhr.responseJSON['errormsg']);
+ });
+ },
});
/* Defining controller class for data grid, which actually
@@ -2357,7 +2455,18 @@ define('tools.querytool', [
});
$('#btn-conn-status i').removeClass('obtaining-conn');
- self.gridView.set_editor_title(_.unescape(url_params.title));
+ self.gridView.set_editor_title(_.unescape(url_params.title), true);
+ let connection_data = {
+ 'server_group': self.gridView.handler.url_params.sgid,
+ 'server': self.gridView.handler.url_params.sid,
+ 'database': self.gridView.handler.url_params.did,
+ 'user': null,
+ 'password': null,
+ 'title': _.unescape(url_params.title),
+ 'is_allow_new_connection': false,
+ };
+ self.gridView.connection_list.unshift(connection_data);
+ self.gridView.render_connection(self.gridView.connection_list);
};
pgBrowser.Events.on('pgadmin:query_tool:connected:' + transId, afterConn);
@@ -2452,6 +2561,7 @@ define('tools.querytool', [
self.on('pgadmin-sqleditor:button:save_file', self._save_file, self);
self.on('pgadmin-sqleditor:button:deleterow', self._delete, self);
self.on('pgadmin-sqleditor:button:show_filter', self._show_filter, self);
+ self.on('pgadmin-sqleditor:button:show_new_connection', self._show_new_connection, self);
self.on('pgadmin-sqleditor:button:include_filter', self._include_filter, self);
self.on('pgadmin-sqleditor:button:exclude_filter', self._exclude_filter, self);
self.on('pgadmin-sqleditor:button:remove_filter', self._remove_filter, self);
@@ -3622,7 +3732,6 @@ define('tools.querytool', [
}
};
},
-
// This function will show the filter in the text area.
_show_filter: function() {
let self = this,
@@ -3637,7 +3746,19 @@ define('tools.querytool', [
}
FilterHandler.dialog(self, reconnect);
},
+ // This function will show the new connection.
+ _show_new_connection: function() {
+ let self = this,
+ reconnect = false;
+ /* When server is disconnected and connected, connection is lost,
+ * To reconnect pass true
+ */
+ if (arguments.length > 0 && arguments[arguments.length - 1] == 'connect') {
+ reconnect = true;
+ }
+ newConnectionHandler.dialog(self, reconnect);
+ },
// This function will include the filter by selection.
_include_filter: function() {
var self = this,
diff --git a/web/pgadmin/tools/sqleditor/static/scss/_sqleditor.scss b/web/pgadmin/tools/sqleditor/static/scss/_sqleditor.scss
index fd1e5d3..53f2449 100644
--- a/web/pgadmin/tools/sqleditor/static/scss/_sqleditor.scss
+++ b/web/pgadmin/tools/sqleditor/static/scss/_sqleditor.scss
@@ -30,6 +30,19 @@
color: $sql-title-fg;
}
+.connection-info {
+ background: $sql-title-bg;
+ color: $sql-title-fg;
+ width:100%;
+ display: inherit;
+}
+
+.conn-info-dd {
+ padding-top: 0.3em;
+ padding-left: 0.2em;
+ cursor: pointer;
+}
+
#editor-panel {
z-index: 0;
diff --git a/web/pgadmin/tools/sqleditor/tests/test_new_connection_database.py b/web/pgadmin/tools/sqleditor/tests/test_new_connection_database.py
new file mode 100644
index 0000000..20fe3e3
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/tests/test_new_connection_database.py
@@ -0,0 +1,98 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+import json
+from pgadmin.utils.route import BaseTestGenerator
+from regression import parent_node_dict
+from regression.test_setup import config_data
+from regression.python_test_utils import test_utils as utils
+
+
+class TestNewConnectionDatabase(BaseTestGenerator):
+ """ This class will test new connection database. """
+ scenarios = [
+ ('New connection dialog',
+ dict(
+ url="/sqleditor/new_connection_database/",
+ is_positive_test=True,
+ mocking_required=False,
+ is_server_conn_required=False,
+ test_data={},
+ mock_data={},
+ expected_data={
+ "status_code": 200
+ }
+ )),
+ ('New connection dialog connect server',
+ dict(
+ url="/sqleditor/new_connection_database/",
+ is_positive_test=True,
+ mocking_required=False,
+ is_server_conn_required=True,
+ test_data={},
+ mock_data={},
+ expected_data={
+ "status_code": 200
+ }
+ )),
+ ('New connection dialog negative',
+ dict(
+ url="/sqleditor/new_connection_database/",
+ is_positive_test=False,
+ mocking_required=False,
+ is_server_conn_required=True,
+ test_data={},
+ mock_data={},
+ expected_data={
+ "status_code": 200
+ }
+ )),
+ ]
+
+ def setUp(self):
+ self.sid = parent_node_dict["server"][-1]["server_id"]
+ self.sgid = config_data['server_group']
+
+ def get_database(self):
+ response = self.tester.get(
+ self.url + str(self.sgid) + '/' + str(self.sid),
+ content_type='html/json'
+ )
+
+ return response
+
+ def runTest(self):
+ if self.is_positive_test:
+ if self.is_server_conn_required:
+ self.server['password'] = self.server['db_password']
+ server_response = self.tester.post(
+ '/browser/server/connect/{0}/{1}'.format(
+ utils.SERVER_GROUP,
+ self.sid),
+ data=json.dumps(self.server),
+ content_type='html/json'
+ )
+ response = self.get_database()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+ else:
+ if self.is_server_conn_required:
+ self.server['password'] = self.server['db_password']
+ server_response = self.tester.post(
+ '/browser/server/connect/{0}/{1}'.format(
+ utils.SERVER_GROUP,
+ self.sid),
+ data=json.dumps(self.server),
+ content_type='html/json'
+ )
+ self.sid = 0
+ response = self.get_database()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+ self.assertEqual(actual_response_code, expected_response_code)
diff --git a/web/pgadmin/tools/sqleditor/tests/test_new_connection_dialog.py b/web/pgadmin/tools/sqleditor/tests/test_new_connection_dialog.py
new file mode 100644
index 0000000..75a47ef
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/tests/test_new_connection_dialog.py
@@ -0,0 +1,50 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+import json
+from pgadmin.utils.route import BaseTestGenerator
+from regression import parent_node_dict
+from regression.test_setup import config_data
+from regression.python_test_utils import test_utils as utils
+
+
+class TestNewConnectionDialog(BaseTestGenerator):
+ """ This class will test new connection dialog. """
+ scenarios = [
+ ('New connection dialog',
+ dict(
+ url="/sqleditor/new_connection_dialog/",
+ is_positive_test=True,
+ mocking_required=False,
+ is_connect_server=False,
+ test_data={},
+ mock_data={},
+ expected_data={
+ "status_code": 200
+ }
+ )),
+ ]
+
+ def setUp(self):
+ self.sid = parent_node_dict["server"][-1]["server_id"]
+ self.sgid = config_data['server_group']
+
+ def new_connection(self):
+ response = self.tester.get(
+ self.url + str(self.sgid) + '/' + str(self.sgid),
+ content_type='html/json'
+ )
+
+ return response
+
+ def runTest(self):
+ if self.is_positive_test:
+ response = self.new_connection()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+ self.assertEqual(actual_response_code, expected_response_code)
diff --git a/web/pgadmin/tools/sqleditor/tests/test_new_connection_user.py b/web/pgadmin/tools/sqleditor/tests/test_new_connection_user.py
new file mode 100644
index 0000000..7b6c12e
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/tests/test_new_connection_user.py
@@ -0,0 +1,98 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+import json
+from pgadmin.utils.route import BaseTestGenerator
+from regression import parent_node_dict
+from regression.test_setup import config_data
+from regression.python_test_utils import test_utils as utils
+
+
+class TestNewConnectionUser(BaseTestGenerator):
+ """ This class will test new connection user. """
+ scenarios = [
+ ('New connection dialog',
+ dict(
+ url="/sqleditor/new_connection_user/",
+ is_positive_test=True,
+ mocking_required=False,
+ is_server_conn_required=False,
+ test_data={},
+ mock_data={},
+ expected_data={
+ "status_code": 200
+ }
+ )),
+ ('New connection dialog connect server',
+ dict(
+ url="/sqleditor/new_connection_user/",
+ is_positive_test=True,
+ mocking_required=False,
+ is_server_conn_required=True,
+ test_data={},
+ mock_data={},
+ expected_data={
+ "status_code": 200
+ }
+ )),
+ ('New connection dialog negative',
+ dict(
+ url="/sqleditor/new_connection_user/",
+ is_positive_test=False,
+ mocking_required=False,
+ is_server_conn_required=True,
+ test_data={},
+ mock_data={},
+ expected_data={
+ "status_code": 200
+ }
+ )),
+ ]
+
+ def setUp(self):
+ self.sid = parent_node_dict["server"][-1]["server_id"]
+ self.sgid = config_data['server_group']
+
+ def get_use(self):
+ response = self.tester.get(
+ self.url + str(self.sgid) + '/' + str(self.sid),
+ content_type='html/json'
+ )
+
+ return response
+
+ def runTest(self):
+ if self.is_positive_test:
+ if self.is_server_conn_required:
+ self.server['password'] = self.server['db_password']
+ server_response = self.tester.post(
+ '/browser/server/connect/{0}/{1}'.format(
+ utils.SERVER_GROUP,
+ self.sid),
+ data=json.dumps(self.server),
+ content_type='html/json'
+ )
+ response = self.get_use()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+ else:
+ if self.is_server_conn_required:
+ self.server['password'] = self.server['db_password']
+ server_response = self.tester.post(
+ '/browser/server/connect/{0}/{1}'.format(
+ utils.SERVER_GROUP,
+ self.sid),
+ data=json.dumps(self.server),
+ content_type='html/json'
+ )
+ self.sid = 0
+ response = self.get_use()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+ self.assertEqual(actual_response_code, expected_response_code)
diff --git a/web/pgadmin/utils/driver/psycopg2/connection.py b/web/pgadmin/utils/driver/psycopg2/connection.py
index 9cb65bc..115598a 100644
--- a/web/pgadmin/utils/driver/psycopg2/connection.py
+++ b/web/pgadmin/utils/driver/psycopg2/connection.py
@@ -21,7 +21,7 @@ import psycopg2
from flask import g, current_app
from flask_babelex import gettext
from flask_security import current_user
-from pgadmin.utils.crypto import decrypt
+from pgadmin.utils.crypto import decrypt, encrypt
from psycopg2.extensions import encodings
import config
@@ -211,8 +211,23 @@ class Connection(BaseConnection):
password = None
passfile = None
manager = self.manager
+ encpass = None
+ is_update_password = True
+ crypt_key_present, crypt_key = get_crypt_key()
+
+ if 'user' in kwargs and kwargs['password']:
+ password = kwargs['password']
+ # kwargs['password'] = encrypt(password, crypt_key)
+ '''
+ Remove the password from kwargs as we don't want to update
+ password in manager when user is connecting through query toll.
+ If we update password in manager will affect existing connections.
+ '''
+ kwargs.pop('password')
+ is_update_password = False
+ else:
+ encpass = kwargs['password'] if 'password' in kwargs else None
- encpass = kwargs['password'] if 'password' in kwargs else None
passfile = kwargs['passfile'] if 'passfile' in kwargs else None
tunnel_password = kwargs['tunnel_password'] if 'tunnel_password' in \
kwargs else ''
@@ -227,16 +242,16 @@ class Connection(BaseConnection):
if manager.use_ssh_tunnel == 1:
manager.check_ssh_tunnel_alive()
- if encpass is None:
- encpass = self.password or getattr(manager, 'password', None)
+ if is_update_password:
+ if encpass is None:
+ encpass = self.password or getattr(manager, 'password', None)
- self.password = encpass
+ self.password = encpass
# Reset the existing connection password
if self.reconnecting is not False:
self.password = None
- crypt_key_present, crypt_key = get_crypt_key()
if not crypt_key_present:
raise CryptKeyMissing()
@@ -269,7 +284,10 @@ class Connection(BaseConnection):
try:
database = self.db
- user = manager.user
+ if 'user' in kwargs and kwargs['user']:
+ user = kwargs['user']
+ else:
+ user = manager.user
conn_id = self.conn_id
import os
@@ -338,10 +356,10 @@ class Connection(BaseConnection):
self.wasConnected = False
raise e
- if status:
+ if status and is_update_password:
manager._update_password(encpass)
else:
- if not self.reconnecting:
+ if not self.reconnecting and is_update_password:
self.wasConnected = False
return status, msg
@@ -491,7 +509,7 @@ WHERE db.datname = current_database()""")
if len(manager.db_info) == 1:
manager.did = res['did']
- self._set_user_info(cur, manager)
+ self._set_user_info(cur, manager, **kwargs)
self._set_server_type_and_password(kwargs, manager)
@@ -499,7 +517,7 @@ WHERE db.datname = current_database()""")
return True, None
- def _set_user_info(self, cur, manager):
+ def _set_user_info(self, cur, manager, **kwargs):
"""
Set user info.
:param cur:
@@ -517,7 +535,7 @@ WHERE db.datname = current_database()""")
WHERE
rolname = current_user""")
- if status is None:
+ if status is None and 'user' not in kwargs:
manager.user_info = dict()
if cur.rowcount > 0:
manager.user_info = cur.fetchmany(1)[0]
^ permalink raw reply [nested|flat] 18+ messages in thread
* Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab
2020-09-17 10:45 [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
@ 2020-09-21 11:54 ` Akshay Joshi <[email protected]>
2020-09-28 05:50 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
0 siblings, 1 reply; 18+ messages in thread
From: Akshay Joshi @ 2020-09-21 11:54 UTC (permalink / raw)
To: Nikhil Mohite <[email protected]>; +Cc: pgadmin-hackers
Hi Nikhil
Following are the initial review comments:
- Open View/Edit data on any table and click on the same database
connection and then click on the Execute button. Got "get_primary_keys()
takes 1 positional argument but 2 were given" error.
- In my opinion, we should hide the option to change the database
connection for View/Edit Data.
- If the user clicks on the same database connection multiple times then
no need to change the backend connection and transaction id. Add validation
at the backend, no action required in this case.
- The role option is missing from the "connect to server" dialog.
- The Password field should not be there on the "connect to server"
dialog. Sometimes we saved the password so asking a password every time is
not correct. Check the pgAdmin 3 behavior.
Code review still remains.
On Thu, Sep 17, 2020 at 4:15 PM Nikhil Mohite <
[email protected]> wrote:
> Hi Team,
>
> Regarding RM-3794 <https://redmine.postgresql.org/issues/3794; allow
> the user to change the database connection from an open query tool:
> I have implemented the feature and also added documentation for it.
>
> PFA patch.
>
> --
> *Thanks & Regards,*
> *Nikhil Mohite*
> *Software Engineer.*
> *EDB Postgres* <https://www.enterprisedb.com/;
> *Mob.No: +91-7798364578.*
>
--
*Thanks & Regards*
*Akshay Joshi*
*pgAdmin Hacker | Sr. Software Architect*
*EDB Postgres <http://edbpostgres.com>*
*Mobile: +91 976-788-8246*
^ permalink raw reply [nested|flat] 18+ messages in thread
* Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab
2020-09-17 10:45 [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-09-21 11:54 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
@ 2020-09-28 05:50 ` Nikhil Mohite <[email protected]>
2020-09-28 12:28 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
0 siblings, 1 reply; 18+ messages in thread
From: Nikhil Mohite @ 2020-09-28 05:50 UTC (permalink / raw)
To: Akshay Joshi <[email protected]>; +Cc: pgadmin-hackers
Hi Akshay,
I have resolved all the review comments and also updated the test cases as
per the new implementation.
PFA updated patch.
On Mon, Sep 21, 2020 at 5:24 PM Akshay Joshi <[email protected]>
wrote:
> Hi Nikhil
>
> Following are the initial review comments:
>
> - Open View/Edit data on any table and click on the same database
> connection and then click on the Execute button. Got "get_primary_keys()
> takes 1 positional argument but 2 were given" error.
> - In my opinion, we should hide the option to change the database
> connection for View/Edit Data.
> - If the user clicks on the same database connection multiple times
> then no need to change the backend connection and transaction id. Add
> validation at the backend, no action required in this case.
> - The role option is missing from the "connect to server" dialog.
> - The Password field should not be there on the "connect to server"
> dialog. Sometimes we saved the password so asking a password every time is
> not correct. Check the pgAdmin 3 behavior.
>
> Code review still remains.
>
> On Thu, Sep 17, 2020 at 4:15 PM Nikhil Mohite <
> [email protected]> wrote:
>
>> Hi Team,
>>
>> Regarding RM-3794 <https://redmine.postgresql.org/issues/3794; allow
>> the user to change the database connection from an open query tool:
>> I have implemented the feature and also added documentation for it.
>>
>> PFA patch.
>>
>> --
>> *Thanks & Regards,*
>> *Nikhil Mohite*
>> *Software Engineer.*
>> *EDB Postgres* <https://www.enterprisedb.com/;
>> *Mob.No: +91-7798364578.*
>>
>
>
> --
> *Thanks & Regards*
> *Akshay Joshi*
> *pgAdmin Hacker | Sr. Software Architect*
> *EDB Postgres <http://edbpostgres.com>*
>
> *Mobile: +91 976-788-8246*
>
Attachments:
[application/octet-stream] RM_3794_V2.patch (220.0K, 3-RM_3794_V2.patch)
download | inline diff:
diff --git a/docs/en_US/images/new_connection_dialog.png b/docs/en_US/images/new_connection_dialog.png
new file mode 100644
index 0000000000000000000000000000000000000000..8cf7ae6aa750360ae7710af61bf8e89f9d520284
GIT binary patch
literal 49691
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	%0^c>O%*OkVmcSQwgzI6WP2m3$pEbze=t&
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
NIVmN{VsYb-{|8bOF{}Up
literal 0
HcmV?d00001
diff --git a/docs/en_US/images/new_connection_options.png b/docs/en_US/images/new_connection_options.png
new file mode 100644
index 0000000000000000000000000000000000000000..7ca94374baf0eb9a8a0e49cbd9d4d426895ff9d0
GIT binary patch
literal 45739
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
literal 0
HcmV?d00001
diff --git a/docs/en_US/query_tool.rst b/docs/en_US/query_tool.rst
index 83933f0..80f27a5 100644
--- a/docs/en_US/query_tool.rst
+++ b/docs/en_US/query_tool.rst
@@ -300,3 +300,24 @@ transaction status by clicking on the status icon in the Query Tool:
.. image:: images/query_tool_connection_status.png
:alt: Query tool connection and transaction statuses
:align: center
+
+Change connection
+*****************
+
+User can connect to another server or database from existing open session of query tool.
+
+* Click on the connection link next to connection status.
+* Now click on the *<New Connection>* option from the dropdown.
+
+.. image:: images/new_connection_options.png
+ :alt: Query tool connection options
+ :align: center
+
+* Now select server, database, user, and role to connect and click OK.
+
+.. image:: images/new_connection_dialog.png
+ :alt: Query tool connection dialog
+ :align: center
+
+* A newly created connection will now get listed in the options.
+* To connect, select the newly created connection from the dropdown list.
diff --git a/web/pgadmin/browser/server_groups/servers/roles/tests/utils.py b/web/pgadmin/browser/server_groups/servers/roles/tests/utils.py
index 3a7ee58..028ee64 100644
--- a/web/pgadmin/browser/server_groups/servers/roles/tests/utils.py
+++ b/web/pgadmin/browser/server_groups/servers/roles/tests/utils.py
@@ -152,3 +152,38 @@ def delete_role(connection, role_names):
exception = "Error while deleting role: %s: line:%s %s" % (
file_name, sys.exc_traceback.tb_lineno, exception)
print(exception, file=sys.stderr)
+
+
+def create_role_with_password(server, role_name, role_password):
+ """
+ This function create the role.
+ :param server:
+ :param role_name:
+ :param role_password:
+ :return:
+ """
+ try:
+ connection = utils.get_db_connection(server['db'],
+ server['username'],
+ server['db_password'],
+ server['host'],
+ server['port'],
+ server['sslmode'])
+ pg_cursor = connection.cursor()
+ pg_cursor.execute(
+ "CREATE ROLE %s LOGIN PASSWORD '%s'" % (role_name, role_password))
+ connection.commit()
+ # Get 'oid' from newly created tablespace
+ pg_cursor.execute(
+ "SELECT pr.oid from pg_catalog.pg_roles pr WHERE pr.rolname='%s'" %
+ role_name)
+ oid = pg_cursor.fetchone()
+ role_id = ''
+ if oid:
+ role_id = oid[0]
+ connection.close()
+ return role_id
+ except Exception as exception:
+ exception = "Error while deleting role: %s: line:%s %s" % (
+ file_name, sys.exc_traceback.tb_lineno, exception)
+ print(exception, file=sys.stderr)
diff --git a/web/pgadmin/model/__init__.py b/web/pgadmin/model/__init__.py
index b33adc0..d24a350 100644
--- a/web/pgadmin/model/__init__.py
+++ b/web/pgadmin/model/__init__.py
@@ -94,6 +94,15 @@ class ServerGroup(db.Model):
name = db.Column(db.String(128), nullable=False)
__table_args__ = (db.UniqueConstraint('user_id', 'name'),)
+ @property
+ def serialize(self):
+ """Return object data in easily serializable format"""
+ return {
+ 'id': self.id,
+ 'user_id': self.user_id,
+ 'name': self.name,
+ }
+
class Server(db.Model):
"""Define a registered Postgres server"""
@@ -175,6 +184,44 @@ class Server(db.Model):
tunnel_password = db.Column(db.String(64), nullable=True)
shared = db.Column(db.Boolean(), nullable=False)
+ @property
+ def serialize(self):
+ """Return object data in easily serializable format"""
+ return {
+ "id": self.id,
+ "user_id": self.user_id,
+ "servergroup_id": self.servergroup_id,
+ "name": self.name,
+ "host": self.host,
+ "hostaddr": self.hostaddr,
+ "port": self.port,
+ "maintenance_db": self.maintenance_db,
+ "username": self.username,
+ "password": self.password,
+ "save_password": self.save_password,
+ "role": self.role,
+ "ssl_mode": self.ssl_mode,
+ "comment": self.comment,
+ "discovery_id": self.discovery_id,
+ "db_res": self.db_res,
+ "passfile": self.passfile,
+ "sslcert": self.sslcert,
+ "sslkey": self.sslkey,
+ "sslrootcert": self.sslrootcert,
+ "sslcrl": self.sslcrl,
+ "sslcompression": self.sslcompression,
+ "bgcolor": self.bgcolor,
+ "fgcolor": self.fgcolor,
+ "service": self.service,
+ "connect_timeout": self.connect_timeout,
+ "use_ssh_tunnel": self.use_ssh_tunnel,
+ "tunnel_host": self.tunnel_host,
+ "tunnel_port": self.tunnel_port,
+ "tunnel_authentication": self.tunnel_authentication,
+ "tunnel_identity_file": self.tunnel_identity_file,
+ "tunnel_password": self.tunnel_password
+ }
+
class ModulePreference(db.Model):
"""Define a preferences table for any modules."""
diff --git a/web/pgadmin/static/js/sqleditor/new_connection_dialog.js b/web/pgadmin/static/js/sqleditor/new_connection_dialog.js
new file mode 100644
index 0000000..dc1c064
--- /dev/null
+++ b/web/pgadmin/static/js/sqleditor/new_connection_dialog.js
@@ -0,0 +1,262 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2020, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import gettext from 'sources/gettext';
+import url_for from 'sources/url_for';
+import $ from 'jquery';
+import Alertify from 'pgadmin.alertifyjs';
+import pgAdmin from 'sources/pgadmin';
+import Backform from 'pgadmin.backform';
+import newConnectionDialogModel from 'sources/sqleditor/new_connection_dialog_model';
+
+
+let NewConnectionDialog = {
+ 'dialog': function(handler, reconnect) {
+ let url = url_for('sqleditor.get_new_connection_data', {
+ 'sid': handler.url_params.sid,
+ 'sgid': handler.url_params.sgid,
+ });
+
+ if(reconnect) {
+ url += '?connect=1';
+ }
+
+ let title = gettext('Connect to server');
+
+ $.ajax({
+ url: url,
+ headers: {
+ 'Cache-Control' : 'no-cache',
+ },
+ }).done(function (res) {
+ let response = res.data.result;
+ response.database_list = [];
+ response.user_list = [];
+ if (Alertify.newConnectionDialog) {
+ delete Alertify.newConnectionDialog;
+ }
+
+ // Create Dialog
+ Alertify.dialog('newConnectionDialog', function factory() {
+ let $container = $('<div class=\'new-connection-dialog\'></div>');
+ return {
+ main: function(message) {
+ this.msg = message;
+ },
+ build: function() {
+ this.elements.content.appendChild($container.get(0));
+ Alertify.pgDialogBuild.apply(this);
+ },
+ setup: function(){
+ return {
+ buttons: [
+ {
+ text: '',
+ key: 112,
+ className: 'btn btn-primary-icon pull-left fa fa-question pg-alertify-icon-button',
+ attrs: {
+ name: 'dialog_help',
+ type: 'button',
+ label: gettext('Help'),
+ 'aria-label': gettext('Help'),
+ url: url_for('help.static', {
+ 'filename': 'query_tool.html',
+ }),
+ },
+ },
+ {
+ text: gettext('Cancel'),
+ key: 27,
+ className: 'btn btn-secondary fa fa-times pg-alertify-button',
+ 'data-btn-name': 'cancel',
+ }, {
+ text: gettext('OK'),
+ key: 13,
+ className: 'btn btn-primary fa fa-check pg-alertify-button',
+ 'data-btn-name': 'ok',
+ },
+ ],
+ // Set options for dialog
+ options: {
+ title: title,
+ //disable both padding and overflow control.
+ padding: !1,
+ overflow: !1,
+ model: 0,
+ resizable: true,
+ maximizable: false,
+ pinnable: false,
+ closableByDimmer: false,
+ modal: false,
+ autoReset: false,
+ closable: true,
+ },
+ };
+ },
+ prepare: function() {
+ let self = this;
+ $container.html('');
+ // Disable Ok button
+ this.__internal.buttons[2].element.disabled = true;
+
+ // Status bar
+ this.statusBar = $(
+ '<div class=\'pg-prop-status-bar pg-el-xs-12 d-none\'>' +
+ ' <div class="error-in-footer"> ' +
+ ' <div class="d-flex px-2 py-1"> ' +
+ ' <div class="pr-2"> ' +
+ ' <i class="fa fa-exclamation-triangle text-danger" aria-hidden="true"></i> ' +
+ ' </div> ' +
+ ' <div class="alert-text" role="alert"></div> ' +
+ ' </div> ' +
+ ' </div> ' +
+ '</div>').appendTo($container);
+
+ // To show progress on filter Saving/Updating on AJAX
+ this.showNewConnectionProgress = $(
+ `<div id="show_filter_progress" class="pg-sp-container sql-editor-busy-fetching d-none">
+ <div class="pg-sp-content">
+ <div class="row"><div class="col-12 pg-sp-icon sql-editor-busy-icon"></div></div>
+ <div class="row"><div class="col-12 pg-sp-text sql-editor-busy-text">` + gettext('Loading data...') + `</div></div>
+ </div>
+ </div>`
+ ).appendTo($container);
+ $(
+ self.showNewConnectionProgress[0]
+ ).removeClass('d-none');
+
+ self.newConnCollectionModel = newConnectionDialogModel(response, handler.url_params.sgid, handler.url_params.sid);
+ let fields = Backform.generateViewSchema(null, self.newConnCollectionModel, 'create', null, null, true);
+
+ let view = this.view = new Backform.Dialog({
+ el: '<div></div>',
+ model: self.newConnCollectionModel,
+ schema: fields,
+ });
+
+ $(this.elements.body.childNodes[0]).addClass(
+ 'alertify_tools_dialog_properties obj_properties'
+ );
+
+ $container.append(view.render().$el);
+
+ // Enable/disable save button and show/hide statusbar based on session
+ view.listenTo(view.model, 'pgadmin-session:start', function() {
+ view.listenTo(view.model, 'pgadmin-session:invalid', function(msg) {
+ self.statusBar.removeClass('d-none');
+ $(self.statusBar.find('.alert-text')).html(msg);
+ // Disable Okay button
+ self.__internal.buttons[2].element.disabled = true;
+ });
+
+ view.listenTo(view.model, 'pgadmin-session:valid', function() {
+ self.statusBar.addClass('d-none');
+ $(self.statusBar.find('.alert-text')).html('');
+ // Enable Okay button
+ self.__internal.buttons[2].element.disabled = false;
+ });
+ });
+
+ view.listenTo(view.model, 'pgadmin-session:stop', function() {
+ view.stopListening(view.model, 'pgadmin-session:invalid');
+ view.stopListening(view.model, 'pgadmin-session:valid');
+ });
+
+ // Starts monitoring changes to model
+ view.model.startNewSession();
+
+ // Hide Progress ...
+ $(
+ self.showNewConnectionProgress[0]
+ ).addClass('d-none');
+ },
+ callback: function(e) {
+ let self = this;
+ if (e.button.element.name == 'dialog_help') {
+ e.cancel = true;
+ pgAdmin.Browser.showHelp(e.button.element.name, e.button.element.getAttribute('url'),
+ null, null);
+ return;
+ } else if (e.button['data-btn-name'] === 'ok') {
+ e.cancel = true; // Do not close dialog
+ let newConnCollectionModel = this.newConnCollectionModel.toJSON();
+
+ let selected_database_name = null;
+ response.database_list.forEach(function(data){
+ if(newConnCollectionModel['database'] == data['value']) {
+ selected_database_name = data['label'];
+ return false;
+ }
+ });
+ let title = '';
+ if(newConnCollectionModel['role']) {
+ title = selected_database_name + '/' + newConnCollectionModel['role'] + '@' + response.server_name;
+ } else {
+ title = selected_database_name + '/' + newConnCollectionModel['user'] + '@' + response.server_name;
+ newConnCollectionModel['role'] = null;
+ }
+
+ let is_create_connection = true;
+
+ handler.gridView.connection_list.forEach(function(connection_data){
+ if(parseInt(connection_data['server']) == newConnCollectionModel['server']
+ && parseInt(connection_data['database']) == newConnCollectionModel['database']
+ && connection_data['user'] == newConnCollectionModel['user'] && connection_data['role'] == newConnCollectionModel['role']) {
+ is_create_connection = false;
+ // break for loop by return false.
+ return false;
+ }
+
+ if(title == connection_data['title']) {
+ is_create_connection = false;
+ return false;
+ }
+ });
+ if(!is_create_connection) {
+ let errmsg = 'Connection with this configuration already present.';
+ Alertify.info(errmsg);
+ }else {
+ let connection_details = {
+ 'server_group': handler.gridView.handler.url_params.sgid,
+ 'server': newConnCollectionModel['server'],
+ 'database': newConnCollectionModel['database'],
+ 'title': title,
+ 'user': newConnCollectionModel['user'],
+ 'role': newConnCollectionModel['role'],
+ 'password': response.password,
+ };
+ handler.gridView.on_change_connection(connection_details, self);
+ }
+ } else {
+ self.close();
+ }
+ },
+ };
+ });
+ setTimeout(function(){
+ Alertify.newConnectionDialog('Connect to server.').resizeTo(pgAdmin.Browser.stdW.md,pgAdmin.Browser.stdH.md);
+ }, 500);
+ }).fail(function(error) {
+ Alertify.alert().setting({
+ 'title': gettext('Connection lost'),
+ 'label':gettext('Ok'),
+ 'message': gettext('Connection to the server has been lost.'),
+ 'onok': function(){
+ alert(error);
+ //Close the window after connection is lost
+ window.close();
+ },
+ }).show();
+ });
+
+ },
+
+};
+
+module.exports = NewConnectionDialog;
diff --git a/web/pgadmin/static/js/sqleditor/new_connection_dialog_model.js b/web/pgadmin/static/js/sqleditor/new_connection_dialog_model.js
new file mode 100644
index 0000000..1cba6e6
--- /dev/null
+++ b/web/pgadmin/static/js/sqleditor/new_connection_dialog_model.js
@@ -0,0 +1,339 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2020, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import gettext from 'sources/gettext';
+import _ from 'underscore';
+import $ from 'jquery';
+import pgAdmin from 'sources/pgadmin';
+import Backform from 'pgadmin.backform';
+import url_for from 'sources/url_for';
+import alertify from 'pgadmin.alertifyjs';
+
+export default function newConnectionDialogModel(response, sgid, sid) {
+
+ let server_name = '';
+ let database_name = '';
+
+ let NewConnectionSelect2Control = Backform.Select2Control.extend({
+ fetchData: function(){
+ let self = this;
+ url = self.field.get('url');
+
+ let url = url_for(url, {
+ 'sid': self.model.attributes.server,
+ 'sgid': sgid,
+ });
+
+ $.ajax({
+ async: false,
+ url: url,
+ headers: {
+ 'Cache-Control' : 'no-cache',
+ },
+ }).done(function (res) {
+ var transform = self.field.get('transform');
+ if(res.data.status){
+ let data = res.data.result.data;
+
+ if (transform && _.isFunction(transform)) {
+ self.field.set('options', transform.bind(self, data));
+ } else {
+ self.field.set('options', data);
+ }
+ } else {
+ if (transform && _.isFunction(transform)) {
+ self.field.set('options', transform.bind(self, []));
+ } else {
+ self.field.set('options', []);
+ }
+ //alertify.error(res.data.msg);
+ }
+ }).fail(function(e){
+ let msg = '';
+ if(e.status == 404) {
+ msg = 'Unable to find url.';
+ } else {
+ msg = e.responseJSON.errormsg;
+ }
+ alertify.error(msg);
+ });
+ },
+ render: function() {
+ this.fetchData();
+ return Backform.Select2Control.prototype.render.apply(this, arguments);
+ },
+ onChange: function() {
+ Backform.Select2Control.prototype.onChange.apply(this, arguments);
+ },
+ });
+
+ let newConnectionModel = pgAdmin.Browser.DataModel.extend({
+ idAttribute: 'name',
+ defaults: {
+ server: parseInt(sid),
+ database: null,
+ user: null,
+ password: null,
+ server_name: server_name,
+ database_name: database_name,
+ },
+ schema: [{
+ id: 'server',
+ name: 'server',
+ label: gettext('Server'),
+ type: 'text',
+ editable: true,
+ disabled: false,
+ select2: {
+ allowClear: false,
+ },
+ control: Backform.Select2Control.extend({
+ connect: function(self) {
+ /*if (alertify.connectServer) {
+ delete alertify.connectServer;
+ }*/
+ if(!alertify.connectServer){
+ alertify.dialog('connectServer', function factory() {
+ return {
+ main: function(
+ title, message, sid, submit_password=true
+ ) {
+ this.set('title', title);
+ this.message = message;
+ this.server_id = sid;
+ this.submit_password = submit_password;
+ },
+ setup:function() {
+ return {
+ buttons:[{
+ text: gettext('Cancel'), className: 'btn btn-secondary fa fa-times pg-alertify-button',
+ key: 27,
+ },{
+ text: gettext('OK'), key: 13, className: 'btn btn-primary fa fa-check pg-alertify-button',
+ }],
+ focus: {element: '#password', select: true},
+ options: {
+ modal: 0, resizable: false, maximizable: false, pinnable: false,
+ },
+ };
+ },
+ build:function() {
+ },
+ prepare:function() {
+ this.setContent(this.message);
+ },
+ callback: function(closeEvent) {
+
+ if (closeEvent.button.text == gettext('OK')) {
+ if(this.submit_password) {
+ var _url = url_for('schema_diff.connect_server', {'sid': this.server_id});
+
+ $.ajax({
+ type: 'POST',
+ timeout: 30000,
+ url: _url,
+ data: $('#frmPassword').serialize(),
+ })
+ .done(function() {
+ self.model.attributes.database = null;
+ self.model.attributes.user = null;
+ self.model.attributes.role = null;
+ Backform.Select2Control.prototype.onChange.apply(self, arguments);
+ response.server_list.forEach(function(obj){
+ if(obj.id==self.model.changed.server) {
+ response.server_name = obj.name;
+ }
+ });
+ })
+ .fail(function(xhr) {
+ alertify.connectServer('Connect to server', xhr.responseJSON.result, self.getValueFromDOM());
+ });
+ } else {
+ response.password = $('#password').val();
+ }
+ } else {
+ self.model.attributes.database = null;
+ self.model.attributes.user = null;
+ self.model.attributes.role = null;
+ Backform.Select2Control.prototype.onChange.apply(self, arguments);
+ }
+ closeEvent.close = true;
+ },
+ };
+ });
+ }
+ },
+ render: function() {
+ let self = this;
+ self.connect(self);
+ return Backform.Select2Control.prototype.render.apply(self, arguments);
+ },
+ onChange: function() {
+ this.model.attributes.database = null;
+ this.model.attributes.user = null;
+ let self = this;
+ self.connect(self);
+
+ let url = url_for('sqleditor.connect_server', {
+ 'sid': self.getValueFromDOM(),
+ });
+ $.ajax({
+ async: false,
+ url: url,
+ headers: {
+ 'Cache-Control' : 'no-cache',
+ },
+ }).done(function () {
+ Backform.Select2Control.prototype.onChange.apply(self, arguments);
+ response.server_list.forEach(function(obj){
+ if(obj.id==self.model.changed.server) {
+ response.server_name = obj.name;
+ }
+ });
+ }).fail(function(xhr){
+ alertify.connectServer('Connect to server', xhr.responseJSON.result, self.getValueFromDOM());
+ });
+
+ },
+ }),
+ options: function() {
+ return _.map(response.server_list, (obj) => {
+ if (obj.id == parseInt(sid))
+ response.server_name = obj.name;
+
+ return {
+ value: obj.id,
+ label: obj.name,
+ };
+ });
+ },
+ },
+ {
+ id: 'database',
+ name: 'database',
+ label: gettext('Database'),
+ type: 'text',
+ editable: true,
+ disabled: function(m) {
+ let self_local = this;
+ if (!_.isUndefined(m.get('server')) && !_.isNull(m.get('server'))
+ && m.get('server') !== '') {
+ setTimeout(function() {
+ if(self_local.options.length) {
+ m.set('database', self_local.options[0].value);
+ }
+ }, 10);
+ return false;
+ }
+
+ return true;
+ },
+ deps: ['server'],
+ url: 'sqleditor.get_new_connection_database',
+ select2: {
+ allowClear: false,
+ width: '100%',
+ first_empty: true,
+ select_first: false,
+ },
+ extraClasses:['new-connection-dialog-style'],
+ control: NewConnectionSelect2Control,
+ transform: function(data) {
+ response.database_list = data;
+ return data;
+ },
+ },
+ {
+ id: 'user',
+ name: 'user',
+ label: gettext('User'),
+ type: 'text',
+ editable: true,
+ deps: ['server'],
+ select2: {
+ allowClear: false,
+ width: '100%',
+ },
+ control: NewConnectionSelect2Control,
+ url: 'sqleditor.get_new_connection_user',
+ disabled: function(m) {
+ let self_local = this;
+ if (!_.isUndefined(m.get('server')) && !_.isNull(m.get('server'))
+ && m.get('server') !== '') {
+ setTimeout(function() {
+ if(self_local.options.length) {
+ m.set('user', self_local.options[0].value);
+ }
+ }, 10);
+ return false;
+ }
+ return true;
+ },
+ },{
+ id: 'role',
+ name: 'role',
+ label: gettext('Role'),
+ type: 'text',
+ editable: true,
+ deps: ['server'],
+ select2: {
+ allowClear: false,
+ width: '100%',
+ first_empty: true,
+ },
+ control: NewConnectionSelect2Control,
+ url: 'sqleditor.get_new_connection_role',
+ disabled: false,
+ },
+ /*{
+ id: 'password',
+ name: 'password',
+ label: gettext('Password'tools/sqleditor/__init__.py),
+ type: 'password',
+ editable: true,
+ disabled: true,
+ deps: ['user'],
+ control: Backform.InputControl.extend({
+ render: function() {
+ let self = this;
+ self.model.attributes.password = null;
+ Backform.InputControl.prototype.render.apply(self, arguments);
+ return self;
+ },
+ onChange: function() {
+ let self = this;
+ Backform.InputControl.prototype.onChange.apply(self, arguments);
+ },
+ }),
+ },*/
+ ],
+ validate: function() {
+ let msg = null;
+ this.errorModel.clear();
+ if(_.isUndefined(this.get('database')) || _.isNull(this.get('database'))){
+ msg = gettext('Please select database');
+ this.errorModel.set('database', msg);
+ return msg;
+ } else if(_.isUndefined(this.get('database')) || _.isUndefined(this.get('user'))|| _.isNull(this.get('user'))) {
+ msg = gettext('Please select user');
+ this.errorModel.set('user', msg);
+ return msg;
+ }
+ /*else if((this.attributes.password == '' || _.isUndefined(this.get('password')) || _.isNull(this.get('password')))) {
+ msg = gettext('Please enter password');
+ this.errorModel.set('password', msg);
+ return msg;
+ }*/
+ return null;
+ },
+ });
+
+ let model = new newConnectionModel();
+ return model;
+}
diff --git a/web/pgadmin/static/scss/_alert.scss b/web/pgadmin/static/scss/_alert.scss
index dac552b..836f0af 100644
--- a/web/pgadmin/static/scss/_alert.scss
+++ b/web/pgadmin/static/scss/_alert.scss
@@ -92,6 +92,7 @@
right: 0;
left: 0;
bottom: 0;
+ z-index: 1;
}
.pg-prop-status-bar {
diff --git a/web/pgadmin/tools/datagrid/__init__.py b/web/pgadmin/tools/datagrid/__init__.py
index f5fc78c..07459be 100644
--- a/web/pgadmin/tools/datagrid/__init__.py
+++ b/web/pgadmin/tools/datagrid/__init__.py
@@ -18,21 +18,22 @@ from flask import Response, url_for, session, request, make_response
from werkzeug.useragents import UserAgent
from flask import current_app as app, render_template
from flask_babelex import gettext
-from flask_security import login_required
+from flask_security import login_required, current_user
from pgadmin.tools.sqleditor.command import ObjectRegistry, SQLFilter
+from pgadmin.tools.sqleditor import check_transaction_status
from pgadmin.utils import PgAdminModule
from pgadmin.utils.ajax import make_json_response, bad_request, \
- internal_server_error
+ internal_server_error, unauthorized
from config import PG_DEFAULT_DRIVER
-from pgadmin.model import Server
+from pgadmin.model import Server, User
from pgadmin.utils.driver import get_driver
from pgadmin.utils.exception import ConnectionLost, SSHTunnelConnectionLost
from pgadmin.utils.preferences import Preferences
from pgadmin.settings import get_setting
from pgadmin.browser.utils import underscore_unescape
from pgadmin.utils.exception import ObjectGone
-from pgadmin.utils.constants import MIMETYPE_APP_JS
+from pgadmin.utils.constants import MIMETYPE_APP_JS, UNAUTH_REQ
MODULE_NAME = 'datagrid'
@@ -73,7 +74,8 @@ class DataGridModule(PgAdminModule):
'datagrid.filter_validate',
'datagrid.filter',
'datagrid.panel',
- 'datagrid.close'
+ 'datagrid.close',
+ 'datagrid.update_query_tool_connection'
]
def on_logout(self, user):
@@ -320,10 +322,23 @@ def initialize_query_tool(trans_id, sgid, sid, did=None):
req_args['recreate'] == '1'):
connect = False
+ is_error, errmsg, conn_id, version = _init_query_tool(trans_id, connect,
+ sgid, sid, did)
+ if is_error:
+ return errmsg
+
+ return make_json_response(
+ data={
+ 'connId': str(conn_id),
+ 'serverVersion': version,
+ }
+ )
+
+
+def _init_query_tool(trans_id, connect, sgid, sid, did, **kwargs):
# Create asynchronous connection using random connection id.
conn_id = str(random.randint(1, 9999999))
- # Use Maintenance database OID
manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)
if did is None:
@@ -334,24 +349,53 @@ def initialize_query_tool(trans_id, sgid, sid, did=None):
)
except Exception as e:
app.logger.error(e)
- return internal_server_error(errormsg=str(e))
+ return True, internal_server_error(errormsg=str(e)), '', ''
try:
conn = manager.connection(did=did, conn_id=conn_id,
auto_reconnect=False,
use_binary_placeholder=True,
array_to_string=True)
+ is_ask_password = False
if connect:
- status, msg = conn.connect()
+ user = None
+ role = None
+ password = None
+
+ if 'user' in kwargs and 'role' in kwargs:
+ user = kwargs['user']
+ role = kwargs['role'] if kwargs['role'] else None
+ password = kwargs['password'] if kwargs['password'] else None
+ is_ask_password = True
+ if user:
+ status, msg = conn.connect(user=user, role=role,
+ password=password)
+ else:
+ status, msg = conn.connect()
if not status:
app.logger.error(msg)
- return internal_server_error(errormsg=str(msg))
+ if is_ask_password:
+ server = Server.query.filter_by(id=sid).first()
+ return True, make_json_response(
+ success=0,
+ status=428,
+ result=render_template(
+ 'servers/password.html',
+ server_label=server.name,
+ username=user,
+ errmsg=msg,
+ _=gettext,
+ )
+ ), '', ''
+ else:
+ return True, internal_server_error(
+ errormsg=str(msg)), '', ''
except (ConnectionLost, SSHTunnelConnectionLost) as e:
app.logger.error(e)
raise
except Exception as e:
app.logger.error(e)
- return internal_server_error(errormsg=str(e))
+ return True, internal_server_error(errormsg=str(e)), '', ''
if 'gridData' not in session:
sql_grid_data = dict()
@@ -373,10 +417,80 @@ def initialize_query_tool(trans_id, sgid, sid, did=None):
# Store the grid dictionary into the session variable
session['gridData'] = sql_grid_data
+ return False, '', conn_id, manager.version
+
+
[email protected](
+ '/initialize/query_tool/update_connection/<int:trans_id>/'
+ '<int:sgid>/<int:sid>/<int:did>',
+ methods=["POST"], endpoint='update_query_tool_connection'
+)
+def update_query_tool_connection(trans_id, sgid, sid, did):
+ # Remove transaction Id.
+ with query_tool_close_session_lock:
+ data = dict()
+ if request.data:
+ data = json.loads(request.data, encoding='utf-8')
+
+ if 'gridData' not in session:
+ return make_json_response(data={'status': True})
+
+ grid_data = session['gridData']
+
+ # Return from the function if transaction id not found
+ if str(trans_id) not in grid_data:
+ return make_json_response(data={'status': True})
+
+ connect = True
+
+ req_args = request.args
+ if ('recreate' in req_args and
+ req_args['recreate'] == '1'):
+ connect = False
+
+ new_trans_id = str(random.randint(1, 9999999))
+ kwargs = {
+ 'user': data['user'],
+ 'role': data['role'],
+ 'password': data['password'] if 'password' in data else None
+ }
+
+ is_error, errmsg, conn_id, version = _init_query_tool(
+ new_trans_id, connect, sgid, sid, did, **kwargs)
+
+ if is_error:
+ return errmsg
+ else:
+ try:
+ # Check the transaction and connection status
+ status, error_msg, conn, trans_obj, session_obj = \
+ check_transaction_status(trans_id)
+
+ status, error_msg, new_conn, new_trans_obj, new_session_obj = \
+ check_transaction_status(new_trans_id)
+
+ new_session_obj['primary_keys'] = session_obj[
+ 'primary_keys'] if 'primary_keys' in session_obj else None
+ new_session_obj['columns_info'] = session_obj[
+ 'columns_info'] if 'columns_info' in session_obj else None
+ new_session_obj['client_primary_key'] = session_obj[
+ 'client_primary_key'] if 'client_primary_key'\
+ in session_obj else None
+
+ close_query_tool_session(trans_id)
+ # Remove the information of unique transaction id from the
+ # session variable.
+ grid_data.pop(str(trans_id), None)
+ session['gridData'] = grid_data
+ except Exception as e:
+ app.logger.error(e)
+ # return internal_server_error(errormsg=str(e))
+
return make_json_response(
data={
'connId': str(conn_id),
- 'serverVersion': manager.version,
+ 'serverVersion': version,
+ 'tran_id': new_trans_id
}
)
diff --git a/web/pgadmin/tools/datagrid/templates/datagrid/index.html b/web/pgadmin/tools/datagrid/templates/datagrid/index.html
index 097a42e..16a209f 100644
--- a/web/pgadmin/tools/datagrid/templates/datagrid/index.html
+++ b/web/pgadmin/tools/datagrid/templates/datagrid/index.html
@@ -392,8 +392,17 @@
title="" role="img">
</i>
</div>
- <div class="editor-title"
- style="background-color: {% if fgcolor %}{{ bgcolor or '#FFFFFF' }}{% endif %}; color: {% if fgcolor %}{{ fgcolor }}{% endif %};"> </div>
+ <div class="connection-info btn-group mr-1" role="group" aria-label="">
+ <div class="editor-title" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"
+ style="background-color: {% if fgcolor %}{{ bgcolor or '#FFFFFF' }}{% endif %}; color: {% if fgcolor %}{{ fgcolor }}{% endif %};">
+ </div>
+ <span class="conn-info-dd dropdown-toggle dropdown-toggle-split"
+ data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></span>
+ <ul class="dropdown-menu" id="connections-list">
+ </ul>
+ </div>
+
+
</div>
<div id="editor-panel" tabindex="0">
<div id="fetching_data" class="pg-sp-container sql-editor-busy-fetching">
@@ -456,6 +465,7 @@ require(['sources/generated/browser_nodes', 'sources/generated/codemirror', 'sou
var script_type_url = '';
{% endif %}
// Start the query tool.
+
sqlEditorController.start(
{{ uniqueId }},
{{ url_params|safe}},
diff --git a/web/pgadmin/tools/datagrid/tests/__init__.py b/web/pgadmin/tools/datagrid/tests/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/web/pgadmin/tools/datagrid/tests/datagrid_test_data.json b/web/pgadmin/tools/datagrid/tests/datagrid_test_data.json
new file mode 100644
index 0000000..0075f35
--- /dev/null
+++ b/web/pgadmin/tools/datagrid/tests/datagrid_test_data.json
@@ -0,0 +1,134 @@
+{
+ "data_grid_init_query_tool": [
+ {
+ "name": "Datagrid init query tool",
+ "url": "/datagrid/initialize/query_tool/",
+ "is_positive_test": true,
+ "mocking_required": false,
+ "test_data": {},
+ "mock_data": {},
+ "expected_data": {
+ "status_code": 200
+ }
+ }
+ ],
+ "data_grid_query_tool_close": [
+ {
+ "name": "Datagrid query tool close",
+ "url": "/datagrid/close/",
+ "is_positive_test": true,
+ "mocking_required": false,
+ "test_data": {},
+ "mock_data": {},
+ "expected_data": {
+ "status_code": 200
+ }
+ }
+ ],
+ "data_grid_validate_filter": [
+ {
+ "name": "Datagrid validate filter",
+ "url": "/datagrid/filter/validate/",
+ "is_positive_test": true,
+ "mocking_required": false,
+ "test_data": "id = 1",
+ "mock_data": {},
+ "expected_data": {
+ "status_code": 200
+ }
+ },
+ {
+ "name": "Datagrid validate filter",
+ "url": "/datagrid/filter/validate/",
+ "is_positive_test": false,
+ "mocking_required": true,
+ "test_data": "id = 1",
+ "mock_data": {
+ "function_name": "pgadmin.utils.driver.psycopg2.connection.Connection.execute_scalar",
+ "return_value": "(False, 'Mocked Internal Server Error while validate filter')"
+ },
+ "expected_data": {
+ "status_code": 200
+ }
+ }
+ ],
+ "data_grid_update_connection": [
+ {
+ "name": "Datagrid update connection positive",
+ "url": "/datagrid/initialize/query_tool/update_connection/",
+ "is_positive_test": true,
+ "mocking_required": false,
+ "is_create_role": false,
+ "test_data": {},
+ "mock_data": {},
+ "expected_data": {
+ "status_code": 200
+ }
+ },
+ {
+ "name": "Datagrid update connection with new user",
+ "url": "/datagrid/initialize/query_tool/update_connection/",
+ "is_positive_test": true,
+ "mocking_required": false,
+ "is_create_role": true,
+ "test_data": {},
+ "mock_data": {},
+ "expected_data": {
+ "status_code": 200
+ }
+ }
+ ],
+ "data_grid_panel": [
+ {
+ "name": "Datagrid Panel",
+ "url": "/datagrid/panel/",
+ "is_positive_test": true,
+ "mocking_required": false,
+ "test_data": {},
+ "mock_data": {
+ },
+ "expected_data": {
+ "status_code": 200
+ }
+ }
+ ],
+ "data_grid_initialize": [
+ {
+ "name": "Datagrid Initialize",
+ "url": "/datagrid/initialize/datagrid/",
+ "is_positive_test": true,
+ "mocking_required": false,
+ "test_data": "id=1",
+ "mock_data": {
+
+ },
+ "expected_data": {
+ "status_code": 200
+ }
+ },{
+ "name": "Datagrid Initialize",
+ "url": "/datagrid/initialize/datagrid/",
+ "is_positive_test": true,
+ "mocking_required": false,
+ "test_data": null,
+ "mock_data": {},
+ "expected_data": {
+ "status_code": 200
+ }
+ },
+ {
+ "name": "Datagrid Initialize",
+ "url": "/datagrid/initialize/datagrid/",
+ "is_positive_test": false,
+ "mocking_required": true,
+ "test_data": "id=1",
+ "mock_data": {
+ "function_name": "pgadmin.utils.driver.psycopg2.connection.Connection.execute_dict",
+ "return_value": "(False, 'Mocked Internal Server Error while initialize datagrid.')"
+ },
+ "expected_data": {
+ "status_code": 500
+ }
+ }
+ ]
+}
diff --git a/web/pgadmin/tools/datagrid/tests/test_data_grid_init_query_tool.py b/web/pgadmin/tools/datagrid/tests/test_data_grid_init_query_tool.py
new file mode 100644
index 0000000..6ecf5de
--- /dev/null
+++ b/web/pgadmin/tools/datagrid/tests/test_data_grid_init_query_tool.py
@@ -0,0 +1,72 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+
+import json
+import uuid
+import random
+
+from unittest.mock import patch
+from pgadmin.browser.server_groups.servers.databases.tests import utils as \
+ database_utils
+
+from pgadmin.utils.route import BaseTestGenerator
+from regression import parent_node_dict
+from regression.python_test_utils import test_utils as utils
+from regression.test_setup import config_data
+from . import utils as data_grid_utils
+
+
+class DatagridInitQueryToolTestCase(BaseTestGenerator):
+ """
+ This will init query-tool connection.
+ """
+
+ scenarios = utils.generate_scenarios(
+ 'data_grid_init_query_tool',
+ data_grid_utils.test_cases
+ )
+
+ def setUp(self):
+ self.database_info = parent_node_dict["database"][-1]
+ self.db_name = self.database_info["db_name"]
+ self.did = self.database_info["db_id"]
+ self.sid = parent_node_dict["server"][-1]["server_id"]
+ self.sgid = config_data['server_group']
+
+ db_con = database_utils.connect_database(self, utils.SERVER_GROUP,
+ self.sid, self.did)
+
+ self.trans_id = str(random.randint(1, 9999999))
+
+ if not db_con['data']["connected"]:
+ raise Exception("Could not connect to database to add a table.")
+
+ def init_query_tool(self):
+ response = self.tester.post(
+ self.url + str(self.trans_id) + '/' + str(self.sgid) + '/' + str(
+ self.sid) + '/' + str(self.did),
+ content_type='html/json'
+ )
+ return response
+
+ def runTest(self):
+ """ This function will init query tool connection."""
+
+ if self.is_positive_test:
+ response = self.init_query_tool()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+
+ self.assertEqual(actual_response_code, expected_response_code)
+
+ def tearDown(self):
+ """This function disconnect database."""
+ database_utils.disconnect_database(self, self.sid,
+ self.did)
diff --git a/web/pgadmin/tools/datagrid/tests/test_data_grid_panel.py b/web/pgadmin/tools/datagrid/tests/test_data_grid_panel.py
new file mode 100644
index 0000000..ae8ec10
--- /dev/null
+++ b/web/pgadmin/tools/datagrid/tests/test_data_grid_panel.py
@@ -0,0 +1,93 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+
+import json
+import uuid
+import random
+
+from unittest.mock import patch
+from pgadmin.browser.server_groups.servers.databases.tests import utils as \
+ database_utils
+
+from pgadmin.utils.route import BaseTestGenerator
+from regression import parent_node_dict
+from regression.python_test_utils import test_utils as utils
+from regression.test_setup import config_data
+from pgadmin.browser.server_groups.servers.databases.schemas.tests import \
+ utils as schema_utils
+from pgadmin.browser.server_groups.servers.databases.schemas.tables.tests \
+ import utils as tables_utils
+from . import utils as data_grid_utils
+
+
+class DatagridPanelTestCase(BaseTestGenerator):
+ """
+ This will data grid panel.
+ """
+
+ scenarios = utils.generate_scenarios(
+ 'data_grid_panel',
+ data_grid_utils.test_cases
+ )
+
+ def setUp(self):
+ self.database_info = parent_node_dict["database"][-1]
+ self.db_name = self.database_info["db_name"]
+ self.did = self.database_info["db_id"]
+ self.sid = parent_node_dict["server"][-1]["server_id"]
+ self.sgid = config_data['server_group']
+
+ db_con = database_utils.connect_database(self, utils.SERVER_GROUP,
+ self.sid, self.did)
+ if not db_con['data']["connected"]:
+ raise Exception("Could not connect to database to add a table.")
+
+ self.trans_id = str(random.randint(1, 9999999))
+ qt_init = data_grid_utils._init_query_tool(self, self.trans_id,
+ self.sgid, self.sid,
+ self.did)
+
+ if not qt_init['success']:
+ raise Exception("Could not initialize querty tool.")
+
+ def panel(self):
+ query_param = \
+ '?is_query_tool={0}&sgid={1}&sid={2}&server_type={3}' \
+ '&did={4}&title={5}'.format(True, self.sgid, self.sid,
+ self.server_information['type'],
+ self.did, 'Query panel')
+
+ response = self.tester.post(
+ self.url + str(self.trans_id) + query_param,
+ data=json.dumps(self.test_data),
+ content_type='html/json'
+ )
+ return response
+
+ def runTest(self):
+ """ This function will update query tool connection."""
+
+ if self.is_positive_test:
+ response = self.panel()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+ else:
+ with patch(self.mock_data["function_name"],
+ return_value=eval(self.mock_data["return_value"])):
+ response = self.panel()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+
+ self.assertEqual(actual_response_code, expected_response_code)
+
+ def tearDown(self):
+ """This function disconnect database."""
+ database_utils.disconnect_database(self, self.sid,
+ self.did)
diff --git a/web/pgadmin/tools/datagrid/tests/test_data_grid_query_tool_close.py b/web/pgadmin/tools/datagrid/tests/test_data_grid_query_tool_close.py
new file mode 100644
index 0000000..822c2e1
--- /dev/null
+++ b/web/pgadmin/tools/datagrid/tests/test_data_grid_query_tool_close.py
@@ -0,0 +1,77 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+
+import json
+import uuid
+import random
+
+from unittest.mock import patch
+from pgadmin.browser.server_groups.servers.databases.tests import utils as \
+ database_utils
+
+from pgadmin.utils.route import BaseTestGenerator
+from regression import parent_node_dict
+from regression.python_test_utils import test_utils as utils
+from regression.test_setup import config_data
+from . import utils as data_grid_utils
+
+
+class DatagridQueryToolCloseTestCase(BaseTestGenerator):
+ """
+ This will close query-tool connection.
+ """
+
+ scenarios = utils.generate_scenarios(
+ 'data_grid_query_tool_close',
+ data_grid_utils.test_cases
+ )
+
+ def setUp(self):
+ self.database_info = parent_node_dict["database"][-1]
+ self.db_name = self.database_info["db_name"]
+ self.did = self.database_info["db_id"]
+ self.sid = parent_node_dict["server"][-1]["server_id"]
+ self.sgid = config_data['server_group']
+
+ db_con = database_utils.connect_database(self, utils.SERVER_GROUP,
+ self.sid, self.did)
+
+ if not db_con['data']["connected"]:
+ raise Exception("Could not connect to database to add a table.")
+
+ self.trans_id = str(random.randint(1, 9999999))
+ qt_init = data_grid_utils._init_query_tool(self, self.trans_id,
+ self.sgid, self.sid,
+ self.did)
+
+ if not qt_init['success']:
+ raise Exception("Could not initialize querty tool.")
+
+ def close_connection(self):
+ response = self.tester.delete(
+ self.url + str(self.trans_id),
+ content_type='html/json'
+ )
+ return response
+
+ def runTest(self):
+ """ This function will update query tool connection."""
+
+ if self.is_positive_test:
+ response = self.close_connection()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+
+ self.assertEqual(actual_response_code, expected_response_code)
+
+ def tearDown(self):
+ """This function disconnect database."""
+ database_utils.disconnect_database(self, self.sid,
+ self.did)
diff --git a/web/pgadmin/tools/datagrid/tests/test_data_grid_update_connection.py b/web/pgadmin/tools/datagrid/tests/test_data_grid_update_connection.py
new file mode 100644
index 0000000..5d2b14a
--- /dev/null
+++ b/web/pgadmin/tools/datagrid/tests/test_data_grid_update_connection.py
@@ -0,0 +1,120 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+
+import json
+import uuid
+import random
+
+from unittest.mock import patch
+from pgadmin.browser.server_groups.servers.databases.tests import utils as \
+ database_utils
+
+from pgadmin.utils.route import BaseTestGenerator
+from regression import parent_node_dict
+from regression.python_test_utils import test_utils as utils
+from regression.test_setup import config_data
+from pgadmin.browser.server_groups.servers.roles.tests import \
+ utils as roles_utils
+from . import utils as data_grid_utils
+
+
+class DatagridUpdateConnectionTestCase(BaseTestGenerator):
+ """
+ This will update query-tool connection.
+ """
+
+ scenarios = utils.generate_scenarios(
+ 'data_grid_update_connection',
+ data_grid_utils.test_cases
+ )
+
+ def setUp(self):
+ self.database_info = parent_node_dict["database"][-1]
+ self.db_name = self.database_info["db_name"]
+ self.did = self.database_info["db_id"]
+ self.sid = parent_node_dict["server"][-1]["server_id"]
+ self.sgid = config_data['server_group']
+
+ db_con = database_utils.connect_database(self, utils.SERVER_GROUP,
+ self.sid, self.did)
+
+ self.trans_id = str(random.randint(1, 9999999))
+ self.roles = None
+
+ if self.is_create_role:
+ data = roles_utils.get_role_data(self.server['db_password'])
+ self.role_name = data['rolname']
+ self.role_password = data['rolpassword']
+ roles_utils.create_role_with_password(
+ self.server, self.role_name, self.role_password)
+
+ if not self.is_positive_test or self.is_create_role:
+ qt_init = data_grid_utils._init_query_tool(self, self.trans_id,
+ self.sgid, self.sid,
+ self.did)
+
+ if not qt_init['success']:
+ raise Exception("Could not initialize querty tool.")
+
+ self.test_data = {
+ "database": self.did,
+ "server": self.sid,
+ }
+
+ if self.server_information['type'] == 'ppas':
+ self.test_data['password'] = 'enterprisedb'
+ self.test_data['user'] = 'enterprisedb'
+ else:
+ self.test_data['password'] = 'postgres'
+ self.test_data['user'] = 'postgres'
+
+ if not db_con['data']["connected"]:
+ raise Exception("Could not connect to database to add a table.")
+
+ def update_connection(self, user_data=None):
+ if user_data:
+ response = self.tester.post(
+ self.url + str(self.trans_id) + '/' + str(self.sgid) +
+ '/' + str(self.sid) + '/' + str(self.did),
+ data=json.dumps(user_data),
+ content_type='html/json'
+ )
+ else:
+ response = self.tester.post(
+ self.url + str(self.trans_id) + '/' + str(self.sgid) + '/' +
+ str(self.sid) + '/' + str(self.did),
+ data=json.dumps(self.test_data),
+ content_type='html/json'
+ )
+ return response
+
+ def runTest(self):
+ """ This function will update query tool connection."""
+
+ if self.is_positive_test:
+ user_data = dict()
+ if self.is_create_role:
+ user_data['user'] = self.role_name
+ user_data['password'] = self.role_password
+ user_data['role'] = None
+ response = self.update_connection(user_data=user_data)
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+ else:
+ response = self.update_connection()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+
+ self.assertEqual(actual_response_code, expected_response_code)
+
+ def tearDown(self):
+ """This function disconnect database."""
+ database_utils.disconnect_database(self, self.sid,
+ self.did)
diff --git a/web/pgadmin/tools/datagrid/tests/test_data_grid_validate_filter.py b/web/pgadmin/tools/datagrid/tests/test_data_grid_validate_filter.py
new file mode 100644
index 0000000..0aba5d8
--- /dev/null
+++ b/web/pgadmin/tools/datagrid/tests/test_data_grid_validate_filter.py
@@ -0,0 +1,91 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+
+import json
+import uuid
+import random
+
+from unittest.mock import patch
+from pgadmin.browser.server_groups.servers.databases.tests import utils as \
+ database_utils
+
+from pgadmin.utils.route import BaseTestGenerator
+from regression import parent_node_dict
+from regression.python_test_utils import test_utils as utils
+from regression.test_setup import config_data
+from pgadmin.browser.server_groups.servers.databases.schemas.tests import \
+ utils as schema_utils
+from pgadmin.browser.server_groups.servers.databases.schemas.tables.tests \
+ import utils as tables_utils
+from . import utils as data_grid_utils
+
+
+class DatagridValidateFilterTestCase(BaseTestGenerator):
+ """
+ This will validate filter connection.
+ """
+
+ scenarios = utils.generate_scenarios(
+ 'data_grid_validate_filter',
+ data_grid_utils.test_cases
+ )
+
+ def setUp(self):
+ self.database_info = parent_node_dict["database"][-1]
+ self.db_name = self.database_info["db_name"]
+ self.did = self.database_info["db_id"]
+ self.sid = parent_node_dict["server"][-1]["server_id"]
+ self.sgid = config_data['server_group']
+
+ db_con = database_utils.connect_database(self, utils.SERVER_GROUP,
+ self.sid, self.did)
+ if not db_con['data']["connected"]:
+ raise Exception("Could not connect to database to add a table.")
+ self.schema_id = parent_node_dict['schema'][-1]["schema_id"]
+ self.schema_name = parent_node_dict['schema'][-1]["schema_name"]
+ schema_response = schema_utils.verify_schemas(self.server,
+ self.db_name,
+ self.schema_name)
+ if not schema_response:
+ raise Exception("Could not find the schema to add a table.")
+ self.table_name = "table_for_wizard%s" % (str(uuid.uuid4())[1:8])
+ self.table_id = tables_utils.create_table(self.server, self.db_name,
+ self.schema_name,
+ self.table_name)
+
+ def validate_filter(self):
+ response = self.tester.post(
+ self.url + str(self.sid) + '/' + str(self.did) + '/' +
+ str(self.table_id),
+ data=json.dumps(self.test_data),
+ content_type='html/json'
+ )
+ return response
+
+ def runTest(self):
+ """ This function will update query tool connection."""
+
+ if self.is_positive_test:
+ response = self.validate_filter()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+ else:
+ with patch(self.mock_data["function_name"],
+ return_value=eval(self.mock_data["return_value"])):
+ response = self.validate_filter()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+
+ self.assertEqual(actual_response_code, expected_response_code)
+
+ def tearDown(self):
+ """This function disconnect database."""
+ database_utils.disconnect_database(self, self.sid,
+ self.did)
diff --git a/web/pgadmin/tools/datagrid/tests/test_initialize_data_grid.py b/web/pgadmin/tools/datagrid/tests/test_initialize_data_grid.py
new file mode 100644
index 0000000..130a1d6
--- /dev/null
+++ b/web/pgadmin/tools/datagrid/tests/test_initialize_data_grid.py
@@ -0,0 +1,108 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+
+import json
+import uuid
+import random
+
+from unittest.mock import patch
+from pgadmin.browser.server_groups.servers.databases.tests import utils as \
+ database_utils
+
+from pgadmin.utils.route import BaseTestGenerator
+from regression import parent_node_dict
+from regression.python_test_utils import test_utils as utils
+from regression.test_setup import config_data
+from pgadmin.browser.server_groups.servers.databases.schemas.tests import \
+ utils as schema_utils
+from pgadmin.browser.server_groups.servers.databases.schemas.tables.tests \
+ import utils as tables_utils
+from . import utils as data_grid_utils
+
+
+class DatagridInitializeTestCase(BaseTestGenerator):
+ """
+ This will Initialize datagrid
+ """
+
+ scenarios = utils.generate_scenarios(
+ 'data_grid_initialize',
+ data_grid_utils.test_cases
+ )
+
+ def setUp(self):
+ self.database_info = parent_node_dict["database"][-1]
+ self.db_name = self.database_info["db_name"]
+ self.did = self.database_info["db_id"]
+ self.sid = parent_node_dict["server"][-1]["server_id"]
+ self.sgid = config_data['server_group']
+
+ db_con = database_utils.connect_database(self, utils.SERVER_GROUP,
+ self.sid, self.did)
+ if not db_con['data']["connected"]:
+ raise Exception("Could not connect to database to add a table.")
+
+ self.schema_id = parent_node_dict['schema'][-1]["schema_id"]
+ self.schema_name = parent_node_dict['schema'][-1]["schema_name"]
+ schema_response = schema_utils.verify_schemas(self.server,
+ self.db_name,
+ self.schema_name)
+ if not schema_response:
+ raise Exception("Could not find the schema to add a table.")
+ self.table_name = "table_for_wizard%s" % (str(uuid.uuid4())[1:8])
+ self.table_id = tables_utils.create_table(self.server, self.db_name,
+ self.schema_name,
+ self.table_name)
+ self.trans_id = str(random.randint(1, 9999999))
+ qt_init = data_grid_utils._init_query_tool(self, self.trans_id,
+ self.sgid, self.sid,
+ self.did)
+
+ if not qt_init['success']:
+ raise Exception("Could not initialize query tool.")
+
+ def initialize_datagrid(self):
+ if self.test_data:
+ response = self.tester.post(
+ self.url + str(self.trans_id) + '/4/table/' +
+ str(self.sgid) + '/' + str(self.sid) + '/' +
+ str(self.did) + '/' + str(self.table_id),
+ data=json.dumps(self.test_data),
+ content_type='html/json'
+ )
+ else:
+ response = self.tester.post(
+ self.url + str(self.trans_id) + '/4/table/' +
+ str(self.sgid) + '/' + str(self.sid) + '/' +
+ str(self.did) + '/' + str(self.table_id),
+ content_type='html/json'
+ )
+ return response
+
+ def runTest(self):
+ """ This function will update query tool connection."""
+
+ if self.is_positive_test:
+ response = self.initialize_datagrid()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+ else:
+ with patch(self.mock_data["function_name"],
+ return_value=eval(self.mock_data["return_value"])):
+ response = self.initialize_datagrid()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+
+ self.assertEqual(actual_response_code, expected_response_code)
+
+ def tearDown(self):
+ """This function disconnect database."""
+ database_utils.disconnect_database(self, self.sid,
+ self.did)
diff --git a/web/pgadmin/tools/datagrid/tests/utils.py b/web/pgadmin/tools/datagrid/tests/utils.py
new file mode 100644
index 0000000..c3d4bb5
--- /dev/null
+++ b/web/pgadmin/tools/datagrid/tests/utils.py
@@ -0,0 +1,33 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+import os
+import json
+
+file_name = os.path.basename(__file__)
+CURRENT_PATH = os.path.dirname(os.path.realpath(__file__))
+with open(CURRENT_PATH + "/datagrid_test_data.json") as data_file:
+ test_cases = json.load(data_file)
+
+
+def _init_query_tool(self, trans_id, server_group, server_id, db_id):
+ QUERY_TOOL_INIT_URL = '/datagrid/initialize/query_tool'
+
+ qt_init = self.tester.post(
+ '{0}/{1}/{2}/{3}/{4}'.format(
+ QUERY_TOOL_INIT_URL,
+ trans_id,
+ server_group,
+ server_id,
+ db_id
+ ),
+ follow_redirects=True
+ )
+ assert qt_init.status_code == 200
+ qt_init = json.loads(qt_init.data.decode('utf-8'))
+ return qt_init
diff --git a/web/pgadmin/tools/sqleditor/__init__.py b/web/pgadmin/tools/sqleditor/__init__.py
index c62bc7c..a02f6b9 100644
--- a/web/pgadmin/tools/sqleditor/__init__.py
+++ b/web/pgadmin/tools/sqleditor/__init__.py
@@ -10,33 +10,38 @@
"""A blueprint module implementing the sqleditor frame."""
import os
import pickle
-import sys
import re
+from urllib.parse import unquote
import simplejson as json
-from flask import Response, url_for, render_template, session, request, \
- current_app
+from config import PG_DEFAULT_DRIVER, ON_DEMAND_RECORD_COUNT
+from flask import Response, url_for, render_template, session, current_app
+from flask import request, jsonify
from flask_babelex import gettext
from flask_security import login_required, current_user
-from urllib.parse import unquote
-
-from config import PG_DEFAULT_DRIVER, ON_DEMAND_RECORD_COUNT
from pgadmin.misc.file_manager import Filemanager
from pgadmin.tools.sqleditor.command import QueryToolCommand
from pgadmin.tools.sqleditor.utils.constant_definition import ASYNC_OK, \
ASYNC_EXECUTION_ABORTED, \
CONNECTION_STATUS_MESSAGE_MAPPING, TX_STATUS_INERROR
+from pgadmin.tools.sqleditor.utils.filter_dialog import FilterDialog
+from pgadmin.tools.sqleditor.utils.query_history import QueryHistory
+from pgadmin.tools.sqleditor.utils.query_tool_fs_utils import \
+ read_file_generator
+from pgadmin.tools.sqleditor.utils.query_tool_preferences import \
+ register_query_tool_preferences
from pgadmin.tools.sqleditor.utils.start_running_query import StartRunningQuery
from pgadmin.tools.sqleditor.utils.update_session_grid_transaction import \
update_session_grid_transaction
from pgadmin.utils import PgAdminModule
from pgadmin.utils import get_storage_directory
from pgadmin.utils.ajax import make_json_response, bad_request, \
- success_return, internal_server_error
+ internal_server_error
+from pgadmin.utils.constants import MIMETYPE_APP_JS
from pgadmin.utils.driver import get_driver
-from pgadmin.utils.menu import MenuItem
-from pgadmin.utils.exception import ConnectionLost, SSHTunnelConnectionLost,\
+from pgadmin.utils.exception import ConnectionLost, SSHTunnelConnectionLost, \
CryptKeyMissing
+from pgadmin.utils.menu import MenuItem
from pgadmin.utils.sqlautocomplete.autocomplete import SQLAutoComplete
from pgadmin.tools.sqleditor.utils.query_tool_preferences import \
register_query_tool_preferences
@@ -44,8 +49,10 @@ from pgadmin.tools.sqleditor.utils.query_tool_fs_utils import \
read_file_generator
from pgadmin.tools.sqleditor.utils.filter_dialog import FilterDialog
from pgadmin.tools.sqleditor.utils.query_history import QueryHistory
-from pgadmin.utils.constants import MIMETYPE_APP_JS, SERVER_CONNECTION_CLOSED,\
- ERROR_MSG_TRANS_ID_NOT_FOUND
+from pgadmin.utils.constants import MIMETYPE_APP_JS, \
+ SERVER_CONNECTION_CLOSED, ERROR_MSG_TRANS_ID_NOT_FOUND
+from pgadmin.model import Server
+from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry
MODULE_NAME = 'sqleditor'
@@ -109,6 +116,12 @@ class SqlEditorModule(PgAdminModule):
'sqleditor.get_query_history',
'sqleditor.add_query_history',
'sqleditor.clear_query_history',
+ 'sqleditor.get_new_connection_data',
+ 'sqleditor.get_new_connection_database',
+ 'sqleditor.get_new_connection_user',
+ 'sqleditor.get_new_connection_role',
+ 'sqleditor.connect_server',
+ 'sqleditor.connect_server_with_user',
]
def register_preferences(self):
@@ -224,7 +237,7 @@ def start_view_data(trans_id):
)
if status and conn is not None and \
- trans_obj is not None and session_obj is not None:
+ trans_obj is not None and session_obj is not None:
# set fetched row count to 0 as we are executing query again.
trans_obj.update_fetched_row_cnt(0)
@@ -370,7 +383,7 @@ def poll(trans_id):
if isinstance(trans_obj, QueryToolCommand):
trans_status = conn.transaction_status()
if trans_status == TX_STATUS_INERROR and \
- trans_obj.auto_rollback:
+ trans_obj.auto_rollback:
conn.execute_void("ROLLBACK;")
st, result = conn.async_fetchmany_2darray(ON_DEMAND_RECORD_COUNT)
@@ -680,13 +693,12 @@ def save(trans_id):
status=404)
if status and conn is not None and \
- trans_obj is not None and session_obj is not None:
+ trans_obj is not None and session_obj is not None:
# If there is no primary key found then return from the function.
if ('primary_keys' not in session_obj or
- len(session_obj['primary_keys']) <= 0 or
- len(changed_data) <= 0) and \
- 'has_oids' not in session_obj:
+ len(session_obj['primary_keys']) <= 0 or
+ len(changed_data) <= 0) and 'has_oids' not in session_obj:
return make_json_response(
data={
'status': False,
@@ -753,7 +765,7 @@ def append_filter_inclusive(trans_id):
status=404)
if status and conn is not None and \
- trans_obj is not None and session_obj is not None:
+ trans_obj is not None and session_obj is not None:
res = None
filter_sql = ''
@@ -807,7 +819,7 @@ def append_filter_exclusive(trans_id):
info='DATAGRID_TRANSACTION_REQUIRED',
status=404)
if status and conn is not None and \
- trans_obj is not None and session_obj is not None:
+ trans_obj is not None and session_obj is not None:
res = None
filter_sql = ''
@@ -860,7 +872,7 @@ def remove_filter(trans_id):
status=404)
if status and conn is not None and \
- trans_obj is not None and session_obj is not None:
+ trans_obj is not None and session_obj is not None:
res = None
@@ -904,7 +916,7 @@ def set_limit(trans_id):
status=404)
if status and conn is not None and \
- trans_obj is not None and session_obj is not None:
+ trans_obj is not None and session_obj is not None:
res = None
@@ -1046,7 +1058,7 @@ def get_object_name(trans_id):
status=404)
if status and conn is not None and \
- trans_obj is not None and session_obj is not None:
+ trans_obj is not None and session_obj is not None:
res = trans_obj.object_name
else:
status = False
@@ -1082,7 +1094,7 @@ def set_auto_commit(trans_id):
status=404)
if status and conn is not None and \
- trans_obj is not None and session_obj is not None:
+ trans_obj is not None and session_obj is not None:
res = None
@@ -1127,7 +1139,7 @@ def set_auto_rollback(trans_id):
status=404)
if status and conn is not None and \
- trans_obj is not None and session_obj is not None:
+ trans_obj is not None and session_obj is not None:
res = None
@@ -1179,7 +1191,7 @@ def auto_complete(trans_id):
status=404)
if status and conn is not None and \
- trans_obj is not None and session_obj is not None:
+ trans_obj is not None and session_obj is not None:
# Create object of SQLAutoComplete class and pass connection object
auto_complete_obj = SQLAutoComplete(
@@ -1465,6 +1477,284 @@ def get_filter_data(trans_id):
return FilterDialog.get(status, error_msg, conn, trans_obj, session_ob)
[email protected](
+ '/new_connection_dialog/<int:sgid>/<int:sid>',
+ methods=["GET"], endpoint='get_new_connection_data'
+)
+@login_required
+def get_new_connection_data(sgid, sid=None):
+ """
+ This method is used to get required data for get new connection.
+ :extract_sql_from_network_parameters,
+ """
+ try:
+ # if sid and not did:
+ servers = Server.query.all()
+ server_list = [
+ {'name': server.serialize['name'], "id": server.serialize['id']}
+ for server in servers]
+
+ msg = "Success"
+ return make_json_response(
+ data={
+ 'status': True,
+ 'msg': msg,
+ 'result': {
+ 'server_list': server_list
+ }
+ }
+ )
+
+ except Exception as e:
+ return make_json_response(
+ data={
+ 'status': False,
+ 'msg': 'Unable to fetch data.',
+ 'result': {
+ 'server_list': []
+ }
+ }
+ )
+
+
[email protected](
+ '/new_connection_database/<int:sgid>/<int:sid>',
+ methods=["GET"], endpoint='get_new_connection_database'
+)
+@login_required
+def get_new_connection_database(sgid, sid=None):
+ """
+ This method is used to get required data for get new connection.
+ :extract_sql_from_network_parameters,
+ """
+ try:
+ database_list = []
+ from pgadmin.utils.driver import get_driver
+ manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)
+ conn = manager.connection()
+ if conn.connected():
+ is_connected = True
+ else:
+ # connection = conn.connect()
+ is_connected = False
+ if is_connected:
+ if sid:
+ template_path = 'databases/sql/#{0}#'.format(manager.version)
+ last_system_oid = 0
+ server_node_res = manager
+
+ db_disp_res = None
+ params = None
+ if server_node_res and server_node_res.db_res:
+ db_disp_res = ", ".join(
+ ['%s'] * len(server_node_res.db_res.split(','))
+ )
+ params = tuple(server_node_res.db_res.split(','))
+ sql = render_template(
+ "/".join([template_path, 'nodes.sql']),
+ last_system_oid=last_system_oid,
+ db_restrictions=db_disp_res
+ )
+ status, databases = conn.execute_dict(sql, params)
+ database_list = [
+ {'label': database['name'], 'value': database['did']} for
+ database in databases['rows']]
+ else:
+ status = False
+
+ msg = "Success"
+ return make_json_response(
+ data={
+ 'status': status,
+ 'msg': msg,
+ 'result': {
+ 'data': database_list,
+ }
+ }
+ )
+ else:
+ return make_json_response(
+ data={
+ 'status': False,
+ 'msg': 'Server not connected, '
+ 'Please connect with server and try again.',
+ 'result': {
+ 'database_list': [],
+ }
+ }
+ )
+ except Exception as e:
+ return make_json_response(
+ data={
+ 'status': False,
+ 'msg': 'Unable to fetch data.',
+ 'result': {
+ 'database_list': [],
+ }
+ }
+ )
+
+
[email protected](
+ '/new_connection_user/<int:sgid>/<int:sid>',
+ methods=["GET"], endpoint='get_new_connection_user'
+)
+@login_required
+def get_new_connection_user(sgid, sid=None):
+ """
+ This method is used to get required data for get new connection.
+ :extract_sql_from_network_parameters,
+ """
+ try:
+ from pgadmin.utils.driver import get_driver
+ manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)
+ conn = manager.connection()
+ user_list = []
+ if conn.connected():
+ is_connected = True
+ else:
+ is_connected = False
+ if is_connected:
+ if sid:
+ sql_path = 'roles/sql/#{0}#'.format(manager.version)
+ status, users = conn.execute_2darray(
+ render_template(sql_path + 'nodes.sql')
+ )
+ user_list = [
+ {'value': user['rolname'], 'label': user['rolname']} for
+ user in users['rows'] if user['rolcanlogin']]
+ else:
+ status = False
+
+ msg = "Success"
+ return make_json_response(
+ data={
+ 'status': status,
+ 'msg': msg,
+ 'result': {
+ 'data': user_list,
+ }
+ }
+ )
+ else:
+ return make_json_response(
+ data={
+ 'status': False,
+ 'msg': 'Server not connected, '
+ 'Please connect with server and try again.',
+ 'result': {
+ 'user_list': [],
+ }
+ }
+ )
+ except Exception as e:
+ return make_json_response(
+ data={
+ 'status': False,
+ 'msg': 'Unable to fetch data.',
+ 'result': {
+ 'user_list': [],
+ }
+ }
+ )
+
+
[email protected](
+ '/new_connection_role/<int:sgid>/<int:sid>',
+ methods=["GET"], endpoint='get_new_connection_role'
+)
+@login_required
+def get_new_connection_role(sgid, sid=None):
+ """
+ This method is used to get required data for get new connection.
+ :extract_sql_from_network_parameters,
+ """
+ try:
+ from pgadmin.utils.driver import get_driver
+ manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)
+ conn = manager.connection()
+ role_list = []
+ if conn.connected():
+ is_connected = True
+ else:
+ is_connected = False
+ if is_connected:
+ if sid:
+ sql_path = 'roles/sql/#{0}#'.format(manager.version)
+ status, roles = conn.execute_2darray(
+ render_template(sql_path + 'nodes.sql')
+ )
+ role_list = [
+ {'value': role['rolname'], 'label': role['rolname']} for
+ role in roles['rows']]
+ else:
+ status = False
+
+ msg = "Success"
+ return make_json_response(
+ data={
+ 'status': status,
+ 'msg': msg,
+ 'result': {
+ 'data': role_list,
+ }
+ }
+ )
+ else:
+ return make_json_response(
+ data={
+ 'status': False,
+ 'msg': 'Server not connected, '
+ 'Please connect with server and try again.',
+ 'result': {
+ 'user_list': [],
+ }
+ }
+ )
+ except Exception as e:
+ return make_json_response(
+ data={
+ 'status': False,
+ 'msg': 'Unable to fetch data.',
+ 'result': {
+ 'user_list': [],
+ }
+ }
+ )
+
+
[email protected](
+ '/connect_server/<int:sid>/<usr>',
+ methods=["GET"],
+ endpoint="connect_server_with_user"
+)
[email protected](
+ '/connect_server/<int:sid>',
+ methods=["GET"],
+ endpoint="connect_server"
+)
+@login_required
+def connect_server(sid, usr=None):
+ # Check if server is already connected then no need to reconnect again.
+ server = Server.query.filter_by(id=sid).first()
+ driver = get_driver(PG_DEFAULT_DRIVER)
+ manager = driver.connection_manager(sid)
+ conn = manager.connection()
+ user = None
+ if usr and manager.user != usr:
+ user = usr
+ else:
+ if conn.connected():
+ return make_json_response(
+ success=1,
+ info=gettext("Server connected."),
+ data={}
+ )
+
+ view = SchemaDiffRegistry.get_node_view('server')
+ return view.connect(server.servergroup_id, sid, user=user)
+
+
@blueprint.route(
'/filter_dialog/<int:trans_id>',
methods=["PUT"], endpoint='set_filter_data'
diff --git a/web/pgadmin/tools/sqleditor/static/css/sqleditor.css b/web/pgadmin/tools/sqleditor/static/css/sqleditor.css
index 00543ff..b8c2d2e 100644
--- a/web/pgadmin/tools/sqleditor/static/css/sqleditor.css
+++ b/web/pgadmin/tools/sqleditor/static/css/sqleditor.css
@@ -315,10 +315,6 @@ input.editor-checkbox:focus {
padding: 10px 0px;
}
-.editor-title {
- width:100%;
-}
-
.connection-status-hide {
display: none !important;
}
@@ -395,3 +391,7 @@ input.editor-checkbox:focus {
.hide-vertical-scrollbar {
overflow-y: hidden;
}
+
+.new-connection-dialog-style {
+ width: 100% !important;
+}
diff --git a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
index ddd9ad7..9389db5 100644
--- a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
+++ b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
@@ -14,6 +14,7 @@ define('tools.querytool', [
'jqueryui.position', 'underscore', 'pgadmin.alertifyjs',
'sources/pgadmin', 'backbone', 'bundled_codemirror', 'sources/utils',
'pgadmin.misc.explain',
+ 'pgadmin.user_management.current_user',
'sources/selection/grid_selector',
'sources/selection/active_cell_capture',
'sources/selection/clipboard',
@@ -26,6 +27,7 @@ define('tools.querytool', [
'sources/sqleditor/execute_query',
'sources/sqleditor/query_tool_http_error_handler',
'sources/sqleditor/filter_dialog',
+ 'sources/sqleditor/new_connection_dialog',
'sources/sqleditor/geometry_viewer',
'sources/sqleditor/history/history_collection.js',
'sources/sqleditor/history/query_history',
@@ -52,8 +54,8 @@ define('tools.querytool', [
'pgadmin.tools.user_management',
], function(
gettext, url_for, $, jqueryui, jqueryui_position, _, alertify, pgAdmin, Backbone, codemirror, pgadminUtils,
- pgExplain, GridSelector, ActiveCellCapture, clipboard, copyData, RangeSelectionHelper, handleQueryOutputKeyboardEvent,
- XCellSelectionModel, setStagedRows, SqlEditorUtils, ExecuteQuery, httpErrorHandler, FilterHandler,
+ pgExplain, current_user, GridSelector, ActiveCellCapture, clipboard, copyData, RangeSelectionHelper, handleQueryOutputKeyboardEvent,
+ XCellSelectionModel, setStagedRows, SqlEditorUtils, ExecuteQuery, httpErrorHandler, FilterHandler, newConnectionHandler,
GeometryViewer, historyColl, queryHist, querySources,
keyboardShortcuts, queryToolActions, queryToolNotifications, Datagrid,
modifyAnimation, calculateQueryRunTime, callRenderAfterPoll, queryToolPref, queryTxnStatus, csrfToken, panelTitleFunc,
@@ -89,6 +91,9 @@ define('tools.querytool', [
this.layout = opts.layout;
this.set_server_version(opts.server_ver);
this.trigger('pgadmin-sqleditor:view:initialised');
+ this.connection_list = [
+ {'server_group': null,'server': null, 'database': null, 'user': null, 'role': null, 'title': '<New Connection>'},
+ ];
},
// Bind all the events
@@ -151,6 +156,35 @@ define('tools.querytool', [
'click #btn-rollback': 'on_rollback_transaction',
},
+ render_connection: function(data_list) {
+ if(this.handler.is_query_tool) {
+ var dropdownElement = document.getElementById('connections-list');
+ dropdownElement.innerHTML = '';
+ data_list.forEach((option, index) => {
+ $('#connections-list').append('<li class="connection-list-item" data-index='+ index +'><a class="dropdown-item" href="#" tabindex="0">'+ option.title +'</a></li>');
+
+ });
+ var self = this;
+ $('.connection-list-item').click(function() {
+ self.get_connection_data(this);
+ });
+ } else {
+ $('.conn-info-dd').hide();
+ $('.editor-title').css({pointerEvents: 'none'});
+ }
+ },
+
+ get_connection_data: function(event){
+ var index = $(event).attr('data-index');
+ var connection_details = this.connection_list[index];
+ if(connection_details.server_group) {
+ this.on_change_connection(connection_details);
+ } else {
+ this.on_new_connection();
+ }
+
+ },
+
reflectPreferences: function() {
let self = this,
browser = pgWindow.default.pgAdmin.Browser,
@@ -199,8 +233,15 @@ define('tools.querytool', [
});
},
- set_editor_title: function(title) {
- this.$el.find('.editor-title').text(title);
+ set_editor_title: function(title, is_connected) {
+ if(is_connected) {
+ this.$el.find('.editor-title').text(title);
+ this.render_connection(this.connection_list);
+ } else {
+ this.$el.find('.editor-title').text(title);
+ this.render_connection(this.connection_list);
+ }
+
},
// This function is used to render the template.
@@ -684,6 +725,8 @@ define('tools.querytool', [
pgBrowser.register_to_activity_listener(document, ()=>{
alertify.alert(gettext('Timeout'), gettext('Your session has timed out due to inactivity. Please close the window and login again.'));
});
+
+ self.render_connection(self.connection_list);
},
/* Regarding SlickGrid usage in render_grid function.
@@ -1595,6 +1638,17 @@ define('tools.querytool', [
);
},
+ on_new_connection: function() {
+ var self = this;
+
+ // Trigger the show_filter signal to the SqlEditorController class
+ self.handler.trigger(
+ 'pgadmin-sqleditor:button:show_new_connection',
+ self,
+ self.handler
+ );
+ },
+
// Callback function for include filter button click.
on_include_filter: function(ev) {
var self = this;
@@ -2038,6 +2092,83 @@ define('tools.querytool', [
queryToolActions.executeRollback(this.handler);
},
+ on_change_connection: function(connection_details, ref) {
+ let title = this.$el.find('.editor-title').html();
+ if(connection_details['title'] != title) {
+ var self = this;
+ $.ajax({
+ async: false,
+ url: url_for('datagrid.update_query_tool_connection', {
+ 'trans_id': self.transId,
+ 'sgid': connection_details['server_group'],
+ 'sid': connection_details['server'],
+ 'did': connection_details['database'],
+ }),
+ method: 'POST',
+ contentType: 'application/json',
+ data: JSON.stringify(connection_details),
+ })
+ .done(function(res) {
+ if(res.success) {
+ self.transId = res.data.tran_id;
+ self.handler.transId = res.data.tran_id;
+ self.handler.url_params = {
+ 'did': connection_details['database'],
+ 'is_query_tool': self.handler.url_params.is_query_tool,
+ 'server_type': self.handler.url_params.server_type,
+ 'sgid': connection_details['server_group'],
+ 'sid': connection_details['server'],
+ 'title': connection_details['title'],
+ };
+ self.set_editor_title(self.handler.url_params.title);
+ self.handler.setTitle(self.handler.url_params.title);
+ alertify.success('connected successfully');
+ if(ref){
+ let connection_data = {
+ 'server_group': self.handler.url_params.sgid,
+ 'server': connection_details['server'],
+ 'database': connection_details['database'],
+ 'user': connection_details['user'],
+ 'title': connection_details['title'],
+ 'role': connection_details['role'],
+ 'password': connection_details['password'],
+ 'is_allow_new_connection': true,
+ };
+ self.connection_list.unshift(connection_data);
+ self.render_connection(self.connection_list);
+ ref.close();
+ }
+ }
+ return true;
+ })
+ .fail(function(xhr) {
+ if(xhr.status == 428) {
+ alertify.connectServer('Connect to server', xhr.responseJSON.result, connection_details['server'], false);
+ } else {
+ alertify.error(xhr.responseJSON['errormsg']);
+ }
+ /*let url = url_for('sqleditor.connect_server_with_user', {
+ 'sid': newConnCollectionModel['server'],
+ 'usr': newConnCollectionModel['user']
+ });
+ $.ajax({
+ async: false,
+ url: url,
+ headers: {
+ 'Cache-Control' : 'no-cache',
+ },
+ }).done(function () {
+ Backform.Select2Control.prototype.onChange.apply(self, arguments);
+ response.server_list.forEach(function(obj){
+ if(obj.id==self.model.changed.server) {
+ response.server_name = obj.name;
+ }
+ });
+ }).fail(function(xhr){});*/
+
+ });
+ }
+ },
});
/* Defining controller class for data grid, which actually
@@ -2357,7 +2488,18 @@ define('tools.querytool', [
});
$('#btn-conn-status i').removeClass('obtaining-conn');
- self.gridView.set_editor_title(_.unescape(url_params.title));
+ self.gridView.set_editor_title(_.unescape(url_params.title), true);
+ let connection_data = {
+ 'server_group': self.gridView.handler.url_params.sgid,
+ 'server': self.gridView.handler.url_params.sid,
+ 'database': self.gridView.handler.url_params.did,
+ 'user': null,
+ 'role': null,
+ 'title': _.unescape(url_params.title),
+ 'is_allow_new_connection': false,
+ };
+ self.gridView.connection_list.unshift(connection_data);
+ self.gridView.render_connection(self.gridView.connection_list);
};
pgBrowser.Events.on('pgadmin:query_tool:connected:' + transId, afterConn);
@@ -2452,6 +2594,7 @@ define('tools.querytool', [
self.on('pgadmin-sqleditor:button:save_file', self._save_file, self);
self.on('pgadmin-sqleditor:button:deleterow', self._delete, self);
self.on('pgadmin-sqleditor:button:show_filter', self._show_filter, self);
+ self.on('pgadmin-sqleditor:button:show_new_connection', self._show_new_connection, self);
self.on('pgadmin-sqleditor:button:include_filter', self._include_filter, self);
self.on('pgadmin-sqleditor:button:exclude_filter', self._exclude_filter, self);
self.on('pgadmin-sqleditor:button:remove_filter', self._remove_filter, self);
@@ -3622,7 +3765,6 @@ define('tools.querytool', [
}
};
},
-
// This function will show the filter in the text area.
_show_filter: function() {
let self = this,
@@ -3637,7 +3779,19 @@ define('tools.querytool', [
}
FilterHandler.dialog(self, reconnect);
},
+ // This function will show the new connection.
+ _show_new_connection: function() {
+ let self = this,
+ reconnect = false;
+ /* When server is disconnected and connected, connection is lost,
+ * To reconnect pass true
+ */
+ if (arguments.length > 0 && arguments[arguments.length - 1] == 'connect') {
+ reconnect = true;
+ }
+ newConnectionHandler.dialog(self, reconnect);
+ },
// This function will include the filter by selection.
_include_filter: function() {
var self = this,
diff --git a/web/pgadmin/tools/sqleditor/static/scss/_sqleditor.scss b/web/pgadmin/tools/sqleditor/static/scss/_sqleditor.scss
index fd1e5d3..53f2449 100644
--- a/web/pgadmin/tools/sqleditor/static/scss/_sqleditor.scss
+++ b/web/pgadmin/tools/sqleditor/static/scss/_sqleditor.scss
@@ -30,6 +30,19 @@
color: $sql-title-fg;
}
+.connection-info {
+ background: $sql-title-bg;
+ color: $sql-title-fg;
+ width:100%;
+ display: inherit;
+}
+
+.conn-info-dd {
+ padding-top: 0.3em;
+ padding-left: 0.2em;
+ cursor: pointer;
+}
+
#editor-panel {
z-index: 0;
diff --git a/web/pgadmin/tools/sqleditor/tests/test_new_connection_database.py b/web/pgadmin/tools/sqleditor/tests/test_new_connection_database.py
new file mode 100644
index 0000000..20fe3e3
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/tests/test_new_connection_database.py
@@ -0,0 +1,98 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+import json
+from pgadmin.utils.route import BaseTestGenerator
+from regression import parent_node_dict
+from regression.test_setup import config_data
+from regression.python_test_utils import test_utils as utils
+
+
+class TestNewConnectionDatabase(BaseTestGenerator):
+ """ This class will test new connection database. """
+ scenarios = [
+ ('New connection dialog',
+ dict(
+ url="/sqleditor/new_connection_database/",
+ is_positive_test=True,
+ mocking_required=False,
+ is_server_conn_required=False,
+ test_data={},
+ mock_data={},
+ expected_data={
+ "status_code": 200
+ }
+ )),
+ ('New connection dialog connect server',
+ dict(
+ url="/sqleditor/new_connection_database/",
+ is_positive_test=True,
+ mocking_required=False,
+ is_server_conn_required=True,
+ test_data={},
+ mock_data={},
+ expected_data={
+ "status_code": 200
+ }
+ )),
+ ('New connection dialog negative',
+ dict(
+ url="/sqleditor/new_connection_database/",
+ is_positive_test=False,
+ mocking_required=False,
+ is_server_conn_required=True,
+ test_data={},
+ mock_data={},
+ expected_data={
+ "status_code": 200
+ }
+ )),
+ ]
+
+ def setUp(self):
+ self.sid = parent_node_dict["server"][-1]["server_id"]
+ self.sgid = config_data['server_group']
+
+ def get_database(self):
+ response = self.tester.get(
+ self.url + str(self.sgid) + '/' + str(self.sid),
+ content_type='html/json'
+ )
+
+ return response
+
+ def runTest(self):
+ if self.is_positive_test:
+ if self.is_server_conn_required:
+ self.server['password'] = self.server['db_password']
+ server_response = self.tester.post(
+ '/browser/server/connect/{0}/{1}'.format(
+ utils.SERVER_GROUP,
+ self.sid),
+ data=json.dumps(self.server),
+ content_type='html/json'
+ )
+ response = self.get_database()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+ else:
+ if self.is_server_conn_required:
+ self.server['password'] = self.server['db_password']
+ server_response = self.tester.post(
+ '/browser/server/connect/{0}/{1}'.format(
+ utils.SERVER_GROUP,
+ self.sid),
+ data=json.dumps(self.server),
+ content_type='html/json'
+ )
+ self.sid = 0
+ response = self.get_database()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+ self.assertEqual(actual_response_code, expected_response_code)
diff --git a/web/pgadmin/tools/sqleditor/tests/test_new_connection_dialog.py b/web/pgadmin/tools/sqleditor/tests/test_new_connection_dialog.py
new file mode 100644
index 0000000..75a47ef
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/tests/test_new_connection_dialog.py
@@ -0,0 +1,50 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+import json
+from pgadmin.utils.route import BaseTestGenerator
+from regression import parent_node_dict
+from regression.test_setup import config_data
+from regression.python_test_utils import test_utils as utils
+
+
+class TestNewConnectionDialog(BaseTestGenerator):
+ """ This class will test new connection dialog. """
+ scenarios = [
+ ('New connection dialog',
+ dict(
+ url="/sqleditor/new_connection_dialog/",
+ is_positive_test=True,
+ mocking_required=False,
+ is_connect_server=False,
+ test_data={},
+ mock_data={},
+ expected_data={
+ "status_code": 200
+ }
+ )),
+ ]
+
+ def setUp(self):
+ self.sid = parent_node_dict["server"][-1]["server_id"]
+ self.sgid = config_data['server_group']
+
+ def new_connection(self):
+ response = self.tester.get(
+ self.url + str(self.sgid) + '/' + str(self.sgid),
+ content_type='html/json'
+ )
+
+ return response
+
+ def runTest(self):
+ if self.is_positive_test:
+ response = self.new_connection()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+ self.assertEqual(actual_response_code, expected_response_code)
diff --git a/web/pgadmin/tools/sqleditor/tests/test_new_connection_user.py b/web/pgadmin/tools/sqleditor/tests/test_new_connection_user.py
new file mode 100644
index 0000000..7b6c12e
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/tests/test_new_connection_user.py
@@ -0,0 +1,98 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+import json
+from pgadmin.utils.route import BaseTestGenerator
+from regression import parent_node_dict
+from regression.test_setup import config_data
+from regression.python_test_utils import test_utils as utils
+
+
+class TestNewConnectionUser(BaseTestGenerator):
+ """ This class will test new connection user. """
+ scenarios = [
+ ('New connection dialog',
+ dict(
+ url="/sqleditor/new_connection_user/",
+ is_positive_test=True,
+ mocking_required=False,
+ is_server_conn_required=False,
+ test_data={},
+ mock_data={},
+ expected_data={
+ "status_code": 200
+ }
+ )),
+ ('New connection dialog connect server',
+ dict(
+ url="/sqleditor/new_connection_user/",
+ is_positive_test=True,
+ mocking_required=False,
+ is_server_conn_required=True,
+ test_data={},
+ mock_data={},
+ expected_data={
+ "status_code": 200
+ }
+ )),
+ ('New connection dialog negative',
+ dict(
+ url="/sqleditor/new_connection_user/",
+ is_positive_test=False,
+ mocking_required=False,
+ is_server_conn_required=True,
+ test_data={},
+ mock_data={},
+ expected_data={
+ "status_code": 200
+ }
+ )),
+ ]
+
+ def setUp(self):
+ self.sid = parent_node_dict["server"][-1]["server_id"]
+ self.sgid = config_data['server_group']
+
+ def get_use(self):
+ response = self.tester.get(
+ self.url + str(self.sgid) + '/' + str(self.sid),
+ content_type='html/json'
+ )
+
+ return response
+
+ def runTest(self):
+ if self.is_positive_test:
+ if self.is_server_conn_required:
+ self.server['password'] = self.server['db_password']
+ server_response = self.tester.post(
+ '/browser/server/connect/{0}/{1}'.format(
+ utils.SERVER_GROUP,
+ self.sid),
+ data=json.dumps(self.server),
+ content_type='html/json'
+ )
+ response = self.get_use()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+ else:
+ if self.is_server_conn_required:
+ self.server['password'] = self.server['db_password']
+ server_response = self.tester.post(
+ '/browser/server/connect/{0}/{1}'.format(
+ utils.SERVER_GROUP,
+ self.sid),
+ data=json.dumps(self.server),
+ content_type='html/json'
+ )
+ self.sid = 0
+ response = self.get_use()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+ self.assertEqual(actual_response_code, expected_response_code)
diff --git a/web/pgadmin/utils/driver/psycopg2/connection.py b/web/pgadmin/utils/driver/psycopg2/connection.py
index 9cb65bc..83c2c17 100644
--- a/web/pgadmin/utils/driver/psycopg2/connection.py
+++ b/web/pgadmin/utils/driver/psycopg2/connection.py
@@ -21,7 +21,7 @@ import psycopg2
from flask import g, current_app
from flask_babelex import gettext
from flask_security import current_user
-from pgadmin.utils.crypto import decrypt
+from pgadmin.utils.crypto import decrypt, encrypt
from psycopg2.extensions import encodings
import config
@@ -211,8 +211,17 @@ class Connection(BaseConnection):
password = None
passfile = None
manager = self.manager
+ encpass = None
+ is_update_password = True
+ crypt_key_present, crypt_key = get_crypt_key()
+
+ if 'user' in kwargs and kwargs['password']:
+ password = kwargs['password']
+ kwargs.pop('password')
+ is_update_password = False
+ else:
+ encpass = kwargs['password'] if 'password' in kwargs else None
- encpass = kwargs['password'] if 'password' in kwargs else None
passfile = kwargs['passfile'] if 'passfile' in kwargs else None
tunnel_password = kwargs['tunnel_password'] if 'tunnel_password' in \
kwargs else ''
@@ -227,16 +236,16 @@ class Connection(BaseConnection):
if manager.use_ssh_tunnel == 1:
manager.check_ssh_tunnel_alive()
- if encpass is None:
- encpass = self.password or getattr(manager, 'password', None)
+ if is_update_password:
+ if encpass is None:
+ encpass = self.password or getattr(manager, 'password', None)
- self.password = encpass
+ self.password = encpass
# Reset the existing connection password
if self.reconnecting is not False:
self.password = None
- crypt_key_present, crypt_key = get_crypt_key()
if not crypt_key_present:
raise CryptKeyMissing()
@@ -269,7 +278,10 @@ class Connection(BaseConnection):
try:
database = self.db
- user = manager.user
+ if 'user' in kwargs and kwargs['user']:
+ user = kwargs['user']
+ else:
+ user = manager.user
conn_id = self.conn_id
import os
@@ -338,10 +350,10 @@ class Connection(BaseConnection):
self.wasConnected = False
raise e
- if status:
+ if status and is_update_password:
manager._update_password(encpass)
else:
- if not self.reconnecting:
+ if not self.reconnecting and is_update_password:
self.wasConnected = False
return status, msg
@@ -359,7 +371,7 @@ class Connection(BaseConnection):
else:
self.conn.autocommit = True
- def _set_role(self, manager, cur, conn_id):
+ def _set_role(self, manager, cur, conn_id, **kwargs):
"""
Set role
:param manager:
@@ -367,8 +379,18 @@ class Connection(BaseConnection):
:param conn_id:
:return:
"""
- if manager.role:
- status = self._execute(cur, "SET ROLE TO %s", [manager.role])
+ is_set_role = False
+ role = None
+
+ if 'role' in kwargs and kwargs['role']:
+ is_set_role = True
+ role = kwargs['role']
+ elif manager.role:
+ is_set_role = True
+ role = manager.role
+
+ if is_set_role:
+ status = self._execute(cur, "SET ROLE TO %s", [role])
if status is not None:
self.conn.close()
@@ -382,7 +404,7 @@ class Connection(BaseConnection):
msg=status
)
)
- return False, \
+ return True, \
_(
"Failed to setup the role with error message:\n{0}"
).format(status)
@@ -445,7 +467,7 @@ class Connection(BaseConnection):
return False, status
- is_error, errmsg = self._set_role(manager, cur, conn_id)
+ is_error, errmsg = self._set_role(manager, cur, conn_id, **kwargs)
if is_error:
return False, errmsg
@@ -491,7 +513,7 @@ WHERE db.datname = current_database()""")
if len(manager.db_info) == 1:
manager.did = res['did']
- self._set_user_info(cur, manager)
+ self._set_user_info(cur, manager, **kwargs)
self._set_server_type_and_password(kwargs, manager)
@@ -499,7 +521,7 @@ WHERE db.datname = current_database()""")
return True, None
- def _set_user_info(self, cur, manager):
+ def _set_user_info(self, cur, manager, **kwargs):
"""
Set user info.
:param cur:
@@ -517,7 +539,7 @@ WHERE db.datname = current_database()""")
WHERE
rolname = current_user""")
- if status is None:
+ if status is None and 'user' not in kwargs:
manager.user_info = dict()
if cur.rowcount > 0:
manager.user_info = cur.fetchmany(1)[0]
^ permalink raw reply [nested|flat] 18+ messages in thread
* Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab
2020-09-17 10:45 [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-09-21 11:54 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-09-28 05:50 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
@ 2020-09-28 12:28 ` Akshay Joshi <[email protected]>
2020-09-28 14:01 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
0 siblings, 1 reply; 18+ messages in thread
From: Akshay Joshi @ 2020-09-28 12:28 UTC (permalink / raw)
To: Nikhil Mohite <[email protected]>; +Cc: pgadmin-hackers
Hi Nikhil
The patch is not applying, rebase, and send it again. Please check your
code should not create any new SonarQube issues.
On Mon, Sep 28, 2020 at 11:20 AM Nikhil Mohite <
[email protected]> wrote:
> Hi Akshay,
>
> I have resolved all the review comments and also updated the test cases as
> per the new implementation.
>
> PFA updated patch.
>
>
>
> On Mon, Sep 21, 2020 at 5:24 PM Akshay Joshi <
> [email protected]> wrote:
>
>> Hi Nikhil
>>
>> Following are the initial review comments:
>>
>> - Open View/Edit data on any table and click on the same database
>> connection and then click on the Execute button. Got "get_primary_keys()
>> takes 1 positional argument but 2 were given" error.
>> - In my opinion, we should hide the option to change the database
>> connection for View/Edit Data.
>> - If the user clicks on the same database connection multiple times
>> then no need to change the backend connection and transaction id. Add
>> validation at the backend, no action required in this case.
>> - The role option is missing from the "connect to server" dialog.
>> - The Password field should not be there on the "connect to server"
>> dialog. Sometimes we saved the password so asking a password every time is
>> not correct. Check the pgAdmin 3 behavior.
>>
>> Code review still remains.
>>
>> On Thu, Sep 17, 2020 at 4:15 PM Nikhil Mohite <
>> [email protected]> wrote:
>>
>>> Hi Team,
>>>
>>> Regarding RM-3794 <https://redmine.postgresql.org/issues/3794; allow
>>> the user to change the database connection from an open query tool:
>>> I have implemented the feature and also added documentation for it.
>>>
>>> PFA patch.
>>>
>>> --
>>> *Thanks & Regards,*
>>> *Nikhil Mohite*
>>> *Software Engineer.*
>>> *EDB Postgres* <https://www.enterprisedb.com/;
>>> *Mob.No: +91-7798364578.*
>>>
>>
>>
>> --
>> *Thanks & Regards*
>> *Akshay Joshi*
>> *pgAdmin Hacker | Sr. Software Architect*
>> *EDB Postgres <http://edbpostgres.com>*
>>
>> *Mobile: +91 976-788-8246*
>>
>
--
*Thanks & Regards*
*Akshay Joshi*
*pgAdmin Hacker | Sr. Software Architect*
*EDB Postgres <http://edbpostgres.com>*
*Mobile: +91 976-788-8246*
^ permalink raw reply [nested|flat] 18+ messages in thread
* Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab
2020-09-17 10:45 [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-09-21 11:54 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-09-28 05:50 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-09-28 12:28 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
@ 2020-09-28 14:01 ` Nikhil Mohite <[email protected]>
2020-09-29 06:01 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
0 siblings, 1 reply; 18+ messages in thread
From: Nikhil Mohite @ 2020-09-28 14:01 UTC (permalink / raw)
To: Akshay Joshi <[email protected]>; +Cc: pgadmin-hackers
Hi Akshay,
I have resolved code conflict issues and sonarqube issues.
PFA updated patch.
Regards,
Nikhil Mohite.
On Mon, Sep 28, 2020 at 5:58 PM Akshay Joshi <[email protected]>
wrote:
> Hi Nikhil
>
> The patch is not applying, rebase, and send it again. Please check your
> code should not create any new SonarQube issues.
>
> On Mon, Sep 28, 2020 at 11:20 AM Nikhil Mohite <
> [email protected]> wrote:
>
>> Hi Akshay,
>>
>> I have resolved all the review comments and also updated the test cases
>> as per the new implementation.
>>
>> PFA updated patch.
>>
>>
>>
>> On Mon, Sep 21, 2020 at 5:24 PM Akshay Joshi <
>> [email protected]> wrote:
>>
>>> Hi Nikhil
>>>
>>> Following are the initial review comments:
>>>
>>> - Open View/Edit data on any table and click on the same database
>>> connection and then click on the Execute button. Got "get_primary_keys()
>>> takes 1 positional argument but 2 were given" error.
>>> - In my opinion, we should hide the option to change the database
>>> connection for View/Edit Data.
>>> - If the user clicks on the same database connection multiple times
>>> then no need to change the backend connection and transaction id. Add
>>> validation at the backend, no action required in this case.
>>> - The role option is missing from the "connect to server" dialog.
>>> - The Password field should not be there on the "connect to server"
>>> dialog. Sometimes we saved the password so asking a password every time is
>>> not correct. Check the pgAdmin 3 behavior.
>>>
>>> Code review still remains.
>>>
>>> On Thu, Sep 17, 2020 at 4:15 PM Nikhil Mohite <
>>> [email protected]> wrote:
>>>
>>>> Hi Team,
>>>>
>>>> Regarding RM-3794 <https://redmine.postgresql.org/issues/3794; allow
>>>> the user to change the database connection from an open query tool:
>>>> I have implemented the feature and also added documentation for it.
>>>>
>>>> PFA patch.
>>>>
>>>> --
>>>> *Thanks & Regards,*
>>>> *Nikhil Mohite*
>>>> *Software Engineer.*
>>>> *EDB Postgres* <https://www.enterprisedb.com/;
>>>> *Mob.No: +91-7798364578.*
>>>>
>>>
>>>
>>> --
>>> *Thanks & Regards*
>>> *Akshay Joshi*
>>> *pgAdmin Hacker | Sr. Software Architect*
>>> *EDB Postgres <http://edbpostgres.com>*
>>>
>>> *Mobile: +91 976-788-8246*
>>>
>>
>
> --
> *Thanks & Regards*
> *Akshay Joshi*
> *pgAdmin Hacker | Sr. Software Architect*
> *EDB Postgres <http://edbpostgres.com>*
>
> *Mobile: +91 976-788-8246*
>
Attachments:
[application/octet-stream] RM_3794_V3.patch (219.9K, 3-RM_3794_V3.patch)
download | inline diff:
diff --git a/docs/en_US/images/new_connection_dialog.png b/docs/en_US/images/new_connection_dialog.png
new file mode 100644
index 0000000000000000000000000000000000000000..8cf7ae6aa750360ae7710af61bf8e89f9d520284
GIT binary patch
literal 49691
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	%0^c>O%*OkVmcSQwgzI6WP2m3$pEbze=t&
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
NIVmN{VsYb-{|8bOF{}Up
literal 0
HcmV?d00001
diff --git a/docs/en_US/images/new_connection_options.png b/docs/en_US/images/new_connection_options.png
new file mode 100644
index 0000000000000000000000000000000000000000..7ca94374baf0eb9a8a0e49cbd9d4d426895ff9d0
GIT binary patch
literal 45739
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
literal 0
HcmV?d00001
diff --git a/docs/en_US/query_tool.rst b/docs/en_US/query_tool.rst
index 83933f0..80f27a5 100644
--- a/docs/en_US/query_tool.rst
+++ b/docs/en_US/query_tool.rst
@@ -300,3 +300,24 @@ transaction status by clicking on the status icon in the Query Tool:
.. image:: images/query_tool_connection_status.png
:alt: Query tool connection and transaction statuses
:align: center
+
+Change connection
+*****************
+
+User can connect to another server or database from existing open session of query tool.
+
+* Click on the connection link next to connection status.
+* Now click on the *<New Connection>* option from the dropdown.
+
+.. image:: images/new_connection_options.png
+ :alt: Query tool connection options
+ :align: center
+
+* Now select server, database, user, and role to connect and click OK.
+
+.. image:: images/new_connection_dialog.png
+ :alt: Query tool connection dialog
+ :align: center
+
+* A newly created connection will now get listed in the options.
+* To connect, select the newly created connection from the dropdown list.
diff --git a/web/pgadmin/browser/server_groups/servers/roles/tests/utils.py b/web/pgadmin/browser/server_groups/servers/roles/tests/utils.py
index 3a7ee58..028ee64 100644
--- a/web/pgadmin/browser/server_groups/servers/roles/tests/utils.py
+++ b/web/pgadmin/browser/server_groups/servers/roles/tests/utils.py
@@ -152,3 +152,38 @@ def delete_role(connection, role_names):
exception = "Error while deleting role: %s: line:%s %s" % (
file_name, sys.exc_traceback.tb_lineno, exception)
print(exception, file=sys.stderr)
+
+
+def create_role_with_password(server, role_name, role_password):
+ """
+ This function create the role.
+ :param server:
+ :param role_name:
+ :param role_password:
+ :return:
+ """
+ try:
+ connection = utils.get_db_connection(server['db'],
+ server['username'],
+ server['db_password'],
+ server['host'],
+ server['port'],
+ server['sslmode'])
+ pg_cursor = connection.cursor()
+ pg_cursor.execute(
+ "CREATE ROLE %s LOGIN PASSWORD '%s'" % (role_name, role_password))
+ connection.commit()
+ # Get 'oid' from newly created tablespace
+ pg_cursor.execute(
+ "SELECT pr.oid from pg_catalog.pg_roles pr WHERE pr.rolname='%s'" %
+ role_name)
+ oid = pg_cursor.fetchone()
+ role_id = ''
+ if oid:
+ role_id = oid[0]
+ connection.close()
+ return role_id
+ except Exception as exception:
+ exception = "Error while deleting role: %s: line:%s %s" % (
+ file_name, sys.exc_traceback.tb_lineno, exception)
+ print(exception, file=sys.stderr)
diff --git a/web/pgadmin/model/__init__.py b/web/pgadmin/model/__init__.py
index 425c9e6..1d30092 100644
--- a/web/pgadmin/model/__init__.py
+++ b/web/pgadmin/model/__init__.py
@@ -94,6 +94,15 @@ class ServerGroup(db.Model):
name = db.Column(db.String(128), nullable=False)
__table_args__ = (db.UniqueConstraint('user_id', 'name'),)
+ @property
+ def serialize(self):
+ """Return object data in easily serializable format"""
+ return {
+ 'id': self.id,
+ 'user_id': self.user_id,
+ 'name': self.name,
+ }
+
class Server(db.Model):
"""Define a registered Postgres server"""
@@ -175,6 +184,44 @@ class Server(db.Model):
tunnel_password = db.Column(db.String(64), nullable=True)
shared = db.Column(db.Boolean(), nullable=False)
+ @property
+ def serialize(self):
+ """Return object data in easily serializable format"""
+ return {
+ "id": self.id,
+ "user_id": self.user_id,
+ "servergroup_id": self.servergroup_id,
+ "name": self.name,
+ "host": self.host,
+ "hostaddr": self.hostaddr,
+ "port": self.port,
+ "maintenance_db": self.maintenance_db,
+ "username": self.username,
+ "password": self.password,
+ "save_password": self.save_password,
+ "role": self.role,
+ "ssl_mode": self.ssl_mode,
+ "comment": self.comment,
+ "discovery_id": self.discovery_id,
+ "db_res": self.db_res,
+ "passfile": self.passfile,
+ "sslcert": self.sslcert,
+ "sslkey": self.sslkey,
+ "sslrootcert": self.sslrootcert,
+ "sslcrl": self.sslcrl,
+ "sslcompression": self.sslcompression,
+ "bgcolor": self.bgcolor,
+ "fgcolor": self.fgcolor,
+ "service": self.service,
+ "connect_timeout": self.connect_timeout,
+ "use_ssh_tunnel": self.use_ssh_tunnel,
+ "tunnel_host": self.tunnel_host,
+ "tunnel_port": self.tunnel_port,
+ "tunnel_authentication": self.tunnel_authentication,
+ "tunnel_identity_file": self.tunnel_identity_file,
+ "tunnel_password": self.tunnel_password
+ }
+
class ModulePreference(db.Model):
"""Define a preferences table for any modules."""
diff --git a/web/pgadmin/static/js/sqleditor/new_connection_dialog.js b/web/pgadmin/static/js/sqleditor/new_connection_dialog.js
new file mode 100644
index 0000000..dc1c064
--- /dev/null
+++ b/web/pgadmin/static/js/sqleditor/new_connection_dialog.js
@@ -0,0 +1,262 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2020, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import gettext from 'sources/gettext';
+import url_for from 'sources/url_for';
+import $ from 'jquery';
+import Alertify from 'pgadmin.alertifyjs';
+import pgAdmin from 'sources/pgadmin';
+import Backform from 'pgadmin.backform';
+import newConnectionDialogModel from 'sources/sqleditor/new_connection_dialog_model';
+
+
+let NewConnectionDialog = {
+ 'dialog': function(handler, reconnect) {
+ let url = url_for('sqleditor.get_new_connection_data', {
+ 'sid': handler.url_params.sid,
+ 'sgid': handler.url_params.sgid,
+ });
+
+ if(reconnect) {
+ url += '?connect=1';
+ }
+
+ let title = gettext('Connect to server');
+
+ $.ajax({
+ url: url,
+ headers: {
+ 'Cache-Control' : 'no-cache',
+ },
+ }).done(function (res) {
+ let response = res.data.result;
+ response.database_list = [];
+ response.user_list = [];
+ if (Alertify.newConnectionDialog) {
+ delete Alertify.newConnectionDialog;
+ }
+
+ // Create Dialog
+ Alertify.dialog('newConnectionDialog', function factory() {
+ let $container = $('<div class=\'new-connection-dialog\'></div>');
+ return {
+ main: function(message) {
+ this.msg = message;
+ },
+ build: function() {
+ this.elements.content.appendChild($container.get(0));
+ Alertify.pgDialogBuild.apply(this);
+ },
+ setup: function(){
+ return {
+ buttons: [
+ {
+ text: '',
+ key: 112,
+ className: 'btn btn-primary-icon pull-left fa fa-question pg-alertify-icon-button',
+ attrs: {
+ name: 'dialog_help',
+ type: 'button',
+ label: gettext('Help'),
+ 'aria-label': gettext('Help'),
+ url: url_for('help.static', {
+ 'filename': 'query_tool.html',
+ }),
+ },
+ },
+ {
+ text: gettext('Cancel'),
+ key: 27,
+ className: 'btn btn-secondary fa fa-times pg-alertify-button',
+ 'data-btn-name': 'cancel',
+ }, {
+ text: gettext('OK'),
+ key: 13,
+ className: 'btn btn-primary fa fa-check pg-alertify-button',
+ 'data-btn-name': 'ok',
+ },
+ ],
+ // Set options for dialog
+ options: {
+ title: title,
+ //disable both padding and overflow control.
+ padding: !1,
+ overflow: !1,
+ model: 0,
+ resizable: true,
+ maximizable: false,
+ pinnable: false,
+ closableByDimmer: false,
+ modal: false,
+ autoReset: false,
+ closable: true,
+ },
+ };
+ },
+ prepare: function() {
+ let self = this;
+ $container.html('');
+ // Disable Ok button
+ this.__internal.buttons[2].element.disabled = true;
+
+ // Status bar
+ this.statusBar = $(
+ '<div class=\'pg-prop-status-bar pg-el-xs-12 d-none\'>' +
+ ' <div class="error-in-footer"> ' +
+ ' <div class="d-flex px-2 py-1"> ' +
+ ' <div class="pr-2"> ' +
+ ' <i class="fa fa-exclamation-triangle text-danger" aria-hidden="true"></i> ' +
+ ' </div> ' +
+ ' <div class="alert-text" role="alert"></div> ' +
+ ' </div> ' +
+ ' </div> ' +
+ '</div>').appendTo($container);
+
+ // To show progress on filter Saving/Updating on AJAX
+ this.showNewConnectionProgress = $(
+ `<div id="show_filter_progress" class="pg-sp-container sql-editor-busy-fetching d-none">
+ <div class="pg-sp-content">
+ <div class="row"><div class="col-12 pg-sp-icon sql-editor-busy-icon"></div></div>
+ <div class="row"><div class="col-12 pg-sp-text sql-editor-busy-text">` + gettext('Loading data...') + `</div></div>
+ </div>
+ </div>`
+ ).appendTo($container);
+ $(
+ self.showNewConnectionProgress[0]
+ ).removeClass('d-none');
+
+ self.newConnCollectionModel = newConnectionDialogModel(response, handler.url_params.sgid, handler.url_params.sid);
+ let fields = Backform.generateViewSchema(null, self.newConnCollectionModel, 'create', null, null, true);
+
+ let view = this.view = new Backform.Dialog({
+ el: '<div></div>',
+ model: self.newConnCollectionModel,
+ schema: fields,
+ });
+
+ $(this.elements.body.childNodes[0]).addClass(
+ 'alertify_tools_dialog_properties obj_properties'
+ );
+
+ $container.append(view.render().$el);
+
+ // Enable/disable save button and show/hide statusbar based on session
+ view.listenTo(view.model, 'pgadmin-session:start', function() {
+ view.listenTo(view.model, 'pgadmin-session:invalid', function(msg) {
+ self.statusBar.removeClass('d-none');
+ $(self.statusBar.find('.alert-text')).html(msg);
+ // Disable Okay button
+ self.__internal.buttons[2].element.disabled = true;
+ });
+
+ view.listenTo(view.model, 'pgadmin-session:valid', function() {
+ self.statusBar.addClass('d-none');
+ $(self.statusBar.find('.alert-text')).html('');
+ // Enable Okay button
+ self.__internal.buttons[2].element.disabled = false;
+ });
+ });
+
+ view.listenTo(view.model, 'pgadmin-session:stop', function() {
+ view.stopListening(view.model, 'pgadmin-session:invalid');
+ view.stopListening(view.model, 'pgadmin-session:valid');
+ });
+
+ // Starts monitoring changes to model
+ view.model.startNewSession();
+
+ // Hide Progress ...
+ $(
+ self.showNewConnectionProgress[0]
+ ).addClass('d-none');
+ },
+ callback: function(e) {
+ let self = this;
+ if (e.button.element.name == 'dialog_help') {
+ e.cancel = true;
+ pgAdmin.Browser.showHelp(e.button.element.name, e.button.element.getAttribute('url'),
+ null, null);
+ return;
+ } else if (e.button['data-btn-name'] === 'ok') {
+ e.cancel = true; // Do not close dialog
+ let newConnCollectionModel = this.newConnCollectionModel.toJSON();
+
+ let selected_database_name = null;
+ response.database_list.forEach(function(data){
+ if(newConnCollectionModel['database'] == data['value']) {
+ selected_database_name = data['label'];
+ return false;
+ }
+ });
+ let title = '';
+ if(newConnCollectionModel['role']) {
+ title = selected_database_name + '/' + newConnCollectionModel['role'] + '@' + response.server_name;
+ } else {
+ title = selected_database_name + '/' + newConnCollectionModel['user'] + '@' + response.server_name;
+ newConnCollectionModel['role'] = null;
+ }
+
+ let is_create_connection = true;
+
+ handler.gridView.connection_list.forEach(function(connection_data){
+ if(parseInt(connection_data['server']) == newConnCollectionModel['server']
+ && parseInt(connection_data['database']) == newConnCollectionModel['database']
+ && connection_data['user'] == newConnCollectionModel['user'] && connection_data['role'] == newConnCollectionModel['role']) {
+ is_create_connection = false;
+ // break for loop by return false.
+ return false;
+ }
+
+ if(title == connection_data['title']) {
+ is_create_connection = false;
+ return false;
+ }
+ });
+ if(!is_create_connection) {
+ let errmsg = 'Connection with this configuration already present.';
+ Alertify.info(errmsg);
+ }else {
+ let connection_details = {
+ 'server_group': handler.gridView.handler.url_params.sgid,
+ 'server': newConnCollectionModel['server'],
+ 'database': newConnCollectionModel['database'],
+ 'title': title,
+ 'user': newConnCollectionModel['user'],
+ 'role': newConnCollectionModel['role'],
+ 'password': response.password,
+ };
+ handler.gridView.on_change_connection(connection_details, self);
+ }
+ } else {
+ self.close();
+ }
+ },
+ };
+ });
+ setTimeout(function(){
+ Alertify.newConnectionDialog('Connect to server.').resizeTo(pgAdmin.Browser.stdW.md,pgAdmin.Browser.stdH.md);
+ }, 500);
+ }).fail(function(error) {
+ Alertify.alert().setting({
+ 'title': gettext('Connection lost'),
+ 'label':gettext('Ok'),
+ 'message': gettext('Connection to the server has been lost.'),
+ 'onok': function(){
+ alert(error);
+ //Close the window after connection is lost
+ window.close();
+ },
+ }).show();
+ });
+
+ },
+
+};
+
+module.exports = NewConnectionDialog;
diff --git a/web/pgadmin/static/js/sqleditor/new_connection_dialog_model.js b/web/pgadmin/static/js/sqleditor/new_connection_dialog_model.js
new file mode 100644
index 0000000..1cba6e6
--- /dev/null
+++ b/web/pgadmin/static/js/sqleditor/new_connection_dialog_model.js
@@ -0,0 +1,339 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2020, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import gettext from 'sources/gettext';
+import _ from 'underscore';
+import $ from 'jquery';
+import pgAdmin from 'sources/pgadmin';
+import Backform from 'pgadmin.backform';
+import url_for from 'sources/url_for';
+import alertify from 'pgadmin.alertifyjs';
+
+export default function newConnectionDialogModel(response, sgid, sid) {
+
+ let server_name = '';
+ let database_name = '';
+
+ let NewConnectionSelect2Control = Backform.Select2Control.extend({
+ fetchData: function(){
+ let self = this;
+ url = self.field.get('url');
+
+ let url = url_for(url, {
+ 'sid': self.model.attributes.server,
+ 'sgid': sgid,
+ });
+
+ $.ajax({
+ async: false,
+ url: url,
+ headers: {
+ 'Cache-Control' : 'no-cache',
+ },
+ }).done(function (res) {
+ var transform = self.field.get('transform');
+ if(res.data.status){
+ let data = res.data.result.data;
+
+ if (transform && _.isFunction(transform)) {
+ self.field.set('options', transform.bind(self, data));
+ } else {
+ self.field.set('options', data);
+ }
+ } else {
+ if (transform && _.isFunction(transform)) {
+ self.field.set('options', transform.bind(self, []));
+ } else {
+ self.field.set('options', []);
+ }
+ //alertify.error(res.data.msg);
+ }
+ }).fail(function(e){
+ let msg = '';
+ if(e.status == 404) {
+ msg = 'Unable to find url.';
+ } else {
+ msg = e.responseJSON.errormsg;
+ }
+ alertify.error(msg);
+ });
+ },
+ render: function() {
+ this.fetchData();
+ return Backform.Select2Control.prototype.render.apply(this, arguments);
+ },
+ onChange: function() {
+ Backform.Select2Control.prototype.onChange.apply(this, arguments);
+ },
+ });
+
+ let newConnectionModel = pgAdmin.Browser.DataModel.extend({
+ idAttribute: 'name',
+ defaults: {
+ server: parseInt(sid),
+ database: null,
+ user: null,
+ password: null,
+ server_name: server_name,
+ database_name: database_name,
+ },
+ schema: [{
+ id: 'server',
+ name: 'server',
+ label: gettext('Server'),
+ type: 'text',
+ editable: true,
+ disabled: false,
+ select2: {
+ allowClear: false,
+ },
+ control: Backform.Select2Control.extend({
+ connect: function(self) {
+ /*if (alertify.connectServer) {
+ delete alertify.connectServer;
+ }*/
+ if(!alertify.connectServer){
+ alertify.dialog('connectServer', function factory() {
+ return {
+ main: function(
+ title, message, sid, submit_password=true
+ ) {
+ this.set('title', title);
+ this.message = message;
+ this.server_id = sid;
+ this.submit_password = submit_password;
+ },
+ setup:function() {
+ return {
+ buttons:[{
+ text: gettext('Cancel'), className: 'btn btn-secondary fa fa-times pg-alertify-button',
+ key: 27,
+ },{
+ text: gettext('OK'), key: 13, className: 'btn btn-primary fa fa-check pg-alertify-button',
+ }],
+ focus: {element: '#password', select: true},
+ options: {
+ modal: 0, resizable: false, maximizable: false, pinnable: false,
+ },
+ };
+ },
+ build:function() {
+ },
+ prepare:function() {
+ this.setContent(this.message);
+ },
+ callback: function(closeEvent) {
+
+ if (closeEvent.button.text == gettext('OK')) {
+ if(this.submit_password) {
+ var _url = url_for('schema_diff.connect_server', {'sid': this.server_id});
+
+ $.ajax({
+ type: 'POST',
+ timeout: 30000,
+ url: _url,
+ data: $('#frmPassword').serialize(),
+ })
+ .done(function() {
+ self.model.attributes.database = null;
+ self.model.attributes.user = null;
+ self.model.attributes.role = null;
+ Backform.Select2Control.prototype.onChange.apply(self, arguments);
+ response.server_list.forEach(function(obj){
+ if(obj.id==self.model.changed.server) {
+ response.server_name = obj.name;
+ }
+ });
+ })
+ .fail(function(xhr) {
+ alertify.connectServer('Connect to server', xhr.responseJSON.result, self.getValueFromDOM());
+ });
+ } else {
+ response.password = $('#password').val();
+ }
+ } else {
+ self.model.attributes.database = null;
+ self.model.attributes.user = null;
+ self.model.attributes.role = null;
+ Backform.Select2Control.prototype.onChange.apply(self, arguments);
+ }
+ closeEvent.close = true;
+ },
+ };
+ });
+ }
+ },
+ render: function() {
+ let self = this;
+ self.connect(self);
+ return Backform.Select2Control.prototype.render.apply(self, arguments);
+ },
+ onChange: function() {
+ this.model.attributes.database = null;
+ this.model.attributes.user = null;
+ let self = this;
+ self.connect(self);
+
+ let url = url_for('sqleditor.connect_server', {
+ 'sid': self.getValueFromDOM(),
+ });
+ $.ajax({
+ async: false,
+ url: url,
+ headers: {
+ 'Cache-Control' : 'no-cache',
+ },
+ }).done(function () {
+ Backform.Select2Control.prototype.onChange.apply(self, arguments);
+ response.server_list.forEach(function(obj){
+ if(obj.id==self.model.changed.server) {
+ response.server_name = obj.name;
+ }
+ });
+ }).fail(function(xhr){
+ alertify.connectServer('Connect to server', xhr.responseJSON.result, self.getValueFromDOM());
+ });
+
+ },
+ }),
+ options: function() {
+ return _.map(response.server_list, (obj) => {
+ if (obj.id == parseInt(sid))
+ response.server_name = obj.name;
+
+ return {
+ value: obj.id,
+ label: obj.name,
+ };
+ });
+ },
+ },
+ {
+ id: 'database',
+ name: 'database',
+ label: gettext('Database'),
+ type: 'text',
+ editable: true,
+ disabled: function(m) {
+ let self_local = this;
+ if (!_.isUndefined(m.get('server')) && !_.isNull(m.get('server'))
+ && m.get('server') !== '') {
+ setTimeout(function() {
+ if(self_local.options.length) {
+ m.set('database', self_local.options[0].value);
+ }
+ }, 10);
+ return false;
+ }
+
+ return true;
+ },
+ deps: ['server'],
+ url: 'sqleditor.get_new_connection_database',
+ select2: {
+ allowClear: false,
+ width: '100%',
+ first_empty: true,
+ select_first: false,
+ },
+ extraClasses:['new-connection-dialog-style'],
+ control: NewConnectionSelect2Control,
+ transform: function(data) {
+ response.database_list = data;
+ return data;
+ },
+ },
+ {
+ id: 'user',
+ name: 'user',
+ label: gettext('User'),
+ type: 'text',
+ editable: true,
+ deps: ['server'],
+ select2: {
+ allowClear: false,
+ width: '100%',
+ },
+ control: NewConnectionSelect2Control,
+ url: 'sqleditor.get_new_connection_user',
+ disabled: function(m) {
+ let self_local = this;
+ if (!_.isUndefined(m.get('server')) && !_.isNull(m.get('server'))
+ && m.get('server') !== '') {
+ setTimeout(function() {
+ if(self_local.options.length) {
+ m.set('user', self_local.options[0].value);
+ }
+ }, 10);
+ return false;
+ }
+ return true;
+ },
+ },{
+ id: 'role',
+ name: 'role',
+ label: gettext('Role'),
+ type: 'text',
+ editable: true,
+ deps: ['server'],
+ select2: {
+ allowClear: false,
+ width: '100%',
+ first_empty: true,
+ },
+ control: NewConnectionSelect2Control,
+ url: 'sqleditor.get_new_connection_role',
+ disabled: false,
+ },
+ /*{
+ id: 'password',
+ name: 'password',
+ label: gettext('Password'tools/sqleditor/__init__.py),
+ type: 'password',
+ editable: true,
+ disabled: true,
+ deps: ['user'],
+ control: Backform.InputControl.extend({
+ render: function() {
+ let self = this;
+ self.model.attributes.password = null;
+ Backform.InputControl.prototype.render.apply(self, arguments);
+ return self;
+ },
+ onChange: function() {
+ let self = this;
+ Backform.InputControl.prototype.onChange.apply(self, arguments);
+ },
+ }),
+ },*/
+ ],
+ validate: function() {
+ let msg = null;
+ this.errorModel.clear();
+ if(_.isUndefined(this.get('database')) || _.isNull(this.get('database'))){
+ msg = gettext('Please select database');
+ this.errorModel.set('database', msg);
+ return msg;
+ } else if(_.isUndefined(this.get('database')) || _.isUndefined(this.get('user'))|| _.isNull(this.get('user'))) {
+ msg = gettext('Please select user');
+ this.errorModel.set('user', msg);
+ return msg;
+ }
+ /*else if((this.attributes.password == '' || _.isUndefined(this.get('password')) || _.isNull(this.get('password')))) {
+ msg = gettext('Please enter password');
+ this.errorModel.set('password', msg);
+ return msg;
+ }*/
+ return null;
+ },
+ });
+
+ let model = new newConnectionModel();
+ return model;
+}
diff --git a/web/pgadmin/static/scss/_alert.scss b/web/pgadmin/static/scss/_alert.scss
index dac552b..836f0af 100644
--- a/web/pgadmin/static/scss/_alert.scss
+++ b/web/pgadmin/static/scss/_alert.scss
@@ -92,6 +92,7 @@
right: 0;
left: 0;
bottom: 0;
+ z-index: 1;
}
.pg-prop-status-bar {
diff --git a/web/pgadmin/tools/datagrid/__init__.py b/web/pgadmin/tools/datagrid/__init__.py
index 1bd841f..f7b836a 100644
--- a/web/pgadmin/tools/datagrid/__init__.py
+++ b/web/pgadmin/tools/datagrid/__init__.py
@@ -18,22 +18,23 @@ from flask import Response, url_for, session, request, make_response
from werkzeug.useragents import UserAgent
from flask import current_app as app, render_template
from flask_babelex import gettext
-from flask_security import login_required
+from flask_security import login_required, current_user
from pgadmin.tools.sqleditor.command import ObjectRegistry, SQLFilter
+from pgadmin.tools.sqleditor import check_transaction_status
from pgadmin.utils import PgAdminModule
from pgadmin.utils.ajax import make_json_response, bad_request, \
- internal_server_error
+ internal_server_error, unauthorized
from config import PG_DEFAULT_DRIVER
-from pgadmin.model import Server
+from pgadmin.model import Server, User
from pgadmin.utils.driver import get_driver
from pgadmin.utils.exception import ConnectionLost, SSHTunnelConnectionLost
from pgadmin.utils.preferences import Preferences
from pgadmin.settings import get_setting
from pgadmin.browser.utils import underscore_unescape
from pgadmin.utils.exception import ObjectGone
-from pgadmin.utils.constants import MIMETYPE_APP_JS
from pgadmin.tools.sqleditor.utils.macros import get_user_macros
+from pgadmin.utils.constants import MIMETYPE_APP_JS, UNAUTH_REQ
MODULE_NAME = 'datagrid'
@@ -74,7 +75,8 @@ class DataGridModule(PgAdminModule):
'datagrid.filter_validate',
'datagrid.filter',
'datagrid.panel',
- 'datagrid.close'
+ 'datagrid.close',
+ 'datagrid.update_query_tool_connection'
]
def on_logout(self, user):
@@ -324,10 +326,48 @@ def initialize_query_tool(trans_id, sgid, sid, did=None):
req_args['recreate'] == '1'):
connect = False
+ is_error, errmsg, conn_id, version = _init_query_tool(trans_id, connect,
+ sgid, sid, did)
+ if is_error:
+ return errmsg
+
+ return make_json_response(
+ data={
+ 'connId': str(conn_id),
+ 'serverVersion': version,
+ }
+ )
+
+
+def _connect(conn, **kwargs):
+ """
+ Connect the database.
+ :param conn: Connection instance.
+ :param kwargs: user, role and password data from user.
+ :return:
+ """
+ user = None
+ role = None
+ password = None
+ is_ask_password = False
+ if 'user' in kwargs and 'role' in kwargs:
+ user = kwargs['user']
+ role = kwargs['role'] if kwargs['role'] else None
+ password = kwargs['password'] if kwargs['password'] else None
+ is_ask_password = True
+ if user:
+ status, msg = conn.connect(user=user, role=role,
+ password=password)
+ else:
+ status, msg = conn.connect()
+
+ return status, msg, is_ask_password, user, role, password
+
+
+def _init_query_tool(trans_id, connect, sgid, sid, did, **kwargs):
# Create asynchronous connection using random connection id.
conn_id = str(random.randint(1, 9999999))
- # Use Maintenance database OID
manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)
if did is None:
@@ -338,24 +378,41 @@ def initialize_query_tool(trans_id, sgid, sid, did=None):
)
except Exception as e:
app.logger.error(e)
- return internal_server_error(errormsg=str(e))
+ return True, internal_server_error(errormsg=str(e)), '', ''
try:
conn = manager.connection(did=did, conn_id=conn_id,
auto_reconnect=False,
use_binary_placeholder=True,
array_to_string=True)
+
if connect:
- status, msg = conn.connect()
+ status, msg, is_ask_password, user, role, password = _connect(
+ conn, **kwargs)
if not status:
app.logger.error(msg)
- return internal_server_error(errormsg=str(msg))
+ if is_ask_password:
+ server = Server.query.filter_by(id=sid).first()
+ return True, make_json_response(
+ success=0,
+ status=428,
+ result=render_template(
+ 'servers/password.html',
+ server_label=server.name,
+ username=user,
+ errmsg=msg,
+ _=gettext,
+ )
+ ), '', ''
+ else:
+ return True, internal_server_error(
+ errormsg=str(msg)), '', ''
except (ConnectionLost, SSHTunnelConnectionLost) as e:
app.logger.error(e)
raise
except Exception as e:
app.logger.error(e)
- return internal_server_error(errormsg=str(e))
+ return True, internal_server_error(errormsg=str(e)), '', ''
if 'gridData' not in session:
sql_grid_data = dict()
@@ -377,10 +434,77 @@ def initialize_query_tool(trans_id, sgid, sid, did=None):
# Store the grid dictionary into the session variable
session['gridData'] = sql_grid_data
+ return False, '', conn_id, manager.version
+
+
[email protected](
+ '/initialize/query_tool/update_connection/<int:trans_id>/'
+ '<int:sgid>/<int:sid>/<int:did>',
+ methods=["POST"], endpoint='update_query_tool_connection'
+)
+def update_query_tool_connection(trans_id, sgid, sid, did):
+ # Remove transaction Id.
+ with query_tool_close_session_lock:
+ data = json.loads(request.data, encoding='utf-8')
+
+ if 'gridData' not in session:
+ return make_json_response(data={'status': True})
+
+ grid_data = session['gridData']
+
+ # Return from the function if transaction id not found
+ if str(trans_id) not in grid_data:
+ return make_json_response(data={'status': True})
+
+ connect = True
+
+ req_args = request.args
+ if ('recreate' in req_args and
+ req_args['recreate'] == '1'):
+ connect = False
+
+ new_trans_id = str(random.randint(1, 9999999))
+ kwargs = {
+ 'user': data['user'],
+ 'role': data['role'],
+ 'password': data['password'] if 'password' in data else None
+ }
+
+ is_error, errmsg, conn_id, version = _init_query_tool(
+ new_trans_id, connect, sgid, sid, did, **kwargs)
+
+ if is_error:
+ return errmsg
+ else:
+ try:
+ # Check the transaction and connection status
+ status, error_msg, conn, trans_obj, session_obj = \
+ check_transaction_status(trans_id)
+
+ status, error_msg, new_conn, new_trans_obj, new_session_obj = \
+ check_transaction_status(new_trans_id)
+
+ new_session_obj['primary_keys'] = session_obj[
+ 'primary_keys'] if 'primary_keys' in session_obj else None
+ new_session_obj['columns_info'] = session_obj[
+ 'columns_info'] if 'columns_info' in session_obj else None
+ new_session_obj['client_primary_key'] = session_obj[
+ 'client_primary_key'] if 'client_primary_key'\
+ in session_obj else None
+
+ close_query_tool_session(trans_id)
+ # Remove the information of unique transaction id from the
+ # session variable.
+ grid_data.pop(str(trans_id), None)
+ session['gridData'] = grid_data
+ except Exception as e:
+ app.logger.error(e)
+
return make_json_response(
data={
'connId': str(conn_id),
- 'serverVersion': manager.version,
+ 'serverVersion': version,
+ 'tran_id': new_trans_id
}
)
diff --git a/web/pgadmin/tools/datagrid/templates/datagrid/index.html b/web/pgadmin/tools/datagrid/templates/datagrid/index.html
index a0eebc8..4970027 100644
--- a/web/pgadmin/tools/datagrid/templates/datagrid/index.html
+++ b/web/pgadmin/tools/datagrid/templates/datagrid/index.html
@@ -417,8 +417,17 @@
title="" role="img">
</i>
</div>
- <div class="editor-title"
- style="background-color: {% if fgcolor %}{{ bgcolor or '#FFFFFF' }}{% endif %}; color: {% if fgcolor %}{{ fgcolor }}{% endif %};"> </div>
+ <div class="connection-info btn-group mr-1" role="group" aria-label="">
+ <div class="editor-title" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"
+ style="background-color: {% if fgcolor %}{{ bgcolor or '#FFFFFF' }}{% endif %}; color: {% if fgcolor %}{{ fgcolor }}{% endif %};">
+ </div>
+ <span class="conn-info-dd dropdown-toggle dropdown-toggle-split"
+ data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></span>
+ <ul class="dropdown-menu" id="connections-list">
+ </ul>
+ </div>
+
+
</div>
<div id="editor-panel" tabindex="0">
<div id="fetching_data" class="pg-sp-container sql-editor-busy-fetching">
@@ -481,6 +490,7 @@ require(['sources/generated/browser_nodes', 'sources/generated/codemirror', 'sou
var script_type_url = '';
{% endif %}
// Start the query tool.
+
sqlEditorController.start(
{{ uniqueId }},
{{ url_params|safe}},
diff --git a/web/pgadmin/tools/datagrid/tests/__init__.py b/web/pgadmin/tools/datagrid/tests/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/web/pgadmin/tools/datagrid/tests/datagrid_test_data.json b/web/pgadmin/tools/datagrid/tests/datagrid_test_data.json
new file mode 100644
index 0000000..0075f35
--- /dev/null
+++ b/web/pgadmin/tools/datagrid/tests/datagrid_test_data.json
@@ -0,0 +1,134 @@
+{
+ "data_grid_init_query_tool": [
+ {
+ "name": "Datagrid init query tool",
+ "url": "/datagrid/initialize/query_tool/",
+ "is_positive_test": true,
+ "mocking_required": false,
+ "test_data": {},
+ "mock_data": {},
+ "expected_data": {
+ "status_code": 200
+ }
+ }
+ ],
+ "data_grid_query_tool_close": [
+ {
+ "name": "Datagrid query tool close",
+ "url": "/datagrid/close/",
+ "is_positive_test": true,
+ "mocking_required": false,
+ "test_data": {},
+ "mock_data": {},
+ "expected_data": {
+ "status_code": 200
+ }
+ }
+ ],
+ "data_grid_validate_filter": [
+ {
+ "name": "Datagrid validate filter",
+ "url": "/datagrid/filter/validate/",
+ "is_positive_test": true,
+ "mocking_required": false,
+ "test_data": "id = 1",
+ "mock_data": {},
+ "expected_data": {
+ "status_code": 200
+ }
+ },
+ {
+ "name": "Datagrid validate filter",
+ "url": "/datagrid/filter/validate/",
+ "is_positive_test": false,
+ "mocking_required": true,
+ "test_data": "id = 1",
+ "mock_data": {
+ "function_name": "pgadmin.utils.driver.psycopg2.connection.Connection.execute_scalar",
+ "return_value": "(False, 'Mocked Internal Server Error while validate filter')"
+ },
+ "expected_data": {
+ "status_code": 200
+ }
+ }
+ ],
+ "data_grid_update_connection": [
+ {
+ "name": "Datagrid update connection positive",
+ "url": "/datagrid/initialize/query_tool/update_connection/",
+ "is_positive_test": true,
+ "mocking_required": false,
+ "is_create_role": false,
+ "test_data": {},
+ "mock_data": {},
+ "expected_data": {
+ "status_code": 200
+ }
+ },
+ {
+ "name": "Datagrid update connection with new user",
+ "url": "/datagrid/initialize/query_tool/update_connection/",
+ "is_positive_test": true,
+ "mocking_required": false,
+ "is_create_role": true,
+ "test_data": {},
+ "mock_data": {},
+ "expected_data": {
+ "status_code": 200
+ }
+ }
+ ],
+ "data_grid_panel": [
+ {
+ "name": "Datagrid Panel",
+ "url": "/datagrid/panel/",
+ "is_positive_test": true,
+ "mocking_required": false,
+ "test_data": {},
+ "mock_data": {
+ },
+ "expected_data": {
+ "status_code": 200
+ }
+ }
+ ],
+ "data_grid_initialize": [
+ {
+ "name": "Datagrid Initialize",
+ "url": "/datagrid/initialize/datagrid/",
+ "is_positive_test": true,
+ "mocking_required": false,
+ "test_data": "id=1",
+ "mock_data": {
+
+ },
+ "expected_data": {
+ "status_code": 200
+ }
+ },{
+ "name": "Datagrid Initialize",
+ "url": "/datagrid/initialize/datagrid/",
+ "is_positive_test": true,
+ "mocking_required": false,
+ "test_data": null,
+ "mock_data": {},
+ "expected_data": {
+ "status_code": 200
+ }
+ },
+ {
+ "name": "Datagrid Initialize",
+ "url": "/datagrid/initialize/datagrid/",
+ "is_positive_test": false,
+ "mocking_required": true,
+ "test_data": "id=1",
+ "mock_data": {
+ "function_name": "pgadmin.utils.driver.psycopg2.connection.Connection.execute_dict",
+ "return_value": "(False, 'Mocked Internal Server Error while initialize datagrid.')"
+ },
+ "expected_data": {
+ "status_code": 500
+ }
+ }
+ ]
+}
diff --git a/web/pgadmin/tools/datagrid/tests/test_data_grid_init_query_tool.py b/web/pgadmin/tools/datagrid/tests/test_data_grid_init_query_tool.py
new file mode 100644
index 0000000..6ecf5de
--- /dev/null
+++ b/web/pgadmin/tools/datagrid/tests/test_data_grid_init_query_tool.py
@@ -0,0 +1,72 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+
+import json
+import uuid
+import random
+
+from unittest.mock import patch
+from pgadmin.browser.server_groups.servers.databases.tests import utils as \
+ database_utils
+
+from pgadmin.utils.route import BaseTestGenerator
+from regression import parent_node_dict
+from regression.python_test_utils import test_utils as utils
+from regression.test_setup import config_data
+from . import utils as data_grid_utils
+
+
+class DatagridInitQueryToolTestCase(BaseTestGenerator):
+ """
+ This will init query-tool connection.
+ """
+
+ scenarios = utils.generate_scenarios(
+ 'data_grid_init_query_tool',
+ data_grid_utils.test_cases
+ )
+
+ def setUp(self):
+ self.database_info = parent_node_dict["database"][-1]
+ self.db_name = self.database_info["db_name"]
+ self.did = self.database_info["db_id"]
+ self.sid = parent_node_dict["server"][-1]["server_id"]
+ self.sgid = config_data['server_group']
+
+ db_con = database_utils.connect_database(self, utils.SERVER_GROUP,
+ self.sid, self.did)
+
+ self.trans_id = str(random.randint(1, 9999999))
+
+ if not db_con['data']["connected"]:
+ raise Exception("Could not connect to database to add a table.")
+
+ def init_query_tool(self):
+ response = self.tester.post(
+ self.url + str(self.trans_id) + '/' + str(self.sgid) + '/' + str(
+ self.sid) + '/' + str(self.did),
+ content_type='html/json'
+ )
+ return response
+
+ def runTest(self):
+ """ This function will init query tool connection."""
+
+ if self.is_positive_test:
+ response = self.init_query_tool()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+
+ self.assertEqual(actual_response_code, expected_response_code)
+
+ def tearDown(self):
+ """This function disconnect database."""
+ database_utils.disconnect_database(self, self.sid,
+ self.did)
diff --git a/web/pgadmin/tools/datagrid/tests/test_data_grid_panel.py b/web/pgadmin/tools/datagrid/tests/test_data_grid_panel.py
new file mode 100644
index 0000000..ae8ec10
--- /dev/null
+++ b/web/pgadmin/tools/datagrid/tests/test_data_grid_panel.py
@@ -0,0 +1,93 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+
+import json
+import uuid
+import random
+
+from unittest.mock import patch
+from pgadmin.browser.server_groups.servers.databases.tests import utils as \
+ database_utils
+
+from pgadmin.utils.route import BaseTestGenerator
+from regression import parent_node_dict
+from regression.python_test_utils import test_utils as utils
+from regression.test_setup import config_data
+from pgadmin.browser.server_groups.servers.databases.schemas.tests import \
+ utils as schema_utils
+from pgadmin.browser.server_groups.servers.databases.schemas.tables.tests \
+ import utils as tables_utils
+from . import utils as data_grid_utils
+
+
+class DatagridPanelTestCase(BaseTestGenerator):
+ """
+ This will data grid panel.
+ """
+
+ scenarios = utils.generate_scenarios(
+ 'data_grid_panel',
+ data_grid_utils.test_cases
+ )
+
+ def setUp(self):
+ self.database_info = parent_node_dict["database"][-1]
+ self.db_name = self.database_info["db_name"]
+ self.did = self.database_info["db_id"]
+ self.sid = parent_node_dict["server"][-1]["server_id"]
+ self.sgid = config_data['server_group']
+
+ db_con = database_utils.connect_database(self, utils.SERVER_GROUP,
+ self.sid, self.did)
+ if not db_con['data']["connected"]:
+ raise Exception("Could not connect to database to add a table.")
+
+ self.trans_id = str(random.randint(1, 9999999))
+ qt_init = data_grid_utils._init_query_tool(self, self.trans_id,
+ self.sgid, self.sid,
+ self.did)
+
+ if not qt_init['success']:
+ raise Exception("Could not initialize querty tool.")
+
+ def panel(self):
+ query_param = \
+ '?is_query_tool={0}&sgid={1}&sid={2}&server_type={3}' \
+ '&did={4}&title={5}'.format(True, self.sgid, self.sid,
+ self.server_information['type'],
+ self.did, 'Query panel')
+
+ response = self.tester.post(
+ self.url + str(self.trans_id) + query_param,
+ data=json.dumps(self.test_data),
+ content_type='html/json'
+ )
+ return response
+
+ def runTest(self):
+ """ This function will update query tool connection."""
+
+ if self.is_positive_test:
+ response = self.panel()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+ else:
+ with patch(self.mock_data["function_name"],
+ return_value=eval(self.mock_data["return_value"])):
+ response = self.panel()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+
+ self.assertEqual(actual_response_code, expected_response_code)
+
+ def tearDown(self):
+ """This function disconnect database."""
+ database_utils.disconnect_database(self, self.sid,
+ self.did)
diff --git a/web/pgadmin/tools/datagrid/tests/test_data_grid_query_tool_close.py b/web/pgadmin/tools/datagrid/tests/test_data_grid_query_tool_close.py
new file mode 100644
index 0000000..822c2e1
--- /dev/null
+++ b/web/pgadmin/tools/datagrid/tests/test_data_grid_query_tool_close.py
@@ -0,0 +1,77 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+
+import json
+import uuid
+import random
+
+from unittest.mock import patch
+from pgadmin.browser.server_groups.servers.databases.tests import utils as \
+ database_utils
+
+from pgadmin.utils.route import BaseTestGenerator
+from regression import parent_node_dict
+from regression.python_test_utils import test_utils as utils
+from regression.test_setup import config_data
+from . import utils as data_grid_utils
+
+
+class DatagridQueryToolCloseTestCase(BaseTestGenerator):
+ """
+ This will close query-tool connection.
+ """
+
+ scenarios = utils.generate_scenarios(
+ 'data_grid_query_tool_close',
+ data_grid_utils.test_cases
+ )
+
+ def setUp(self):
+ self.database_info = parent_node_dict["database"][-1]
+ self.db_name = self.database_info["db_name"]
+ self.did = self.database_info["db_id"]
+ self.sid = parent_node_dict["server"][-1]["server_id"]
+ self.sgid = config_data['server_group']
+
+ db_con = database_utils.connect_database(self, utils.SERVER_GROUP,
+ self.sid, self.did)
+
+ if not db_con['data']["connected"]:
+ raise Exception("Could not connect to database to add a table.")
+
+ self.trans_id = str(random.randint(1, 9999999))
+ qt_init = data_grid_utils._init_query_tool(self, self.trans_id,
+ self.sgid, self.sid,
+ self.did)
+
+ if not qt_init['success']:
+ raise Exception("Could not initialize querty tool.")
+
+ def close_connection(self):
+ response = self.tester.delete(
+ self.url + str(self.trans_id),
+ content_type='html/json'
+ )
+ return response
+
+ def runTest(self):
+ """ This function will update query tool connection."""
+
+ if self.is_positive_test:
+ response = self.close_connection()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+
+ self.assertEqual(actual_response_code, expected_response_code)
+
+ def tearDown(self):
+ """This function disconnect database."""
+ database_utils.disconnect_database(self, self.sid,
+ self.did)
diff --git a/web/pgadmin/tools/datagrid/tests/test_data_grid_update_connection.py b/web/pgadmin/tools/datagrid/tests/test_data_grid_update_connection.py
new file mode 100644
index 0000000..5d2b14a
--- /dev/null
+++ b/web/pgadmin/tools/datagrid/tests/test_data_grid_update_connection.py
@@ -0,0 +1,120 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+
+import json
+import uuid
+import random
+
+from unittest.mock import patch
+from pgadmin.browser.server_groups.servers.databases.tests import utils as \
+ database_utils
+
+from pgadmin.utils.route import BaseTestGenerator
+from regression import parent_node_dict
+from regression.python_test_utils import test_utils as utils
+from regression.test_setup import config_data
+from pgadmin.browser.server_groups.servers.roles.tests import \
+ utils as roles_utils
+from . import utils as data_grid_utils
+
+
+class DatagridUpdateConnectionTestCase(BaseTestGenerator):
+ """
+ This will update query-tool connection.
+ """
+
+ scenarios = utils.generate_scenarios(
+ 'data_grid_update_connection',
+ data_grid_utils.test_cases
+ )
+
+ def setUp(self):
+ self.database_info = parent_node_dict["database"][-1]
+ self.db_name = self.database_info["db_name"]
+ self.did = self.database_info["db_id"]
+ self.sid = parent_node_dict["server"][-1]["server_id"]
+ self.sgid = config_data['server_group']
+
+ db_con = database_utils.connect_database(self, utils.SERVER_GROUP,
+ self.sid, self.did)
+
+ self.trans_id = str(random.randint(1, 9999999))
+ self.roles = None
+
+ if self.is_create_role:
+ data = roles_utils.get_role_data(self.server['db_password'])
+ self.role_name = data['rolname']
+ self.role_password = data['rolpassword']
+ roles_utils.create_role_with_password(
+ self.server, self.role_name, self.role_password)
+
+ if not self.is_positive_test or self.is_create_role:
+ qt_init = data_grid_utils._init_query_tool(self, self.trans_id,
+ self.sgid, self.sid,
+ self.did)
+
+ if not qt_init['success']:
+ raise Exception("Could not initialize querty tool.")
+
+ self.test_data = {
+ "database": self.did,
+ "server": self.sid,
+ }
+
+ if self.server_information['type'] == 'ppas':
+ self.test_data['password'] = 'enterprisedb'
+ self.test_data['user'] = 'enterprisedb'
+ else:
+ self.test_data['password'] = 'postgres'
+ self.test_data['user'] = 'postgres'
+
+ if not db_con['data']["connected"]:
+ raise Exception("Could not connect to database to add a table.")
+
+ def update_connection(self, user_data=None):
+ if user_data:
+ response = self.tester.post(
+ self.url + str(self.trans_id) + '/' + str(self.sgid) +
+ '/' + str(self.sid) + '/' + str(self.did),
+ data=json.dumps(user_data),
+ content_type='html/json'
+ )
+ else:
+ response = self.tester.post(
+ self.url + str(self.trans_id) + '/' + str(self.sgid) + '/' +
+ str(self.sid) + '/' + str(self.did),
+ data=json.dumps(self.test_data),
+ content_type='html/json'
+ )
+ return response
+
+ def runTest(self):
+ """ This function will update query tool connection."""
+
+ if self.is_positive_test:
+ user_data = dict()
+ if self.is_create_role:
+ user_data['user'] = self.role_name
+ user_data['password'] = self.role_password
+ user_data['role'] = None
+ response = self.update_connection(user_data=user_data)
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+ else:
+ response = self.update_connection()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+
+ self.assertEqual(actual_response_code, expected_response_code)
+
+ def tearDown(self):
+ """This function disconnect database."""
+ database_utils.disconnect_database(self, self.sid,
+ self.did)
diff --git a/web/pgadmin/tools/datagrid/tests/test_data_grid_validate_filter.py b/web/pgadmin/tools/datagrid/tests/test_data_grid_validate_filter.py
new file mode 100644
index 0000000..0aba5d8
--- /dev/null
+++ b/web/pgadmin/tools/datagrid/tests/test_data_grid_validate_filter.py
@@ -0,0 +1,91 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+
+import json
+import uuid
+import random
+
+from unittest.mock import patch
+from pgadmin.browser.server_groups.servers.databases.tests import utils as \
+ database_utils
+
+from pgadmin.utils.route import BaseTestGenerator
+from regression import parent_node_dict
+from regression.python_test_utils import test_utils as utils
+from regression.test_setup import config_data
+from pgadmin.browser.server_groups.servers.databases.schemas.tests import \
+ utils as schema_utils
+from pgadmin.browser.server_groups.servers.databases.schemas.tables.tests \
+ import utils as tables_utils
+from . import utils as data_grid_utils
+
+
+class DatagridValidateFilterTestCase(BaseTestGenerator):
+ """
+ This will validate filter connection.
+ """
+
+ scenarios = utils.generate_scenarios(
+ 'data_grid_validate_filter',
+ data_grid_utils.test_cases
+ )
+
+ def setUp(self):
+ self.database_info = parent_node_dict["database"][-1]
+ self.db_name = self.database_info["db_name"]
+ self.did = self.database_info["db_id"]
+ self.sid = parent_node_dict["server"][-1]["server_id"]
+ self.sgid = config_data['server_group']
+
+ db_con = database_utils.connect_database(self, utils.SERVER_GROUP,
+ self.sid, self.did)
+ if not db_con['data']["connected"]:
+ raise Exception("Could not connect to database to add a table.")
+ self.schema_id = parent_node_dict['schema'][-1]["schema_id"]
+ self.schema_name = parent_node_dict['schema'][-1]["schema_name"]
+ schema_response = schema_utils.verify_schemas(self.server,
+ self.db_name,
+ self.schema_name)
+ if not schema_response:
+ raise Exception("Could not find the schema to add a table.")
+ self.table_name = "table_for_wizard%s" % (str(uuid.uuid4())[1:8])
+ self.table_id = tables_utils.create_table(self.server, self.db_name,
+ self.schema_name,
+ self.table_name)
+
+ def validate_filter(self):
+ response = self.tester.post(
+ self.url + str(self.sid) + '/' + str(self.did) + '/' +
+ str(self.table_id),
+ data=json.dumps(self.test_data),
+ content_type='html/json'
+ )
+ return response
+
+ def runTest(self):
+ """ This function will update query tool connection."""
+
+ if self.is_positive_test:
+ response = self.validate_filter()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+ else:
+ with patch(self.mock_data["function_name"],
+ return_value=eval(self.mock_data["return_value"])):
+ response = self.validate_filter()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+
+ self.assertEqual(actual_response_code, expected_response_code)
+
+ def tearDown(self):
+ """This function disconnect database."""
+ database_utils.disconnect_database(self, self.sid,
+ self.did)
diff --git a/web/pgadmin/tools/datagrid/tests/test_initialize_data_grid.py b/web/pgadmin/tools/datagrid/tests/test_initialize_data_grid.py
new file mode 100644
index 0000000..130a1d6
--- /dev/null
+++ b/web/pgadmin/tools/datagrid/tests/test_initialize_data_grid.py
@@ -0,0 +1,108 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+
+import json
+import uuid
+import random
+
+from unittest.mock import patch
+from pgadmin.browser.server_groups.servers.databases.tests import utils as \
+ database_utils
+
+from pgadmin.utils.route import BaseTestGenerator
+from regression import parent_node_dict
+from regression.python_test_utils import test_utils as utils
+from regression.test_setup import config_data
+from pgadmin.browser.server_groups.servers.databases.schemas.tests import \
+ utils as schema_utils
+from pgadmin.browser.server_groups.servers.databases.schemas.tables.tests \
+ import utils as tables_utils
+from . import utils as data_grid_utils
+
+
+class DatagridInitializeTestCase(BaseTestGenerator):
+ """
+ This will Initialize datagrid
+ """
+
+ scenarios = utils.generate_scenarios(
+ 'data_grid_initialize',
+ data_grid_utils.test_cases
+ )
+
+ def setUp(self):
+ self.database_info = parent_node_dict["database"][-1]
+ self.db_name = self.database_info["db_name"]
+ self.did = self.database_info["db_id"]
+ self.sid = parent_node_dict["server"][-1]["server_id"]
+ self.sgid = config_data['server_group']
+
+ db_con = database_utils.connect_database(self, utils.SERVER_GROUP,
+ self.sid, self.did)
+ if not db_con['data']["connected"]:
+ raise Exception("Could not connect to database to add a table.")
+
+ self.schema_id = parent_node_dict['schema'][-1]["schema_id"]
+ self.schema_name = parent_node_dict['schema'][-1]["schema_name"]
+ schema_response = schema_utils.verify_schemas(self.server,
+ self.db_name,
+ self.schema_name)
+ if not schema_response:
+ raise Exception("Could not find the schema to add a table.")
+ self.table_name = "table_for_wizard%s" % (str(uuid.uuid4())[1:8])
+ self.table_id = tables_utils.create_table(self.server, self.db_name,
+ self.schema_name,
+ self.table_name)
+ self.trans_id = str(random.randint(1, 9999999))
+ qt_init = data_grid_utils._init_query_tool(self, self.trans_id,
+ self.sgid, self.sid,
+ self.did)
+
+ if not qt_init['success']:
+ raise Exception("Could not initialize query tool.")
+
+ def initialize_datagrid(self):
+ if self.test_data:
+ response = self.tester.post(
+ self.url + str(self.trans_id) + '/4/table/' +
+ str(self.sgid) + '/' + str(self.sid) + '/' +
+ str(self.did) + '/' + str(self.table_id),
+ data=json.dumps(self.test_data),
+ content_type='html/json'
+ )
+ else:
+ response = self.tester.post(
+ self.url + str(self.trans_id) + '/4/table/' +
+ str(self.sgid) + '/' + str(self.sid) + '/' +
+ str(self.did) + '/' + str(self.table_id),
+ content_type='html/json'
+ )
+ return response
+
+ def runTest(self):
+ """ This function will update query tool connection."""
+
+ if self.is_positive_test:
+ response = self.initialize_datagrid()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+ else:
+ with patch(self.mock_data["function_name"],
+ return_value=eval(self.mock_data["return_value"])):
+ response = self.initialize_datagrid()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+
+ self.assertEqual(actual_response_code, expected_response_code)
+
+ def tearDown(self):
+ """This function disconnect database."""
+ database_utils.disconnect_database(self, self.sid,
+ self.did)
diff --git a/web/pgadmin/tools/datagrid/tests/utils.py b/web/pgadmin/tools/datagrid/tests/utils.py
new file mode 100644
index 0000000..c3d4bb5
--- /dev/null
+++ b/web/pgadmin/tools/datagrid/tests/utils.py
@@ -0,0 +1,33 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+import os
+import json
+
+file_name = os.path.basename(__file__)
+CURRENT_PATH = os.path.dirname(os.path.realpath(__file__))
+with open(CURRENT_PATH + "/datagrid_test_data.json") as data_file:
+ test_cases = json.load(data_file)
+
+
+def _init_query_tool(self, trans_id, server_group, server_id, db_id):
+ QUERY_TOOL_INIT_URL = '/datagrid/initialize/query_tool'
+
+ qt_init = self.tester.post(
+ '{0}/{1}/{2}/{3}/{4}'.format(
+ QUERY_TOOL_INIT_URL,
+ trans_id,
+ server_group,
+ server_id,
+ db_id
+ ),
+ follow_redirects=True
+ )
+ assert qt_init.status_code == 200
+ qt_init = json.loads(qt_init.data.decode('utf-8'))
+ return qt_init
diff --git a/web/pgadmin/tools/sqleditor/__init__.py b/web/pgadmin/tools/sqleditor/__init__.py
index 9d0903b..ca98ec4 100644
--- a/web/pgadmin/tools/sqleditor/__init__.py
+++ b/web/pgadmin/tools/sqleditor/__init__.py
@@ -10,17 +10,15 @@
"""A blueprint module implementing the sqleditor frame."""
import os
import pickle
-import sys
import re
+from urllib.parse import unquote
import simplejson as json
-from flask import Response, url_for, render_template, session, request, \
- current_app
+from config import PG_DEFAULT_DRIVER, ON_DEMAND_RECORD_COUNT
+from flask import Response, url_for, render_template, session, current_app
+from flask import request, jsonify
from flask_babelex import gettext
from flask_security import login_required, current_user
-from urllib.parse import unquote
-
-from config import PG_DEFAULT_DRIVER, ON_DEMAND_RECORD_COUNT
from pgadmin.misc.file_manager import Filemanager
from pgadmin.tools.sqleditor.command import QueryToolCommand
from pgadmin.tools.sqleditor.utils.constant_definition import ASYNC_OK, \
@@ -32,11 +30,11 @@ from pgadmin.tools.sqleditor.utils.update_session_grid_transaction import \
from pgadmin.utils import PgAdminModule
from pgadmin.utils import get_storage_directory
from pgadmin.utils.ajax import make_json_response, bad_request, \
- success_return, internal_server_error, make_response as ajax_response
+ success_return, internal_server_error
from pgadmin.utils.driver import get_driver
-from pgadmin.utils.menu import MenuItem
-from pgadmin.utils.exception import ConnectionLost, SSHTunnelConnectionLost,\
+from pgadmin.utils.exception import ConnectionLost, SSHTunnelConnectionLost, \
CryptKeyMissing
+from pgadmin.utils.menu import MenuItem
from pgadmin.utils.sqlautocomplete.autocomplete import SQLAutoComplete
from pgadmin.tools.sqleditor.utils.query_tool_preferences import \
register_query_tool_preferences
@@ -44,10 +42,12 @@ from pgadmin.tools.sqleditor.utils.query_tool_fs_utils import \
read_file_generator
from pgadmin.tools.sqleditor.utils.filter_dialog import FilterDialog
from pgadmin.tools.sqleditor.utils.query_history import QueryHistory
-from pgadmin.utils.constants import MIMETYPE_APP_JS, SERVER_CONNECTION_CLOSED,\
- ERROR_MSG_TRANS_ID_NOT_FOUND
from pgadmin.tools.sqleditor.utils.macros import get_macros,\
get_user_macros, set_macros
+from pgadmin.utils.constants import MIMETYPE_APP_JS, \
+ SERVER_CONNECTION_CLOSED, ERROR_MSG_TRANS_ID_NOT_FOUND
+from pgadmin.model import Server
+from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry
MODULE_NAME = 'sqleditor'
@@ -113,7 +113,13 @@ class SqlEditorModule(PgAdminModule):
'sqleditor.clear_query_history',
'sqleditor.get_macro',
'sqleditor.get_macros',
- 'sqleditor.set_macros'
+ 'sqleditor.set_macros',
+ 'sqleditor.get_new_connection_data',
+ 'sqleditor.get_new_connection_database',
+ 'sqleditor.get_new_connection_user',
+ 'sqleditor.get_new_connection_role',
+ 'sqleditor.connect_server',
+ 'sqleditor.connect_server_with_user',
]
def register_preferences(self):
@@ -229,7 +235,7 @@ def start_view_data(trans_id):
)
if status and conn is not None and \
- trans_obj is not None and session_obj is not None:
+ trans_obj is not None and session_obj is not None:
# set fetched row count to 0 as we are executing query again.
trans_obj.update_fetched_row_cnt(0)
@@ -375,7 +381,7 @@ def poll(trans_id):
if isinstance(trans_obj, QueryToolCommand):
trans_status = conn.transaction_status()
if trans_status == TX_STATUS_INERROR and \
- trans_obj.auto_rollback:
+ trans_obj.auto_rollback:
conn.execute_void("ROLLBACK;")
st, result = conn.async_fetchmany_2darray(ON_DEMAND_RECORD_COUNT)
@@ -685,13 +691,12 @@ def save(trans_id):
status=404)
if status and conn is not None and \
- trans_obj is not None and session_obj is not None:
+ trans_obj is not None and session_obj is not None:
# If there is no primary key found then return from the function.
if ('primary_keys' not in session_obj or
- len(session_obj['primary_keys']) <= 0 or
- len(changed_data) <= 0) and \
- 'has_oids' not in session_obj:
+ len(session_obj['primary_keys']) <= 0 or
+ len(changed_data) <= 0) and 'has_oids' not in session_obj:
return make_json_response(
data={
'status': False,
@@ -758,7 +763,7 @@ def append_filter_inclusive(trans_id):
status=404)
if status and conn is not None and \
- trans_obj is not None and session_obj is not None:
+ trans_obj is not None and session_obj is not None:
res = None
filter_sql = ''
@@ -812,7 +817,7 @@ def append_filter_exclusive(trans_id):
info='DATAGRID_TRANSACTION_REQUIRED',
status=404)
if status and conn is not None and \
- trans_obj is not None and session_obj is not None:
+ trans_obj is not None and session_obj is not None:
res = None
filter_sql = ''
@@ -865,7 +870,7 @@ def remove_filter(trans_id):
status=404)
if status and conn is not None and \
- trans_obj is not None and session_obj is not None:
+ trans_obj is not None and session_obj is not None:
res = None
@@ -909,7 +914,7 @@ def set_limit(trans_id):
status=404)
if status and conn is not None and \
- trans_obj is not None and session_obj is not None:
+ trans_obj is not None and session_obj is not None:
res = None
@@ -1051,7 +1056,7 @@ def get_object_name(trans_id):
status=404)
if status and conn is not None and \
- trans_obj is not None and session_obj is not None:
+ trans_obj is not None and session_obj is not None:
res = trans_obj.object_name
else:
status = False
@@ -1087,7 +1092,7 @@ def set_auto_commit(trans_id):
status=404)
if status and conn is not None and \
- trans_obj is not None and session_obj is not None:
+ trans_obj is not None and session_obj is not None:
res = None
@@ -1132,7 +1137,7 @@ def set_auto_rollback(trans_id):
status=404)
if status and conn is not None and \
- trans_obj is not None and session_obj is not None:
+ trans_obj is not None and session_obj is not None:
res = None
@@ -1184,7 +1189,7 @@ def auto_complete(trans_id):
status=404)
if status and conn is not None and \
- trans_obj is not None and session_obj is not None:
+ trans_obj is not None and session_obj is not None:
# Create object of SQLAutoComplete class and pass connection object
auto_complete_obj = SQLAutoComplete(
@@ -1470,6 +1475,284 @@ def get_filter_data(trans_id):
return FilterDialog.get(status, error_msg, conn, trans_obj, session_ob)
[email protected](
+ '/new_connection_dialog/<int:sgid>/<int:sid>',
+ methods=["GET"], endpoint='get_new_connection_data'
+)
+@login_required
+def get_new_connection_data(sgid, sid=None):
+ """
+ This method is used to get required data for get new connection.
+ :extract_sql_from_network_parameters,
+ """
+ try:
+ # if sid and not did:
+ servers = Server.query.all()
+ server_list = [
+ {'name': server.serialize['name'], "id": server.serialize['id']}
+ for server in servers]
+
+ msg = "Success"
+ return make_json_response(
+ data={
+ 'status': True,
+ 'msg': msg,
+ 'result': {
+ 'server_list': server_list
+ }
+ }
+ )
+
+ except Exception as e:
+ return make_json_response(
+ data={
+ 'status': False,
+ 'msg': 'Unable to fetch data.',
+ 'result': {
+ 'server_list': []
+ }
+ }
+ )
+
+
[email protected](
+ '/new_connection_database/<int:sgid>/<int:sid>',
+ methods=["GET"], endpoint='get_new_connection_database'
+)
+@login_required
+def get_new_connection_database(sgid, sid=None):
+ """
+ This method is used to get required data for get new connection.
+ :extract_sql_from_network_parameters,
+ """
+ try:
+ database_list = []
+ from pgadmin.utils.driver import get_driver
+ manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)
+ conn = manager.connection()
+ if conn.connected():
+ is_connected = True
+ else:
+ # connection = conn.connect()
+ is_connected = False
+ if is_connected:
+ if sid:
+ template_path = 'databases/sql/#{0}#'.format(manager.version)
+ last_system_oid = 0
+ server_node_res = manager
+
+ db_disp_res = None
+ params = None
+ if server_node_res and server_node_res.db_res:
+ db_disp_res = ", ".join(
+ ['%s'] * len(server_node_res.db_res.split(','))
+ )
+ params = tuple(server_node_res.db_res.split(','))
+ sql = render_template(
+ "/".join([template_path, 'nodes.sql']),
+ last_system_oid=last_system_oid,
+ db_restrictions=db_disp_res
+ )
+ status, databases = conn.execute_dict(sql, params)
+ database_list = [
+ {'label': database['name'], 'value': database['did']} for
+ database in databases['rows']]
+ else:
+ status = False
+
+ msg = "Success"
+ return make_json_response(
+ data={
+ 'status': status,
+ 'msg': msg,
+ 'result': {
+ 'data': database_list,
+ }
+ }
+ )
+ else:
+ return make_json_response(
+ data={
+ 'status': False,
+ 'msg': 'Server not connected, '
+ 'Please connect with server and try again.',
+ 'result': {
+ 'database_list': [],
+ }
+ }
+ )
+ except Exception as e:
+ return make_json_response(
+ data={
+ 'status': False,
+ 'msg': 'Unable to fetch data.',
+ 'result': {
+ 'database_list': [],
+ }
+ }
+ )
+
+
[email protected](
+ '/new_connection_user/<int:sgid>/<int:sid>',
+ methods=["GET"], endpoint='get_new_connection_user'
+)
+@login_required
+def get_new_connection_user(sgid, sid=None):
+ """
+ This method is used to get required data for get new connection.
+ :extract_sql_from_network_parameters,
+ """
+ try:
+ from pgadmin.utils.driver import get_driver
+ manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)
+ conn = manager.connection()
+ user_list = []
+ if conn.connected():
+ is_connected = True
+ else:
+ is_connected = False
+ if is_connected:
+ if sid:
+ sql_path = 'roles/sql/#{0}#'.format(manager.version)
+ status, users = conn.execute_2darray(
+ render_template(sql_path + 'nodes.sql')
+ )
+ user_list = [
+ {'value': user['rolname'], 'label': user['rolname']} for
+ user in users['rows'] if user['rolcanlogin']]
+ else:
+ status = False
+
+ msg = "Success"
+ return make_json_response(
+ data={
+ 'status': status,
+ 'msg': msg,
+ 'result': {
+ 'data': user_list,
+ }
+ }
+ )
+ else:
+ return make_json_response(
+ data={
+ 'status': False,
+ 'msg': 'Server not connected, '
+ 'Please connect with server and try again.',
+ 'result': {
+ 'user_list': [],
+ }
+ }
+ )
+ except Exception as e:
+ return make_json_response(
+ data={
+ 'status': False,
+ 'msg': 'Unable to fetch data.',
+ 'result': {
+ 'user_list': [],
+ }
+ }
+ )
+
+
[email protected](
+ '/new_connection_role/<int:sgid>/<int:sid>',
+ methods=["GET"], endpoint='get_new_connection_role'
+)
+@login_required
+def get_new_connection_role(sgid, sid=None):
+ """
+ This method is used to get required data for get new connection.
+ :extract_sql_from_network_parameters,
+ """
+ try:
+ from pgadmin.utils.driver import get_driver
+ manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)
+ conn = manager.connection()
+ role_list = []
+ if conn.connected():
+ is_connected = True
+ else:
+ is_connected = False
+ if is_connected:
+ if sid:
+ sql_path = 'roles/sql/#{0}#'.format(manager.version)
+ status, roles = conn.execute_2darray(
+ render_template(sql_path + 'nodes.sql')
+ )
+ role_list = [
+ {'value': role['rolname'], 'label': role['rolname']} for
+ role in roles['rows']]
+ else:
+ status = False
+
+ msg = "Success"
+ return make_json_response(
+ data={
+ 'status': status,
+ 'msg': msg,
+ 'result': {
+ 'data': role_list,
+ }
+ }
+ )
+ else:
+ return make_json_response(
+ data={
+ 'status': False,
+ 'msg': 'Server not connected, '
+ 'Please connect with server and try again.',
+ 'result': {
+ 'user_list': [],
+ }
+ }
+ )
+ except Exception as e:
+ return make_json_response(
+ data={
+ 'status': False,
+ 'msg': 'Unable to fetch data.',
+ 'result': {
+ 'user_list': [],
+ }
+ }
+ )
+
+
[email protected](
+ '/connect_server/<int:sid>/<usr>',
+ methods=["GET"],
+ endpoint="connect_server_with_user"
+)
[email protected](
+ '/connect_server/<int:sid>',
+ methods=["GET"],
+ endpoint="connect_server"
+)
+@login_required
+def connect_server(sid, usr=None):
+ # Check if server is already connected then no need to reconnect again.
+ server = Server.query.filter_by(id=sid).first()
+ driver = get_driver(PG_DEFAULT_DRIVER)
+ manager = driver.connection_manager(sid)
+ conn = manager.connection()
+ user = None
+ if usr and manager.user != usr:
+ user = usr
+ else:
+ if conn.connected():
+ return make_json_response(
+ success=1,
+ info=gettext("Server connected."),
+ data={}
+ )
+
+ view = SchemaDiffRegistry.get_node_view('server')
+ return view.connect(server.servergroup_id, sid, user=user)
+
+
@blueprint.route(
'/filter_dialog/<int:trans_id>',
methods=["PUT"], endpoint='set_filter_data'
diff --git a/web/pgadmin/tools/sqleditor/static/css/sqleditor.css b/web/pgadmin/tools/sqleditor/static/css/sqleditor.css
index c281d53..20590d3 100644
--- a/web/pgadmin/tools/sqleditor/static/css/sqleditor.css
+++ b/web/pgadmin/tools/sqleditor/static/css/sqleditor.css
@@ -315,10 +315,6 @@ input.editor-checkbox:focus {
padding: 10px 0px;
}
-.editor-title {
- width:100%;
-}
-
.connection-status-hide {
display: none !important;
}
@@ -396,7 +392,6 @@ input.editor-checkbox:focus {
overflow-y: hidden;
}
-
/* Macros */
.macro-tab {
@@ -424,3 +419,7 @@ input.editor-checkbox:focus {
.macro_dialog .pg-prop-status-bar {
z-index: 1;
}
+
+.new-connection-dialog-style {
+ width: 100% !important;
+}
diff --git a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
index ed3bd59..6abcf1d 100644
--- a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
+++ b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
@@ -14,6 +14,7 @@ define('tools.querytool', [
'jqueryui.position', 'underscore', 'pgadmin.alertifyjs',
'sources/pgadmin', 'backbone', 'bundled_codemirror', 'sources/utils',
'pgadmin.misc.explain',
+ 'pgadmin.user_management.current_user',
'sources/selection/grid_selector',
'sources/selection/active_cell_capture',
'sources/selection/clipboard',
@@ -26,6 +27,7 @@ define('tools.querytool', [
'sources/sqleditor/execute_query',
'sources/sqleditor/query_tool_http_error_handler',
'sources/sqleditor/filter_dialog',
+ 'sources/sqleditor/new_connection_dialog',
'sources/sqleditor/geometry_viewer',
'sources/sqleditor/history/history_collection.js',
'sources/sqleditor/history/query_history',
@@ -53,8 +55,8 @@ define('tools.querytool', [
'pgadmin.tools.user_management',
], function(
gettext, url_for, $, jqueryui, jqueryui_position, _, alertify, pgAdmin, Backbone, codemirror, pgadminUtils,
- pgExplain, GridSelector, ActiveCellCapture, clipboard, copyData, RangeSelectionHelper, handleQueryOutputKeyboardEvent,
- XCellSelectionModel, setStagedRows, SqlEditorUtils, ExecuteQuery, httpErrorHandler, FilterHandler,
+ pgExplain, current_user, GridSelector, ActiveCellCapture, clipboard, copyData, RangeSelectionHelper, handleQueryOutputKeyboardEvent,
+ XCellSelectionModel, setStagedRows, SqlEditorUtils, ExecuteQuery, httpErrorHandler, FilterHandler, newConnectionHandler,
GeometryViewer, historyColl, queryHist, querySources,
keyboardShortcuts, queryToolActions, queryToolNotifications, Datagrid,
modifyAnimation, calculateQueryRunTime, callRenderAfterPoll, queryToolPref, queryTxnStatus, csrfToken, panelTitleFunc,
@@ -98,6 +100,9 @@ define('tools.querytool', [
this.layout = opts.layout;
this.set_server_version(opts.server_ver);
this.trigger('pgadmin-sqleditor:view:initialised');
+ this.connection_list = [
+ {'server_group': null,'server': null, 'database': null, 'user': null, 'role': null, 'title': '<New Connection>'},
+ ];
},
// Bind all the events
@@ -163,6 +168,35 @@ define('tools.querytool', [
'click .btn-macro': 'on_execute_macro',
},
+ render_connection: function(data_list) {
+ if(this.handler.is_query_tool) {
+ var dropdownElement = document.getElementById('connections-list');
+ dropdownElement.innerHTML = '';
+ data_list.forEach((option, index) => {
+ $('#connections-list').append('<li class="connection-list-item" data-index='+ index +'><a class="dropdown-item" href="#" tabindex="0">'+ option.title +'</a></li>');
+
+ });
+ var self = this;
+ $('.connection-list-item').click(function() {
+ self.get_connection_data(this);
+ });
+ } else {
+ $('.conn-info-dd').hide();
+ $('.editor-title').css({pointerEvents: 'none'});
+ }
+ },
+
+ get_connection_data: function(event){
+ var index = $(event).attr('data-index');
+ var connection_details = this.connection_list[index];
+ if(connection_details.server_group) {
+ this.on_change_connection(connection_details);
+ } else {
+ this.on_new_connection();
+ }
+
+ },
+
reflectPreferences: function() {
let self = this,
browser = pgWindow.default.pgAdmin.Browser,
@@ -211,8 +245,15 @@ define('tools.querytool', [
});
},
- set_editor_title: function(title) {
- this.$el.find('.editor-title').text(title);
+ set_editor_title: function(title, is_connected) {
+ if(is_connected) {
+ this.$el.find('.editor-title').text(title);
+ this.render_connection(this.connection_list);
+ } else {
+ this.$el.find('.editor-title').text(title);
+ this.render_connection(this.connection_list);
+ }
+
},
// This function is used to render the template.
@@ -696,6 +737,8 @@ define('tools.querytool', [
pgBrowser.register_to_activity_listener(document, ()=>{
alertify.alert(gettext('Timeout'), gettext('Your session has timed out due to inactivity. Please close the window and login again.'));
});
+
+ self.render_connection(self.connection_list);
},
/* Regarding SlickGrid usage in render_grid function.
@@ -1607,6 +1650,17 @@ define('tools.querytool', [
);
},
+ on_new_connection: function() {
+ var self = this;
+
+ // Trigger the show_filter signal to the SqlEditorController class
+ self.handler.trigger(
+ 'pgadmin-sqleditor:button:show_new_connection',
+ self,
+ self.handler
+ );
+ },
+
// Callback function for include filter button click.
on_include_filter: function(ev) {
var self = this;
@@ -2070,6 +2124,83 @@ define('tools.querytool', [
queryToolActions.executeMacro(this.handler, macroId);
},
+ on_change_connection: function(connection_details, ref) {
+ let title = this.$el.find('.editor-title').html();
+ if(connection_details['title'] != title) {
+ var self = this;
+ $.ajax({
+ async: false,
+ url: url_for('datagrid.update_query_tool_connection', {
+ 'trans_id': self.transId,
+ 'sgid': connection_details['server_group'],
+ 'sid': connection_details['server'],
+ 'did': connection_details['database'],
+ }),
+ method: 'POST',
+ contentType: 'application/json',
+ data: JSON.stringify(connection_details),
+ })
+ .done(function(res) {
+ if(res.success) {
+ self.transId = res.data.tran_id;
+ self.handler.transId = res.data.tran_id;
+ self.handler.url_params = {
+ 'did': connection_details['database'],
+ 'is_query_tool': self.handler.url_params.is_query_tool,
+ 'server_type': self.handler.url_params.server_type,
+ 'sgid': connection_details['server_group'],
+ 'sid': connection_details['server'],
+ 'title': connection_details['title'],
+ };
+ self.set_editor_title(self.handler.url_params.title);
+ self.handler.setTitle(self.handler.url_params.title);
+ alertify.success('connected successfully');
+ if(ref){
+ let connection_data = {
+ 'server_group': self.handler.url_params.sgid,
+ 'server': connection_details['server'],
+ 'database': connection_details['database'],
+ 'user': connection_details['user'],
+ 'title': connection_details['title'],
+ 'role': connection_details['role'],
+ 'password': connection_details['password'],
+ 'is_allow_new_connection': true,
+ };
+ self.connection_list.unshift(connection_data);
+ self.render_connection(self.connection_list);
+ ref.close();
+ }
+ }
+ return true;
+ })
+ .fail(function(xhr) {
+ if(xhr.status == 428) {
+ alertify.connectServer('Connect to server', xhr.responseJSON.result, connection_details['server'], false);
+ } else {
+ alertify.error(xhr.responseJSON['errormsg']);
+ }
+ /*let url = url_for('sqleditor.connect_server_with_user', {
+ 'sid': newConnCollectionModel['server'],
+ 'usr': newConnCollectionModel['user']
+ });
+ $.ajax({
+ async: false,
+ url: url,
+ headers: {
+ 'Cache-Control' : 'no-cache',
+ },
+ }).done(function () {
+ Backform.Select2Control.prototype.onChange.apply(self, arguments);
+ response.server_list.forEach(function(obj){
+ if(obj.id==self.model.changed.server) {
+ response.server_name = obj.name;
+ }
+ });
+ }).fail(function(xhr){});*/
+
+ });
+ }
+ },
});
@@ -2392,7 +2523,18 @@ define('tools.querytool', [
});
$('#btn-conn-status i').removeClass('obtaining-conn');
- self.gridView.set_editor_title(_.unescape(url_params.title));
+ self.gridView.set_editor_title(_.unescape(url_params.title), true);
+ let connection_data = {
+ 'server_group': self.gridView.handler.url_params.sgid,
+ 'server': self.gridView.handler.url_params.sid,
+ 'database': self.gridView.handler.url_params.did,
+ 'user': null,
+ 'role': null,
+ 'title': _.unescape(url_params.title),
+ 'is_allow_new_connection': false,
+ };
+ self.gridView.connection_list.unshift(connection_data);
+ self.gridView.render_connection(self.gridView.connection_list);
};
pgBrowser.Events.on('pgadmin:query_tool:connected:' + transId, afterConn);
@@ -2487,6 +2629,7 @@ define('tools.querytool', [
self.on('pgadmin-sqleditor:button:save_file', self._save_file, self);
self.on('pgadmin-sqleditor:button:deleterow', self._delete, self);
self.on('pgadmin-sqleditor:button:show_filter', self._show_filter, self);
+ self.on('pgadmin-sqleditor:button:show_new_connection', self._show_new_connection, self);
self.on('pgadmin-sqleditor:button:include_filter', self._include_filter, self);
self.on('pgadmin-sqleditor:button:exclude_filter', self._exclude_filter, self);
self.on('pgadmin-sqleditor:button:remove_filter', self._remove_filter, self);
@@ -3696,7 +3839,6 @@ define('tools.querytool', [
}
};
},
-
// This function will show the filter in the text area.
_show_filter: function() {
let self = this,
@@ -3711,7 +3853,19 @@ define('tools.querytool', [
}
FilterHandler.dialog(self, reconnect);
},
+ // This function will show the new connection.
+ _show_new_connection: function() {
+ let self = this,
+ reconnect = false;
+ /* When server is disconnected and connected, connection is lost,
+ * To reconnect pass true
+ */
+ if (arguments.length > 0 && arguments[arguments.length - 1] == 'connect') {
+ reconnect = true;
+ }
+ newConnectionHandler.dialog(self, reconnect);
+ },
// This function will include the filter by selection.
_include_filter: function() {
var self = this,
diff --git a/web/pgadmin/tools/sqleditor/static/scss/_sqleditor.scss b/web/pgadmin/tools/sqleditor/static/scss/_sqleditor.scss
index fd1e5d3..53f2449 100644
--- a/web/pgadmin/tools/sqleditor/static/scss/_sqleditor.scss
+++ b/web/pgadmin/tools/sqleditor/static/scss/_sqleditor.scss
@@ -30,6 +30,19 @@
color: $sql-title-fg;
}
+.connection-info {
+ background: $sql-title-bg;
+ color: $sql-title-fg;
+ width:100%;
+ display: inherit;
+}
+
+.conn-info-dd {
+ padding-top: 0.3em;
+ padding-left: 0.2em;
+ cursor: pointer;
+}
+
#editor-panel {
z-index: 0;
diff --git a/web/pgadmin/tools/sqleditor/tests/test_new_connection_database.py b/web/pgadmin/tools/sqleditor/tests/test_new_connection_database.py
new file mode 100644
index 0000000..20fe3e3
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/tests/test_new_connection_database.py
@@ -0,0 +1,98 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+import json
+from pgadmin.utils.route import BaseTestGenerator
+from regression import parent_node_dict
+from regression.test_setup import config_data
+from regression.python_test_utils import test_utils as utils
+
+
+class TestNewConnectionDatabase(BaseTestGenerator):
+ """ This class will test new connection database. """
+ scenarios = [
+ ('New connection dialog',
+ dict(
+ url="/sqleditor/new_connection_database/",
+ is_positive_test=True,
+ mocking_required=False,
+ is_server_conn_required=False,
+ test_data={},
+ mock_data={},
+ expected_data={
+ "status_code": 200
+ }
+ )),
+ ('New connection dialog connect server',
+ dict(
+ url="/sqleditor/new_connection_database/",
+ is_positive_test=True,
+ mocking_required=False,
+ is_server_conn_required=True,
+ test_data={},
+ mock_data={},
+ expected_data={
+ "status_code": 200
+ }
+ )),
+ ('New connection dialog negative',
+ dict(
+ url="/sqleditor/new_connection_database/",
+ is_positive_test=False,
+ mocking_required=False,
+ is_server_conn_required=True,
+ test_data={},
+ mock_data={},
+ expected_data={
+ "status_code": 200
+ }
+ )),
+ ]
+
+ def setUp(self):
+ self.sid = parent_node_dict["server"][-1]["server_id"]
+ self.sgid = config_data['server_group']
+
+ def get_database(self):
+ response = self.tester.get(
+ self.url + str(self.sgid) + '/' + str(self.sid),
+ content_type='html/json'
+ )
+
+ return response
+
+ def runTest(self):
+ if self.is_positive_test:
+ if self.is_server_conn_required:
+ self.server['password'] = self.server['db_password']
+ server_response = self.tester.post(
+ '/browser/server/connect/{0}/{1}'.format(
+ utils.SERVER_GROUP,
+ self.sid),
+ data=json.dumps(self.server),
+ content_type='html/json'
+ )
+ response = self.get_database()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+ else:
+ if self.is_server_conn_required:
+ self.server['password'] = self.server['db_password']
+ server_response = self.tester.post(
+ '/browser/server/connect/{0}/{1}'.format(
+ utils.SERVER_GROUP,
+ self.sid),
+ data=json.dumps(self.server),
+ content_type='html/json'
+ )
+ self.sid = 0
+ response = self.get_database()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+ self.assertEqual(actual_response_code, expected_response_code)
diff --git a/web/pgadmin/tools/sqleditor/tests/test_new_connection_dialog.py b/web/pgadmin/tools/sqleditor/tests/test_new_connection_dialog.py
new file mode 100644
index 0000000..75a47ef
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/tests/test_new_connection_dialog.py
@@ -0,0 +1,50 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+import json
+from pgadmin.utils.route import BaseTestGenerator
+from regression import parent_node_dict
+from regression.test_setup import config_data
+from regression.python_test_utils import test_utils as utils
+
+
+class TestNewConnectionDialog(BaseTestGenerator):
+ """ This class will test new connection dialog. """
+ scenarios = [
+ ('New connection dialog',
+ dict(
+ url="/sqleditor/new_connection_dialog/",
+ is_positive_test=True,
+ mocking_required=False,
+ is_connect_server=False,
+ test_data={},
+ mock_data={},
+ expected_data={
+ "status_code": 200
+ }
+ )),
+ ]
+
+ def setUp(self):
+ self.sid = parent_node_dict["server"][-1]["server_id"]
+ self.sgid = config_data['server_group']
+
+ def new_connection(self):
+ response = self.tester.get(
+ self.url + str(self.sgid) + '/' + str(self.sgid),
+ content_type='html/json'
+ )
+
+ return response
+
+ def runTest(self):
+ if self.is_positive_test:
+ response = self.new_connection()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+ self.assertEqual(actual_response_code, expected_response_code)
diff --git a/web/pgadmin/tools/sqleditor/tests/test_new_connection_user.py b/web/pgadmin/tools/sqleditor/tests/test_new_connection_user.py
new file mode 100644
index 0000000..7b6c12e
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/tests/test_new_connection_user.py
@@ -0,0 +1,98 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+import json
+from pgadmin.utils.route import BaseTestGenerator
+from regression import parent_node_dict
+from regression.test_setup import config_data
+from regression.python_test_utils import test_utils as utils
+
+
+class TestNewConnectionUser(BaseTestGenerator):
+ """ This class will test new connection user. """
+ scenarios = [
+ ('New connection dialog',
+ dict(
+ url="/sqleditor/new_connection_user/",
+ is_positive_test=True,
+ mocking_required=False,
+ is_server_conn_required=False,
+ test_data={},
+ mock_data={},
+ expected_data={
+ "status_code": 200
+ }
+ )),
+ ('New connection dialog connect server',
+ dict(
+ url="/sqleditor/new_connection_user/",
+ is_positive_test=True,
+ mocking_required=False,
+ is_server_conn_required=True,
+ test_data={},
+ mock_data={},
+ expected_data={
+ "status_code": 200
+ }
+ )),
+ ('New connection dialog negative',
+ dict(
+ url="/sqleditor/new_connection_user/",
+ is_positive_test=False,
+ mocking_required=False,
+ is_server_conn_required=True,
+ test_data={},
+ mock_data={},
+ expected_data={
+ "status_code": 200
+ }
+ )),
+ ]
+
+ def setUp(self):
+ self.sid = parent_node_dict["server"][-1]["server_id"]
+ self.sgid = config_data['server_group']
+
+ def get_use(self):
+ response = self.tester.get(
+ self.url + str(self.sgid) + '/' + str(self.sid),
+ content_type='html/json'
+ )
+
+ return response
+
+ def runTest(self):
+ if self.is_positive_test:
+ if self.is_server_conn_required:
+ self.server['password'] = self.server['db_password']
+ server_response = self.tester.post(
+ '/browser/server/connect/{0}/{1}'.format(
+ utils.SERVER_GROUP,
+ self.sid),
+ data=json.dumps(self.server),
+ content_type='html/json'
+ )
+ response = self.get_use()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+ else:
+ if self.is_server_conn_required:
+ self.server['password'] = self.server['db_password']
+ server_response = self.tester.post(
+ '/browser/server/connect/{0}/{1}'.format(
+ utils.SERVER_GROUP,
+ self.sid),
+ data=json.dumps(self.server),
+ content_type='html/json'
+ )
+ self.sid = 0
+ response = self.get_use()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+ self.assertEqual(actual_response_code, expected_response_code)
diff --git a/web/pgadmin/utils/driver/psycopg2/connection.py b/web/pgadmin/utils/driver/psycopg2/connection.py
index 9cb65bc..83c2c17 100644
--- a/web/pgadmin/utils/driver/psycopg2/connection.py
+++ b/web/pgadmin/utils/driver/psycopg2/connection.py
@@ -21,7 +21,7 @@ import psycopg2
from flask import g, current_app
from flask_babelex import gettext
from flask_security import current_user
-from pgadmin.utils.crypto import decrypt
+from pgadmin.utils.crypto import decrypt, encrypt
from psycopg2.extensions import encodings
import config
@@ -211,8 +211,17 @@ class Connection(BaseConnection):
password = None
passfile = None
manager = self.manager
+ encpass = None
+ is_update_password = True
+ crypt_key_present, crypt_key = get_crypt_key()
+
+ if 'user' in kwargs and kwargs['password']:
+ password = kwargs['password']
+ kwargs.pop('password')
+ is_update_password = False
+ else:
+ encpass = kwargs['password'] if 'password' in kwargs else None
- encpass = kwargs['password'] if 'password' in kwargs else None
passfile = kwargs['passfile'] if 'passfile' in kwargs else None
tunnel_password = kwargs['tunnel_password'] if 'tunnel_password' in \
kwargs else ''
@@ -227,16 +236,16 @@ class Connection(BaseConnection):
if manager.use_ssh_tunnel == 1:
manager.check_ssh_tunnel_alive()
- if encpass is None:
- encpass = self.password or getattr(manager, 'password', None)
+ if is_update_password:
+ if encpass is None:
+ encpass = self.password or getattr(manager, 'password', None)
- self.password = encpass
+ self.password = encpass
# Reset the existing connection password
if self.reconnecting is not False:
self.password = None
- crypt_key_present, crypt_key = get_crypt_key()
if not crypt_key_present:
raise CryptKeyMissing()
@@ -269,7 +278,10 @@ class Connection(BaseConnection):
try:
database = self.db
- user = manager.user
+ if 'user' in kwargs and kwargs['user']:
+ user = kwargs['user']
+ else:
+ user = manager.user
conn_id = self.conn_id
import os
@@ -338,10 +350,10 @@ class Connection(BaseConnection):
self.wasConnected = False
raise e
- if status:
+ if status and is_update_password:
manager._update_password(encpass)
else:
- if not self.reconnecting:
+ if not self.reconnecting and is_update_password:
self.wasConnected = False
return status, msg
@@ -359,7 +371,7 @@ class Connection(BaseConnection):
else:
self.conn.autocommit = True
- def _set_role(self, manager, cur, conn_id):
+ def _set_role(self, manager, cur, conn_id, **kwargs):
"""
Set role
:param manager:
@@ -367,8 +379,18 @@ class Connection(BaseConnection):
:param conn_id:
:return:
"""
- if manager.role:
- status = self._execute(cur, "SET ROLE TO %s", [manager.role])
+ is_set_role = False
+ role = None
+
+ if 'role' in kwargs and kwargs['role']:
+ is_set_role = True
+ role = kwargs['role']
+ elif manager.role:
+ is_set_role = True
+ role = manager.role
+
+ if is_set_role:
+ status = self._execute(cur, "SET ROLE TO %s", [role])
if status is not None:
self.conn.close()
@@ -382,7 +404,7 @@ class Connection(BaseConnection):
msg=status
)
)
- return False, \
+ return True, \
_(
"Failed to setup the role with error message:\n{0}"
).format(status)
@@ -445,7 +467,7 @@ class Connection(BaseConnection):
return False, status
- is_error, errmsg = self._set_role(manager, cur, conn_id)
+ is_error, errmsg = self._set_role(manager, cur, conn_id, **kwargs)
if is_error:
return False, errmsg
@@ -491,7 +513,7 @@ WHERE db.datname = current_database()""")
if len(manager.db_info) == 1:
manager.did = res['did']
- self._set_user_info(cur, manager)
+ self._set_user_info(cur, manager, **kwargs)
self._set_server_type_and_password(kwargs, manager)
@@ -499,7 +521,7 @@ WHERE db.datname = current_database()""")
return True, None
- def _set_user_info(self, cur, manager):
+ def _set_user_info(self, cur, manager, **kwargs):
"""
Set user info.
:param cur:
@@ -517,7 +539,7 @@ WHERE db.datname = current_database()""")
WHERE
rolname = current_user""")
- if status is None:
+ if status is None and 'user' not in kwargs:
manager.user_info = dict()
if cur.rowcount > 0:
manager.user_info = cur.fetchmany(1)[0]
^ permalink raw reply [nested|flat] 18+ messages in thread
* Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab
2020-09-17 10:45 [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-09-21 11:54 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-09-28 05:50 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-09-28 12:28 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-09-28 14:01 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
@ 2020-09-29 06:01 ` Akshay Joshi <[email protected]>
2020-10-01 05:12 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
0 siblings, 1 reply; 18+ messages in thread
From: Akshay Joshi @ 2020-09-29 06:01 UTC (permalink / raw)
To: Nikhil Mohite <[email protected]>; +Cc: pgadmin-hackers
Hi Nikhil
Your patch introduces 1 new Bug and 13 new code smells, please fix those
and resend the patch.
On Mon, Sep 28, 2020 at 7:31 PM Nikhil Mohite <
[email protected]> wrote:
> Hi Akshay,
>
> I have resolved code conflict issues and sonarqube issues.
> PFA updated patch.
>
> Regards,
> Nikhil Mohite.
>
> On Mon, Sep 28, 2020 at 5:58 PM Akshay Joshi <
> [email protected]> wrote:
>
>> Hi Nikhil
>>
>> The patch is not applying, rebase, and send it again. Please check your
>> code should not create any new SonarQube issues.
>>
>> On Mon, Sep 28, 2020 at 11:20 AM Nikhil Mohite <
>> [email protected]> wrote:
>>
>>> Hi Akshay,
>>>
>>> I have resolved all the review comments and also updated the test cases
>>> as per the new implementation.
>>>
>>> PFA updated patch.
>>>
>>>
>>>
>>> On Mon, Sep 21, 2020 at 5:24 PM Akshay Joshi <
>>> [email protected]> wrote:
>>>
>>>> Hi Nikhil
>>>>
>>>> Following are the initial review comments:
>>>>
>>>> - Open View/Edit data on any table and click on the same database
>>>> connection and then click on the Execute button. Got "get_primary_keys()
>>>> takes 1 positional argument but 2 were given" error.
>>>> - In my opinion, we should hide the option to change the database
>>>> connection for View/Edit Data.
>>>> - If the user clicks on the same database connection multiple times
>>>> then no need to change the backend connection and transaction id. Add
>>>> validation at the backend, no action required in this case.
>>>> - The role option is missing from the "connect to server" dialog.
>>>> - The Password field should not be there on the "connect to server"
>>>> dialog. Sometimes we saved the password so asking a password every time is
>>>> not correct. Check the pgAdmin 3 behavior.
>>>>
>>>> Code review still remains.
>>>>
>>>> On Thu, Sep 17, 2020 at 4:15 PM Nikhil Mohite <
>>>> [email protected]> wrote:
>>>>
>>>>> Hi Team,
>>>>>
>>>>> Regarding RM-3794 <https://redmine.postgresql.org/issues/3794; allow
>>>>> the user to change the database connection from an open query tool:
>>>>> I have implemented the feature and also added documentation for it.
>>>>>
>>>>> PFA patch.
>>>>>
>>>>> --
>>>>> *Thanks & Regards,*
>>>>> *Nikhil Mohite*
>>>>> *Software Engineer.*
>>>>> *EDB Postgres* <https://www.enterprisedb.com/;
>>>>> *Mob.No: +91-7798364578.*
>>>>>
>>>>
>>>>
>>>> --
>>>> *Thanks & Regards*
>>>> *Akshay Joshi*
>>>> *pgAdmin Hacker | Sr. Software Architect*
>>>> *EDB Postgres <http://edbpostgres.com>*
>>>>
>>>> *Mobile: +91 976-788-8246*
>>>>
>>>
>>
>> --
>> *Thanks & Regards*
>> *Akshay Joshi*
>> *pgAdmin Hacker | Sr. Software Architect*
>> *EDB Postgres <http://edbpostgres.com>*
>>
>> *Mobile: +91 976-788-8246*
>>
>
--
*Thanks & Regards*
*Akshay Joshi*
*pgAdmin Hacker | Sr. Software Architect*
*EDB Postgres <http://edbpostgres.com>*
*Mobile: +91 976-788-8246*
^ permalink raw reply [nested|flat] 18+ messages in thread
* Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab
2020-09-17 10:45 [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-09-21 11:54 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-09-28 05:50 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-09-28 12:28 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-09-28 14:01 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-09-29 06:01 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
@ 2020-10-01 05:12 ` Nikhil Mohite <[email protected]>
2020-10-01 08:01 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
0 siblings, 1 reply; 18+ messages in thread
From: Nikhil Mohite @ 2020-10-01 05:12 UTC (permalink / raw)
To: Akshay Joshi <[email protected]>; +Cc: pgadmin-hackers
Hi Akshay,
I have resolved the sonarQube issues, PFA updated patch for the same.
Regards,
Nikhil Mohite.
On Tue, Sep 29, 2020 at 11:31 AM Akshay Joshi <[email protected]>
wrote:
> Hi Nikhil
>
> Your patch introduces 1 new Bug and 13 new code smells, please fix those
> and resend the patch.
>
> On Mon, Sep 28, 2020 at 7:31 PM Nikhil Mohite <
> [email protected]> wrote:
>
>> Hi Akshay,
>>
>> I have resolved code conflict issues and sonarqube issues.
>> PFA updated patch.
>>
>> Regards,
>> Nikhil Mohite.
>>
>> On Mon, Sep 28, 2020 at 5:58 PM Akshay Joshi <
>> [email protected]> wrote:
>>
>>> Hi Nikhil
>>>
>>> The patch is not applying, rebase, and send it again. Please check your
>>> code should not create any new SonarQube issues.
>>>
>>> On Mon, Sep 28, 2020 at 11:20 AM Nikhil Mohite <
>>> [email protected]> wrote:
>>>
>>>> Hi Akshay,
>>>>
>>>> I have resolved all the review comments and also updated the test cases
>>>> as per the new implementation.
>>>>
>>>> PFA updated patch.
>>>>
>>>>
>>>>
>>>> On Mon, Sep 21, 2020 at 5:24 PM Akshay Joshi <
>>>> [email protected]> wrote:
>>>>
>>>>> Hi Nikhil
>>>>>
>>>>> Following are the initial review comments:
>>>>>
>>>>> - Open View/Edit data on any table and click on the same database
>>>>> connection and then click on the Execute button. Got "get_primary_keys()
>>>>> takes 1 positional argument but 2 were given" error.
>>>>> - In my opinion, we should hide the option to change the database
>>>>> connection for View/Edit Data.
>>>>> - If the user clicks on the same database connection multiple
>>>>> times then no need to change the backend connection and transaction id. Add
>>>>> validation at the backend, no action required in this case.
>>>>> - The role option is missing from the "connect to server" dialog.
>>>>> - The Password field should not be there on the "connect to
>>>>> server" dialog. Sometimes we saved the password so asking a password every
>>>>> time is not correct. Check the pgAdmin 3 behavior.
>>>>>
>>>>> Code review still remains.
>>>>>
>>>>> On Thu, Sep 17, 2020 at 4:15 PM Nikhil Mohite <
>>>>> [email protected]> wrote:
>>>>>
>>>>>> Hi Team,
>>>>>>
>>>>>> Regarding RM-3794 <https://redmine.postgresql.org/issues/3794; allow
>>>>>> the user to change the database connection from an open query tool:
>>>>>> I have implemented the feature and also added documentation for it.
>>>>>>
>>>>>> PFA patch.
>>>>>>
>>>>>> --
>>>>>> *Thanks & Regards,*
>>>>>> *Nikhil Mohite*
>>>>>> *Software Engineer.*
>>>>>> *EDB Postgres* <https://www.enterprisedb.com/;
>>>>>> *Mob.No: +91-7798364578.*
>>>>>>
>>>>>
>>>>>
>>>>> --
>>>>> *Thanks & Regards*
>>>>> *Akshay Joshi*
>>>>> *pgAdmin Hacker | Sr. Software Architect*
>>>>> *EDB Postgres <http://edbpostgres.com>*
>>>>>
>>>>> *Mobile: +91 976-788-8246*
>>>>>
>>>>
>>>
>>> --
>>> *Thanks & Regards*
>>> *Akshay Joshi*
>>> *pgAdmin Hacker | Sr. Software Architect*
>>> *EDB Postgres <http://edbpostgres.com>*
>>>
>>> *Mobile: +91 976-788-8246*
>>>
>>
>
> --
> *Thanks & Regards*
> *Akshay Joshi*
> *pgAdmin Hacker | Sr. Software Architect*
> *EDB Postgres <http://edbpostgres.com>*
>
> *Mobile: +91 976-788-8246*
>
Attachments:
[application/octet-stream] RM_3794_V5.patch (224.5K, 3-RM_3794_V5.patch)
download | inline diff:
diff --git a/docs/en_US/images/new_connection_dialog.png b/docs/en_US/images/new_connection_dialog.png
new file mode 100644
index 0000000000000000000000000000000000000000..8cf7ae6aa750360ae7710af61bf8e89f9d520284
GIT binary patch
literal 49691
zcmZ^}1z23k(g2DD2`<4M0s+Dh++7BTKyVALgF6fo+#P}khv4oK++l#=?(S}RWbfX)
z+5f$F<~v{abXT?1>5{Ih5Jh<)>TAN+P*6~)Qj%gyP*5=bP*BkJi105hR`EmAP*5m_
z=AxpCQlg^diuN`p=2pf~P?8}DY6$Ae1GpJlN>XONh>}RHA(ZSs@fezL&_!Pg{i&lB
z4P+7NogFn^%>c61Au5>SYHC8$UxF=la6DGj6+ce*s8|)YAR+BGm3y9et}Na;Y!7=&
zn;fS@3n}y@4V-@jL%q%oIQQ>YoNlOb#NERrhoS!f)zlHmq}b1OcE$`fDD=z4%>k8s
z|G6qP<sft8mkLS%WdIZuHSGHPED=R0J60&y5Fr{SI3IlYt2$+5V~h8Jvyy1A{;v6i
zJ7%XW?4uK_peGI~B<)}#X!6NhPd7BErc%W*daO+$(j_29vrGUM9hAES1_DyICq=gu
z20FvLuO+<>6wY6+S%O_ehDYy1S$Aw6HT)huy++BWH-9p1eh3js9Ls$6PTcmE5pJ{Z
z2!gd@tK_2>P8Td$i)a~uSjhS{$fUd>Dkx*`KA{$oTY?rUGBEyB)l15b_pYJB%Ll2l
zIhqU?wUWCVbK-Fpzf^!h3+sm;ei^P~N^W#gZhEs~E~3JeeOZ!%d@xbRoIL=@TOY}E
za#LJqqq6<hq2`gZn{pZI*MWhZ5`Fh4s1Ft~)NgSObb=#r+(ICPM8o;&!x1DEF$~yK
zXA8o^xKdQ2?2qaab$pb<mP`zaD4Sskp>JE%8Oz?|(2ASy`yFAOK5TIMHt14sQKO#*
z<7^nf+hc}ycCt4bf+)?aIP?xPN<Od&^VVx6l9ku?4P-w`)-J;vu!?`ob_NK1#{kHq
z3X)PWgey#RhnQ}k1N$u3e}oC4tbX~>0V6$I)nOEkObyaO)j3Tvp@up2CEkR>w4Jno
zH_5$@l3ZiO#PJg($Aa!zZ5e<5i23fzy@H?S$Rw)KO%(3cSU+qO1po>{PWG&NJr;}R
zE(0C)P2wE=1P0$yz-qtJd!I`YJLl&+1Xm4r*u-vI1AOLCd|}7lkyCO$s3_ktj9ETg
zj$=-YBRr2zLlNFJ=rwv_@dj=wCCXnkiOfQ;?x<=AOu^~Q$57m@XNr6nRiY+oA*s-u
zi^%tDt3Sr8Ij8Jv?TQ^tpHle{SZ}{?Yx0KjzRE+%@>fi2OD{UeKH%@qF58{Cf^?^_
ztE(KZ_SZi*>iyI}eAu;NtPGfiKE!b*=Jg}#C4@re4P-aisid;tYX~I0D$jzpL3%pd
z6eb-Id_1ca_?hsjVW!EY$$Ki&R|HZcas|I3pD33kW8WR{9_{gUusUDS=i3j7LCrzD
zQ2REa+w51NbH7T3zM8i0Zg;g3zdJfO;C&RwdxG}KEM4PJ14FoVzm&t?3JA1ne~ZU6
zcWk&9_OXHGZ^)7LeOvKL{sR^IDlCi;A|gVLFtH?z`De<KYWy-&?ys*+zPNqEiT5-9
z%&v~W;amEdb{6*958f7^7@_@(!zyLNYwND7N$3ne>r?nkN<<NnT|`)B@e&M=?l)&b
zvyvECk=^7-l7z7#o8(GjA;V(ZR9pS8jYz6q6$yKZpi%V2uuAgUV>d=QQhHO6DWc?M
zNff@?_lXpmdxsDq5jT!xhQ{-XQ3CIq%(!$7;ZnqOP9MZ!SLIWH0MO%u&)#R*VM2$l
zS$w>(u8B%3*~pRJ!HU-<gvDJctAbzxWqqAlkF;P`Tij##8C#r;aO6|ETPsiIHnf$H
znT^t0<41wz2ydEkq-{90FM_^6d|*F??Av%Odc*}J1SHxu>wWST3ak*>Y$^!eaW9(z
zzz*Jy*$&o@sWN$1-c$0BB1STwT1|<ZF0z~UO{}z(rBt(2pJb9GNv_Mw4&TiA5ev)H
z$_%{Se0>f$|9JlP9NV5rJkvnotf2d&fBas2UOXtCX7H6XLSB-EkW((aQe0u<l-rcF
z1<4il71~uoWMpJSWag+kNyw;nx^z02r@-m>prwFdLfcGR6JAbAOJqUVGJh$zHG6Uo
z?GnSCr!~6GUsqI6xE<(SFf>k5HZ=KWe-u(WQ99F_`ydjPZCc(3Ni9?>QOj}>I{BiT
zBp|*boLM|JlUb^l=PmiG$tw!F>vOJfny~K=13w*jQ<MzCSQHYJF4TZPWR!Zsj1Qju
z9r$%90MU-GW3#<n1G^t63AqW+!tEmlBd+ml37m+C2t5c2@D*6gSOg483=CuuXrhwG
zv<DZ*T6!NpbQ>U=2AlO~Q#gqNP~9w9rl`BcjFe2OW;VYMASfrAH<)Ma*-yexHk&jg
zhs~*17nu-^$fr1=vJ(=1`1GO1YWF#-e)yvU-UZwR!M4`OSn7?sx~7<B|NCjpw2Iv-
zBh5hdZOyVOK8rc?o{HC1vFfE&JSJD+#>49s=Nbtms;c$1jZX2k6YgP7sZQk)IdWBU
zAtSn}&2^u&>Ogfz^b5EP*1QbO%+9Pw7Dv`|%7=5;1`q7l1&1p<Ts*lvo+sn0r&Eyb
zm>u;)^#j+wkYk)fww|K-y--$2@=@AO%a6Nk<o)#{O6MZ`#?{cK`vz%k&W1X4DwGQ%
z8WL6h!zQAUhz-?E)dOd5E(h1))7UlCF1_#yq8I^#yI^H1fo6~9on{+{cK!+5&E1%{
zRH+irTWjkSr-kPY>ryx2r)e8=yLS^qKV>#+AqRn+T^`{#H_MF6nVz2DO#vr?^fu?V
z<hBEFd3D6>*<sJ&q!(PfVmr52(9`n6!!6$Z$;~g;4n}gO&uSf$A0u<ba_S=7@y)RN
z(HF4_UmLtKh6kZ3z0yLs@GJHs^Pdew6s-&5TjO%D=Wga^5?K=6%0dubv43i;ZWz&5
zS2t4EP}geEceTH5I`zQhz~siP#axP6j$x%t%|ZMK`8bqQcJba7Mi;f*qvm8+BS$O8
z91;RK$R_J<=<YX6>HTIX5v9SsEnfmIVXLD+>pojo?Olp+3D3m(HKe{idt0euNJbkj
z`!(5?J|>2X&Q5JTHFrEWPpUpHJvL#0k<OitfR3HcM!8E#E%$qG1U{$bVk28Mo0O&@
z4;}wou!sBD?de%@pNo;JX}pPc>MEE1ygw`U2+g(vAB&U?miL6OtcZ!lcG+;$BbOI@
zJ8f7}tw6C>$D0yNKD<CgH6&hf-|RQpv~#|53{{!iaN8xrdpHPT4Ba|dWk%_n#@3&X
z^B3#g?A&<I@6VMlZg8WKS&-4BQ1jd-rUw$^3goPl8V99~ztcS{t19dz0*C4uFnZv`
z4YM%$S~Asm=-QQ+nO@U>r+4RMH{SO(ho8)y993{hZZo@&TMu3*GhJncW7Ra#982T8
zcz!=x!dM+Mr%|o7Jv*xLp}^33b20s*@Zx;?Np)SfDtq(laq3PE`91Ei&gp4$|A~c+
zlgw7$Ogs9m*QAJNmXZDf<(Rl79vFa~n9k_JRQ~Q)gL=j7Av%?J;bWGK)OBoKo`4J|
zfC>-?Xs+XPy&HW4&k(A{r_F6*{6s(fy-KBAO+gc!-&t%hNw`nAMW|vdYrLV^Tst)F
zrA@2rQcGWBapHb)6*k?zM80TiZrI=3B!`<i$O)c(Kj&#Evzd5r5wss#+FdHHMP?&2
z=e!TRj$&UJsY|l?RU_IYTFa_$Y8hc8Hv+iOk+Fnm8iRI@!<X=G{Ey5_9EumJk{ath
z$z}@{Zw?EFGs}*mmRIb3z~7EdA2MgijELVC1NRGNTsVV?K1G;E90f^`S(v{z<GCF=
z)pI7U6VP_*zU`mLnaV+qye44~n4WxZdCZgH@TfnOo47#Ro8B<NQ&)RrbU85DF7fET
zj<YjWIq6$~o_Qwdu>1bOs*5|*;cBOKjO=mbq23Q90J_5SWMb33Y@VGG`2Mt;w<^=e
z6r}Tf_4;J6vA9xgq5Q30vmO6N(|+J-?_}y7r`|Gs(@zhNW0KDFs^)tahU4Uw;^(Ei
zn^t<VpD_>mH)Dd5p0*EJt7{j)P#_sEE?>T2nXAE7?sie%WLG+7I$mak_w<?ebxVEm
zCAy#Bbi1wl*+nLWcg6|n%+i_CbJa<CPr0n#k?YY_Qyy*G&T{G(&Q_bD<>a=7yNsvj
zsh}xz1@{6kYH-Qp=3~*G!{rVy_!St;(w4U3jnna2w7MGl))E2gk_`$UZ|zRMWqWjn
zDR?_$KB~jJGsQ!{{T50Qf%nQTeF3FIV3v4W#CmoLc$JIc<L-{eQTncmpPx;e&2@vK
z<>0`>7#gYw$w%S5t!)_<49;97dDDy}bb&LXqs%Dg$~jxocJL*Yp%49*3}=M@vY5F2
z$d?C(=@j&f=>ZsG$)k=m^K$D*G**`~k&}a>f2kuvy@JMqf_<q$zx<$}387y7RfmF-
zh9>%7T?zW#-!L#xP(kKUaDT&SzLdY8=$H2e`|lDqHV_Ko<sa6|>;DbrztAxL-(de+
zhqiy|gA!I2m6CcXl@0BUjjbKbY#c2f&q7}skZdJ29H5|Z-~N7~rIe^oU#?A#=AYCZ
z)#YS)3~fLFeIpwKV}L8j_V+kYe6Bn%RgkfxKDjH%%G!a)m7nr22%eYv?`9@S^1nbF
zE%_<c<rK+9ZS0N7*#T^T50nD0$;rw2?2Sx#l*A<dMt}LoPif}pXv@RI<l^E2aA5`5
z*qbsjb8~YuePCf?VPSlMV03V^cGP!ew05BS&mjLDN6gs4(B9nE(cH$G{P(!}1~yKP
z{FIcxC;H#>pLH6$n*TGCwZq@rdf6b;?;a**zz3%PFEmGUlm8#I-#!09`)gnSnU3%G
zU_2@g#`dB%Ads=Oqrg8D=lg4>e|7$|&i^1Pn!6fXsf(GvU^=|)Nr0K{1IOQJ|F`S^
z6IJ6MRIU$Pe`5a8^9SbdJ@6>lo4+hn|2Gi@nE9CgUv2*vUdh<O#>(k8xQey8qX5g_
zDF3JVZ!A8h-{0kr@ARKT@K@{0DG+$g$MnAwCGfgR2bl~CN(f3yO!$*4^q~%dk4n!^
z0V|t|=F8<g&b-2Jl+Le+u9qKdq{2g5-#W(?LhvmOPFfi#-+htB#@K7E?x<>I`gOP6
zR?UlY({|&mk?1VZ$m%dZ{Im?_-ASjb|Iu`nTE}K4goBMOdKV!6Nn1&RnS2~~zUtL4
z*$^gY0=8g(BUz2jLq2L)0j$kdEdTK@Y70Dm-yWGJ7B(gD+;=T>Xa$=^L?bt6%KBZg
zYMUjhFhfpeEI^Krhlr@x(T^Waxtgzk_I3e)`?dYjXl~8KQr<2bHYcs1axc(X=#(|J
zm)t^WQESQ|a3PaKza9XZ?#4BW*|Z5SwM^nKDlLz>)sg|~Ay*NJ;SH_buDNFGEofWG
z9GlV-k0}UfYEn1H+zKB}sWSCv$VK%!a}jJ4%oOcs)rN0y!;|Iihv{sxu%alil#^02
zLnOa0er2|j={?nee@&5bSpfa|HuC}>@Tr0+F&aYwRXNT~CU2+D7Z0H^bWr-<?%vaV
zr<tIJquGZ<<hc2$CF_bumLvZim0jFAX^MLTvQdoKO!1xyrXm~;w7Agk!Z+Pd2=s)1
zX721*4nZRpyoN54$}!^Eif(fp?>LUfz#FPzoFcb0yiP<?-hmAKx=n+xmCf3`<)TM0
z1m2XO9Rl}rj$Gl{cWALgw)=vf$lVneMCz{&t4@#juvQeN75bvK3mDk6NR6q9yi-r?
zS}iejjI_OSn|2!;i+9eCniwsUC^n{_F0BZft%j^^xCxtiHECj>Bs~RDvLToR1NyXu
zhcglwa#oV0)N0D^a!0QQkVzV;A9!il+O-{ZGRT(l=Zw|xu8!84o2>c!1vaS%o)(&%
ze;)uWvMNeHVx%L)5e!d&@FRIsR0v2CvM<44$_Yi1YkQ+g)SU5iwRj1TyU3yKRgsrs
zx9Dt<-wG0Zx}>V5xY;gfE*>8Tiopyq(3?%1^SK{2<?QyuKHvv^0JMLb6@y<xsWba3
z>&9oC);-y0J<o1Eb+U1qBXyED!~P^4EH}`Tu^F@L+?<UKs6U!B79>fvNdjq%#~cb4
zIFlGd5{TKT>yJOyx8B!M9Dx`}WbGEhKB3or;v_cLvtRf=PK_`ISf~P^+s9h?#qG|&
zjIb6o<hL0B;I{e}jx(jEH64c%70}<{7ERJW2U@Uqo~zwabJ<#I%9Q6yDL$F*+WUe>
z&E&WlYlY&ED+j34)~F;YT_Wc-nAu)1UR!7+(=&?|T?T};$yE<Q&c)HVFJs_fD|fat
zK5Z87iYG3ria5SqQSHnTTSwUQZR2mM)2XCt9B~+`3)cxG-j@VClR6z(QtQOvrWSUU
z71P=~9}fnj?j<oY7;E(bfJ1oPoHx~5gI0P=Z49ymoZEZrStP;B_`H7bH^*WG5lVDr
zxw-Mv)_@N&!d!lm7|Tv^dMl4+*CdE~_NhTtWIATCz?_Zw*Aq^lpP$C+ER%Ga0ZfIY
zVlGE_KS4UphqOdu4p)>v{SK$z=*%ZA)-kXR{HPwPv%+9XYbicatG_y`$}EvVj>T)*
z%-)#}h+K@?0aiOb4hr)CmdX~41ycbd2`&d^p1Dmgq_X_ZbY4bM53q-9t~qv{X)nYW
zf!cJOkG>uE;iYWGY+4KjWu!mOW2bD^c{bR~ZGp7185&2bhhlE)O0<?qbruq5V-~j?
zjkS6KVQDYq>`eMSox@hwKtq)hkW^~yh2oX^lP^r+cm7$UYYjQ^b}mvU`fU;#cw;)?
z^5dAPHxmUQa(WX-Ny?l~d6wc5mmoHoM#9K;#u<KTVXBGp8{#{dP@lgF4;mQT<%)7D
z3P3NHlS--`m?|sUSx4eM^d(1Xyy~ZvE5GQDZT(x1-j7D?vk@20SSO^G@HfLzS|`$f
z@_i|$Pc<&GO>}x~Nv9Hhk2S`vXRYK}CF(m?=17Az#B&J)8(tR7q~UwvLyl|WPC`P?
zryl_id-vJwY<cT{FXit77sj@76(zCyLRGUeAkfKNn17x<&qx4}&m(O{2Zytk^Cls2
zvL%6NH|ecLd<UAo_In`Fo#2?aX#DttDX~Z;cIifPfA2Wt_4PZ<FN<&DxJth|+{n<#
z4z@tmWrj}tS5*C$ShH)0wn9;pxb*s_r<*@?b{qU2KK3~?(q+s%sqp$^V2zCc)LhUf
zdyKcXYOJ<+EFQXK-M!C6zPyqp*bpP2)(fhb@2-1W*h6IFg#6T0>BE7Oq@23xx}TEK
z9i(Z9%<7Ta9PAR{(e3F$d{f*|lKmR6_~sZ^h_<)SK6CEEq`(<(kE5wT>K{U08^G~-
zGX!$rBtSZ@<#r3Rz=hr?WeWk4?YibP!SzNUj;9v3FwI;V`^(BNPnqx(LD5k;zNYwe
zZt0dn#pg1GUs~IK@#fOjnr8^hsM_S#7XY$0;k636iI^D6*<wVmyJWwZ&*j3{OmT(F
zV)tS4$%Yup6Y)gXyG^ivz1W8E2cgACNks<<U=?l+%Y;TT%Ue!wWAQYAn~<WuM0&`r
z!{T3XdGc6)<fyetlwD#`nuRUjoNTVQ&cQnWupc*$S1x|?OnHTUV|_eU*VFros8LX3
zHRJ(lDK>e*RBvI|FH%65G)PkO!)#)0$v=m%nGWU@_+c8a+Ge6w1>VVQp>t8HwjG`c
z3M;mos?vPej|$UXvBI|joVq=gr$Q5<SF?O8x^5~2SC?ath)JN`HM;stk}|=smM%#$
z_m23z*y^ZOOQ`kJ4e;`ziFiLPW>_nEh%PKANHST<F1}-OD4G$lBT2$Js4_@b^a0X~
zvfVWRjgBVIcVa)P59ZP)Xt{+fQtFXcH#UU7o?YwC?#B_knWfo<GN$r?K%upS=epk8
zI|2B;r+uf2WwhK+Yb$H08In>eyHff`SMe;*cS8kAcLxsSbM;IZvn>U_icqEG6E5aP
zT>7g^%LKEc5v2hp(H(%g@oeoiU#c22i--eccRYkRTRbd<xIIr^c?wp<ye6{x)-Oia
zivFE?lg)6RWY#BC>c(@rD_dcg{Ci`_37+R?xU@AZyi?)WcX*XgxwGmUx7j*!|1eX<
z4*6$K*f#2lc0YFS*wtAI{ljwlYFd3tuxyg;8xc*3*sY!zf0E(I&=vAF)Z1G;DwIi)
zj#s1e5@^axc60AGP`sF}>6dO#EiZc-=%#WUh?)*!4b4RFa5dAlWUC!y3Jo{dE^Iot
z4K&phEz;V#oO@s2!g~!d<jKoTyo*B@Ps>!cA{IWC=91u~D)*l^$yag^Aon0$5bj2i
zUzsg|3Ekl=yfjZ1E}l!)?r;w|4FAD=Z*54yE+v!Qb|}sSpf8YX!43+gH@9uKc3k!1
zFLH`=97N{UmqLNtvVSN|9Vg#DnQ2DWwsn{hJZ0mW-<{!!tGd&rsn+r7c}}vMC-;|J
zX&DiZACq79;Yf~;jThSB!2L*I2nz%q!h0Q&t;jGP@AOtNdU}iZHugNdxp>;LR|c`P
zdN3I=DW=UE9Fuvk$0x@M6<>s!;~Gx2it`xfQcv8<iGS+6As8YvdVgbsy5enO!ID?~
zK27dUXhd#T{2yL+_JA9nMf>IVL?b9DmnOVeXa}z$nI@cd=Sx1CHG&=V!OB1SK{(iG
zWCV~K7UY;$MxqHBW8I##n`xCwULu^p&K${lRbH2sp|dcV1!6IiP$dO>P7X{pYXLWQ
z8FeZXNx@r0?uO6q_m6=aMIHsy@kx4;AWe4GkG1IS&&dGrzSjM1BzBSa*l3M46_8+|
zp6!X4bxrO5;UR7}X(Zq>Q~9PKE{exTDK;)xuNFAg${U%HQRCBe(R{nycxy}OC8T$q
zy<zrq9`*9}z;HKN5VgRXL`(VY$><#ekj=yUnQG}Ag0tTbsWSzT$QKZUp;T<e?1aW5
z+1AQUA;LGUqGM^!Q0ca}pQWW`HwCsQJ@lSrZ#=pE;MwK#S)5x|zm|Otx(a{dfg}_}
zeZw6gOzk3WPvwd`20s8(Mn_MTX;Jf)Be|oMlw-}5odNt0qv}m32XmK8Bn3)4Xr&_s
zgk3SMEng_<-_z%}!=@4x??NU6$GK^VMasP%`N5|bFz!3O%5uRCxh3IVDI7@>OE(n!
z_S@t<hEu{NY9ggtY+1~Lqu(IE9>XP^K%J0D`;CTqvWmnve;c#V;-5Jspycsm-`x3L
zz?)W7R79H$e-%bTLYgX2Ad@y)$%{w-`g~NsQrOV&_5S8Wp{S^c=6r`Brr7wsm<Su@
zFFSulO`NrT;ucqDL^b=GGMgFIKxP|=barGBn|<asTZ0xnf1msqvW>TB+|16TcR8<b
zAnKtw!;BH6^KG##92yz{$Xtk#OKGN22if9h3-gFhLmFY#q~niW%&Cx>+gghh;Ug{1
z9Ka#Uvw(N<oGQ4t->;ApxK_5(&Z=X#{qfW2D5OD(wUGVb*In#}n+1DD83A{+Jiqfh
zSY@&HN1iBmClC<*X$@sQUQ6gsHf)V<*NKQj*;yXCmTj`5GQO5Bio<%48M8inT}iXX
zigC%s$foHroGL<@+pU++A|+XVM)yN(8FvLk#M(pWC38Mli&HjRi%HOTzQ#&g4=nie
za<6715RymbJ}Le*5Ba%6Y?#(oiEXMl*&DH|fJdf4lFE^L&6ukApek1+B_XO=X~CF)
zlQb?ADUWtA>B~hd2_&0}(&UgUSV=O4IB=%-qB(h_d@$xH9h(=U0-|IGjS`B`g`7=A
zE*vcvp>Az$D_iSApM8<U7GJvFD1An$&HY2mP{3sUSR&n6LrIK}ALm^06cr6Z%c~v}
zNcFm1Ll_(x$wR`X8GnVREz}i=np58XJl{VT(@hSy3cs$W9)vMya=@xk%ps@1UdOS6
z_oDK3tTFS>a1NU%lU_FFB^y*Tl<Nn7oy(1^rnsV<nfrvmr=P8^!}Z;DC~Ju>iOSEG
z|JVI6(D8JYclY9Ka=7L8R9>zqnuI!+R_`Gcjldomd^@6$zuc%|$a59!oBw#|v9Wgx
z6v!EC&y*)XRo-O+Ugc#yIMZ@DQJuIZNAlc~T}~iu3@091X8sB^8P+hk1l;!_IPMg*
zE6oh!Sj1(>)(bwvJ{{DSquQL81UUW}7=!Fm!KdCQptp@WlNM-Yx-N*pMUk~Jopth8
z6A=(8%(Khq_3oL+ccJZ&oXBQdl7WM0<kCfr4@)vU7N2{2!^k`pXqSJ5Gzwl`q=+^t
zqpk2x=#hE{1)_PzXSjQ$B_AKH<iJ8Iwu#+9a+4-Z20*S+u3Et-p3F$_Dfa!tfuS2u
zQ*0#HH_n83w*!{;?SAvr>zfJwlV`D@ddUqJg$n@1gE=$f-XCK08V+uimqH3H$oBeh
zE|A0f+0%V92DrOdkeLr815b$|{`ksnnHYkT{oeiEL*71}p<tOfE3bYWS5@x`uY^Un
z<;<!t+m@3@@g0e4kkeM-N&+2QiJDedX1x*n)&9y?Le!9)WMtKG%&vRoAXPcpOH^K$
zh>I$lo$qw=vVFZ5_uRP#Ei!JB=-6{zqw|i=g@k!|_{-^P8L}>V{PXd4d%c!;%3OmD
z?oTu+BM0zfjpp9&$6-Z|HxGi7N@W}3Ba%UmAc;>(MjXb}oy|wn!SkF7oGnwEV$wW0
zc*gdd01pS!(w*J47`2-mV@r<Yq9fc9mLi=vqxt@8O*_ZapN*D#{pfv=<Q%koZl=Zl
zm_ASUhp-UG$)sxYWBm-}yB!$`2xC(E=fjPORzqa$<*kL>!xqZ<U!BL7%a2ZuyugoZ
z?*;8Tz4+uv6tD}%3c&OBdfcdolL2*}UP*Rhm54RmK%y<L^YgIaNK09MH!4|x4d0I@
zi9am~Q%drjmQ7S5j$B9%aPoX>XfN3`BWoazHZ}OOd(+N>w)<Y5<9<<rQjSCf3VHTU
zP~@7OYsJ3YTQ8As`o5IJb-hJrHPH%<5*9<snQXj4s%&!kNDiEnerlv+$|b0KT{Z>y
zUWB~n?M(B-CsL6)7wD!tG0Ja&jl=MW&br|j^_7cUQma|4ET9M{pBAF7(~9Jr27k_A
zTcDlpDxRUY@7tV80p3j9l0`{B50`u;u~Apn0n3Z&NxCJi3G=Rb$S<1V4L|(W3xt^q
z!tR8Y$~<&l@BGRMX~QT~JEQWnQ17LFOX#6@2_*diuZ(LfL<LADOS$!_5U0tlMU0|d
z06XVAtq!g!p3n-+f~Ug>Q>8krJg$;9tmzS6hH~T_E1m+*EY@F#X1TScHwu`tiN<&b
z7pXXEKL;sX=NpLNoKoxJxuKOKi(Ov!*j4XkueCxLQ`lXQ7&V(hXJlzoP#KiH^dxlV
z*_in*kvU;xvyomAy>0#9B`vhWItnV^nfdx=7vfjeaxSge<VY7m!kY+>f-m%vU6Mr=
z$@QhIX`g=oXK_-a+F5z$QbP!agr;clsxrZe&(TQVfG3wzOh_*YvlZ0SQ(af#JDSLh
zrhN>a&qAElqo?^yrV#jLNxSiH8IC{ubsb%!5*^p|{bb2{N3=T;#68edxyjODY>*(G
z-*M;-a)NfC)+@x&4(+;aud`K5EV#pRVrbJI01>~OkWhDjhTxVB<i9#(b6Twp`b9i4
zIcSIExYN@&afKJ2oiRdKRX>D=y<qk;7t={vK|ct`Nm2HKMnT?ywo>PN^0xd&dNX<^
zbGWM=m5#%NFWj<r#xr15r%i4W%bbHZG5GrF>YjCPTzF~k5L@t@nqa!wVU10UR5e~1
z8-z!;DATsLnayOLa=%r<V{n7YuFk564UHl)a$t+fGLS<iOMzx|tv0I1<hA?tmn}9h
z1&{}?oH^3SFj76^`S6Bq|E#=7BM!x3+t3SI;im)33Aqt3u6tk^-hbR%f87~*G2ZRe
zM_Da4#8g#Pxl&oaeuIN^T4Ekr<QJJ|1)<?ezQuD+Vh*RE399!h*<$yLYCnHt^PMZ^
z*>&w*(`Ai;MK`eTv+1dz?iRb7r_~Sx8CXFr6O8h7FpVsG$rv)z-FmY{?d~yb(DX2|
z(UPO=nuD;Vl7dKlXgD|-=5Ci<04(|%WxJN%AUSduoT!}nl!$w?FN>Gyj-M`IkDq5Z
z^*(jxl;Y{3m8{URug($H2CDJ$k_zSIVW4_1A~O+EDcWLadfa^c$vt@nSNFWr#Zj{y
zcdFj)wVX~%MnRB+Yq}w4%5%BQZlE_<OlP4!yE;nnYvfLP@9b&S=RJ%2u|C0%zY>c)
z55snFym_3M(}zbFPM;pm^Am#R%w@lgxCLC!MOE*GaTlBadVUypqebQ9&7QpN$3L}Y
zlNa%9v9qviGr;9->~f{QuQg}bg<lTff7i+Y7BnU`wrv;4Y){mEnQSM+6)~a3yLb*M
zeRy)fmV?2tb;AyY<93sc?!T^!&<@=IC>SSOcv|^1$@)ci2Dwy@k;PxzX~c1+rlyl4
zoGVt-*rcAPKMwWI!uBa|QD{W^St^rs>_(Oh^8uHm-G)IuPV5-UUG8m(bZ3`K>+e%o
zK%{RfX`8rdqiAH)Wun@i8Np*{)6hpOw}=T_HJ;B{^h}Id><1hW8M0qO_1=5|?sZb4
z4N==?6w}H(<=GJ<21OJq<0)5jY{tvd%j{Z0Lo-(2GQIWW=k`$F1uTvKED;1hzfP$Z
zNLol!5O49zk1vR(9n+Td2A?5oRu=iFUL^Ynp5{l|XOxYs>C}^jC(Ej}uw>lZ!C4P>
zvYQ-zme&=KOREf?K9k?Takf%+b9aH}Ehkbf--v_0D-m*j)ZN+L=^o%4(N-4{C$1%W
zn6z=6kuiK1mIHQ$MRvwFtaqXylXyD6)xgpEYSVJE*UpqryFw_l!lW)Ua5&_3Hc5PJ
znlU%Wn!p+IEpw%`Ki`D;4b22M=m&l1R=?SM-X7*h=OmcOLV#AfxSpJ@(B5Lb>@w05
zA#b|Ikm?!QwvvE^DOGDH{`1~O(NrS(((-U~810C5_buL8*3XQ|7Bw*TrOAH{Y+rt2
z=vy5`a(xLq@^%a(D5j{clDChnvUodNTae=NHffu-vp``O<-YnQXCijc{8S08cC`s=
zt@Tn0JSw5sz`%ft``bg&r~3!2l+N<9t|7q+V6Hzn))2ud?~}lE`46T^6Pw1nIi@=h
zMZ)$7zB&S4yzT8d{PJh!xTq{<T}qn?{Eicr(M=aZ!wB-xeXRi-HjR#*EB)hYHw&06
zBLQnGg3QVKG#l#=3WBti7u8u6;U-mw!g5NtVDJ`O(h+HNvemLk>`3a{{OSnbh*^&E
zm7%qJBZ2S~^~{KmE0<yx!;WHjXA)5ao=_YvJ_dLAiCI>#M9d%f9}GEcIzH2ud2irY
z45KklFEOW>>|7G+s&PeKO~h`P{h}$_c5-evswQ!Mo_)xnu%b;MVIwAuKu0KCeU^3`
z^q^Frx_CGq#y!l4z`9HKLE09jUsYRw`qK6-({l{W5?iQY55u_bp*P?R;rt9;a*B)9
zqHiqWMMu(YO47+eD5AyX>hY%BR1AeP?qMm3nZf9Z?hxt*T_5Y%;r0DveAypnGY8Bm
zEQWvQTwfCUi7;NeGnaAxg%Z~_(kh05cUL@}aya&EmMB8gS|CxO%ZYAFpe=y^`F5k`
z^A8yJRbK?N)72j)ab>$(wu*b+CtflN-kd=^6(>rA0+?x|edSz%DHjkQRIiLdv^57k
zLWI{O!JB3sS;D4oe|WR;#w0_zhj$@FJ&|pkei-6X&v4k1!|Pi6Vu(OW{8sj34hjNN
zQw5V}I|oJIFSWN2=n*cBVNA^g47?6e-d&<wYoc!t`fG!0y5lyvAXkco3E*OJx)|ww
z=Z{h-L*5YZIE1z@obkKyAJRPn(+8m<LDZF=*T1D+h?sX#^A$n?22%LeTxTMq79kMx
zvDR{iP`6mM6lKg6@9=X+%%u||cf-}}8=$g-0Q!6hh1kY=^tUWqsWfasg4xrJuC}9o
z*DH&lp{*ZC@B7}yTeXc-2eIzVxXlFi%b@Yg2FSdAsga}8;&mhM=VbCVZSq7;cwy+=
zrZD^?=&U25c%`stL~N0N9m4gYM}A(YTekCF4XH*1D#ng?<L=s`#C%`Oj0W`+24SES
z@aKH{Lu37p%iN1XK{F2b7*tjGB=x+xfVjr4Hc(I`*(b!|31RCHS0Z>+AZLzQ_8~i}
zaQjnQ-G%?8X8cFc{&p2lA;Vy`%4|L1I@}I#y=tMTmy)0g?b8@h#R85~s)$2@2nV-3
z*O3k%+YJ2XJ|m~pz>bY``F~I+Az>4s`crEEH5e<*Y4_>;yhIDO>vEI!z3U^21B%W}
zWGS6*)CFr6|BwUn0zXF}p6h$fx<G+n`nV$+siG0H63MkDqqn|?$h})@BwdFzoyy&O
z4henub+3%+|3|Ff;~yfzttROsnA2yvvp64HXeK7eJk$)6r=3{kht5IbFi7pG&sO}=
z2t~YlJYfbqow#dkMM?82`425INH$MlvAOT@@w!ac^=~X5aj(%v0Mj5M-;Zq65y2E$
zzERXp1N;{3O9{d{R*h8^zDD(QY*|!zDGanuTDZoXjo?==p^#>_N(vA;5X)^d)}Pj?
zeeowYN!9{S9VlzzTqKUTg`77VK_kY_VT}UMZO?BO9(|x}p-SmGx2`ReF_qr=(LN^5
ztI4U-w4ScY8ciqbZmx0xPa_8Vp{(sxT-R#WF>9`I1W#vkB2o^UXFk!s(2S*7!Fnzc
zdx4UQ+elFYi=bQ;{KkvDhasfp^dnxga1A+0C5aL2qb*0?OWdWZP$sqWVn|$f9<P<W
zDHAnIO3zB?)E{6k64zA2_;eLl@1)J$eocNdDCJQG76d~QoIloYbxSB(*S|I1G*Gi@
zWJ_f-hSV=$rR*Xr?3RIF;y=5T^;-des?Pccs+Mosj?q-YYDOP{8nLrZ=5;Ooq2aTz
zON`A`lvIwK^$VB}*@hPu-)T9b7n~0{v{SEJgWGoJ-6@0k-i5R<T25N=XDzVNA?!8t
zOyG^@z2}H-b}Z_m>u%;t>F%AB_R^a7bl$b_97MFT&7nI)<XGFdKh0oG>)5&TXo>Mp
zK83XtktIgs?H*m7P-Mv1*_|<Ne$Z6?UySlEBTgT&f}t*Nkr$UKc?NrZi_jL=GH&%}
zLIrK`q#OpjSUhImW5W3q^J7OmjzAXy0`a=7NQ*&fY^#`qm^4UJndBdd@teCyzWBHp
z$bw*;XW6Qpl57Q7vIE3zvF6ab+u`Hx<ldg5Xw_O-tS73Ao7u3=&HGRP8G}%SbH;@^
zJ+fHGZ931e>5l=T!IbIL$DCgv4>tzd&FV~0vo5vZ8k;Vq)fN9ElwZ8OitUK;7igQE
zos_Hr!wAY7o+<W|u+<KEUJu!&|A%#1VGt45Jw?u*Y`>xQk+!Z<eNFlI60Tw3-q$mY
zrphcMTWi{U{Ktv;0`{2!E}X1?vZr>Xt0jTE^q)(~UySCASOG*V_>f<BLi1x@beNZD
z%pR}lA#@yfU#C!J{>`6sa+ZSYjw@F2$B@T+lq%s~>@=Nmy-N4av{f`Tv|Hy`!R1r^
z<IedzM)d>HAI6hiSM;5Hl6v4p0NnlPVm!<Z`jO4>XkNpty0lHa#eLqtB^|Uk9JS~6
z5zBSbSzhN}O1<kF@bi-+Jp*G-{3bQebA|9!mRxn|CMLTWK1;suO(fq3JFGh50`<lK
zg&q^FN{S#XPp*V$;ho{;tFKYi?irJNXQ%gX9I7}jh&tdI*jHW7S}!4R`~6Hlq~PK4
z^yA!=(Stv{6*#^+xX{<rvq7Oh-lo8rqU_CZ%j^y#tmKoXYfkP_1u2E)?>V0N*-Wgz
zu|!>QSniiyqErgPqY<59>y0GZ-+ntd?2s<JLC&(YTghs6Is4rXO+z8y4<dnbL4g;>
zAyTy~#LQb{>{%8`^CxL>d=U{lS`BF<sQhEFjn+zjK->=_Dlf;~f+jv9oLJ)U)|m?v
zaqd87Gfk9toEal7R7cSkE;W$pXv=-v9YG2pEe__YAvM#F+S+N{h#o&;Ri^S)l=mTx
zrE5O~(i)3c@f6K92(Aw1l2eS>*jfUHrWIL$lryHF>=#GIelL$ks=fJjOP&=|;Qi&U
z?2EH1+)7D=xTmitrAqhTC+!R7hi7tyes^Rjzb!ubcRUT;w3&%E^xvLad5D_CV9WUq
z%y@p~uBoU#U-Zsg{%FuM9|<IGI$WzahX=lip^;0K@w0LFT_$1;Fac=G@!NPM)5v97
zx(0~9y&Ol*9QW75o5)Vkxb%){5g$@BCRo<edlsXzc8ekPc<`8yRu*!=E6>HY`9;R4
z*BsI-p8=c^Jcln`X_)IzOjOBi0wKD8{Fk6Wl<;ZoETr@|fR5?tr9+DUieV%9>ahFG
zn<QH?KqS_>;hi2anAZ-TQM=KX^kJw`!Xy)oajy}bW;~9c_ah7NMB>IwSS{0ydLekD
z)+_mP4!wG}1MD$QYe=iir$F~>YXUo5S9${d@|d%IoQ#QBDuno~`97M|vFjBTstqM?
zqJl1K==H%7cfQ)w(c2A2Qn&Fo6CR$;c0!Fm+ylQk5stzKpMtnE#Z`f?R7)u`f<iZi
z#S*ji*OFsO4RzQk*>pr%^Jkn9?TXl$OU%}rp9&_*1EK6h=0EPW>FMcMQw8rGt&P)!
z)y*dP{Jhd?;|E<~2TR0@BOo_z&mrrx9h;2|-*LGT)vU7wT1-Apj6b=L54#QRZlU*L
z<_FJ!0OVFI!kz9S)m`0Fm)ZXkR2VNDk!68HVKr!Zue$k0*-JP^qnfk929MBAi0s~n
z%1s?R_|w3smb(R(@o6Pf<DGbmL<MRD{zPgl_J$kib0c>(MjO#1M~PH-FV@u19Gz80
zj6`z8#PEEVA^M>lNYjWgb+$6xDIiwh!TL4$w#&xx$h?d0?b{HqXE3))6-w+G!iab-
zyV}3L?5Vg9_=Epcwmo9y8f=mIhJ@pU58CB59`;@o9*Kf^(LkWCuC6?hLzrx%O!GFI
z;qq&q$BwrA$RQeCk6^e0bmgJVWY)>%H-|zrr@hhOd5%1#NCDDbG#ad3w?kLTwOhhN
z8i_B;cjq&cyD4!&=e=?Afq_eX6Tr=VbVBuikx1whq8Ru?tqc5T*rxOFuH9*Gu+R<v
z1qJHSOvp57I1in6G^bd8afQvbd)(`?N5Ns&*HkbU_TDj@S$SJ|1yH&-9MgqpiB^3P
zR?GvxV=bjxRuo0Z99{P7f%otUJOG@p$-e3RC6N<!lhZX@IXtf|8PJm_fp&nSK(w6n
ze82DF*wag_*-DW_dbi0r?~=(8n5nEN`zqkA3dl_OU&^3^3|b5!hEVp=csubLq;-z@
zL5uADQDuEwsPflTJxM{iOkk=5av_BR`pIMws?*W<cWL~X)=bwMTaFV%>ptMn<;l+w
zrC2QUwFwmyX@b69@=~4lkZB|1NuOmM*>PLgcH_+?y`Aa{H4&Zp`don}9z{N0AJ(1s
z`Qevr7AsAS5QeRy?Mzq0fpd0^8YUnTCPjCR!&upo*=Xk*lL&*I>(Z}v4l5Z3JjMUg
z3(mrD(`74?GD+oUk9i@2W3v+(W9U|sgAR3+S9qzMEfGY|vBL6SWuiXe9n=vIO0w|3
zXNhgD61Jx-Z~QAy;_tAk2Lntmg?8Qi`w`?)3KYiuFc?fOzwHD&lhTYE4oo|&)+-OK
zi`MGbt@UxUXe35UO`sXTg8eS#yl9kuSM^DPPU^?S$k0D^XvGXAly_)7NUXl7TO6r8
zR+J@@kk`2KaZH{=Z*a9szdBAU=)HkY1(Cc0WqcX2RWrxYb<H2K^WT<0gpk>BtutEI
z*N{z5@%1ngUQR&H2G_i2vo_H~UE3c4t=~ynx5OeF1cB4r15<~~aN#@&9+L&7H{bu^
zSTDq{?Ea{y7MSw;#CO8EY=7#RYb&^0MmyBPnwqrf(`mzMADgkZ70_{+#w1E8GKqUD
zsxs0)@#hyaTp#hX#4IXI?^>E*+Ao)J@QLDszk2A7nLeEj=bZaPUxpuCem}M$ks?L=
zo4l_l4%gmgIm4HM%LIH`wPDdeo@64MSH2G?NDdK$+;)MfA3VrL>s~1OFo)Nsf!V={
zbD11UY$d5-uh%>*M%FcOyBClX!Z^@1V2zG`4YB(%+p_>#DGiz`u5i5ayWcjYl~mo0
zaeTb>ViWZ6vgbfGCZetUIMcY4Z@F;k8zMZ$xcOw%ImgEHo;*`<UwEK)V>$~VCD--|
zhhP&Tz?w}3@~=Lb;e>F4&R^ZfY+8}z^u30B-r6V7X`2O?4S<`UY7U$==4;I%HHJfB
zsJu5Det||S*!wZ_7uCj>B1vY?iSTniNW63QN@z3f6?`04D-4Geb8JynN9?gW3z3%&
zS`4H;Tg^_AcMuB;ba3xTK(k=*Y(zkU@tRpX@7g_CB%WgwVCjkPOCmz=T42X}y3%h6
z+EQD*{l>r$a6Vx{)2?d-QE8UQD;JhH8*36?|5;PU=46n9hP948Tcyfe8C(rnKybN9
zE<*oH64X;TQacBS_{@xTFoArp0u!;El@zJGvxjSM+t1*zh#r=a1}6+qN-^0KrxvYb
zZys>sY~umaUG|cz<m)L&fmxQrr;WYlI?<9DPA0b*Gjq*)QGEccUN~m}F0}1ROiEA_
ztjPfCsJ7UTia}$bVw$=-NG(b;Uxk$>+tKWK;@<acvXv;4c{Bt6l2?_e{M{(AF=*oJ
z4+A0cTzsA9-Dds|w%xL|fTKaMB(F7zqhLyZb0Y)8l`R;TNJ16IA3qvssPL&P8Kn>V
zNwRSvsq~4&8hVuI!Po9hLPC5>iU?CW=let7$r!@LrQ7Wps2oLgbulh61THhBKUBeI
z8zHDjeGND6WH9v`Pc;2kz`>V@v-y<%L$wDSr`?Hk&tv3fT1#i^LH?EXe_dBEX7P!H
zkDuS0Hwh}$G<H~)SK2?C5ao|`Z)s?02@tOZ)Ld`w(`xGf;RZz)L~@a@nR=h6e{@>x
z{&doX{fUgahTkb}@1RiT-m7aQgqJipR#daq9k@GtUWcj$9OTfu?8%s|G`8Yj3v1c*
zFx38ZtmE(xJI?ZAVt5ZUdpIK&BVCJhSrjM`(+z-Ttx|OAj1|$=8y%aE)n4pAXAl9M
z%<;9%!9Qzx;mim`t0SVGPtATFRQT>=|LPh+bj={=n6>zmNH4ik$e2F7!QNV>6pP4q
z<66r-n6vH#U4Uhcqm2Iw$civ>#a1TjCbS|^d=h#3+Bl4N3=BILU2a;Qh2Y1~n5v~b
zavme?3>_eWl$ErOS=sx|N3-bqI7Zke%b7$}dSDd=N8ZVVyy8+~JxE5wzS3{8E})6J
zGN9=*rSWDbUdHCC6c(X9XNbUVnYN-}04X5J`Eed6V^>DUe*B1<<I5&UgDwnJP^WSu
zNpz)0*orc$x2!)542f%#)Y+u2zY__gNpfCXoKEoaqd9_uShdCXB-Z#gWwworD;O!(
zJBgz;G9C=h@zgOw>YaKLGv(R%D(R|}k~kEBRS%e-uBls#84V|os`N7RbW(TrzmZb!
z)E&%M-Z>L+Eb*90iriQ%tIXG%D+RWkzFb@Vt%tHvn)(sP6jaUXO620IW`zLY3iFuV
z)i6eLnf=wC?rtS*?Gm>$#C-_06Jn0Al<__Z^kWqr0KjpyeieEySHfeb(<Yo{^W{n0
zw*9RNPet#!1TG$(A1TXtN8M1(cOJ3By2s6@Lm!iRDi#W@2|)igsi<=V+Ta8kK>7hA
zKvzY_(JC3ERJvE$H(yHStK>>|MzQG}uFhtijxt_;G5TC|hbm89+L8of5`8QK?bB7I
zx!9e-yfEp0Tn&Sh=<;_gEF8C@z<bKT?#PPz>@Lzxoo2MsK?%uGBZ}njUP1Hhd~Jv%
zM}bp{CDu_}#k*M|36ZDY;of@y6!tT&_KSB}+Ub31oakiGC;&sPU&5)~RDOV%sL9<k
z5)0Iw1Pilum~r?0iq^3{$8rC3uR}TbG>1ILUMJ#Q--zCYvWK6OD6pSq<w0Rbqv;sK
zJW=THTDY*KxMZa2{eAxf>Au685geF9VO^|)ITg|6K#XPipI{O0Z)7T&K|j@U5YiPr
ze6K7pC5i6n=M<O5`Bk0B;241U-=nTu9mqBXsPR%`7eLmqF`|W4`t=lnT%79->aM<^
z10;YJ<fHKGJ(VjuBTJ%Y*9UDW=7glaj|K)`3W$1CNIKsMDaG6azcn=YW6fe(%~R*>
zUwye+K?eQr;yn9=@s5!p=SpMllcwgNyuQJw7?Gt<RS)1%567rVlZxywD_LEF0o?>8
zdpyCCP;0<M>gM+~DVqz$dU198%D6YR1<P7G8|unw7W8^EPo*KPO^22`bD>7GSnV`7
zWD!=m*OX^D<~bZJ6J?uT9Gz#6n>l$$@0uZXdPI$8_bAC`o8w+J_O*MK)Fk1_NM~nL
zHFi9_{>C4<{VIyk^r?*!^iB{zsh-Q!xgCkh%7(i|AGjNaAe9Fl4+3VkCF<4MIY84v
zXvdZct@Ta4<@sDcKed%TXkmLe&CN7by%~^yNm@S^B%G5x5?=>SN`Q=Vbq1cS{5S3f
zfHDgoopV6#9QKe)0v1Sr)|1(m#pFydPZs-2=FAyR*Or+d{XT|-nCdI%*DzU`YEDNe
z$MGHziT_GgUw4sjb)$tm{#TdK=B)n`Oy3c$Zt}5saf%>mI#-6vy!Xa}VGSEgYi>0U
zhchd-o~MtFo#1&_2N&4P0TgpuO^;f0UhVeyg8$ey6%LrQ)-TD{fYFkx@0&l0vRgcZ
zO77+xG0YEmU-@cww>+gKH`!|McLs)K%{o53cZ@;rb8*Yw*schHbDJTEzqZm6aX6CX
z$I{zm`}-;}3&to+5DkWgMt(LlG^FF?OgcY5fA{unG7Ae!glMo%HvVUF+5r+<f57kj
zXfJ0gLaX4C*-6h65hzn!0}p<zj0o3d{e;+9r7Pm~yNXPL8@IC!UwCc^`zQ|Y_qvjA
z?^%?90MP6?B`u$)_iJS2BK@ww7saNzgj*j5rz<ZX*_d>Bd0Be<D&SR1-1{Gk#h7Rf
zl3Jhf-n{u}Z(kJ?|Il^g^pebi)3vf=BA06YGtg4W(TDSJoeTEAo|a}on93w==buZc
zlS*_H6ir>o%;aB}GWo99OonG1hxDwow}z4?d2L#y@V_;=oan<B4oZ79!=dA=V2YJd
z`ND&#x#Hguc_dy0yZgb<>hJFf0XR5DQMy^k-B^}CAMF!uxX&?Sc3gb?=sN~R&dba9
zZ$)s!CBIyB7=!#kAhRMaj1qkQ`uwo&J#sOT14O?-S$C_+B-jmZ+cYEkr3e&pyW0O*
zWAT&TV!DI?J6M}Ho9bK%7`ZwB;t%qQSBsytEN_*lU$jRqRj>S#;dO6rvDm=ubTt37
z26>6uuy@>TCmmRAGLj7>b?f#7dfXg?+BjGBTz>>i)q%7uX3EmJzauz5DS3Gb$Ylzo
zXMnFYQkV_lUoss^_62NhT$bHvBZxR71s+#n@1Jf~%wK{T`HXwbv+s8Zpgi$7LGxni
zJWiCK7phIizI)(skNka#ol#&|1RAG}hb#A@M}`>K1d_?fYFGryKQOg-Jd%{Z_`p8;
z?oAdzBD@<SmpvcAr1<VBh+W*KnexWOd5yd4G7)xS=F-yA!%&Dp*r-%l*tR%BLkzB;
zzkl#UF<TdtCa4<kBG**iCMgSMc@~EE!^VQAm-FC+IhZNul>ga-r)NbuoyuvGzY)nh
z$|qQm>DA;0?;Cxz+3!FT8DCNkt25zGMmz3LMl+7Bz^5;%QuQcay(P9hdQ{gP6K{IG
zz`Qcy{p&%`eJeq&@Fl8R<9cSWKaqzFzS~N2yo3NJ6$GEeUMNca(igD`SEf-NC=o^a
zLfM@Rk1}(Uqo&&J=FjzX#&<cs0-baIhGFl>(}2_)@#i<&W8TlKs01wjW$n-I*!;xI
z2L9&gpvI>@7u>xF&gE|}#8w6WLixz_H{<i7K;L-~TQ6cJ&pT%aqUm3e2Sb~zJi&j=
z6adrF(S;PWOzehuKc#v-fD-K<Ke&26{Y+xBTaSuQOq8HP<4vPN7k#lo!g@U~-sG_y
z-ON?*t%?Rt#`c8mMy~~(fBw1LQg&F()c)=A5{2h6m@2$AO7L-9jLhR|U#>5L*myp*
z+gYt#L+*}GCw*Aik2cdYl;B&pMhPSN_j(UXpS?vIxfFba53H<{@p|AZm8O^de<v<_
zNgYd}QOK~$=f-@?d)fgTVF+gmI-IS%>_Qj(ni?d^;kc_vDCorpzFY(L{t#n3shN~#
zy%xORrMkbG(JL}6h@9f8uDrW)8fNYr&;5Ux`U;>b*8gu1q)Skc?vn2AZUhDC?(WV5
z(%s#qfOK={mhSHEZg|(<_1^zGv%?HC=j_>i_NniuRx0iX9)s*@rrS<RFZ|=`>x)G2
zL<cs{<5Bf+{PFJ;mi^+qNahhDoa3cN7O&f7mtJ_0XW&yDIT^rzvrmc#V>wW*PZvYt
zgjQ_ZCCmBU`GQ4LFL{u#RbPLq^u9Eof)Ot%^d9c+HlsKqDdCc(!N7;j=Btdp+bp65
z-7MO)vEAAq*UrLl=ZUNR`&^v-eL3CFvJemuei*lI*|0uk7V?HA^bSRe*0Cc$jxxPf
zzbKp>b6o{LhqK(AiJ+dv&&WDov7fEg5@tBfa2fmJ@!V)UtbuO#j~qtWBG>p_R;6g%
z%#l8K4bitIuWV3>xMbR!PZpNh;rQI&n`l!e2+q&XL#H9*vgJD+&WwX@S0(s7u5%qm
zd9o<%Yj(m$1z1ps@kV9nn;_Q37m6%6px*e7)wqx-z6)jDJRuK-EkaaIyR-A}Uu?Zy
zk?AAwphd=E4MvmdW~D|iG$!=E)8pQbSKf;jL19w<p<E=tZPLO3{Lrv0)-5nl2eG?Z
zPxOND#g7;2@-XB%{lKp;9tI}tukBpdQ_2xguv(sWgq~+Y2XmEqW;WY{3C8BQoum?x
z`EAd~HdjxVqZoU^_Jr<xIRS9^Gaot|k9tP=Vp}G$V?;~Cx?P~*<<(i?M~Kjm1_{yj
z^p{D}RXE2avs>?X(kS$Un>@3|ICO&r!~qsOZ5)!m;$OpnZXXGePZ-nYLnk7`SeydC
zSuaXFlQVI$FH~_1@EG`h=mm_Wo%C#YAu+#tKIvSoG@p(wyDSUI;c(<kj&R12(sccX
zxJeRt_Ir7mYjchG%&>XobUKq!D4QZHamG5Qhey|PeZ%9byONX_B5IQE#@>2)qmiB^
zCX+~8_zZ)R4|${semS5quE6o$gTpYw^BOF1oXx18gs05xMm(F`JMnmtR@L!hv!S5k
zu;_ybKys~x6mfFafnHnkyq;EVsif}W^$EkI%WXbs8j@|*FL{F}RGJ0MVOfm>z!UoI
znX?w4_kbZ_U*V1FS-Rhag=+DD!np>~b-3ErHn869bvevY187Wef#vBsQOzX?A-yn&
zz&-en5~|DN_4#)AL4?yM7@XlS!c{ZM4_K`bMpn8*FyJ3%H60>S=)q^5h;om%rnqe8
zU$)OW=+QH6XlJ8MH0{rhm=BygpErY&WmQ3x7tW&i%5#s$^;se5!Z6Q*X%qYp?!ZrD
z<R&D?+N`v20e$En_VGv%ddr4sCfuMGcF37=hIr4{X$hD!s^Br#BwC?G%_lQ(cmhnJ
zLGUn1<#;L0Cl3P2{T;tr3T>zCBqliwz<DEDO-d9Bt6mr9L09i2;N#q2FZ!}t6oM)Y
zg0$i)pVX_m14J`JX_!GP{Ev>CkQ8YgMYMSZGD&0YejsXgibU80<g3T@`KwuvQ)VdE
z!sVt5-%vAs0)k;84HXqVk`EuxSdUxb)>68yU5@(2M3E!0@C0QiN!|qYYXHw790aB2
zS$X!GXwYd!%_M~Z6KtB?B*w4X4gp1xsTvYM%pAm4cDz{s>kW<$f5_T;5ITu#>`b|q
zg`-W|vok5wF5+Z23F<CJOyBfRmC1(lO*r{o|F?+YUYs=-J>Ln?4}xdr+aS(iAqKXd
zpx!C(ZX995=yboUxhA6Iu6P`v%o;oV)h3^;B#vLT7A69>dktu-3$b)O$33y$(RIJ)
zdm);?rL{8ut4#n3>a|nu7Xp{lm1Stov)hjbIEl~gT-}_+K8F^I<`H<pjxfk1uNPsX
zC0s5r7G-Ecd*jDn2qafFg|@#oUDxrTjbu;C3rxz+f53cSwRtCjaXhpQi3^47tkzpG
z83c)Pn1jN1#YhmolB2IZ8{p&*AHrlD^B5yRAZL={MW2K?tQ+1>6uB!nB?3dcAIF0>
zgZX}X@foc>>aAr!I|Dh#x|<)Afkf9@OGiov3&@lx*I}%WUZXVc5tziSo#{5QyKugd
zl_vC5qq<4iq7j40P}0s6`>t4L7WG;QGL>aVbLEZ#dX3*%l9?@zJWj*HUR;g}6djf~
zkCYyQ9D<7rME5GJT?S$cTEOgDsf_Y;pGcH@&Zx7s?jyfkpm@g{xN->0$dgpTn<y4W
zb;JqKHwai-cRaUTO~~|~XR?@}JYNhk6eiG=H{=eMD^yGWt@n#Xpex>?`Jq7bo}v2W
zo!`27C`Zs`>xrsVRdbBnNuU(nUt%Q-**||DW_3DvZ$94gmdAD2bgu$r_E1xTcf8mB
zu8SV;KG~*l2i;9^c%^0>NtAad8Mi~1-S8D^uTJAdAEqu6pXamNJSGmWq0fW!Zw})M
zV2&-aBQP0G*DD-eLt_?!%{}&dbA#r^o3xX%_UJdMqBa0q5JX~OYhSVXF-<o5j1)Q&
zc?MDc2X?b<WeAjVH!Qwm^nAZxo_6%w`;N~bx@xXa(ugrJ|F~N~DV(ZfQk;jnVVi@Z
z!O(V5Wk+;g(BsIEPQ{sM%WKneE{-uCKuZ#{>hlV6X+fS{7tI~L+!<zB<&d>7D7pmJ
z3uU&iVUGa>=;@2O8@Zp5kj5JYwHYB1y?S<%7diby`gMj9=_0`WYp&Q!JTGJEJ}>uI
zBRr>LY^pj2><2_mA=I{G#~#k9Q$PMeN(AwVecNj9cp~u*XJIccxXGptH%@IMNCGCX
zk%}oZSW{t>q?^v8W-qT6z%{1p!sfq~B{^R9DOh~9yoO)Lp>AJ?EuSRU{LuCzc}@%I
zcugtD&pR4c9wbEi1VbS&12E)Sn-eR&5hfkKMY*2bV~ySKX7t1)67vR+6avvp3Z<a5
zT~Os=J1IN<*btyB_5RY2^=1?!{iA61_K?G*90#@pOr#1s4|l8EkDJ^udgj}RVKUAz
z)Dh&PcBbw~{rnfGhe<#uuZzca^zA!-sYW15VS(Kea;~eSEkOZlu>tp831wC;%Lr#z
z=@7`lg;;_lrotL6NLt5v-bfrJlEj&RQzjDH<U?~f6Ta<62<1dx6u&thyQPwl|Kv=G
zG9|`5NIBF)9Qs{zGP(4pTkH3C{@{TLgf$Qq^$hY$dzwD~8Vj5qt=wC}w+Ye7f-XyT
zLG!dA?`5CYXJfg%IiW~u1X)$0hfwGLCT3UYZq&H8?UU8}<9#7eNZaqZi6$e5Nbh`c
zD_Hz!>L2Op!!>qY;sm1T!w|%~$hp-81>`lx*Er^AlfG_rasm1AMT-brRsLc(6?&)?
z-!$Ihz(~)tg{G~xPkM1Ra#37RIc%e2cRY|UicfLm_&1ZixoeS;vC}ppueDquJK!e~
zh6zF%=inH?f{}mUc_jG_Hn^_a4H^~YgZEMYgYbBlBg>HpY~*{AnBckf3iHzDF84NI
zXVz?vu)rHP!vm@;e>Z}eC_XpYx(7Q$ZoSknfVT(@izu}qhV5y&CYTm%nA>JMwic|H
zisu*ui~3nIW#c<IgK-+!ZC!+5m2`>c1Q$M;Xx-!cDe2yw-*<L0AsAN(M9(O;zbn@i
z!<!2ZwHM>Z+Q_!^pDHFPUU=U2E2|g6>b1rh@MqJ5#G+lya2<Ye-x({`2oKf_e0v4*
zKN0W*%`y5%4kBYig4d?if{FYAO^1h7H{&uery+?;Mo-u568Vlcv!mn?L9_+RS;lR-
z)nW_BEk&73D)!<CSgLfmi{N2;p-Y^4Co^{7ecl}W{`0XG-YrMQdQor+Z<k6xsG8G>
z-A5wf`Jt-Rb3ozWEP%WPrRKjSf)hwk4!;ve4tr6nyFDTx7(_M}5Q&%JM;iR4XA=_l
zGi(fo*478&rZX~^Mc-0xe?g%*A|m;)EEYIyw?raJryQx1J`nBRT@WGhTv_-o)-OL1
zR!?4q`i<X08ndjrS^==JHRO#$7)@K95|NV2>p_Ipw1~^*z3;_F3uN%!>ts23TpbW!
z>bHL$`F4b~oeNKB{zI7o?xE}%l$@pNRb67<EpJZ&^=L~@Zr9WsMZn}qdXR?bUZ@GR
zzUA+ly-Ao(DI24okyUyP@|qp^5ysy0{Yq*fsSeQOjPI0ZYSbTpG+}68RhAeNmmd1x
zeILERS=Ng*Jc?wyWW$LD`EnWs%P-1c8mfsps+cnb-gI%<*4CG%{%s=<o;Pg#&c-&d
zvZp?$;t(h)oW9O(r{ZF#+J7(AMM4n2bNLx#N%@g5^#_vrW?i+72B{OzBgWXx-<tLb
zZEaevR{8*K27&$SLJVB@nf#T#H#zZ0W58zZPwqe-bg0_0e`%yC6v$ChT59S7X%kXC
zPLBh$?;;Xz*O3tE0xaZ1k-9UzM?Vb04_ZkVvi}2*aY=jy5z;%5)9inneH<S#-7kz?
zKI>Y+ZAwp1@Ba0jrf}MVLy<Sj$qa~2!?o5t{Xejc0Gd<qTdz$<XjIf@(g!k8zM(>t
z^&8*vCZ6|@oPo*g<x8E-?uQlR(39J&ADZz>;A0Z-E}XeyUrxIFyollDwXF^hBU^5@
z#+?<j{P#5SzH0p}@vJqrHItYVwQw@4r>G+eBOBEr(*i8_d^AoD$UqiDRN=I#Q(nTQ
z1xva$XJ%HJsoDCGf6)75lS8T5Bb@SQBAM~MIcDeiQDZW2mb}(P?7fv;venOlSQ;m|
z!wU>W4W~#!O}rJy&DP_>k%*r{|HAsZ3ZlMAI@8_Q(9j2HCn2bv1da-9UfH+V8nYVz
zNfQo`A%U|Xn_E$-LsuAOa^NUKX-1RX&^Jmfr_!@-F=OdXDq?yKSf%#+Q^Gqx89tll
zk~Y#orCO+N`P-|m))C)QvKLqnWz#-O%*%UUkkIhXIx8;Mg;nk4(cbFrl8~}a#pFjW
z$$5LlIg73&R$4XM>uc;ONt_%=X}Q+X4(fL$Iv7122wezE&`X^lI+-cBI@5D|U#z><
zA;&*IKs3!cJrZ5i(GN;?#@z^E@K7~V2U9wlX&i-u=diePNb8;Q=`WnMwgfT2WBJxY
zGHP1|Rs(tKA}~^p`FvyfaygAo{S5<&F;z613s5?yb+lJPNq7Jhf5EmM5^2#_Yj*iL
zk<6>|kP7y?qcAKI_{LaI`IayS{#GufQ5vOtEXX8D43m{?F;U{YT<D4jzY(^QrcRSb
zvya{&yrgB6OkXL8kwo;9Viw$5M?^%AM@7A&ogMXuq{K9*fa`f!E17>u@41Y3!Mock
zQX%K@?YJ^)Ncyr7Tkn5}H1POrc<8RKBVm3>8h<#_92{UMv`-gnfL$qCiT$rf=b;H!
zFvetb^hPKZn;GNuT*Aa5cpsZLkeW^rFMZDXzEJk=4LUY06>Sb+{Q3M@3Y(m=_p`k)
zuhqH^y9W8J$a`lp?kyrxuJEQNCD>5~b{rhXm?dXTErm~apS=7CV&|$I#sZ_N(kyY6
zFtNw0kLOucQJgeX?wn5!@=gwdk*)$v79r$f`$N;uEV&JrodX!0qr>P2g*?;ft%M1r
z4)i4q3sA6ga`582BhLz~cRN9Rm8Xef;&;W(%$;%GVn4dR_h-`%)FGx?jnXYXbb?U!
z>hPP9!2J*Vh;ojinC_D0R%4>QQLx|)&E{MvY_r9Lq^4n)<6wYK=^F_Q!joD^)HLna
z!XAr$`qL>AneE9`SMsbqt`DK|yUVZoX5IsWMc$Z*L<aYMEv+PzMd;D8X4bT*s%2HP
zp&6n1gm<mB_G`o;avFV`uCum|F&MbTA_P*VO1H(|M{K}FB$;{|83|oNiAv_PRZMzu
z#zr5D6!C>26)!VFAy(v-)K%n%X03D4M<!!8OS1F*cgpCpW9>!Z?<2@Hyb!l=x4bdT
zJ#cU8wJh((1F)<Jt|B5-d{pCqB5EESg8d&3CP=4AuV<&R?unYt>M5gUoNMfzb<Fb&
z4i}9U6XVJ2w{xk3CK3C>dT%1*W57~iKC9`#h-0%=1uQS&s#k(pRRPcfe69yy**%*&
zKsbBXNb(hj$k&#JCm_dd6dS}DhRTHJ4Bv~rkE=~ts8-%<eGc;yFXoiO7luVEn-O?3
zCxsMbySeiQ16y|833dLh(beOF{=qbR+yaM%sa4jkTj<Xhxcl>^#~6WcBs7r){Zh-r
z9jFo*&)IlQvH4adu-g-vXIi*Q^~a6eV;@qpT86MRfG`R^YSP=u`b8{$!<c~G=^uv^
z{Ci`S+e=k62A3UiTMO^q6;BrI4#zOkPQ2dEzsDDFt7Kp&rn#jz+VYli6kQnw?@93#
z|1b4p2vya^tqwo_`7T>3mLK*Hqb8<mx!APUXrDdLycS09D%n}@q?KA`ko0^N$k0d!
zs?s486@P%xA}jHzE&Jtt1F{rRw#>ydoUNngCON!lPcppB$Jao$ch(xrejGqt-I+HU
zzl?(#l^d%y`dkNSN#*JnYxYSPSYt4n<|MQhJ`mWCSO3GQjrVbP!0@uQ1RDRE6IE8-
zn)IzJiEoeTNN*t-wE8BSf~@RHtrA#($fK<kq!>NsSwyT$psP)j+|{OEfmRow@GWa-
zWbJIF%uKO9k*7>&q2j-R%tJEv%Yo&=bb=()XsCeSN~t8gZw_!C51J<bt_NY)o4Y8T
zDBueV314RTL(HHIIx?|GciZPP^85;Co{Tls?Z@u^mGKc{wU@N?{|kHYM~k*|a$RM=
zTAJlLLefVf*G~MZCx6`T8oFt^<YwRm2SWSm-b(D7UGK&z7pC!BzF3?(gyLgl*@dfX
zguu5C1i$Lb%|!mL@PI`uM9^I64S8Q*KxF!QF%gg78PssIuGa>Q(|zdNGCJe&Rk@#U
zX~3B~2$ti^8v~1-@^QW6!2j-%h6D|V<tLut)p&TxI_%_fC-If_U|+`a2LZTaWx99K
zuwOF`hr+$DFh8n)`IkL65B6p0elW9{jc3ARlcF+kfOrv$6Xn3U|HiNQIiu55aT#Rz
zgMgI&OTyvpi}^+PjLm2b+rKMaw)ML|<cS};yt=0+Ao5`{_QLY)s;W|YQSD8GCV?do
z6Ng#0tq%mf2R5nK5rV7&DhJI93;)M9ll~!J;7k|=1fmk@w2blELkAJer;6V>oh${S
zqoX6q!Awf?pTgk6C<YNIy`5D2&H`tmutzmSy-@cAum-}<57!*8(lRm;UDPP#M{dra
zemAj<S!G&bIPPOzS!P~AI?6Q@zHx;a_Dg?~U3at1f*LzpA{Ofc(Pn<=&!ruWeE#i=
z+I&+@+dps#t<^s_%3|1gEn#W;pLe1Gu0NT?v84ubbM>YWKz{b%(!mKdDia;B_<}wU
zm&5U~>9UiBaygJtH+eGjt>MhfILdBrDcD!&aj~*wl`Splp<!Xe?a3)ALRnr9)NIyE
z0Vtejf`datq@0oGmK?-PF*;vWq{PI;zK(3bp0-KQsn=xJ)Z~0oPi=pdp7(CV4rW@@
zKa-P^V)~YijNQ)U+Sk(qu$bt#1rs%>I4~|-4X96XV83#+TTvqR-p5M4+m)Bn^Z5<6
z==YJ7qf-+FGW+$b-g%$%#If0UDJ#}*(;H9--*-nY7J8;UKaf`apGATG%}0S~vR@lE
zZ33q3gsSslKhQ#*zu1c!Z}oq69A=xH$g+Mw<U8+fGXpB?tE;vweUqWEVzTPE#H=iZ
zFf4{#fJwmKMdYY-KIR6nwOO;LJG&b1XYS2(Axx-~338d_wi%?RkBm&VfCVycVv^+>
zdsrfaj`h}B|JN_^%IG}lAH!A4zX{Q(&~gy@1v#V7H+H|j0dBx_IF+r}?&|7OxVD}i
zG0;H4Rn^sUUbwz_Z$tx~+26Q|0~q%2>e#rruF(PaNpicV9>1Z%LFQ3Rev`4SAgBNi
z>m|v<8MjdqZ{2vnHV(k?ds(kfCJB+8wiTdVZ09or82F33cObk`WiEj<eG`DLD)gBk
z<ozXy%WlbNCGB%{&r<PkE=0SKU@vSqq4z5hJ#(b_)bv5LRLv)1hWGrRaH1lv3-vVW
ztmS4`&m<O#WMH|U!>hwFZZAjU_U?Bx*Ms$7hSQlqCk>a@3odPbSNlbTxP8C!8v;_<
zt)u}!3Wdi?W`BT^V6%M}bMq?0bwkQ=u%swW=mXT@qD`yNCK%Tuy_sI?HU|LM(^D(x
zVa}6u9vBX3Njags9&a|OwzuO`Qlt)3ZkMWGnMQO86zm9{@7_61Nab-c&nIKjeiM9k
zz0#AGk}F{QsXAUeyDTVjBhW7TuE#GJ6Fp&(3vTVc3kypA^$%}!$+cOH4-g#qfU({Y
z;Ea@+C4Ld#4$}M@3xLP??cis6!8WUDkjeK1=LW#4`+x<XCB{-T*QRkBGZnz4T3;Rx
zDM;=XEPjoDP}LMFc`D@vQ1+|MC;_F}a;=}1tIms7PkAOxd@tRbp2qs&W}D#06PD-3
z{lc_jx67R;1EaUk+YCNALLeCIqVr{!)dGgLr->&?^WoJD06vRMWwS7T_=K`KBJ@?`
z>pyeZTmo8jr8PY4?=#d6dXGfJC$QIJYNdps#pG)$PrwW>8;m|uac~V0`dq#g26sY6
z1Dk~kz!GHaMa=5tA<{Ple<$Sj2R|IhxUapmP55Mb#f)%1(>^#ousY*-j7w2Qv2)x2
zXr^M8_wycrsN@79VvVhnq7?EUEgf^%qDg2YxJh$P&-s(OKU#527G-(q!|V^VJz2tx
z!xDIQZ&7AB@&X+zO+3pU!RzJTq<7r&A!XOKbyJD^I36RSD>-!`i@<Zu#&Je%cPzLI
z#l+-VwdrtVaX)2On^oSw6!2JQ|4{aU>j$C2dN0(NO;s7mGyyo^{?(*H(Rx=<n#mb}
z8m$o$WufC$m}Gg)(9*dAxXj+T1YrRzp=Y%5NV?Q+x&um?X5$2>AuuO2+d=;VKbU)C
zGBE!kz33TKC0VxXSKPz2z3+`KNnuCU=PC{0QHXOvqffion_I_iuP^awX)?D<4pDZ}
zs9alUlOq7%XKG2S*$}OZ5o7CVGPcVAzVjauc=g;|8}yncj9vJ<lWs4Mgy;>6;(zGp
z4BX$}?{(>v#Ykk!0k@hpV-CCN4SmGu5y6IX5Z3n(sUjyMo5*lk7Aae-+3v9YZbu4?
zWjA%MG#rz{lo3+<VBCNrkB5DeiFXosJr;GwpKl5+4`#}E^rW#6$q^9{04^;mhhD2$
zF^OKw6{0o0Hww3J(m8Gaj5IR;a-=O9Qr)sJg2(wt62N?5utec0<(~X7e`hI_aU!rd
zKScq#AUd1joiVa97N#T#$PKHj-aOv{+t}bc+|kPwZQZYJFKE6{fxD(#{V_X@0Q-#p
zyh-@Vs0?<k`$Js|=cAyIvI}T=p7&e<WOJICj_7H0g$kO0C4u`M@g|W!%)O|#w{J1=
z#BP?4_0d`%N@m#~hFA2pU4(E=kc2s87v~22e4}ALS3z9%t+CUSqc^;RrS7+AaSjyr
z)oJ5yW**?*2N}kS!@Y(7m#b!^&<*dn3)B^Uyg448^N-VJ+`)7B&14+j;i_+F0k1|!
z60leo;P?LUCmOk*L1IVdrxk_A<76AxPbV1MHw`iINol+I%3UY~ZStd{nD0WmDA4vg
z%{(_Mea7BN5DY8ZN#Rwh>Fq%Pkz_7pml2qlm~Pn>&zjB;Xn=EgMfCRhSer$xb(DYE
zlnb>X<Y=nn9?$6sek#6bJ=5bvfHkQIf}!;Zf!pn>#JG$U=0Jh}K6qOi*_Y#KJ<3=b
z?GW8baOH+&Wt!R*=A2i`KM$ZN?mqjO8E~4#GF~sXdbqF*hf!<N61oD6&Hl^dX;EGz
zcW8O*jXG)QZex7j+Fk5AR{)(I5wR~E*9);<)b2a4_RAK#O$zhr9|<XuiNP#|;pdiq
z{8>Kv*~1>Etx59y_lBGKq4-`eNcrx6OL913T|7PllZxLckO2VUGeN7xeKTh;oD5<@
z8#=6`oL(ZnVsA)h-^AF6hzq*6d;&pn-WP>THM<a2qnza;j^?RV?<!?!FPqI@OyebB
zo4}}A!qGAOVjS;|k0(=ab*Izv7*4pF!%!HH2_lr9=Xz`01rM|MQ9I8R#?bm5t|a4G
zxpr;nLal|(!qe#F>&|}1JI8&aBt_a^HlhLV5Fa2>(q-{f9X=``8sId!j9MS5xn$qq
zb0_Z+&G5d_tbuYPvqYHFR*WESouUDj#G_H<L)x_#y&qU8IhL$KMHR%*$~|`pfy3h8
zl!Hq)G;CTEUT*5!QhZPao+|bQGP%^0LnVo4-VoTfLw<+lP8XCa-n<tzAJ1*H6K2W?
z=Lc3~2DTYsJ)DX@Y1l-W+(G785@cvOd-J@G>ci+6!y^4}Ln=algoMY`O+=8%TWizx
zEI~9ugP3(4{FufsyMYAw=DD4L6p*XIA%^kdn7*-~EN?Mv-V6%-AP~FD3yJ%A&+h*7
z?TQgMYPjbJYvEGiWfKD@D7e`1XfC8sK1KEti@pF=z;$)53qqqKlC(>gJ^g6};LtC5
zmK|n~d0Rd6vHO+X7_(USB~p1@Aid7FIUHZTeAqy&mfpjMm^Q8VqsCqzb6LF}u9!v$
zNkYoh>vQT|i&%D6cSo}%6kZPPs;S?)+tRZh!@!+Y2Lzb1qR+s00=Xe}0n?R{X!}(|
z^qafeaGSw;985E4`mF?b;<AgKOi#dImkYoh#lw@#7e~g$POAtCaKbT3xBEWcF|wYd
z?LH82*3pH9E?>lZ`)w@@nf_PHQ|Nq1xZI~G-iu%}sHQUYI_4ND(f*jNIWpz(uz&aT
zXfTkaU;EIx$#_1Zi<6Vk+{6x*H#O_2|Cm4i_?er`SLjzTY!!57n|6&Uv7CVfN}tWs
zbC$xG_aidvXLi|v$d7g(TkoD6t`<wxrs6o<kqD~V?4HzX&8?i_d<HD4RL)KSF$JOd
z760>=0S?QKh57Kv!k8ZHXNSltU04eC(yP7sI2RXrM)_^E-SnZ!u|6g^J3Qv)K3Qdf
z*QaU<5{DzZyx(h~Y{AcusM$`QrNcZnZBnb3Ba>nD;CK`~?%zv?YeFD5(#|grdtJ9?
zn5Vw{chT#-$4gzDAWx5U;T7?k>|)Kbcfoq?K&U1)Ptt5S^U+)KCsJR5MeXA>SN4>h
zTeIXYPBq9Os6Tp)^r`|pY|ot(6WMsw97o=t_&m<}IU+$50Ig;-1BpVwJ10Bdd4D?9
zi1A0E@^ahudXDa|@Vlww^|9@hW!$uMwjC87NHqI`HVS*BD@Q^SXnR*tjgPB(7M@Gz
zDRsWxy$}fqw8p!oFotW+0Tsw#%t03rHp~%eUC&}g@+sIUgztWy|Fq>&wp$|)RO=o7
z<u8ay6adltJ9Lna)oI<`JQLHTcLti^k{PGq%<SX0xc<#gyUE1ELkwre6%;Sb1~P|<
z$?7s?@1hwEJqm0`IB!g5pi?{2?l~_}-=QJd!&&6&$yVk>LSjr-2lwWL`>f!Uq9<CJ
z8Zv$)B{qlBASW;26$E7&&p3arpmT_3Tcp{z*7}AEqqf&U592x4qZ<JYzJk0N^Wm&Y
z#=JU8N_LK@&^~wz$s1pZw+=LOo)}H_oe;TtQdgFUCwhkc82^)-El09(CUud-UHJAT
z7?3T<!Qn^ZX425=30C3L#-CflDO~l?W`5;Glw?=f<J3WuH-*Ux330AmMC3`Xr0$k;
zZg%#u<f<2_i=-?1&y7u_4lUe8@hJ7->Ni$(v4Twc>~DTWB@!pg$?ovRgd<vgyspCK
zuNE_Qf`MLzrnt7-jIQ&CMT}$q#ylY(B|isU27^E`LjwJMJ?mQiD{Emw7#T9s((IGw
zrl?I7AsyI=4;_8Fkv%UJAb0A+oNzP#eW<<cbt`IHi1j04tAf5`QX$kyuf422?C;vn
zxX@?ex<5XqNk*T!ZKlMS$Ym1qD!6(F1dT54bYMfHO1V}L%aara*n!uA-;w^Vj*{GZ
z<*W*Z=G&9>&zwVt{W9Y=2NVO@CWm48|E|1%En5&^2#C3Z{N!R+bzxGGLaCZBT=U@3
zVHDQ8{4g}e-lDp924Q^i+(Xd15+!hh;wMXH(GKcId=;@0gq5-sM6IeLJ(SvWQk8(n
z!bK<PP}Oq!P-`~XJ9WM^msR8mc8%OpWA^t9>BhkC*E6|*Lgm#hEIQxZ1STAgLsY$=
zkU;>j<Ml=^lgTZzKw*6$AoD+4VK(^P9DUFj!IM9?_0XJA|Kqe(YW%V0ptDEzykoQF
zLL~`D9l;>8X|rS5=_feJGmpiW{#_&qM2p+#wfK<DZS#iFkYQ9nh0d9=;VSJRPGPE@
z7U-YJG+Vb!NcW$${W_%GnG1kiKU-NSNMv&_g!34+m2<y+4EV~79teu7%|lO@GlR9t
zCfGVUF6J-R`VTzl1#0V0jZpv&?j`tnVnnpvLtTV&AURTWb8|Y#0LFxc>Y;(<-DKUr
zvQkK)>`%0vfj`bC(YjngrKJEPReUcmE=Zs!`-+ipfkGu!#g6(u#(pUJL+0LmW523K
zZR2!P9o~@?{zP4QjnQR&-8VW0077-*0d(jSi8WA7V~Uts_2KTU)ahD#G8z++8H=9v
zbO0<hMUKU7pF=DW%ig+Bry2tb<ku>@lu4y!FCH&At~XkR(50OmgdkZWj#^9H#fH%h
zXmKBOVt8aXvRh>iVP#%PIa+Z$lKBCx?p_zE<?Ph+9blfcl~b4H<(vxmw(J#z9ZqHL
zoIRow$gE^eCO)HX+|R$Cx8*hKs-tsfXV6qT5)nTcPjtE0*V)T8m_25;42iVjC_XAe
zR5PQLRM^X9a8gxxAhBNqKwPqV084AJbtf@6M5LptKW?tG;!&z|n5#Kewrs4C{;hp3
z7@E<&&76osJcCA_h4MpW4=ObF02Pwc$M$YxLtog0^sAD&J}sS^s-OkO2gRY@=|1B5
zQ9DJh;$0F3w;KDp7A;IKfmFiLgg78Om$Z>^xnSO@nU+0w_}LNYP~<^C`flCkMLvBa
zoedCaOJ|T|h24ylBI&dU-1_yCs}Qr0W2N5ZF^~YQc^8W~3Z(6xEjR5+{x3^+ykA&+
zi@e^<dcGVmNUTZyKnCszOQSz8r;%C7#i5eMv1IYMfw$Q@$qK*z2*@xN!jv*3yDc8q
zA>D`9v2T_$BM&y26l2#s8En3`ZDJo)7%h}iv)91dmb@YY&xLejaq(c`4|H78JdXc~
z^By6ZNZ(HTP5lX;7o2$Zj`gF7K?X877o>?VjEhWn`X2hMi#(F!dp;Rua#zV@>_*8~
zb*@$(yLS<IbD8RFQj6|#>+<^r?EjgqjyB*b7=ruPT0W%h9j4Hf=nTdGKq;aiKWlQq
z8`#ZQQ|f{HFh%V6Zrr_<bj@nSR8VeW($qma#?3-1h(hXpl9ur4Q$sZR<~^;<Ndi9R
zuUmO@(0_O!Jy10bA`sBpmFoC7!%Qk~6gSlP1CC1SR=rR=Opvt@ekYM(ytWOTZZ(Ye
zIIFBwzKR^~_(3UNIY%~oO2`y9={RTb^$fO^<o`#8tTC|>vwz1I@%8}T@nolIy-W+z
z;lQ(4kwkaI3E~cDz6=5fUE?F_rEypr&Y+jC#+wirF6EX6o#_XxECgHGXl(?o-*tjY
zb=)+5zF0l%FR}{oWY2qrlb!a}Uu$_x6z<J)xi;vGCvn@8#ash+3d6UQX_Q8)y??w|
zy9dE^17eh#C~9rqjokW^!(#O6+<*<M`RGD3&D%+JH33p5cGU}EbVMu7d6|BZn@UE*
znFr{BH*vsV5J=YoTukyul5Lv!IbrD;VWnH_s7Cd}L|hsQjseE?gi@<fHj5xS0c<lw
zH!^TPQ4%;LvWDETUlJX=L}D;6p?>?QYOdbQS?w;i`A?rBKAq|Lu7jdRhU<~GV^`n0
z!0ZiBFo4&=LMc&G-S$uzb&xn}%e#=6N8?jTu#On4HA12;+i><(jUh_;7fUDOY9>9c
zDdL*;v5gT4h{?WT9FF{&crt)X(l4qA&kI~R+&aj25n9bN4S)19c{UxN`+=B(Kkv-S
zIW6`m#O}X0zP589{zJlmB|Y3iAf48gEg{O#M&e=_+;p8<qovvub0oSkn?G7-8NE!@
zv?sn1rrF@>VHq%q+=n=uCD|hB48&4L8oEeabX|+M#!5^^*;dhymPr^0_=l?{jatCq
zR+0wy>ynwE?MxAeL;R>ZB!p}+<Vo?F&OhtY>=+?R7yVa74s<#+{5PCwSh$iEc!&n(
zW2}`{a{mkpbbw;(feH^`CNRhN$^;Fb>K2H{hTJ1*bc$+vp0#xbxe8q^Touw=iJ7<Y
zFIGLGmoF1(qXA^3eT{v2=9JZQc2&Q%0+`ftk$s$_o<PpWA`c@%OQVq8s)5^jxvq_L
z(AvY=OQJ?iIW6YleTdHF&C^zWJ+j6s{bTJ{+=ZKk0RC7WXRBXYqd#0~t?aP<-UGhe
z&|d6-bnXB_p-K86U*2}t;(FZx`!>lvCeu4~qPGd>UTD}c4n{&)*v<H3n)5`dizt@T
z7vMOhpVl<#k;~S##%w4PsAr0iy@^Y>TgPNpQYhNdw5})5z7$fHix2v$rqQ+A`AAJP
zG`wS=tDGRi!zTCQ$9-He@ZCD)jVOgAp`*r3qQfzQ!UJVpOc-(u<nT$o7R+cJ9k-Bk
z52n2ttP$C4<k_=j6RU>GVCJ;dqTPKokQXkDfom_Y78brmLdsoNIvmG*MJA=j_(3ZT
zphMn{7TzZilU}SrX^_PWVG)=D!6P?<0NdK=G+yhV(M#8C1-CkLOCy5vDoV)gob@yq
z{SLpL)@iG*oaXexN-ZsjId)NDDn@kl=U1KO8zQXy&4u#1s#-4%ox!@&v3$C%i)AQ=
z;$L;h)Qfe3aStk7QR#S!B~nU36%?i=maQbdLD;Xq<11GktR{1*Wllkc9rbS3TCTB+
zo@p>m8UC(9j(afYmhOu4){AvULyOG9NlP=8+xeD5@WaD@ENa$6O{^s{^i|f&QS^ZK
zmYH7buK(GUez+{m8*O>inzgDtT;tY)Ly{tuf38W$sEu7~v95gR^5-I}#w@)E%m5JF
zABCGjI3jL!G|%naSa^JQy;{4jip9rM|G2JtlQ;k1y=}|w)FF~qmm%q-I&)GD_!#TL
z<E_B)<XG4PR!v%%;4Su1`k>9Q^eqxxYM1Q-dalDh)?g-sqPyT2S^r-sdaP6>vaO^L
z5*4MT;eb59(TaCsChlZ0O_G^?^%reTHeN&%Z`#fxHDT`DnVy(f)%9obhu51CD8cd{
zE3a+G%~I_1a;L2jeu`x1l9GbNXwv@tB^QeEpT1=(W%+n1iM|&V6nh)Lmj~*M(+0r^
zQ$h;Eqe)6CuzPt7z25S<cvqX)7#o`y%T!gbLh%$m%ReshaJAiMIGkM12TVRS1R5j=
z;rG`!$<Az0L1D>I^uzPZtuWXaJ+ZdQa^lI*YvY4U84RA!4%yZ6dj@Zf3<AquY_(gi
zD)2UDg1#MV@KHdy^{a7joTn!+x%Ky$lWMLon7DbTuGR$(2YI&Gax$naDO?^_I^MCv
z2&|%tzyL3rOslTK<Pe}WFrpJkv@t4`Zwn_BpLad~wjV8#vCf<SXa?8SV=e$(Nm)39
zjTtyS5Mx`YceI{6hTzwzO0OZU^>dvorvP64Lkx<^b{Q%rsy)d2`*tTY1)=~iy#<2`
zcH8i`?b4MBya|kHi|w_?>xcdIih+BpV@(3__m;JvXldi|y!JsXKLUP+zAF;`2!#;m
zOb1B?s;#YkP^By&?@{^|-DbhlJtO4{EeRaiacJ%0KSM)5&DXUmH965JZ8P$eWGWNO
zB47k^heCKOu<cdHW+UAkDwBwby~UqqT_lD?Cjt(?aH43P*xfo!A?l;?JP#$6&aJ85
z@b)etM}#-#_}%cEyk6F^)F96T*`fyfBQp4C2>9rBYr*!N-x~z3QRk8QS=%y!wj2$v
zN1Y?k6_%DGi0bHG|2y-alYfM^9O>pg7%gBDO(XwIZ227&v;5unIAz(2yF2pFsenU}
zk~F$gbAdZ>>?oEX%TS@(RqP|#KNt7EzjJ}m6Dn{ycRfnHTJA_<c36=yaxUU;<Y;=E
zOK%&J7O(gQn=Q#KlmFKazR>6xk}|~IqA6)<G;iMyEBNY{H5C_2W_iS=O@$<OYZ=hP
zAF01-$cp1DZ?o_i{IC6-peGdJTqM1{!L-j?7;aZFK1~t<4H01v>1m;%2#v2`55wz&
zukO;Uqtt<}0g_%`t#Oyl2nFc-cb8?1a2)LHoNn<2q<)5QYuX*HP;KSl@_D|%i}RCu
zSGVGyO-&2SlMND|2~hT5yew{uYa=)>h8th@pz5Akx5y8o5(*UFkO}@d#33%ltK0gy
z%Z3#wlFGBHgoE9fB+5WED&^6O*3TlM#J^`|^9}bk$bW1vu09l6($Y92esHXkh$2u@
zQp!b@uyjX3nb33FpRX<&0u{W&_=J~Ps`c#~*>basi_bnv_;wQv<Ri(N*-=B$+V1v_
z*r|Y`FOqjn`;Q2L{`qH222^H3pQp;FeFYzTBSivf{0SfsgCPCS^{$V7Ka6_e6i&Tn
z(@rZ#6Zq5XfP)AI(!_JJk?KHVWzFAJqv47`whY74TN{jo&(1pmYg)EvSSOoeUy%k~
zT@8YN$f#dU1Ptni{n0th?pLNog8H%D76aN|*B?r#enp>S^bP@<?=Lx}F-AlD4l*Mj
zK5S1EkLKZWc(+(U#aU<|H_^3MMrtXvzo(%x|Ec=RdGW*GRbT(kaGKF+>#ef@r!342
z7_c|>Q9EH!$VT&fJ&2z6{P^+74Uo-eh}pKR*Z}ls9N-Wcm>lITQ#q3`>J7&(=YO)D
zud_<?yju?%k>=O$hMTW3iwnnQ5wEkH-+0OXNYXN^?ZHKE3%EzBJn!vW+h7qE0=vAz
z*;hSI`EJD%`EKS!+;2}>5B;iO|9OnfbdaT-DepQ@$#%G&^j^#&WE6;}FzxcYOKJCf
zsf%lWOd0L-Tsa?^#(SJL$`IV|z7AaN2Mru;8V{7!KdZ@e-+w7W0%TOkRrcEm*gVeZ
zn&;J7{blv5IqQ}3FMY3GDZkU*;?q3`OXP|3N#;^z(i@HB=DU)%cG7GT0j5J`g!_=z
z4Y0RBhpywhED=M&AVptX*f-v7giT8?4^tb%bzQ&~*d5N4*#l-MZX+~l;Fc%T?Sly^
zf3@8-DIX{j8s;E*A8M?|0_9NAQY+von5ehWX}lca$(R6`lE;CZAc9YTSt?&Pl~roR
zbrYEj0D!iBvy8?A5r+#2d;qnT(fLrF_57Rqdqt)~2KQG?25njlN_odsnrE|wMyv=@
z(*F%_$q)!fIx3HEwW%C7nQ23euR>e_l~5)ekz6|;O};VC$KwRU6_ugo@^Vit9os(E
zR~Xh?Evhu0`Cl5n$o%TB5kZOFXF-Vr@aZM$n9(2GZxa*Jd_2OgKGDa%BNZRi*?kf7
z_8w-)c)R;_s!1g9oJBK*fI&+xLY%KOm(FS+2Yn{ik+cyc@RXBo*NaWRneH$W<MVQv
zWq&hoG6axYC$#>GgKNDJxRgm+&MCYPdwCat!>=E}de`E`EtNU%)<8fLYTHRK-S1{G
z08XW>Y<kc0Y+V$5?k|7rU`14&8-EN;jojztv?rfw9d<{Y63^HyX8HrMw5d?}@3O=3
zxRm6%w%?nu@g5ZC8TN#JkoObI(sK$3L{pKV3Q&ZR8+ZC}3HoQAm%(6shW#n~Z8%lH
z2Yhw9x?1*Q<EYoHC}StSV`c=uObM>iW-MO#ZVWZ2TTj^sH3RS0%RR$ZJ;y55;cUhy
zkE6ySO~><slU#7^8C|{R?Oa||10WIwGZ41CW~y7%HFQzI;@M`WwbxrOi;Gpr_#nsP
z@}BTfsQ?+sp_rWe`EggU{c$IVP_L;L<smyA2zT{u&vN`aFO5flvR7UWq{Qj4BU{gl
z)Psg{Ju@>Se%b*LXfl83g(4&pLIl0?mpi|%s_PKSjJ@~b-M|R)Qhk|L)k&y#T7*6T
zN}yK3&)rp5NAuO&vu!W67MFm5%cf3e2K-bU6!}jRSYWiL``*L7*!AJ>B7MyAekhUY
zda48*GZK)s#xIf%9rLu#YVv*}3S%KlOL%5u7@f9rqOTc<V4B?wA3yovu&pHrFdbm_
z`%*EkofKcWr}<T@K*j^tk%W*=;BH;49FO1p(A>%2$`&*oWy%djh887w2Fl;p?pP-~
zn(VS?{=Lt6ZH=P0m1ITh;P%JAB%8^rM&P;z*%3=$`rWqTVV%E@fi#|f3W%_17lM%{
zyy@+no|%y}Gb;%*P|!0nDyWv!SHp2y0{X>?3=*Nr?>{tP!Y1th)<X#R0C?TV4o^@~
zOMABOSDMmKdTHo*7etddNmYT2V@hJuGSWsXm!tjS8DAtK2sWMhT7Rb9V9{w(0iM`N
zxqF)JK)2DMfg*1$lq@DW9zKys-VH{z);ifsiDI!LMezNwjb!AF&rqH8JlHF(I(Djr
zIhcI6(683ow+whP86b$04^;Zw?q~x`fXyYn2kj9?Zvf<EljU_|H?Ih;5DfznB3^s<
z5Oe|h_J!wV_ro%KfDIo4Y=B4J(R06;-**$t&g%GRp#XfSD(ZVas&v~uC~)zPZdrF`
z7nj3M17DD&1P`YZ>QCbMA+>7b#<rj3O@nl7X&)wSCJ*xdSqjH$;dMRU@4z1M_1#eY
zrcCzOz-O0AP#HuZ#_9Y1IPP9`!Gq<kL*I6UUTd3vcw_tgFgrW2)V5NRLFa_KOX#}h
zt##_H5Xom<XC2@vPv3ZDZ&Jb~%(0y1y)d9uoEZCj%Oglf`5F&}nog_3b58b^k>g;0
zOgmX``J1h)*l|orWY~q4pRbEX`^Iha>WjR4(v34^07L7Ql8YkLN81X6nbN%FBsd*1
zTGZDKtTtN};Vp$M{$*;z!ZSSq7(96x5utRiC(G8LD5s&G=zSo;8VQG0(i*U3VDs~G
znxP&N(S|t>B6{#u*q}Y4GBm8c=`h<=)%F%Ef4#mwxnhYQ2kc`q;*ogNr{KqO9@`C^
z?SH#RH!%c0k~DFqJ!BALX`t156=uuLO=Jhu<Ih6uqcrJ4B}a%M>u)d~&)2htA?g}{
zW!h*0{J76N2PtnL2CFA@KZ|(jxH@IN48hfoPusD{&q(wQO~=y?Y^h3vfRcdc+5g3a
z*b^WYm*7(Yn@AsZW^}<JH?(c5g+=Aj0u|KbtrVwia(|BRIxGT8_*y$Gm<;gF*-?7s
zIHmyp&CxBL*M}ta-1@`3n8F5f{h==HaKP@w^w5yng~IdOt3$U3AghZq{M~X-q-Z~j
zp+)6&m>E+A6dwXhihWUdB?nrf#F0V`17&IA+!LY&Sz?qP5Q@aK@B~m7#|Q2F;_0m7
zT%Pr=ql*bS|8y3#FkiZNl&K6v)#leQ)CVf>9khk!Lqnz20GYo!w$lI?ejr)~OWAuB
za8rM;$5DMJn9lJHii~OF4^9%ov{PK1gJpi2t&UvK0A3vZ7GsIQo1S%usbia+AYQFm
zqUcEBV2|yMF#1^K=_**GQJ03KHGMBL^+CiWwI;_B9{ecemWJ75#|s6Y2jw;?Eb-;_
z3ySv)4BAZze>=0_n*nsmuf(!$19RN|11N)dw4TX$OesRx0kI|+#PPgO9T$*BsLXZw
zFoz_acOay6!aD9!z6Y}CV2COHqj6AIQu_(pk_6nN2Q6A}>&~v)lJ{?-Nc$b4u*p7$
z>q=(V6QYIAG-vOpO+ub`@gSs^wY8u_VZ7D1TldfKNlqWm{#iVHp}C_47chm}qS);g
z4EJ`4@fZZO-5Yo}wSpi{atxEgBM{#I08HWyv!s|<x3-}l-#1mrIJWf+RXa`vg4ctP
zGfCkBQ66E3bMa4CB)EQ((qb^+8}0qw31+iFGjbGG8K&0-j-491=3ZW+MJ5q4@M&hA
z(@SO#aL#V5<vguNkl89$=~ILH|94`<B7;*}dAc+a4?XN576EPV7DpP1tiL;X?oh!r
zssQ|HwK|bE=WvekwZjXQ-ijqG=vV&~J=`pip8_)O4~*<rNrQ&sD%&ANB&dz?r{yVh
z)FUJQSu054_C!-VLF&#crql1d3~|Y#eSNK`!wC9)-(c1al2Z-R{;gH^CV*1Ka*Ctx
z*i_d0^}~lwdauPJZ%|)EcwEEvb;LtB7-3dOecQS1u$v&}e}&6YCh(1=s#?t|$0)BB
zuj};{hXGF6fO9Q*2o4(|JwMBpCar_3aq+ir<$eV$Zf6+4{%t5-7m950j?lqRCFLcP
zMK<rrTL;AhZC${{S~WHm=sWw>bk)~6yC1%?Jw$GpTcUr#aZl=cI6e)5)9C7QQyKY|
z+A`4dgJB}xqHnam;=XtiRRcE>W%b~qE790XP<yV?U5+s}gw?@2Q73BPTk7@bL>ceX
zH)iit1|UC%Q^gDH$w_#vo?5GS9k40MVd4ht3;ZM#DbzE-Ed%PQJzi=*XASl5RYoo}
zH4zV<_tRGi5$nJFy6#A-cJ^FdK$^8+?ne!ZF}_wO@Z^+{n7h~rd}glfq%wKF=%On~
zq4nha$rH?W&7{ITVTt5DH>j?jTUBXTI;ux_{cx<t<DE)~GSoL%eN#q%NMmRa*yZ$n
zQ-xkzV<z&RBB-I(#O>_av$>tZ#OmI@znb}ct8<@cIuHKk7XDfTJ773)QK)I!2%KfS
zO*<U*c&(v!i81iVtddpG5p;QoqU5g-C-S>f1;&gji3*?D7L2!{5>$J*@$gQ~>~N(O
zH$T?-3tXJISVtt8u1p`_mEKq#VJ_*SiN-&hcCs-wc-{Grqr|d3pNVAyF^zcQA>Ipp
z0^x_INBr~14I%u*`D8aR`;|T-q9)`+gysJICQ!RoG-F79#qM@3MmsrCO?duibNjyv
z+nGWzup$Gp+Wp_M7!rMM8QvCk{lq(Pt}Xsns3!Qgym<)`N<$B8nFnw;1F2Dt0+#mp
z+w~@k#klkoX+%HwyVH6Ac>0E6>I+(}g|WA6PF<UENhHhHd(JM?%%#rcG(<{5iy?5K
zlIpjasK@m^$(HlJ-%$v4Bx|+nvlIFrc7r)6G!|f;Uxe1wt4XyL2|yTvm%!&th9vWb
z0{7>qWLJF(arYQ1hUrLQex<C{BG+j_7)lSj<a+Y^Z}*iGV=66<TRrvz{UQ`!B?1}0
z>c^ilzHyfRkwaV?r-VNv#4h@`f^8;-<Q>KGDZ%@lFWi8uE$K{6r1<qK9WpZV!w&k1
z<r<&R-wy(s;Y(c*zdi6GsnuulpYN53Wkcx$xnm&`sefMiO`X9a?~%H>waD?r3M`WR
zuT<Dug8np}tyE|C6NN-3p|d5QHjuT|@x&t2uC<SuX~iHs$^Q0K;~>7n&}Z2Y4+$ms
z??f^|G*(uu_;7VX&Bn$iMGZ3bN6^E`R?GK-U)w*ZK-k&Yf#$I9j6*(N#F~-+dvzHh
zzW7wspJ>Mt>PquJ2$%Ob75-dGc);Nz4opk=XCwNj`sI4_Qj;=>B#o$N?@2B8_q#x+
zz?RAWia>nheD5=$B%Jd7`$PV4^1zG?7kb4blNx%o!u8v=ff+fje<vSWX!TmdaFeAw
zlqk3kbe&uye%W^5)xZ;F8XWcW<AZnCjIwV|2C}Gi+r8eJ+-h?Y{QT5k<Ji{w2}@q4
zu*^1Prkkg!b>=F;=<m!3>$iiW73%ffsON=I>cfW*Tt=f5rM6Czqr<}q0Gt6-hN=ZW
zswM`4wm(w(eSup8GxwkfFdiHp=GoQi^_30|S}r$zRr#g8{K=xW7QZ@&UHYm`8Gl?G
z*C}oKAK1nb{SCDS91|1pnuz`h1^yU|@<wSONu``p_!1i9FbRBR#N~YhKk^VHg^d!I
z#3ehUh*NL5LK`%iOhHI{FY11EM2+lDi-S_dety4Q%Ik#=hr!rWURS50?epxU)@Wbg
zfkt^UM6FyXsnFV{zpQ*m3?;+1l|-vX0=yX{<R!V%19dQep*c@ZT|@G$>+9FA{x?9p
z#fOAzC~zqu%Is#6&_}O;58i+fxeADEPhme+q^8~{PrH5uof4HE(KRM6Anw+GA-w#j
z!JL7Fsx*9Oc9XzK<%ptEw2dC0WXU0#!R_t041>$Jyi~1^SBViuMnJXvN~%$38RIa*
zw@U58#U&SoA{4~bp~slKMWOJLJ6P<;tiRj=Z4!qOFI9b!&rLMwg8#<3O}G13%SEVi
z{GXz>2#sF7M)HsLjqAJ{!{C#A-7@4Y^9i5B3)WD)kVTPN=B<dy{l#{b<GvCXptsUu
zqadsvk>rXyKV)^gq(hZH2I|jJ0q~o*XWL0SiGa>=XvO0c|9Rva_T}cZjZ>;7r}h5d
zCDK{o&GEgB^8vzoWN?pY!jx3pO&pU!-;e3sFBGyL8D?KD+_|>epSR+b+vxv?jF@|+
zaGbi8l$J)HZ&LPqKN1r0xnD*1;&&*q#LLM3PHxHcxWT>$5-yC+qKWPIa#->Kqx#5!
zKE~~di`rb?av%io?st{4SP%-gBp~zFsz+M^do@4$y^+B&1g?_LWPpIzwIZB=Z^25Z
zy%pAkpJ1aAh+&~-V8H5+n0{OY`{Hs~#{=OxbI;$fx$N_ifGS$0^xHaZ_+p)v!**{J
z;p)*>A*yD*E|?<5TvWv^Ma=zgKg4l5hXi!!3m*6u97g%H+BAzu2=_30-}E>Ekd0EI
z09X-4Wem`Jb5T>}XZgG|dSl64HM6g5>miv!ZPpu|x85ua)xu&{%o%t-Ki=M^8T<n#
z>T;0zx@F5F8*}}?_P+Wps;zxr5u_xPmZ4ixy1PMAI!73#JETQGq>&m)>F(}EfuXyR
z?i#v3+jBg}_r1RV!S{OkVJ@zj*?ab^S!=K7xu5&KpQlZz2Awy)KCq($PcRr<;GTR%
z+MX-?Il3L=3r>PSW_`V;F~hXwNVAPjts-4p2+I{Kkjf^%2wPeGWOOgT-@K#5bF^-{
zA)BR3=uEFf;dwIblh3trz<wEm+;h#Zdb<?2C|AYO0cLovDaHRxR`S7trGGjS<5Ioz
zPEz~hOP~`@JA-p=;QHXVgBS^V6O{{m7X8iqPH0cu+b?Xk3xYL2oTo~`@co7Z>9y3c
zU+Bxe_ugnugy7|BG(5;Av6LBSia2BsI~NPnnX=JkPuFd?>_-x)SLpucxEN%b+SwkA
z3MtCe3ev;(-bi+hTx+Jw&RiyYctWNydLb4-CZ~egUw#P?4bhp>RWQM6GQ@VJ<sv{s
zgpd4XNnwpG_qPYdBxCAYS{qlNoOrz_L65!vMvA3c@2m}hto~;G6Y*+xqD>TF;&KlE
zA#$hsi>>Gev{Fd=@*tUe+Uap*eSnHyn`A6Bkq*e|g9LBKqSe%NDIvtYt3h<6j+)}y
z=KFOUEJU1kkvE&^3Yod&G5~l-HJbAZFy0OSVq-C_-SD=mBC}BYs4nR;>^`RJfcZ<1
zM;O#TU(oP<+a-K|*%h)qCXwN%_|A?gdKxj+ZpqMJ#0_p*6T3A1?whk_`<MKSSAb%d
z;Z<o$kxqrQlE|$SG9nuFYV%<LYpo}}v`Tp?6#M!(T=>e24mn^40B|CK6kbdC=1}6D
z#to1LZo&^0o?K&r!5w5XtwNZA!toH|;AH@)paa<G&Tdm!C^|8|PYT_%F+WLol+KX=
z%t9N*Z0vMQ`jkx`ri0Ry2*Q``3=O3SZ%{7*;KMaeSeyBv<=%UehdwncE35w8E@4(7
z=<Zj}jlgY2a4Ip}CBj|`ba>g}@4)(NGx44O_+TCF#v{z9<K199GuG`21rNdN?Kb@z
z;s~GnvMQ9`)PA8YC&5<%Dv4?m-QU9?HmI056LlM&dN%jRB<w4t9rF&e=>fJqNjS~h
zxb=IpmYK@mnjOYee{_E;$6(K|4eYgY#ksJkii$beW&nV#2t9npk@Eu~c=vtJ^hOdv
zRe2@{Tig~UB7$F%dYs*7#OrO%5?yB7#>-C?t$=Ppp)?8I@~m-VL(Z?6st$n0z3TdE
zSg~WcJhuR9EZ@cor(g`H_K<Nhar!LL&s5R&=Yb-;Oj|P)FQBPp+4^EnJK2u`uIAbm
zie>7!pYYNToEd+?W*HQ{xovS!`Qg$K{uSoC2_bC`3O{}}!<&u=eA?62o|`-|ordmg
zNSOB8Tt$Z0KM=jdp5GNR!6R)}9RA6VQYBCoh3(3%=F2qzUm~I$#DNqcf%<1}?+tTi
z(>QU(6`$$Yu=8C+n%5g0auRW-MpUrPId9ze_GP@STVpUQ_*w}c%Lhk~t}`c6;>gD-
z$!8T0PK}9JU7#?#Ru<sk3;>|lGH!Q5!uF<r%>Dx%mI~C|;AQU*#^(5%JWK)M-`ETR
zyL8512%$o^N>s21K5QZSY@!G)R_Uc*0G0<!A3utG?RjY>zxfFD{JHK{45Rjd3aR~d
zIR$25dA6NS0&ECNy?6nzjjGD@$-jPs<!ofgq#*(JlkjM!G?$0Mo_JjZ6E<^9kOEmI
z7cRTXG5~Uto$uIZyr{LyKGgBRIK^Ngv2S@bW&-c7xcWIerAn~u^mmL)BGY<*{vKFy
zHX)>E?FVH)NcR^nsxMsmPKEs0F!Ci<!omdRBz06Z=#XFWY17pg*`U_x)rh;Yt!6Lk
zv(BE_a|(I5Kx+t?W6sBdeVYuwB6{GSJKY^VrE;FONM7AoYmgZR*|icRbs^zk(b6aO
z@nZ_;Z8IV6E__h)Yx0>=c!!Aw+UJ<dd4!DV)RJpw_DG?KSMp&9S&~-|_NO&WWNzbe
zKTxljn_nz8q%`wMAiK)$FI<{H=DPG*!f`Au=%n=->vm@pD`vK-3@&STMsc^+3lu#X
zR8RVJGaO9k6_1a+R}nNOp~Uu9f}b3%YgMq%R%E%ujn%Y_Hda-Uy@$l`FHt%Wh**g8
z;z<84T?ycn`b2;~|AIx+XTxsza<~j+r^U^pWlSV8JkPe&c(#l`eymz6zrXWwm^#U;
zKetT)xm-Y<)b9iwE+Np|(>GXd+dV&4<3#3JB%In`tsE#mc)(6ACn@$?H~qRSZp*x9
zX6AYFdzoJAntKf;p-Yj~KHG15BW1MSZ2^mpD;5R5)sLUS_EQiQ`Fy$D*Zbn(+XW@t
zJ!cwGMv<(HT7^z=!UAF;qgCe%9wq8uOKmYl6oI^6KKyox9JMg?mu>-orMekx0aqud
zrQ5UFL6V8qhfn8rv<*{)z3Lx5n*PPpT{CGR#T6MtmD+24gyj^dN^TTsL=LI%M}Sq^
z)z%!%+Yb(CHnA0I^QV0VDd8nP`z*Zl0ONbaZP-N>N&q2R8Z(Va9_W<HW>IGhxodn|
zY_5dBS@dZNuYJ1S`gcQ_U7t>2DbVY-JGoH2wf;As`F|q{{GUEB1e{4?+*~hc-jlv#
ze!sUz;)i@TnN__n7x6gE#0QW2Cl(=FE3;9W`b9^^Tlej9Vp<_Z>2LWXwQ@y3Q;uiE
zg70&c-lR{&%e$gvebY5C_G&vuncSqn0kO43k;O<V)>q0>d&#j&WAk`e7rl@h0l+Qf
zVD)l8zW{W6UV1*2XM5y<v4NDIaT>b)AQ<N_nSxY$Bj2LYV(N&Zia*~sQ3wRZW!oFI
zyKj;m$;@9UiRN*_s$)AOX2EWqaE^V;6P!H>Qs*=hUQeUQKr%_Y3*j`(%8<LCWDl6|
zGM*^WOB_jx{A6RP1S2c|%m9s)J+aZ0+~w<ZeR7#g=GEbf?SZOFY9cN?BjMAEJt6Kn
zIjr2R+oIob^DD1>_-xY-;lh=0U_D-&q&4DPqku<NwI-X+_`-o+FKsqgvq&fBICvjN
zS%yMVhOO;q+vsV1rzweq#8)#mU*i#RZ^IOu7@1FTZIT!|ik)n3Z`#EJhZ$ljeAAe=
zQ+M1E?XWTnu)k*ncns__8__0_hSNn!8B)5nSg|je?#SyZ5sxn#+8fl8ijcB~_TDts
z#&Z0PG8sdGMEDohMM%}T8vN<`a|@RQvoh)c&l@gI(U(uo?7@^}<tWz<-g8h#qzf?w
zxo1F+hv08MWE4P34}NXE_N+%I#i%9Vmj8|O_%>F}PVd$r4Jfp%UXfcrB1>kCa!qjn
z-eIlnuFayW>`$~zYYC1CqIu^$O!wEIbb}Dg8T`3D6PcJW7XcBTlGH#+A{Yp%$dj)z
zPP-U4a_FMaHz@W63Vz(Yb)yh4WFqPg98HtF8i4oLG`?O2(g$gSrP^`jzhNkH4wOv$
zUS5vJ1FF00z?KHLS&(yy_?=j?`#r#G@2d?5A^k?~ftV-r)dw{2%%*m!0ji{?q_whs
zLv>g0-&uw%`3e9>&3L-1wm${3-OqvX4Idwz6^!r9a_&Y-u*-wX8T%qbl58C$DoSHr
zFBejo=M~VD`emLKHh!}Ow-g5S^TbPfYfmhL7fU+e00Kg_dmjLCi8{rhY+s@l*$*t*
zkGqkCL=p^O<pivl4YIBT7O@5u$M^W}9IbdK;)joi8$rp=W+OCM^mDpy`P;!QqyWwk
z_tu+t9ysSg!CJu1XXrkRA}N=}2>+BOIPka~UN++!dT_&kQ`*z1AmPqCID?);myhmN
zTr8VrHyyXE={j`|(;>XAI8=fku~Xh2uTfa|c=NX}Ien9>4N5lch;=(EEv$kxkMF^B
z60}}bq_SF`@)~hOy&(P)LbH=|`6R)aT)LB$U_dE`Ag-#mKfWzmKLJxu*IV!#8d#8B
zaghemj%s6l8H4Mpc=wf_0a+B^P~>g|d3Rnjku!cZHI%GB>~^Ksl;Z7L6XmcmN1bf`
zH+;NM92A(Y;O=!7^Wex0Z~L8*_FFm~5eiC>5`4vbYTKqq`SoMBr5ih4-te;<cJHaH
zsLh0UMPM@T*O)J9?_D@wf?IC2*V>(R=SrwRXFEq7wC~K<14o%j&u*J_4Qs>SZ#9G3
zgSO!iRipIxysS)Q1ti@U10@|;iHeP!nSsZJO?K|-^^qIf!MJEpYX%$VK(CghfMF5z
z18Oo>su2+*()6>994e5~dAxt(lpMEyJOLI=S8rW!zBwt&ZJZPRs$4VTn2Rc}{Arzb
zxp%@~st)oS$s32IT}d~vPKZE8uy8Y$BR%OYv=0<qUc?Wv3LB;Kt)^9Z{W=%Ryf_Z9
z71^}JIG(9@pOE<tOAyID(Ch+iR&!ABr44B#`$Y4wX(fW#NtDP11QZsxRaZ$34fQ66
zmi8*d{=p>*5f9;5H`8!?_LWreuTN-top>M4`uOrp4gC#>{hII6<aiKlE`Vwh#+oJY
zRWzT+3E4HG|M%;n(%3)KqC#s!L5TmcM3jLga<!`+^?MlsxG4KaU?u(UZT}mPztN@4
z|ILv9Et%itlK<Tt{y(@|lIyu-8hxet9S8kI9pe}A-7VZL91uYZ|C>to_aiw3K}Sa?
zDmIz9ZhX9|m~Bk_IlB6K_aA7TddM7gY-~*4+MvnwU`mXubktDFCF#$dQ&;SP1-Vn(
zTzWc!7RGqH=8&oC@9peY$n1}ZhJH*VBBIRfANCqaVCmEZ`sboj#}SQ5tgNhJtd`=V
zG&@X@{*-ry;!zLzepMUG4*uiG$B4Pu)ej{#|EXyJFD1tVJK2Ee>6GiAPc?gl@ET?I
z0cFA8ar>`F-vLG7XMNY~zANx&W2}7muo+g1L0*pb=h)3Y#Ea~x|M!H`ZdXng{D0m@
z?pv%teto<K%SuZV19~{07`PY^Aj<qx&j7B>AXfA&<ak5Dr8=28O?pbh7BM{WpJ7Yg
zdDcxTX!?0|eLdgVF0kU6ivcc(@6VMvy+W97@;JB07pM3C4G8=_3<eRRXIG~?S!HEk
zCfbT!_xRrYxg<Npa=_Vu>h$#Viv&&OGkEiVeEni+a*y@CjJZ3pCm_18t$)`#@J~Iy
z@9y30wIddIvVI-J{O2M#sUi*Zv?iH+pZ@s_;A=~Xl8JRgmWzQpzwVzG%#>PFWakBA
zU;cTD+?PcH7<6de7>NGQSv!I992+-5cGTZ=)L)ELC<RclgXvpf#{4rr9}wT*bmI$$
z@N*OW=NkzqB8!wW_9-RS*#69-4~U)!I_dX$EMMoRhJNO7DE{Z%l|td<<d$#R{#dsb
zJ8siGgz38b^U9u}?hZuz=aOWzwtcy(s-hCVK?&N#Ad%<)kzY8`Ah=z+?ehlhQ#|?f
zYQ<Qq{szSO&!M7PwAhAt;VJCv!%@YadUm`2&Q*gT(Gx}cPRg&AeuKReGS5a7&3HGX
z|9s`|&swLy<f`Rn^hsr^Du0cEY8|S`eK&N$4E-|%2{<3_kh`#1YE%cKfQcfzxFD4H
z#J{_P2*-zHdU-v=X^N(oL&xYlmYz4INStRn=W1KmbLGg2W8N<|&Ix2n73E9ruNFL8
zl466_5(PbZzj;KCoS2utVt7RX3t5xsEtxbetnBhyWHs56l^`70K0f}Kv?@}>QoyRO
z#Y<YUuI->%bBY7$FfUt<thwr|Dfc#P<P;$R^A`N@6zNCx7xv_Xi6`*wATKkA4MI<0
zdc3N7-t)A!&8I&~7NIrnUStrH9_{4AB18XI+`m}kJ&8=q&Bu@;LlF@Ww_9{oZO`Qv
z$`1>kY0d(i=0&AOcbp!eRNbt5i+_Io4s0o+IqPj$%hF;1tKcj^x3?6CQ%vV%e0S?#
z$fr<oM=`&u7j*>t1iOHkj0bTt9>CH`6?him{WAPXJ)b)80}o)7q_E4q3ph{}*GcHY
z2@@C>EST-3umM*>t(s3Unq#q`r;tLD=pz_~)g^wlrveYzR`c=b)3fAL*oY!GQ=n(}
z)up$NLXCT$attO>3ub2r{Bz<lglbC#k;ir#pX>3uIC|g2+H!ijIHUAGG>TtKi@XEX
zYBpsBS9~+1ts#`k138WJ3Jvnf@#o>6g_jC@T8i^sApLC@6Dnq`q+(FBR2gh|>BFCi
zoyv&)d6)R)w7FG$3h(jY%RejT0cs~Twl>PFUGh|4<USL}cEFYSsYjeF)bw&wSC-bI
ztJ0}k62~CZTmoX+Gpp1q>9F;0g7D0Yd-9Dxx0dvVexh-|77+(>e4$8=uAwB;6)wL*
zM4Xt(Q-T4Bxt=X#CKXL-KULKN-ZyDzN)=ZKnr_`PNSdyd+^<5iWfCg6sltAYq7PxP
zRoeLL?o_IVqMHQs>`Oj^IkU}vknL0YDc%-O)|n7><0P^RHIG|h8hNdkdEeF*Mm0*^
zkNtZIIq4$2+~D`wb_6T1EfU*sGCV(I_xq`2A?7)}?${U|g7SmDB0Bw<4ZU={*~U$M
zM$q%aUQUh47_+M&yq6Gufd;wnaQ+6{727eHZfYT6<DnLPFD7te=pw(Wp(>4DO{_q_
zK&`yWBmiI}TzV5QVKnnG9&8|Cz3vR?y8e27vPHw^uFJnp(3St2+XU<=UqAHQV_<}-
zRht)#jgRwLV#lPk`hU6{`6gmr${nFQ2MvmScdDJzb8U~Vmwaf_qtiSdR2v&cw;09L
zd+Geu^QXkouFjl6u!oZ)K8HG57@2!1c$8bG!aKvIZnyxQFL|&|b%#8p-Z^@>136zf
zCbnESQ{lVI%MoPb4_ZYG(HfRKwdfDl3aLOW?(v3W<2&5FDJ!28#KgRNvH8=#S^dK<
z(v@gkpV{8ly*!K_OimwtMMf0pRKJdR*t(`+2{S$XAk6M$=hHuSTq&UFi>q_e_RgVy
zE@#d6e9}zw@nOf1LwQ!FUh~bie~-g=RE|t?5mp*6S-f^y(SCbc0*;E+?EYEPuI*EH
z`*ztJY+9}Qy-Z=ldU0XhdbP6!$Je@+8`hIJ6aUZ<tQA_PJH{S-<U7w$){>2|31D|Q
z8LGCX?yxAxEsS#2IAUaC8iLxML|t7yKBo*{E8yu4;1ObCEn1-b5zZ9L-<XyWEf7jR
zJ5LtoFu<iOXMaw~w>Fh0w<9rxNEIkQ5}39pP@~Irv?OsQ6WjKa0G<7|o|%+k70$$*
zOLtLO+38S<xvceov{1;VkIl_%IIz5_aU?#dUvPJ_m+haqCXet{-dTRGxFo))r~oSG
z5oX^}%2(PWZ+1*qep}kLM3(^lg7uF5P7t)p>GkRO=BypexHf4CUJck`cRRklV#4s=
zb=pLpZ@}y620uc&Ld6x&Y-84Md~K*(rSUdmiz>DXL#+fY4JPhdNUWqLQW#Q}a0Jnd
zIzBG4IbK<#n74fdrwu7RWuWx)NRt7lfcWcLZua5>9B!b^<nvVcro;llU5>uugub}S
z^s4n-BdH_|YRsU4z8SH)Lo+~`(~l*;s}BHYwo!m1jZ4edQa5%HR;}mHpX<M6{51BO
zPvZCfQ%a0o%2#B#!Q1C-SBn1z)4!r_N-DwpslZ^OfF;pZFJci_$zTZ#tA?7GadD(C
zLW2zMqQoPVnaj3F6rS{Y6&v-;V7Ur}m;Yp9%TO`LH9j5E%w_Ifc!Fr&9emacJ?_w~
zxh#Xf$qHDbVn6IH*vVA2jjNxT$rUzTKJwgoXGd4{=5{W4*-qYsdgSJN=wv{)lRmFm
z_5R=st6n9-c4(*42%?m=8EVMO=->MtKgq+ss<#-ObMJ!R<PggWxK5p5o2(^-Y|}rt
zRDtLU9a>@)r3q~-J_h&=oSRrk^ntZnQH~$5z0~E(1KzwQVF+GVFY4laug}}?e&XYr
zPLlAyoAe~jkb4t(erY9or)ZrOdpL~+V!0id`0JKxGk7R#OGcYJH|_FWDhe&#Y$Np<
zw(ibS67|X!{yFRJ_ILqgx_m8voLjcV`_^>#>&yBh4xY-BB-228>3_luY?=?36G^t7
z_FG>&xn`AlbS^X};2rix)lD4}foC>!<5yxlJa6JHhh(N!x6F;Sx8Cq+AASHa=A%G1
zKF@ZTLA$kZRVBh{&A#M@uKmatNak#Twn_UtS(r-^%_8a<7%)5DU-9tWHy)T-w=Iag
zzP=^9QMub`X=bJRgq5Q<g*Lz>|NB~+DUkv&k4UC`P+mP^Sw{MjtsHGFZ>|P1S-W(w
zCXx1-x(<}cWFy>T!3;J4+l24S6hhZxozoj|vW#r1PZ&pJn%^f&@*26>7UV%Ed9=i+
zoPsYt*tnHz1qt)ZmdG{PB<q#t$v<9S>;AfoaLyz~AIEBy+xqkkUu9)C!x!%+LmYFY
zTZIx26t0=Ls+u7h{nz?K2?@{ZuC^^-6AALXP7j;|L>BOVj=`v^oryYP0yJupe}5)8
zBHLoS^+Kc0!uw+ko&#~B!jcl#USoj1-FUTKpr)FJ7RVo6Q_2tpuwTE>rMwq6e7}VB
zi@_dUB^VQw^6+roLZrWa>57@8830iCQq77;qb_=sy(tF;k3(@zci^yUEyB2_Qp=&T
zbNcRBd7;x=2c4{&%ef5G1-u;C*vngpghq13e%g~_abI1GNsS44Us9B-+aUGTX@!;-
zSU&gKe9kRP@PrqitFB~|45g6Z8zqC0A>j+kFSehbsK{m%mgGbAnz^M&1wFDuK`NAM
z`R}W%VzDjAr3pYUwnW2*SZ)Ae!`ive9kF)LF3x&4U`9rvEPNWHi#!;|5U3bUkm4hK
zciP^fFO1tA-h5FE{;R70D@3NF{!oW6PaMkH0`SRA;l9z3{kb|8OmyOyHly2ZEp|vR
zZXIBmF^@tKar{6NAHNPL*=hi67Ocw-vO97D!}n=VKx`@tZW|<Y2e=B%eta``Y+5UW
z%gqPd5vx?Y8`B`HMS#UV%%$NhE8{a+IE*YOv&=FyK&BECJcjVK;Fehpmh95ONAK6A
z@_2l?##kat(CY}q=l-3*G`*R8Mdlrd+4+T00UAj!L4H@MVr&~lQ+*E^>s5m1MrDTS
zwy#G|6036D4(qLQ-BN+NX<NSaNQu^$B;j}UNls$~YZw}A_f6IM#dlXueD`DV?BG>$
zooa9c%d2;m$+=b5`eUTI8ruas{1Z#d%k6;4ind|meIdZV#p=;&arhWY%roGeSzD{k
z?B8O)6kDKKM~uT0Hcw45t5^NcI!%T?SXXic$QCJy>dO3E)Hy#5?7Q$-&>gMD#01q$
z7aH9h0KP*nKxm-@+JEG>%DVzOlU~3Rju8TZT=h-~7ZbJvyeF20S4PQ?kqfABVd?3V
z)1_d))s2k?PkN7aV?yyd!0>ImBLh9-SO^XdlDq`cDuJ%2D_{AY^P?-^dwV7tHRN;(
z!c+PT-)xJ%l+hruH@mt{7e=^j;i*2pZz>i~sWS}+y>xEbWWTA$NHidnmeR(z+#!Yx
zS3i$;y?dzy_I!IMUbpu}%d{_5t?r`FWUVkdJdyz4>xJG6i#ZNjOH1da%P;Kl*AXQ>
zJ|O2(&y(pF)Au>9y(r^hGI=8?QWea}22XPnwwY$)UUinhY(CqY{OHYs>hH#g2`X;^
z!jMrTHlNrI%=>aiLDOz*S1R&1kmbo<(7#z7_J=6s0JPu<EbqfdmjkY)T1HL<6XWb2
zXI3>g!508Sie>S3-}R}}CjxrI>H5Z?@?Vmho5Mhn7*?+>g(xVL?w2+WjWR}pNkRg!
zf4VJF{3dMhwm)GF9>-#^z3ncqb@NM2b$L1H!}5c~H3?+j`4j;O2NTwcC3yIO(GhTz
z>jjwUUSfPFb&ZYs-hf=7=flHCrhrx!4l}wN(cu^t#!Tg|E@NNi$6a}Z-<lFo2cJvW
zbLf3YwVG9+^u^?yh&G~Rib=v9zdmbLlt_?5<*^2-2RlLIrEg#DJ_SSnXj3>pDTw>B
zu~GUh24n1_b0WHe6Rrl1l8eATBSYHByHkw1y|#_{v@azj)e%gKy3A||!0be2ZCFyd
z{H}#LItjOl;wWx1k}kNk`S>D(D__4l!Z9LQ6~e{{cTDxt4bow>p`NoN6!dnUb3PY>
z7jd)@#R~21+%p*6!J+yfhkoxi$t=Qu^Y_nEtugHjO&$VWg_V_KmozjqN;d>87Xj+{
zX`FAAJ}n^~GlOtB=$%zIe_F9F+6nDmZe~Qt52gsM0evgt4!jOL;8!~(u+8C&we|x;
z`rKonArlnn!2$y;TuP)<>Kp(yUoUgb+%hiA!l2!-(k`u4{_?N`OTcpF7s;Ne;4!5(
zI-pdv&q-|tO518GDnCa8F^Fk@wWUd2Y{NOzhtBLhLY8vhA{N1i#=_i?pF}G(do`Xg
z>@loJW2y%1Qe3WtkcHTghH5`P4=eX$=RKWif%$e_s-*|Y=kM4p^D0d2c(<-Rj+0Fb
z-|&lot~dpOgv(1}PPbSNJgeK1c}dwzYjC%toQhaRS8ieVUISLs<xOfVwfn+4O+{*F
zWGYOFPSZpsIhG0=7Nh)29qDxnAR9q-I6jN+YD}Bg`TfVxWHwcs`pX*lY@%(Hw}rGz
zi>9aJ8Os(kG0k6<OaLR2dEiJ7inFmEAmOrzQIG)jk|*`VN8I}UBC#<BdJ@`_N8ARg
zJYK>SJQQv7#j!fo%?T8x+vidRezg`}Dy;2fPV*@Bn^KzH0;5|jt+ye%`1Wjwut|VJ
z`Q;0hOfO(qN%c#>RH$F4M(Vcuv~D#@0kM~HrVemXTm$rMuA1VOlNw`B$$~L2;P7L8
z%Uk#Jm;#oI{3Ne#ZMWryAG|Pf#MD7v$RU9bltBnR)94%_PmM+;dVbp@V0)@-9*tTn
zR>D95KhHe!RlQ)N6f}j=*^Q7flke	%0^c>O%*OkVmcSQwgzI6WP2m3$pEbze=t&
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
NIVmN{VsYb-{|8bOF{}Up
literal 0
HcmV?d00001
diff --git a/docs/en_US/images/new_connection_options.png b/docs/en_US/images/new_connection_options.png
new file mode 100644
index 0000000000000000000000000000000000000000..7ca94374baf0eb9a8a0e49cbd9d4d426895ff9d0
GIT binary patch
literal 45739
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
literal 0
HcmV?d00001
diff --git a/docs/en_US/query_tool.rst b/docs/en_US/query_tool.rst
index 83933f0..80f27a5 100644
--- a/docs/en_US/query_tool.rst
+++ b/docs/en_US/query_tool.rst
@@ -300,3 +300,24 @@ transaction status by clicking on the status icon in the Query Tool:
.. image:: images/query_tool_connection_status.png
:alt: Query tool connection and transaction statuses
:align: center
+
+Change connection
+*****************
+
+User can connect to another server or database from existing open session of query tool.
+
+* Click on the connection link next to connection status.
+* Now click on the *<New Connection>* option from the dropdown.
+
+.. image:: images/new_connection_options.png
+ :alt: Query tool connection options
+ :align: center
+
+* Now select server, database, user, and role to connect and click OK.
+
+.. image:: images/new_connection_dialog.png
+ :alt: Query tool connection dialog
+ :align: center
+
+* A newly created connection will now get listed in the options.
+* To connect, select the newly created connection from the dropdown list.
diff --git a/web/pgadmin/browser/server_groups/servers/__init__.py b/web/pgadmin/browser/server_groups/servers/__init__.py
index af845e5..34680be 100644
--- a/web/pgadmin/browser/server_groups/servers/__init__.py
+++ b/web/pgadmin/browser/server_groups/servers/__init__.py
@@ -1241,7 +1241,7 @@ class ServerNode(PGChildNodeView):
}
)
- def connect(self, gid, sid):
+ def connect(self, gid, sid, user_name=None):
"""
Connect the Server and return the connection object.
Verification Process before Connection:
@@ -1363,7 +1363,8 @@ class ServerNode(PGChildNodeView):
# not provided, or password has not been saved earlier.
if prompt_password or prompt_tunnel_password:
return self.get_response_for_password(server, 428, prompt_password,
- prompt_tunnel_password)
+ prompt_tunnel_password,
+ user=user_name)
status = True
try:
@@ -1797,7 +1798,8 @@ class ServerNode(PGChildNodeView):
return internal_server_error(errormsg=str(e))
def get_response_for_password(self, server, status, prompt_password=False,
- prompt_tunnel_password=False, errmsg=None):
+ prompt_tunnel_password=False, errmsg=None,
+ user=None):
if server.use_ssh_tunnel:
return make_json_response(
@@ -1824,7 +1826,7 @@ class ServerNode(PGChildNodeView):
result=render_template(
'servers/password.html',
server_label=server.name,
- username=server.username,
+ username=user if user else server.username,
errmsg=errmsg,
service=server.service,
_=gettext,
diff --git a/web/pgadmin/browser/server_groups/servers/roles/tests/utils.py b/web/pgadmin/browser/server_groups/servers/roles/tests/utils.py
index 3a7ee58..028ee64 100644
--- a/web/pgadmin/browser/server_groups/servers/roles/tests/utils.py
+++ b/web/pgadmin/browser/server_groups/servers/roles/tests/utils.py
@@ -152,3 +152,38 @@ def delete_role(connection, role_names):
exception = "Error while deleting role: %s: line:%s %s" % (
file_name, sys.exc_traceback.tb_lineno, exception)
print(exception, file=sys.stderr)
+
+
+def create_role_with_password(server, role_name, role_password):
+ """
+ This function create the role.
+ :param server:
+ :param role_name:
+ :param role_password:
+ :return:
+ """
+ try:
+ connection = utils.get_db_connection(server['db'],
+ server['username'],
+ server['db_password'],
+ server['host'],
+ server['port'],
+ server['sslmode'])
+ pg_cursor = connection.cursor()
+ pg_cursor.execute(
+ "CREATE ROLE %s LOGIN PASSWORD '%s'" % (role_name, role_password))
+ connection.commit()
+ # Get 'oid' from newly created tablespace
+ pg_cursor.execute(
+ "SELECT pr.oid from pg_catalog.pg_roles pr WHERE pr.rolname='%s'" %
+ role_name)
+ oid = pg_cursor.fetchone()
+ role_id = ''
+ if oid:
+ role_id = oid[0]
+ connection.close()
+ return role_id
+ except Exception as exception:
+ exception = "Error while deleting role: %s: line:%s %s" % (
+ file_name, sys.exc_traceback.tb_lineno, exception)
+ print(exception, file=sys.stderr)
diff --git a/web/pgadmin/model/__init__.py b/web/pgadmin/model/__init__.py
index 976e962..c3fdaf3 100644
--- a/web/pgadmin/model/__init__.py
+++ b/web/pgadmin/model/__init__.py
@@ -95,6 +95,15 @@ class ServerGroup(db.Model):
name = db.Column(db.String(128), nullable=False)
__table_args__ = (db.UniqueConstraint('user_id', 'name'),)
+ @property
+ def serialize(self):
+ """Return object data in easily serializable format"""
+ return {
+ 'id': self.id,
+ 'user_id': self.user_id,
+ 'name': self.name,
+ }
+
class Server(db.Model):
"""Define a registered Postgres server"""
@@ -176,6 +185,44 @@ class Server(db.Model):
tunnel_password = db.Column(db.String(64), nullable=True)
shared = db.Column(db.Boolean(), nullable=False)
+ @property
+ def serialize(self):
+ """Return object data in easily serializable format"""
+ return {
+ "id": self.id,
+ "user_id": self.user_id,
+ "servergroup_id": self.servergroup_id,
+ "name": self.name,
+ "host": self.host,
+ "hostaddr": self.hostaddr,
+ "port": self.port,
+ "maintenance_db": self.maintenance_db,
+ "username": self.username,
+ "password": self.password,
+ "save_password": self.save_password,
+ "role": self.role,
+ "ssl_mode": self.ssl_mode,
+ "comment": self.comment,
+ "discovery_id": self.discovery_id,
+ "db_res": self.db_res,
+ "passfile": self.passfile,
+ "sslcert": self.sslcert,
+ "sslkey": self.sslkey,
+ "sslrootcert": self.sslrootcert,
+ "sslcrl": self.sslcrl,
+ "sslcompression": self.sslcompression,
+ "bgcolor": self.bgcolor,
+ "fgcolor": self.fgcolor,
+ "service": self.service,
+ "connect_timeout": self.connect_timeout,
+ "use_ssh_tunnel": self.use_ssh_tunnel,
+ "tunnel_host": self.tunnel_host,
+ "tunnel_port": self.tunnel_port,
+ "tunnel_authentication": self.tunnel_authentication,
+ "tunnel_identity_file": self.tunnel_identity_file,
+ "tunnel_password": self.tunnel_password
+ }
+
class ModulePreference(db.Model):
"""Define a preferences table for any modules."""
diff --git a/web/pgadmin/static/js/sqleditor/new_connection_dialog.js b/web/pgadmin/static/js/sqleditor/new_connection_dialog.js
new file mode 100644
index 0000000..3fcd374
--- /dev/null
+++ b/web/pgadmin/static/js/sqleditor/new_connection_dialog.js
@@ -0,0 +1,262 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2020, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import gettext from 'sources/gettext';
+import url_for from 'sources/url_for';
+import $ from 'jquery';
+import Alertify from 'pgadmin.alertifyjs';
+import pgAdmin from 'sources/pgadmin';
+import Backform from 'pgadmin.backform';
+import newConnectionDialogModel from 'sources/sqleditor/new_connection_dialog_model';
+
+
+let NewConnectionDialog = {
+ 'dialog': function(handler, reconnect) {
+ let url = url_for('sqleditor.get_new_connection_data', {
+ 'sid': handler.url_params.sid,
+ 'sgid': handler.url_params.sgid,
+ });
+
+ if(reconnect) {
+ url += '?connect=1';
+ }
+
+ let title = gettext('Connect to server');
+
+ $.ajax({
+ url: url,
+ headers: {
+ 'Cache-Control' : 'no-cache',
+ },
+ }).done(function (res) {
+ let response = res.data.result;
+ response.database_list = [];
+ response.user_list = [];
+ if (Alertify.newConnectionDialog) {
+ delete Alertify.newConnectionDialog;
+ }
+
+ // Create Dialog
+ Alertify.dialog('newConnectionDialog', function factory() {
+ let $container = $('<div class=\'new-connection-dialog\'></div>');
+ return {
+ main: function(message) {
+ this.msg = message;
+ },
+ build: function() {
+ this.elements.content.appendChild($container.get(0));
+ Alertify.pgDialogBuild.apply(this);
+ },
+ setup: function(){
+ return {
+ buttons: [
+ {
+ text: '',
+ key: 112,
+ className: 'btn btn-primary-icon pull-left fa fa-question pg-alertify-icon-button',
+ attrs: {
+ name: 'dialog_help',
+ type: 'button',
+ label: gettext('Help'),
+ 'aria-label': gettext('Help'),
+ url: url_for('help.static', {
+ 'filename': 'query_tool.html',
+ }),
+ },
+ },
+ {
+ text: gettext('Cancel'),
+ key: 27,
+ className: 'btn btn-secondary fa fa-times pg-alertify-button',
+ 'data-btn-name': 'cancel',
+ }, {
+ text: gettext('OK'),
+ key: 13,
+ className: 'btn btn-primary fa fa-check pg-alertify-button',
+ 'data-btn-name': 'ok',
+ },
+ ],
+ // Set options for dialog
+ options: {
+ title: title,
+ //disable both padding and overflow control.
+ padding: !1,
+ overflow: !1,
+ model: 0,
+ resizable: true,
+ maximizable: false,
+ pinnable: false,
+ closableByDimmer: false,
+ modal: false,
+ autoReset: false,
+ closable: true,
+ },
+ };
+ },
+ prepare: function() {
+ let self = this;
+ $container.html('');
+ // Disable Ok button
+ this.__internal.buttons[2].element.disabled = true;
+
+ // Status bar
+ this.statusBar = $(
+ '<div class=\'pg-prop-status-bar pg-el-xs-12 d-none\'>' +
+ ' <div class="error-in-footer"> ' +
+ ' <div class="d-flex px-2 py-1"> ' +
+ ' <div class="pr-2"> ' +
+ ' <i class="fa fa-exclamation-triangle text-danger" aria-hidden="true"></i> ' +
+ ' </div> ' +
+ ' <div class="alert-text" role="alert"></div> ' +
+ ' </div> ' +
+ ' </div> ' +
+ '</div>').appendTo($container);
+
+ // To show progress on filter Saving/Updating on AJAX
+ this.showNewConnectionProgress = $(
+ `<div id="show_filter_progress" class="pg-sp-container sql-editor-busy-fetching d-none">
+ <div class="pg-sp-content">
+ <div class="row"><div class="col-12 pg-sp-icon sql-editor-busy-icon"></div></div>
+ <div class="row"><div class="col-12 pg-sp-text sql-editor-busy-text">` + gettext('Loading data...') + `</div></div>
+ </div>
+ </div>`
+ ).appendTo($container);
+ $(
+ self.showNewConnectionProgress[0]
+ ).removeClass('d-none');
+
+ self.newConnCollectionModel = newConnectionDialogModel(response, handler.url_params.sgid, handler.url_params.sid);
+ let fields = Backform.generateViewSchema(null, self.newConnCollectionModel, 'create', null, null, true);
+
+ let view = this.view = new Backform.Dialog({
+ el: '<div></div>',
+ model: self.newConnCollectionModel,
+ schema: fields,
+ });
+
+ $(this.elements.body.childNodes[0]).addClass(
+ 'alertify_tools_dialog_properties obj_properties'
+ );
+
+ $container.append(view.render().$el);
+
+ // Enable/disable save button and show/hide statusbar based on session
+ view.listenTo(view.model, 'pgadmin-session:start', function() {
+ view.listenTo(view.model, 'pgadmin-session:invalid', function(msg) {
+ self.statusBar.removeClass('d-none');
+ $(self.statusBar.find('.alert-text')).html(msg);
+ // Disable Okay button
+ self.__internal.buttons[2].element.disabled = true;
+ });
+
+ view.listenTo(view.model, 'pgadmin-session:valid', function() {
+ self.statusBar.addClass('d-none');
+ $(self.statusBar.find('.alert-text')).html('');
+ // Enable Okay button
+ self.__internal.buttons[2].element.disabled = false;
+ });
+ });
+
+ view.listenTo(view.model, 'pgadmin-session:stop', function() {
+ view.stopListening(view.model, 'pgadmin-session:invalid');
+ view.stopListening(view.model, 'pgadmin-session:valid');
+ });
+
+ // Starts monitoring changes to model
+ view.model.startNewSession();
+
+ // Hide Progress ...
+ $(
+ self.showNewConnectionProgress[0]
+ ).addClass('d-none');
+ },
+ callback: function(e) {
+ let self = this;
+ if (e.button.element.name == 'dialog_help') {
+ e.cancel = true;
+ pgAdmin.Browser.showHelp(e.button.element.name, e.button.element.getAttribute('url'),
+ null, null);
+ return;
+ } else if (e.button['data-btn-name'] === 'ok') {
+ e.cancel = true; // Do not close dialog
+ let newConnCollectionModel = this.newConnCollectionModel.toJSON();
+
+ let selected_database_name = null;
+ response.database_list.forEach(function(data){
+ if(newConnCollectionModel['database'] == data['value']) {
+ selected_database_name = data['label'];
+ return false;
+ }
+ });
+ let tab_title = '';
+ if(newConnCollectionModel['role']) {
+ tab_title = selected_database_name + '/' + newConnCollectionModel['role'] + '@' + response.server_name;
+ } else {
+ tab_title = selected_database_name + '/' + newConnCollectionModel['user'] + '@' + response.server_name;
+ newConnCollectionModel['role'] = null;
+ }
+
+ let is_create_connection = true;
+
+ handler.gridView.connection_list.forEach(function(connection_data){
+ if(parseInt(connection_data['server']) == newConnCollectionModel['server']
+ && parseInt(connection_data['database']) == newConnCollectionModel['database']
+ && connection_data['user'] == newConnCollectionModel['user'] && connection_data['role'] == newConnCollectionModel['role']) {
+ is_create_connection = false;
+ // break for loop by return false.
+ return false;
+ }
+
+ if(tab_title == connection_data['title']) {
+ is_create_connection = false;
+ return false;
+ }
+ });
+ if(!is_create_connection) {
+ let errmsg = 'Connection with this configuration already present.';
+ Alertify.info(errmsg);
+ }else {
+ let connection_details = {
+ 'server_group': handler.gridView.handler.url_params.sgid,
+ 'server': newConnCollectionModel['server'],
+ 'database': newConnCollectionModel['database'],
+ 'title': tab_title,
+ 'user': newConnCollectionModel['user'],
+ 'role': newConnCollectionModel['role'],
+ 'password': response.password,
+ };
+ handler.gridView.on_change_connection(connection_details, self);
+ }
+ } else {
+ self.close();
+ }
+ },
+ };
+ });
+ setTimeout(function(){
+ Alertify.newConnectionDialog('Connect to server.').resizeTo(pgAdmin.Browser.stdW.md,pgAdmin.Browser.stdH.md);
+ }, 500);
+ }).fail(function(error) {
+ Alertify.alert().setting({
+ 'title': gettext('Connection lost'),
+ 'label':gettext('Ok'),
+ 'message': gettext('Connection to the server has been lost.'),
+ 'onok': function(){
+ alert(error);
+ //Close the window after connection is lost
+ window.close();
+ },
+ }).show();
+ });
+
+ },
+
+};
+
+module.exports = NewConnectionDialog;
diff --git a/web/pgadmin/static/js/sqleditor/new_connection_dialog_model.js b/web/pgadmin/static/js/sqleditor/new_connection_dialog_model.js
new file mode 100644
index 0000000..e262d0e
--- /dev/null
+++ b/web/pgadmin/static/js/sqleditor/new_connection_dialog_model.js
@@ -0,0 +1,339 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2020, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import gettext from 'sources/gettext';
+import _ from 'underscore';
+import $ from 'jquery';
+import pgAdmin from 'sources/pgadmin';
+import Backform from 'pgadmin.backform';
+import url_for from 'sources/url_for';
+import alertify from 'pgadmin.alertifyjs';
+
+export default function newConnectionDialogModel(response, sgid, sid) {
+
+ let server_name = '';
+ let database_name = '';
+
+ let NewConnectionSelect2Control = Backform.Select2Control.extend({
+ fetchData: function(){
+ let self = this;
+ let url = self.field.get('url');
+
+ url = url_for(url, {
+ 'sid': self.model.attributes.server,
+ 'sgid': sgid,
+ });
+
+ $.ajax({
+ async: false,
+ url: url,
+ headers: {
+ 'Cache-Control' : 'no-cache',
+ },
+ }).done(function (res) {
+ var transform = self.field.get('transform');
+ if(res.data.status){
+ let data = res.data.result.data;
+
+ if (transform && _.isFunction(transform)) {
+ self.field.set('options', transform.bind(self, data));
+ } else {
+ self.field.set('options', data);
+ }
+ } else {
+ if (transform && _.isFunction(transform)) {
+ self.field.set('options', transform.bind(self, []));
+ } else {
+ self.field.set('options', []);
+ }
+ //alertify.error(res.data.msg);
+ }
+ }).fail(function(e){
+ let msg = '';
+ if(e.status == 404) {
+ msg = 'Unable to find url.';
+ } else {
+ msg = e.responseJSON.errormsg;
+ }
+ alertify.error(msg);
+ });
+ },
+ render: function() {
+ this.fetchData();
+ return Backform.Select2Control.prototype.render.apply(this, arguments);
+ },
+ onChange: function() {
+ Backform.Select2Control.prototype.onChange.apply(this, arguments);
+ },
+ });
+
+ let newConnectionModel = pgAdmin.Browser.DataModel.extend({
+ idAttribute: 'name',
+ defaults: {
+ server: parseInt(sid),
+ database: null,
+ user: null,
+ password: null,
+ server_name: server_name,
+ database_name: database_name,
+ },
+ schema: [{
+ id: 'server',
+ name: 'server',
+ label: gettext('Server'),
+ type: 'text',
+ editable: true,
+ disabled: false,
+ select2: {
+ allowClear: false,
+ },
+ control: Backform.Select2Control.extend({
+ connect: function(self) {
+ let local_self = self;
+ if(!alertify.connectServer){
+ alertify.dialog('connectServer', function factory() {
+ return {
+ main: function(
+ title, message, server_id, submit_password=true
+ ) {
+ this.set('title', title);
+ this.message = message;
+ this.server_id = server_id;
+ this.submit_password = submit_password;
+ },
+ setup:function() {
+ return {
+ buttons:[{
+ text: gettext('Cancel'), className: 'btn btn-secondary fa fa-times pg-alertify-button',
+ key: 27,
+ },{
+ text: gettext('OK'), key: 13, className: 'btn btn-primary fa fa-check pg-alertify-button',
+ }],
+ focus: {element: '#password', select: true},
+ options: {
+ modal: 0, resizable: false, maximizable: false, pinnable: false,
+ },
+ };
+ },
+ build:function() {
+ },
+ prepare:function() {
+ this.setContent(this.message);
+ },
+ callback: function(closeEvent) {
+
+ if (closeEvent.button.text == gettext('OK')) {
+ if(this.submit_password) {
+ var _url = url_for('sqleditor.connect_server', {'sid': this.server_id});
+
+ $.ajax({
+ type: 'POST',
+ timeout: 30000,
+ url: _url,
+ data: $('#frmPassword').serialize(),
+ })
+ .done(function() {
+ local_self.model.attributes.database = null;
+ local_self.model.attributes.user = null;
+ local_self.model.attributes.role = null;
+ Backform.Select2Control.prototype.onChange.apply(local_self, arguments);
+ response.server_list.forEach(function(obj){
+ if(obj.id==self.model.changed.server) {
+ response.server_name = obj.name;
+ }
+ });
+ })
+ .fail(function(xhr) {
+ alertify.connectServer('Connect to server', xhr.responseJSON.result, local_self.getValueFromDOM());
+ });
+ } else {
+ response.password = $('#password').val();
+ }
+ } else {
+ local_self.model.attributes.database = null;
+ local_self.model.attributes.user = null;
+ local_self.model.attributes.role = null;
+ Backform.Select2Control.prototype.onChange.apply(local_self, arguments);
+ }
+ closeEvent.close = true;
+ },
+ };
+ });
+ }
+ },
+ render: function() {
+ let self = this;
+ self.connect(self);
+ return Backform.Select2Control.prototype.render.apply(self, arguments);
+ },
+ onChange: function() {
+ this.model.attributes.database = null;
+ this.model.attributes.user = null;
+ let self = this;
+ self.connect(self);
+
+ let url = url_for('sqleditor.connect_server', {
+ 'sid': self.getValueFromDOM(),
+ 'usr': self.model.attributes.user,
+ });
+ $.ajax({
+ async: false,
+ url: url,
+ type: 'POST',
+ headers: {
+ 'Cache-Control' : 'no-cache',
+ },
+ }).done(function () {
+ Backform.Select2Control.prototype.onChange.apply(self, arguments);
+ response.server_list.forEach(function(obj){
+ if(obj.id==self.model.changed.server) {
+ response.server_name = obj.name;
+ }
+ });
+ }).fail(function(xhr){
+ alertify.connectServer('Connect to server', xhr.responseJSON.result, self.getValueFromDOM());
+ });
+
+ },
+ }),
+ options: function() {
+ return _.map(response.server_list, (obj) => {
+ if (obj.id == parseInt(sid))
+ response.server_name = obj.name;
+
+ return {
+ value: obj.id,
+ label: obj.name,
+ };
+ });
+ },
+ },
+ {
+ id: 'database',
+ name: 'database',
+ label: gettext('Database'),
+ type: 'text',
+ editable: true,
+ disabled: function(m) {
+ let self_local = this;
+ if (!_.isUndefined(m.get('server')) && !_.isNull(m.get('server'))
+ && m.get('server') !== '') {
+ setTimeout(function() {
+ if(self_local.options.length) {
+ m.set('database', self_local.options[0].value);
+ }
+ }, 10);
+ return false;
+ }
+
+ return true;
+ },
+ deps: ['server'],
+ url: 'sqleditor.get_new_connection_database',
+ select2: {
+ allowClear: false,
+ width: '100%',
+ first_empty: true,
+ select_first: false,
+ },
+ extraClasses:['new-connection-dialog-style'],
+ control: NewConnectionSelect2Control,
+ transform: function(data) {
+ response.database_list = data;
+ return data;
+ },
+ },
+ {
+ id: 'user',
+ name: 'user',
+ label: gettext('User'),
+ type: 'text',
+ editable: true,
+ deps: ['server'],
+ select2: {
+ allowClear: false,
+ width: '100%',
+ },
+ control: NewConnectionSelect2Control,
+ url: 'sqleditor.get_new_connection_user',
+ disabled: function(m) {
+ let self_local = this;
+ if (!_.isUndefined(m.get('server')) && !_.isNull(m.get('server'))
+ && m.get('server') !== '') {
+ setTimeout(function() {
+ if(self_local.options.length) {
+ m.set('user', self_local.options[0].value);
+ }
+ }, 10);
+ return false;
+ }
+ return true;
+ },
+ },{
+ id: 'role',
+ name: 'role',
+ label: gettext('Role'),
+ type: 'text',
+ editable: true,
+ deps: ['server'],
+ select2: {
+ allowClear: false,
+ width: '100%',
+ first_empty: true,
+ },
+ control: NewConnectionSelect2Control,
+ url: 'sqleditor.get_new_connection_role',
+ disabled: false,
+ },
+ /*{
+ id: 'password',
+ name: 'password',
+ label: gettext('Password'tools/sqleditor/__init__.py),
+ type: 'password',
+ editable: true,
+ disabled: true,
+ deps: ['user'],
+ control: Backform.InputControl.extend({
+ render: function() {
+ let self = this;
+ self.model.attributes.password = null;
+ Backform.InputControl.prototype.render.apply(self, arguments);
+ return self;
+ },
+ onChange: function() {
+ let self = this;
+ Backform.InputControl.prototype.onChange.apply(self, arguments);
+ },
+ }),
+ },*/
+ ],
+ validate: function() {
+ let msg = null;
+ this.errorModel.clear();
+ if(_.isUndefined(this.get('database')) || _.isNull(this.get('database'))){
+ msg = gettext('Please select database');
+ this.errorModel.set('database', msg);
+ return msg;
+ } else if(_.isUndefined(this.get('database')) || _.isUndefined(this.get('user'))|| _.isNull(this.get('user'))) {
+ msg = gettext('Please select user');
+ this.errorModel.set('user', msg);
+ return msg;
+ }
+ /*else if((this.attributes.password == '' || _.isUndefined(this.get('password')) || _.isNull(this.get('password')))) {
+ msg = gettext('Please enter password');
+ this.errorModel.set('password', msg);
+ return msg;
+ }*/
+ return null;
+ },
+ });
+
+ let model = new newConnectionModel();
+ return model;
+}
diff --git a/web/pgadmin/static/scss/_alert.scss b/web/pgadmin/static/scss/_alert.scss
index dac552b..836f0af 100644
--- a/web/pgadmin/static/scss/_alert.scss
+++ b/web/pgadmin/static/scss/_alert.scss
@@ -92,6 +92,7 @@
right: 0;
left: 0;
bottom: 0;
+ z-index: 1;
}
.pg-prop-status-bar {
diff --git a/web/pgadmin/tools/datagrid/__init__.py b/web/pgadmin/tools/datagrid/__init__.py
index 1bd841f..f7b836a 100644
--- a/web/pgadmin/tools/datagrid/__init__.py
+++ b/web/pgadmin/tools/datagrid/__init__.py
@@ -18,22 +18,23 @@ from flask import Response, url_for, session, request, make_response
from werkzeug.useragents import UserAgent
from flask import current_app as app, render_template
from flask_babelex import gettext
-from flask_security import login_required
+from flask_security import login_required, current_user
from pgadmin.tools.sqleditor.command import ObjectRegistry, SQLFilter
+from pgadmin.tools.sqleditor import check_transaction_status
from pgadmin.utils import PgAdminModule
from pgadmin.utils.ajax import make_json_response, bad_request, \
- internal_server_error
+ internal_server_error, unauthorized
from config import PG_DEFAULT_DRIVER
-from pgadmin.model import Server
+from pgadmin.model import Server, User
from pgadmin.utils.driver import get_driver
from pgadmin.utils.exception import ConnectionLost, SSHTunnelConnectionLost
from pgadmin.utils.preferences import Preferences
from pgadmin.settings import get_setting
from pgadmin.browser.utils import underscore_unescape
from pgadmin.utils.exception import ObjectGone
-from pgadmin.utils.constants import MIMETYPE_APP_JS
from pgadmin.tools.sqleditor.utils.macros import get_user_macros
+from pgadmin.utils.constants import MIMETYPE_APP_JS, UNAUTH_REQ
MODULE_NAME = 'datagrid'
@@ -74,7 +75,8 @@ class DataGridModule(PgAdminModule):
'datagrid.filter_validate',
'datagrid.filter',
'datagrid.panel',
- 'datagrid.close'
+ 'datagrid.close',
+ 'datagrid.update_query_tool_connection'
]
def on_logout(self, user):
@@ -324,10 +326,48 @@ def initialize_query_tool(trans_id, sgid, sid, did=None):
req_args['recreate'] == '1'):
connect = False
+ is_error, errmsg, conn_id, version = _init_query_tool(trans_id, connect,
+ sgid, sid, did)
+ if is_error:
+ return errmsg
+
+ return make_json_response(
+ data={
+ 'connId': str(conn_id),
+ 'serverVersion': version,
+ }
+ )
+
+
+def _connect(conn, **kwargs):
+ """
+ Connect the database.
+ :param conn: Connection instance.
+ :param kwargs: user, role and password data from user.
+ :return:
+ """
+ user = None
+ role = None
+ password = None
+ is_ask_password = False
+ if 'user' in kwargs and 'role' in kwargs:
+ user = kwargs['user']
+ role = kwargs['role'] if kwargs['role'] else None
+ password = kwargs['password'] if kwargs['password'] else None
+ is_ask_password = True
+ if user:
+ status, msg = conn.connect(user=user, role=role,
+ password=password)
+ else:
+ status, msg = conn.connect()
+
+ return status, msg, is_ask_password, user, role, password
+
+
+def _init_query_tool(trans_id, connect, sgid, sid, did, **kwargs):
# Create asynchronous connection using random connection id.
conn_id = str(random.randint(1, 9999999))
- # Use Maintenance database OID
manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)
if did is None:
@@ -338,24 +378,41 @@ def initialize_query_tool(trans_id, sgid, sid, did=None):
)
except Exception as e:
app.logger.error(e)
- return internal_server_error(errormsg=str(e))
+ return True, internal_server_error(errormsg=str(e)), '', ''
try:
conn = manager.connection(did=did, conn_id=conn_id,
auto_reconnect=False,
use_binary_placeholder=True,
array_to_string=True)
+
if connect:
- status, msg = conn.connect()
+ status, msg, is_ask_password, user, role, password = _connect(
+ conn, **kwargs)
if not status:
app.logger.error(msg)
- return internal_server_error(errormsg=str(msg))
+ if is_ask_password:
+ server = Server.query.filter_by(id=sid).first()
+ return True, make_json_response(
+ success=0,
+ status=428,
+ result=render_template(
+ 'servers/password.html',
+ server_label=server.name,
+ username=user,
+ errmsg=msg,
+ _=gettext,
+ )
+ ), '', ''
+ else:
+ return True, internal_server_error(
+ errormsg=str(msg)), '', ''
except (ConnectionLost, SSHTunnelConnectionLost) as e:
app.logger.error(e)
raise
except Exception as e:
app.logger.error(e)
- return internal_server_error(errormsg=str(e))
+ return True, internal_server_error(errormsg=str(e)), '', ''
if 'gridData' not in session:
sql_grid_data = dict()
@@ -377,10 +434,77 @@ def initialize_query_tool(trans_id, sgid, sid, did=None):
# Store the grid dictionary into the session variable
session['gridData'] = sql_grid_data
+ return False, '', conn_id, manager.version
+
+
[email protected](
+ '/initialize/query_tool/update_connection/<int:trans_id>/'
+ '<int:sgid>/<int:sid>/<int:did>',
+ methods=["POST"], endpoint='update_query_tool_connection'
+)
+def update_query_tool_connection(trans_id, sgid, sid, did):
+ # Remove transaction Id.
+ with query_tool_close_session_lock:
+ data = json.loads(request.data, encoding='utf-8')
+
+ if 'gridData' not in session:
+ return make_json_response(data={'status': True})
+
+ grid_data = session['gridData']
+
+ # Return from the function if transaction id not found
+ if str(trans_id) not in grid_data:
+ return make_json_response(data={'status': True})
+
+ connect = True
+
+ req_args = request.args
+ if ('recreate' in req_args and
+ req_args['recreate'] == '1'):
+ connect = False
+
+ new_trans_id = str(random.randint(1, 9999999))
+ kwargs = {
+ 'user': data['user'],
+ 'role': data['role'],
+ 'password': data['password'] if 'password' in data else None
+ }
+
+ is_error, errmsg, conn_id, version = _init_query_tool(
+ new_trans_id, connect, sgid, sid, did, **kwargs)
+
+ if is_error:
+ return errmsg
+ else:
+ try:
+ # Check the transaction and connection status
+ status, error_msg, conn, trans_obj, session_obj = \
+ check_transaction_status(trans_id)
+
+ status, error_msg, new_conn, new_trans_obj, new_session_obj = \
+ check_transaction_status(new_trans_id)
+
+ new_session_obj['primary_keys'] = session_obj[
+ 'primary_keys'] if 'primary_keys' in session_obj else None
+ new_session_obj['columns_info'] = session_obj[
+ 'columns_info'] if 'columns_info' in session_obj else None
+ new_session_obj['client_primary_key'] = session_obj[
+ 'client_primary_key'] if 'client_primary_key'\
+ in session_obj else None
+
+ close_query_tool_session(trans_id)
+ # Remove the information of unique transaction id from the
+ # session variable.
+ grid_data.pop(str(trans_id), None)
+ session['gridData'] = grid_data
+ except Exception as e:
+ app.logger.error(e)
+
return make_json_response(
data={
'connId': str(conn_id),
- 'serverVersion': manager.version,
+ 'serverVersion': version,
+ 'tran_id': new_trans_id
}
)
diff --git a/web/pgadmin/tools/datagrid/templates/datagrid/index.html b/web/pgadmin/tools/datagrid/templates/datagrid/index.html
index a0eebc8..4970027 100644
--- a/web/pgadmin/tools/datagrid/templates/datagrid/index.html
+++ b/web/pgadmin/tools/datagrid/templates/datagrid/index.html
@@ -417,8 +417,17 @@
title="" role="img">
</i>
</div>
- <div class="editor-title"
- style="background-color: {% if fgcolor %}{{ bgcolor or '#FFFFFF' }}{% endif %}; color: {% if fgcolor %}{{ fgcolor }}{% endif %};"> </div>
+ <div class="connection-info btn-group mr-1" role="group" aria-label="">
+ <div class="editor-title" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"
+ style="background-color: {% if fgcolor %}{{ bgcolor or '#FFFFFF' }}{% endif %}; color: {% if fgcolor %}{{ fgcolor }}{% endif %};">
+ </div>
+ <span class="conn-info-dd dropdown-toggle dropdown-toggle-split"
+ data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></span>
+ <ul class="dropdown-menu" id="connections-list">
+ </ul>
+ </div>
+
+
</div>
<div id="editor-panel" tabindex="0">
<div id="fetching_data" class="pg-sp-container sql-editor-busy-fetching">
@@ -481,6 +490,7 @@ require(['sources/generated/browser_nodes', 'sources/generated/codemirror', 'sou
var script_type_url = '';
{% endif %}
// Start the query tool.
+
sqlEditorController.start(
{{ uniqueId }},
{{ url_params|safe}},
diff --git a/web/pgadmin/tools/datagrid/tests/__init__.py b/web/pgadmin/tools/datagrid/tests/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/web/pgadmin/tools/datagrid/tests/datagrid_test_data.json b/web/pgadmin/tools/datagrid/tests/datagrid_test_data.json
new file mode 100644
index 0000000..0075f35
--- /dev/null
+++ b/web/pgadmin/tools/datagrid/tests/datagrid_test_data.json
@@ -0,0 +1,134 @@
+{
+ "data_grid_init_query_tool": [
+ {
+ "name": "Datagrid init query tool",
+ "url": "/datagrid/initialize/query_tool/",
+ "is_positive_test": true,
+ "mocking_required": false,
+ "test_data": {},
+ "mock_data": {},
+ "expected_data": {
+ "status_code": 200
+ }
+ }
+ ],
+ "data_grid_query_tool_close": [
+ {
+ "name": "Datagrid query tool close",
+ "url": "/datagrid/close/",
+ "is_positive_test": true,
+ "mocking_required": false,
+ "test_data": {},
+ "mock_data": {},
+ "expected_data": {
+ "status_code": 200
+ }
+ }
+ ],
+ "data_grid_validate_filter": [
+ {
+ "name": "Datagrid validate filter",
+ "url": "/datagrid/filter/validate/",
+ "is_positive_test": true,
+ "mocking_required": false,
+ "test_data": "id = 1",
+ "mock_data": {},
+ "expected_data": {
+ "status_code": 200
+ }
+ },
+ {
+ "name": "Datagrid validate filter",
+ "url": "/datagrid/filter/validate/",
+ "is_positive_test": false,
+ "mocking_required": true,
+ "test_data": "id = 1",
+ "mock_data": {
+ "function_name": "pgadmin.utils.driver.psycopg2.connection.Connection.execute_scalar",
+ "return_value": "(False, 'Mocked Internal Server Error while validate filter')"
+ },
+ "expected_data": {
+ "status_code": 200
+ }
+ }
+ ],
+ "data_grid_update_connection": [
+ {
+ "name": "Datagrid update connection positive",
+ "url": "/datagrid/initialize/query_tool/update_connection/",
+ "is_positive_test": true,
+ "mocking_required": false,
+ "is_create_role": false,
+ "test_data": {},
+ "mock_data": {},
+ "expected_data": {
+ "status_code": 200
+ }
+ },
+ {
+ "name": "Datagrid update connection with new user",
+ "url": "/datagrid/initialize/query_tool/update_connection/",
+ "is_positive_test": true,
+ "mocking_required": false,
+ "is_create_role": true,
+ "test_data": {},
+ "mock_data": {},
+ "expected_data": {
+ "status_code": 200
+ }
+ }
+ ],
+ "data_grid_panel": [
+ {
+ "name": "Datagrid Panel",
+ "url": "/datagrid/panel/",
+ "is_positive_test": true,
+ "mocking_required": false,
+ "test_data": {},
+ "mock_data": {
+ },
+ "expected_data": {
+ "status_code": 200
+ }
+ }
+ ],
+ "data_grid_initialize": [
+ {
+ "name": "Datagrid Initialize",
+ "url": "/datagrid/initialize/datagrid/",
+ "is_positive_test": true,
+ "mocking_required": false,
+ "test_data": "id=1",
+ "mock_data": {
+
+ },
+ "expected_data": {
+ "status_code": 200
+ }
+ },{
+ "name": "Datagrid Initialize",
+ "url": "/datagrid/initialize/datagrid/",
+ "is_positive_test": true,
+ "mocking_required": false,
+ "test_data": null,
+ "mock_data": {},
+ "expected_data": {
+ "status_code": 200
+ }
+ },
+ {
+ "name": "Datagrid Initialize",
+ "url": "/datagrid/initialize/datagrid/",
+ "is_positive_test": false,
+ "mocking_required": true,
+ "test_data": "id=1",
+ "mock_data": {
+ "function_name": "pgadmin.utils.driver.psycopg2.connection.Connection.execute_dict",
+ "return_value": "(False, 'Mocked Internal Server Error while initialize datagrid.')"
+ },
+ "expected_data": {
+ "status_code": 500
+ }
+ }
+ ]
+}
diff --git a/web/pgadmin/tools/datagrid/tests/test_data_grid_init_query_tool.py b/web/pgadmin/tools/datagrid/tests/test_data_grid_init_query_tool.py
new file mode 100644
index 0000000..f64e561
--- /dev/null
+++ b/web/pgadmin/tools/datagrid/tests/test_data_grid_init_query_tool.py
@@ -0,0 +1,73 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+
+import json
+import uuid
+import random
+
+from unittest.mock import patch
+from pgadmin.browser.server_groups.servers.databases.tests import utils as \
+ database_utils
+
+from pgadmin.utils.route import BaseTestGenerator
+from pgadmin.utils.exception import ExecuteError
+from regression import parent_node_dict
+from regression.python_test_utils import test_utils as utils
+from regression.test_setup import config_data
+from . import utils as data_grid_utils
+
+
+class DatagridInitQueryToolTestCase(BaseTestGenerator):
+ """
+ This will init query-tool connection.
+ """
+
+ scenarios = utils.generate_scenarios(
+ 'data_grid_init_query_tool',
+ data_grid_utils.test_cases
+ )
+
+ def setUp(self):
+ self.database_info = parent_node_dict["database"][-1]
+ self.db_name = self.database_info["db_name"]
+ self.did = self.database_info["db_id"]
+ self.sid = parent_node_dict["server"][-1]["server_id"]
+ self.sgid = config_data['server_group']
+
+ db_con = database_utils.connect_database(self, utils.SERVER_GROUP,
+ self.sid, self.did)
+
+ self.trans_id = str(random.randint(1, 9999999))
+
+ if not db_con['data']["connected"]:
+ raise ExecuteError("Could not connect to database to add a table.")
+
+ def init_query_tool(self):
+ response = self.tester.post(
+ self.url + str(self.trans_id) + '/' + str(self.sgid) + '/' + str(
+ self.sid) + '/' + str(self.did),
+ content_type='html/json'
+ )
+ return response
+
+ def runTest(self):
+ """ This function will init query tool connection."""
+
+ if self.is_positive_test:
+ response = self.init_query_tool()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+
+ self.assertEqual(actual_response_code, expected_response_code)
+
+ def tearDown(self):
+ """This function disconnect database."""
+ database_utils.disconnect_database(self, self.sid,
+ self.did)
diff --git a/web/pgadmin/tools/datagrid/tests/test_data_grid_panel.py b/web/pgadmin/tools/datagrid/tests/test_data_grid_panel.py
new file mode 100644
index 0000000..54bbe31
--- /dev/null
+++ b/web/pgadmin/tools/datagrid/tests/test_data_grid_panel.py
@@ -0,0 +1,90 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+
+import json
+import uuid
+import random
+
+from unittest.mock import patch
+from pgadmin.browser.server_groups.servers.databases.tests import utils as \
+ database_utils
+
+from pgadmin.utils.route import BaseTestGenerator
+from pgadmin.utils.exception import ExecuteError
+from regression import parent_node_dict
+from regression.python_test_utils import test_utils as utils
+from regression.test_setup import config_data
+from . import utils as data_grid_utils
+
+
+class DatagridPanelTestCase(BaseTestGenerator):
+ """
+ This will data grid panel.
+ """
+
+ scenarios = utils.generate_scenarios(
+ 'data_grid_panel',
+ data_grid_utils.test_cases
+ )
+
+ def setUp(self):
+ self.database_info = parent_node_dict["database"][-1]
+ self.db_name = self.database_info["db_name"]
+ self.did = self.database_info["db_id"]
+ self.sid = parent_node_dict["server"][-1]["server_id"]
+ self.sgid = config_data['server_group']
+
+ db_con = database_utils.connect_database(self, utils.SERVER_GROUP,
+ self.sid, self.did)
+ if not db_con['data']["connected"]:
+ raise ExecuteError("Could not connect to database to add a table.")
+
+ self.trans_id = str(random.randint(1, 9999999))
+ qt_init = data_grid_utils._init_query_tool(self, self.trans_id,
+ self.sgid, self.sid,
+ self.did)
+
+ if not qt_init['success']:
+ raise ExecuteError("Could not initialize querty tool.")
+
+ def panel(self):
+ query_param = \
+ '?is_query_tool={0}&sgid={1}&sid={2}&server_type={3}' \
+ '&did={4}&title={5}'.format(True, self.sgid, self.sid,
+ self.server_information['type'],
+ self.did, 'Query panel')
+
+ response = self.tester.post(
+ self.url + str(self.trans_id) + query_param,
+ data=json.dumps(self.test_data),
+ content_type='html/json'
+ )
+ return response
+
+ def runTest(self):
+ """ This function will update query tool connection."""
+
+ if self.is_positive_test:
+ response = self.panel()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+ else:
+ with patch(self.mock_data["function_name"],
+ return_value=eval(self.mock_data["return_value"])):
+ response = self.panel()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+
+ self.assertEqual(actual_response_code, expected_response_code)
+
+ def tearDown(self):
+ """This function disconnect database."""
+ database_utils.disconnect_database(self, self.sid,
+ self.did)
diff --git a/web/pgadmin/tools/datagrid/tests/test_data_grid_query_tool_close.py b/web/pgadmin/tools/datagrid/tests/test_data_grid_query_tool_close.py
new file mode 100644
index 0000000..7e4b56c
--- /dev/null
+++ b/web/pgadmin/tools/datagrid/tests/test_data_grid_query_tool_close.py
@@ -0,0 +1,78 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+
+import json
+import uuid
+import random
+
+from unittest.mock import patch
+from pgadmin.browser.server_groups.servers.databases.tests import utils as \
+ database_utils
+
+from pgadmin.utils.route import BaseTestGenerator
+from regression import parent_node_dict
+from regression.python_test_utils import test_utils as utils
+from regression.test_setup import config_data
+from . import utils as data_grid_utils
+from pgadmin.utils.exception import ExecuteError
+
+
+class DatagridQueryToolCloseTestCase(BaseTestGenerator):
+ """
+ This will close query-tool connection.
+ """
+
+ scenarios = utils.generate_scenarios(
+ 'data_grid_query_tool_close',
+ data_grid_utils.test_cases
+ )
+
+ def setUp(self):
+ self.database_info = parent_node_dict["database"][-1]
+ self.db_name = self.database_info["db_name"]
+ self.did = self.database_info["db_id"]
+ self.sid = parent_node_dict["server"][-1]["server_id"]
+ self.sgid = config_data['server_group']
+
+ db_con = database_utils.connect_database(self, utils.SERVER_GROUP,
+ self.sid, self.did)
+
+ if not db_con['data']["connected"]:
+ raise ExecuteError("Could not connect to database to add a table.")
+
+ self.trans_id = str(random.randint(1, 9999999))
+ qt_init = data_grid_utils._init_query_tool(self, self.trans_id,
+ self.sgid, self.sid,
+ self.did)
+
+ if not qt_init['success']:
+ raise ExecuteError("Could not initialize querty tool.")
+
+ def close_connection(self):
+ response = self.tester.delete(
+ self.url + str(self.trans_id),
+ content_type='html/json'
+ )
+ return response
+
+ def runTest(self):
+ """ This function will update query tool connection."""
+
+ if self.is_positive_test:
+ response = self.close_connection()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+
+ self.assertEqual(actual_response_code, expected_response_code)
+
+ def tearDown(self):
+ """This function disconnect database."""
+ database_utils.disconnect_database(self, self.sid,
+ self.did)
diff --git a/web/pgadmin/tools/datagrid/tests/test_data_grid_update_connection.py b/web/pgadmin/tools/datagrid/tests/test_data_grid_update_connection.py
new file mode 100644
index 0000000..a15702c
--- /dev/null
+++ b/web/pgadmin/tools/datagrid/tests/test_data_grid_update_connection.py
@@ -0,0 +1,121 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+
+import json
+import uuid
+import random
+
+from unittest.mock import patch
+from pgadmin.browser.server_groups.servers.databases.tests import utils as \
+ database_utils
+
+from pgadmin.utils.route import BaseTestGenerator
+from regression import parent_node_dict
+from regression.python_test_utils import test_utils as utils
+from regression.test_setup import config_data
+from pgadmin.browser.server_groups.servers.roles.tests import \
+ utils as roles_utils
+from . import utils as data_grid_utils
+from pgadmin.utils.exception import ExecuteError
+
+
+class DatagridUpdateConnectionTestCase(BaseTestGenerator):
+ """
+ This will update query-tool connection.
+ """
+
+ scenarios = utils.generate_scenarios(
+ 'data_grid_update_connection',
+ data_grid_utils.test_cases
+ )
+
+ def setUp(self):
+ self.database_info = parent_node_dict["database"][-1]
+ self.db_name = self.database_info["db_name"]
+ self.did = self.database_info["db_id"]
+ self.sid = parent_node_dict["server"][-1]["server_id"]
+ self.sgid = config_data['server_group']
+
+ db_con = database_utils.connect_database(self, utils.SERVER_GROUP,
+ self.sid, self.did)
+
+ self.trans_id = str(random.randint(1, 9999999))
+ self.roles = None
+
+ if self.is_create_role:
+ data = roles_utils.get_role_data(self.server['db_password'])
+ self.role_name = data['rolname']
+ self.role_password = data['rolpassword']
+ roles_utils.create_role_with_password(
+ self.server, self.role_name, self.role_password)
+
+ if not self.is_positive_test or self.is_create_role:
+ qt_init = data_grid_utils._init_query_tool(self, self.trans_id,
+ self.sgid, self.sid,
+ self.did)
+
+ if not qt_init['success']:
+ raise ExecuteError("Could not initialize querty tool.")
+
+ self.test_data = {
+ "database": self.did,
+ "server": self.sid,
+ }
+
+ if self.server_information['type'] == 'ppas':
+ self.test_data['password'] = 'enterprisedb'
+ self.test_data['user'] = 'enterprisedb'
+ else:
+ self.test_data['password'] = 'postgres'
+ self.test_data['user'] = 'postgres'
+
+ if not db_con['data']["connected"]:
+ raise ExecuteError("Could not connect to database to add a table.")
+
+ def update_connection(self, user_data=None):
+ if user_data:
+ response = self.tester.post(
+ self.url + str(self.trans_id) + '/' + str(self.sgid) +
+ '/' + str(self.sid) + '/' + str(self.did),
+ data=json.dumps(user_data),
+ content_type='html/json'
+ )
+ else:
+ response = self.tester.post(
+ self.url + str(self.trans_id) + '/' + str(self.sgid) + '/' +
+ str(self.sid) + '/' + str(self.did),
+ data=json.dumps(self.test_data),
+ content_type='html/json'
+ )
+ return response
+
+ def runTest(self):
+ """ This function will update query tool connection."""
+
+ if self.is_positive_test:
+ user_data = dict()
+ if self.is_create_role:
+ user_data['user'] = self.role_name
+ user_data['password'] = self.role_password
+ user_data['role'] = None
+ response = self.update_connection(user_data=user_data)
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+ else:
+ response = self.update_connection()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+
+ self.assertEqual(actual_response_code, expected_response_code)
+
+ def tearDown(self):
+ """This function disconnect database."""
+ database_utils.disconnect_database(self, self.sid,
+ self.did)
diff --git a/web/pgadmin/tools/datagrid/tests/test_data_grid_validate_filter.py b/web/pgadmin/tools/datagrid/tests/test_data_grid_validate_filter.py
new file mode 100644
index 0000000..b467b6d
--- /dev/null
+++ b/web/pgadmin/tools/datagrid/tests/test_data_grid_validate_filter.py
@@ -0,0 +1,92 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+
+import json
+import uuid
+import random
+
+from unittest.mock import patch
+from pgadmin.browser.server_groups.servers.databases.tests import utils as \
+ database_utils
+
+from pgadmin.utils.route import BaseTestGenerator
+from regression import parent_node_dict
+from regression.python_test_utils import test_utils as utils
+from regression.test_setup import config_data
+from pgadmin.browser.server_groups.servers.databases.schemas.tests import \
+ utils as schema_utils
+from pgadmin.browser.server_groups.servers.databases.schemas.tables.tests \
+ import utils as tables_utils
+from . import utils as data_grid_utils
+from pgadmin.utils.exception import ExecuteError
+
+
+class DatagridValidateFilterTestCase(BaseTestGenerator):
+ """
+ This will validate filter connection.
+ """
+
+ scenarios = utils.generate_scenarios(
+ 'data_grid_validate_filter',
+ data_grid_utils.test_cases
+ )
+
+ def setUp(self):
+ self.database_info = parent_node_dict["database"][-1]
+ self.db_name = self.database_info["db_name"]
+ self.did = self.database_info["db_id"]
+ self.sid = parent_node_dict["server"][-1]["server_id"]
+ self.sgid = config_data['server_group']
+
+ db_con = database_utils.connect_database(self, utils.SERVER_GROUP,
+ self.sid, self.did)
+ if not db_con['data']["connected"]:
+ raise ExecuteError("Could not connect to database to add a table.")
+ self.schema_id = parent_node_dict['schema'][-1]["schema_id"]
+ self.schema_name = parent_node_dict['schema'][-1]["schema_name"]
+ schema_response = schema_utils.verify_schemas(self.server,
+ self.db_name,
+ self.schema_name)
+ if not schema_response:
+ raise ExecuteError("Could not find the schema to add a table.")
+ self.table_name = "table_for_wizard%s" % (str(uuid.uuid4())[1:8])
+ self.table_id = tables_utils.create_table(self.server, self.db_name,
+ self.schema_name,
+ self.table_name)
+
+ def validate_filter(self):
+ response = self.tester.post(
+ self.url + str(self.sid) + '/' + str(self.did) + '/' +
+ str(self.table_id),
+ data=json.dumps(self.test_data),
+ content_type='html/json'
+ )
+ return response
+
+ def runTest(self):
+ """ This function will update query tool connection."""
+
+ if self.is_positive_test:
+ response = self.validate_filter()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+ else:
+ with patch(self.mock_data["function_name"],
+ return_value=eval(self.mock_data["return_value"])):
+ response = self.validate_filter()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+
+ self.assertEqual(actual_response_code, expected_response_code)
+
+ def tearDown(self):
+ """This function disconnect database."""
+ database_utils.disconnect_database(self, self.sid,
+ self.did)
diff --git a/web/pgadmin/tools/datagrid/tests/test_initialize_data_grid.py b/web/pgadmin/tools/datagrid/tests/test_initialize_data_grid.py
new file mode 100644
index 0000000..8d07139
--- /dev/null
+++ b/web/pgadmin/tools/datagrid/tests/test_initialize_data_grid.py
@@ -0,0 +1,109 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+
+import json
+import uuid
+import random
+
+from unittest.mock import patch
+from pgadmin.browser.server_groups.servers.databases.tests import utils as \
+ database_utils
+
+from pgadmin.utils.route import BaseTestGenerator
+from regression import parent_node_dict
+from regression.python_test_utils import test_utils as utils
+from regression.test_setup import config_data
+from pgadmin.browser.server_groups.servers.databases.schemas.tests import \
+ utils as schema_utils
+from pgadmin.browser.server_groups.servers.databases.schemas.tables.tests \
+ import utils as tables_utils
+from . import utils as data_grid_utils
+from pgadmin.utils.exception import ExecuteError
+
+
+class DatagridInitializeTestCase(BaseTestGenerator):
+ """
+ This will Initialize datagrid
+ """
+
+ scenarios = utils.generate_scenarios(
+ 'data_grid_initialize',
+ data_grid_utils.test_cases
+ )
+
+ def setUp(self):
+ self.database_info = parent_node_dict["database"][-1]
+ self.db_name = self.database_info["db_name"]
+ self.did = self.database_info["db_id"]
+ self.sid = parent_node_dict["server"][-1]["server_id"]
+ self.sgid = config_data['server_group']
+
+ db_con = database_utils.connect_database(self, utils.SERVER_GROUP,
+ self.sid, self.did)
+ if not db_con['data']["connected"]:
+ raise ExecuteError("Could not connect to database to add a table.")
+
+ self.schema_id = parent_node_dict['schema'][-1]["schema_id"]
+ self.schema_name = parent_node_dict['schema'][-1]["schema_name"]
+ schema_response = schema_utils.verify_schemas(self.server,
+ self.db_name,
+ self.schema_name)
+ if not schema_response:
+ raise ExecuteError("Could not find the schema to add a table.")
+ self.table_name = "table_for_wizard%s" % (str(uuid.uuid4())[1:8])
+ self.table_id = tables_utils.create_table(self.server, self.db_name,
+ self.schema_name,
+ self.table_name)
+ self.trans_id = str(random.randint(1, 9999999))
+ qt_init = data_grid_utils._init_query_tool(self, self.trans_id,
+ self.sgid, self.sid,
+ self.did)
+
+ if not qt_init['success']:
+ raise ExecuteError("Could not initialize query tool.")
+
+ def initialize_datagrid(self):
+ if self.test_data:
+ response = self.tester.post(
+ self.url + str(self.trans_id) + '/4/table/' +
+ str(self.sgid) + '/' + str(self.sid) + '/' +
+ str(self.did) + '/' + str(self.table_id),
+ data=json.dumps(self.test_data),
+ content_type='html/json'
+ )
+ else:
+ response = self.tester.post(
+ self.url + str(self.trans_id) + '/4/table/' +
+ str(self.sgid) + '/' + str(self.sid) + '/' +
+ str(self.did) + '/' + str(self.table_id),
+ content_type='html/json'
+ )
+ return response
+
+ def runTest(self):
+ """ This function will update query tool connection."""
+
+ if self.is_positive_test:
+ response = self.initialize_datagrid()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+ else:
+ with patch(self.mock_data["function_name"],
+ return_value=eval(self.mock_data["return_value"])):
+ response = self.initialize_datagrid()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+
+ self.assertEqual(actual_response_code, expected_response_code)
+
+ def tearDown(self):
+ """This function disconnect database."""
+ database_utils.disconnect_database(self, self.sid,
+ self.did)
diff --git a/web/pgadmin/tools/datagrid/tests/utils.py b/web/pgadmin/tools/datagrid/tests/utils.py
new file mode 100644
index 0000000..c3d4bb5
--- /dev/null
+++ b/web/pgadmin/tools/datagrid/tests/utils.py
@@ -0,0 +1,33 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+import os
+import json
+
+file_name = os.path.basename(__file__)
+CURRENT_PATH = os.path.dirname(os.path.realpath(__file__))
+with open(CURRENT_PATH + "/datagrid_test_data.json") as data_file:
+ test_cases = json.load(data_file)
+
+
+def _init_query_tool(self, trans_id, server_group, server_id, db_id):
+ QUERY_TOOL_INIT_URL = '/datagrid/initialize/query_tool'
+
+ qt_init = self.tester.post(
+ '{0}/{1}/{2}/{3}/{4}'.format(
+ QUERY_TOOL_INIT_URL,
+ trans_id,
+ server_group,
+ server_id,
+ db_id
+ ),
+ follow_redirects=True
+ )
+ assert qt_init.status_code == 200
+ qt_init = json.loads(qt_init.data.decode('utf-8'))
+ return qt_init
diff --git a/web/pgadmin/tools/sqleditor/__init__.py b/web/pgadmin/tools/sqleditor/__init__.py
index 0f56b60..9da8842 100644
--- a/web/pgadmin/tools/sqleditor/__init__.py
+++ b/web/pgadmin/tools/sqleditor/__init__.py
@@ -10,17 +10,15 @@
"""A blueprint module implementing the sqleditor frame."""
import os
import pickle
-import sys
import re
+from urllib.parse import unquote
import simplejson as json
-from flask import Response, url_for, render_template, session, request, \
- current_app
+from config import PG_DEFAULT_DRIVER, ON_DEMAND_RECORD_COUNT
+from flask import Response, url_for, render_template, session, current_app
+from flask import request, jsonify
from flask_babelex import gettext
from flask_security import login_required, current_user
-from urllib.parse import unquote
-
-from config import PG_DEFAULT_DRIVER, ON_DEMAND_RECORD_COUNT
from pgadmin.misc.file_manager import Filemanager
from pgadmin.tools.sqleditor.command import QueryToolCommand
from pgadmin.tools.sqleditor.utils.constant_definition import ASYNC_OK, \
@@ -32,11 +30,11 @@ from pgadmin.tools.sqleditor.utils.update_session_grid_transaction import \
from pgadmin.utils import PgAdminModule
from pgadmin.utils import get_storage_directory
from pgadmin.utils.ajax import make_json_response, bad_request, \
- success_return, internal_server_error, make_response as ajax_response
+ success_return, internal_server_error
from pgadmin.utils.driver import get_driver
-from pgadmin.utils.menu import MenuItem
-from pgadmin.utils.exception import ConnectionLost, SSHTunnelConnectionLost,\
+from pgadmin.utils.exception import ConnectionLost, SSHTunnelConnectionLost, \
CryptKeyMissing
+from pgadmin.utils.menu import MenuItem
from pgadmin.utils.sqlautocomplete.autocomplete import SQLAutoComplete
from pgadmin.tools.sqleditor.utils.query_tool_preferences import \
register_query_tool_preferences
@@ -44,13 +42,16 @@ from pgadmin.tools.sqleditor.utils.query_tool_fs_utils import \
read_file_generator
from pgadmin.tools.sqleditor.utils.filter_dialog import FilterDialog
from pgadmin.tools.sqleditor.utils.query_history import QueryHistory
-from pgadmin.utils.constants import MIMETYPE_APP_JS, SERVER_CONNECTION_CLOSED,\
- ERROR_MSG_TRANS_ID_NOT_FOUND
from pgadmin.tools.sqleditor.utils.macros import get_macros,\
get_user_macros, set_macros
+from pgadmin.utils.constants import MIMETYPE_APP_JS, \
+ SERVER_CONNECTION_CLOSED, ERROR_MSG_TRANS_ID_NOT_FOUND, ERROR_FETCHING_DATA
+from pgadmin.model import Server
+from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry
MODULE_NAME = 'sqleditor'
TRANSACTION_STATUS_CHECK_FAILED = gettext("Transaction status check failed.")
+_NODES_SQL = 'nodes.sql'
class SqlEditorModule(PgAdminModule):
@@ -114,7 +115,13 @@ class SqlEditorModule(PgAdminModule):
'sqleditor.clear_query_history',
'sqleditor.get_macro',
'sqleditor.get_macros',
- 'sqleditor.set_macros'
+ 'sqleditor.set_macros',
+ 'sqleditor.get_new_connection_data',
+ 'sqleditor.get_new_connection_database',
+ 'sqleditor.get_new_connection_user',
+ 'sqleditor.get_new_connection_role',
+ 'sqleditor.connect_server',
+ 'sqleditor.connect_server_with_user',
]
def register_preferences(self):
@@ -230,7 +237,7 @@ def start_view_data(trans_id):
)
if status and conn is not None and \
- trans_obj is not None and session_obj is not None:
+ trans_obj is not None and session_obj is not None:
# set fetched row count to 0 as we are executing query again.
trans_obj.update_fetched_row_cnt(0)
@@ -376,7 +383,7 @@ def poll(trans_id):
if isinstance(trans_obj, QueryToolCommand):
trans_status = conn.transaction_status()
if trans_status == TX_STATUS_INERROR and \
- trans_obj.auto_rollback:
+ trans_obj.auto_rollback:
conn.execute_void("ROLLBACK;")
st, result = conn.async_fetchmany_2darray(ON_DEMAND_RECORD_COUNT)
@@ -686,13 +693,12 @@ def save(trans_id):
status=404)
if status and conn is not None and \
- trans_obj is not None and session_obj is not None:
+ trans_obj is not None and session_obj is not None:
# If there is no primary key found then return from the function.
if ('primary_keys' not in session_obj or
- len(session_obj['primary_keys']) <= 0 or
- len(changed_data) <= 0) and \
- 'has_oids' not in session_obj:
+ len(session_obj['primary_keys']) <= 0 or
+ len(changed_data) <= 0) and 'has_oids' not in session_obj:
return make_json_response(
data={
'status': False,
@@ -759,7 +765,7 @@ def append_filter_inclusive(trans_id):
status=404)
if status and conn is not None and \
- trans_obj is not None and session_obj is not None:
+ trans_obj is not None and session_obj is not None:
res = None
filter_sql = ''
@@ -813,7 +819,7 @@ def append_filter_exclusive(trans_id):
info='DATAGRID_TRANSACTION_REQUIRED',
status=404)
if status and conn is not None and \
- trans_obj is not None and session_obj is not None:
+ trans_obj is not None and session_obj is not None:
res = None
filter_sql = ''
@@ -866,7 +872,7 @@ def remove_filter(trans_id):
status=404)
if status and conn is not None and \
- trans_obj is not None and session_obj is not None:
+ trans_obj is not None and session_obj is not None:
res = None
@@ -910,7 +916,7 @@ def set_limit(trans_id):
status=404)
if status and conn is not None and \
- trans_obj is not None and session_obj is not None:
+ trans_obj is not None and session_obj is not None:
res = None
@@ -1052,7 +1058,7 @@ def get_object_name(trans_id):
status=404)
if status and conn is not None and \
- trans_obj is not None and session_obj is not None:
+ trans_obj is not None and session_obj is not None:
res = trans_obj.object_name
else:
status = False
@@ -1088,7 +1094,7 @@ def set_auto_commit(trans_id):
status=404)
if status and conn is not None and \
- trans_obj is not None and session_obj is not None:
+ trans_obj is not None and session_obj is not None:
res = None
@@ -1133,7 +1139,7 @@ def set_auto_rollback(trans_id):
status=404)
if status and conn is not None and \
- trans_obj is not None and session_obj is not None:
+ trans_obj is not None and session_obj is not None:
res = None
@@ -1185,7 +1191,7 @@ def auto_complete(trans_id):
status=404)
if status and conn is not None and \
- trans_obj is not None and session_obj is not None:
+ trans_obj is not None and session_obj is not None:
# Create object of SQLAutoComplete class and pass connection object
auto_complete_obj = SQLAutoComplete(
@@ -1472,6 +1478,282 @@ def get_filter_data(trans_id):
return FilterDialog.get(status, error_msg, conn, trans_obj, session_ob)
[email protected](
+ '/new_connection_dialog/<int:sgid>/<int:sid>',
+ methods=["GET"], endpoint='get_new_connection_data'
+)
+@login_required
+def get_new_connection_data(sgid, sid=None):
+ """
+ This method is used to get required data for get new connection.
+ :extract_sql_from_network_parameters,
+ """
+ try:
+ # if sid and not did:
+ servers = Server.query.all()
+ server_list = [
+ {'name': server.serialize['name'], "id": server.serialize['id']}
+ for server in servers]
+
+ msg = "Success"
+ return make_json_response(
+ data={
+ 'status': True,
+ 'msg': msg,
+ 'result': {
+ 'server_list': server_list
+ }
+ }
+ )
+
+ except Exception:
+ return make_json_response(
+ data={
+ 'status': False,
+ 'msg': ERROR_FETCHING_DATA,
+ 'result': {
+ 'server_list': []
+ }
+ }
+ )
+
+
[email protected](
+ '/new_connection_database/<int:sgid>/<int:sid>',
+ methods=["GET"], endpoint='get_new_connection_database'
+)
+@login_required
+def get_new_connection_database(sgid, sid=None):
+ """
+ This method is used to get required data for get new connection.
+ :extract_sql_from_network_parameters,
+ """
+ try:
+ database_list = []
+ from pgadmin.utils.driver import get_driver
+ manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)
+ conn = manager.connection()
+ if conn.connected():
+ is_connected = True
+ else:
+ is_connected = False
+ if is_connected:
+ if sid:
+ template_path = 'databases/sql/#{0}#'.format(manager.version)
+ last_system_oid = 0
+ server_node_res = manager
+
+ db_disp_res = None
+ params = None
+ if server_node_res and server_node_res.db_res:
+ db_disp_res = ", ".join(
+ ['%s'] * len(server_node_res.db_res.split(','))
+ )
+ params = tuple(server_node_res.db_res.split(','))
+ sql = render_template(
+ "/".join([template_path, _NODES_SQL]),
+ last_system_oid=last_system_oid,
+ db_restrictions=db_disp_res
+ )
+ status, databases = conn.execute_dict(sql, params)
+ database_list = [
+ {'label': database['name'], 'value': database['did']} for
+ database in databases['rows']]
+ else:
+ status = False
+
+ msg = "Success"
+ return make_json_response(
+ data={
+ 'status': status,
+ 'msg': msg,
+ 'result': {
+ 'data': database_list,
+ }
+ }
+ )
+ else:
+ return make_json_response(
+ data={
+ 'status': False,
+ 'msg': SERVER_CONNECTION_CLOSED,
+ 'result': {
+ 'database_list': [],
+ }
+ }
+ )
+ except Exception:
+ return make_json_response(
+ data={
+ 'status': False,
+ 'msg': ERROR_FETCHING_DATA,
+ 'result': {
+ 'database_list': [],
+ }
+ }
+ )
+
+
[email protected](
+ '/new_connection_user/<int:sgid>/<int:sid>',
+ methods=["GET"], endpoint='get_new_connection_user'
+)
+@login_required
+def get_new_connection_user(sgid, sid=None):
+ """
+ This method is used to get required data for get new connection.
+ :extract_sql_from_network_parameters,
+ """
+ try:
+ from pgadmin.utils.driver import get_driver
+ manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)
+ conn = manager.connection()
+ user_list = []
+ if conn.connected():
+ is_connected = True
+ else:
+ is_connected = False
+ if is_connected:
+ if sid:
+ sql_path = 'roles/sql/#{0}#'.format(manager.version)
+ status, users = conn.execute_2darray(
+ render_template(sql_path + _NODES_SQL)
+ )
+ user_list = [
+ {'value': user['rolname'], 'label': user['rolname']} for
+ user in users['rows'] if user['rolcanlogin']]
+ else:
+ status = False
+
+ msg = "Success"
+ return make_json_response(
+ data={
+ 'status': status,
+ 'msg': msg,
+ 'result': {
+ 'data': user_list,
+ }
+ }
+ )
+ else:
+ return make_json_response(
+ data={
+ 'status': False,
+ 'msg': SERVER_CONNECTION_CLOSED,
+ 'result': {
+ 'user_list': [],
+ }
+ }
+ )
+ except Exception:
+ return make_json_response(
+ data={
+ 'status': False,
+ 'msg': 'Unable to fetch data.',
+ 'result': {
+ 'user_list': [],
+ }
+ }
+ )
+
+
[email protected](
+ '/new_connection_role/<int:sgid>/<int:sid>',
+ methods=["GET"], endpoint='get_new_connection_role'
+)
+@login_required
+def get_new_connection_role(sgid, sid=None):
+ """
+ This method is used to get required data for get new connection.
+ :extract_sql_from_network_parameters,
+ """
+ try:
+ from pgadmin.utils.driver import get_driver
+ manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)
+ conn = manager.connection()
+ role_list = []
+ if conn.connected():
+ is_connected = True
+ else:
+ is_connected = False
+ if is_connected:
+ if sid:
+ sql_path = 'roles/sql/#{0}#'.format(manager.version)
+ status, roles = conn.execute_2darray(
+ render_template(sql_path + _NODES_SQL)
+ )
+ role_list = [
+ {'value': role['rolname'], 'label': role['rolname']} for
+ role in roles['rows']]
+ else:
+ status = False
+
+ msg = "Success"
+ return make_json_response(
+ data={
+ 'status': status,
+ 'msg': msg,
+ 'result': {
+ 'data': role_list,
+ }
+ }
+ )
+ else:
+ return make_json_response(
+ data={
+ 'status': False,
+ 'msg': SERVER_CONNECTION_CLOSED,
+ 'result': {
+ 'user_list': [],
+ }
+ }
+ )
+ except Exception:
+ return make_json_response(
+ data={
+ 'status': False,
+ 'msg': 'Unable to fetch data.',
+ 'result': {
+ 'user_list': [],
+ }
+ }
+ )
+
+
[email protected](
+ '/connect_server/<int:sid>/<usr>',
+ methods=["POST"],
+ endpoint="connect_server_with_user"
+)
[email protected](
+ '/connect_server/<int:sid>',
+ methods=["POST"],
+ endpoint="connect_server"
+)
+@login_required
+def connect_server(sid, usr=None):
+ # Check if server is already connected then no need to reconnect again.
+ server = Server.query.filter_by(id=sid).first()
+ driver = get_driver(PG_DEFAULT_DRIVER)
+ manager = driver.connection_manager(sid)
+ conn = manager.connection()
+ user = None
+
+ if usr and manager.user != usr:
+ user = usr
+ else:
+ user = manager.user
+ if conn.connected():
+ return make_json_response(
+ success=1,
+ info=gettext("Server connected."),
+ data={}
+ )
+
+ view = SchemaDiffRegistry.get_node_view('server')
+ return view.connect(server.servergroup_id, sid, user_name=user)
+
+
@blueprint.route(
'/filter_dialog/<int:trans_id>',
methods=["PUT"], endpoint='set_filter_data'
diff --git a/web/pgadmin/tools/sqleditor/static/css/sqleditor.css b/web/pgadmin/tools/sqleditor/static/css/sqleditor.css
index c281d53..20590d3 100644
--- a/web/pgadmin/tools/sqleditor/static/css/sqleditor.css
+++ b/web/pgadmin/tools/sqleditor/static/css/sqleditor.css
@@ -315,10 +315,6 @@ input.editor-checkbox:focus {
padding: 10px 0px;
}
-.editor-title {
- width:100%;
-}
-
.connection-status-hide {
display: none !important;
}
@@ -396,7 +392,6 @@ input.editor-checkbox:focus {
overflow-y: hidden;
}
-
/* Macros */
.macro-tab {
@@ -424,3 +419,7 @@ input.editor-checkbox:focus {
.macro_dialog .pg-prop-status-bar {
z-index: 1;
}
+
+.new-connection-dialog-style {
+ width: 100% !important;
+}
diff --git a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
index ed3bd59..4ef4b8f 100644
--- a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
+++ b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
@@ -14,6 +14,7 @@ define('tools.querytool', [
'jqueryui.position', 'underscore', 'pgadmin.alertifyjs',
'sources/pgadmin', 'backbone', 'bundled_codemirror', 'sources/utils',
'pgadmin.misc.explain',
+ 'pgadmin.user_management.current_user',
'sources/selection/grid_selector',
'sources/selection/active_cell_capture',
'sources/selection/clipboard',
@@ -26,6 +27,7 @@ define('tools.querytool', [
'sources/sqleditor/execute_query',
'sources/sqleditor/query_tool_http_error_handler',
'sources/sqleditor/filter_dialog',
+ 'sources/sqleditor/new_connection_dialog',
'sources/sqleditor/geometry_viewer',
'sources/sqleditor/history/history_collection.js',
'sources/sqleditor/history/query_history',
@@ -53,8 +55,8 @@ define('tools.querytool', [
'pgadmin.tools.user_management',
], function(
gettext, url_for, $, jqueryui, jqueryui_position, _, alertify, pgAdmin, Backbone, codemirror, pgadminUtils,
- pgExplain, GridSelector, ActiveCellCapture, clipboard, copyData, RangeSelectionHelper, handleQueryOutputKeyboardEvent,
- XCellSelectionModel, setStagedRows, SqlEditorUtils, ExecuteQuery, httpErrorHandler, FilterHandler,
+ pgExplain, current_user, GridSelector, ActiveCellCapture, clipboard, copyData, RangeSelectionHelper, handleQueryOutputKeyboardEvent,
+ XCellSelectionModel, setStagedRows, SqlEditorUtils, ExecuteQuery, httpErrorHandler, FilterHandler, newConnectionHandler,
GeometryViewer, historyColl, queryHist, querySources,
keyboardShortcuts, queryToolActions, queryToolNotifications, Datagrid,
modifyAnimation, calculateQueryRunTime, callRenderAfterPoll, queryToolPref, queryTxnStatus, csrfToken, panelTitleFunc,
@@ -98,6 +100,9 @@ define('tools.querytool', [
this.layout = opts.layout;
this.set_server_version(opts.server_ver);
this.trigger('pgadmin-sqleditor:view:initialised');
+ this.connection_list = [
+ {'server_group': null,'server': null, 'database': null, 'user': null, 'role': null, 'title': '<New Connection>'},
+ ];
},
// Bind all the events
@@ -163,6 +168,35 @@ define('tools.querytool', [
'click .btn-macro': 'on_execute_macro',
},
+ render_connection: function(data_list) {
+ if(this.handler.is_query_tool) {
+ var dropdownElement = document.getElementById('connections-list');
+ dropdownElement.innerHTML = '';
+ data_list.forEach((option, index) => {
+ $('#connections-list').append('<li class="connection-list-item" data-index='+ index +'><a class="dropdown-item" href="#" tabindex="0">'+ option.title +'</a></li>');
+
+ });
+ var self = this;
+ $('.connection-list-item').click(function() {
+ self.get_connection_data(this);
+ });
+ } else {
+ $('.conn-info-dd').hide();
+ $('.editor-title').css({pointerEvents: 'none'});
+ }
+ },
+
+ get_connection_data: function(event){
+ var index = $(event).attr('data-index');
+ var connection_details = this.connection_list[index];
+ if(connection_details.server_group) {
+ this.on_change_connection(connection_details);
+ } else {
+ this.on_new_connection();
+ }
+
+ },
+
reflectPreferences: function() {
let self = this,
browser = pgWindow.default.pgAdmin.Browser,
@@ -213,6 +247,7 @@ define('tools.querytool', [
set_editor_title: function(title) {
this.$el.find('.editor-title').text(title);
+ this.render_connection(this.connection_list);
},
// This function is used to render the template.
@@ -696,6 +731,8 @@ define('tools.querytool', [
pgBrowser.register_to_activity_listener(document, ()=>{
alertify.alert(gettext('Timeout'), gettext('Your session has timed out due to inactivity. Please close the window and login again.'));
});
+
+ self.render_connection(self.connection_list);
},
/* Regarding SlickGrid usage in render_grid function.
@@ -1607,6 +1644,17 @@ define('tools.querytool', [
);
},
+ on_new_connection: function() {
+ var self = this;
+
+ // Trigger the show_filter signal to the SqlEditorController class
+ self.handler.trigger(
+ 'pgadmin-sqleditor:button:show_new_connection',
+ self,
+ self.handler
+ );
+ },
+
// Callback function for include filter button click.
on_include_filter: function(ev) {
var self = this;
@@ -2070,6 +2118,83 @@ define('tools.querytool', [
queryToolActions.executeMacro(this.handler, macroId);
},
+ on_change_connection: function(connection_details, ref) {
+ let title = this.$el.find('.editor-title').html();
+ if(connection_details['title'] != title) {
+ var self = this;
+ $.ajax({
+ async: false,
+ url: url_for('datagrid.update_query_tool_connection', {
+ 'trans_id': self.transId,
+ 'sgid': connection_details['server_group'],
+ 'sid': connection_details['server'],
+ 'did': connection_details['database'],
+ }),
+ method: 'POST',
+ contentType: 'application/json',
+ data: JSON.stringify(connection_details),
+ })
+ .done(function(res) {
+ if(res.success) {
+ self.transId = res.data.tran_id;
+ self.handler.transId = res.data.tran_id;
+ self.handler.url_params = {
+ 'did': connection_details['database'],
+ 'is_query_tool': self.handler.url_params.is_query_tool,
+ 'server_type': self.handler.url_params.server_type,
+ 'sgid': connection_details['server_group'],
+ 'sid': connection_details['server'],
+ 'title': connection_details['title'],
+ };
+ self.set_editor_title(self.handler.url_params.title);
+ self.handler.setTitle(self.handler.url_params.title);
+ alertify.success('connected successfully');
+ if(ref){
+ let connection_data = {
+ 'server_group': self.handler.url_params.sgid,
+ 'server': connection_details['server'],
+ 'database': connection_details['database'],
+ 'user': connection_details['user'],
+ 'title': connection_details['title'],
+ 'role': connection_details['role'],
+ 'password': connection_details['password'],
+ 'is_allow_new_connection': true,
+ };
+ self.connection_list.unshift(connection_data);
+ self.render_connection(self.connection_list);
+ ref.close();
+ }
+ }
+ return true;
+ })
+ .fail(function(xhr) {
+ if(xhr.status == 428) {
+ alertify.connectServer('Connect to server', xhr.responseJSON.result, connection_details['server'], false);
+ } else {
+ alertify.error(xhr.responseJSON['errormsg']);
+ }
+ /*let url = url_for('sqleditor.connect_server_with_user', {
+ 'sid': newConnCollectionModel['server'],
+ 'usr': newConnCollectionModel['user']
+ });
+ $.ajax({
+ async: false,
+ url: url,
+ headers: {
+ 'Cache-Control' : 'no-cache',
+ },
+ }).done(function () {
+ Backform.Select2Control.prototype.onChange.apply(self, arguments);
+ response.server_list.forEach(function(obj){
+ if(obj.id==self.model.changed.server) {
+ response.server_name = obj.name;
+ }
+ });
+ }).fail(function(xhr){});*/
+
+ });
+ }
+ },
});
@@ -2393,6 +2518,17 @@ define('tools.querytool', [
$('#btn-conn-status i').removeClass('obtaining-conn');
self.gridView.set_editor_title(_.unescape(url_params.title));
+ let connection_data = {
+ 'server_group': self.gridView.handler.url_params.sgid,
+ 'server': self.gridView.handler.url_params.sid,
+ 'database': self.gridView.handler.url_params.did,
+ 'user': null,
+ 'role': null,
+ 'title': _.unescape(url_params.title),
+ 'is_allow_new_connection': false,
+ };
+ self.gridView.connection_list.unshift(connection_data);
+ self.gridView.render_connection(self.gridView.connection_list);
};
pgBrowser.Events.on('pgadmin:query_tool:connected:' + transId, afterConn);
@@ -2487,6 +2623,7 @@ define('tools.querytool', [
self.on('pgadmin-sqleditor:button:save_file', self._save_file, self);
self.on('pgadmin-sqleditor:button:deleterow', self._delete, self);
self.on('pgadmin-sqleditor:button:show_filter', self._show_filter, self);
+ self.on('pgadmin-sqleditor:button:show_new_connection', self._show_new_connection, self);
self.on('pgadmin-sqleditor:button:include_filter', self._include_filter, self);
self.on('pgadmin-sqleditor:button:exclude_filter', self._exclude_filter, self);
self.on('pgadmin-sqleditor:button:remove_filter', self._remove_filter, self);
@@ -3696,7 +3833,6 @@ define('tools.querytool', [
}
};
},
-
// This function will show the filter in the text area.
_show_filter: function() {
let self = this,
@@ -3711,7 +3847,19 @@ define('tools.querytool', [
}
FilterHandler.dialog(self, reconnect);
},
+ // This function will show the new connection.
+ _show_new_connection: function() {
+ let self = this,
+ reconnect = false;
+ /* When server is disconnected and connected, connection is lost,
+ * To reconnect pass true
+ */
+ if (arguments.length > 0 && arguments[arguments.length - 1] == 'connect') {
+ reconnect = true;
+ }
+ newConnectionHandler.dialog(self, reconnect);
+ },
// This function will include the filter by selection.
_include_filter: function() {
var self = this,
diff --git a/web/pgadmin/tools/sqleditor/static/scss/_sqleditor.scss b/web/pgadmin/tools/sqleditor/static/scss/_sqleditor.scss
index fd1e5d3..53f2449 100644
--- a/web/pgadmin/tools/sqleditor/static/scss/_sqleditor.scss
+++ b/web/pgadmin/tools/sqleditor/static/scss/_sqleditor.scss
@@ -30,6 +30,19 @@
color: $sql-title-fg;
}
+.connection-info {
+ background: $sql-title-bg;
+ color: $sql-title-fg;
+ width:100%;
+ display: inherit;
+}
+
+.conn-info-dd {
+ padding-top: 0.3em;
+ padding-left: 0.2em;
+ cursor: pointer;
+}
+
#editor-panel {
z-index: 0;
diff --git a/web/pgadmin/tools/sqleditor/tests/test_new_connection_database.py b/web/pgadmin/tools/sqleditor/tests/test_new_connection_database.py
new file mode 100644
index 0000000..e799095
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/tests/test_new_connection_database.py
@@ -0,0 +1,100 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+import json
+from pgadmin.utils.route import BaseTestGenerator
+from regression import parent_node_dict
+from regression.test_setup import config_data
+from regression.python_test_utils import test_utils as utils
+
+
+class TestNewConnectionDatabase(BaseTestGenerator):
+ """ This class will test new connection database. """
+ API_URL = "/sqleditor/new_connection_database/"
+ scenarios = [
+ ('New connection dialog',
+ dict(
+ url=API_URL,
+ is_positive_test=True,
+ mocking_required=False,
+ is_server_conn_required=False,
+ test_data={},
+ mock_data={},
+ expected_data={
+ "status_code": 200
+ }
+ )),
+ ('New connection dialog connect server',
+ dict(
+ url=API_URL,
+ is_positive_test=True,
+ mocking_required=False,
+ is_server_conn_required=True,
+ test_data={},
+ mock_data={},
+ expected_data={
+ "status_code": 200
+ }
+ )),
+ ('New connection dialog negative',
+ dict(
+ url=API_URL,
+ is_positive_test=False,
+ mocking_required=False,
+ is_server_conn_required=True,
+ test_data={},
+ mock_data={},
+ expected_data={
+ "status_code": 200
+ }
+ )),
+ ]
+
+ def setUp(self):
+ self.content_type = 'html/json'
+ self.sid = parent_node_dict["server"][-1]["server_id"]
+ self.sgid = config_data['server_group']
+
+ def get_database(self):
+ response = self.tester.get(
+ self.url + str(self.sgid) + '/' + str(self.sid),
+ content_type=self.content_type
+ )
+
+ return response
+
+ def runTest(self):
+ if self.is_positive_test:
+ if self.is_server_conn_required:
+ self.server['password'] = self.server['db_password']
+ self.tester.post(
+ '/browser/server/connect/{0}/{1}'.format(
+ utils.SERVER_GROUP,
+ self.sid),
+ data=json.dumps(self.server),
+ content_type=self.content_type
+ )
+ response = self.get_database()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+ else:
+ if self.is_server_conn_required:
+ self.server['password'] = self.server['db_password']
+ self.tester.post(
+ '/browser/server/connect/{0}/{1}'.format(
+ utils.SERVER_GROUP,
+ self.sid),
+ data=json.dumps(self.server),
+ content_type=self.content_type
+ )
+ self.sid = 0
+ response = self.get_database()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+ self.assertEqual(actual_response_code, expected_response_code)
diff --git a/web/pgadmin/tools/sqleditor/tests/test_new_connection_dialog.py b/web/pgadmin/tools/sqleditor/tests/test_new_connection_dialog.py
new file mode 100644
index 0000000..75a47ef
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/tests/test_new_connection_dialog.py
@@ -0,0 +1,50 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+import json
+from pgadmin.utils.route import BaseTestGenerator
+from regression import parent_node_dict
+from regression.test_setup import config_data
+from regression.python_test_utils import test_utils as utils
+
+
+class TestNewConnectionDialog(BaseTestGenerator):
+ """ This class will test new connection dialog. """
+ scenarios = [
+ ('New connection dialog',
+ dict(
+ url="/sqleditor/new_connection_dialog/",
+ is_positive_test=True,
+ mocking_required=False,
+ is_connect_server=False,
+ test_data={},
+ mock_data={},
+ expected_data={
+ "status_code": 200
+ }
+ )),
+ ]
+
+ def setUp(self):
+ self.sid = parent_node_dict["server"][-1]["server_id"]
+ self.sgid = config_data['server_group']
+
+ def new_connection(self):
+ response = self.tester.get(
+ self.url + str(self.sgid) + '/' + str(self.sgid),
+ content_type='html/json'
+ )
+
+ return response
+
+ def runTest(self):
+ if self.is_positive_test:
+ response = self.new_connection()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+ self.assertEqual(actual_response_code, expected_response_code)
diff --git a/web/pgadmin/tools/sqleditor/tests/test_new_connection_user.py b/web/pgadmin/tools/sqleditor/tests/test_new_connection_user.py
new file mode 100644
index 0000000..7c69bbd
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/tests/test_new_connection_user.py
@@ -0,0 +1,100 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+import json
+from pgadmin.utils.route import BaseTestGenerator
+from regression import parent_node_dict
+from regression.test_setup import config_data
+from regression.python_test_utils import test_utils as utils
+
+
+class TestNewConnectionUser(BaseTestGenerator):
+ """ This class will test new connection user. """
+ API_URL = '/sqleditor/new_connection_user/'
+ scenarios = [
+ ('New connection dialog',
+ dict(
+ url=API_URL,
+ is_positive_test=True,
+ mocking_required=False,
+ is_server_conn_required=False,
+ test_data={},
+ mock_data={},
+ expected_data={
+ "status_code": 200
+ }
+ )),
+ ('New connection dialog connect server',
+ dict(
+ url=API_URL,
+ is_positive_test=True,
+ mocking_required=False,
+ is_server_conn_required=True,
+ test_data={},
+ mock_data={},
+ expected_data={
+ "status_code": 200
+ }
+ )),
+ ('New connection dialog negative',
+ dict(
+ url=API_URL,
+ is_positive_test=False,
+ mocking_required=False,
+ is_server_conn_required=True,
+ test_data={},
+ mock_data={},
+ expected_data={
+ "status_code": 200
+ }
+ )),
+ ]
+
+ def setUp(self):
+ self.content_type = 'html/json'
+ self.sid = parent_node_dict["server"][-1]["server_id"]
+ self.sgid = config_data['server_group']
+
+ def get_use(self):
+ response = self.tester.get(
+ self.url + str(self.sgid) + '/' + str(self.sid),
+ content_type=self.content_type
+ )
+
+ return response
+
+ def runTest(self):
+ if self.is_positive_test:
+ if self.is_server_conn_required:
+ self.server['password'] = self.server['db_password']
+ self.tester.post(
+ '/browser/server/connect/{0}/{1}'.format(
+ utils.SERVER_GROUP,
+ self.sid),
+ data=json.dumps(self.server),
+ content_type=self.content_type
+ )
+ response = self.get_use()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+ else:
+ if self.is_server_conn_required:
+ self.server['password'] = self.server['db_password']
+ self.tester.post(
+ '/browser/server/connect/{0}/{1}'.format(
+ utils.SERVER_GROUP,
+ self.sid),
+ data=json.dumps(self.server),
+ content_type='html/json'
+ )
+ self.sid = 0
+ response = self.get_use()
+ actual_response_code = response.status_code
+ expected_response_code = self.expected_data['status_code']
+ self.assertEqual(actual_response_code, expected_response_code)
diff --git a/web/pgadmin/utils/constants.py b/web/pgadmin/utils/constants.py
index 010185f..278881b 100644
--- a/web/pgadmin/utils/constants.py
+++ b/web/pgadmin/utils/constants.py
@@ -44,3 +44,5 @@ ERROR_MSG_TRANS_ID_NOT_FOUND = gettext(
# Role module constant
ERROR_FETCHING_ROLE_INFORMATION = gettext(
'Error fetching role information from the database server.')
+
+ERROR_FETCHING_DATA = gettext('Unable to fetch data.')
diff --git a/web/pgadmin/utils/driver/psycopg2/connection.py b/web/pgadmin/utils/driver/psycopg2/connection.py
index 5295c51..4f40e65 100644
--- a/web/pgadmin/utils/driver/psycopg2/connection.py
+++ b/web/pgadmin/utils/driver/psycopg2/connection.py
@@ -21,7 +21,7 @@ import psycopg2
from flask import g, current_app
from flask_babelex import gettext
from flask_security import current_user
-from pgadmin.utils.crypto import decrypt
+from pgadmin.utils.crypto import decrypt, encrypt
from psycopg2.extensions import encodings
import config
@@ -204,6 +204,45 @@ class Connection(BaseConnection):
def __str__(self):
return self.__repr__()
+ def _check_user_password(self, kwargs):
+ """
+ Check user and password.
+ """
+ password = None
+ encpass = None
+ is_update_password = True
+
+ if 'user' in kwargs and kwargs['password']:
+ password = kwargs['password']
+ kwargs.pop('password')
+ is_update_password = False
+ else:
+ encpass = kwargs['password'] if 'password' in kwargs else None
+
+ return password, encpass, is_update_password
+
+ def _decode_password(self, encpass, manager, password, crypt_key):
+ if encpass:
+ # Fetch Logged in User Details.
+ user = User.query.filter_by(id=current_user.id).first()
+
+ if user is None:
+ return True, self.UNAUTHORIZED_REQUEST, password
+
+ try:
+ password = decrypt(encpass, crypt_key)
+ # password is in bytes, for python3 we need it in string
+ if isinstance(password, bytes):
+ password = password.decode()
+ except Exception as e:
+ manager.stop_ssh_tunnel()
+ current_app.logger.exception(e)
+ return True, \
+ _(
+ "Failed to decrypt the saved password.\nError: {0}"
+ ).format(str(e))
+ return False, '', password
+
def connect(self, **kwargs):
if self.conn:
if self.conn.closed:
@@ -212,11 +251,13 @@ class Connection(BaseConnection):
return True, None
pg_conn = None
- password = None
passfile = None
manager = self.manager
+ crypt_key_present, crypt_key = get_crypt_key()
+
+ password, encpass, is_update_password = self._check_user_password(
+ kwargs)
- encpass = kwargs['password'] if 'password' in kwargs else None
passfile = kwargs['passfile'] if 'passfile' in kwargs else None
tunnel_password = kwargs['tunnel_password'] if 'tunnel_password' in \
kwargs else ''
@@ -231,38 +272,23 @@ class Connection(BaseConnection):
if manager.use_ssh_tunnel == 1:
manager.check_ssh_tunnel_alive()
- if encpass is None:
- encpass = self.password or getattr(manager, 'password', None)
+ if is_update_password:
+ if encpass is None:
+ encpass = self.password or getattr(manager, 'password', None)
- self.password = encpass
+ self.password = encpass
# Reset the existing connection password
if self.reconnecting is not False:
self.password = None
- crypt_key_present, crypt_key = get_crypt_key()
if not crypt_key_present:
raise CryptKeyMissing()
- if encpass:
- # Fetch Logged in User Details.
- user = User.query.filter_by(id=current_user.id).first()
-
- if user is None:
- return False, self.UNAUTHORIZED_REQUEST
-
- try:
- password = decrypt(encpass, crypt_key)
- # password is in bytes, for python3 we need it in string
- if isinstance(password, bytes):
- password = password.decode()
- except Exception as e:
- manager.stop_ssh_tunnel()
- current_app.logger.exception(e)
- return False, \
- _(
- "Failed to decrypt the saved password.\nError: {0}"
- ).format(str(e))
+ is_error, errmsg, password = self._decode_password(encpass, manager,
+ password, crypt_key)
+ if is_error:
+ return False, errmsg
# If no password credential is found then connect request might
# come from Query tool, ViewData grid, debugger etc tools.
@@ -273,7 +299,10 @@ class Connection(BaseConnection):
try:
database = self.db
- user = manager.user
+ if 'user' in kwargs and kwargs['user']:
+ user = kwargs['user']
+ else:
+ user = manager.user
conn_id = self.conn_id
import os
@@ -342,10 +371,10 @@ class Connection(BaseConnection):
self.wasConnected = False
raise e
- if status:
+ if status and is_update_password:
manager._update_password(encpass)
else:
- if not self.reconnecting:
+ if not self.reconnecting and is_update_password:
self.wasConnected = False
return status, msg
@@ -363,7 +392,7 @@ class Connection(BaseConnection):
else:
self.conn.autocommit = True
- def _set_role(self, manager, cur, conn_id):
+ def _set_role(self, manager, cur, conn_id, **kwargs):
"""
Set role
:param manager:
@@ -371,8 +400,18 @@ class Connection(BaseConnection):
:param conn_id:
:return:
"""
- if manager.role:
- status = self._execute(cur, "SET ROLE TO %s", [manager.role])
+ is_set_role = False
+ role = None
+
+ if 'role' in kwargs and kwargs['role']:
+ is_set_role = True
+ role = kwargs['role']
+ elif manager.role:
+ is_set_role = True
+ role = manager.role
+
+ if is_set_role:
+ status = self._execute(cur, "SET ROLE TO %s", [role])
if status is not None:
self.conn.close()
@@ -386,7 +425,7 @@ class Connection(BaseConnection):
msg=status
)
)
- return False, \
+ return True, \
_(
"Failed to setup the role with error message:\n{0}"
).format(status)
@@ -449,7 +488,7 @@ class Connection(BaseConnection):
return False, status
- is_error, errmsg = self._set_role(manager, cur, conn_id)
+ is_error, errmsg = self._set_role(manager, cur, conn_id, **kwargs)
if is_error:
return False, errmsg
@@ -495,7 +534,7 @@ WHERE db.datname = current_database()""")
if len(manager.db_info) == 1:
manager.did = res['did']
- self._set_user_info(cur, manager)
+ self._set_user_info(cur, manager, **kwargs)
self._set_server_type_and_password(kwargs, manager)
@@ -503,7 +542,7 @@ WHERE db.datname = current_database()""")
return True, None
- def _set_user_info(self, cur, manager):
+ def _set_user_info(self, cur, manager, **kwargs):
"""
Set user info.
:param cur:
@@ -521,7 +560,7 @@ WHERE db.datname = current_database()""")
WHERE
rolname = current_user""")
- if status is None:
+ if status is None and 'user' not in kwargs:
manager.user_info = dict()
if cur.rowcount > 0:
manager.user_info = cur.fetchmany(1)[0]
^ permalink raw reply [nested|flat] 18+ messages in thread
* Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab
2020-09-17 10:45 [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-09-21 11:54 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-09-28 05:50 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-09-28 12:28 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-09-28 14:01 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-09-29 06:01 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-10-01 05:12 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
@ 2020-10-01 08:01 ` Akshay Joshi <[email protected]>
2020-10-06 08:51 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-10-06 11:51 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Murtuza Zabuawala <[email protected]>
0 siblings, 2 replies; 18+ messages in thread
From: Akshay Joshi @ 2020-10-01 08:01 UTC (permalink / raw)
To: Nikhil Mohite <[email protected]>; +Cc: pgadmin-hackers
Thanks, patch applied.
On Thu, Oct 1, 2020 at 10:42 AM Nikhil Mohite <
[email protected]> wrote:
> Hi Akshay,
>
> I have resolved the sonarQube issues, PFA updated patch for the same.
>
>
> Regards,
> Nikhil Mohite.
>
>
> On Tue, Sep 29, 2020 at 11:31 AM Akshay Joshi <
> [email protected]> wrote:
>
>> Hi Nikhil
>>
>> Your patch introduces 1 new Bug and 13 new code smells, please fix those
>> and resend the patch.
>>
>> On Mon, Sep 28, 2020 at 7:31 PM Nikhil Mohite <
>> [email protected]> wrote:
>>
>>> Hi Akshay,
>>>
>>> I have resolved code conflict issues and sonarqube issues.
>>> PFA updated patch.
>>>
>>> Regards,
>>> Nikhil Mohite.
>>>
>>> On Mon, Sep 28, 2020 at 5:58 PM Akshay Joshi <
>>> [email protected]> wrote:
>>>
>>>> Hi Nikhil
>>>>
>>>> The patch is not applying, rebase, and send it again. Please check your
>>>> code should not create any new SonarQube issues.
>>>>
>>>> On Mon, Sep 28, 2020 at 11:20 AM Nikhil Mohite <
>>>> [email protected]> wrote:
>>>>
>>>>> Hi Akshay,
>>>>>
>>>>> I have resolved all the review comments and also updated the test
>>>>> cases as per the new implementation.
>>>>>
>>>>> PFA updated patch.
>>>>>
>>>>>
>>>>>
>>>>> On Mon, Sep 21, 2020 at 5:24 PM Akshay Joshi <
>>>>> [email protected]> wrote:
>>>>>
>>>>>> Hi Nikhil
>>>>>>
>>>>>> Following are the initial review comments:
>>>>>>
>>>>>> - Open View/Edit data on any table and click on the same database
>>>>>> connection and then click on the Execute button. Got "get_primary_keys()
>>>>>> takes 1 positional argument but 2 were given" error.
>>>>>> - In my opinion, we should hide the option to change the database
>>>>>> connection for View/Edit Data.
>>>>>> - If the user clicks on the same database connection multiple
>>>>>> times then no need to change the backend connection and transaction id. Add
>>>>>> validation at the backend, no action required in this case.
>>>>>> - The role option is missing from the "connect to server" dialog.
>>>>>> - The Password field should not be there on the "connect to
>>>>>> server" dialog. Sometimes we saved the password so asking a password every
>>>>>> time is not correct. Check the pgAdmin 3 behavior.
>>>>>>
>>>>>> Code review still remains.
>>>>>>
>>>>>> On Thu, Sep 17, 2020 at 4:15 PM Nikhil Mohite <
>>>>>> [email protected]> wrote:
>>>>>>
>>>>>>> Hi Team,
>>>>>>>
>>>>>>> Regarding RM-3794 <https://redmine.postgresql.org/issues/3794;
>>>>>>> allow the user to change the database connection from an open query tool:
>>>>>>> I have implemented the feature and also added documentation for it.
>>>>>>>
>>>>>>> PFA patch.
>>>>>>>
>>>>>>> --
>>>>>>> *Thanks & Regards,*
>>>>>>> *Nikhil Mohite*
>>>>>>> *Software Engineer.*
>>>>>>> *EDB Postgres* <https://www.enterprisedb.com/;
>>>>>>> *Mob.No: +91-7798364578.*
>>>>>>>
>>>>>>
>>>>>>
>>>>>> --
>>>>>> *Thanks & Regards*
>>>>>> *Akshay Joshi*
>>>>>> *pgAdmin Hacker | Sr. Software Architect*
>>>>>> *EDB Postgres <http://edbpostgres.com>*
>>>>>>
>>>>>> *Mobile: +91 976-788-8246*
>>>>>>
>>>>>
>>>>
>>>> --
>>>> *Thanks & Regards*
>>>> *Akshay Joshi*
>>>> *pgAdmin Hacker | Sr. Software Architect*
>>>> *EDB Postgres <http://edbpostgres.com>*
>>>>
>>>> *Mobile: +91 976-788-8246*
>>>>
>>>
>>
>> --
>> *Thanks & Regards*
>> *Akshay Joshi*
>> *pgAdmin Hacker | Sr. Software Architect*
>> *EDB Postgres <http://edbpostgres.com>*
>>
>> *Mobile: +91 976-788-8246*
>>
>
--
*Thanks & Regards*
*Akshay Joshi*
*pgAdmin Hacker | Sr. Software Architect*
*EDB Postgres <http://edbpostgres.com>*
*Mobile: +91 976-788-8246*
^ permalink raw reply [nested|flat] 18+ messages in thread
* Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab
2020-09-17 10:45 [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-09-21 11:54 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-09-28 05:50 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-09-28 12:28 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-09-28 14:01 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-09-29 06:01 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-10-01 05:12 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-10-01 08:01 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
@ 2020-10-06 08:51 ` Nikhil Mohite <[email protected]>
1 sibling, 0 replies; 18+ messages in thread
From: Nikhil Mohite @ 2020-10-06 08:51 UTC (permalink / raw)
To: Akshay Joshi <[email protected]>; +Cc: pgadmin-hackers
Hi Akshay,
I have updated the following points as per suggestions:
1. Updated connection success message. (Added database name in the success
message.)
2. Resolve issue of the mouse pointer and dropdown will show below the
connection string only.
3. Added loader for both new connections and load existing connections.
4. Removed async: false for update connection.
Regards,
Nikhil Mohite.
On Thu, Oct 1, 2020 at 1:31 PM Akshay Joshi <[email protected]>
wrote:
> Thanks, patch applied.
>
> On Thu, Oct 1, 2020 at 10:42 AM Nikhil Mohite <
> [email protected]> wrote:
>
>> Hi Akshay,
>>
>> I have resolved the sonarQube issues, PFA updated patch for the same.
>>
>>
>> Regards,
>> Nikhil Mohite.
>>
>>
>> On Tue, Sep 29, 2020 at 11:31 AM Akshay Joshi <
>> [email protected]> wrote:
>>
>>> Hi Nikhil
>>>
>>> Your patch introduces 1 new Bug and 13 new code smells, please fix those
>>> and resend the patch.
>>>
>>> On Mon, Sep 28, 2020 at 7:31 PM Nikhil Mohite <
>>> [email protected]> wrote:
>>>
>>>> Hi Akshay,
>>>>
>>>> I have resolved code conflict issues and sonarqube issues.
>>>> PFA updated patch.
>>>>
>>>> Regards,
>>>> Nikhil Mohite.
>>>>
>>>> On Mon, Sep 28, 2020 at 5:58 PM Akshay Joshi <
>>>> [email protected]> wrote:
>>>>
>>>>> Hi Nikhil
>>>>>
>>>>> The patch is not applying, rebase, and send it again. Please check
>>>>> your code should not create any new SonarQube issues.
>>>>>
>>>>> On Mon, Sep 28, 2020 at 11:20 AM Nikhil Mohite <
>>>>> [email protected]> wrote:
>>>>>
>>>>>> Hi Akshay,
>>>>>>
>>>>>> I have resolved all the review comments and also updated the test
>>>>>> cases as per the new implementation.
>>>>>>
>>>>>> PFA updated patch.
>>>>>>
>>>>>>
>>>>>>
>>>>>> On Mon, Sep 21, 2020 at 5:24 PM Akshay Joshi <
>>>>>> [email protected]> wrote:
>>>>>>
>>>>>>> Hi Nikhil
>>>>>>>
>>>>>>> Following are the initial review comments:
>>>>>>>
>>>>>>> - Open View/Edit data on any table and click on the same
>>>>>>> database connection and then click on the Execute button. Got
>>>>>>> "get_primary_keys() takes 1 positional argument but 2 were given" error.
>>>>>>> - In my opinion, we should hide the option to change the
>>>>>>> database connection for View/Edit Data.
>>>>>>> - If the user clicks on the same database connection multiple
>>>>>>> times then no need to change the backend connection and transaction id. Add
>>>>>>> validation at the backend, no action required in this case.
>>>>>>> - The role option is missing from the "connect to server" dialog.
>>>>>>> - The Password field should not be there on the "connect to
>>>>>>> server" dialog. Sometimes we saved the password so asking a password every
>>>>>>> time is not correct. Check the pgAdmin 3 behavior.
>>>>>>>
>>>>>>> Code review still remains.
>>>>>>>
>>>>>>> On Thu, Sep 17, 2020 at 4:15 PM Nikhil Mohite <
>>>>>>> [email protected]> wrote:
>>>>>>>
>>>>>>>> Hi Team,
>>>>>>>>
>>>>>>>> Regarding RM-3794 <https://redmine.postgresql.org/issues/3794;
>>>>>>>> allow the user to change the database connection from an open query tool:
>>>>>>>> I have implemented the feature and also added documentation for it.
>>>>>>>>
>>>>>>>> PFA patch.
>>>>>>>>
>>>>>>>> --
>>>>>>>> *Thanks & Regards,*
>>>>>>>> *Nikhil Mohite*
>>>>>>>> *Software Engineer.*
>>>>>>>> *EDB Postgres* <https://www.enterprisedb.com/;
>>>>>>>> *Mob.No: +91-7798364578.*
>>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> --
>>>>>>> *Thanks & Regards*
>>>>>>> *Akshay Joshi*
>>>>>>> *pgAdmin Hacker | Sr. Software Architect*
>>>>>>> *EDB Postgres <http://edbpostgres.com>*
>>>>>>>
>>>>>>> *Mobile: +91 976-788-8246*
>>>>>>>
>>>>>>
>>>>>
>>>>> --
>>>>> *Thanks & Regards*
>>>>> *Akshay Joshi*
>>>>> *pgAdmin Hacker | Sr. Software Architect*
>>>>> *EDB Postgres <http://edbpostgres.com>*
>>>>>
>>>>> *Mobile: +91 976-788-8246*
>>>>>
>>>>
>>>
>>> --
>>> *Thanks & Regards*
>>> *Akshay Joshi*
>>> *pgAdmin Hacker | Sr. Software Architect*
>>> *EDB Postgres <http://edbpostgres.com>*
>>>
>>> *Mobile: +91 976-788-8246*
>>>
>>
>
> --
> *Thanks & Regards*
> *Akshay Joshi*
> *pgAdmin Hacker | Sr. Software Architect*
> *EDB Postgres <http://edbpostgres.com>*
>
> *Mobile: +91 976-788-8246*
>
Attachments:
[application/octet-stream] RM_3794_V6.patch (6.0K, 3-RM_3794_V6.patch)
download | inline diff:
diff --git a/web/pgadmin/static/js/sqleditor/new_connection_dialog.js b/web/pgadmin/static/js/sqleditor/new_connection_dialog.js
index 3fcd374..a674f2c 100644
--- a/web/pgadmin/static/js/sqleditor/new_connection_dialog.js
+++ b/web/pgadmin/static/js/sqleditor/new_connection_dialog.js
@@ -230,6 +230,8 @@ let NewConnectionDialog = {
'user': newConnCollectionModel['user'],
'role': newConnCollectionModel['role'],
'password': response.password,
+ 'server_name': response.server_name,
+ 'database_name': selected_database_name,
};
handler.gridView.on_change_connection(connection_details, self);
}
diff --git a/web/pgadmin/tools/datagrid/templates/datagrid/index.html b/web/pgadmin/tools/datagrid/templates/datagrid/index.html
index 4970027..feedc63 100644
--- a/web/pgadmin/tools/datagrid/templates/datagrid/index.html
+++ b/web/pgadmin/tools/datagrid/templates/datagrid/index.html
@@ -418,11 +418,13 @@
</i>
</div>
<div class="connection-info btn-group mr-1" role="group" aria-label="">
- <div class="editor-title" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"
- style="background-color: {% if fgcolor %}{{ bgcolor or '#FFFFFF' }}{% endif %}; color: {% if fgcolor %}{{ fgcolor }}{% endif %};">
+ <div class="connection-data" data-toggle="dropdown">
+ <div class="editor-title" aria-haspopup="true" aria-expanded="false"
+ style="background-color: {% if fgcolor %}{{ bgcolor or '#FFFFFF' }}{% endif %}; color: {% if fgcolor %}{{ fgcolor }}{% endif %};">
+ </div>
+ <span class="conn-info-dd dropdown-toggle dropdown-toggle-split"
+ aria-haspopup="true" aria-expanded="false"></span>
</div>
- <span class="conn-info-dd dropdown-toggle dropdown-toggle-split"
- data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></span>
<ul class="dropdown-menu" id="connections-list">
</ul>
</div>
diff --git a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
index 4ef4b8f..600a644 100644
--- a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
+++ b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
@@ -182,7 +182,7 @@ define('tools.querytool', [
});
} else {
$('.conn-info-dd').hide();
- $('.editor-title').css({pointerEvents: 'none'});
+ $('.connection-data').css({pointerEvents: 'none', cursor: 'arrow'});
}
},
@@ -2122,8 +2122,20 @@ define('tools.querytool', [
let title = this.$el.find('.editor-title').html();
if(connection_details['title'] != title) {
var self = this;
+ var loadingDiv = null;
+ var msgDiv = null;
+ if(ref){
+ loadingDiv = $('#show_filter_progress');
+ loadingDiv.removeClass('d-none');
+ msgDiv = loadingDiv.find('.sql-editor-busy-text');
+ msgDiv.text('Connecting to database...');
+ } else{
+ loadingDiv = $('#fetching_data');
+ loadingDiv.removeClass('d-none');
+ msgDiv = loadingDiv.find('.sql-editor-busy-text');
+ }
+
$.ajax({
- async: false,
url: url_for('datagrid.update_query_tool_connection', {
'trans_id': self.transId,
'sgid': connection_details['server_group'],
@@ -2148,7 +2160,8 @@ define('tools.querytool', [
};
self.set_editor_title(self.handler.url_params.title);
self.handler.setTitle(self.handler.url_params.title);
- alertify.success('connected successfully');
+ let success_msg = connection_details['server_name'] + '/' + connection_details['database_name']+ '- Database connected';
+ alertify.success(success_msg);
if(ref){
let connection_data = {
'server_group': self.handler.url_params.sgid,
@@ -2159,15 +2172,21 @@ define('tools.querytool', [
'role': connection_details['role'],
'password': connection_details['password'],
'is_allow_new_connection': true,
+ 'database_name': connection_details['database_name'],
+ 'server_name': connection_details['server_name'],
};
self.connection_list.unshift(connection_data);
self.render_connection(self.connection_list);
+ loadingDiv.addClass('d-none');
ref.close();
+ } else {
+ loadingDiv.addClass('d-none');
}
}
return true;
})
.fail(function(xhr) {
+ loadingDiv.addClass('d-none');
if(xhr.status == 428) {
alertify.connectServer('Connect to server', xhr.responseJSON.result, connection_details['server'], false);
} else {
@@ -2526,6 +2545,8 @@ define('tools.querytool', [
'role': null,
'title': _.unescape(url_params.title),
'is_allow_new_connection': false,
+ 'database_name': url_params.title.split('/')[0],
+ 'server_name': url_params.title.split('@')[1],
};
self.gridView.connection_list.unshift(connection_data);
self.gridView.render_connection(self.gridView.connection_list);
diff --git a/web/pgadmin/tools/sqleditor/static/scss/_sqleditor.scss b/web/pgadmin/tools/sqleditor/static/scss/_sqleditor.scss
index 53f2449..7fc576a 100644
--- a/web/pgadmin/tools/sqleditor/static/scss/_sqleditor.scss
+++ b/web/pgadmin/tools/sqleditor/static/scss/_sqleditor.scss
@@ -43,6 +43,12 @@
cursor: pointer;
}
+.connection-data {
+ display: inherit;
+ cursor: pointer;
+ width: auto;
+}
+
#editor-panel {
z-index: 0;
^ permalink raw reply [nested|flat] 18+ messages in thread
* Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab
2020-09-17 10:45 [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-09-21 11:54 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-09-28 05:50 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-09-28 12:28 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-09-28 14:01 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-09-29 06:01 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-10-01 05:12 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-10-01 08:01 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
@ 2020-10-06 11:51 ` Murtuza Zabuawala <[email protected]>
2020-10-06 11:54 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Dave Page <[email protected]>
1 sibling, 1 reply; 18+ messages in thread
From: Murtuza Zabuawala @ 2020-10-06 11:51 UTC (permalink / raw)
To: Akshay Joshi <[email protected]>; Dave Page <[email protected]>; +Cc: pgadmin-hackers
Hi Akshay,
We have used aysnc=False in most ajax calls with this feature, It is
causing UI hang in case of slow server response.
You can try adding a time.sleep() call at the python side response and
check the UI hang, I think we should avoid sync calls as much as possible.
--
Regards,
Murtuza Zabuawala
*EDB*
*POWER TO POSTGRES*
https://www.edbpostgres.com
On Thu, Oct 1, 2020 at 1:31 PM Akshay Joshi <[email protected]>
wrote:
> Thanks, patch applied.
>
> On Thu, Oct 1, 2020 at 10:42 AM Nikhil Mohite <
> [email protected]> wrote:
>
>> Hi Akshay,
>>
>> I have resolved the sonarQube issues, PFA updated patch for the same.
>>
>>
>> Regards,
>> Nikhil Mohite.
>>
>>
>> On Tue, Sep 29, 2020 at 11:31 AM Akshay Joshi <
>> [email protected]> wrote:
>>
>>> Hi Nikhil
>>>
>>> Your patch introduces 1 new Bug and 13 new code smells, please fix those
>>> and resend the patch.
>>>
>>> On Mon, Sep 28, 2020 at 7:31 PM Nikhil Mohite <
>>> [email protected]> wrote:
>>>
>>>> Hi Akshay,
>>>>
>>>> I have resolved code conflict issues and sonarqube issues.
>>>> PFA updated patch.
>>>>
>>>> Regards,
>>>> Nikhil Mohite.
>>>>
>>>> On Mon, Sep 28, 2020 at 5:58 PM Akshay Joshi <
>>>> [email protected]> wrote:
>>>>
>>>>> Hi Nikhil
>>>>>
>>>>> The patch is not applying, rebase, and send it again. Please check
>>>>> your code should not create any new SonarQube issues.
>>>>>
>>>>> On Mon, Sep 28, 2020 at 11:20 AM Nikhil Mohite <
>>>>> [email protected]> wrote:
>>>>>
>>>>>> Hi Akshay,
>>>>>>
>>>>>> I have resolved all the review comments and also updated the test
>>>>>> cases as per the new implementation.
>>>>>>
>>>>>> PFA updated patch.
>>>>>>
>>>>>>
>>>>>>
>>>>>> On Mon, Sep 21, 2020 at 5:24 PM Akshay Joshi <
>>>>>> [email protected]> wrote:
>>>>>>
>>>>>>> Hi Nikhil
>>>>>>>
>>>>>>> Following are the initial review comments:
>>>>>>>
>>>>>>> - Open View/Edit data on any table and click on the same
>>>>>>> database connection and then click on the Execute button. Got
>>>>>>> "get_primary_keys() takes 1 positional argument but 2 were given" error.
>>>>>>> - In my opinion, we should hide the option to change the
>>>>>>> database connection for View/Edit Data.
>>>>>>> - If the user clicks on the same database connection multiple
>>>>>>> times then no need to change the backend connection and transaction id. Add
>>>>>>> validation at the backend, no action required in this case.
>>>>>>> - The role option is missing from the "connect to server" dialog.
>>>>>>> - The Password field should not be there on the "connect to
>>>>>>> server" dialog. Sometimes we saved the password so asking a password every
>>>>>>> time is not correct. Check the pgAdmin 3 behavior.
>>>>>>>
>>>>>>> Code review still remains.
>>>>>>>
>>>>>>> On Thu, Sep 17, 2020 at 4:15 PM Nikhil Mohite <
>>>>>>> [email protected]> wrote:
>>>>>>>
>>>>>>>> Hi Team,
>>>>>>>>
>>>>>>>> Regarding RM-3794 <https://redmine.postgresql.org/issues/3794;
>>>>>>>> allow the user to change the database connection from an open query tool:
>>>>>>>> I have implemented the feature and also added documentation for it.
>>>>>>>>
>>>>>>>> PFA patch.
>>>>>>>>
>>>>>>>> --
>>>>>>>> *Thanks & Regards,*
>>>>>>>> *Nikhil Mohite*
>>>>>>>> *Software Engineer.*
>>>>>>>> *EDB Postgres* <https://www.enterprisedb.com/;
>>>>>>>> *Mob.No: +91-7798364578.*
>>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> --
>>>>>>> *Thanks & Regards*
>>>>>>> *Akshay Joshi*
>>>>>>> *pgAdmin Hacker | Sr. Software Architect*
>>>>>>> *EDB Postgres <http://edbpostgres.com>*
>>>>>>>
>>>>>>> *Mobile: +91 976-788-8246*
>>>>>>>
>>>>>>
>>>>>
>>>>> --
>>>>> *Thanks & Regards*
>>>>> *Akshay Joshi*
>>>>> *pgAdmin Hacker | Sr. Software Architect*
>>>>> *EDB Postgres <http://edbpostgres.com>*
>>>>>
>>>>> *Mobile: +91 976-788-8246*
>>>>>
>>>>
>>>
>>> --
>>> *Thanks & Regards*
>>> *Akshay Joshi*
>>> *pgAdmin Hacker | Sr. Software Architect*
>>> *EDB Postgres <http://edbpostgres.com>*
>>>
>>> *Mobile: +91 976-788-8246*
>>>
>>
>
> --
> *Thanks & Regards*
> *Akshay Joshi*
> *pgAdmin Hacker | Sr. Software Architect*
> *EDB Postgres <http://edbpostgres.com>*
>
> *Mobile: +91 976-788-8246*
>
^ permalink raw reply [nested|flat] 18+ messages in thread
* Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab
2020-09-17 10:45 [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-09-21 11:54 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-09-28 05:50 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-09-28 12:28 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-09-28 14:01 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-09-29 06:01 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-10-01 05:12 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-10-01 08:01 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-10-06 11:51 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Murtuza Zabuawala <[email protected]>
@ 2020-10-06 11:54 ` Dave Page <[email protected]>
2020-10-06 12:49 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
0 siblings, 1 reply; 18+ messages in thread
From: Dave Page @ 2020-10-06 11:54 UTC (permalink / raw)
To: Murtuza Zabuawala <[email protected]>; +Cc: Akshay Joshi <[email protected]>; pgadmin-hackers
On Tue, Oct 6, 2020 at 12:51 PM Murtuza Zabuawala <
[email protected]> wrote:
> Hi Akshay,
>
> We have used aysnc=False in most ajax calls with this feature, It is
> causing UI hang in case of slow server response.
> You can try adding a time.sleep() call at the python side response and
> check the UI hang, I think we should avoid sync calls as much as possible.
>
I consider a sync ajax call to be a bug.
>
>
> --
> Regards,
> Murtuza Zabuawala
> *EDB*
> *POWER TO POSTGRES*
> https://www.edbpostgres.com
>
>
> On Thu, Oct 1, 2020 at 1:31 PM Akshay Joshi <[email protected]>
> wrote:
>
>> Thanks, patch applied.
>>
>> On Thu, Oct 1, 2020 at 10:42 AM Nikhil Mohite <
>> [email protected]> wrote:
>>
>>> Hi Akshay,
>>>
>>> I have resolved the sonarQube issues, PFA updated patch for the same.
>>>
>>>
>>> Regards,
>>> Nikhil Mohite.
>>>
>>>
>>> On Tue, Sep 29, 2020 at 11:31 AM Akshay Joshi <
>>> [email protected]> wrote:
>>>
>>>> Hi Nikhil
>>>>
>>>> Your patch introduces 1 new Bug and 13 new code smells, please fix
>>>> those and resend the patch.
>>>>
>>>> On Mon, Sep 28, 2020 at 7:31 PM Nikhil Mohite <
>>>> [email protected]> wrote:
>>>>
>>>>> Hi Akshay,
>>>>>
>>>>> I have resolved code conflict issues and sonarqube issues.
>>>>> PFA updated patch.
>>>>>
>>>>> Regards,
>>>>> Nikhil Mohite.
>>>>>
>>>>> On Mon, Sep 28, 2020 at 5:58 PM Akshay Joshi <
>>>>> [email protected]> wrote:
>>>>>
>>>>>> Hi Nikhil
>>>>>>
>>>>>> The patch is not applying, rebase, and send it again. Please check
>>>>>> your code should not create any new SonarQube issues.
>>>>>>
>>>>>> On Mon, Sep 28, 2020 at 11:20 AM Nikhil Mohite <
>>>>>> [email protected]> wrote:
>>>>>>
>>>>>>> Hi Akshay,
>>>>>>>
>>>>>>> I have resolved all the review comments and also updated the test
>>>>>>> cases as per the new implementation.
>>>>>>>
>>>>>>> PFA updated patch.
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> On Mon, Sep 21, 2020 at 5:24 PM Akshay Joshi <
>>>>>>> [email protected]> wrote:
>>>>>>>
>>>>>>>> Hi Nikhil
>>>>>>>>
>>>>>>>> Following are the initial review comments:
>>>>>>>>
>>>>>>>> - Open View/Edit data on any table and click on the same
>>>>>>>> database connection and then click on the Execute button. Got
>>>>>>>> "get_primary_keys() takes 1 positional argument but 2 were given" error.
>>>>>>>> - In my opinion, we should hide the option to change the
>>>>>>>> database connection for View/Edit Data.
>>>>>>>> - If the user clicks on the same database connection multiple
>>>>>>>> times then no need to change the backend connection and transaction id. Add
>>>>>>>> validation at the backend, no action required in this case.
>>>>>>>> - The role option is missing from the "connect to server"
>>>>>>>> dialog.
>>>>>>>> - The Password field should not be there on the "connect to
>>>>>>>> server" dialog. Sometimes we saved the password so asking a password every
>>>>>>>> time is not correct. Check the pgAdmin 3 behavior.
>>>>>>>>
>>>>>>>> Code review still remains.
>>>>>>>>
>>>>>>>> On Thu, Sep 17, 2020 at 4:15 PM Nikhil Mohite <
>>>>>>>> [email protected]> wrote:
>>>>>>>>
>>>>>>>>> Hi Team,
>>>>>>>>>
>>>>>>>>> Regarding RM-3794 <https://redmine.postgresql.org/issues/3794;
>>>>>>>>> allow the user to change the database connection from an open query tool:
>>>>>>>>> I have implemented the feature and also added documentation for it.
>>>>>>>>>
>>>>>>>>> PFA patch.
>>>>>>>>>
>>>>>>>>> --
>>>>>>>>> *Thanks & Regards,*
>>>>>>>>> *Nikhil Mohite*
>>>>>>>>> *Software Engineer.*
>>>>>>>>> *EDB Postgres* <https://www.enterprisedb.com/;
>>>>>>>>> *Mob.No: +91-7798364578.*
>>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> --
>>>>>>>> *Thanks & Regards*
>>>>>>>> *Akshay Joshi*
>>>>>>>> *pgAdmin Hacker | Sr. Software Architect*
>>>>>>>> *EDB Postgres <http://edbpostgres.com>*
>>>>>>>>
>>>>>>>> *Mobile: +91 976-788-8246*
>>>>>>>>
>>>>>>>
>>>>>>
>>>>>> --
>>>>>> *Thanks & Regards*
>>>>>> *Akshay Joshi*
>>>>>> *pgAdmin Hacker | Sr. Software Architect*
>>>>>> *EDB Postgres <http://edbpostgres.com>*
>>>>>>
>>>>>> *Mobile: +91 976-788-8246*
>>>>>>
>>>>>
>>>>
>>>> --
>>>> *Thanks & Regards*
>>>> *Akshay Joshi*
>>>> *pgAdmin Hacker | Sr. Software Architect*
>>>> *EDB Postgres <http://edbpostgres.com>*
>>>>
>>>> *Mobile: +91 976-788-8246*
>>>>
>>>
>>
>> --
>> *Thanks & Regards*
>> *Akshay Joshi*
>> *pgAdmin Hacker | Sr. Software Architect*
>> *EDB Postgres <http://edbpostgres.com>*
>>
>> *Mobile: +91 976-788-8246*
>>
>
--
Dave Page
VP & Chief Architect, Database Infrastructure
EDB: http://www.enterprisedb.com
Blog: http://pgsnake.blogspot.com
Twitter: @pgsnake
^ permalink raw reply [nested|flat] 18+ messages in thread
* Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab
2020-09-17 10:45 [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-09-21 11:54 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-09-28 05:50 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-09-28 12:28 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-09-28 14:01 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-09-29 06:01 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-10-01 05:12 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-10-01 08:01 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-10-06 11:51 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Murtuza Zabuawala <[email protected]>
2020-10-06 11:54 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Dave Page <[email protected]>
@ 2020-10-06 12:49 ` Akshay Joshi <[email protected]>
2020-10-07 06:41 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
0 siblings, 1 reply; 18+ messages in thread
From: Akshay Joshi @ 2020-10-06 12:49 UTC (permalink / raw)
To: Nikhil Mohite <[email protected]>; +Cc: Murtuza Zabuawala <[email protected]>; pgadmin-hackers; Dave Page <[email protected]>
Hi Nikhil
Please verify and remove async = false wherever possible.
On Tue, Oct 6, 2020 at 5:24 PM Dave Page <[email protected]> wrote:
>
>
> On Tue, Oct 6, 2020 at 12:51 PM Murtuza Zabuawala <
> [email protected]> wrote:
>
>> Hi Akshay,
>>
>> We have used aysnc=False in most ajax calls with this feature, It is
>> causing UI hang in case of slow server response.
>> You can try adding a time.sleep() call at the python side response and
>> check the UI hang, I think we should avoid sync calls as much as possible.
>>
>
> I consider a sync ajax call to be a bug.
>
>
>>
>>
>> --
>> Regards,
>> Murtuza Zabuawala
>> *EDB*
>> *POWER TO POSTGRES*
>> https://www.edbpostgres.com
>>
>>
>> On Thu, Oct 1, 2020 at 1:31 PM Akshay Joshi <
>> [email protected]> wrote:
>>
>>> Thanks, patch applied.
>>>
>>> On Thu, Oct 1, 2020 at 10:42 AM Nikhil Mohite <
>>> [email protected]> wrote:
>>>
>>>> Hi Akshay,
>>>>
>>>> I have resolved the sonarQube issues, PFA updated patch for the same.
>>>>
>>>>
>>>> Regards,
>>>> Nikhil Mohite.
>>>>
>>>>
>>>> On Tue, Sep 29, 2020 at 11:31 AM Akshay Joshi <
>>>> [email protected]> wrote:
>>>>
>>>>> Hi Nikhil
>>>>>
>>>>> Your patch introduces 1 new Bug and 13 new code smells, please fix
>>>>> those and resend the patch.
>>>>>
>>>>> On Mon, Sep 28, 2020 at 7:31 PM Nikhil Mohite <
>>>>> [email protected]> wrote:
>>>>>
>>>>>> Hi Akshay,
>>>>>>
>>>>>> I have resolved code conflict issues and sonarqube issues.
>>>>>> PFA updated patch.
>>>>>>
>>>>>> Regards,
>>>>>> Nikhil Mohite.
>>>>>>
>>>>>> On Mon, Sep 28, 2020 at 5:58 PM Akshay Joshi <
>>>>>> [email protected]> wrote:
>>>>>>
>>>>>>> Hi Nikhil
>>>>>>>
>>>>>>> The patch is not applying, rebase, and send it again. Please check
>>>>>>> your code should not create any new SonarQube issues.
>>>>>>>
>>>>>>> On Mon, Sep 28, 2020 at 11:20 AM Nikhil Mohite <
>>>>>>> [email protected]> wrote:
>>>>>>>
>>>>>>>> Hi Akshay,
>>>>>>>>
>>>>>>>> I have resolved all the review comments and also updated the test
>>>>>>>> cases as per the new implementation.
>>>>>>>>
>>>>>>>> PFA updated patch.
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> On Mon, Sep 21, 2020 at 5:24 PM Akshay Joshi <
>>>>>>>> [email protected]> wrote:
>>>>>>>>
>>>>>>>>> Hi Nikhil
>>>>>>>>>
>>>>>>>>> Following are the initial review comments:
>>>>>>>>>
>>>>>>>>> - Open View/Edit data on any table and click on the same
>>>>>>>>> database connection and then click on the Execute button. Got
>>>>>>>>> "get_primary_keys() takes 1 positional argument but 2 were given" error.
>>>>>>>>> - In my opinion, we should hide the option to change the
>>>>>>>>> database connection for View/Edit Data.
>>>>>>>>> - If the user clicks on the same database connection multiple
>>>>>>>>> times then no need to change the backend connection and transaction id. Add
>>>>>>>>> validation at the backend, no action required in this case.
>>>>>>>>> - The role option is missing from the "connect to server"
>>>>>>>>> dialog.
>>>>>>>>> - The Password field should not be there on the "connect to
>>>>>>>>> server" dialog. Sometimes we saved the password so asking a password every
>>>>>>>>> time is not correct. Check the pgAdmin 3 behavior.
>>>>>>>>>
>>>>>>>>> Code review still remains.
>>>>>>>>>
>>>>>>>>> On Thu, Sep 17, 2020 at 4:15 PM Nikhil Mohite <
>>>>>>>>> [email protected]> wrote:
>>>>>>>>>
>>>>>>>>>> Hi Team,
>>>>>>>>>>
>>>>>>>>>> Regarding RM-3794 <https://redmine.postgresql.org/issues/3794;
>>>>>>>>>> allow the user to change the database connection from an open query tool:
>>>>>>>>>> I have implemented the feature and also added documentation for
>>>>>>>>>> it.
>>>>>>>>>>
>>>>>>>>>> PFA patch.
>>>>>>>>>>
>>>>>>>>>> --
>>>>>>>>>> *Thanks & Regards,*
>>>>>>>>>> *Nikhil Mohite*
>>>>>>>>>> *Software Engineer.*
>>>>>>>>>> *EDB Postgres* <https://www.enterprisedb.com/;
>>>>>>>>>> *Mob.No: +91-7798364578.*
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> --
>>>>>>>>> *Thanks & Regards*
>>>>>>>>> *Akshay Joshi*
>>>>>>>>> *pgAdmin Hacker | Sr. Software Architect*
>>>>>>>>> *EDB Postgres <http://edbpostgres.com>*
>>>>>>>>>
>>>>>>>>> *Mobile: +91 976-788-8246*
>>>>>>>>>
>>>>>>>>
>>>>>>>
>>>>>>> --
>>>>>>> *Thanks & Regards*
>>>>>>> *Akshay Joshi*
>>>>>>> *pgAdmin Hacker | Sr. Software Architect*
>>>>>>> *EDB Postgres <http://edbpostgres.com>*
>>>>>>>
>>>>>>> *Mobile: +91 976-788-8246*
>>>>>>>
>>>>>>
>>>>>
>>>>> --
>>>>> *Thanks & Regards*
>>>>> *Akshay Joshi*
>>>>> *pgAdmin Hacker | Sr. Software Architect*
>>>>> *EDB Postgres <http://edbpostgres.com>*
>>>>>
>>>>> *Mobile: +91 976-788-8246*
>>>>>
>>>>
>>>
>>> --
>>> *Thanks & Regards*
>>> *Akshay Joshi*
>>> *pgAdmin Hacker | Sr. Software Architect*
>>> *EDB Postgres <http://edbpostgres.com>*
>>>
>>> *Mobile: +91 976-788-8246*
>>>
>>
>
> --
> Dave Page
> VP & Chief Architect, Database Infrastructure
> EDB: http://www.enterprisedb.com
>
> Blog: http://pgsnake.blogspot.com
> Twitter: @pgsnake
>
--
*Thanks & Regards*
*Akshay Joshi*
*pgAdmin Hacker | Sr. Software Architect*
*EDB Postgres <http://edbpostgres.com>*
*Mobile: +91 976-788-8246*
^ permalink raw reply [nested|flat] 18+ messages in thread
* Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab
2020-09-17 10:45 [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-09-21 11:54 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-09-28 05:50 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-09-28 12:28 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-09-28 14:01 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-09-29 06:01 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-10-01 05:12 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-10-01 08:01 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-10-06 11:51 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Murtuza Zabuawala <[email protected]>
2020-10-06 11:54 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Dave Page <[email protected]>
2020-10-06 12:49 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
@ 2020-10-07 06:41 ` Nikhil Mohite <[email protected]>
2020-10-08 06:09 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
0 siblings, 1 reply; 18+ messages in thread
From: Nikhil Mohite @ 2020-10-07 06:41 UTC (permalink / raw)
To: Akshay Joshi <[email protected]>; +Cc: pgadmin-hackers; Murtuza Zabuawala <[email protected]>
Hi Akshay,
I checked the implementation and found 2 locations which I missed in the
last patch to remove async: False.
I have removed all occurrences of async: False now also added missing
loader in required places.
PFA updated the patch for the same.
Regards,
Nikhil Mohite.
On Tue, Oct 6, 2020 at 6:19 PM Akshay Joshi <[email protected]>
wrote:
> Hi Nikhil
>
> Please verify and remove async = false wherever possible.
>
> On Tue, Oct 6, 2020 at 5:24 PM Dave Page <[email protected]>
> wrote:
>
>>
>>
>> On Tue, Oct 6, 2020 at 12:51 PM Murtuza Zabuawala <
>> [email protected]> wrote:
>>
>>> Hi Akshay,
>>>
>>> We have used aysnc=False in most ajax calls with this feature, It is
>>> causing UI hang in case of slow server response.
>>> You can try adding a time.sleep() call at the python side response and
>>> check the UI hang, I think we should avoid sync calls as much as possible.
>>>
>>
>> I consider a sync ajax call to be a bug.
>>
>>
>>>
>>>
>>> --
>>> Regards,
>>> Murtuza Zabuawala
>>> *EDB*
>>> *POWER TO POSTGRES*
>>> https://www.edbpostgres.com
>>>
>>>
>>> On Thu, Oct 1, 2020 at 1:31 PM Akshay Joshi <
>>> [email protected]> wrote:
>>>
>>>> Thanks, patch applied.
>>>>
>>>> On Thu, Oct 1, 2020 at 10:42 AM Nikhil Mohite <
>>>> [email protected]> wrote:
>>>>
>>>>> Hi Akshay,
>>>>>
>>>>> I have resolved the sonarQube issues, PFA updated patch for the same.
>>>>>
>>>>>
>>>>> Regards,
>>>>> Nikhil Mohite.
>>>>>
>>>>>
>>>>> On Tue, Sep 29, 2020 at 11:31 AM Akshay Joshi <
>>>>> [email protected]> wrote:
>>>>>
>>>>>> Hi Nikhil
>>>>>>
>>>>>> Your patch introduces 1 new Bug and 13 new code smells, please fix
>>>>>> those and resend the patch.
>>>>>>
>>>>>> On Mon, Sep 28, 2020 at 7:31 PM Nikhil Mohite <
>>>>>> [email protected]> wrote:
>>>>>>
>>>>>>> Hi Akshay,
>>>>>>>
>>>>>>> I have resolved code conflict issues and sonarqube issues.
>>>>>>> PFA updated patch.
>>>>>>>
>>>>>>> Regards,
>>>>>>> Nikhil Mohite.
>>>>>>>
>>>>>>> On Mon, Sep 28, 2020 at 5:58 PM Akshay Joshi <
>>>>>>> [email protected]> wrote:
>>>>>>>
>>>>>>>> Hi Nikhil
>>>>>>>>
>>>>>>>> The patch is not applying, rebase, and send it again. Please check
>>>>>>>> your code should not create any new SonarQube issues.
>>>>>>>>
>>>>>>>> On Mon, Sep 28, 2020 at 11:20 AM Nikhil Mohite <
>>>>>>>> [email protected]> wrote:
>>>>>>>>
>>>>>>>>> Hi Akshay,
>>>>>>>>>
>>>>>>>>> I have resolved all the review comments and also updated the test
>>>>>>>>> cases as per the new implementation.
>>>>>>>>>
>>>>>>>>> PFA updated patch.
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> On Mon, Sep 21, 2020 at 5:24 PM Akshay Joshi <
>>>>>>>>> [email protected]> wrote:
>>>>>>>>>
>>>>>>>>>> Hi Nikhil
>>>>>>>>>>
>>>>>>>>>> Following are the initial review comments:
>>>>>>>>>>
>>>>>>>>>> - Open View/Edit data on any table and click on the same
>>>>>>>>>> database connection and then click on the Execute button. Got
>>>>>>>>>> "get_primary_keys() takes 1 positional argument but 2 were given" error.
>>>>>>>>>> - In my opinion, we should hide the option to change the
>>>>>>>>>> database connection for View/Edit Data.
>>>>>>>>>> - If the user clicks on the same database connection multiple
>>>>>>>>>> times then no need to change the backend connection and transaction id. Add
>>>>>>>>>> validation at the backend, no action required in this case.
>>>>>>>>>> - The role option is missing from the "connect to server"
>>>>>>>>>> dialog.
>>>>>>>>>> - The Password field should not be there on the "connect to
>>>>>>>>>> server" dialog. Sometimes we saved the password so asking a password every
>>>>>>>>>> time is not correct. Check the pgAdmin 3 behavior.
>>>>>>>>>>
>>>>>>>>>> Code review still remains.
>>>>>>>>>>
>>>>>>>>>> On Thu, Sep 17, 2020 at 4:15 PM Nikhil Mohite <
>>>>>>>>>> [email protected]> wrote:
>>>>>>>>>>
>>>>>>>>>>> Hi Team,
>>>>>>>>>>>
>>>>>>>>>>> Regarding RM-3794 <https://redmine.postgresql.org/issues/3794;
>>>>>>>>>>> allow the user to change the database connection from an open query tool:
>>>>>>>>>>> I have implemented the feature and also added documentation for
>>>>>>>>>>> it.
>>>>>>>>>>>
>>>>>>>>>>> PFA patch.
>>>>>>>>>>>
>>>>>>>>>>> --
>>>>>>>>>>> *Thanks & Regards,*
>>>>>>>>>>> *Nikhil Mohite*
>>>>>>>>>>> *Software Engineer.*
>>>>>>>>>>> *EDB Postgres* <https://www.enterprisedb.com/;
>>>>>>>>>>> *Mob.No: +91-7798364578.*
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> --
>>>>>>>>>> *Thanks & Regards*
>>>>>>>>>> *Akshay Joshi*
>>>>>>>>>> *pgAdmin Hacker | Sr. Software Architect*
>>>>>>>>>> *EDB Postgres <http://edbpostgres.com>*
>>>>>>>>>>
>>>>>>>>>> *Mobile: +91 976-788-8246*
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>
>>>>>>>> --
>>>>>>>> *Thanks & Regards*
>>>>>>>> *Akshay Joshi*
>>>>>>>> *pgAdmin Hacker | Sr. Software Architect*
>>>>>>>> *EDB Postgres <http://edbpostgres.com>*
>>>>>>>>
>>>>>>>> *Mobile: +91 976-788-8246*
>>>>>>>>
>>>>>>>
>>>>>>
>>>>>> --
>>>>>> *Thanks & Regards*
>>>>>> *Akshay Joshi*
>>>>>> *pgAdmin Hacker | Sr. Software Architect*
>>>>>> *EDB Postgres <http://edbpostgres.com>*
>>>>>>
>>>>>> *Mobile: +91 976-788-8246*
>>>>>>
>>>>>
>>>>
>>>> --
>>>> *Thanks & Regards*
>>>> *Akshay Joshi*
>>>> *pgAdmin Hacker | Sr. Software Architect*
>>>> *EDB Postgres <http://edbpostgres.com>*
>>>>
>>>> *Mobile: +91 976-788-8246*
>>>>
>>>
>>
>> --
>> Dave Page
>> VP & Chief Architect, Database Infrastructure
>> EDB: http://www.enterprisedb.com
>>
>> Blog: http://pgsnake.blogspot.com
>> Twitter: @pgsnake
>>
>
>
> --
> *Thanks & Regards*
> *Akshay Joshi*
> *pgAdmin Hacker | Sr. Software Architect*
> *EDB Postgres <http://edbpostgres.com>*
>
> *Mobile: +91 976-788-8246*
>
Attachments:
[application/octet-stream] RM_3794_V7.patch (11.1K, 3-RM_3794_V7.patch)
download | inline diff:
diff --git a/web/pgadmin/static/js/sqleditor/new_connection_dialog.js b/web/pgadmin/static/js/sqleditor/new_connection_dialog.js
index 3fcd374..13267ab 100644
--- a/web/pgadmin/static/js/sqleditor/new_connection_dialog.js
+++ b/web/pgadmin/static/js/sqleditor/new_connection_dialog.js
@@ -95,7 +95,7 @@ let NewConnectionDialog = {
closableByDimmer: false,
modal: false,
autoReset: false,
- closable: true,
+ closable: false,
},
};
},
@@ -152,14 +152,18 @@ let NewConnectionDialog = {
self.statusBar.removeClass('d-none');
$(self.statusBar.find('.alert-text')).html(msg);
// Disable Okay button
- self.__internal.buttons[2].element.disabled = true;
+ if(self.__internal){
+ self.__internal.buttons[2].element.disabled = true;
+ }
});
view.listenTo(view.model, 'pgadmin-session:valid', function() {
self.statusBar.addClass('d-none');
$(self.statusBar.find('.alert-text')).html('');
// Enable Okay button
- self.__internal.buttons[2].element.disabled = false;
+ if(self.__internal) {
+ self.__internal.buttons[2].element.disabled = false;
+ }
});
});
@@ -230,15 +234,18 @@ let NewConnectionDialog = {
'user': newConnCollectionModel['user'],
'role': newConnCollectionModel['role'],
'password': response.password,
+ 'server_name': response.server_name,
+ 'database_name': selected_database_name,
};
handler.gridView.on_change_connection(connection_details, self);
}
} else {
- self.close();
+ Alertify.newConnectionDialog().destroy();
}
},
};
});
+
setTimeout(function(){
Alertify.newConnectionDialog('Connect to server.').resizeTo(pgAdmin.Browser.stdW.md,pgAdmin.Browser.stdH.md);
}, 500);
diff --git a/web/pgadmin/static/js/sqleditor/new_connection_dialog_model.js b/web/pgadmin/static/js/sqleditor/new_connection_dialog_model.js
index e262d0e..9e03051 100644
--- a/web/pgadmin/static/js/sqleditor/new_connection_dialog_model.js
+++ b/web/pgadmin/static/js/sqleditor/new_connection_dialog_model.js
@@ -31,7 +31,6 @@ export default function newConnectionDialogModel(response, sgid, sid) {
});
$.ajax({
- async: false,
url: url,
headers: {
'Cache-Control' : 'no-cache',
@@ -52,8 +51,8 @@ export default function newConnectionDialogModel(response, sgid, sid) {
} else {
self.field.set('options', []);
}
- //alertify.error(res.data.msg);
}
+ Backform.Select2Control.prototype.render.apply(self, arguments);
}).fail(function(e){
let msg = '';
if(e.status == 404) {
@@ -131,7 +130,8 @@ export default function newConnectionDialogModel(response, sgid, sid) {
if (closeEvent.button.text == gettext('OK')) {
if(this.submit_password) {
var _url = url_for('sqleditor.connect_server', {'sid': this.server_id});
-
+ var loadingDiv = $('#show_filter_progress');
+ loadingDiv.removeClass('d-none');
$.ajax({
type: 'POST',
timeout: 30000,
@@ -148,8 +148,10 @@ export default function newConnectionDialogModel(response, sgid, sid) {
response.server_name = obj.name;
}
});
+ loadingDiv.addClass('d-none');
})
.fail(function(xhr) {
+ loadingDiv.addClass('d-none');
alertify.connectServer('Connect to server', xhr.responseJSON.result, local_self.getValueFromDOM());
});
} else {
@@ -182,8 +184,9 @@ export default function newConnectionDialogModel(response, sgid, sid) {
'sid': self.getValueFromDOM(),
'usr': self.model.attributes.user,
});
+ var loadingDiv = $('#show_filter_progress');
+ loadingDiv.removeClass('d-none');
$.ajax({
- async: false,
url: url,
type: 'POST',
headers: {
@@ -196,7 +199,9 @@ export default function newConnectionDialogModel(response, sgid, sid) {
response.server_name = obj.name;
}
});
+ loadingDiv.addClass('d-none');
}).fail(function(xhr){
+ loadingDiv.addClass('d-none');
alertify.connectServer('Connect to server', xhr.responseJSON.result, self.getValueFromDOM());
});
diff --git a/web/pgadmin/tools/datagrid/templates/datagrid/index.html b/web/pgadmin/tools/datagrid/templates/datagrid/index.html
index 4970027..feedc63 100644
--- a/web/pgadmin/tools/datagrid/templates/datagrid/index.html
+++ b/web/pgadmin/tools/datagrid/templates/datagrid/index.html
@@ -418,11 +418,13 @@
</i>
</div>
<div class="connection-info btn-group mr-1" role="group" aria-label="">
- <div class="editor-title" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"
- style="background-color: {% if fgcolor %}{{ bgcolor or '#FFFFFF' }}{% endif %}; color: {% if fgcolor %}{{ fgcolor }}{% endif %};">
+ <div class="connection-data" data-toggle="dropdown">
+ <div class="editor-title" aria-haspopup="true" aria-expanded="false"
+ style="background-color: {% if fgcolor %}{{ bgcolor or '#FFFFFF' }}{% endif %}; color: {% if fgcolor %}{{ fgcolor }}{% endif %};">
+ </div>
+ <span class="conn-info-dd dropdown-toggle dropdown-toggle-split"
+ aria-haspopup="true" aria-expanded="false"></span>
</div>
- <span class="conn-info-dd dropdown-toggle dropdown-toggle-split"
- data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></span>
<ul class="dropdown-menu" id="connections-list">
</ul>
</div>
diff --git a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
index 4ef4b8f..cb1f8a2 100644
--- a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
+++ b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
@@ -182,7 +182,7 @@ define('tools.querytool', [
});
} else {
$('.conn-info-dd').hide();
- $('.editor-title').css({pointerEvents: 'none'});
+ $('.connection-data').css({pointerEvents: 'none', cursor: 'arrow'});
}
},
@@ -2122,8 +2122,20 @@ define('tools.querytool', [
let title = this.$el.find('.editor-title').html();
if(connection_details['title'] != title) {
var self = this;
+ var loadingDiv = null;
+ var msgDiv = null;
+ if(ref){
+ loadingDiv = $('#show_filter_progress');
+ loadingDiv.removeClass('d-none');
+ msgDiv = loadingDiv.find('.sql-editor-busy-text');
+ msgDiv.text('Connecting to database...');
+ } else{
+ loadingDiv = $('#fetching_data');
+ loadingDiv.removeClass('d-none');
+ msgDiv = loadingDiv.find('.sql-editor-busy-text');
+ }
+
$.ajax({
- async: false,
url: url_for('datagrid.update_query_tool_connection', {
'trans_id': self.transId,
'sgid': connection_details['server_group'],
@@ -2148,7 +2160,8 @@ define('tools.querytool', [
};
self.set_editor_title(self.handler.url_params.title);
self.handler.setTitle(self.handler.url_params.title);
- alertify.success('connected successfully');
+ let success_msg = connection_details['server_name'] + '/' + connection_details['database_name']+ '- Database connected';
+ alertify.success(success_msg);
if(ref){
let connection_data = {
'server_group': self.handler.url_params.sgid,
@@ -2159,39 +2172,27 @@ define('tools.querytool', [
'role': connection_details['role'],
'password': connection_details['password'],
'is_allow_new_connection': true,
+ 'database_name': connection_details['database_name'],
+ 'server_name': connection_details['server_name'],
};
self.connection_list.unshift(connection_data);
self.render_connection(self.connection_list);
+ loadingDiv.addClass('d-none');
+ alertify.newConnectionDialog().destroy();
ref.close();
+ } else {
+ loadingDiv.addClass('d-none');
}
}
return true;
})
.fail(function(xhr) {
+ loadingDiv.addClass('d-none');
if(xhr.status == 428) {
alertify.connectServer('Connect to server', xhr.responseJSON.result, connection_details['server'], false);
} else {
alertify.error(xhr.responseJSON['errormsg']);
}
- /*let url = url_for('sqleditor.connect_server_with_user', {
- 'sid': newConnCollectionModel['server'],
- 'usr': newConnCollectionModel['user']
- });
- $.ajax({
- async: false,
- url: url,
- headers: {
- 'Cache-Control' : 'no-cache',
- },
- }).done(function () {
- Backform.Select2Control.prototype.onChange.apply(self, arguments);
- response.server_list.forEach(function(obj){
- if(obj.id==self.model.changed.server) {
- response.server_name = obj.name;
- }
- });
- }).fail(function(xhr){});*/
-
});
}
},
@@ -2526,6 +2527,8 @@ define('tools.querytool', [
'role': null,
'title': _.unescape(url_params.title),
'is_allow_new_connection': false,
+ 'database_name': url_params.title.split('/')[0],
+ 'server_name': url_params.title.split('@')[1],
};
self.gridView.connection_list.unshift(connection_data);
self.gridView.render_connection(self.gridView.connection_list);
diff --git a/web/pgadmin/tools/sqleditor/static/scss/_sqleditor.scss b/web/pgadmin/tools/sqleditor/static/scss/_sqleditor.scss
index 53f2449..7fc576a 100644
--- a/web/pgadmin/tools/sqleditor/static/scss/_sqleditor.scss
+++ b/web/pgadmin/tools/sqleditor/static/scss/_sqleditor.scss
@@ -43,6 +43,12 @@
cursor: pointer;
}
+.connection-data {
+ display: inherit;
+ cursor: pointer;
+ width: auto;
+}
+
#editor-panel {
z-index: 0;
^ permalink raw reply [nested|flat] 18+ messages in thread
* Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab
2020-09-17 10:45 [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-09-21 11:54 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-09-28 05:50 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-09-28 12:28 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-09-28 14:01 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-09-29 06:01 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-10-01 05:12 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-10-01 08:01 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-10-06 11:51 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Murtuza Zabuawala <[email protected]>
2020-10-06 11:54 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Dave Page <[email protected]>
2020-10-06 12:49 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-10-07 06:41 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
@ 2020-10-08 06:09 ` Akshay Joshi <[email protected]>
2020-10-21 05:38 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
0 siblings, 1 reply; 18+ messages in thread
From: Akshay Joshi @ 2020-10-08 06:09 UTC (permalink / raw)
To: Nikhil Mohite <[email protected]>; +Cc: pgadmin-hackers; Murtuza Zabuawala <[email protected]>
Thanks, patch applied.
On Wed, Oct 7, 2020 at 12:11 PM Nikhil Mohite <
[email protected]> wrote:
> Hi Akshay,
>
> I checked the implementation and found 2 locations which I missed in the
> last patch to remove async: False.
> I have removed all occurrences of async: False now also added missing
> loader in required places.
>
> PFA updated the patch for the same.
>
> Regards,
> Nikhil Mohite.
>
> On Tue, Oct 6, 2020 at 6:19 PM Akshay Joshi <[email protected]>
> wrote:
>
>> Hi Nikhil
>>
>> Please verify and remove async = false wherever possible.
>>
>> On Tue, Oct 6, 2020 at 5:24 PM Dave Page <[email protected]>
>> wrote:
>>
>>>
>>>
>>> On Tue, Oct 6, 2020 at 12:51 PM Murtuza Zabuawala <
>>> [email protected]> wrote:
>>>
>>>> Hi Akshay,
>>>>
>>>> We have used aysnc=False in most ajax calls with this feature, It is
>>>> causing UI hang in case of slow server response.
>>>> You can try adding a time.sleep() call at the python side response and
>>>> check the UI hang, I think we should avoid sync calls as much as possible.
>>>>
>>>
>>> I consider a sync ajax call to be a bug.
>>>
>>>
>>>>
>>>>
>>>> --
>>>> Regards,
>>>> Murtuza Zabuawala
>>>> *EDB*
>>>> *POWER TO POSTGRES*
>>>> https://www.edbpostgres.com
>>>>
>>>>
>>>> On Thu, Oct 1, 2020 at 1:31 PM Akshay Joshi <
>>>> [email protected]> wrote:
>>>>
>>>>> Thanks, patch applied.
>>>>>
>>>>> On Thu, Oct 1, 2020 at 10:42 AM Nikhil Mohite <
>>>>> [email protected]> wrote:
>>>>>
>>>>>> Hi Akshay,
>>>>>>
>>>>>> I have resolved the sonarQube issues, PFA updated patch for the same.
>>>>>>
>>>>>>
>>>>>> Regards,
>>>>>> Nikhil Mohite.
>>>>>>
>>>>>>
>>>>>> On Tue, Sep 29, 2020 at 11:31 AM Akshay Joshi <
>>>>>> [email protected]> wrote:
>>>>>>
>>>>>>> Hi Nikhil
>>>>>>>
>>>>>>> Your patch introduces 1 new Bug and 13 new code smells, please fix
>>>>>>> those and resend the patch.
>>>>>>>
>>>>>>> On Mon, Sep 28, 2020 at 7:31 PM Nikhil Mohite <
>>>>>>> [email protected]> wrote:
>>>>>>>
>>>>>>>> Hi Akshay,
>>>>>>>>
>>>>>>>> I have resolved code conflict issues and sonarqube issues.
>>>>>>>> PFA updated patch.
>>>>>>>>
>>>>>>>> Regards,
>>>>>>>> Nikhil Mohite.
>>>>>>>>
>>>>>>>> On Mon, Sep 28, 2020 at 5:58 PM Akshay Joshi <
>>>>>>>> [email protected]> wrote:
>>>>>>>>
>>>>>>>>> Hi Nikhil
>>>>>>>>>
>>>>>>>>> The patch is not applying, rebase, and send it again. Please check
>>>>>>>>> your code should not create any new SonarQube issues.
>>>>>>>>>
>>>>>>>>> On Mon, Sep 28, 2020 at 11:20 AM Nikhil Mohite <
>>>>>>>>> [email protected]> wrote:
>>>>>>>>>
>>>>>>>>>> Hi Akshay,
>>>>>>>>>>
>>>>>>>>>> I have resolved all the review comments and also updated the test
>>>>>>>>>> cases as per the new implementation.
>>>>>>>>>>
>>>>>>>>>> PFA updated patch.
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> On Mon, Sep 21, 2020 at 5:24 PM Akshay Joshi <
>>>>>>>>>> [email protected]> wrote:
>>>>>>>>>>
>>>>>>>>>>> Hi Nikhil
>>>>>>>>>>>
>>>>>>>>>>> Following are the initial review comments:
>>>>>>>>>>>
>>>>>>>>>>> - Open View/Edit data on any table and click on the same
>>>>>>>>>>> database connection and then click on the Execute button. Got
>>>>>>>>>>> "get_primary_keys() takes 1 positional argument but 2 were given" error.
>>>>>>>>>>> - In my opinion, we should hide the option to change the
>>>>>>>>>>> database connection for View/Edit Data.
>>>>>>>>>>> - If the user clicks on the same database connection
>>>>>>>>>>> multiple times then no need to change the backend connection and
>>>>>>>>>>> transaction id. Add validation at the backend, no action required in this
>>>>>>>>>>> case.
>>>>>>>>>>> - The role option is missing from the "connect to server"
>>>>>>>>>>> dialog.
>>>>>>>>>>> - The Password field should not be there on the "connect to
>>>>>>>>>>> server" dialog. Sometimes we saved the password so asking a password every
>>>>>>>>>>> time is not correct. Check the pgAdmin 3 behavior.
>>>>>>>>>>>
>>>>>>>>>>> Code review still remains.
>>>>>>>>>>>
>>>>>>>>>>> On Thu, Sep 17, 2020 at 4:15 PM Nikhil Mohite <
>>>>>>>>>>> [email protected]> wrote:
>>>>>>>>>>>
>>>>>>>>>>>> Hi Team,
>>>>>>>>>>>>
>>>>>>>>>>>> Regarding RM-3794 <https://redmine.postgresql.org/issues/3794;
>>>>>>>>>>>> allow the user to change the database connection from an open query tool:
>>>>>>>>>>>> I have implemented the feature and also added documentation for
>>>>>>>>>>>> it.
>>>>>>>>>>>>
>>>>>>>>>>>> PFA patch.
>>>>>>>>>>>>
>>>>>>>>>>>> --
>>>>>>>>>>>> *Thanks & Regards,*
>>>>>>>>>>>> *Nikhil Mohite*
>>>>>>>>>>>> *Software Engineer.*
>>>>>>>>>>>> *EDB Postgres* <https://www.enterprisedb.com/;
>>>>>>>>>>>> *Mob.No: +91-7798364578.*
>>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> --
>>>>>>>>>>> *Thanks & Regards*
>>>>>>>>>>> *Akshay Joshi*
>>>>>>>>>>> *pgAdmin Hacker | Sr. Software Architect*
>>>>>>>>>>> *EDB Postgres <http://edbpostgres.com>*
>>>>>>>>>>>
>>>>>>>>>>> *Mobile: +91 976-788-8246*
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>> --
>>>>>>>>> *Thanks & Regards*
>>>>>>>>> *Akshay Joshi*
>>>>>>>>> *pgAdmin Hacker | Sr. Software Architect*
>>>>>>>>> *EDB Postgres <http://edbpostgres.com>*
>>>>>>>>>
>>>>>>>>> *Mobile: +91 976-788-8246*
>>>>>>>>>
>>>>>>>>
>>>>>>>
>>>>>>> --
>>>>>>> *Thanks & Regards*
>>>>>>> *Akshay Joshi*
>>>>>>> *pgAdmin Hacker | Sr. Software Architect*
>>>>>>> *EDB Postgres <http://edbpostgres.com>*
>>>>>>>
>>>>>>> *Mobile: +91 976-788-8246*
>>>>>>>
>>>>>>
>>>>>
>>>>> --
>>>>> *Thanks & Regards*
>>>>> *Akshay Joshi*
>>>>> *pgAdmin Hacker | Sr. Software Architect*
>>>>> *EDB Postgres <http://edbpostgres.com>*
>>>>>
>>>>> *Mobile: +91 976-788-8246*
>>>>>
>>>>
>>>
>>> --
>>> Dave Page
>>> VP & Chief Architect, Database Infrastructure
>>> EDB: http://www.enterprisedb.com
>>>
>>> Blog: http://pgsnake.blogspot.com
>>> Twitter: @pgsnake
>>>
>>
>>
>> --
>> *Thanks & Regards*
>> *Akshay Joshi*
>> *pgAdmin Hacker | Sr. Software Architect*
>> *EDB Postgres <http://edbpostgres.com>*
>>
>> *Mobile: +91 976-788-8246*
>>
>
--
*Thanks & Regards*
*Akshay Joshi*
*pgAdmin Hacker | Sr. Software Architect*
*EDB Postgres <http://edbpostgres.com>*
*Mobile: +91 976-788-8246*
^ permalink raw reply [nested|flat] 18+ messages in thread
* Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab
2020-09-17 10:45 [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-09-21 11:54 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-09-28 05:50 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-09-28 12:28 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-09-28 14:01 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-09-29 06:01 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-10-01 05:12 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-10-01 08:01 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-10-06 11:51 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Murtuza Zabuawala <[email protected]>
2020-10-06 11:54 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Dave Page <[email protected]>
2020-10-06 12:49 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-10-07 06:41 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-10-08 06:09 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
@ 2020-10-21 05:38 ` Nikhil Mohite <[email protected]>
2020-10-21 09:24 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
0 siblings, 1 reply; 18+ messages in thread
From: Nikhil Mohite @ 2020-10-21 05:38 UTC (permalink / raw)
To: Akshay Joshi <[email protected]>; +Cc: pgadmin-hackers
Hi Akshay,
I have updated the existing implementation as per suggestions.
1. Show servers in server groups in the dropdown.
2. Current selected connection in the new connection dropdown is now
highlighted as selected.
3. Notification to the user before the change connection action.
4. If we connect to the server through a new connection dialog, the tree
will use the same connection and it will not create a new connection.
(In earlier implementation it was asking for the password even we have
connected from a new connection dialog.)
PFA patch
Regards,
Nikhil Mohite.
On Thu, Oct 8, 2020 at 11:39 AM Akshay Joshi <[email protected]>
wrote:
> Thanks, patch applied.
>
> On Wed, Oct 7, 2020 at 12:11 PM Nikhil Mohite <
> [email protected]> wrote:
>
>> Hi Akshay,
>>
>> I checked the implementation and found 2 locations which I missed in the
>> last patch to remove async: False.
>> I have removed all occurrences of async: False now also added missing
>> loader in required places.
>>
>> PFA updated the patch for the same.
>>
>> Regards,
>> Nikhil Mohite.
>>
>> On Tue, Oct 6, 2020 at 6:19 PM Akshay Joshi <
>> [email protected]> wrote:
>>
>>> Hi Nikhil
>>>
>>> Please verify and remove async = false wherever possible.
>>>
>>> On Tue, Oct 6, 2020 at 5:24 PM Dave Page <[email protected]>
>>> wrote:
>>>
>>>>
>>>>
>>>> On Tue, Oct 6, 2020 at 12:51 PM Murtuza Zabuawala <
>>>> [email protected]> wrote:
>>>>
>>>>> Hi Akshay,
>>>>>
>>>>> We have used aysnc=False in most ajax calls with this feature, It is
>>>>> causing UI hang in case of slow server response.
>>>>> You can try adding a time.sleep() call at the python side response and
>>>>> check the UI hang, I think we should avoid sync calls as much as possible.
>>>>>
>>>>
>>>> I consider a sync ajax call to be a bug.
>>>>
>>>>
>>>>>
>>>>>
>>>>> --
>>>>> Regards,
>>>>> Murtuza Zabuawala
>>>>> *EDB*
>>>>> *POWER TO POSTGRES*
>>>>> https://www.edbpostgres.com
>>>>>
>>>>>
>>>>> On Thu, Oct 1, 2020 at 1:31 PM Akshay Joshi <
>>>>> [email protected]> wrote:
>>>>>
>>>>>> Thanks, patch applied.
>>>>>>
>>>>>> On Thu, Oct 1, 2020 at 10:42 AM Nikhil Mohite <
>>>>>> [email protected]> wrote:
>>>>>>
>>>>>>> Hi Akshay,
>>>>>>>
>>>>>>> I have resolved the sonarQube issues, PFA updated patch for the same.
>>>>>>>
>>>>>>>
>>>>>>> Regards,
>>>>>>> Nikhil Mohite.
>>>>>>>
>>>>>>>
>>>>>>> On Tue, Sep 29, 2020 at 11:31 AM Akshay Joshi <
>>>>>>> [email protected]> wrote:
>>>>>>>
>>>>>>>> Hi Nikhil
>>>>>>>>
>>>>>>>> Your patch introduces 1 new Bug and 13 new code smells, please fix
>>>>>>>> those and resend the patch.
>>>>>>>>
>>>>>>>> On Mon, Sep 28, 2020 at 7:31 PM Nikhil Mohite <
>>>>>>>> [email protected]> wrote:
>>>>>>>>
>>>>>>>>> Hi Akshay,
>>>>>>>>>
>>>>>>>>> I have resolved code conflict issues and sonarqube issues.
>>>>>>>>> PFA updated patch.
>>>>>>>>>
>>>>>>>>> Regards,
>>>>>>>>> Nikhil Mohite.
>>>>>>>>>
>>>>>>>>> On Mon, Sep 28, 2020 at 5:58 PM Akshay Joshi <
>>>>>>>>> [email protected]> wrote:
>>>>>>>>>
>>>>>>>>>> Hi Nikhil
>>>>>>>>>>
>>>>>>>>>> The patch is not applying, rebase, and send it again. Please
>>>>>>>>>> check your code should not create any new SonarQube issues.
>>>>>>>>>>
>>>>>>>>>> On Mon, Sep 28, 2020 at 11:20 AM Nikhil Mohite <
>>>>>>>>>> [email protected]> wrote:
>>>>>>>>>>
>>>>>>>>>>> Hi Akshay,
>>>>>>>>>>>
>>>>>>>>>>> I have resolved all the review comments and also updated the
>>>>>>>>>>> test cases as per the new implementation.
>>>>>>>>>>>
>>>>>>>>>>> PFA updated patch.
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> On Mon, Sep 21, 2020 at 5:24 PM Akshay Joshi <
>>>>>>>>>>> [email protected]> wrote:
>>>>>>>>>>>
>>>>>>>>>>>> Hi Nikhil
>>>>>>>>>>>>
>>>>>>>>>>>> Following are the initial review comments:
>>>>>>>>>>>>
>>>>>>>>>>>> - Open View/Edit data on any table and click on the same
>>>>>>>>>>>> database connection and then click on the Execute button. Got
>>>>>>>>>>>> "get_primary_keys() takes 1 positional argument but 2 were given" error.
>>>>>>>>>>>> - In my opinion, we should hide the option to change the
>>>>>>>>>>>> database connection for View/Edit Data.
>>>>>>>>>>>> - If the user clicks on the same database connection
>>>>>>>>>>>> multiple times then no need to change the backend connection and
>>>>>>>>>>>> transaction id. Add validation at the backend, no action required in this
>>>>>>>>>>>> case.
>>>>>>>>>>>> - The role option is missing from the "connect to server"
>>>>>>>>>>>> dialog.
>>>>>>>>>>>> - The Password field should not be there on the "connect to
>>>>>>>>>>>> server" dialog. Sometimes we saved the password so asking a password every
>>>>>>>>>>>> time is not correct. Check the pgAdmin 3 behavior.
>>>>>>>>>>>>
>>>>>>>>>>>> Code review still remains.
>>>>>>>>>>>>
>>>>>>>>>>>> On Thu, Sep 17, 2020 at 4:15 PM Nikhil Mohite <
>>>>>>>>>>>> [email protected]> wrote:
>>>>>>>>>>>>
>>>>>>>>>>>>> Hi Team,
>>>>>>>>>>>>>
>>>>>>>>>>>>> Regarding RM-3794 <https://redmine.postgresql.org/issues/3794;
>>>>>>>>>>>>> allow the user to change the database connection from an open query tool:
>>>>>>>>>>>>> I have implemented the feature and also added documentation
>>>>>>>>>>>>> for it.
>>>>>>>>>>>>>
>>>>>>>>>>>>> PFA patch.
>>>>>>>>>>>>>
>>>>>>>>>>>>> --
>>>>>>>>>>>>> *Thanks & Regards,*
>>>>>>>>>>>>> *Nikhil Mohite*
>>>>>>>>>>>>> *Software Engineer.*
>>>>>>>>>>>>> *EDB Postgres* <https://www.enterprisedb.com/;
>>>>>>>>>>>>> *Mob.No: +91-7798364578.*
>>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>> --
>>>>>>>>>>>> *Thanks & Regards*
>>>>>>>>>>>> *Akshay Joshi*
>>>>>>>>>>>> *pgAdmin Hacker | Sr. Software Architect*
>>>>>>>>>>>> *EDB Postgres <http://edbpostgres.com>*
>>>>>>>>>>>>
>>>>>>>>>>>> *Mobile: +91 976-788-8246*
>>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> --
>>>>>>>>>> *Thanks & Regards*
>>>>>>>>>> *Akshay Joshi*
>>>>>>>>>> *pgAdmin Hacker | Sr. Software Architect*
>>>>>>>>>> *EDB Postgres <http://edbpostgres.com>*
>>>>>>>>>>
>>>>>>>>>> *Mobile: +91 976-788-8246*
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>
>>>>>>>> --
>>>>>>>> *Thanks & Regards*
>>>>>>>> *Akshay Joshi*
>>>>>>>> *pgAdmin Hacker | Sr. Software Architect*
>>>>>>>> *EDB Postgres <http://edbpostgres.com>*
>>>>>>>>
>>>>>>>> *Mobile: +91 976-788-8246*
>>>>>>>>
>>>>>>>
>>>>>>
>>>>>> --
>>>>>> *Thanks & Regards*
>>>>>> *Akshay Joshi*
>>>>>> *pgAdmin Hacker | Sr. Software Architect*
>>>>>> *EDB Postgres <http://edbpostgres.com>*
>>>>>>
>>>>>> *Mobile: +91 976-788-8246*
>>>>>>
>>>>>
>>>>
>>>> --
>>>> Dave Page
>>>> VP & Chief Architect, Database Infrastructure
>>>> EDB: http://www.enterprisedb.com
>>>>
>>>> Blog: http://pgsnake.blogspot.com
>>>> Twitter: @pgsnake
>>>>
>>>
>>>
>>> --
>>> *Thanks & Regards*
>>> *Akshay Joshi*
>>> *pgAdmin Hacker | Sr. Software Architect*
>>> *EDB Postgres <http://edbpostgres.com>*
>>>
>>> *Mobile: +91 976-788-8246*
>>>
>>
>
> --
> *Thanks & Regards*
> *Akshay Joshi*
> *pgAdmin Hacker | Sr. Software Architect*
> *EDB Postgres <http://edbpostgres.com>*
>
> *Mobile: +91 976-788-8246*
>
Attachments:
[application/octet-stream] RM_3794_review_comments.patch (23.1K, 3-RM_3794_review_comments.patch)
download | inline diff:
diff --git a/web/pgadmin/browser/server_groups/servers/__init__.py b/web/pgadmin/browser/server_groups/servers/__init__.py
index 2630d1e..ab4bea4 100644
--- a/web/pgadmin/browser/server_groups/servers/__init__.py
+++ b/web/pgadmin/browser/server_groups/servers/__init__.py
@@ -1310,7 +1310,8 @@ class ServerNode(PGChildNodeView):
# Connect the Server
manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)
- manager.update(server)
+ if not manager.connection().connected():
+ manager.update(server)
conn = manager.connection()
# Get enc key
diff --git a/web/pgadmin/static/js/sqleditor/new_connection_dialog.js b/web/pgadmin/static/js/sqleditor/new_connection_dialog.js
index 10850e1..b19a50a 100644
--- a/web/pgadmin/static/js/sqleditor/new_connection_dialog.js
+++ b/web/pgadmin/static/js/sqleditor/new_connection_dialog.js
@@ -243,7 +243,6 @@ let NewConnectionDialog = {
'title': _.escape(tab_title),
'user': newConnCollectionModel['user'],
'role': newConnCollectionModel['role'],
- 'password': response.password,
'server_name': _.escape(response.server_name),
'database_name': _.escape(selected_database_name),
'is_selected': false,
@@ -260,13 +259,12 @@ let NewConnectionDialog = {
setTimeout(function(){
Alertify.newConnectionDialog('Connect to server.').resizeTo(pgAdmin.Browser.stdW.md,pgAdmin.Browser.stdH.md);
}, 500);
- }).fail(function(error) {
+ }).fail(function() {
Alertify.alert().setting({
'title': gettext('Connection lost'),
'label':gettext('Ok'),
'message': gettext('Connection to the server has been lost.'),
'onok': function(){
- alert(error);
//Close the window after connection is lost
window.close();
},
diff --git a/web/pgadmin/static/js/sqleditor/new_connection_dialog_model.js b/web/pgadmin/static/js/sqleditor/new_connection_dialog_model.js
index 09b2979..1dd32b7 100644
--- a/web/pgadmin/static/js/sqleditor/new_connection_dialog_model.js
+++ b/web/pgadmin/static/js/sqleditor/new_connection_dialog_model.js
@@ -78,7 +78,6 @@ export default function newConnectionDialogModel(response, sgid, sid) {
server: parseInt(sid),
database: null,
user: null,
- password: null,
server_name: server_name,
database_name: database_name,
},
@@ -92,7 +91,63 @@ export default function newConnectionDialogModel(response, sgid, sid) {
select2: {
allowClear: false,
},
+ transform: function(data) {
+ let group_template_options = [];
+ for (let key in data) {
+ if (data.hasOwnProperty(key)) {
+ group_template_options.push({'group': key, 'optval': data[key]});
+ }
+ }
+ return group_template_options;
+ },
control: Backform.Select2Control.extend({
+ template: _.template([
+ '<% if(label == false) {} else {%>',
+ ' <label class="<%=Backform.controlLabelClassName%>"><%=label%></label>',
+ '<% }%>',
+ '<div class="<%=controlsClassName%>">',
+ ' <select class="<%=Backform.controlClassName%> <%=extraClasses.join(\' \')%>"',
+ ' name="<%=name%>" value="<%-value%>" <%=disabled ? "disabled" : ""%>',
+ ' <%=required ? "required" : ""%><%= select2.multiple ? " multiple>" : ">" %>',
+ ' <%=select2.first_empty ? " <option></option>" : ""%>',
+ ' <% for (var i=0; i < options.length; i++) {%>',
+ ' <% if (options[i].group) { %>',
+ ' <% var group = options[i].group; %>',
+ ' <% if (options[i].optval) { %> <% var option_length = options[i].optval.length; %>',
+ ' <optgroup label="<%=group%>">',
+ ' <% for (var subindex=0; subindex < option_length; subindex++) {%>',
+ ' <% var option = options[i].optval[subindex]; %>',
+ ' <option ',
+ ' <% if (option.image) { %> data-image=<%=option.image%> <%}%>',
+ ' <% if (option.connected) { %> data-connected=connected <%}%>',
+ ' value=<%- formatter.fromRaw(option.value) %>',
+ ' <% if (option.selected) {%>selected="selected"<%} else {%>',
+ ' <% if (!select2.multiple && option.value === rawValue) {%>selected="selected"<%}%>',
+ ' <% if (select2.multiple && rawValue && rawValue.indexOf(option.value) != -1){%>selected="selected" data-index="rawValue.indexOf(option.value)"<%}%>',
+ ' <%}%>',
+ ' <%= disabled ? "disabled" : ""%>><%-option.label%></option>',
+ ' <%}%>',
+ ' </optgroup>',
+ ' <%}%>',
+ ' <%} else {%>',
+ ' <% var option = options[i]; %>',
+ ' <option ',
+ ' <% if (option.image) { %> data-image=<%=option.image%> <%}%>',
+ ' <% if (option.connected) { %> data-connected=connected <%}%>',
+ ' value=<%- formatter.fromRaw(option.value) %>',
+ ' <% if (option.selected) {%>selected="selected"<%} else {%>',
+ ' <% if (!select2.multiple && option.value === rawValue) {%>selected="selected"<%}%>',
+ ' <% if (select2.multiple && rawValue && rawValue.indexOf(option.value) != -1){%>selected="selected" data-index="rawValue.indexOf(option.value)"<%}%>',
+ ' <%}%>',
+ ' <%= disabled ? "disabled" : ""%>><%-option.label%></option>',
+ ' <%}%>',
+ ' <%}%>',
+ ' </select>',
+ ' <% if (helpMessage && helpMessage.length) { %>',
+ ' <span class="<%=Backform.helpMessageClassName%>"><%=helpMessage%></span>',
+ ' <% } %>',
+ '</div>',
+ ].join('\n')),
connect: function(self) {
let local_self = self;
@@ -147,11 +202,14 @@ export default function newConnectionDialogModel(response, sgid, sid) {
local_self.model.attributes.user = null;
local_self.model.attributes.role = null;
Backform.Select2Control.prototype.onChange.apply(local_self, arguments);
- response.server_list.forEach(function(obj){
- if(obj.id==self.model.changed.server) {
- response.server_name = obj.name;
- }
+ Object.keys(response.server_list).forEach(key => {
+ response.server_list[key].forEach(option => {
+ if (option.value == parseInt(sid)) {
+ response.server_name = option.label;
+ }
+ });
});
+
loadingDiv.addClass('d-none');
alertify.connectServer().destroy();
})
@@ -160,8 +218,6 @@ export default function newConnectionDialogModel(response, sgid, sid) {
alertify.connectServer().destroy();
alertify.connectServer('Connect to server', xhr.responseJSON.result, local_self.getValueFromDOM());
});
- } else {
- response.password = $('#password').val();
}
} else {
local_self.model.attributes.database = null;
@@ -178,6 +234,19 @@ export default function newConnectionDialogModel(response, sgid, sid) {
render: function() {
let self = this;
self.connect(self);
+ Object.keys(response.server_list).forEach(key => {
+ response.server_list[key].forEach(option => {
+ if (option.value == parseInt(sid)) {
+ response.server_name = option.label;
+ }
+ });
+ });
+ var transform = self.field.get('transform') || self.defaults.transform;
+ if (transform && _.isFunction(transform)) {
+ self.field.set('options', transform.bind(self, response.server_list));
+ } else {
+ self.field.set('options', response.server_list);
+ }
return Backform.Select2Control.prototype.render.apply(self, arguments);
},
onChange: function() {
@@ -200,10 +269,12 @@ export default function newConnectionDialogModel(response, sgid, sid) {
},
}).done(function () {
Backform.Select2Control.prototype.onChange.apply(self, arguments);
- response.server_list.forEach(function(obj){
- if(obj.id==self.model.changed.server) {
- response.server_name = obj.name;
- }
+ Object.keys(response.server_list).forEach(key => {
+ response.server_list[key].forEach(option => {
+ if (option.value == parseInt(sid)) {
+ response.server_name = option.label;
+ }
+ });
});
loadingDiv.addClass('d-none');
}).fail(function(xhr){
@@ -213,17 +284,6 @@ export default function newConnectionDialogModel(response, sgid, sid) {
},
}),
- options: function() {
- return _.map(response.server_list, (obj) => {
- if (obj.id == parseInt(sid))
- response.server_name = obj.name;
-
- return {
- value: obj.id,
- label: obj.name,
- };
- });
- },
},
{
id: 'database',
@@ -302,27 +362,6 @@ export default function newConnectionDialogModel(response, sgid, sid) {
url: 'sqleditor.get_new_connection_role',
disabled: false,
},
- /*{
- id: 'password',
- name: 'password',
- label: gettext('Password'tools/sqleditor/__init__.py),
- type: 'password',
- editable: true,
- disabled: true,
- deps: ['user'],
- control: Backform.InputControl.extend({
- render: function() {
- let self = this;
- self.model.attributes.password = null;
- Backform.InputControl.prototype.render.apply(self, arguments);
- return self;
- },
- onChange: function() {
- let self = this;
- Backform.InputControl.prototype.onChange.apply(self, arguments);
- },
- }),
- },*/
],
validate: function() {
let msg = null;
@@ -336,11 +375,6 @@ export default function newConnectionDialogModel(response, sgid, sid) {
this.errorModel.set('user', msg);
return msg;
}
- /*else if((this.attributes.password == '' || _.isUndefined(this.get('password')) || _.isNull(this.get('password')))) {
- msg = gettext('Please enter password');
- this.errorModel.set('password', msg);
- return msg;
- }*/
return null;
},
});
diff --git a/web/pgadmin/tools/debugger/__init__.py b/web/pgadmin/tools/debugger/__init__.py
index 1949709..422a8ae 100644
--- a/web/pgadmin/tools/debugger/__init__.py
+++ b/web/pgadmin/tools/debugger/__init__.py
@@ -98,6 +98,18 @@ class DebuggerModule(PgAdminModule):
)
)
+ self.tab_title = self.preference.register(
+ 'display', 'debugger_tab_title_placeholder',
+ gettext("Debugger tab title placeholder"),
+ 'text', '%FUNCTION%/%SCHEMA%/%DATABASE%',
+ category_label=PREF_LABEL_DISPLAY,
+ help_str=gettext(
+ 'Supported placeholders: FUNCTION, SCHEMA and DATABASE. '
+ 'You can also provide any string with or '
+ 'without placeholders'
+ )
+ )
+
self.preference.register(
'keyboard_shortcuts', 'btn_start',
gettext('Accesskey (Continue/Start)'), 'keyboardshortcut',
diff --git a/web/pgadmin/tools/sqleditor/__init__.py b/web/pgadmin/tools/sqleditor/__init__.py
index 9da8842..f9fb26f 100644
--- a/web/pgadmin/tools/sqleditor/__init__.py
+++ b/web/pgadmin/tools/sqleditor/__init__.py
@@ -46,7 +46,7 @@ from pgadmin.tools.sqleditor.utils.macros import get_macros,\
get_user_macros, set_macros
from pgadmin.utils.constants import MIMETYPE_APP_JS, \
SERVER_CONNECTION_CLOSED, ERROR_MSG_TRANS_ID_NOT_FOUND, ERROR_FETCHING_DATA
-from pgadmin.model import Server
+from pgadmin.model import Server, ServerGroup
from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry
MODULE_NAME = 'sqleditor'
@@ -1489,10 +1489,14 @@ def get_new_connection_data(sgid, sid=None):
:extract_sql_from_network_parameters,
"""
try:
- # if sid and not did:
+ server_groups = ServerGroup.query.all()
+ server_group_data = {server_group.name: [] for server_group in
+ server_groups}
servers = Server.query.all()
- server_list = [
- {'name': server.serialize['name'], "id": server.serialize['id']}
+
+ [server_group_data[server.servers.name].append(
+ {'label': server.serialize['name'],
+ "value": server.serialize['id']})
for server in servers]
msg = "Success"
@@ -1501,7 +1505,7 @@ def get_new_connection_data(sgid, sid=None):
'status': True,
'msg': msg,
'result': {
- 'server_list': server_list
+ 'server_list': server_group_data
}
}
)
diff --git a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
index d3a2d25..905d782 100644
--- a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
+++ b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
@@ -173,8 +173,13 @@ define('tools.querytool', [
var dropdownElement = document.getElementById('connections-list');
dropdownElement.innerHTML = '';
data_list.forEach((option, index) => {
- $('#connections-list').append('<li class="connection-list-item" data-index='+ index +'><a class="dropdown-item" href="#" tabindex="0">'+ option.title +'</a></li>');
-
+ var opt = '';
+ if ('is_selected' in option && option['is_selected']) {
+ opt = '<li class="connection-list-item selected-connection" data-index='+ index +'><a class="dropdown-item" href="#" tabindex="0">'+ option.title +'</a></li>';
+ } else {
+ opt = '<li class="connection-list-item" data-index='+ index +'><a class="dropdown-item" href="#" tabindex="0">'+ option.title +'</a></li>';
+ }
+ $('#connections-list').append(opt);
});
var self = this;
$('.connection-list-item').click(function() {
@@ -2131,7 +2136,6 @@ define('tools.querytool', [
on_change_connection: function(connection_details, ref) {
if(!connection_details['is_selected']) {
var self = this;
- self.set_selected_option(connection_details);
var loadingDiv = null;
var msgDiv = null;
if(ref){
@@ -2145,66 +2149,78 @@ define('tools.querytool', [
msgDiv = loadingDiv.find('.sql-editor-busy-text');
}
- $.ajax({
- url: url_for('datagrid.update_query_tool_connection', {
- 'trans_id': self.transId,
- 'sgid': connection_details['server_group'],
- 'sid': connection_details['server'],
- 'did': connection_details['database'],
- }),
- method: 'POST',
- contentType: 'application/json',
- data: JSON.stringify(connection_details),
- })
- .done(function(res) {
- if(res.success) {
- self.transId = res.data.tran_id;
- self.handler.transId = res.data.tran_id;
- self.handler.url_params = {
- 'did': connection_details['database'],
- 'is_query_tool': self.handler.url_params.is_query_tool,
- 'server_type': self.handler.url_params.server_type,
+ alertify.confirm(gettext('Change connection.'),
+ gettext('Change connection will lose all non committed changes for current connection, do you want to continue?'),
+ function() {
+ self.set_selected_option(connection_details);
+ $.ajax({
+ url: url_for('datagrid.update_query_tool_connection', {
+ 'trans_id': self.transId,
'sgid': connection_details['server_group'],
'sid': connection_details['server'],
- 'title': connection_details['title'],
- };
- self.set_editor_title(_.unescape(self.handler.url_params.title));
- self.handler.setTitle(_.unescape(self.handler.url_params.title));
- let success_msg = connection_details['server_name'] + '/' + connection_details['database_name'] + '- Database connected';
- alertify.success(success_msg);
- if(ref){
- let connection_data = {
- 'server_group': self.handler.url_params.sgid,
- 'server': connection_details['server'],
- 'database': connection_details['database'],
- 'user': connection_details['user'],
- 'title': connection_details['title'],
- 'role': connection_details['role'],
- 'password': connection_details['password'],
- 'is_allow_new_connection': true,
- 'database_name': connection_details['database_name'],
- 'server_name': connection_details['server_name'],
- 'is_selected': true,
- };
- self.connection_list.unshift(connection_data);
- self.render_connection(self.connection_list);
- loadingDiv.addClass('d-none');
- alertify.newConnectionDialog().destroy();
- ref.close();
- } else {
- loadingDiv.addClass('d-none');
- }
- }
- return true;
- })
- .fail(function(xhr) {
+ 'did': connection_details['database'],
+ }),
+ method: 'POST',
+ contentType: 'application/json',
+ data: JSON.stringify(connection_details),
+ })
+ .done(function(res) {
+ if(res.success) {
+ self.transId = res.data.tran_id;
+ self.handler.transId = res.data.tran_id;
+ self.handler.url_params = {
+ 'did': connection_details['database'],
+ 'is_query_tool': self.handler.url_params.is_query_tool,
+ 'server_type': self.handler.url_params.server_type,
+ 'sgid': connection_details['server_group'],
+ 'sid': connection_details['server'],
+ 'title': connection_details['title'],
+ };
+ self.set_editor_title(_.unescape(self.handler.url_params.title));
+ self.handler.setTitle(_.unescape(self.handler.url_params.title));
+ let success_msg = connection_details['server_name'] + '/' + connection_details['database_name'] + '- Database connected';
+ alertify.success(success_msg);
+ if(ref){
+ let connection_data = {
+ 'server_group': self.handler.url_params.sgid,
+ 'server': connection_details['server'],
+ 'database': connection_details['database'],
+ 'user': connection_details['user'],
+ 'title': connection_details['title'],
+ 'role': connection_details['role'],
+ 'is_allow_new_connection': true,
+ 'database_name': connection_details['database_name'],
+ 'server_name': connection_details['server_name'],
+ 'is_selected': true,
+ };
+ self.connection_list.unshift(connection_data);
+ self.render_connection(self.connection_list);
+ loadingDiv.addClass('d-none');
+ alertify.newConnectionDialog().destroy();
+ ref.close();
+ } else {
+ loadingDiv.addClass('d-none');
+ }
+ }
+ return true;
+ })
+ .fail(function(xhr) {
+ if(xhr.status == 428) {
+ alertify.connectServer('Connect to server', xhr.responseJSON.result, connection_details['server'], false);
+ } else {
+ alertify.error(xhr.responseJSON['errormsg']);
+ }
+ });
+ },
+ function() {
loadingDiv.addClass('d-none');
- if(xhr.status == 428) {
- alertify.connectServer('Connect to server', xhr.responseJSON.result, connection_details['server'], false);
- } else {
- alertify.error(xhr.responseJSON['errormsg']);
- }
- });
+ alertify.newConnectionDialog().destroy();
+ return true;
+ }
+ ).set('labels', {
+ ok: gettext('Yes'),
+ cancel: gettext('No'),
+ });
}
},
});
diff --git a/web/pgadmin/tools/sqleditor/static/scss/_sqleditor.scss b/web/pgadmin/tools/sqleditor/static/scss/_sqleditor.scss
index 7fc576a..9d5ba4d 100644
--- a/web/pgadmin/tools/sqleditor/static/scss/_sqleditor.scss
+++ b/web/pgadmin/tools/sqleditor/static/scss/_sqleditor.scss
@@ -371,6 +371,9 @@ div.strikeout:after {
height: 100%;
}
+.selected-connection {
+ background-color: $color-primary-light;
+}
/* Setting it to hardcoded white as the SVG generated is having white bg
* Need to check what can be done.
diff --git a/web/pgadmin/tools/sqleditor/utils/query_tool_preferences.py b/web/pgadmin/tools/sqleditor/utils/query_tool_preferences.py
index 76523b1..642a1eb 100644
--- a/web/pgadmin/tools/sqleditor/utils/query_tool_preferences.py
+++ b/web/pgadmin/tools/sqleditor/utils/query_tool_preferences.py
@@ -326,6 +326,29 @@ def register_query_tool_preferences(self):
)
)
+ self.qt_tab_title = self.preference.register(
+ 'display', 'qt_tab_title_placeholder',
+ gettext("Query tool tab title placeholder"),
+ 'text', '%DATABASE%/%USERNAME%@%SERVER%',
+ category_label=PREF_LABEL_DISPLAY,
+ help_str=gettext(
+ 'Supported placeholders: DATABASE, USERNAME and SERVER. '
+ 'You can also provide any string with or without placeholders.'
+ )
+ )
+
+ self.ve_edt_tab_title = self.preference.register(
+ 'display', 'vw_edt_tab_title_placeholder',
+ gettext("View/Edit tab title placeholder"),
+ 'text', '%SCHEMA%.%TABLE%/%DATABASE%/%USERNAME%@%SERVER%',
+ category_label=PREF_LABEL_DISPLAY,
+ help_str=gettext(
+ 'Supported placeholders: SCHEMA, TABLE, DATABASE, USERNAME and '
+ 'SERVER. You can also provide any string with or '
+ 'without placeholders.'
+ )
+ )
+
self.connection_status = self.preference.register(
'display', 'connection_status_fetch_time',
gettext("Connection status refresh rate"), 'integer', 2,
^ permalink raw reply [nested|flat] 18+ messages in thread
* Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab
2020-09-17 10:45 [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-09-21 11:54 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-09-28 05:50 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-09-28 12:28 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-09-28 14:01 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-09-29 06:01 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-10-01 05:12 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-10-01 08:01 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-10-06 11:51 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Murtuza Zabuawala <[email protected]>
2020-10-06 11:54 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Dave Page <[email protected]>
2020-10-06 12:49 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-10-07 06:41 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-10-08 06:09 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-10-21 05:38 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
@ 2020-10-21 09:24 ` Akshay Joshi <[email protected]>
2020-10-21 11:15 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
0 siblings, 1 reply; 18+ messages in thread
From: Akshay Joshi @ 2020-10-21 09:24 UTC (permalink / raw)
To: Nikhil Mohite <[email protected]>; +Cc: pgadmin-hackers
Hi Nikhil
Following are the review comments:
- Connect to any server from the browser tree. Open the query tool and
then open the new connection dialog. Click on the "OK" button without
changing any field. It shows the popup for "Change connection" which should
not be raised because the server is the same.
- In the above scenario, if you click on the Yes button it is showing
a duplicate entry for the same server.
- The server name is not getting changed when we connect to any
other server from the new connection. Changes needed in alertify message,
tab title, and a combo box.
- Remove the "." from the "Change connection." title.
- Change the string "Change connection will lose all non committed
changes for current connection, do you want to continue?" to "*By
changing the connection you will lose all your unsaved data for the current
connection.*
*Do you want to continue?*"
Please fix the above changes and send the patch again.
On Wed, Oct 21, 2020 at 11:08 AM Nikhil Mohite <
[email protected]> wrote:
> Hi Akshay,
>
> I have updated the existing implementation as per suggestions.
> 1. Show servers in server groups in the dropdown.
> 2. Current selected connection in the new connection dropdown is now
> highlighted as selected.
> 3. Notification to the user before the change connection action.
> 4. If we connect to the server through a new connection dialog, the tree
> will use the same connection and it will not create a new connection.
> (In earlier implementation it was asking for the password even we have
> connected from a new connection dialog.)
>
> PFA patch
>
> Regards,
> Nikhil Mohite.
>
> On Thu, Oct 8, 2020 at 11:39 AM Akshay Joshi <
> [email protected]> wrote:
>
>> Thanks, patch applied.
>>
>> On Wed, Oct 7, 2020 at 12:11 PM Nikhil Mohite <
>> [email protected]> wrote:
>>
>>> Hi Akshay,
>>>
>>> I checked the implementation and found 2 locations which I missed in the
>>> last patch to remove async: False.
>>> I have removed all occurrences of async: False now also added missing
>>> loader in required places.
>>>
>>> PFA updated the patch for the same.
>>>
>>> Regards,
>>> Nikhil Mohite.
>>>
>>> On Tue, Oct 6, 2020 at 6:19 PM Akshay Joshi <
>>> [email protected]> wrote:
>>>
>>>> Hi Nikhil
>>>>
>>>> Please verify and remove async = false wherever possible.
>>>>
>>>> On Tue, Oct 6, 2020 at 5:24 PM Dave Page <[email protected]>
>>>> wrote:
>>>>
>>>>>
>>>>>
>>>>> On Tue, Oct 6, 2020 at 12:51 PM Murtuza Zabuawala <
>>>>> [email protected]> wrote:
>>>>>
>>>>>> Hi Akshay,
>>>>>>
>>>>>> We have used aysnc=False in most ajax calls with this feature, It is
>>>>>> causing UI hang in case of slow server response.
>>>>>> You can try adding a time.sleep() call at the python side response
>>>>>> and check the UI hang, I think we should avoid sync calls as much as
>>>>>> possible.
>>>>>>
>>>>>
>>>>> I consider a sync ajax call to be a bug.
>>>>>
>>>>>
>>>>>>
>>>>>>
>>>>>> --
>>>>>> Regards,
>>>>>> Murtuza Zabuawala
>>>>>> *EDB*
>>>>>> *POWER TO POSTGRES*
>>>>>> https://www.edbpostgres.com
>>>>>>
>>>>>>
>>>>>> On Thu, Oct 1, 2020 at 1:31 PM Akshay Joshi <
>>>>>> [email protected]> wrote:
>>>>>>
>>>>>>> Thanks, patch applied.
>>>>>>>
>>>>>>> On Thu, Oct 1, 2020 at 10:42 AM Nikhil Mohite <
>>>>>>> [email protected]> wrote:
>>>>>>>
>>>>>>>> Hi Akshay,
>>>>>>>>
>>>>>>>> I have resolved the sonarQube issues, PFA updated patch for the
>>>>>>>> same.
>>>>>>>>
>>>>>>>>
>>>>>>>> Regards,
>>>>>>>> Nikhil Mohite.
>>>>>>>>
>>>>>>>>
>>>>>>>> On Tue, Sep 29, 2020 at 11:31 AM Akshay Joshi <
>>>>>>>> [email protected]> wrote:
>>>>>>>>
>>>>>>>>> Hi Nikhil
>>>>>>>>>
>>>>>>>>> Your patch introduces 1 new Bug and 13 new code smells, please fix
>>>>>>>>> those and resend the patch.
>>>>>>>>>
>>>>>>>>> On Mon, Sep 28, 2020 at 7:31 PM Nikhil Mohite <
>>>>>>>>> [email protected]> wrote:
>>>>>>>>>
>>>>>>>>>> Hi Akshay,
>>>>>>>>>>
>>>>>>>>>> I have resolved code conflict issues and sonarqube issues.
>>>>>>>>>> PFA updated patch.
>>>>>>>>>>
>>>>>>>>>> Regards,
>>>>>>>>>> Nikhil Mohite.
>>>>>>>>>>
>>>>>>>>>> On Mon, Sep 28, 2020 at 5:58 PM Akshay Joshi <
>>>>>>>>>> [email protected]> wrote:
>>>>>>>>>>
>>>>>>>>>>> Hi Nikhil
>>>>>>>>>>>
>>>>>>>>>>> The patch is not applying, rebase, and send it again. Please
>>>>>>>>>>> check your code should not create any new SonarQube issues.
>>>>>>>>>>>
>>>>>>>>>>> On Mon, Sep 28, 2020 at 11:20 AM Nikhil Mohite <
>>>>>>>>>>> [email protected]> wrote:
>>>>>>>>>>>
>>>>>>>>>>>> Hi Akshay,
>>>>>>>>>>>>
>>>>>>>>>>>> I have resolved all the review comments and also updated the
>>>>>>>>>>>> test cases as per the new implementation.
>>>>>>>>>>>>
>>>>>>>>>>>> PFA updated patch.
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>> On Mon, Sep 21, 2020 at 5:24 PM Akshay Joshi <
>>>>>>>>>>>> [email protected]> wrote:
>>>>>>>>>>>>
>>>>>>>>>>>>> Hi Nikhil
>>>>>>>>>>>>>
>>>>>>>>>>>>> Following are the initial review comments:
>>>>>>>>>>>>>
>>>>>>>>>>>>> - Open View/Edit data on any table and click on the same
>>>>>>>>>>>>> database connection and then click on the Execute button. Got
>>>>>>>>>>>>> "get_primary_keys() takes 1 positional argument but 2 were given" error.
>>>>>>>>>>>>> - In my opinion, we should hide the option to change the
>>>>>>>>>>>>> database connection for View/Edit Data.
>>>>>>>>>>>>> - If the user clicks on the same database connection
>>>>>>>>>>>>> multiple times then no need to change the backend connection and
>>>>>>>>>>>>> transaction id. Add validation at the backend, no action required in this
>>>>>>>>>>>>> case.
>>>>>>>>>>>>> - The role option is missing from the "connect to server"
>>>>>>>>>>>>> dialog.
>>>>>>>>>>>>> - The Password field should not be there on the "connect
>>>>>>>>>>>>> to server" dialog. Sometimes we saved the password so asking a password
>>>>>>>>>>>>> every time is not correct. Check the pgAdmin 3 behavior.
>>>>>>>>>>>>>
>>>>>>>>>>>>> Code review still remains.
>>>>>>>>>>>>>
>>>>>>>>>>>>> On Thu, Sep 17, 2020 at 4:15 PM Nikhil Mohite <
>>>>>>>>>>>>> [email protected]> wrote:
>>>>>>>>>>>>>
>>>>>>>>>>>>>> Hi Team,
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> Regarding RM-3794
>>>>>>>>>>>>>> <https://redmine.postgresql.org/issues/3794; allow the user
>>>>>>>>>>>>>> to change the database connection from an open query tool:
>>>>>>>>>>>>>> I have implemented the feature and also added documentation
>>>>>>>>>>>>>> for it.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> PFA patch.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> --
>>>>>>>>>>>>>> *Thanks & Regards,*
>>>>>>>>>>>>>> *Nikhil Mohite*
>>>>>>>>>>>>>> *Software Engineer.*
>>>>>>>>>>>>>> *EDB Postgres* <https://www.enterprisedb.com/;
>>>>>>>>>>>>>> *Mob.No: +91-7798364578.*
>>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>> --
>>>>>>>>>>>>> *Thanks & Regards*
>>>>>>>>>>>>> *Akshay Joshi*
>>>>>>>>>>>>> *pgAdmin Hacker | Sr. Software Architect*
>>>>>>>>>>>>> *EDB Postgres <http://edbpostgres.com>*
>>>>>>>>>>>>>
>>>>>>>>>>>>> *Mobile: +91 976-788-8246*
>>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> --
>>>>>>>>>>> *Thanks & Regards*
>>>>>>>>>>> *Akshay Joshi*
>>>>>>>>>>> *pgAdmin Hacker | Sr. Software Architect*
>>>>>>>>>>> *EDB Postgres <http://edbpostgres.com>*
>>>>>>>>>>>
>>>>>>>>>>> *Mobile: +91 976-788-8246*
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>> --
>>>>>>>>> *Thanks & Regards*
>>>>>>>>> *Akshay Joshi*
>>>>>>>>> *pgAdmin Hacker | Sr. Software Architect*
>>>>>>>>> *EDB Postgres <http://edbpostgres.com>*
>>>>>>>>>
>>>>>>>>> *Mobile: +91 976-788-8246*
>>>>>>>>>
>>>>>>>>
>>>>>>>
>>>>>>> --
>>>>>>> *Thanks & Regards*
>>>>>>> *Akshay Joshi*
>>>>>>> *pgAdmin Hacker | Sr. Software Architect*
>>>>>>> *EDB Postgres <http://edbpostgres.com>*
>>>>>>>
>>>>>>> *Mobile: +91 976-788-8246*
>>>>>>>
>>>>>>
>>>>>
>>>>> --
>>>>> Dave Page
>>>>> VP & Chief Architect, Database Infrastructure
>>>>> EDB: http://www.enterprisedb.com
>>>>>
>>>>> Blog: http://pgsnake.blogspot.com
>>>>> Twitter: @pgsnake
>>>>>
>>>>
>>>>
>>>> --
>>>> *Thanks & Regards*
>>>> *Akshay Joshi*
>>>> *pgAdmin Hacker | Sr. Software Architect*
>>>> *EDB Postgres <http://edbpostgres.com>*
>>>>
>>>> *Mobile: +91 976-788-8246*
>>>>
>>>
>>
>> --
>> *Thanks & Regards*
>> *Akshay Joshi*
>> *pgAdmin Hacker | Sr. Software Architect*
>> *EDB Postgres <http://edbpostgres.com>*
>>
>> *Mobile: +91 976-788-8246*
>>
>
--
*Thanks & Regards*
*Akshay Joshi*
*pgAdmin Hacker | Sr. Software Architect*
*EDB Postgres <http://edbpostgres.com>*
*Mobile: +91 976-788-8246*
^ permalink raw reply [nested|flat] 18+ messages in thread
* Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab
2020-09-17 10:45 [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-09-21 11:54 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-09-28 05:50 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-09-28 12:28 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-09-28 14:01 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-09-29 06:01 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-10-01 05:12 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-10-01 08:01 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-10-06 11:51 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Murtuza Zabuawala <[email protected]>
2020-10-06 11:54 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Dave Page <[email protected]>
2020-10-06 12:49 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-10-07 06:41 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-10-08 06:09 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-10-21 05:38 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-10-21 09:24 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
@ 2020-10-21 11:15 ` Nikhil Mohite <[email protected]>
2020-10-21 11:49 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
0 siblings, 1 reply; 18+ messages in thread
From: Nikhil Mohite @ 2020-10-21 11:15 UTC (permalink / raw)
To: Akshay Joshi <[email protected]>; +Cc: pgadmin-hackers
Hi Akshay,
I have fixed the review comments, PFA the updated patch for the same.
Regards,
Nikhil Mohite.
On Wed, Oct 21, 2020 at 2:55 PM Akshay Joshi <[email protected]>
wrote:
> Hi Nikhil
>
> Following are the review comments:
>
> - Connect to any server from the browser tree. Open the query tool and
> then open the new connection dialog. Click on the "OK" button without
> changing any field. It shows the popup for "Change connection" which should
> not be raised because the server is the same.
> - In the above scenario, if you click on the Yes button it is showing
> a duplicate entry for the same server.
> - The server name is not getting changed when we connect to any
> other server from the new connection. Changes needed in alertify message,
> tab title, and a combo box.
> - Remove the "." from the "Change connection." title.
> - Change the string "Change connection will lose all non committed
> changes for current connection, do you want to continue?" to "*By
> changing the connection you will lose all your unsaved data for the current
> connection.*
> *Do you want to continue?*"
>
> Please fix the above changes and send the patch again.
>
> On Wed, Oct 21, 2020 at 11:08 AM Nikhil Mohite <
> [email protected]> wrote:
>
>> Hi Akshay,
>>
>> I have updated the existing implementation as per suggestions.
>> 1. Show servers in server groups in the dropdown.
>> 2. Current selected connection in the new connection dropdown is now
>> highlighted as selected.
>> 3. Notification to the user before the change connection action.
>> 4. If we connect to the server through a new connection dialog, the tree
>> will use the same connection and it will not create a new connection.
>> (In earlier implementation it was asking for the password even we have
>> connected from a new connection dialog.)
>>
>> PFA patch
>>
>> Regards,
>> Nikhil Mohite.
>>
>> On Thu, Oct 8, 2020 at 11:39 AM Akshay Joshi <
>> [email protected]> wrote:
>>
>>> Thanks, patch applied.
>>>
>>> On Wed, Oct 7, 2020 at 12:11 PM Nikhil Mohite <
>>> [email protected]> wrote:
>>>
>>>> Hi Akshay,
>>>>
>>>> I checked the implementation and found 2 locations which I missed in
>>>> the last patch to remove async: False.
>>>> I have removed all occurrences of async: False now also added missing
>>>> loader in required places.
>>>>
>>>> PFA updated the patch for the same.
>>>>
>>>> Regards,
>>>> Nikhil Mohite.
>>>>
>>>> On Tue, Oct 6, 2020 at 6:19 PM Akshay Joshi <
>>>> [email protected]> wrote:
>>>>
>>>>> Hi Nikhil
>>>>>
>>>>> Please verify and remove async = false wherever possible.
>>>>>
>>>>> On Tue, Oct 6, 2020 at 5:24 PM Dave Page <[email protected]>
>>>>> wrote:
>>>>>
>>>>>>
>>>>>>
>>>>>> On Tue, Oct 6, 2020 at 12:51 PM Murtuza Zabuawala <
>>>>>> [email protected]> wrote:
>>>>>>
>>>>>>> Hi Akshay,
>>>>>>>
>>>>>>> We have used aysnc=False in most ajax calls with this feature, It is
>>>>>>> causing UI hang in case of slow server response.
>>>>>>> You can try adding a time.sleep() call at the python side response
>>>>>>> and check the UI hang, I think we should avoid sync calls as much as
>>>>>>> possible.
>>>>>>>
>>>>>>
>>>>>> I consider a sync ajax call to be a bug.
>>>>>>
>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> --
>>>>>>> Regards,
>>>>>>> Murtuza Zabuawala
>>>>>>> *EDB*
>>>>>>> *POWER TO POSTGRES*
>>>>>>> https://www.edbpostgres.com
>>>>>>>
>>>>>>>
>>>>>>> On Thu, Oct 1, 2020 at 1:31 PM Akshay Joshi <
>>>>>>> [email protected]> wrote:
>>>>>>>
>>>>>>>> Thanks, patch applied.
>>>>>>>>
>>>>>>>> On Thu, Oct 1, 2020 at 10:42 AM Nikhil Mohite <
>>>>>>>> [email protected]> wrote:
>>>>>>>>
>>>>>>>>> Hi Akshay,
>>>>>>>>>
>>>>>>>>> I have resolved the sonarQube issues, PFA updated patch for the
>>>>>>>>> same.
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> Regards,
>>>>>>>>> Nikhil Mohite.
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> On Tue, Sep 29, 2020 at 11:31 AM Akshay Joshi <
>>>>>>>>> [email protected]> wrote:
>>>>>>>>>
>>>>>>>>>> Hi Nikhil
>>>>>>>>>>
>>>>>>>>>> Your patch introduces 1 new Bug and 13 new code smells, please
>>>>>>>>>> fix those and resend the patch.
>>>>>>>>>>
>>>>>>>>>> On Mon, Sep 28, 2020 at 7:31 PM Nikhil Mohite <
>>>>>>>>>> [email protected]> wrote:
>>>>>>>>>>
>>>>>>>>>>> Hi Akshay,
>>>>>>>>>>>
>>>>>>>>>>> I have resolved code conflict issues and sonarqube issues.
>>>>>>>>>>> PFA updated patch.
>>>>>>>>>>>
>>>>>>>>>>> Regards,
>>>>>>>>>>> Nikhil Mohite.
>>>>>>>>>>>
>>>>>>>>>>> On Mon, Sep 28, 2020 at 5:58 PM Akshay Joshi <
>>>>>>>>>>> [email protected]> wrote:
>>>>>>>>>>>
>>>>>>>>>>>> Hi Nikhil
>>>>>>>>>>>>
>>>>>>>>>>>> The patch is not applying, rebase, and send it again. Please
>>>>>>>>>>>> check your code should not create any new SonarQube issues.
>>>>>>>>>>>>
>>>>>>>>>>>> On Mon, Sep 28, 2020 at 11:20 AM Nikhil Mohite <
>>>>>>>>>>>> [email protected]> wrote:
>>>>>>>>>>>>
>>>>>>>>>>>>> Hi Akshay,
>>>>>>>>>>>>>
>>>>>>>>>>>>> I have resolved all the review comments and also updated the
>>>>>>>>>>>>> test cases as per the new implementation.
>>>>>>>>>>>>>
>>>>>>>>>>>>> PFA updated patch.
>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>> On Mon, Sep 21, 2020 at 5:24 PM Akshay Joshi <
>>>>>>>>>>>>> [email protected]> wrote:
>>>>>>>>>>>>>
>>>>>>>>>>>>>> Hi Nikhil
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> Following are the initial review comments:
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> - Open View/Edit data on any table and click on the same
>>>>>>>>>>>>>> database connection and then click on the Execute button. Got
>>>>>>>>>>>>>> "get_primary_keys() takes 1 positional argument but 2 were given" error.
>>>>>>>>>>>>>> - In my opinion, we should hide the option to change the
>>>>>>>>>>>>>> database connection for View/Edit Data.
>>>>>>>>>>>>>> - If the user clicks on the same database connection
>>>>>>>>>>>>>> multiple times then no need to change the backend connection and
>>>>>>>>>>>>>> transaction id. Add validation at the backend, no action required in this
>>>>>>>>>>>>>> case.
>>>>>>>>>>>>>> - The role option is missing from the "connect to server"
>>>>>>>>>>>>>> dialog.
>>>>>>>>>>>>>> - The Password field should not be there on the "connect
>>>>>>>>>>>>>> to server" dialog. Sometimes we saved the password so asking a password
>>>>>>>>>>>>>> every time is not correct. Check the pgAdmin 3 behavior.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> Code review still remains.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> On Thu, Sep 17, 2020 at 4:15 PM Nikhil Mohite <
>>>>>>>>>>>>>> [email protected]> wrote:
>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> Hi Team,
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> Regarding RM-3794
>>>>>>>>>>>>>>> <https://redmine.postgresql.org/issues/3794; allow the user
>>>>>>>>>>>>>>> to change the database connection from an open query tool:
>>>>>>>>>>>>>>> I have implemented the feature and also added documentation
>>>>>>>>>>>>>>> for it.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> PFA patch.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> --
>>>>>>>>>>>>>>> *Thanks & Regards,*
>>>>>>>>>>>>>>> *Nikhil Mohite*
>>>>>>>>>>>>>>> *Software Engineer.*
>>>>>>>>>>>>>>> *EDB Postgres* <https://www.enterprisedb.com/;
>>>>>>>>>>>>>>> *Mob.No: +91-7798364578.*
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> --
>>>>>>>>>>>>>> *Thanks & Regards*
>>>>>>>>>>>>>> *Akshay Joshi*
>>>>>>>>>>>>>> *pgAdmin Hacker | Sr. Software Architect*
>>>>>>>>>>>>>> *EDB Postgres <http://edbpostgres.com>*
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> *Mobile: +91 976-788-8246*
>>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>> --
>>>>>>>>>>>> *Thanks & Regards*
>>>>>>>>>>>> *Akshay Joshi*
>>>>>>>>>>>> *pgAdmin Hacker | Sr. Software Architect*
>>>>>>>>>>>> *EDB Postgres <http://edbpostgres.com>*
>>>>>>>>>>>>
>>>>>>>>>>>> *Mobile: +91 976-788-8246*
>>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> --
>>>>>>>>>> *Thanks & Regards*
>>>>>>>>>> *Akshay Joshi*
>>>>>>>>>> *pgAdmin Hacker | Sr. Software Architect*
>>>>>>>>>> *EDB Postgres <http://edbpostgres.com>*
>>>>>>>>>>
>>>>>>>>>> *Mobile: +91 976-788-8246*
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>
>>>>>>>> --
>>>>>>>> *Thanks & Regards*
>>>>>>>> *Akshay Joshi*
>>>>>>>> *pgAdmin Hacker | Sr. Software Architect*
>>>>>>>> *EDB Postgres <http://edbpostgres.com>*
>>>>>>>>
>>>>>>>> *Mobile: +91 976-788-8246*
>>>>>>>>
>>>>>>>
>>>>>>
>>>>>> --
>>>>>> Dave Page
>>>>>> VP & Chief Architect, Database Infrastructure
>>>>>> EDB: http://www.enterprisedb.com
>>>>>>
>>>>>> Blog: http://pgsnake.blogspot.com
>>>>>> Twitter: @pgsnake
>>>>>>
>>>>>
>>>>>
>>>>> --
>>>>> *Thanks & Regards*
>>>>> *Akshay Joshi*
>>>>> *pgAdmin Hacker | Sr. Software Architect*
>>>>> *EDB Postgres <http://edbpostgres.com>*
>>>>>
>>>>> *Mobile: +91 976-788-8246*
>>>>>
>>>>
>>>
>>> --
>>> *Thanks & Regards*
>>> *Akshay Joshi*
>>> *pgAdmin Hacker | Sr. Software Architect*
>>> *EDB Postgres <http://edbpostgres.com>*
>>>
>>> *Mobile: +91 976-788-8246*
>>>
>>
>
> --
> *Thanks & Regards*
> *Akshay Joshi*
> *pgAdmin Hacker | Sr. Software Architect*
> *EDB Postgres <http://edbpostgres.com>*
>
> *Mobile: +91 976-788-8246*
>
Attachments:
[application/octet-stream] RM_3794_review_comments_v2.patch (23.6K, 3-RM_3794_review_comments_v2.patch)
download | inline diff:
diff --git a/web/pgadmin/browser/server_groups/servers/__init__.py b/web/pgadmin/browser/server_groups/servers/__init__.py
index 2630d1e..ab4bea4 100644
--- a/web/pgadmin/browser/server_groups/servers/__init__.py
+++ b/web/pgadmin/browser/server_groups/servers/__init__.py
@@ -1310,7 +1310,8 @@ class ServerNode(PGChildNodeView):
# Connect the Server
manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)
- manager.update(server)
+ if not manager.connection().connected():
+ manager.update(server)
conn = manager.connection()
# Get enc key
diff --git a/web/pgadmin/static/js/sqleditor/new_connection_dialog.js b/web/pgadmin/static/js/sqleditor/new_connection_dialog.js
index 10850e1..b19a50a 100644
--- a/web/pgadmin/static/js/sqleditor/new_connection_dialog.js
+++ b/web/pgadmin/static/js/sqleditor/new_connection_dialog.js
@@ -243,7 +243,6 @@ let NewConnectionDialog = {
'title': _.escape(tab_title),
'user': newConnCollectionModel['user'],
'role': newConnCollectionModel['role'],
- 'password': response.password,
'server_name': _.escape(response.server_name),
'database_name': _.escape(selected_database_name),
'is_selected': false,
@@ -260,13 +259,12 @@ let NewConnectionDialog = {
setTimeout(function(){
Alertify.newConnectionDialog('Connect to server.').resizeTo(pgAdmin.Browser.stdW.md,pgAdmin.Browser.stdH.md);
}, 500);
- }).fail(function(error) {
+ }).fail(function() {
Alertify.alert().setting({
'title': gettext('Connection lost'),
'label':gettext('Ok'),
'message': gettext('Connection to the server has been lost.'),
'onok': function(){
- alert(error);
//Close the window after connection is lost
window.close();
},
diff --git a/web/pgadmin/static/js/sqleditor/new_connection_dialog_model.js b/web/pgadmin/static/js/sqleditor/new_connection_dialog_model.js
index 09b2979..0bbda60 100644
--- a/web/pgadmin/static/js/sqleditor/new_connection_dialog_model.js
+++ b/web/pgadmin/static/js/sqleditor/new_connection_dialog_model.js
@@ -78,7 +78,6 @@ export default function newConnectionDialogModel(response, sgid, sid) {
server: parseInt(sid),
database: null,
user: null,
- password: null,
server_name: server_name,
database_name: database_name,
},
@@ -92,7 +91,63 @@ export default function newConnectionDialogModel(response, sgid, sid) {
select2: {
allowClear: false,
},
+ transform: function(data) {
+ let group_template_options = [];
+ for (let key in data) {
+ if (data.hasOwnProperty(key)) {
+ group_template_options.push({'group': key, 'optval': data[key]});
+ }
+ }
+ return group_template_options;
+ },
control: Backform.Select2Control.extend({
+ template: _.template([
+ '<% if(label == false) {} else {%>',
+ ' <label class="<%=Backform.controlLabelClassName%>"><%=label%></label>',
+ '<% }%>',
+ '<div class="<%=controlsClassName%>">',
+ ' <select class="<%=Backform.controlClassName%> <%=extraClasses.join(\' \')%>"',
+ ' name="<%=name%>" value="<%-value%>" <%=disabled ? "disabled" : ""%>',
+ ' <%=required ? "required" : ""%><%= select2.multiple ? " multiple>" : ">" %>',
+ ' <%=select2.first_empty ? " <option></option>" : ""%>',
+ ' <% for (var i=0; i < options.length; i++) {%>',
+ ' <% if (options[i].group) { %>',
+ ' <% var group = options[i].group; %>',
+ ' <% if (options[i].optval) { %> <% var option_length = options[i].optval.length; %>',
+ ' <optgroup label="<%=group%>">',
+ ' <% for (var subindex=0; subindex < option_length; subindex++) {%>',
+ ' <% var option = options[i].optval[subindex]; %>',
+ ' <option ',
+ ' <% if (option.image) { %> data-image=<%=option.image%> <%}%>',
+ ' <% if (option.connected) { %> data-connected=connected <%}%>',
+ ' value=<%- formatter.fromRaw(option.value) %>',
+ ' <% if (option.selected) {%>selected="selected"<%} else {%>',
+ ' <% if (!select2.multiple && option.value === rawValue) {%>selected="selected"<%}%>',
+ ' <% if (select2.multiple && rawValue && rawValue.indexOf(option.value) != -1){%>selected="selected" data-index="rawValue.indexOf(option.value)"<%}%>',
+ ' <%}%>',
+ ' <%= disabled ? "disabled" : ""%>><%-option.label%></option>',
+ ' <%}%>',
+ ' </optgroup>',
+ ' <%}%>',
+ ' <%} else {%>',
+ ' <% var option = options[i]; %>',
+ ' <option ',
+ ' <% if (option.image) { %> data-image=<%=option.image%> <%}%>',
+ ' <% if (option.connected) { %> data-connected=connected <%}%>',
+ ' value=<%- formatter.fromRaw(option.value) %>',
+ ' <% if (option.selected) {%>selected="selected"<%} else {%>',
+ ' <% if (!select2.multiple && option.value === rawValue) {%>selected="selected"<%}%>',
+ ' <% if (select2.multiple && rawValue && rawValue.indexOf(option.value) != -1){%>selected="selected" data-index="rawValue.indexOf(option.value)"<%}%>',
+ ' <%}%>',
+ ' <%= disabled ? "disabled" : ""%>><%-option.label%></option>',
+ ' <%}%>',
+ ' <%}%>',
+ ' </select>',
+ ' <% if (helpMessage && helpMessage.length) { %>',
+ ' <span class="<%=Backform.helpMessageClassName%>"><%=helpMessage%></span>',
+ ' <% } %>',
+ '</div>',
+ ].join('\n')),
connect: function(self) {
let local_self = self;
@@ -147,11 +202,14 @@ export default function newConnectionDialogModel(response, sgid, sid) {
local_self.model.attributes.user = null;
local_self.model.attributes.role = null;
Backform.Select2Control.prototype.onChange.apply(local_self, arguments);
- response.server_list.forEach(function(obj){
- if(obj.id==self.model.changed.server) {
- response.server_name = obj.name;
- }
+ Object.keys(response.server_list).forEach(key => {
+ response.server_list[key].forEach(option => {
+ if (option.value == local_self.getValueFromDOM()) {
+ response.server_name = option.label;
+ }
+ });
});
+
loadingDiv.addClass('d-none');
alertify.connectServer().destroy();
})
@@ -160,8 +218,6 @@ export default function newConnectionDialogModel(response, sgid, sid) {
alertify.connectServer().destroy();
alertify.connectServer('Connect to server', xhr.responseJSON.result, local_self.getValueFromDOM());
});
- } else {
- response.password = $('#password').val();
}
} else {
local_self.model.attributes.database = null;
@@ -178,6 +234,19 @@ export default function newConnectionDialogModel(response, sgid, sid) {
render: function() {
let self = this;
self.connect(self);
+ Object.keys(response.server_list).forEach(key => {
+ response.server_list[key].forEach(option => {
+ if (option.value == parseInt(sid)) {
+ response.server_name = option.label;
+ }
+ });
+ });
+ var transform = self.field.get('transform') || self.defaults.transform;
+ if (transform && _.isFunction(transform)) {
+ self.field.set('options', transform.bind(self, response.server_list));
+ } else {
+ self.field.set('options', response.server_list);
+ }
return Backform.Select2Control.prototype.render.apply(self, arguments);
},
onChange: function() {
@@ -200,10 +269,12 @@ export default function newConnectionDialogModel(response, sgid, sid) {
},
}).done(function () {
Backform.Select2Control.prototype.onChange.apply(self, arguments);
- response.server_list.forEach(function(obj){
- if(obj.id==self.model.changed.server) {
- response.server_name = obj.name;
- }
+ Object.keys(response.server_list).forEach(key => {
+ response.server_list[key].forEach(option => {
+ if (option.value == self.getValueFromDOM()) {
+ response.server_name = option.label;
+ }
+ });
});
loadingDiv.addClass('d-none');
}).fail(function(xhr){
@@ -213,17 +284,6 @@ export default function newConnectionDialogModel(response, sgid, sid) {
},
}),
- options: function() {
- return _.map(response.server_list, (obj) => {
- if (obj.id == parseInt(sid))
- response.server_name = obj.name;
-
- return {
- value: obj.id,
- label: obj.name,
- };
- });
- },
},
{
id: 'database',
@@ -302,27 +362,6 @@ export default function newConnectionDialogModel(response, sgid, sid) {
url: 'sqleditor.get_new_connection_role',
disabled: false,
},
- /*{
- id: 'password',
- name: 'password',
- label: gettext('Password'tools/sqleditor/__init__.py),
- type: 'password',
- editable: true,
- disabled: true,
- deps: ['user'],
- control: Backform.InputControl.extend({
- render: function() {
- let self = this;
- self.model.attributes.password = null;
- Backform.InputControl.prototype.render.apply(self, arguments);
- return self;
- },
- onChange: function() {
- let self = this;
- Backform.InputControl.prototype.onChange.apply(self, arguments);
- },
- }),
- },*/
],
validate: function() {
let msg = null;
@@ -336,11 +375,6 @@ export default function newConnectionDialogModel(response, sgid, sid) {
this.errorModel.set('user', msg);
return msg;
}
- /*else if((this.attributes.password == '' || _.isUndefined(this.get('password')) || _.isNull(this.get('password')))) {
- msg = gettext('Please enter password');
- this.errorModel.set('password', msg);
- return msg;
- }*/
return null;
},
});
diff --git a/web/pgadmin/tools/debugger/__init__.py b/web/pgadmin/tools/debugger/__init__.py
index 1949709..422a8ae 100644
--- a/web/pgadmin/tools/debugger/__init__.py
+++ b/web/pgadmin/tools/debugger/__init__.py
@@ -98,6 +98,18 @@ class DebuggerModule(PgAdminModule):
)
)
+ self.tab_title = self.preference.register(
+ 'display', 'debugger_tab_title_placeholder',
+ gettext("Debugger tab title placeholder"),
+ 'text', '%FUNCTION%/%SCHEMA%/%DATABASE%',
+ category_label=PREF_LABEL_DISPLAY,
+ help_str=gettext(
+ 'Supported placeholders: FUNCTION, SCHEMA and DATABASE. '
+ 'You can also provide any string with or '
+ 'without placeholders'
+ )
+ )
+
self.preference.register(
'keyboard_shortcuts', 'btn_start',
gettext('Accesskey (Continue/Start)'), 'keyboardshortcut',
diff --git a/web/pgadmin/tools/sqleditor/__init__.py b/web/pgadmin/tools/sqleditor/__init__.py
index 9da8842..f9fb26f 100644
--- a/web/pgadmin/tools/sqleditor/__init__.py
+++ b/web/pgadmin/tools/sqleditor/__init__.py
@@ -46,7 +46,7 @@ from pgadmin.tools.sqleditor.utils.macros import get_macros,\
get_user_macros, set_macros
from pgadmin.utils.constants import MIMETYPE_APP_JS, \
SERVER_CONNECTION_CLOSED, ERROR_MSG_TRANS_ID_NOT_FOUND, ERROR_FETCHING_DATA
-from pgadmin.model import Server
+from pgadmin.model import Server, ServerGroup
from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry
MODULE_NAME = 'sqleditor'
@@ -1489,10 +1489,14 @@ def get_new_connection_data(sgid, sid=None):
:extract_sql_from_network_parameters,
"""
try:
- # if sid and not did:
+ server_groups = ServerGroup.query.all()
+ server_group_data = {server_group.name: [] for server_group in
+ server_groups}
servers = Server.query.all()
- server_list = [
- {'name': server.serialize['name'], "id": server.serialize['id']}
+
+ [server_group_data[server.servers.name].append(
+ {'label': server.serialize['name'],
+ "value": server.serialize['id']})
for server in servers]
msg = "Success"
@@ -1501,7 +1505,7 @@ def get_new_connection_data(sgid, sid=None):
'status': True,
'msg': msg,
'result': {
- 'server_list': server_list
+ 'server_list': server_group_data
}
}
)
diff --git a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
index d3a2d25..26c88bc 100644
--- a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
+++ b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
@@ -173,8 +173,13 @@ define('tools.querytool', [
var dropdownElement = document.getElementById('connections-list');
dropdownElement.innerHTML = '';
data_list.forEach((option, index) => {
- $('#connections-list').append('<li class="connection-list-item" data-index='+ index +'><a class="dropdown-item" href="#" tabindex="0">'+ option.title +'</a></li>');
-
+ var opt = '';
+ if ('is_selected' in option && option['is_selected']) {
+ opt = '<li class="connection-list-item selected-connection" data-index='+ index +'><a class="dropdown-item" href="#" tabindex="0">'+ option.title +'</a></li>';
+ } else {
+ opt = '<li class="connection-list-item" data-index='+ index +'><a class="dropdown-item" href="#" tabindex="0">'+ option.title +'</a></li>';
+ }
+ $('#connections-list').append(opt);
});
var self = this;
$('.connection-list-item').click(function() {
@@ -2131,7 +2136,6 @@ define('tools.querytool', [
on_change_connection: function(connection_details, ref) {
if(!connection_details['is_selected']) {
var self = this;
- self.set_selected_option(connection_details);
var loadingDiv = null;
var msgDiv = null;
if(ref){
@@ -2145,66 +2149,78 @@ define('tools.querytool', [
msgDiv = loadingDiv.find('.sql-editor-busy-text');
}
- $.ajax({
- url: url_for('datagrid.update_query_tool_connection', {
- 'trans_id': self.transId,
- 'sgid': connection_details['server_group'],
- 'sid': connection_details['server'],
- 'did': connection_details['database'],
- }),
- method: 'POST',
- contentType: 'application/json',
- data: JSON.stringify(connection_details),
- })
- .done(function(res) {
- if(res.success) {
- self.transId = res.data.tran_id;
- self.handler.transId = res.data.tran_id;
- self.handler.url_params = {
- 'did': connection_details['database'],
- 'is_query_tool': self.handler.url_params.is_query_tool,
- 'server_type': self.handler.url_params.server_type,
+ alertify.confirm(gettext('Change connection'),
+ gettext('By changing the connection you will lose all your unsaved data for the current connection. <br> Do you want to continue?'),
+ function() {
+ self.set_selected_option(connection_details);
+ $.ajax({
+ url: url_for('datagrid.update_query_tool_connection', {
+ 'trans_id': self.transId,
'sgid': connection_details['server_group'],
'sid': connection_details['server'],
- 'title': connection_details['title'],
- };
- self.set_editor_title(_.unescape(self.handler.url_params.title));
- self.handler.setTitle(_.unescape(self.handler.url_params.title));
- let success_msg = connection_details['server_name'] + '/' + connection_details['database_name'] + '- Database connected';
- alertify.success(success_msg);
- if(ref){
- let connection_data = {
- 'server_group': self.handler.url_params.sgid,
- 'server': connection_details['server'],
- 'database': connection_details['database'],
- 'user': connection_details['user'],
- 'title': connection_details['title'],
- 'role': connection_details['role'],
- 'password': connection_details['password'],
- 'is_allow_new_connection': true,
- 'database_name': connection_details['database_name'],
- 'server_name': connection_details['server_name'],
- 'is_selected': true,
- };
- self.connection_list.unshift(connection_data);
- self.render_connection(self.connection_list);
- loadingDiv.addClass('d-none');
- alertify.newConnectionDialog().destroy();
- ref.close();
- } else {
- loadingDiv.addClass('d-none');
- }
- }
- return true;
- })
- .fail(function(xhr) {
+ 'did': connection_details['database'],
+ }),
+ method: 'POST',
+ contentType: 'application/json',
+ data: JSON.stringify(connection_details),
+ })
+ .done(function(res) {
+ if(res.success) {
+ self.transId = res.data.tran_id;
+ self.handler.transId = res.data.tran_id;
+ self.handler.url_params = {
+ 'did': connection_details['database'],
+ 'is_query_tool': self.handler.url_params.is_query_tool,
+ 'server_type': self.handler.url_params.server_type,
+ 'sgid': connection_details['server_group'],
+ 'sid': connection_details['server'],
+ 'title': connection_details['title'],
+ };
+ self.set_editor_title(_.unescape(self.handler.url_params.title));
+ self.handler.setTitle(_.unescape(self.handler.url_params.title));
+ let success_msg = connection_details['server_name'] + '/' + connection_details['database_name'] + '- Database connected';
+ alertify.success(success_msg);
+ if(ref){
+ let connection_data = {
+ 'server_group': self.handler.url_params.sgid,
+ 'server': connection_details['server'],
+ 'database': connection_details['database'],
+ 'user': connection_details['user'],
+ 'title': connection_details['title'],
+ 'role': connection_details['role'],
+ 'is_allow_new_connection': true,
+ 'database_name': connection_details['database_name'],
+ 'server_name': connection_details['server_name'],
+ 'is_selected': true,
+ };
+ self.connection_list.unshift(connection_data);
+ self.render_connection(self.connection_list);
+ loadingDiv.addClass('d-none');
+ alertify.newConnectionDialog().destroy();
+ ref.close();
+ } else {
+ loadingDiv.addClass('d-none');
+ }
+ }
+ return true;
+ })
+ .fail(function(xhr) {
+ if(xhr.status == 428) {
+ alertify.connectServer('Connect to server', xhr.responseJSON.result, connection_details['server'], false);
+ } else {
+ alertify.error(xhr.responseJSON['errormsg']);
+ }
+ });
+ },
+ function() {
loadingDiv.addClass('d-none');
- if(xhr.status == 428) {
- alertify.connectServer('Connect to server', xhr.responseJSON.result, connection_details['server'], false);
- } else {
- alertify.error(xhr.responseJSON['errormsg']);
- }
- });
+ alertify.newConnectionDialog().destroy();
+ return true;
+ }
+ ).set('labels', {
+ ok: gettext('Yes'),
+ cancel: gettext('No'),
+ });
}
},
});
@@ -2542,7 +2558,7 @@ define('tools.querytool', [
'server_group': self.gridView.handler.url_params.sgid,
'server': self.gridView.handler.url_params.sid,
'database': self.gridView.handler.url_params.did,
- 'user': null,
+ 'user': server_data.data.user.name,
'role': null,
'title': _.unescape(url_params.title),
'is_allow_new_connection': false,
diff --git a/web/pgadmin/tools/sqleditor/static/scss/_sqleditor.scss b/web/pgadmin/tools/sqleditor/static/scss/_sqleditor.scss
index 7fc576a..9d5ba4d 100644
--- a/web/pgadmin/tools/sqleditor/static/scss/_sqleditor.scss
+++ b/web/pgadmin/tools/sqleditor/static/scss/_sqleditor.scss
@@ -371,6 +371,9 @@ div.strikeout:after {
height: 100%;
}
+.selected-connection {
+ background-color: $color-primary-light;
+}
/* Setting it to hardcoded white as the SVG generated is having white bg
* Need to check what can be done.
diff --git a/web/pgadmin/tools/sqleditor/utils/query_tool_preferences.py b/web/pgadmin/tools/sqleditor/utils/query_tool_preferences.py
index 76523b1..642a1eb 100644
--- a/web/pgadmin/tools/sqleditor/utils/query_tool_preferences.py
+++ b/web/pgadmin/tools/sqleditor/utils/query_tool_preferences.py
@@ -326,6 +326,29 @@ def register_query_tool_preferences(self):
)
)
+ self.qt_tab_title = self.preference.register(
+ 'display', 'qt_tab_title_placeholder',
+ gettext("Query tool tab title placeholder"),
+ 'text', '%DATABASE%/%USERNAME%@%SERVER%',
+ category_label=PREF_LABEL_DISPLAY,
+ help_str=gettext(
+ 'Supported placeholders: DATABASE, USERNAME and SERVER. '
+ 'You can also provide any string with or without placeholders.'
+ )
+ )
+
+ self.ve_edt_tab_title = self.preference.register(
+ 'display', 'vw_edt_tab_title_placeholder',
+ gettext("View/Edit tab title placeholder"),
+ 'text', '%SCHEMA%.%TABLE%/%DATABASE%/%USERNAME%@%SERVER%',
+ category_label=PREF_LABEL_DISPLAY,
+ help_str=gettext(
+ 'Supported placeholders: SCHEMA, TABLE, DATABASE, USERNAME and '
+ 'SERVER. You can also provide any string with or '
+ 'without placeholders.'
+ )
+ )
+
self.connection_status = self.preference.register(
'display', 'connection_status_fetch_time',
gettext("Connection status refresh rate"), 'integer', 2,
^ permalink raw reply [nested|flat] 18+ messages in thread
* Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab
2020-09-17 10:45 [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-09-21 11:54 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-09-28 05:50 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-09-28 12:28 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-09-28 14:01 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-09-29 06:01 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-10-01 05:12 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-10-01 08:01 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-10-06 11:51 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Murtuza Zabuawala <[email protected]>
2020-10-06 11:54 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Dave Page <[email protected]>
2020-10-06 12:49 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-10-07 06:41 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-10-08 06:09 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-10-21 05:38 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-10-21 09:24 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Akshay Joshi <[email protected]>
2020-10-21 11:15 ` Re: [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
@ 2020-10-21 11:49 ` Akshay Joshi <[email protected]>
0 siblings, 0 replies; 18+ messages in thread
From: Akshay Joshi @ 2020-10-21 11:49 UTC (permalink / raw)
To: Nikhil Mohite <[email protected]>; +Cc: pgadmin-hackers
Thanks, patch applied.
On Wed, Oct 21, 2020 at 4:45 PM Nikhil Mohite <
[email protected]> wrote:
> Hi Akshay,
>
> I have fixed the review comments, PFA the updated patch for the same.
>
>
> Regards,
> Nikhil Mohite.
>
> On Wed, Oct 21, 2020 at 2:55 PM Akshay Joshi <
> [email protected]> wrote:
>
>> Hi Nikhil
>>
>> Following are the review comments:
>>
>> - Connect to any server from the browser tree. Open the query tool
>> and then open the new connection dialog. Click on the "OK" button without
>> changing any field. It shows the popup for "Change connection" which should
>> not be raised because the server is the same.
>> - In the above scenario, if you click on the Yes button it is showing
>> a duplicate entry for the same server.
>> - The server name is not getting changed when we connect to any
>> other server from the new connection. Changes needed in alertify message,
>> tab title, and a combo box.
>> - Remove the "." from the "Change connection." title.
>> - Change the string "Change connection will lose all non committed
>> changes for current connection, do you want to continue?" to "*By
>> changing the connection you will lose all your unsaved data for the current
>> connection.*
>> *Do you want to continue?*"
>>
>> Please fix the above changes and send the patch again.
>>
>> On Wed, Oct 21, 2020 at 11:08 AM Nikhil Mohite <
>> [email protected]> wrote:
>>
>>> Hi Akshay,
>>>
>>> I have updated the existing implementation as per suggestions.
>>> 1. Show servers in server groups in the dropdown.
>>> 2. Current selected connection in the new connection dropdown is now
>>> highlighted as selected.
>>> 3. Notification to the user before the change connection action.
>>> 4. If we connect to the server through a new connection dialog, the tree
>>> will use the same connection and it will not create a new connection.
>>> (In earlier implementation it was asking for the password even we have
>>> connected from a new connection dialog.)
>>>
>>> PFA patch
>>>
>>> Regards,
>>> Nikhil Mohite.
>>>
>>> On Thu, Oct 8, 2020 at 11:39 AM Akshay Joshi <
>>> [email protected]> wrote:
>>>
>>>> Thanks, patch applied.
>>>>
>>>> On Wed, Oct 7, 2020 at 12:11 PM Nikhil Mohite <
>>>> [email protected]> wrote:
>>>>
>>>>> Hi Akshay,
>>>>>
>>>>> I checked the implementation and found 2 locations which I missed in
>>>>> the last patch to remove async: False.
>>>>> I have removed all occurrences of async: False now also added missing
>>>>> loader in required places.
>>>>>
>>>>> PFA updated the patch for the same.
>>>>>
>>>>> Regards,
>>>>> Nikhil Mohite.
>>>>>
>>>>> On Tue, Oct 6, 2020 at 6:19 PM Akshay Joshi <
>>>>> [email protected]> wrote:
>>>>>
>>>>>> Hi Nikhil
>>>>>>
>>>>>> Please verify and remove async = false wherever possible.
>>>>>>
>>>>>> On Tue, Oct 6, 2020 at 5:24 PM Dave Page <[email protected]>
>>>>>> wrote:
>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> On Tue, Oct 6, 2020 at 12:51 PM Murtuza Zabuawala <
>>>>>>> [email protected]> wrote:
>>>>>>>
>>>>>>>> Hi Akshay,
>>>>>>>>
>>>>>>>> We have used aysnc=False in most ajax calls with this feature, It
>>>>>>>> is causing UI hang in case of slow server response.
>>>>>>>> You can try adding a time.sleep() call at the python side response
>>>>>>>> and check the UI hang, I think we should avoid sync calls as much as
>>>>>>>> possible.
>>>>>>>>
>>>>>>>
>>>>>>> I consider a sync ajax call to be a bug.
>>>>>>>
>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> --
>>>>>>>> Regards,
>>>>>>>> Murtuza Zabuawala
>>>>>>>> *EDB*
>>>>>>>> *POWER TO POSTGRES*
>>>>>>>> https://www.edbpostgres.com
>>>>>>>>
>>>>>>>>
>>>>>>>> On Thu, Oct 1, 2020 at 1:31 PM Akshay Joshi <
>>>>>>>> [email protected]> wrote:
>>>>>>>>
>>>>>>>>> Thanks, patch applied.
>>>>>>>>>
>>>>>>>>> On Thu, Oct 1, 2020 at 10:42 AM Nikhil Mohite <
>>>>>>>>> [email protected]> wrote:
>>>>>>>>>
>>>>>>>>>> Hi Akshay,
>>>>>>>>>>
>>>>>>>>>> I have resolved the sonarQube issues, PFA updated patch for the
>>>>>>>>>> same.
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> Regards,
>>>>>>>>>> Nikhil Mohite.
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> On Tue, Sep 29, 2020 at 11:31 AM Akshay Joshi <
>>>>>>>>>> [email protected]> wrote:
>>>>>>>>>>
>>>>>>>>>>> Hi Nikhil
>>>>>>>>>>>
>>>>>>>>>>> Your patch introduces 1 new Bug and 13 new code smells, please
>>>>>>>>>>> fix those and resend the patch.
>>>>>>>>>>>
>>>>>>>>>>> On Mon, Sep 28, 2020 at 7:31 PM Nikhil Mohite <
>>>>>>>>>>> [email protected]> wrote:
>>>>>>>>>>>
>>>>>>>>>>>> Hi Akshay,
>>>>>>>>>>>>
>>>>>>>>>>>> I have resolved code conflict issues and sonarqube issues.
>>>>>>>>>>>> PFA updated patch.
>>>>>>>>>>>>
>>>>>>>>>>>> Regards,
>>>>>>>>>>>> Nikhil Mohite.
>>>>>>>>>>>>
>>>>>>>>>>>> On Mon, Sep 28, 2020 at 5:58 PM Akshay Joshi <
>>>>>>>>>>>> [email protected]> wrote:
>>>>>>>>>>>>
>>>>>>>>>>>>> Hi Nikhil
>>>>>>>>>>>>>
>>>>>>>>>>>>> The patch is not applying, rebase, and send it again. Please
>>>>>>>>>>>>> check your code should not create any new SonarQube issues.
>>>>>>>>>>>>>
>>>>>>>>>>>>> On Mon, Sep 28, 2020 at 11:20 AM Nikhil Mohite <
>>>>>>>>>>>>> [email protected]> wrote:
>>>>>>>>>>>>>
>>>>>>>>>>>>>> Hi Akshay,
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> I have resolved all the review comments and also updated the
>>>>>>>>>>>>>> test cases as per the new implementation.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> PFA updated patch.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>>
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> On Mon, Sep 21, 2020 at 5:24 PM Akshay Joshi <
>>>>>>>>>>>>>> [email protected]> wrote:
>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> Hi Nikhil
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> Following are the initial review comments:
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> - Open View/Edit data on any table and click on the same
>>>>>>>>>>>>>>> database connection and then click on the Execute button. Got
>>>>>>>>>>>>>>> "get_primary_keys() takes 1 positional argument but 2 were given" error.
>>>>>>>>>>>>>>> - In my opinion, we should hide the option to change the
>>>>>>>>>>>>>>> database connection for View/Edit Data.
>>>>>>>>>>>>>>> - If the user clicks on the same database connection
>>>>>>>>>>>>>>> multiple times then no need to change the backend connection and
>>>>>>>>>>>>>>> transaction id. Add validation at the backend, no action required in this
>>>>>>>>>>>>>>> case.
>>>>>>>>>>>>>>> - The role option is missing from the "connect to
>>>>>>>>>>>>>>> server" dialog.
>>>>>>>>>>>>>>> - The Password field should not be there on the "connect
>>>>>>>>>>>>>>> to server" dialog. Sometimes we saved the password so asking a password
>>>>>>>>>>>>>>> every time is not correct. Check the pgAdmin 3 behavior.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> Code review still remains.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> On Thu, Sep 17, 2020 at 4:15 PM Nikhil Mohite <
>>>>>>>>>>>>>>> [email protected]> wrote:
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> Hi Team,
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> Regarding RM-3794
>>>>>>>>>>>>>>>> <https://redmine.postgresql.org/issues/3794; allow
>>>>>>>>>>>>>>>> the user to change the database connection from an open query tool:
>>>>>>>>>>>>>>>> I have implemented the feature and also added documentation
>>>>>>>>>>>>>>>> for it.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> PFA patch.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> --
>>>>>>>>>>>>>>>> *Thanks & Regards,*
>>>>>>>>>>>>>>>> *Nikhil Mohite*
>>>>>>>>>>>>>>>> *Software Engineer.*
>>>>>>>>>>>>>>>> *EDB Postgres* <https://www.enterprisedb.com/;
>>>>>>>>>>>>>>>> *Mob.No: +91-7798364578.*
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> --
>>>>>>>>>>>>>>> *Thanks & Regards*
>>>>>>>>>>>>>>> *Akshay Joshi*
>>>>>>>>>>>>>>> *pgAdmin Hacker | Sr. Software Architect*
>>>>>>>>>>>>>>> *EDB Postgres <http://edbpostgres.com>*
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> *Mobile: +91 976-788-8246*
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>> --
>>>>>>>>>>>>> *Thanks & Regards*
>>>>>>>>>>>>> *Akshay Joshi*
>>>>>>>>>>>>> *pgAdmin Hacker | Sr. Software Architect*
>>>>>>>>>>>>> *EDB Postgres <http://edbpostgres.com>*
>>>>>>>>>>>>>
>>>>>>>>>>>>> *Mobile: +91 976-788-8246*
>>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> --
>>>>>>>>>>> *Thanks & Regards*
>>>>>>>>>>> *Akshay Joshi*
>>>>>>>>>>> *pgAdmin Hacker | Sr. Software Architect*
>>>>>>>>>>> *EDB Postgres <http://edbpostgres.com>*
>>>>>>>>>>>
>>>>>>>>>>> *Mobile: +91 976-788-8246*
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>> --
>>>>>>>>> *Thanks & Regards*
>>>>>>>>> *Akshay Joshi*
>>>>>>>>> *pgAdmin Hacker | Sr. Software Architect*
>>>>>>>>> *EDB Postgres <http://edbpostgres.com>*
>>>>>>>>>
>>>>>>>>> *Mobile: +91 976-788-8246*
>>>>>>>>>
>>>>>>>>
>>>>>>>
>>>>>>> --
>>>>>>> Dave Page
>>>>>>> VP & Chief Architect, Database Infrastructure
>>>>>>> EDB: http://www.enterprisedb.com
>>>>>>>
>>>>>>> Blog: http://pgsnake.blogspot.com
>>>>>>> Twitter: @pgsnake
>>>>>>>
>>>>>>
>>>>>>
>>>>>> --
>>>>>> *Thanks & Regards*
>>>>>> *Akshay Joshi*
>>>>>> *pgAdmin Hacker | Sr. Software Architect*
>>>>>> *EDB Postgres <http://edbpostgres.com>*
>>>>>>
>>>>>> *Mobile: +91 976-788-8246*
>>>>>>
>>>>>
>>>>
>>>> --
>>>> *Thanks & Regards*
>>>> *Akshay Joshi*
>>>> *pgAdmin Hacker | Sr. Software Architect*
>>>> *EDB Postgres <http://edbpostgres.com>*
>>>>
>>>> *Mobile: +91 976-788-8246*
>>>>
>>>
>>
>> --
>> *Thanks & Regards*
>> *Akshay Joshi*
>> *pgAdmin Hacker | Sr. Software Architect*
>> *EDB Postgres <http://edbpostgres.com>*
>>
>> *Mobile: +91 976-788-8246*
>>
>
--
*Thanks & Regards*
*Akshay Joshi*
*pgAdmin Hacker | Sr. Software Architect*
*EDB Postgres <http://edbpostgres.com>*
*Mobile: +91 976-788-8246*
^ permalink raw reply [nested|flat] 18+ messages in thread
end of thread, other threads:[~2020-10-21 11:49 UTC | newest]
Thread overview: 18+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2020-09-17 10:45 [pgAdmin][RM3794]:Allow User to Change Database Connection from an Open Query Tool Tab Nikhil Mohite <[email protected]>
2020-09-21 11:54 ` Akshay Joshi <[email protected]>
2020-09-28 05:50 ` Nikhil Mohite <[email protected]>
2020-09-28 12:28 ` Akshay Joshi <[email protected]>
2020-09-28 14:01 ` Nikhil Mohite <[email protected]>
2020-09-29 06:01 ` Akshay Joshi <[email protected]>
2020-10-01 05:12 ` Nikhil Mohite <[email protected]>
2020-10-01 08:01 ` Akshay Joshi <[email protected]>
2020-10-06 08:51 ` Nikhil Mohite <[email protected]>
2020-10-06 11:51 ` Murtuza Zabuawala <[email protected]>
2020-10-06 11:54 ` Dave Page <[email protected]>
2020-10-06 12:49 ` Akshay Joshi <[email protected]>
2020-10-07 06:41 ` Nikhil Mohite <[email protected]>
2020-10-08 06:09 ` Akshay Joshi <[email protected]>
2020-10-21 05:38 ` Nikhil Mohite <[email protected]>
2020-10-21 09:24 ` Akshay Joshi <[email protected]>
2020-10-21 11:15 ` Nikhil Mohite <[email protected]>
2020-10-21 11:49 ` Akshay Joshi <[email protected]>
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox