diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/js/package.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/js/package.js index e9d325b91..e269c60c8 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/js/package.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/js/package.js @@ -46,7 +46,7 @@ function($, _, S, pgAdmin, pgBrowser, alertify) { name: 'create_package', node: 'schema', module: this, applies: ['object', 'context'], callback: 'show_obj_properties', category: 'create', priority: 4, label: '{{ _('Package...') }}', - icon: 'wcTabIcon icon-package', data: {action: 'create', check: false}, + icon: 'wcTabIcon icon-package', data: {action: 'create', check: true}, enable: 'canCreate' } ]); @@ -59,26 +59,16 @@ function($, _, S, pgAdmin, pgBrowser, alertify) { if (data && data.check == false) return true; - var t = pgBrowser.tree, i = item, d = itemData; - // To iterate over tree to check parent node - while (i) { - // If it is schema then allow user to create collation - if (_.indexOf(['schema'], d._type) > -1) - return true; + var treeData = this.getTreeNodeHierarchy(item), + server = treeData['server']; + + if (server && server.server_type === 'pg') + return false; + + // If it is catalog then don't allow user to create package + if (treeData['catalog'] != undefined) + return false; - if ('coll-package' == d._type) { - //Check if we are not child of catalog - prev_i = t.hasParent(i) ? t.parent(i) : null; - prev_d = prev_i ? t.itemData(prev_i) : null; - if( prev_d._type == 'catalog') { - return false; - } else { - return true; - } - } - i = t.hasParent(i) ? t.parent(i) : null; - d = i ? t.itemData(i) : null; - } // by default we want to allow create menu return true; }, diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/__init__.py new file mode 100644 index 000000000..9d2e474f2 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/__init__.py @@ -0,0 +1,634 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2016, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## + +""" Implements Synonym Node """ + +import simplejson as json +from functools import wraps + +import pgadmin.browser.server_groups.servers.databases as database +from flask import render_template, request, jsonify +from flask_babel import gettext +from pgadmin.browser.server_groups.servers.databases.schemas.utils \ + import SchemaChildModule +from pgadmin.browser.utils import PGChildNodeView +from pgadmin.utils.ajax import make_json_response, \ + make_response as ajax_response, internal_server_error +from pgadmin.utils.ajax import precondition_required +from pgadmin.utils.driver import get_driver + +from config import PG_DEFAULT_DRIVER + + +class SynonymModule(SchemaChildModule): + """ + class SynonymModule(CollectionNodeModule) + + A module class for Synonym node derived from CollectionNodeModule. + + Methods: + ------- + * __init__(*args, **kwargs) + - Method is used to initialize the Synonym and it's base module. + + * get_nodes(gid, sid, did, scid, syid) + - Method is used to generate the browser collection node. + + * node_inode() + - Method is overridden from its base class to make the node as leaf node. + + * script_load() + - Load the module script for schema, when any of the server node is + initialized. + """ + + NODE_TYPE = 'synonym' + COLLECTION_LABEL = gettext("Synonyms") + + def __init__(self, *args, **kwargs): + """ + Method is used to initialize the SynonymModule and it's base module. + + Args: + *args: + **kwargs: + """ + + super(SynonymModule, self).__init__(*args, **kwargs) + self.min_ver = 90100 + self.max_ver = None + self.server_type = ['ppas'] + + def get_nodes(self, gid, sid, did, scid): + """ + Generate the collection node + """ + yield self.generate_browser_collection_node(scid) + + @property + def script_load(self): + """ + Load the module script for database, when any of the database node is + initialized. + """ + return database.DatabaseModule.NODE_TYPE + + @property + def node_inode(self): + return False + + +blueprint = SynonymModule(__name__) + + +class SynonymView(PGChildNodeView): + """ + This class is responsible for generating routes for Synonym node + + Methods: + ------- + * __init__(**kwargs) + - Method is used to initialize the SynonymView and it's base view. + + * check_precondition() + - This function will behave as a decorator which will checks + database connection before running view, it will also attaches + manager,conn & template_path properties to self + + * list() + - This function is used to list all the Synonym nodes within that + collection. + + * nodes() + - This function will used to create all the child node within that + collection, Here it will create all the Synonym node. + + * properties(gid, sid, did, scid, syid) + - This function will show the properties of the selected Synonym node + + * create(gid, sid, did, scid) + - This function will create the new Synonym object + + * update(gid, sid, did, scid, syid) + - This function will update the data for the selected Synonym node + + * delete(self, gid, sid, scid, syid): + - This function will drop the Synonym object + + * msql(gid, sid, did, scid, syid) + - This function is used to return modified SQL for the selected + Synonym node + + * get_sql(data, scid, syid) + - This function will generate sql from model data + + * sql(gid, sid, did, scid): + - This function will generate sql to show it in sql pane for the + selected Synonym node. + + * dependency(gid, sid, did, scid): + - This function will generate dependency list show it in dependency + pane for the selected Synonym node. + + * dependent(gid, sid, did, scid): + - This function will generate dependent list to show it in dependent + pane for the selected Synonym node. + """ + + node_type = blueprint.node_type + + parent_ids = [ + {'type': 'int', 'id': 'gid'}, + {'type': 'int', 'id': 'sid'}, + {'type': 'int', 'id': 'did'}, + {'type': 'int', 'id': 'scid'} + ] + ids = [ + {'type': 'string', 'id': 'syid'} + ] + + operations = dict({ + 'obj': [ + {'get': 'properties', 'delete': 'delete', 'put': 'update'}, + {'get': 'list', 'post': 'create'} + ], + 'delete': [{'delete': 'delete'}], + 'children': [{'get': 'children'}], + 'nodes': [{'get': 'node'}, {'get': 'nodes'}], + 'sql': [{'get': 'sql'}], + 'msql': [{'get': 'msql'}, {'get': 'msql'}], + 'stats': [{'get': 'statistics'}], + 'dependency': [{'get': 'dependencies'}], + 'dependent': [{'get': 'dependents'}], + 'module.js': [{}, {}, {'get': 'module_js'}], + 'get_target_objects': [{'get': 'get_target_objects'}, + {'get': 'get_target_objects'}] + }) + + def check_precondition(f): + """ + This function will behave as a decorator which will checks + database connection before running view, it will also attaches + manager,conn & template_path properties to self + """ + + @wraps(f) + def wrap(*args, **kwargs): + # Here args[0] will hold self & kwargs will hold gid,sid,did + self = args[0] + self.manager = get_driver(PG_DEFAULT_DRIVER).connection_manager( + kwargs['sid'] + ) + self.conn = self.manager.connection(did=kwargs['did']) + # If DB not connected then return error to browser + if not self.conn.connected(): + return precondition_required( + gettext( + "Connection to the server has been lost!" + ) + ) + + # we will set template path for sql scripts + self.template_path = 'synonym/sql/9.1_plus' + + return f(*args, **kwargs) + + return wrap + + @check_precondition + def list(self, gid, sid, did, scid): + """ + This function is used to list all the synonym nodes within that collection. + + Args: + gid: Server group ID + sid: Server ID + did: Database ID + scid: Schema ID + + Returns: + JSON of available synonym nodes + """ + + SQL = render_template("/".join([self.template_path, + 'properties.sql']), scid=scid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return internal_server_error(errormsg=res) + return ajax_response( + response=res['rows'], + status=200 + ) + + @check_precondition + def nodes(self, gid, sid, did, scid): + """ + This function will used to create all the child node within that collection. + Here it will create all the synonym node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + scid: Schema ID + + Returns: + JSON of available synonym child nodes + """ + + res = [] + SQL = render_template("/".join([self.template_path, + 'nodes.sql']), scid=scid) + status, rset = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=rset) + + for row in rset['rows']: + res.append( + self.blueprint.generate_browser_node( + row['name'], + scid, + row['name'], + icon="icon-synonym" + )) + + return make_json_response( + data=res, + status=200 + ) + + @check_precondition + def get_target_objects(self, gid, sid, did, scid, syid=None): + """ + This function will provide list of objects as per user selection. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + scid: Schema ID + scid: Schema ID + syid: Synonym ID + + Returns: + List of objects + """ + res = [] + data = dict() + for k, v in request.args.items(): + try: + data[k] = json.loads(v, encoding='utf-8') + except ValueError: + data[k] = v + + sql = render_template("/".join([self.template_path, + 'get_objects.sql']), + trgTyp=data['trgTyp'], + trgSchema=data['trgSchema']) + status, rset = self.conn.execute_dict(sql) + + if not status: + return internal_server_error(errormsg=rset) + + for row in rset['rows']: + res.append({'label': row['name'], + 'value': row['name']}) + + return make_json_response( + data=res, + status=200 + ) + + @check_precondition + def properties(self, gid, sid, did, scid, syid): + """ + This function will show the properties of the selected synonym node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + scid: Schema ID + scid: Schema ID + syid: Synonym ID + + Returns: + JSON of selected synonym node + """ + + try: + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + scid=scid, syid=syid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return internal_server_error(errormsg=res) + + return ajax_response( + response=res['rows'][0], + status=200 + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def create(self, gid, sid, did, scid): + """ + This function will creates new the synonym object + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + scid: Schema ID + """ + + data = request.form if request.form else json.loads( + request.data, encoding='utf-8' + ) + + required_args = [ + 'name', 'targettype', 'synobjschema', 'synobjname' + ] + + for arg in required_args: + if arg not in data: + return make_json_response( + status=410, + success=0, + errormsg=gettext( + "Could not find the required parameter (%s)." % arg + ) + ) + + try: + SQL = render_template("/".join([self.template_path, + 'create.sql']), + data=data, conn=self.conn, comment=False) + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + return jsonify( + node=self.blueprint.generate_browser_node( + data['name'], + scid, + data['name'], + icon="icon-synonym" + ) + ) + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def delete(self, gid, sid, did, scid, syid): + """ + This function will delete existing the synonym object + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + scid: Schema ID + syid: Synonym ID + """ + + # Below will decide if it's simple drop or drop with cascade call + + try: + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + scid=scid, syid=syid) + + status, res = self.conn.execute_dict(SQL) + + if not status: + return internal_server_error(errormsg=res) + + data = res['rows'][0] + + if data['name'] is None: + return make_json_response( + success=0, + errormsg=gettext( + 'Error: Object not found.' + ), + info=gettext( + 'The specified synonym could not be found.\n' + ) + ) + + SQL = render_template("/".join([self.template_path, + 'delete.sql']), + data=data, + conn=self.conn) + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info=gettext("Synonym dropped"), + data={ + 'id': syid, + 'scid': scid, + 'did': did + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def update(self, gid, sid, did, scid, syid): + """ + This function will updates existing the synonym object + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + scid: Schema ID + syid: Synonym ID + """ + data = request.form if request.form else json.loads( + request.data, encoding='utf-8' + ) + SQL = self.get_sql(gid, sid, data, scid, syid) + try: + if SQL and SQL.strip('\n') and SQL.strip(' '): + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info="Synonym updated", + data={ + 'id': syid, + 'scid': scid, + 'did': did + } + ) + else: + return make_json_response( + success=1, + info="Nothing to update", + data={ + 'id': syid, + 'scid': scid, + 'did': did + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def msql(self, gid, sid, did, scid, syid=None): + """ + This function will generates modified sql for synonym object + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + scid: Schema ID + syid: Synonym ID + """ + data = dict() + for k, v in request.args.items(): + try: + data[k] = json.loads(v, encoding='utf-8') + except ValueError: + data[k] = v + + try: + SQL = self.get_sql(gid, sid, data, scid, syid) + if SQL and SQL.strip('\n') and SQL.strip(' '): + return make_json_response( + data=SQL, + status=200 + ) + except Exception as e: + return internal_server_error(errormsg=str(e)) + + def get_sql(self, gid, sid, data, scid, syid=None): + """ + This function will genrate sql from model data + """ + if syid is not None: + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + scid=scid, syid=syid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + old_data = res['rows'][0] + # If target schema/object is not present then take it from + # old data, it means it does not changed + if 'synobjschema' not in data: + data['synobjschema'] = old_data['synobjschema'] + if 'synobjname' not in data: + data['synobjname'] = old_data['synobjname'] + + SQL = render_template( + "/".join([self.template_path, 'update.sql']), + data=data, o_data=old_data, conn=self.conn + ) + else: + required_args = [ + 'name', 'targettype', 'synobjschema', 'synobjname' + ] + + for arg in required_args: + if arg not in data: + return "-- missing definition" + + SQL = render_template("/".join([self.template_path, + 'create.sql']), comment=False, + data=data, conn=self.conn) + return SQL.strip('\n') + + @check_precondition + def sql(self, gid, sid, did, scid, syid): + """ + This function will generates reverse engineered sql for synonym object + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + scid: Schema ID + syid: Synonym ID + """ + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + scid=scid, syid=syid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + data = res['rows'][0] + + SQL = render_template("/".join([self.template_path, + 'create.sql']), + data=data, conn=self.conn, comment=True) + + return ajax_response(response=SQL) + + @check_precondition + def dependents(self, gid, sid, did, scid, syid): + """ + This function get the dependents and return ajax response + for the Synonym node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + scid: Schema ID + syid: Synonym ID + """ + dependents_result = self.get_dependents( + self.conn, syid, where="WHERE dep.objid=0::oid" + ) + + return ajax_response( + response=dependents_result, + status=200 + ) + + @check_precondition + def dependencies(self, gid, sid, did, scid, syid): + """ + This function get the dependencies and return ajax response + for the Synonym node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + scid: Schema ID + syid: Synonym ID + """ + dependencies_result = self.get_dependencies( + self.conn, syid, where="WHERE dep.objid=0::oid" + ) + + return ajax_response( + response=dependencies_result, + status=200 + ) + + +SynonymView.register_node_view(blueprint) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/static/img/coll-synonym.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/static/img/coll-synonym.png new file mode 100644 index 000000000..7e5307f5e Binary files /dev/null and b/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/static/img/coll-synonym.png differ diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/static/img/synonym.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/static/img/synonym.png new file mode 100644 index 000000000..aa2777c25 Binary files /dev/null and b/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/static/img/synonym.png differ diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/templates/synonym/js/synonym.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/templates/synonym/js/synonym.js new file mode 100644 index 000000000..2d0be1fc8 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/templates/synonym/js/synonym.js @@ -0,0 +1,248 @@ +define( + ['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'], +function($, _, S, pgAdmin, pgBrowser, alertify) { + + if (!pgBrowser.Nodes['coll-synonym']) { + var databases = pgAdmin.Browser.Nodes['coll-synonym'] = + pgAdmin.Browser.Collection.extend({ + node: 'synonym', + label: '{{ _('Synonyms') }}', + type: 'coll-synonym', + columns: ['name', 'owner','is_public_synonym'] + }); + }; + + if (!pgBrowser.Nodes['synonym']) { + pgAdmin.Browser.Nodes['synonym'] = pgBrowser.Node.extend({ + type: 'synonym', + dialogHelp: '{{ url_for('help.static', filename='synonym_dialog.html') }}', + label: '{{ _('Synonym') }}', + collection_type: 'coll-synonym', + hasSQL: true, + hasDepends: true, + parent_type: ['schema', 'catalog'], + Init: function() { + /* Avoid mulitple registration of menus */ + if (this.initialized) + return; + + this.initialized = true; + + pgBrowser.add_menus([{ + name: 'create_synonym_on_coll', node: 'coll-synonym', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Synonym...') }}', + icon: 'wcTabIcon icon-synonym', data: {action: 'create', check: true}, + enable: 'canCreate' + },{ + name: 'create_synonym', node: 'synonym', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Synonym...') }}', + icon: 'wcTabIcon icon-synonym', data: {action: 'create', check: true}, + enable: 'canCreate' + },{ + name: 'create_synonym', node: 'schema', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Synonym...') }}', + icon: 'wcTabIcon icon-synonym', data: {action: 'create', check: true}, + enable: 'canCreate' + } + ]); + + }, + canDrop: pgBrowser.Nodes['schema'].canChildDrop, + model: pgAdmin.Browser.Node.Model.extend({ + isNew: function() { + return !this.fetchFromServer; + }, + idAttribute: 'name', + // Default values! + initialize: function(attrs, args) { + var isNew = (_.size(attrs) === 0); + + if (isNew) { + var userInfo = pgBrowser.serverInfo[args.node_info.server._id].user; + var schemaInfo = args.node_info.schema; + this.set({ + 'owner': userInfo.name, + 'synobjschema': schemaInfo.label, + 'schema': schemaInfo.label, + 'targettype': 'r' + }, {silent: true}); + } else { + this.fetchFromServer = true; + } + pgAdmin.Browser.Node.Model.prototype.initialize.apply(this, arguments); + + }, + schema: [{ + id: 'name', label: '{{ _('Name') }}', cell: 'string', + type: 'text', mode: ['properties', 'create', 'edit'], + disabled: 'inSchemaWithModelCheck' + },{ + id: 'owner', label:'{{ _('Owner') }}', cell: 'string', + type: 'text', mode: ['properties', 'create', 'edit'], + disabled: true , control: 'node-list-by-name', + node: 'role' + },{ + id: 'schema', label:'{{ _('Schema') }}', cell: 'string', + type: 'text', mode: ['properties', 'create', 'edit'], + disabled: true , control: 'node-list-by-name', + node: 'schema' + },{ + type: 'nested', control: 'fieldset', label: '{{ _('Definition') }}', + schema:[{ + id: 'targettype', label:'{{ _('Target Type') }}', cell: 'string', + disabled: 'inSchema', group: '{{ _('Definition') }}', + select2: { width: "50%", allowClear: false }, + options: function(obj) { + return [ + {label: "Table", value: "r"}, + {label: "Sequence", value: "S"}, + {label: "View", value: "v"}, + {label: "Function", value: "f"}, + {label: "Procedure", value: "p"}, + {label: "Public Synonym", value: "s"} + ] + }, + control: 'select2' + },{ + id: 'synobjschema', label:'{{ _('Target Schema') }}', cell: 'string', + type: 'text', mode: ['properties', 'create', 'edit'], + group: '{{ _('Definition') }}', deps: ['targettype'], + select2: { allowClear: false }, control: 'node-list-by-name', + node: 'schema', filter: function(d) { + // Exclude PPAS catalogs + var exclude_catalogs = ['pg_catalog', 'sys', 'dbo', + 'pgagent', 'information_schema', + 'dbms_job_procedure']; + return d && _.indexOf(exclude_catalogs, d.label) == -1; + }, + disabled: function(m) { + // If tagetType is synonym then disable it + if(!m.inSchema.apply(this, [m])) { + var is_synonym = (m.get('targettype') == 's'); + if(is_synonym) { + m.set('synobjschema', 'public', {silent: true}); + return true; + } else { + return false; + } + } + return true; + } + },{ + id: 'synobjname', label:'{{ _('Target Object') }}', cell: 'string', + type: 'text', disabled: 'inSchema', group: '{{ _('Definition') }}', + deps: ['targettype', 'synobjschema'], + control: 'node-ajax-options', + options: function(control) { + var trgTyp = control.model.get('targettype'); + var trgSchema = control.model.get('synobjschema'); + var res = []; + + var node = control.field.get('schema_node'), + _url = node.generate_url.apply( + node, [ + null, 'get_target_objects', control.field.get('node_data'), false, + control.field.get('node_info') ]); + $.ajax({ + type: 'GET', + timeout: 30000, + url: _url, + cache: false, + async: false, + data: {"trgTyp" : trgTyp, "trgSchema" : trgSchema}, + + // On success return function list from server + success: function(result) { + res = result.data; + return res; + }, + + // On failure show error appropriate error message to user + error: function(xhr, status, error) { + try { + var err = $.parseJSON(xhr.responseText); + if (err.success == 0) { + alertify.error(err.errormsg); + } + } catch (e) {} + } + }); + return res; + } + }] + },{ + id: 'is_public_synonym', label:'{{ _('Public Synonym?') }}', + disabled: true, type: 'switch', mode: ['properties'], cell: 'switch', + options: { onText: 'Yes', offText: 'No', onColor: 'success', + offColor: 'primary', size: 'mini'} + } + ], + validate: function() { + var err = {}, + msg = undefined; + this.errorModel.clear(); + + if (_.isUndefined(this.get('name')) + || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') { + msg = '{{ _('Name cannot be empty.') }}'; + this.errorModel.set('name', msg); + } else if (_.isUndefined(this.get('synobjschema')) + || String(this.get('synobjschema')).replace(/^\s+|\s+$/g, '') == '') { + msg = '{{ _('Target schema cannot be empty.') }}'; + this.errorModel.set('synobjschema', msg); + } else if (_.isUndefined(this.get('synobjname')) + || String(this.get('synobjname')).replace(/^\s+|\s+$/g, '') == '') { + msg = '{{ _('Target object cannot be empty.') }}'; + this.errorModel.set('synobjname', msg); + } + return null; + }, + // We will disable everything if we are under catalog node + inSchema: function() { + if(this.node_info && 'catalog' in this.node_info) + { + return true; + } + return false; + }, + // We will check if we are under schema node & in 'create' mode + inSchemaWithModelCheck: function(m) { + if(this.node_info && 'schema' in this.node_info) + { + // We will disbale control if it's in 'edit' mode + if (m.isNew()) { + return false; + } else { + return true; + } + } + return true; + } + }), + canCreate: function(itemData, item, data) { + //If check is false then , we will allow create menu + if (data && data.check == false) + return true; + + var treeData = this.getTreeNodeHierarchy(item), + server = treeData['server']; + + if (server && server.server_type === 'pg') + return false; + + // If it is catalog then don't allow user to create synonyms + if (treeData['catalog'] != undefined) + return false; + + // by default we do not want to allow create menu + return true; + } + }); + + } + + return pgBrowser.Nodes['synonym']; +}); diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/templates/synonym/sql/9.1_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/templates/synonym/sql/9.1_plus/create.sql new file mode 100644 index 000000000..22b89a842 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/templates/synonym/sql/9.1_plus/create.sql @@ -0,0 +1,17 @@ +{% set is_public = False %} +{% if data.schema == 'public' %} +{% set is_public = True %} +{% endif %} +{% if comment %} +-- {% if is_public %}Public{% else %}Private{% endif %} synonym: {{ conn|qtIdent(data.schema, data.name) }}; + +-- DROP {% if is_public %}PUBLIC {% endif %}SYNONYM {{ conn|qtIdent(data.schema, data.name) }}; + +{% endif %} +CREATE OR REPLACE {% if is_public %} +PUBLIC SYNONYM {{ conn|qtIdent(data.name) }} +{% else %} +SYNONYM {{ conn|qtIdent(data.schema, data.name) }} +{% endif %} + FOR {{ conn|qtIdent(data.synobjschema, data.synobjname) }}; + diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/templates/synonym/sql/9.1_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/templates/synonym/sql/9.1_plus/delete.sql new file mode 100644 index 000000000..f6976976e --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/templates/synonym/sql/9.1_plus/delete.sql @@ -0,0 +1,8 @@ +{% set is_public = False %} +{% if data.schema == 'public' %} +{% set is_public = True %} +{% endif %} +DROP {% if is_public %} +PUBLIC SYNONYM {{ conn|qtIdent(data.name) }}{% else %} +SYNONYM {{ conn|qtIdent(data.schema, data.name) }} +{% endif %}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/templates/synonym/sql/9.1_plus/get_objects.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/templates/synonym/sql/9.1_plus/get_objects.sql new file mode 100644 index 000000000..c495300c3 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/templates/synonym/sql/9.1_plus/get_objects.sql @@ -0,0 +1,43 @@ +{###########################################} +{### If Target Type is Function ###} +{###########################################} +{% if trgTyp == 'f' %} +SELECT DISTINCT proname AS name + FROM pg_proc p, pg_namespace n +WHERE p.pronamespace = n.oid AND + n.nspname = {{ trgSchema|qtLiteral }} AND + p.protype = '0' +ORDER BY proname; +{###########################################} +{### If Target Type is Procedure ###} +{###########################################} +{% elif trgTyp == 'p' %} +SELECT DISTINCT proname AS name + FROM pg_proc p, pg_namespace n +WHERE p.pronamespace = n.oid AND + n.nspname = {{ trgSchema|qtLiteral }} AND + p.protype = '1' +ORDER BY proname; +{###########################################} +{### If Target Type is Synonym ###} +{###########################################} +{% elif trgTyp == 's' %} +SELECT synname AS name + FROM pg_synonym +ORDER BY synname; +{% else %} +{###################################################} +{### If Target Type is Table/View/M.View/Sequnce ###} +{###################################################} +SELECT relname AS name + FROM pg_class c, pg_namespace n +WHERE c.relnamespace = n.oid AND + n.nspname = {{ trgSchema|qtLiteral }} AND +{% if trgTyp == 'v' %} +{# If view is select then we need to fetch both view and materialized view #} + (c.relkind = 'v' OR c.relkind = 'm') +{% else %} + c.relkind = {{ trgTyp|qtLiteral }} +{% endif %} +ORDER BY relname; +{% endif %} \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/templates/synonym/sql/9.1_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/templates/synonym/sql/9.1_plus/get_oid.sql new file mode 100644 index 000000000..0abdfe6dc --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/templates/synonym/sql/9.1_plus/get_oid.sql @@ -0,0 +1,8 @@ +{# Below will provide oid for newly created collation #} +{% if data %} +SELECT c.oid +FROM pg_collation c, pg_namespace n +WHERE c.collnamespace=n.oid AND + n.nspname = {{ data.schema|qtLiteral }} AND + c.collname = {{ data.name|qtLiteral }} +{% endif %} \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/templates/synonym/sql/9.1_plus/nodes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/templates/synonym/sql/9.1_plus/nodes.sql new file mode 100644 index 000000000..1f8259b4a --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/templates/synonym/sql/9.1_plus/nodes.sql @@ -0,0 +1,5 @@ +SELECT synname as name +FROM pg_synonym s + JOIN pg_namespace ns ON s.synnamespace = ns.oid + AND s.synnamespace = {{scid}}::oid +ORDER BY synname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/templates/synonym/sql/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/templates/synonym/sql/9.1_plus/properties.sql new file mode 100644 index 000000000..ce6d59b14 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/templates/synonym/sql/9.1_plus/properties.sql @@ -0,0 +1,19 @@ +SELECT synname AS name, pg_get_userbyid(synowner) AS owner, + synobjschema, synobjname, ns.nspname as schema, + COALESCE((SELECT relkind + FROM pg_class c, pg_namespace n + WHERE c.relnamespace = n.oid + AND n.nspname = synobjschema + AND c.relname = synobjname), + (SELECT CASE WHEN p.protype = '0' THEN 'f'::"char" ELSE 'p'::"char" END + FROM pg_proc p, pg_namespace n + WHERE p.pronamespace = n.oid + AND n.nspname = synobjschema + AND p.proname = synobjname LIMIT 1), 's') AS targettype, -- Default s = Synonym + CASE WHEN ns.nspname = 'public' THEN true ELSE false END AS is_public_synonym +FROM pg_synonym s JOIN pg_namespace ns ON s.synnamespace = ns.oid + WHERE s.synnamespace={{scid}}::oid + {% if syid %} + AND s.synname={{ syid|qtLiteral }} + {% endif %} +ORDER BY synname; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/templates/synonym/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/templates/synonym/sql/9.1_plus/update.sql new file mode 100644 index 000000000..de91b9413 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/templates/synonym/sql/9.1_plus/update.sql @@ -0,0 +1,10 @@ +{% set is_public = False %} +{% if o_data.schema == 'public' %} +{% set is_public = True %} +{% endif %} +CREATE OR REPLACE {% if is_public %} +PUBLIC SYNONYM {{ conn|qtIdent(o_data.name) }} +{% else %} +SYNONYM {{ conn|qtIdent(o_data.schema, o_data.name) }} +{% endif %} + FOR {{ conn|qtIdent(data.synobjschema, data.synobjname) }}; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/tests/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/tests/__init__.py new file mode 100644 index 000000000..f65eafc25 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/tests/__init__.py @@ -0,0 +1,17 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2016, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## + +from pgadmin.utils.route import BaseTestGenerator + + +class SynonymTestGenerator(BaseTestGenerator): + + def generate_tests(self): + return + diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/tests/test_synonym_add.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/tests/test_synonym_add.py new file mode 100644 index 000000000..bb86f1263 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/tests/test_synonym_add.py @@ -0,0 +1,74 @@ +# ################################################################# +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2016, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +# ################################################################## + +from regression import test_utils as utils +from pgadmin.utils.route import BaseTestGenerator +from pgadmin.browser.server_groups.servers.tests import utils as server_utils +from pgadmin.browser.server_groups.servers.databases.tests import utils as \ + database_utils +from pgadmin.browser.server_groups.servers.databases.schemas.tests import \ + utils as schema_utils +from . import utils as synonym_utils + + +class SynonymAddTestCase(BaseTestGenerator): + """ This class will add new synonym under schema node. """ + + scenarios = [ + # Fetching default URL for synonym node. + ('Default Node URL', dict(url='/browser/synonym/obj/')) + ] + + @classmethod + def setUpClass(cls): + """ + This function perform the three tasks + 1. Add the test server + 2. Connect to server + 3. Add the databases + 4. Add the schemas + + :return: None + """ + + # Firstly, add the server + server_utils.add_server(cls.tester) + # Connect to server + cls.server_connect_response, cls.server_group, cls.server_ids = \ + server_utils.connect_server(cls.tester) + if len(cls.server_connect_response) == 0: + raise Exception("No Server(s) connected to add the database!!!") + # Add database + database_utils.add_database(cls.tester, cls.server_connect_response, + cls.server_ids) + # Add schemas + schema_utils.add_schemas(cls.tester) + + def runTest(self): + """ This function will add synonym under schema node. """ + + synonym_utils.add_synonym( + self.tester, self.server_connect_response, self.server_ids) + + @classmethod + def tearDownClass(cls): + """ + This function deletes the added synonyms, schemas, database, + server and the 'parent_id.pkl' file which is created in setup() + function. + + :return: None + """ + + synonym_utils.delete_synonym(cls.tester) + schema_utils.delete_schema(cls.tester) + database_utils.delete_database(cls.tester) + server_utils.delete_server(cls.tester) + utils.delete_parent_id_file() + diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/tests/test_synonym_delete.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/tests/test_synonym_delete.py new file mode 100644 index 000000000..5634e8d67 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/tests/test_synonym_delete.py @@ -0,0 +1,77 @@ +# ################################################################# +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2016, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +# ################################################################## + + +from regression import test_utils as utils +from pgadmin.utils.route import BaseTestGenerator +from pgadmin.browser.server_groups.servers.tests import utils as server_utils +from pgadmin.browser.server_groups.servers.databases.tests import utils as \ + database_utils +from pgadmin.browser.server_groups.servers.databases.schemas.tests import \ + utils as schema_utils +from . import utils as synonym_utils + + +class SynonymDeleteTestCase(BaseTestGenerator): + """ This class will delete added synonym under schema node. """ + + scenarios = [ + # Fetching default URL for synonym node. + ('Fetch synonym Node URL', dict(url='/browser/synonym/obj/')) + ] + + @classmethod + def setUpClass(cls): + """ + This function perform the three tasks + 1. Add the test server + 2. Connect to server + 3. Add the databases + 4. Add the schemas + 5. Add the synonyms + + :return: None + """ + + # Firstly, add the server + server_utils.add_server(cls.tester) + # Connect to server + cls.server_connect_response, cls.server_group, cls.server_ids = \ + server_utils.connect_server(cls.tester) + if len(cls.server_connect_response) == 0: + raise Exception("No Server(s) connected to add the database!!!") + # Add database + database_utils.add_database(cls.tester, cls.server_connect_response, + cls.server_ids) + # Add schemas + schema_utils.add_schemas(cls.tester) + # Add synonyms + synonym_utils.add_synonym(cls.tester, cls.server_connect_response, + cls.server_ids) + + def runTest(self): + """ This function will delete synonym under schema node. """ + + synonym_utils.delete_synonym(self.tester) + + @classmethod + def tearDownClass(cls): + """ + This function deletes the added schemas, database, + server and the 'parent_id.pkl' file which is created in setup() + function. + + :return: None + """ + + schema_utils.delete_schema(cls.tester) + database_utils.delete_database(cls.tester) + server_utils.delete_server(cls.tester) + utils.delete_parent_id_file() + diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/tests/test_synonym_get.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/tests/test_synonym_get.py new file mode 100644 index 000000000..6e6180f28 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/tests/test_synonym_get.py @@ -0,0 +1,101 @@ +# ################################################################# +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2016, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +# ################################################################## + +import json + +from regression import test_utils as utils +from pgadmin.utils.route import BaseTestGenerator +from pgadmin.browser.server_groups.servers.tests import utils as server_utils +from pgadmin.browser.server_groups.servers.databases.tests import utils as \ + database_utils +from pgadmin.browser.server_groups.servers.databases.schemas.tests import \ + utils as schema_utils +from . import utils as synonym_utils + + +class SynonymGetTestCase(BaseTestGenerator): + """ This class will fetch new synonym under schema node. """ + + scenarios = [ + # Fetching default URL for synonym node. + ('Fetch synonym Node URL', dict(url='/browser/synonym/obj/')) + ] + + @classmethod + def setUpClass(cls): + """ + This function perform the three tasks + 1. Add the test server + 2. Connect to server + 3. Add the databases + 4. Add the schemas + 5. Add the synonyms + + :return: None + """ + + # Firstly, add the server + server_utils.add_server(cls.tester) + # Connect to server + cls.server_connect_response, cls.server_group, cls.server_ids = \ + server_utils.connect_server(cls.tester) + if len(cls.server_connect_response) == 0: + raise Exception("No Server(s) connected to add the database!!!") + # Add database + database_utils.add_database(cls.tester, cls.server_connect_response, + cls.server_ids) + # Add schemas + schema_utils.add_schemas(cls.tester) + # Add synonyms + synonym_utils.add_synonym(cls.tester, cls.server_connect_response, + cls.server_ids) + + def runTest(self): + """ This function will fetch synonym under schema node. """ + + all_id = utils.get_ids() + server_ids = all_id["sid"] + db_ids_dict = all_id["did"][0] + schema_ids_dict = all_id["scid"][0] + synonym_ids_dict = all_id["syid"][0] + + for server_id in server_ids: + db_id = db_ids_dict[int(server_id)] + db_con = database_utils.verify_database(self.tester, + utils.SERVER_GROUP, + server_id, db_id) + if db_con['data']["connected"]: + schema_info = schema_ids_dict[int(server_id)] + schema_response = schema_utils.verify_schemas( + self.tester, server_id, db_id, schema_info[0]) + schema_response = json.loads( + schema_response.data.decode('utf-8')) + if len(schema_response) != 0: + synonym_id = synonym_ids_dict[int(server_id)] + get_response = synonym_utils.verify_synonym( + self.tester, server_id, db_id, schema_info[0], + synonym_id) + self.assertEquals(get_response.status_code, 200) + + @classmethod + def tearDownClass(cls): + """ + This function deletes the added synonyms, schemas, database, + server and the 'parent_id.pkl' file which is created in setup() + function. + + :return: None + """ + + synonym_utils.delete_synonym(cls.tester) + schema_utils.delete_schema(cls.tester) + database_utils.delete_database(cls.tester) + server_utils.delete_server(cls.tester) + utils.delete_parent_id_file() + diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/tests/test_synonym_put.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/tests/test_synonym_put.py new file mode 100644 index 000000000..1d5a63830 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/tests/test_synonym_put.py @@ -0,0 +1,125 @@ +# ################################################################# +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2016, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +# ################################################################## + +import json + +from regression import test_utils as utils +from pgadmin.utils.route import BaseTestGenerator +from regression.test_setup import advanced_config_data +from pgadmin.browser.server_groups.servers.tests import utils as server_utils +from pgadmin.browser.server_groups.servers.databases.tests import utils as \ + database_utils +from pgadmin.browser.server_groups.servers.databases.schemas.tests import \ + utils as schema_utils +from . import utils as synonym_utils + + +class SynonymPutTestCase(BaseTestGenerator): + """ This class will update added synonym under schema node. """ + + scenarios = [ + # Fetching default URL for synonym node. + ('Fetch synonym Node URL', dict(url='/browser/synonym/obj/')) + ] + + @classmethod + def setUpClass(cls): + """ + This function perform the three tasks + 1. Add the test server + 2. Connect to server + 3. Add the databases + 4. Add the schemas + 5. Add the synonyms + + :return: None + """ + + # Firstly, add the server + server_utils.add_server(cls.tester) + # Connect to server + cls.server_connect_response, cls.server_group, cls.server_ids = \ + server_utils.connect_server(cls.tester) + if len(cls.server_connect_response) == 0: + raise Exception("No Server(s) connected to add the database!!!") + # Add database + database_utils.add_database(cls.tester, cls.server_connect_response, + cls.server_ids) + # Add schemas + schema_utils.add_schemas(cls.tester) + # Add synonyms + synonym_utils.add_synonym(cls.tester, cls.server_connect_response, + cls.server_ids) + + def runTest(self): + """ This function will update synonym under schema node. """ + + all_id = utils.get_ids() + server_ids = all_id["sid"] + db_ids_dict = all_id["did"][0] + schema_ids_dict = all_id["scid"][0] + synonym_ids_dict = all_id["syid"][0] + + for server_id in server_ids: + db_id = db_ids_dict[int(server_id)] + db_con = database_utils.verify_database(self.tester, + utils.SERVER_GROUP, + server_id, db_id) + if db_con['data']["connected"]: + schema_info = schema_ids_dict[int(server_id)] + schema_response = schema_utils.verify_schemas(self.tester, + server_id, + db_id, + schema_info[0]) + schema_response = json.loads( + schema_response.data.decode('utf-8')) + if len(schema_response) != 0: + synonym_id = synonym_ids_dict[int(server_id)] + get_response = synonym_utils.verify_synonym( + self.tester, server_id, db_id, schema_info[0], + synonym_id) + + get_response_data = json.loads( + get_response.data.decode('utf-8')) + if len(get_response_data) == 0: + raise Exception("No synonym node to update.") + + data = { + "description": + advanced_config_data['synonym_update_data'] + ['comment'], + "id": synonym_id, + } + + put_response = self.tester.put( + self.url + str(utils.SERVER_GROUP) + '/' + + str(server_id) + '/' + + str(db_id) + '/' + + str(schema_info[0]) + '/' + + str(synonym_id), + data=json.dumps(data), + follow_redirects=True) + + self.assertEquals(put_response.status_code, 200) + + @classmethod + def tearDownClass(cls): + """ + This function deletes the added synonyms, schemas, database, + server and the 'parent_id.pkl' file which is created in setup() + function. + + :return: None + """ + + synonym_utils.delete_synonym(cls.tester) + schema_utils.delete_schema(cls.tester) + database_utils.delete_database(cls.tester) + server_utils.delete_server(cls.tester) + utils.delete_parent_id_file() diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/tests/utils.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/tests/utils.py new file mode 100644 index 000000000..2c7e430b2 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/tests/utils.py @@ -0,0 +1,154 @@ +# ########################################################################## +# +# #pgAdmin 4 - PostgreSQL Tools +# +# #Copyright (C) 2013 - 2016, The pgAdmin Development Team +# #This software is released under the PostgreSQL Licence +# +# ########################################################################## + +import json +import os +import pickle + +from regression.test_setup import pickle_path, advanced_config_data +from pgadmin.browser.server_groups.servers.databases.tests import \ + utils as database_utils +from pgadmin.browser.server_groups.servers.databases.schemas.tests import \ + utils as schema_utils +from regression import test_utils as utils + +SYNONYM_URL = '/browser/synonym/obj/' + + +def get_synonym_config_data(server_connect_data): + """This function returns the synonym config data""" + + adv_config_data = None + data = None + db_user = server_connect_data['data']['user']['name'] + + # Get the config data of appropriate db user + for config_test_data in \ + advanced_config_data['synonym_credentials']: + if db_user == config_test_data['owner']: + adv_config_data = config_test_data + + if adv_config_data is not None: + data = { + "name": adv_config_data['name'], + "schema": adv_config_data['schema'], + "synobjname": adv_config_data['synobjname'], + "synobjschema": adv_config_data['synobjschema'], + "targettype": adv_config_data['targettype'] + } + return data + + +def write_synonym_id(response_data, server_id): + """ + This function writes the server and synonym id + + :param response_data: synonym response data + :type response_data: dict + :param server_id: server id + :type server_id: int + :return: None + """ + + synonym_id = response_data['node']['_id'] + pickle_id_dict = utils.get_pickle_id_dict() + if os.path.isfile(pickle_path): + existing_server_id = open(pickle_path, 'rb') + tol_server_id = pickle.load(existing_server_id) + pickle_id_dict = tol_server_id + if 'syid' in pickle_id_dict: + if pickle_id_dict['syid']: + # Add the db_id as value in dict + pickle_id_dict["syid"][0].update( + {int(server_id): synonym_id}) + else: + # Create new dict with server_id and db_id + pickle_id_dict["syid"].append( + {int(server_id): synonym_id}) + db_output = open(pickle_path, 'wb') + pickle.dump(pickle_id_dict, db_output) + db_output.close() + + +def add_synonym(tester, server_connect_response, server_ids): + """This function add the synonym to schemas""" + + all_id = utils.get_ids() + db_ids_dict = all_id["did"][0] + schema_ids_dict = all_id["scid"][0] + + for server_connect_response, server_id in zip(server_connect_response, + server_ids): + db_id = db_ids_dict[int(server_id)] + db_con = database_utils.verify_database(tester, utils.SERVER_GROUP, + server_id, db_id) + if db_con['data']["connected"]: + schema_info = schema_ids_dict[int(server_id)] + schema_utils.verify_schemas(tester, server_id, db_id, + schema_info[0]) + data = get_synonym_config_data(server_connect_response) + data['schema'] = schema_info[1] + response = tester.post( + SYNONYM_URL + str(utils.SERVER_GROUP) + '/' + str(server_id) + + '/' + str(db_id) + '/' + str(schema_info[0]) + '/', + data=json.dumps(data), content_type='html/json') + response_data = json.loads(response.data.decode('utf-8')) + write_synonym_id(response_data, server_id) + + +def verify_synonym(tester, server_id, db_id, schema_id, synonym_id): + """This function verifies the synonym using GET API""" + + get_response = tester.get( + SYNONYM_URL + str(utils.SERVER_GROUP) + '/' + str(server_id) + '/' + + str(db_id) + '/' + str(schema_id) + '/' + str(synonym_id), + content_type='html/json') + + return get_response + + +def delete_synonym(tester): + """This function deletes the synonyms from schema""" + + all_id = utils.get_ids() + server_ids = all_id["sid"] + db_ids_dict = all_id["did"][0] + schema_ids_dict = all_id["scid"][0] + synonym_ids_dict = all_id["syid"][0] + + for server_id in server_ids: + db_id = db_ids_dict[int(server_id)] + db_con = database_utils.verify_database(tester, utils.SERVER_GROUP, + server_id, db_id) + if db_con['data']["connected"]: + schema_info = schema_ids_dict[int(server_id)] + schema_response = schema_utils.verify_schemas(tester, server_id, + db_id, + schema_info[0]) + schema_response = json.loads(schema_response.data.decode('utf-8')) + if len(schema_response) != 0: + synonym_id = synonym_ids_dict[int(server_id)] + get_response = verify_synonym( + tester, server_id, db_id, schema_info[0], synonym_id) + + get_response_data = json.loads( + get_response.data.decode('utf-8')) + if len(get_response_data) == 0: + raise Exception("No synonym node to delete.") + + del_response = tester.delete( + SYNONYM_URL + str(utils.SERVER_GROUP) + '/' + + str(server_id) + '/' + str(db_id) + '/' + + str(schema_info[0]) + '/' + str(synonym_id), + follow_redirects=True) + + assert del_response.status_code == 200 + del_response_data = json.loads( + del_response.data.decode('utf-8')) + assert del_response_data['success'] == 1