Hi,

Please find updated patch.

I have also fixed the issue in package node which was displaying create
menu in PG server. (separate patch attached)

--
Regards,
Murtuza Zabuawala
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

On Thu, Aug 25, 2016 at 4:34 PM, Akshay Joshi <akshay.jo...@enterprisedb.com
> wrote:

> Hi Murtuza
>
> Following are my review comments:
>
>    - Create "Synonym" option is visible for Postgres Servers, when right
>    click on schema node.
>
> Fixed

>
>    - Unable to create Public Synonym faced "Internal Server Error".
>
> Fixed

>
>    - SQL help not working when click on "[i]" button.
>
> Fixed, Now it is disbaled

>
>    - "Public Synonyms?" switch control showing True/False when click on
>    Synonyms collection node and it is showing Yes/No when click on synonym
>    node under properties tab.
>
> Fixed

>
> On Thu, Aug 25, 2016 at 3:08 PM, Murtuza Zabuawala <murtuza.zabuawala@
> enterprisedb.com> wrote:
>
>> Hi,
>>
>> PFA updated patch with `--binary` option for .png files.
>>
>> --
>> Regards,
>> Murtuza Zabuawala
>> EnterpriseDB: http://www.enterprisedb.com
>> The Enterprise PostgreSQL Company
>>
>> On Thu, Aug 25, 2016 at 12:27 PM, Murtuza Zabuawala <
>> murtuza.zabuaw...@enterprisedb.com> wrote:
>>
>>> Hi,
>>>
>>> PFA patch for synonym node, this patch also includes basic unit test
>>> cases.
>>> Please review.
>>>
>>> --
>>> Regards,
>>> Murtuza Zabuawala
>>> EnterpriseDB: http://www.enterprisedb.com
>>> The Enterprise PostgreSQL Company
>>>
>>
>>
>>
>> --
>> Sent via pgadmin-hackers mailing list (pgadmin-hackers@postgresql.org)
>> To make changes to your subscription:
>> http://www.postgresql.org/mailpref/pgadmin-hackers
>>
>>
>
>
> --
> *Akshay Joshi*
> *Principal Software Engineer *
>
>
>
> *Phone: +91 20-3058-9517Mobile: +91 976-788-8246*
>
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 e9d325b..a7d0571 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,12 +59,21 @@ function($, _, S, pgAdmin, pgBrowser, alertify) {
           if (data && data.check == false)
             return true;
 
-          var t = pgBrowser.tree, i = item, d = itemData;
+          var t = pgBrowser.tree, i = j = item, d = _d = itemData, server_type;
+          // Check for server type
+          while(j) {
+            if ('server' == _d._type) {
+                server_type = _d.server_type;
+            }
+              j = t.hasParent(j) ? t.parent(j) : null;
+              _d = j ? t.itemData(j) : null;
+          }
+
           // 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;
+              return true && server_type == 'ppas' ;
 
             if ('coll-package' == d._type) {
               //Check if we are not child of catalog
@@ -73,7 +82,7 @@ function($, _, S, pgAdmin, pgBrowser, alertify) {
               if( prev_d._type == 'catalog') {
                 return false;
               } else {
-                return true;
+                return true && server_type == 'ppas';
               }
             }
             i = t.hasParent(i) ? t.parent(i) : null;
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 0000000..9d2e474
--- /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 0000000000000000000000000000000000000000..7e5307f5e9301915afde5191fe795240db6f7b86
GIT binary patch
literal 448
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}U4T!BE0CUh=v?IV)hB-aKltO{
z&aZ#hefqQb-S4&o=So-ZJpA+D_OE|eeE2=>)z89BPfhDDN3VL+{P_Fn=iiPz`m*!Z
z$90$97To=kbmMc{x+mUCAI4t$6maQ-^SSqnPQIFP@VWPe5B6u?MXY>ev*5nf>33!K
zzh>O}9ChuJ@5K*}XWyHhd~3(VssVHXV@Z%-FoVOh8)-leXMsm#F_88EW4Dvpc0fj@
zr;B5V#O2!axA~e31XwN@iLwY@PYM*AvU>af|Bk!Fegz0WD=gYHQ}mEO!|Vi2g{5~|
zE?zU8@G)o7&Ks7=(o5zYyk~skhRlg4D}ES>NT2fGwcztnleBijaIvRrS1GPhjJ+d&
zWy^lk94oQE8`d;S&0qih`Ri-{{%)Sdx!@h^ivus#oB}#RwZt`|BqgyV)hf9t6-Y4{
z85kPq8XD;u7={=bSeY1GnV4!Dm|7Va%zLM|3PnS1eoAIqB}9XPC0GMcwTYF18AQXW
T>7i?Y8W=oX{an^LB{Ts5(=ExX

literal 0
HcmV?d00001

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 0000000000000000000000000000000000000000..aa2777c25eba40eb46f415143f1f0edfda67d26f
GIT binary patch
literal 478
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbMfiU6MwS0Fw2(7DLzt55v;fAGh@
z9bf;h{rG3$+h3Dk{%n2nebTmF$AA6L+xXP9{<8nFhiNxIAN%!x-}ir~pMN{@=*zC#
zAJ<=gx8&68&?_H3&VM-i^MBWtotr=ZNm={YanXIZ^Y5)rzcV=gcK5fx8$bV9`u_L+
z`=7U7|FH7>n|a4xPCf9<;?z66<8Rh~`cuAc+q73d!&g2sn|Dw5*qieEUo&ofj=J{A
z=i&#4v+qq$zSTbV<_z;vHlSM>OM?7@862M7NCR>>3p^r=fwTu0yPeFo12S?wT^vIs
zE+;1_$Ql?OWK@o5=#=D$h`4#f(n3+m=S<8Un>`8#j`NEpCW#3=5fu^>6n!i@ouSjf
z)64U8L+FW9C$$Y2Sw*H!sSFLddd0NDbn5}FEu|%24`_YiRY+s#H8(LbGc+}BztQxN
zPm6)!=qmO^>H0Sy7pRuFMwFx^mZVxG7o`Fz1|tJQ16@NST?4}qBLgcFV=EI=Z39y)
w1A}?*^j4v0$jwj5Osj-wFt7w`0ID{zGBAT^I5j<V4NwDvr>mdKI;Vst0D#`qfB*mh

literal 0
HcmV?d00001

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 0000000..02242a2
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/templates/synonym/js/synonym.js
@@ -0,0 +1,269 @@
+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: "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) {
+                    setTimeout(function() {
+                      m.set('synobjschema', 'public');
+                    }, 10)
+                    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: 'small'}
+        }
+        ],
+        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 t = pgBrowser.tree, i = j = item, d = _d = itemData, server_type;
+          // Check for server type
+          while(j) {
+            if ('server' == _d._type) {
+                server_type = _d.server_type;
+            }
+              j = t.hasParent(j) ? t.parent(j) : null;
+              _d = j ? t.itemData(j) : null;
+          }
+
+          // To iterate over tree to check parent node
+          while (i) {
+            // If it is schema then allow user to create synonym
+            if (_.indexOf(['schema'], d._type) > -1)
+              return true && server_type == 'ppas';
+
+            if ('coll-synonym' == 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 && server_type == 'ppas';
+              }
+            }
+            i = t.hasParent(i) ? t.parent(i) : null;
+            d = i ? t.itemData(i) : null;
+          }
+          // 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 0000000..22b89a8
--- /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 0000000..f697697
--- /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 0000000..c495300
--- /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 0000000..0abdfe6
--- /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 0000000..1f8259b
--- /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 0000000..ce6d59b
--- /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 0000000..de91b94
--- /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 0000000..f65eafc
--- /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 0000000..bb86f12
--- /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 0000000..5634e8d
--- /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 0000000..6e6180f
--- /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 0000000..1d5a638
--- /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 0000000..2c7e430
--- /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
-- 
Sent via pgadmin-hackers mailing list (pgadmin-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers

Reply via email to