Changed the logic to identify the PostgreSQL server variant, when we

connect to the database server. Also, added modified the way, we do
check the node is supported by the server.

Instead of creating separate blueprint for the server types, they will
be independently works. In order to add different variant of the
PostgreSQL, we need to extend the ServerType class, and override the
'instanceOf' function for identification using version string. Please
take a look at the ppas.py for the example.

During checking the back-end support for the node, we will also check
the server type (variant) along with the version within the range of
maximum and minimum version for the node. And, the same support added
for the schema attributes in front-end (JavaScript).

Really thankful to Khushboo Vashi for her initial work in front-end. I
took it further from there.
This commit is contained in:
Ashesh Vashi
2015-11-19 23:15:48 +05:30
parent 3323543b5a
commit 77834ccdda
19 changed files with 306 additions and 341 deletions

View File

@@ -33,7 +33,6 @@ class ServerGroupModule(BrowserPluginModule):
for group in groups:
yield self.generate_browser_node(
"%d" % (group.id),
None,
group.name,
"icon-%s" % self.node_type,
True,
@@ -179,7 +178,6 @@ class ServerGroupView(NodeView):
return jsonify(
node=self.blueprint.generate_browser_node(
"%d" % (sg.id),
None,
sg.name,
"icon-%s" % self.node_type,
True,

View File

@@ -1,44 +0,0 @@
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2015, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
from pgadmin.browser.server_groups.servers import ServerTypeModule
from pgadmin.browser.utils import PGChildModule
from flask.ext.babel import gettext
class PPASServer(ServerTypeModule, PGChildModule):
NODE_TYPE = "ppas"
@property
def node_type(self):
return self.NODE_TYPE
@property
def jssnippets(self):
return []
@property
def type(self):
return "PPAS"
@property
def description(self):
return "Postgres Plus Advanced Server"
@property
def driver(self):
return "psycopg2"
@property
def priority(self):
return 1
def instanceOf(self, ver):
return ver.startswith("EnterpriseDB")
blueprint = PPASServer(__name__, static_url_path='/static')

View File

@@ -1,43 +0,0 @@
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2015, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
from pgadmin.browser.server_groups.servers import ServerTypeModule
from pgadmin.browser.utils import PGChildModule
class PGServer(ServerTypeModule, PGChildModule):
NODE_TYPE = "pg"
@property
def node_type(self):
return self.NODE_TYPE
@property
def jssnippets(self):
return []
@property
def type(self):
return "PG"
@property
def description(self):
return "PostgreSQL"
@property
def driver(self):
return "psycopg2"
@property
def priority(self):
return 10
def instanceOf(self, ver):
return True
blueprint = PGServer(__name__, static_url_path='/static')

View File

@@ -15,7 +15,7 @@ from pgadmin.utils.menu import MenuItem
from pgadmin.utils.ajax import make_json_response, \
make_response as ajax_response, internal_server_error, success_return, \
unauthorized, bad_request, precondition_required, forbidden
from pgadmin.browser.utils import NodeView
from pgadmin.browser.utils import PGChildNodeView
import traceback
from flask.ext.babel import gettext
import pgadmin.browser.server_groups as sg
@@ -23,6 +23,7 @@ from pgadmin.utils.crypto import encrypt, decrypt
from pgadmin.browser import BrowserPluginModule
from config import PG_DEFAULT_DRIVER
import six
from pgadmin.browser.server_groups.servers.types import ServerType
class ServerModule(sg.ServerGroupPluginModule):
NODE_TYPE = "server"
@@ -50,19 +51,18 @@ class ServerModule(sg.ServerGroupPluginModule):
for server in servers:
manager = driver.connection_manager(server.id)
conn = manager.connection()
module = getattr(manager, "module", None)
connected = conn.connected()
yield self.generate_browser_node(
"%d" % (server.id),
"%d" % gid,
server.name,
"icon-server-not-connected" if not connected else
"icon-{0}".format(module.NODE_TYPE),
"icon-{0}".format(manager.server_type),
True,
self.NODE_TYPE,
connected=connected,
server_type=module.type if module is not None else "PG"
server_type=manager.server_type if connected else "pg",
server_version=manager.sversion if connected else 0
)
@property
@@ -79,6 +79,9 @@ class ServerModule(sg.ServerGroupPluginModule):
for submodule in self.submodules:
snippets.extend(submodule.csssnippets)
for st in ServerType.types():
snippets.extend(st.csssnippets)
return snippets
@@ -90,81 +93,7 @@ class ServerMenuItem(MenuItem):
blueprint = ServerModule(__name__)
@six.add_metaclass(ABCMeta)
class ServerTypeModule(BrowserPluginModule):
"""
Base class for different server types.
"""
@abstractproperty
def type(self):
pass
@abstractproperty
def description(self):
pass
@abstractproperty
def priority(self):
pass
def get_nodes(self, manager=None, sid=None):
assert(sid is not None)
nodes = []
for module in self.submodules:
if isinstance(module, PGChildModule):
if manager and module.BackendSupported(manager):
nodes.extend(module.get_nodes(sid=sid, manager=manager))
else:
nodes.extend(module.get_nodes(sid=sid))
return nodes
@abstractmethod
def instanceOf(self, version):
pass
def __str__(self):
return "Type: {0},Description:{1}".format(self.type, self.description)
@property
def csssnippets(self):
"""
Returns a snippet of css to include in the page
"""
snippets = [
render_template(
"css/node.css",
node_type=self.node_type
)
]
for submodule in self.submodules:
snippets.extend(submodule.csssnippets)
return snippets
def get_own_javascripts(self):
scripts = []
for module in self.submodules:
scripts.extend(module.get_own_javascripts())
return scripts
@property
def script_load(self):
"""
Load the module script for all server types, when a server node is
initialized.
"""
return ServerTypeModule.NODE_TYPE
class ServerNode(NodeView):
class ServerNode(PGChildNodeView):
node_type = ServerModule.NODE_TYPE
parent_ids = [{'type': 'int', 'id': 'gid'}]
@@ -196,20 +125,19 @@ class ServerNode(NodeView):
for server in servers:
manager = driver.connection_manager(server.id)
conn = manager.connection()
module = getattr(manager, "module", None)
connected = conn.connected()
res.append(
self.blueprint.generate_browser_node(
"%d" % (server.id),
"%d" % gid,
server.name,
"icon-server-not-connected" if not connected else
"icon-{0}".format(module.NODE_TYPE),
"icon-{0}".format(manager.server_type),
True,
self.node_type,
connected=connected,
server_type=module.type if module is not None else 'PG'
server_type=manager.server_type if connected else 'PG',
version=manager.sversion
)
)
return make_json_response(result=res)
@@ -353,7 +281,6 @@ class ServerNode(NodeView):
manager = driver.connection_manager(sid)
conn = manager.connection()
connected = conn.connected()
module = getattr(manager, 'module', None)
return ajax_response(
response={
@@ -368,8 +295,8 @@ class ServerNode(NodeView):
'comment': server.comment,
'role': server.role,
'connected': connected,
'version': manager.ver,
'server_type': module.type if module is not None else 'PG'
'version': manager.sversion,
'server_type': manager.server_type if connected else 'PG'
}
)
@@ -416,7 +343,6 @@ class ServerNode(NodeView):
return jsonify(
node=self.blueprint.generate_browser_node(
"%d" % (server.id),
"%d" % gid,
server.name,
"icon-server-not-connected",
True,
@@ -433,31 +359,6 @@ class ServerNode(NodeView):
errormsg=e.message
)
def nodes(self, gid, sid):
"""Build a list of treeview nodes from the child nodes."""
from pgadmin.utils.driver import get_driver
manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)
conn = manager.connection()
if not conn.connected():
return precondition_required(
gettext(
"Please make a connection to the server first!"
)
)
nodes = []
# We will rely on individual server type modules to generate nodes for
# them selves.
module = getattr(manager, 'module', None)
if module:
nodes.extend(module.get_nodes(sid=sid, manager=manager))
return make_json_response(data=nodes)
def sql(self, gid, sid):
return make_json_response(data='')
@@ -481,13 +382,7 @@ class ServerNode(NodeView):
return make_response(
render_template(
"servers/servers.js",
server_types=sorted(
[
m for m in self.blueprint.submodules
if isinstance(m, ServerTypeModule)
],
key=lambda x: x.priority
),
server_types=ServerType.types(),
_=gettext
),
200, {'Content-Type': 'application/x-javascript'}
@@ -572,10 +467,7 @@ class ServerNode(NodeView):
try:
status, errmsg = conn.connect(
password=password,
modules=[
m for m in self.blueprint.submodules
if isinstance(m, ServerTypeModule)
]
server_types=ServerType.types()
)
except Exception as e:
# TODO::
@@ -626,9 +518,11 @@ class ServerNode(NodeView):
info=gettext("Server Connected."),
data={
'icon': 'icon-{0}'.format(
manager.module.NODE_TYPE
manager.server_type
),
'connected': True
'connected': True,
'type': manager.server_type,
'version': manager.sversion
}
)

View File

@@ -0,0 +1,19 @@
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2015, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
from flask.ext.babel import gettext
from pgadmin.browser.server_groups.servers.types import ServerType
class PPAS(ServerType):
def instanceOf(self, ver):
return ver.startswith("EnterpriseDB")
# Default Server Type
PPAS('ppas', gettext("Postgres Plus Advanced Server"), 2)

View File

Before

Width:  |  Height:  |  Size: 850 B

After

Width:  |  Height:  |  Size: 850 B

View File

Before

Width:  |  Height:  |  Size: 676 B

After

Width:  |  Height:  |  Size: 676 B

View File

@@ -1,7 +0,0 @@
.icon-{{node_type}} {
background-image: url('{{ url_for('NODE-%s.static' % node_type, filename='img/%s.png' % node_type )}}') !important;
background-repeat: no-repeat;
align-content: center;
vertical-align: middle;
height: 1.3em;
}

View File

@@ -0,0 +1,7 @@
.icon-{{ server_type }} {
background-image: url('{{ url_for('NODE-server.static', filename='img/%s.png' % server_type)}}') !important;
background-repeat: no-repeat;
align-content: center;
vertical-align: middle;
height: 1.3em;
}

View File

@@ -69,7 +69,7 @@ function($, _, S, pgAdmin, pgBrowser, alertify) {
var input = args || {};
obj = this,
t = pgBrowser.tree,
i = input.item || t.selected(),
i = 'item' in input ? input.item : t.selected(),
d = i && i.length == 1 ? t.itemData(i) : undefined;
if (!d)
@@ -80,17 +80,21 @@ function($, _, S, pgAdmin, pgBrowser, alertify) {
S('{{ _('Are you sure you want to disconnect the server - %%s ?') }}').sprintf(d.label).value(),
function(evt) {
$.ajax({
url: obj.generate_url('connect', d, true),
url: obj.generate_url(i, 'connect', d, true),
type:'DELETE',
success: function(res) {
if (res.success == 1) {
alertify.success("{{ _('" + res.info + "') }}");
d = t.itemData(i);
t.removeIcon(i);
d.connected = false;
d.icon = 'icon-server-not-connected';
t.addIcon(i, {icon: d.icon});
t.unload(i);
t.setInode(i);
if (pgBrowser.serverInfo && d._id in pgBrowser.serverInfo) {
delete pgBrowser.serverInfo[d._id]
}
}
},
error: function(xhr, status, error) {
@@ -112,17 +116,21 @@ function($, _, S, pgAdmin, pgBrowser, alertify) {
return false;
},
/* Connect the server (if not connected), before opening this node */
beforeopen: function(item, data, browser) {
beforeopen: function(item, data) {
if(!data || data._type != 'server') {
return false;
}
browser.tree.addIcon(item, {icon: data.icon});
pgBrowser.tree.addIcon(item, {icon: data.icon});
if (!data.connected) {
connect_to_server(this, data, browser.tree, item);
connect_to_server(this, data, pgBrowser.tree, item);
return false;
}
return true;
},
opened: function(item, data) {
pgBrowser.serverInfo = pgBrowser.serverInfo || {};
pgBrowser.serverInfo[data._id] = _.extend({}, data);
}
},
model: pgAdmin.Browser.Node.Model.extend({
@@ -218,18 +226,16 @@ function($, _, S, pgAdmin, pgBrowser, alertify) {
tree.setInode(item);
if (res && res.data) {
if(typeof res.data.connected == 'boolean') {
data.connected = res.data.connected;
}
if (typeof res.data.icon == 'string') {
tree.removeIcon(item);
data.icon = res.data.icon;
tree.addIcon(item, {icon: data.icon});
}
_.extend(data, res.data);
alertify.success(res.info);
setTimeout(function() {tree.select(item);}, 10);
setTimeout(function() {tree.open(item);}, 100);
setTimeout(function() { tree.select(item); tree.open(item); }, 10);
}
};
@@ -273,7 +279,7 @@ function($, _, S, pgAdmin, pgBrowser, alertify) {
if (closeEvent.button.text == "{{ _('OK') }}") {
var _url = _model.generate_url('connect', _sdata, true);
var _url = _model.generate_url(_item, 'connect', _sdata, true);
_tree.setLeaf(_item);
_tree.removeIcon(_item);
@@ -309,7 +315,7 @@ function($, _, S, pgAdmin, pgBrowser, alertify) {
'{{ _('Connect to server') }}',
'{{ _('Do you want to connect the server?') }}',
function(evt) {
url = obj.generate_url("connect", data, true);
url = obj.generate_url(item, "connect", data, true);
$.post(url)
.done(
function(res) {

View File

@@ -0,0 +1,75 @@
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2015, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
import six
from abc import ABCMeta, abstractmethod, abstractproperty
from flask import render_template
from flask.ext.babel import gettext
class ServerType:
"""
Server Type
Create an instance of this class to define new type of the server support,
In order to define new type of instance, you may want to override this
class with overriden function - instanceOf for type checking for
identification based on the version.
"""
registry = dict()
def __init__(self, server_type, description, priority):
self.stype = server_type
self.description = description
self.priority = priority
assert(server_type not in ServerType.registry)
ServerType.registry[server_type] = self
@property
def server_type(self):
return self.stype
@property
def description(self):
return self.description
@property
def priority(self):
return self.priority
def __str__(self):
return "Type: {0}, Description:{1}, Priority: {2}".format(
self.stype, self.description, self.priority
)
def instanceOf(self, version):
return True
@property
def csssnippets(self):
"""
Returns a snippet of css to include in the page
"""
return [
render_template(
"css/server_type.css",
server_type=self.stype
)
]
@classmethod
def types(cls):
return sorted(
ServerType.registry.values(),
key=lambda x: x.priority,
reverse=True
)
# Default Server Type
ServerType('pg', gettext("PostgreSQL"), -1)