Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8dca4b0e0b | ||
|
|
f7dd8cbab4 | ||
|
|
c56e4667df | ||
|
|
689ef8d8d1 | ||
|
|
b6f307256b | ||
|
|
2c6f6609bc | ||
|
|
f5249f2e18 | ||
|
|
2d75eac117 | ||
|
|
f12d981a9d | ||
|
|
1b05464a04 | ||
|
|
5a78dd7ad3 | ||
|
|
7b72448ec9 | ||
|
|
05473a7a35 | ||
|
|
e016869ad5 | ||
|
|
8d7b840e7e | ||
|
|
a535eddfcd | ||
|
|
0ce8b031f8 | ||
|
|
6775adc4c7 | ||
|
|
58a79e4fc6 | ||
|
|
cf1be2a320 | ||
|
|
fe54a124da | ||
|
|
4c2020cdc7 | ||
|
|
5690dfda16 | ||
|
|
d5f6c63ead | ||
|
|
761b54ce69 | ||
|
|
0f99c3b60c | ||
|
|
80f1c2e7ac | ||
|
|
eca47efb03 | ||
|
|
33b4817008 | ||
|
|
86858b18f6 | ||
|
|
d5b0cd29d8 | ||
|
|
7dffb020f1 | ||
|
|
013ff1090e | ||
|
|
d6391c7e9b | ||
|
|
2b1b60bc2b | ||
|
|
8e0a61f556 | ||
|
|
050937a32a | ||
|
|
48a50ce20b | ||
|
|
6701bb191d | ||
|
|
1056e9553f | ||
|
|
91e3d3267f | ||
|
|
b3f25cb4bb | ||
|
|
9ec05d6bb1 | ||
|
|
c336e8a743 | ||
|
|
99b4a0fe5b | ||
|
|
67f481ab11 | ||
|
|
6d839a2924 | ||
|
|
a3e8ba93ae | ||
|
|
035066bd41 | ||
|
|
81e2bc1e80 | ||
|
|
2b13d55016 | ||
|
|
9dfc7bff35 | ||
|
|
33457e6a90 | ||
|
|
48384cd6cd | ||
|
|
8918b8894d | ||
|
|
a025c0ebf0 | ||
|
|
2aea97b798 | ||
|
|
1bcac0b67c | ||
|
|
37ca3579f3 | ||
|
|
5900848842 | ||
|
|
7cf4ac2474 | ||
|
|
f78024808e | ||
|
|
8e099e29c3 | ||
|
|
a43f053a10 | ||
|
|
b00e94e904 | ||
|
|
42f8745aba | ||
|
|
cb35c46f03 | ||
|
|
8c433fbfce | ||
|
|
dfceb63986 | ||
|
|
68497b00f6 | ||
|
|
de568b3021 | ||
|
|
5d52767ba7 | ||
|
|
4f90a5d4f4 | ||
|
|
1ec1e9663c | ||
|
|
ec3b9bb15b | ||
|
|
01252a13cf | ||
|
|
e1cc3dded2 | ||
|
|
16304aafd2 | ||
|
|
9c4eed70b4 | ||
|
|
94aa36f3d5 | ||
|
|
5987af1b44 | ||
|
|
5c3c543d2e | ||
|
|
b6e8d195dc | ||
|
|
f4f8273994 | ||
|
|
06e881570a | ||
|
|
166d42953c | ||
|
|
d3d96d5c05 | ||
|
|
e803464ee0 | ||
|
|
19306934a0 | ||
|
|
790b28fb61 | ||
|
|
875360d1c8 | ||
|
|
9dd3cf2399 | ||
|
|
dbd3c7f4c5 | ||
|
|
dcf5bfc495 | ||
|
|
91559c622b | ||
|
|
60659975ed | ||
|
|
a014cf82a1 | ||
|
|
f783aeac63 | ||
|
|
3d7b40e111 | ||
|
|
a7d7577c7c | ||
|
|
419ead9121 | ||
|
|
417af3676d | ||
|
|
8b35a60c08 | ||
|
|
8e29d16eec | ||
|
|
83a1535f89 | ||
|
|
a6024fc49a | ||
|
|
ae9bbc0305 | ||
|
|
f17c2e3b84 | ||
|
|
c7d25c33f2 | ||
|
|
5560d5b334 | ||
|
|
9ba6bafb2b | ||
|
|
d15dfac60f | ||
|
|
e94b14e7d7 | ||
|
|
517a797947 | ||
|
|
1a129cf30e | ||
|
|
efb254c966 | ||
|
|
573abbe6ef | ||
|
|
fdcb9d2a57 | ||
|
|
f79ca32e45 | ||
|
|
ae692d2416 | ||
|
|
f58c4df85d | ||
|
|
9ad0bd1026 | ||
|
|
47e849dee0 | ||
|
|
d22ea8bf61 | ||
|
|
1ada5031cd | ||
|
|
6fa4065eee | ||
|
|
2b809523eb | ||
|
|
889b69ac82 | ||
|
|
61698b7b4d | ||
|
|
52d1af63a5 | ||
|
|
4077de2187 | ||
|
|
6c6660e576 | ||
|
|
1bd27b847b | ||
|
|
3ee8861d44 | ||
|
|
d4c3b1bcff | ||
|
|
8a12850525 | ||
|
|
51a33e57c9 | ||
|
|
9c39fe2f2e | ||
|
|
7b3b7736d9 | ||
|
|
c45960df5e | ||
|
|
d79524ff60 | ||
|
|
a1d8ca8c1c | ||
|
|
685b2f4ab7 | ||
|
|
aa44122a8a | ||
|
|
80e108aa55 | ||
|
|
71443deceb | ||
|
|
0282ef7992 | ||
|
|
c4504f1dc2 | ||
|
|
d795f37fe5 | ||
|
|
b8c9ccc939 | ||
|
|
8246fc56e8 | ||
|
|
53434030c1 | ||
|
|
9396cb03d5 | ||
|
|
5d0c160fb8 | ||
|
|
1222681888 | ||
|
|
7fbefa507b | ||
|
|
4267545e08 | ||
|
|
4bb849c58c | ||
|
|
78d329fc47 | ||
|
|
e9ad27678f | ||
|
|
d63b54610b | ||
|
|
a7e1e25cf4 | ||
|
|
8f643a1946 | ||
|
|
a1f979c3e4 | ||
|
|
30682c2a68 | ||
|
|
1acc1a7678 | ||
|
|
e4ef927da3 | ||
|
|
8b0e65dc57 | ||
|
|
5928f70a1d | ||
|
|
09569a23bd | ||
|
|
4983eb2f1c | ||
|
|
97d49af43a | ||
|
|
e5ee592470 | ||
|
|
b78b3904cd |
No files matched your search
@@ -23,8 +23,8 @@ pgadmin4.log
|
||||
/mac-build
|
||||
/src-build
|
||||
/pip-build
|
||||
/win-build
|
||||
/dist
|
||||
pkg/win32/win-build
|
||||
pkg/win32/installer.iss
|
||||
runtime/Makefile.Debug
|
||||
runtime/Makefile.Release
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
.. _backup_globals_dialog:
|
||||
|
||||
****************************
|
||||
The Backup Globals... Dialog
|
||||
****************************
|
||||
*************************
|
||||
The Backup Globals Dialog
|
||||
*************************
|
||||
|
||||
Use the *Backup Globals...* dialog to create a plain-text script that recreates all of the database objects within a cluster, and the global objects that are shared by those databases. Global objects include tablespaces, roles, and object properties. You can use the pgAdmin *Query Tool* to play back a plain-text script, and recreate the objects in the backup.
|
||||
Use the *Backup Globals* dialog to create a plain-text script that recreates all of the database objects within a cluster, and the global objects that are shared by those databases. Global objects include tablespaces, roles, and object properties. You can use the pgAdmin *Query Tool* to play back a plain-text script, and recreate the objects in the backup.
|
||||
|
||||
.. image:: images/backup_globals_general.png
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
.. _backup_server_dialog:
|
||||
|
||||
****************************
|
||||
The Backup Server... Dialog
|
||||
****************************
|
||||
*************************
|
||||
The Backup Server Dialog
|
||||
*************************
|
||||
|
||||
Use the *Backup Server...* dialog to create a plain-text script that will recreate the selected server. You can use the pgAdmin *Query Tool* to play back a plain-text script, and recreate the server.
|
||||
Use the *Backup Server* dialog to create a plain-text script that will recreate the selected server. You can use the pgAdmin *Query Tool* to play back a plain-text script, and recreate the server.
|
||||
|
||||
.. image:: images/backup_server_general.png
|
||||
|
||||
@@ -24,5 +24,6 @@ Click the *Backup* button to build and execute a command based on your selection
|
||||
|
||||
If the backup is successful, a popup window will confirm success. Click *Click here for details* on the popup window to launch the *Process Watcher*. The *Process Watcher* logs all the activity associated with the backup and provides additional information for troubleshooting.
|
||||
|
||||
.. image:: images/backup_server_process_watcher.png
|
||||
␍If the backup is unsuccessful, review the error message returned by the *Process Watcher* to resolve any issue.
|
||||
.. image:: images/backup_server_process_watcher.png
|
||||
|
||||
If the backup is unsuccessful, review the error message returned by the *Process Watcher* to resolve any issue.
|
||||
@@ -4,21 +4,27 @@
|
||||
Connecting to a Server
|
||||
**********************
|
||||
|
||||
Before you can use the pgAdmin client to manage your Postgres installation, you must define a connection to the server.
|
||||
Before you can use the pgAdmin client to manage the objects that reside on your Postgres server, you must define a connection to the server. You can (optionally) use the *Server Group* dialog to create server groups to organize the server connections within the tree control for easier management. To open the *Server Group* dialog, right-click on the *Servers* node of the tree control, and select *Server Group* from the *Create* menu.
|
||||
|
||||
Contents:
|
||||
|
||||
.. toctree::
|
||||
|
||||
server_group_dialog
|
||||
|
||||
Use the fields on the *Server* dialog to define the connection properties for each new server that you wish to manage with pgAdmin. To open the *Server* dialog, right-click on the *Servers* node of the tree control, and select *Server* from the *Create* menu.
|
||||
|
||||
Contents:
|
||||
|
||||
.. toctree::
|
||||
|
||||
server_dialog
|
||||
|
||||
After defining a server connection, right-click on the server name, and select *Connect to server* to authenticate with the server, and start using pgAdmin to manage objects that reside on the server.
|
||||
|
||||
Contents:
|
||||
|
||||
.. toctree::
|
||||
|
||||
connect_to_server
|
||||
connect_error
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
connect_error
|
||||
@@ -4,9 +4,31 @@
|
||||
Getting Started
|
||||
***************
|
||||
|
||||
pgAdmin 4 features a highly-customizable display that features drag-and-drop panels that you can arrange to make the best use of your desktop environment.
|
||||
Pre-compiled and configured installation packages for pgAdmin 4 are available for a number of desktop environments; we recommend using an installer whenever possible. A standard installation using the pgAdmin installer is a server deployment.
|
||||
|
||||
When you install pgAdmin in server mode, you will be prompted to provide a role name and pgAdmin password. The first role registered with pgAdmin will be an administrative user. When you authenticate with pgAdmin, pgAdmin displays the server definitions associated with that login role in the tree control. The tree control provides an elegant overview of the managed servers, and the objects that reside on each server. Right-click on a node within the tree control to access context-sensitive menus that provide quick access to management tasks for the selected object.
|
||||
In a Server Deployment, the pgAdmin application is deployed behind a webserver or with the WSGI interface.
|
||||
If you install pgAdmin in server mode, you will be prompted to provide a role name and pgAdmin password when you initially connect to pgAdmin. The first role registered with pgAdmin will be an administrative user; the administrative role can use the pgAdmin *User Management* dialog to create and manage additional pgAdmin user accounts. When a user authenticates with pgAdmin, the pgAdmin tree control displays the server definitions associated with that login role.
|
||||
|
||||
Contents:
|
||||
|
||||
.. toctree::
|
||||
|
||||
server_deployment
|
||||
pgadmin_login
|
||||
pgadmin_user
|
||||
|
||||
In a Desktop Deployment, the pgAdmin application is configured to use the desktop runtime environment to host and display the program on a supported platform. Typically, users will install a pre-built package to run pgAdmin in desktop mode, but a manual desktop deployment can be installed and though it is more difficult to setup, it may be useful for developers interested in understanding how pgAdmin works.
|
||||
|
||||
Contents:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
desktop_deployment
|
||||
|
||||
The pgAdmin 4 client features a highly-customizable display that features drag-and-drop panels that you can arrange to make the best use of your desktop environment.
|
||||
|
||||
The tree control provides an elegant overview of the managed servers, and the objects that reside on each server. Right-click on a node within the tree control to access context-sensitive menus that provide quick access to management tasks for the selected object.
|
||||
|
||||
The tabbed browser provide quick access to statistical information about each object in the tree control, and pgAdmin tools and utilities (such as the Query tool and the debugger). pgAdmin opens additional feature tabs each time you access the extended functionality offered by pgAdmin tools; you can open, close, and re-arrange feature tabs as needed.
|
||||
|
||||
@@ -14,17 +36,20 @@ Use the *Preferences* dialog to customize the content and colors of the pgAdmin
|
||||
|
||||
*Help* buttons in the lower-left corner of each dialog will open the online help for the dialog. You can access additional Postgres help by navigating through the *Help* menu, and selecting the name of the resource that you wish to open.
|
||||
|
||||
An administrative role can use the pgAdmin User Management dialog to create and manage additional pgAdmin user accounts.
|
||||
|
||||
Contents:
|
||||
|
||||
.. toctree::
|
||||
|
||||
pgadmin_login
|
||||
browser
|
||||
pgadmin_menu_bar
|
||||
pgadmin_tabbed_browser
|
||||
pgadmin_tree_control
|
||||
preferences
|
||||
pgadmin_user
|
||||
|
||||
Before using pgAdmin to manage objects that reside on a server, you must define a connection to the server; for more information please see *Connecting to a Server*:
|
||||
|
||||
Contents:
|
||||
|
||||
.. toctree::
|
||||
|
||||
connecting
|
||||
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 77 KiB |
|
After Width: | Height: | Size: 45 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
After Width: | Height: | Size: 63 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
After Width: | Height: | Size: 61 KiB |
|
After Width: | Height: | Size: 93 KiB |
@@ -1,10 +1,10 @@
|
||||
.. _maintenance_dialog:
|
||||
|
||||
*************************
|
||||
The Maintenance... Dialog
|
||||
*************************
|
||||
**********************
|
||||
The Maintenance Dialog
|
||||
**********************
|
||||
|
||||
Use the *Maintenance...* dialog to VACUUM, ANALYZE, REINDEX or CLUSTER a database or selected database objects.
|
||||
Use the *Maintenance* dialog to VACUUM, ANALYZE, REINDEX or CLUSTER a database or selected database objects.
|
||||
|
||||
.. image:: images/maintenance.png
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ Contents:
|
||||
procedure_dialog
|
||||
schema_dialog
|
||||
sequence_dialog
|
||||
trigger_function_dialog
|
||||
type_dialog
|
||||
user_mapping_dialog
|
||||
view_dialog
|
||||
@@ -50,8 +50,10 @@ Expand the **SQL Editor** node to specify your preferences for the SQL Editor to
|
||||
|
||||
.. image:: images/preferences_sql_display.png
|
||||
|
||||
Use the *Display* dialog to specify your preferences for the SQL Editor display. Specify a value in the *Items per page in grid* to instruct the *SQL Editor* on how many rows to display per page.
|
||||
The default is *50*.
|
||||
Use the *Display* dialog to specify your preferences for the SQL Editor display.
|
||||
|
||||
* Specify a value in the *Items per page in grid* field to instruct the *SQL Editor* on how many rows to display per page. The default is *50*.
|
||||
* Use the *Query info notifier timeout* to control the behaviour of the notifier that is displayed when query execution completes. A value of *-1* will disable the notifier, and a value of 0 will display it until clicked. If a positive value above zero is specified, the notifier will be displayed for the specified number of seconds. The default is *5*.
|
||||
|
||||
.. image:: images/preferences_sql_explain_options.png
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ application may be configured similarly to the example below:
|
||||
<VirtualHost *>
|
||||
ServerName pgadmin.example.com
|
||||
|
||||
WSGIDaemonProcess pgadmin processes=1
|
||||
WSGIDaemonProcess pgadmin processes=1 threads=25
|
||||
WSGIScriptAlias / /opt/pgAdmin4/web/pgAdmin4.wsgi
|
||||
|
||||
<Directory /opt/pgAdmin4/web>
|
||||
@@ -80,4 +80,19 @@ application may be configured similarly to the example below:
|
||||
Order deny,allow
|
||||
Allow from all
|
||||
</Directory>
|
||||
</VirtualHost>
|
||||
</VirtualHost>
|
||||
|
||||
**Note:** If you're using Apache HTTPD 2.4 or later, replace the lines:
|
||||
|
||||
.. code-block:: apache
|
||||
|
||||
Order deny,allow
|
||||
Allow from all
|
||||
|
||||
with:
|
||||
|
||||
.. code-block:: apache
|
||||
|
||||
Require all granted
|
||||
|
||||
Adjust as needed to suit your access control requirements.
|
||||
@@ -0,0 +1,102 @@
|
||||
.. _trigger_function_dialog:
|
||||
|
||||
***************************
|
||||
The Trigger function Dialog
|
||||
***************************
|
||||
|
||||
Use the *Trigger function* dialog to create or manage a trigger_function. A trigger function defines the action that will be invoked when a trigger fires.
|
||||
|
||||
The *Trigger function* dialog organizes the development of a trigger function through the following dialog tabs: *General*, *Definition*, *Options*, *Parameters* and *Security*. The *SQL* tab displays the SQL code generated by dialog selections.
|
||||
|
||||
.. image:: images/trigger_function_general.png
|
||||
|
||||
Use the fields in the *General* tab to identify the trigger function:
|
||||
|
||||
* Use the *Name* field to add a descriptive name for the trigger function. The name will be displayed in the *pgAdmin* tree control. Please note that trigger functions will be invoked in alphabetical order.
|
||||
* Use the drop-down listbox next to *Owner* to select a role.
|
||||
* Select the name of the schema in which the trigger function will reside from the drop-down listbox in the *Schema* field.
|
||||
* Store notes about the trigger function in the *Comment* field.
|
||||
|
||||
Click the *Definition* tab to continue.
|
||||
|
||||
.. image:: images/trigger_function_definition.png
|
||||
|
||||
Use the fields in the *Definition* tab to define the trigger function:
|
||||
|
||||
* Use the drop-down listbox next to *Return type* to specify the pseudotype that is associated with the trigger function:
|
||||
|
||||
* Select *trigger* if you are creating a DML trigger.
|
||||
* Select *event_trigger* if you are creating a DDL trigger.
|
||||
|
||||
* Use the drop-down listbox next to *Language* to select the implementation language. The default is *plpgsql*.
|
||||
* Use the *Code* field to write the code that will execute when the trigger function is called.
|
||||
|
||||
Click the *Options* tab to continue.
|
||||
|
||||
.. image:: images/trigger_function_options.png
|
||||
|
||||
Use the fields in the *Options* tab to describe or modify the action of the trigger function:
|
||||
|
||||
* Use the drop-down listbox next to *Volatility* to select one of the following:
|
||||
|
||||
* *VOLATILE* indicates that the trigger function value can change even within a single table scan.
|
||||
* *STABLE* indicates that the trigger function cannot modify the database, and that within a single table scan it will consistently return the same result for the same argument values.
|
||||
* *IMMUTABLE* indicates that the trigger function cannot modify the database and always returns the same result when given the same argument values.
|
||||
|
||||
* Move the *Returns a Set?* switch to indicate if the trigger function returns a set that includes multiple rows. The default is *No*.
|
||||
* Move the *Strict?* switch to indicate if the trigger function always returns NULL whenever any of its arguments are NULL. If *Yes*, the function is not executed when there are NULL arguments; instead a NULL result is assumed automatically. The default is *No*.
|
||||
* Move the *Security of definer?* switch to specify that the trigger function is to be executed with the privileges of the user that created it. The default is *No*.
|
||||
* Move the *Window?* switch to indicate that the trigger function is a window function rather than a plain function. The default is *No*. This is currently only useful for trigger functions written in C.
|
||||
* Use the *Estimated cost* field to specify a positive number representing the estimated execution cost for the trigger function, in units of cpu_operator_cost. If the function returns a set, this is the cost per returned row.
|
||||
* Use the *Estimated rows* field to specify a positive number giving the estimated number of rows that the query planner should expect the trigger function to return. This is only allowed when the function is declared to return a set. The default assumption is 1000 rows.
|
||||
* Move the *Leak proof?* switch to indicate whether the trigger function has side effects. The default is *No*. This option can only be set by the superuser.
|
||||
|
||||
Click the *Parameters* tab to continue.
|
||||
|
||||
.. image:: images/trigger_function_parameters.png
|
||||
|
||||
Use the fields in the *Parameters* tab to specify settings that will be applied when the trigger function is invoked:
|
||||
|
||||
* Use the drop-down listbox next to *Parameter Name* in the *Parameters* panel to select a parameter.
|
||||
* Click the *Add* button to add the variable to *Name* field in the table.
|
||||
* Use the *Value* field to specify the value that will be associated with the selected variable. This field is context-sensitive.
|
||||
|
||||
Click the *Security* tab to continue.
|
||||
|
||||
.. image:: images/trigger_function_security.png
|
||||
|
||||
Use the *Security* tab to assign privileges and define security labels.
|
||||
|
||||
Use the *Privileges* panel to assign usage privileges for the trigger function to a role. Click *Add* to to add a role to the table.
|
||||
|
||||
* Select the name of the role from the drop-down listbox in the *Grantee* field.
|
||||
* Click inside the *Privileges* field. Check the boxes to the left of one or more privileges to grant the selected privilege to the specified user.
|
||||
* Select the name of a role from the drop-down listbox in the *Grantor* field. The default grantor is the owner of the database.
|
||||
|
||||
Click *Add* to assign additional privileges; to discard a privilege, click the trash icon to the left of the row and confirm deletion in the *Delete Row* popup.
|
||||
|
||||
Use the *Security Labels* panel to define security labels applied to the trigger function. Click *Add* to add each security label selection:
|
||||
|
||||
* Specify a security label provider in the *Provider* field. The named provider must be loaded and must consent to the proposed labeling operation.
|
||||
* Specify a a security label in the *Security Label* field. The meaning of a given label is at the discretion of the label provider. PostgreSQL places no restrictions on whether or how a label provider must interpret security labels; it merely provides a mechanism for storing them.
|
||||
|
||||
Click *Add* to assign additional security labels; to discard a security label, click the trash icon to the left of the row and confirm deletion in the *Delete Row* popup.
|
||||
|
||||
Click the *SQL* tab to continue.
|
||||
|
||||
Your entries in the *Trigger function* dialog generate a SQL command (see an example below). Use the *SQL* tab for review; revisit other tabs to modify the SQL command.
|
||||
|
||||
**Example**
|
||||
|
||||
The following is an example of the sql command generated by user selections in the *Trigger function* dialog:
|
||||
|
||||
.. image:: images/trigger_function_sql.png
|
||||
|
||||
The example shown demonstrates creating a trigger function named *emp_stamp* that checks for a new employee's name, and checks that the employee's salary is a positive value.
|
||||
|
||||
* Click the *Info* button (i) to access online help. View context-sensitive help in the *Tabbed browser*, where a new tab displays the PostgreSQL core documentation.
|
||||
* Click the *Save* button to save work.
|
||||
* Click the *Cancel* button to exit without saving work.
|
||||
* Click the *Reset* button to restore configuration parameters.
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ Underscore.string 387ab72d49 MIT http://epeli.github.io/underscore.s
|
||||
Backform.js 5859b4f9db MIT https://github.com/AmiliaApp/backform
|
||||
Backbone 1.1.2 MIT http://backbonejs.org
|
||||
font-Awesome 4.5 SIL OFL http://fortawesome.github.io/Font-Awesome/
|
||||
font-mfizz 1.2 MIT http://fizzed.com/oss/font-mfizz
|
||||
font-mfizz 2.3 MIT http://fizzed.com/oss/font-mfizz
|
||||
backgrid.js 0.3.5 MIT http://backgridjs.com/
|
||||
backbone.undo 0.2 MIT http://backbone.undojs.com/
|
||||
bootstrap-switch 3.3.2 MIT http://www.bootstrap-switch.org/
|
||||
@@ -30,3 +30,6 @@ backgrid-select-all 1a00053 MIT https://github.com/wyuenho/backgrid
|
||||
dropzone 4e20bd4 MIT https://github.com/enyo/dropzone
|
||||
Filemanager 7e060c2 MIT https://github.com/simogeo/Filemanager
|
||||
Unit (Length) d8e6237 MIT https://github.com/heygrady/Units/blob/master/Length.min.js
|
||||
Natural Sort 9565816 MIT https://github.com/javve/natural-sort/blob/master/index.js
|
||||
SlickGrid 2.2.4 MIT https://github.com/6pac/SlickGrid
|
||||
jQuery-UI 1.11.3 MIT https://jqueryui.com/
|
||||
@@ -4,15 +4,15 @@ blinker==1.3
|
||||
django-htmlmin==0.8.0
|
||||
extras==0.0.3
|
||||
fixtures==2.0.0
|
||||
Flask==0.10.1
|
||||
Flask-Babel==0.9
|
||||
Flask==0.11.1
|
||||
Flask-Babel==0.11.1
|
||||
Flask-Gravatar==0.4.2
|
||||
Flask-Login==0.2.11
|
||||
Flask-Login==0.3.2
|
||||
Flask-Mail==0.9.1
|
||||
Flask-Principal==0.4.0
|
||||
Flask-Security==1.7.4
|
||||
Flask-SQLAlchemy==2.0
|
||||
Flask-WTF==0.11
|
||||
Flask-Security==1.7.5
|
||||
Flask-SQLAlchemy==2.1
|
||||
Flask-WTF==0.12
|
||||
html5lib==1.0b3
|
||||
importlib==1.0.3
|
||||
itsdangerous==0.24
|
||||
@@ -21,7 +21,7 @@ linecache2==1.0.0
|
||||
MarkupSafe==0.23
|
||||
passlib==1.6.2
|
||||
pbr==1.9.1
|
||||
psycopg2==2.5.2
|
||||
psycopg2==2.6.2
|
||||
pycrypto==2.6.1
|
||||
pyrsistent==0.11.13
|
||||
python-dateutil==2.5.0
|
||||
@@ -30,7 +30,7 @@ pytz==2014.10
|
||||
simplejson==3.6.5
|
||||
six==1.9.0
|
||||
speaklater==1.3
|
||||
SQLAlchemy==0.9.8
|
||||
SQLAlchemy==1.0.14
|
||||
testscenarios==0.5.0
|
||||
testtools==2.0.0
|
||||
traceback2==1.4.0
|
||||
@@ -38,3 +38,4 @@ unittest2==1.1.0
|
||||
Werkzeug==0.9.6
|
||||
WTForms==2.0.2
|
||||
sqlparse==0.1.19
|
||||
ordereddict; python_version < '2.7'
|
||||
@@ -4,15 +4,15 @@ blinker==1.3
|
||||
django-htmlmin==0.8.0
|
||||
extras==0.0.3
|
||||
fixtures==2.0.0
|
||||
Flask==0.10.1
|
||||
Flask-Babel==0.9
|
||||
Flask==0.11.1
|
||||
Flask-Babel==0.11.1
|
||||
Flask-Gravatar==0.4.2
|
||||
Flask-Login==0.2.11
|
||||
Flask-Login==0.3.2
|
||||
Flask-Mail==0.9.1
|
||||
Flask-Principal==0.4.0
|
||||
Flask-Security==1.7.4
|
||||
Flask-SQLAlchemy==2.0
|
||||
Flask-WTF==0.11
|
||||
Flask-Security==1.7.5
|
||||
Flask-SQLAlchemy==2.1
|
||||
Flask-WTF==0.12
|
||||
html5lib==1.0b3
|
||||
itsdangerous==0.24
|
||||
Jinja2==2.7.3
|
||||
@@ -20,7 +20,7 @@ linecache2==1.0.0
|
||||
MarkupSafe==0.23
|
||||
passlib==1.6.2
|
||||
pbr==1.9.1
|
||||
psycopg2==2.5.2
|
||||
psycopg2==2.6.2
|
||||
pycrypto==2.6.1
|
||||
pyrsistent==0.11.13
|
||||
python-dateutil==2.5.0
|
||||
@@ -29,7 +29,7 @@ pytz==2014.10
|
||||
simplejson==3.6.5
|
||||
six==1.9.0
|
||||
speaklater==1.3
|
||||
SQLAlchemy==0.9.8
|
||||
SQLAlchemy==1.0.14
|
||||
testscenarios==0.5.0
|
||||
testtools==2.0.0
|
||||
traceback2==1.4.0
|
||||
|
||||
@@ -23,7 +23,8 @@
|
||||
#include <QInputDialog>
|
||||
#include <QLineEdit>
|
||||
#endif
|
||||
|
||||
#include <QNetworkRequest>
|
||||
#include <QNetworkReply>
|
||||
// App headers
|
||||
#include "BrowserWindow.h"
|
||||
#include "ConfigWindow.h"
|
||||
@@ -42,6 +43,15 @@ BrowserWindow::BrowserWindow(QString url)
|
||||
m_widget = NULL;
|
||||
m_toolBtnBack = NULL;
|
||||
m_toolBtnForward = NULL;
|
||||
m_downloadStarted = 0;
|
||||
m_downloadCancelled = 0;
|
||||
m_file = NULL;
|
||||
m_downloadFilename = "";
|
||||
m_defaultFilename = "";
|
||||
m_progressDialog = NULL;
|
||||
m_last_open_folder_path = "";
|
||||
m_dir = "";
|
||||
m_reply = NULL;
|
||||
|
||||
m_appServerUrl = url;
|
||||
|
||||
@@ -83,6 +93,11 @@ BrowserWindow::BrowserWindow(QString url)
|
||||
// Register the slot on tab index change
|
||||
connect(m_tabWidget,SIGNAL(currentChanged(int )),this,SLOT(tabIndexChanged(int )));
|
||||
|
||||
// Listen for download file request from the web page
|
||||
m_mainWebView->page()->setForwardUnsupportedContent(true);
|
||||
connect(m_mainWebView->page(), SIGNAL(downloadRequested(const QNetworkRequest &)), this, SLOT(download(const QNetworkRequest &)));
|
||||
connect(m_mainWebView->page(), SIGNAL(unsupportedContent(QNetworkReply*)), this, SLOT(unsupportedContent(QNetworkReply*)));
|
||||
|
||||
m_mainWebView->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks);
|
||||
|
||||
// Restore the geometry
|
||||
@@ -90,6 +105,9 @@ BrowserWindow::BrowserWindow(QString url)
|
||||
restoreGeometry(settings.value("Browser/Geometry").toByteArray());
|
||||
restoreState(settings.value("Browser/WindowState").toByteArray());
|
||||
|
||||
// The last save location
|
||||
m_last_open_folder_path = settings.value("Browser/LastSaveLocation", QDir::homePath()).toString();
|
||||
|
||||
// Display the app
|
||||
m_initialLoad = true;
|
||||
m_loadAttempt = 1;
|
||||
@@ -110,12 +128,12 @@ void BrowserWindow::closeEvent(QCloseEvent *event)
|
||||
void BrowserWindow::createActions()
|
||||
{
|
||||
// Open an arbitrary URL
|
||||
openUrlShortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_U), this);
|
||||
openUrlShortcut = new QShortcut(QKeySequence(Qt::ALT + Qt::SHIFT + Qt::Key_U), this);
|
||||
openUrlShortcut->setContext(Qt::ApplicationShortcut);
|
||||
connect(openUrlShortcut, SIGNAL(activated()), this, SLOT(openUrl()));
|
||||
|
||||
// Set the Python Path
|
||||
preferencesShortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_P), this);
|
||||
preferencesShortcut = new QShortcut(QKeySequence(Qt::ALT + Qt::SHIFT + Qt::Key_P), this);
|
||||
preferencesShortcut->setContext(Qt::ApplicationShortcut);
|
||||
connect(preferencesShortcut, SIGNAL(activated()), this, SLOT(preferences()));
|
||||
|
||||
@@ -125,7 +143,7 @@ void BrowserWindow::createActions()
|
||||
connect(exitShortcut, SIGNAL(activated()), this, SLOT(close()));
|
||||
|
||||
// About box
|
||||
aboutShortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_A), this);
|
||||
aboutShortcut = new QShortcut(QKeySequence(Qt::ALT + Qt::SHIFT + Qt::Key_A), this);
|
||||
aboutShortcut->setContext(Qt::ApplicationShortcut);
|
||||
connect(aboutShortcut, SIGNAL(activated()), this, SLOT(about()));
|
||||
}
|
||||
@@ -199,6 +217,312 @@ int BrowserWindow::findURLTab(const QUrl &name)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Below slot will be called when user right click on download link and select "Save Link..." option from context menu
|
||||
void BrowserWindow::download(const QNetworkRequest &request)
|
||||
{
|
||||
// Check that request contains data for download at client side
|
||||
QUrl name;
|
||||
|
||||
if (m_downloadStarted)
|
||||
{
|
||||
// Inform user that a download is already started
|
||||
QMessageBox::information(this, tr("Download warning"), tr("File download already in progress: %1").arg(m_defaultFilename));
|
||||
return;
|
||||
}
|
||||
|
||||
m_defaultFilename = QFileInfo(request.url().toString()).fileName();
|
||||
|
||||
// Open the dialog to save file
|
||||
QFileDialog save_dialog(this);
|
||||
save_dialog.setAcceptMode(QFileDialog::AcceptSave);
|
||||
save_dialog.setWindowTitle(tr("Save file"));
|
||||
save_dialog.setDirectory(m_last_open_folder_path);
|
||||
save_dialog.selectFile(m_defaultFilename);
|
||||
|
||||
// Register the slot for directory travesing when file dialog is opened and save the last open directory
|
||||
QObject::connect(&save_dialog, SIGNAL(directoryEntered(const QString &)), this, SLOT(current_dir_path(const QString &)));
|
||||
m_dir = m_last_open_folder_path;
|
||||
QString fileName = "";
|
||||
QString f_name = "";
|
||||
|
||||
if (save_dialog.exec() == QDialog::Accepted) {
|
||||
fileName = save_dialog.selectedFiles().first();
|
||||
f_name = fileName.replace(m_dir, "");
|
||||
// Remove the first character(/) from fiename
|
||||
f_name.remove(0,1);
|
||||
m_defaultFilename = f_name;
|
||||
}
|
||||
else
|
||||
return;
|
||||
|
||||
fileName = m_dir + fileName;
|
||||
// Clear the last open directory path
|
||||
m_dir.clear();
|
||||
|
||||
#ifdef __APPLE__
|
||||
// Check that user has given valid file name or not - forward slash is not allowed in file name
|
||||
// In Mac OSX, forward slash is converted to colon(:) by Qt so we need to check for colon.
|
||||
if (f_name.indexOf(":") != -1)
|
||||
{
|
||||
QMessageBox::information(this, tr("File name error"), tr("Invalid file name"));
|
||||
return;
|
||||
}
|
||||
#else
|
||||
// Check that user has given valid file name or not - forward slash is not allowed in file name
|
||||
if (f_name.indexOf("/") != -1)
|
||||
{
|
||||
QMessageBox::information(this, tr("File name error"), tr("Invalid file name"));
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (fileName.isEmpty())
|
||||
return;
|
||||
else
|
||||
{
|
||||
m_downloadFilename = fileName;
|
||||
|
||||
QNetworkRequest newRequest = request;
|
||||
newRequest.setAttribute(QNetworkRequest::User, fileName);
|
||||
|
||||
QObject *obj_web_page = QObject::sender();
|
||||
if (obj_web_page != NULL)
|
||||
{
|
||||
QWebPage *sender_web_page = dynamic_cast<QWebPage*>(obj_web_page);
|
||||
if (sender_web_page != NULL)
|
||||
{
|
||||
QNetworkAccessManager *networkManager = sender_web_page->networkAccessManager();
|
||||
QNetworkReply *reply = networkManager->get(newRequest);
|
||||
if (reply != NULL)
|
||||
{
|
||||
m_downloadStarted = 1;
|
||||
m_downloadCancelled = 0;
|
||||
// Connect the signal for downloadProgress and downloadFinished
|
||||
connect( reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(downloadFileProgress(qint64, qint64)) );
|
||||
connect( reply, SIGNAL(finished()), this, SLOT(downloadFinished()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Below slot will be called when file download is in progress
|
||||
void BrowserWindow::downloadFileProgress(qint64 readData, qint64 totalData)
|
||||
{
|
||||
QNetworkReply *reply = ((QNetworkReply*)sender());
|
||||
QNetworkRequest request = reply->request();
|
||||
QVariant v = request.attribute(QNetworkRequest::User);
|
||||
|
||||
// When download is canceled by user then no need to write data to file
|
||||
if (m_downloadCancelled)
|
||||
return;
|
||||
|
||||
if(reply != NULL && reply->error() != QNetworkReply::NoError)
|
||||
{
|
||||
qDebug() << "Network error occurred whilst downloading: " << m_defaultFilename;
|
||||
return;
|
||||
}
|
||||
|
||||
// Download is started so open the file
|
||||
if (!m_file)
|
||||
{
|
||||
m_file = new QFile(m_downloadFilename);
|
||||
if (!m_file->open(QIODevice::WriteOnly))
|
||||
{
|
||||
qDebug() << "Error opening file: " << m_downloadFilename;
|
||||
m_downloadFilename.clear();
|
||||
m_defaultFilename.clear();
|
||||
m_downloadStarted = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Create progress bar dialog
|
||||
m_progressDialog = new QProgressDialog (tr("Downloading file: %1 ").arg(m_defaultFilename), "Cancel", readData, totalData, this);
|
||||
m_progressDialog->setWindowModality(Qt::WindowModal);
|
||||
m_progressDialog->setWindowTitle(tr("Download progress"));
|
||||
m_progressDialog->setMinimumWidth(450);
|
||||
m_progressDialog->setMinimumHeight(80);
|
||||
m_progressDialog->setWindowFlags(Qt::Window | Qt::CustomizeWindowHint | Qt::WindowMinimizeButtonHint | Qt::WindowCloseButtonHint);
|
||||
// Register slot for file download cancel request
|
||||
QObject::connect(m_progressDialog, SIGNAL(canceled()), this, SLOT(progressCanceled()));
|
||||
m_reply = reply;
|
||||
// Show downloading progress bar
|
||||
m_progressDialog->show();
|
||||
}
|
||||
|
||||
if (m_file)
|
||||
{
|
||||
// Write data to file
|
||||
m_file->write(reply->read(readData));
|
||||
m_progressDialog->setValue(readData);
|
||||
|
||||
// As read data and totalData difference is zero means downloading is finished.
|
||||
if ((totalData - readData) == 0 ||
|
||||
(readData != 0 && totalData == -1))
|
||||
{
|
||||
// As downloading is finished so remove progress bar dialog
|
||||
if (m_progressDialog)
|
||||
{
|
||||
m_progressDialog->deleteLater();
|
||||
m_progressDialog = NULL;
|
||||
}
|
||||
|
||||
m_downloadStarted = 0;
|
||||
m_downloadFilename.clear();
|
||||
m_defaultFilename.clear();
|
||||
m_downloadCancelled = 0;
|
||||
if (m_file)
|
||||
{
|
||||
m_file->close();
|
||||
delete m_file;
|
||||
m_file = NULL;
|
||||
}
|
||||
|
||||
if (m_reply)
|
||||
m_reply = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Below slot will be called when user cancel the downloading file which is in progress.
|
||||
void BrowserWindow::progressCanceled()
|
||||
{
|
||||
m_downloadCancelled = 1;
|
||||
|
||||
if (m_progressDialog)
|
||||
{
|
||||
m_progressDialog->deleteLater();
|
||||
m_progressDialog = NULL;
|
||||
}
|
||||
|
||||
if (m_file)
|
||||
{
|
||||
m_file->close();
|
||||
// Remove the file from file system as downloading is canceled by user
|
||||
m_file->remove();
|
||||
delete m_file;
|
||||
m_file = NULL;
|
||||
}
|
||||
|
||||
if (m_reply)
|
||||
{
|
||||
m_reply->abort();
|
||||
m_reply = NULL;
|
||||
}
|
||||
|
||||
m_downloadFilename.clear();
|
||||
m_defaultFilename.clear();
|
||||
m_downloadStarted = 0;
|
||||
}
|
||||
|
||||
// Below slot will called when file download is finished
|
||||
void BrowserWindow::downloadFinished()
|
||||
{
|
||||
if (m_progressDialog)
|
||||
{
|
||||
m_progressDialog->deleteLater();
|
||||
m_progressDialog = NULL;
|
||||
}
|
||||
|
||||
m_downloadFilename.clear();
|
||||
m_defaultFilename.clear();
|
||||
m_downloadStarted = 0;
|
||||
m_downloadCancelled = 0;
|
||||
if (m_file)
|
||||
{
|
||||
m_file->close();
|
||||
delete m_file;
|
||||
m_file = NULL;
|
||||
}
|
||||
|
||||
if (m_reply)
|
||||
m_reply = NULL;
|
||||
}
|
||||
|
||||
// Below slot will be called when user directly click on any download link
|
||||
void BrowserWindow::unsupportedContent(QNetworkReply * reply)
|
||||
{
|
||||
#if QT_VERSION >= 0x050000
|
||||
// Extract filename and query from encoded URL
|
||||
QUrlQuery query_data(reply->url());
|
||||
QString file_name = query_data.queryItemValue("filename");
|
||||
QString query = query_data.queryItemValue("query");
|
||||
#else
|
||||
QUrl url(reply->url());
|
||||
QString file_name = url.queryItemValue("filename");
|
||||
QString query = url.queryItemValue("query");
|
||||
#endif
|
||||
|
||||
if (m_downloadStarted)
|
||||
{
|
||||
// Inform user that download is already started
|
||||
QMessageBox::information(this, tr("Download warning"), tr("File download already in progress: %1").arg(m_defaultFilename));
|
||||
return;
|
||||
}
|
||||
|
||||
// If encoded URL contains 'filename' attribute then use that filename in file dialog.
|
||||
if (file_name.isEmpty() && query.isEmpty())
|
||||
m_defaultFilename = QFileInfo(reply->url().toString()).fileName();
|
||||
else
|
||||
m_defaultFilename = file_name;
|
||||
|
||||
QFileDialog save_dialog(this);
|
||||
save_dialog.setAcceptMode(QFileDialog::AcceptSave);
|
||||
save_dialog.setWindowTitle(tr("Save file"));
|
||||
save_dialog.setDirectory(m_last_open_folder_path);
|
||||
save_dialog.selectFile(m_defaultFilename);
|
||||
|
||||
QObject::connect(&save_dialog, SIGNAL(directoryEntered(const QString &)), this, SLOT(current_dir_path(const QString &)));
|
||||
m_dir = m_last_open_folder_path;
|
||||
QString fileName = "";
|
||||
QString f_name = "";
|
||||
|
||||
if (save_dialog.exec() == QDialog::Accepted) {
|
||||
fileName = save_dialog.selectedFiles().first();
|
||||
f_name = fileName.replace(m_dir, "");
|
||||
// Remove the first character(/) from fiename
|
||||
f_name.remove(0,1);
|
||||
m_defaultFilename = f_name;
|
||||
}
|
||||
else
|
||||
return;
|
||||
|
||||
fileName = m_dir + fileName;
|
||||
// Clear last open folder path
|
||||
m_dir.clear();
|
||||
|
||||
#ifdef __APPLE__
|
||||
// Check that user has given valid file name or not - forward slash is not allowed in file name
|
||||
// In Mac OSX, forward slash is converted to colon(:) by Qt so we need to check for colon.
|
||||
if (f_name.indexOf(":") != -1)
|
||||
{
|
||||
QMessageBox::information(this, tr("File name error"), tr("Invalid file name"));
|
||||
return;
|
||||
}
|
||||
#else
|
||||
// Check that user has given valid file name or not - forward slash is not allowed in file name
|
||||
if (f_name.indexOf("/") != -1)
|
||||
{
|
||||
QMessageBox::information(this, tr("File name error"), tr("Invalid file name"));
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (fileName.isEmpty())
|
||||
return;
|
||||
else
|
||||
{
|
||||
m_downloadFilename = fileName;
|
||||
if (reply != NULL)
|
||||
{
|
||||
m_downloadStarted = 1;
|
||||
m_downloadCancelled = 0;
|
||||
connect( reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(downloadFileProgress(qint64, qint64)));
|
||||
connect( reply, SIGNAL(finished()), this, SLOT(downloadFinished()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Slot: When the tab index change, hide/show the toolbutton displayed on tab
|
||||
void BrowserWindow::tabIndexChanged(int index)
|
||||
{
|
||||
@@ -340,9 +664,21 @@ void BrowserWindow::tabTitleChanged(const QString &str)
|
||||
}
|
||||
}
|
||||
|
||||
void BrowserWindow::current_dir_path(const QString &dir)
|
||||
{
|
||||
m_dir = dir;
|
||||
m_last_open_folder_path = dir;
|
||||
|
||||
QSettings settings;
|
||||
settings.setValue("Browser/LastSaveLocation", m_last_open_folder_path);
|
||||
}
|
||||
|
||||
// Slot: Link is open from pgAdmin mainwindow
|
||||
void BrowserWindow::urlLinkClicked(const QUrl &name)
|
||||
{
|
||||
// Check that request contains the data download at client side
|
||||
QNetworkRequest request;
|
||||
|
||||
// First check is there any tab opened with same URL then open it again.
|
||||
int tabFound = findURLTab(name);
|
||||
|
||||
@@ -353,6 +689,11 @@ void BrowserWindow::urlLinkClicked(const QUrl &name)
|
||||
m_addNewGridLayout->setContentsMargins(0, 0, 0, 0);
|
||||
m_addNewWebView = new WebViewWindow(m_addNewTab);
|
||||
|
||||
// Listen for the download request from the web page
|
||||
m_addNewWebView->page()->setForwardUnsupportedContent(true);
|
||||
connect(m_addNewWebView->page(), SIGNAL(downloadRequested(const QNetworkRequest &)), this, SLOT(download(const QNetworkRequest &)));
|
||||
connect(m_addNewWebView->page(), SIGNAL(unsupportedContent(QNetworkReply*)), this, SLOT(unsupportedContent(QNetworkReply*)));
|
||||
|
||||
m_widget = new QWidget(m_addNewTab);
|
||||
m_toolBtnBack = new QToolButton(m_widget);
|
||||
m_toolBtnBack->setFixedHeight(PGA_BTN_SIZE);
|
||||
|
||||
@@ -54,6 +54,12 @@ public slots:
|
||||
void tabIndexChanged(int index);
|
||||
void goBackPage();
|
||||
void goForwardPage();
|
||||
void download(const QNetworkRequest &request);
|
||||
void unsupportedContent(QNetworkReply * reply);
|
||||
void downloadFinished();
|
||||
void downloadFileProgress(qint64 , qint64 );
|
||||
void progressCanceled();
|
||||
void current_dir_path(const QString &dir);
|
||||
|
||||
private:
|
||||
QString m_appServerUrl;
|
||||
@@ -79,6 +85,15 @@ private:
|
||||
|
||||
bool m_initialLoad;
|
||||
int m_loadAttempt;
|
||||
QString m_downloadFilename;
|
||||
int m_downloadStarted;
|
||||
int m_downloadCancelled;
|
||||
QFile *m_file;
|
||||
QProgressDialog *m_progressDialog;
|
||||
QString m_defaultFilename;
|
||||
QString m_last_open_folder_path;
|
||||
QString m_dir;
|
||||
QNetworkReply *m_reply;
|
||||
|
||||
void createActions();
|
||||
void pause(int seconds = 1);
|
||||
|
||||
@@ -69,8 +69,9 @@ Server::Server(quint16 port)
|
||||
QString python_path = settings.value("PythonPath").toString();
|
||||
|
||||
// Get the application directory
|
||||
QString app_dir = qApp->applicationDirPath();
|
||||
QString path_env = qgetenv("PATH");
|
||||
QString app_dir = qApp->applicationDirPath(),
|
||||
path_env = qgetenv("PATH"),
|
||||
pythonHome;
|
||||
QStringList path_list;
|
||||
int i;
|
||||
|
||||
@@ -85,6 +86,7 @@ Server::Server(quint16 port)
|
||||
QFileInfo venvLibPath(app_dir + "/../Resources/venv/lib/python");
|
||||
QFileInfo venvDynLibPath(app_dir + "/../Resources/venv/lib/python/lib-dynload");
|
||||
QFileInfo venvSitePackagesPath(app_dir + "/../Resources/venv/lib/python/site-packages");
|
||||
QFileInfo venvPath(app_dir + "/../Resources/venv");
|
||||
|
||||
// Prepend the bin directory to the path
|
||||
add_to_path(path_env, venvBinPath.canonicalFilePath(), true);
|
||||
@@ -92,6 +94,7 @@ Server::Server(quint16 port)
|
||||
add_to_path(python_path, venvLibPath.canonicalFilePath());
|
||||
add_to_path(python_path, venvDynLibPath.canonicalFilePath());
|
||||
add_to_path(python_path, venvSitePackagesPath.canonicalFilePath());
|
||||
add_to_path(pythonHome, venvPath.canonicalFilePath());
|
||||
#elif defined(Q_OS_WIN)
|
||||
|
||||
// In the case we're running in a release application, we need to ensure the
|
||||
@@ -103,6 +106,7 @@ Server::Server(quint16 port)
|
||||
QFileInfo venvLibPath(app_dir + "/../venv/Lib");
|
||||
QFileInfo venvDLLsPath(app_dir + "/../venv/DLLs");
|
||||
QFileInfo venvSitePackagesPath(app_dir + "/../venv/Lib/site-packages");
|
||||
QFileInfo venvPath(app_dir + "/../venv");
|
||||
|
||||
// Prepend the bin directory to the path
|
||||
add_to_path(path_env, venvBinPath.canonicalFilePath(), true);
|
||||
@@ -110,12 +114,14 @@ Server::Server(quint16 port)
|
||||
add_to_path(python_path, venvLibPath.canonicalFilePath());
|
||||
add_to_path(python_path, venvDLLsPath.canonicalFilePath());
|
||||
add_to_path(python_path, venvSitePackagesPath.canonicalFilePath());
|
||||
add_to_path(pythonHome, venvPath.canonicalFilePath());
|
||||
#else
|
||||
// Build (and canonicalise) the virtual environment path
|
||||
QFileInfo venvBinPath(app_dir + "/../venv/bin");
|
||||
QFileInfo venvLibPath(app_dir + "/../venv/lib/python");
|
||||
QFileInfo venvDynLibPath(app_dir + "/../venv/lib/python/lib-dynload");
|
||||
QFileInfo venvSitePackagesPath(app_dir + "/../venv/lib/python/site-packages");
|
||||
QFileInfo venvPath(app_dir + "/../venv");
|
||||
|
||||
// Prepend the bin directory to the path
|
||||
add_to_path(path_env, venvBinPath.canonicalFilePath(), true);
|
||||
@@ -123,6 +129,7 @@ Server::Server(quint16 port)
|
||||
add_to_path(python_path, venvLibPath.canonicalFilePath());
|
||||
add_to_path(python_path, venvDynLibPath.canonicalFilePath());
|
||||
add_to_path(python_path, venvSitePackagesPath.canonicalFilePath());
|
||||
add_to_path(pythonHome, venvPath.canonicalFilePath());
|
||||
#endif
|
||||
|
||||
qputenv("PATH", path_env.toUtf8().data());
|
||||
@@ -149,10 +156,21 @@ Server::Server(quint16 port)
|
||||
qputenv("PYTHONPATH", python_path.toUtf8().data());
|
||||
}
|
||||
|
||||
qDebug() << "Full Python path: " << python_path;
|
||||
qDebug() << "Python path: " << python_path
|
||||
<< "\nPython Home: " << pythonHome;
|
||||
if (!pythonHome.isEmpty())
|
||||
{
|
||||
#ifdef PYTHON2
|
||||
Py_SetPythonHome(pythonHome.toUtf8().data());
|
||||
#else
|
||||
char *python_home = pythonHome.toUtf8().data();
|
||||
const size_t cSize = strlen(python_home) + 1;
|
||||
wchar_t* wcPythonHome = new wchar_t[cSize];
|
||||
mbstowcs (wcPythonHome, python_home, cSize);
|
||||
|
||||
python_path = settings.value("PythonPath").toString();
|
||||
qDebug() << "User Python path: " << python_path;
|
||||
Py_SetPythonHome(wcPythonHome);
|
||||
#endif
|
||||
}
|
||||
|
||||
Py_Initialize();
|
||||
|
||||
@@ -234,10 +252,33 @@ void Server::run()
|
||||
|
||||
// Run the app!
|
||||
#ifdef PYTHON2
|
||||
/*
|
||||
* Untrusted search path vulnerability in the PySys_SetArgv API function in Python 2.6 and earlier, and possibly later
|
||||
* versions, prepends an empty string to sys.path when the argv[0] argument does not contain a path separator,
|
||||
* which might allow local users to execute arbitrary code via a Trojan horse Python file in the current working directory.
|
||||
* Here we have to set arguments explicitly to python interpreter. Check more details in 'PySys_SetArgv' documentation.
|
||||
*/
|
||||
char* n_argv[] = { m_appfile.toUtf8().data() };
|
||||
PySys_SetArgv(1, n_argv);
|
||||
|
||||
PyObject* PyFileObject = PyFile_FromString(m_appfile.toUtf8().data(), (char *)"r");
|
||||
if (PyRun_SimpleFile(PyFile_AsFile(PyFileObject), m_appfile.toUtf8().data()) != 0)
|
||||
int ret = PyRun_SimpleFile(PyFile_AsFile(PyFileObject), m_appfile.toUtf8().data());
|
||||
if (ret != 0)
|
||||
setError(tr("Failed to launch the application server, server thread exiting."));
|
||||
#else
|
||||
/*
|
||||
* Untrusted search path vulnerability in the PySys_SetArgv API function in Python 2.6 and earlier, and possibly later
|
||||
* versions, prepends an empty string to sys.path when the argv[0] argument does not contain a path separator,
|
||||
* which might allow local users to execute arbitrary code via a Trojan horse Python file in the current working directory.
|
||||
* Here we have to set arguments explicitly to python interpreter. Check more details in 'PySys_SetArgv' documentation.
|
||||
*/
|
||||
char *appName = m_appfile.toUtf8().data();
|
||||
const size_t cSize = strlen(appName)+1;
|
||||
wchar_t* wcAppName = new wchar_t[cSize];
|
||||
mbstowcs (wcAppName, appName, cSize);
|
||||
wchar_t* n_argv[] = { wcAppName };
|
||||
PySys_SetArgv(1, n_argv);
|
||||
|
||||
int fd = fileno(cp);
|
||||
PyObject* PyFileObject = PyFile_FromFd(fd, m_appfile.toUtf8().data(), (char *)"r", -1, NULL, NULL,NULL,1);
|
||||
if (PyRun_SimpleFile(fdopen(PyObject_AsFileDescriptor(PyFileObject),"r"), m_appfile.toUtf8().data()) != 0)
|
||||
|
||||
@@ -22,30 +22,46 @@ from logging import *
|
||||
APP_NAME = 'pgAdmin 4'
|
||||
APP_ICON = 'icon-postgres-alt'
|
||||
|
||||
##########################################################################
|
||||
# Application settings
|
||||
##########################################################################
|
||||
|
||||
# NOTE!!!
|
||||
# If you change any of APP_RELEASE, APP_REVISION or APP_SUFFIX, then you
|
||||
# must also change APP_VERSION_INT to match.
|
||||
|
||||
# Application version number components
|
||||
APP_RELEASE = 1
|
||||
APP_REVISION = 0
|
||||
|
||||
# Application version suffix, e.g. 'beta1', 'dev'. Usually an empty string
|
||||
# for GA releases.
|
||||
APP_SUFFIX = 'beta2'
|
||||
APP_SUFFIX = 'rc1'
|
||||
|
||||
# Numeric application version for upgrade checks. Should be in the format:
|
||||
# [X]XYYZZ, where X is the release version, Y is the revision, with a leading
|
||||
# zero if needed, and Z represents the suffix, with a leading zero if needed
|
||||
APP_VERSION_INT = 10005
|
||||
|
||||
# DO NOT CHANGE!
|
||||
# The application version string, constructed from the components
|
||||
APP_VERSION = '%s.%s-%s' % (APP_RELEASE, APP_REVISION, APP_SUFFIX)
|
||||
|
||||
# Copyright string for display in the app
|
||||
APP_COPYRIGHT = 'Copyright 2013 - 2016, The pgAdmin Development Team'
|
||||
|
||||
##########################################################################
|
||||
# Misc stuff
|
||||
##########################################################################
|
||||
|
||||
# Path to the online help.
|
||||
HELP_PATH = '../../../docs/en_US/_build/html/'
|
||||
|
||||
# Languages we support in the UI
|
||||
LANGUAGES = {
|
||||
'en': 'English',
|
||||
'fr': 'Français'
|
||||
'en': 'English'
|
||||
}
|
||||
|
||||
# DO NOT CHANGE!
|
||||
# The application version string, constructed from the components
|
||||
APP_VERSION = '%s.%s-%s' % (APP_RELEASE, APP_REVISION, APP_SUFFIX)
|
||||
|
||||
# DO NOT CHANGE UNLESS YOU KNOW WHAT YOU ARE DOING!
|
||||
# List of modules to skip when dynamically loading
|
||||
MODULE_BLACKLIST = ['test']
|
||||
@@ -137,7 +153,10 @@ SECURITY_PASSWORD_SALT = 'SuperSecret3'
|
||||
SECURITY_PASSWORD_HASH = 'pbkdf2_sha512'
|
||||
|
||||
# Should HTML be minified on the fly when not in debug mode?
|
||||
MINIFY_HTML = True
|
||||
# Note: This is disabled by default as it will error when processing the
|
||||
# docs. If the serving of docs is handled by an Apache HTTPD
|
||||
# instance (rather than via the app), then it can be safely enabled.
|
||||
MINIFY_HTML = False
|
||||
|
||||
##########################################################################
|
||||
# Server Connection Driver Settings
|
||||
@@ -154,10 +173,6 @@ MAX_SESSION_IDLE_TIME = 60
|
||||
# User account and settings storage
|
||||
##########################################################################
|
||||
|
||||
# The schema version number for the configuration database
|
||||
# DO NOT CHANGE UNLESS YOU ARE A PGADMIN DEVELOPER!!
|
||||
SETTINGS_SCHEMA_VERSION = 11
|
||||
|
||||
# The default path to the SQLite database used to store user accounts and
|
||||
# settings. This default places the file in the same directory as this
|
||||
# config file, but generates an absolute path for use througout the app.
|
||||
@@ -204,11 +219,14 @@ SESSION_COOKIE_NAME = 'pga4_session'
|
||||
|
||||
# These settings are used when running in web server mode for confirming
|
||||
# and resetting passwords etc.
|
||||
MAIL_SERVER = 'smtp.gmail.com'
|
||||
MAIL_PORT = 465
|
||||
MAIL_USE_SSL = True
|
||||
MAIL_USERNAME = 'username'
|
||||
MAIL_PASSWORD = 'SuperSecret'
|
||||
# See: http://pythonhosted.org/Flask-Mail/ for more info
|
||||
MAIL_SERVER = 'localhost'
|
||||
MAIL_PORT = 25
|
||||
MAIL_USE_SSL = False
|
||||
MAIL_USE_TLS = False
|
||||
MAIL_USERNAME = ''
|
||||
MAIL_PASSWORD = ''
|
||||
MAIL_DEBUG = False
|
||||
|
||||
##########################################################################
|
||||
# Mail content settings
|
||||
@@ -253,6 +271,12 @@ STORAGE_DIR = os.path.join(
|
||||
##########################################################################
|
||||
THREADED_MODE = True
|
||||
|
||||
##########################################################################
|
||||
# Do not allow SQLALCHEMY to track modification as it is going to be
|
||||
# deprecated in future
|
||||
##########################################################################
|
||||
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
||||
|
||||
##########################################################################
|
||||
# Local config settings
|
||||
##########################################################################
|
||||
|
||||
@@ -23,6 +23,11 @@ if sys.path[0] != root:
|
||||
import config
|
||||
from pgadmin import create_app
|
||||
|
||||
# Get the config database schema version. We store this in pgadmin.model
|
||||
# as it turns out that putting it in the config files isn't a great idea
|
||||
from pgadmin.model import SCHEMA_VERSION
|
||||
config.SETTINGS_SCHEMA_VERSION = SCHEMA_VERSION
|
||||
|
||||
##########################################################################
|
||||
# Sanity checks
|
||||
##########################################################################
|
||||
@@ -75,18 +80,20 @@ else:
|
||||
# Let the application save the status about the runtime for using it later.
|
||||
app.PGADMIN_RUNTIME = PGADMIN_RUNTIME
|
||||
|
||||
# Output a startup message if we're not under the runtime
|
||||
if not PGADMIN_RUNTIME:
|
||||
print("Starting %s. Please navigate to http://localhost:%d in your browser." %
|
||||
(config.APP_NAME, server_port))
|
||||
sys.stdout.flush()
|
||||
# Output a startup message if we're not under the runtime and startup.
|
||||
# If we're under WSGI, we don't need to worry about this
|
||||
if __name__ == '__main__':
|
||||
if not PGADMIN_RUNTIME:
|
||||
print("Starting %s. Please navigate to http://%s:%d in your browser." %
|
||||
(config.APP_NAME, config.DEFAULT_SERVER, server_port))
|
||||
sys.stdout.flush()
|
||||
|
||||
try:
|
||||
app.run(
|
||||
host=config.DEFAULT_SERVER,
|
||||
port=server_port,
|
||||
use_reloader=((not PGADMIN_RUNTIME) and app.debug),
|
||||
threaded=config.THREADED_MODE
|
||||
)
|
||||
except IOError:
|
||||
app.logger.error("Error starting the app server: %s", sys.exc_info())
|
||||
try:
|
||||
app.run(
|
||||
host=config.DEFAULT_SERVER,
|
||||
port=server_port,
|
||||
use_reloader=((not PGADMIN_RUNTIME) and app.debug),
|
||||
threaded=config.THREADED_MODE
|
||||
)
|
||||
except IOError:
|
||||
app.logger.error("Error starting the app server: %s", sys.exc_info())
|
||||
@@ -0,0 +1,8 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
root = os.path.dirname(os.path.realpath(__file__))
|
||||
if sys.path[0] != root:
|
||||
sys.path.insert(0, root)
|
||||
|
||||
from pgAdmin4 import app as application
|
||||
@@ -10,25 +10,35 @@
|
||||
"""The main pgAdmin module. This handles the application initialisation tasks,
|
||||
such as setup of logging, dynamic loading of modules etc."""
|
||||
import logging
|
||||
import sys
|
||||
import os, sys
|
||||
from collections import defaultdict
|
||||
from importlib import import_module
|
||||
|
||||
from flask import Flask, abort, request, current_app
|
||||
from flask.ext.babel import Babel
|
||||
from flask.ext.security import Security, SQLAlchemyUserDatastore
|
||||
from flask_babel import Babel, gettext
|
||||
from flask_login import user_logged_in
|
||||
from flask_security import Security, SQLAlchemyUserDatastore
|
||||
from flask_mail import Mail
|
||||
from flask_security.utils import login_user
|
||||
from htmlmin.minify import html_minify
|
||||
from pgadmin.utils import PgAdminModule, driver
|
||||
from pgadmin.utils.session import ServerSideSessionInterface
|
||||
from pgadmin.utils.session import create_session_interface
|
||||
from werkzeug.local import LocalProxy
|
||||
from werkzeug.utils import find_modules
|
||||
|
||||
from pgadmin.model import db, Role, User, Version
|
||||
from pgadmin.model import db, Role, Server, ServerGroup, User, Version
|
||||
# Configuration settings
|
||||
import config
|
||||
|
||||
# If script is running under python3, it will not have the xrange function
|
||||
# defined
|
||||
winreg = None
|
||||
if sys.version_info.major >= 3:
|
||||
xrange = range
|
||||
if os.name == 'nt':
|
||||
import winreg
|
||||
elif os.name == 'nt':
|
||||
import _winreg as winreg
|
||||
|
||||
class PgAdmin(Flask):
|
||||
def find_submodules(self, basemodule):
|
||||
@@ -114,11 +124,12 @@ def create_app(app_name=config.APP_NAME):
|
||||
# Removes unwanted whitespace from render_template function
|
||||
app.jinja_env.trim_blocks = True
|
||||
app.config.from_object(config)
|
||||
app.config.update(dict(PROPAGATE_EXCEPTIONS=True))
|
||||
|
||||
##########################################################################
|
||||
# Setup session management
|
||||
##########################################################################
|
||||
app.session_interface = ServerSideSessionInterface(config.SESSION_DB_PATH)
|
||||
app.session_interface = create_session_interface(app)
|
||||
|
||||
##########################################################################
|
||||
# Setup logging and log the application startup
|
||||
@@ -137,7 +148,7 @@ def create_app(app_name=config.APP_NAME):
|
||||
logger.setLevel(logging.INFO)
|
||||
|
||||
# File logging
|
||||
fh = logging.FileHandler(config.LOG_FILE)
|
||||
fh = logging.FileHandler(config.LOG_FILE, encoding='utf-8')
|
||||
fh.setLevel(config.FILE_LOG_LEVEL)
|
||||
fh.setFormatter(logging.Formatter(config.FILE_LOG_FORMAT))
|
||||
app.logger.addHandler(fh)
|
||||
@@ -211,9 +222,136 @@ def create_app(app_name=config.APP_NAME):
|
||||
from setup import do_upgrade
|
||||
do_upgrade(app, user_datastore, security, version)
|
||||
|
||||
# Load all available serve drivers
|
||||
# Load all available server drivers
|
||||
driver.init_app(app)
|
||||
|
||||
##########################################################################
|
||||
# Register any local servers we can discover
|
||||
##########################################################################
|
||||
@user_logged_in.connect_via(app)
|
||||
def on_user_logged_in(sender, user):
|
||||
|
||||
# Keep hold of the user ID
|
||||
user_id = user.id
|
||||
|
||||
# Get the first server group for the user
|
||||
servergroup_id = 1
|
||||
servergroups = ServerGroup.query.filter_by(
|
||||
user_id=user_id
|
||||
).order_by("id")
|
||||
|
||||
if servergroups.count() > 0:
|
||||
servergroup = servergroups.first()
|
||||
servergroup_id = servergroup.id
|
||||
|
||||
'''Add a server to the config database'''
|
||||
def add_server(user_id, servergroup_id, name, superuser, port, discovery_id, comment):
|
||||
# Create a server object if needed, and store it.
|
||||
servers = Server.query.filter_by(
|
||||
user_id=user_id,
|
||||
discovery_id=svr_discovery_id
|
||||
).order_by("id")
|
||||
|
||||
if servers.count() > 0:
|
||||
return;
|
||||
|
||||
svr = Server(user_id=user_id,
|
||||
servergroup_id=servergroup_id,
|
||||
name=name,
|
||||
host='localhost',
|
||||
port=port,
|
||||
maintenance_db='postgres',
|
||||
username=superuser,
|
||||
ssl_mode='prefer',
|
||||
comment=svr_comment,
|
||||
discovery_id=discovery_id)
|
||||
|
||||
db.session.add(svr)
|
||||
db.session.commit()
|
||||
|
||||
# Figure out what servers are present
|
||||
if winreg is not None:
|
||||
proc_arch = os.environ['PROCESSOR_ARCHITECTURE'].lower()
|
||||
|
||||
try:
|
||||
proc_arch64 = os.environ['PROCESSOR_ARCHITEW6432'].lower()
|
||||
except:
|
||||
proc_arch64 = None
|
||||
|
||||
if proc_arch == 'x86' and not proc_arch64:
|
||||
arch_keys = {0}
|
||||
elif proc_arch == 'x86' or proc_arch == 'amd64':
|
||||
arch_keys = {winreg.KEY_WOW64_32KEY, winreg.KEY_WOW64_64KEY}
|
||||
|
||||
for arch_key in arch_keys:
|
||||
for server_type in { 'PostgreSQL', 'EnterpriseDB'}:
|
||||
try:
|
||||
root_key = winreg.OpenKey(
|
||||
winreg.HKEY_LOCAL_MACHINE,
|
||||
"SOFTWARE\\" + server_type + "\Services", 0,
|
||||
winreg.KEY_READ | arch_key
|
||||
)
|
||||
for i in xrange(0, winreg.QueryInfoKey(root_key)[0]):
|
||||
inst_id = winreg.EnumKey(root_key, i)
|
||||
inst_key = winreg.OpenKey(root_key, inst_id)
|
||||
|
||||
svr_name = winreg.QueryValueEx(
|
||||
inst_key, 'Display Name'
|
||||
)[0]
|
||||
svr_superuser = winreg.QueryValueEx(
|
||||
inst_key, 'Database Superuser'
|
||||
)[0]
|
||||
svr_port = winreg.QueryValueEx(inst_key, 'Port')[0]
|
||||
svr_discovery_id = inst_id
|
||||
svr_comment = gettext(
|
||||
"Auto-detected %s installation with the data directory at %s" % (
|
||||
winreg.QueryValueEx(
|
||||
inst_key, 'Display Name'
|
||||
)[0],
|
||||
winreg.QueryValueEx(
|
||||
inst_key, 'Data Directory'
|
||||
)[0]
|
||||
)
|
||||
)
|
||||
|
||||
add_server(
|
||||
user_id, servergroup_id, svr_name,
|
||||
svr_superuser, svr_port,
|
||||
svr_discovery_id, svr_comment
|
||||
)
|
||||
|
||||
inst_key.Close()
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
# We use the postgres-winreg.ini file on non-Windows
|
||||
try:
|
||||
from configparser import ConfigParser
|
||||
except ImportError:
|
||||
from ConfigParser import ConfigParser # Python 2
|
||||
|
||||
registry = ConfigParser()
|
||||
|
||||
try:
|
||||
registry.read('/etc/postgres-reg.ini')
|
||||
sections = registry.sections()
|
||||
|
||||
# Loop the sections, and get the data from any that are PG or PPAS
|
||||
for section in sections:
|
||||
if section.startswith('PostgreSQL/') or section.startswith('EnterpriseDB/'):
|
||||
svr_name = registry.get(section, 'Description')
|
||||
svr_superuser = registry.get(section, 'Superuser')
|
||||
svr_port = registry.getint(section, 'Port')
|
||||
svr_discovery_id = section
|
||||
svr_comment = gettext("Auto-detected %s installation with the data directory at %s" % (
|
||||
registry.get(section, 'Description'),
|
||||
registry.get(section, 'DataDirectory')))
|
||||
add_server(user_id, servergroup_id, svr_name, svr_superuser, svr_port, svr_discovery_id, svr_comment)
|
||||
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
##########################################################################
|
||||
# Load plugin modules
|
||||
##########################################################################
|
||||
|
||||
@@ -13,8 +13,8 @@ MODULE_NAME = 'about'
|
||||
import sys
|
||||
|
||||
from flask import Response, render_template, __version__, url_for
|
||||
from flask.ext.babel import gettext
|
||||
from flask.ext.security import current_user, login_required
|
||||
from flask_babel import gettext
|
||||
from flask_security import current_user, login_required
|
||||
from pgadmin.utils import PgAdminModule
|
||||
from pgadmin.utils.menu import MenuItem
|
||||
|
||||
|
||||
@@ -12,9 +12,9 @@ from abc import ABCMeta, abstractmethod, abstractproperty
|
||||
|
||||
import six
|
||||
from flask import current_app, render_template, url_for, make_response, flash
|
||||
from flask.ext.babel import gettext
|
||||
from flask.ext.login import current_user
|
||||
from flask.ext.security import login_required
|
||||
from flask_babel import gettext
|
||||
from flask_login import current_user
|
||||
from flask_security import login_required
|
||||
from flask_gravatar import Gravatar
|
||||
from pgadmin.settings import get_setting
|
||||
from pgadmin.utils import PgAdminModule
|
||||
@@ -189,11 +189,10 @@ class BrowserModule(PgAdminModule):
|
||||
def register_preferences(self):
|
||||
self.show_system_objects = self.preference.register(
|
||||
'display', 'show_system_objects',
|
||||
gettext("Show system objects"), 'boolean', False,
|
||||
gettext("Show system objects?"), 'boolean', False,
|
||||
category_label=gettext('Display')
|
||||
)
|
||||
|
||||
|
||||
blueprint = BrowserModule(MODULE_NAME, __name__)
|
||||
|
||||
|
||||
@@ -462,7 +461,7 @@ def index():
|
||||
pass
|
||||
|
||||
if data is not None:
|
||||
if data['pgadmin4']['version'] != config.APP_VERSION:
|
||||
if data['pgadmin4']['version_int'] > config.APP_VERSION_INT:
|
||||
msg = render_template(
|
||||
MODULE_NAME + "/upgrade.html",
|
||||
current_version=config.APP_VERSION,
|
||||
@@ -495,6 +494,15 @@ def browser_js():
|
||||
edbas_help_path_pref = prefs.preference('edbas_help_path')
|
||||
edbas_help_path = edbas_help_path_pref.get()
|
||||
|
||||
# Get sqleditor options
|
||||
prefs = Preferences.module('sqleditor')
|
||||
|
||||
editor_tab_size_pref = prefs.preference('tab_size')
|
||||
editor_tab_size = editor_tab_size_pref.get()
|
||||
|
||||
editor_use_spaces_pref = prefs.preference('use_spaces')
|
||||
editor_use_spaces = editor_use_spaces_pref.get()
|
||||
|
||||
for submodule in current_blueprint.submodules:
|
||||
snippets.extend(submodule.jssnippets)
|
||||
return make_response(
|
||||
@@ -504,6 +512,8 @@ def browser_js():
|
||||
jssnippets=snippets,
|
||||
pg_help_path=pg_help_path,
|
||||
edbas_help_path=edbas_help_path,
|
||||
editor_tab_size=editor_tab_size,
|
||||
editor_use_spaces=editor_use_spaces,
|
||||
_=gettext
|
||||
),
|
||||
200, {'Content-Type': 'application/x-javascript'})
|
||||
@@ -557,6 +567,16 @@ def collection_js():
|
||||
def browser_css():
|
||||
"""Render and return CSS snippets from the nodes and modules."""
|
||||
snippets = []
|
||||
|
||||
# Get configurable options
|
||||
prefs = Preferences.module('sqleditor')
|
||||
|
||||
sql_font_size_pref = prefs.preference('sql_font_size')
|
||||
sql_font_size = round(float(sql_font_size_pref.get()), 2)
|
||||
|
||||
if sql_font_size != 0:
|
||||
snippets.append('.CodeMirror { font-size: %sem; }' % str(sql_font_size))
|
||||
|
||||
for submodule in blueprint.submodules:
|
||||
snippets.extend(submodule.csssnippets)
|
||||
return make_response(
|
||||
|
||||
@@ -11,7 +11,7 @@ from abc import ABCMeta, abstractmethod
|
||||
|
||||
import six
|
||||
from flask import url_for, render_template
|
||||
from flask.ext.babel import gettext
|
||||
from flask_babel import gettext
|
||||
from pgadmin.browser import BrowserPluginModule
|
||||
from pgadmin.browser.utils import PGChildModule
|
||||
from pgadmin.utils import PgAdminModule
|
||||
@@ -85,7 +85,8 @@ class CollectionNodeModule(PgAdminModule, PGChildModule):
|
||||
"_type": 'coll-%s' % (self.node_type),
|
||||
"_id": parent_id,
|
||||
"_pid": parent_id,
|
||||
"module": 'pgadmin.node.%s' % self.node_type
|
||||
"module": 'pgadmin.node.%s' % self.node_type,
|
||||
"nodes": [self.node_type]
|
||||
}
|
||||
|
||||
for key in kwargs:
|
||||
|
||||
@@ -8,16 +8,16 @@
|
||||
##########################################################################
|
||||
"""Defines views for management of server groups"""
|
||||
|
||||
import json
|
||||
import simplejson as json
|
||||
from abc import ABCMeta, abstractmethod
|
||||
|
||||
import six
|
||||
from flask import request, render_template, make_response, jsonify
|
||||
from flask.ext.babel import gettext
|
||||
from flask.ext.security import current_user
|
||||
from flask_babel import gettext
|
||||
from flask_security import current_user
|
||||
from pgadmin.browser import BrowserPluginModule
|
||||
from pgadmin.browser.utils import NodeView
|
||||
from pgadmin.utils.ajax import make_json_response, \
|
||||
from pgadmin.utils.ajax import make_json_response, gone, \
|
||||
make_response as ajax_response
|
||||
from pgadmin.utils.menu import MenuItem
|
||||
|
||||
@@ -131,7 +131,7 @@ class ServerGroupView(NodeView):
|
||||
|
||||
if sg is None:
|
||||
return make_json_response(
|
||||
status=417,
|
||||
status=410,
|
||||
success=0,
|
||||
errormsg=gettext(
|
||||
'The specified server group could not be found.'
|
||||
@@ -156,7 +156,9 @@ class ServerGroupView(NodeView):
|
||||
user_id=current_user.id,
|
||||
id=gid).first()
|
||||
|
||||
data = request.form if request.form else json.loads(request.data.decode())
|
||||
data = request.form if request.form else json.loads(
|
||||
request.data, encoding='utf-8'
|
||||
)
|
||||
|
||||
if servergroup is None:
|
||||
return make_json_response(
|
||||
@@ -176,7 +178,17 @@ class ServerGroupView(NodeView):
|
||||
status=410, success=0, errormsg=e.message
|
||||
)
|
||||
|
||||
return make_json_response(result=request.form)
|
||||
return jsonify(
|
||||
node=self.blueprint.generate_browser_node(
|
||||
gid,
|
||||
None,
|
||||
servergroup.name,
|
||||
"icon-%s" % self.node_type,
|
||||
True,
|
||||
self.node_type,
|
||||
can_delete=True # This is user created hence can deleted
|
||||
)
|
||||
)
|
||||
|
||||
def properties(self, gid):
|
||||
"""Update the server-group properties"""
|
||||
@@ -188,7 +200,7 @@ class ServerGroupView(NodeView):
|
||||
|
||||
if sg is None:
|
||||
return make_json_response(
|
||||
status=417,
|
||||
status=410,
|
||||
success=0,
|
||||
errormsg=gettext(
|
||||
'The specified server group could not be found.'
|
||||
@@ -201,7 +213,10 @@ class ServerGroupView(NodeView):
|
||||
)
|
||||
|
||||
def create(self):
|
||||
data = request.form if request.form else json.loads(request.data.decode())
|
||||
"""Creates new server-group """
|
||||
data = request.form if request.form else json.loads(
|
||||
request.data, encoding='utf-8'
|
||||
)
|
||||
if data[u'name'] != '':
|
||||
try:
|
||||
check_sg = ServerGroup.query.filter_by(
|
||||
@@ -227,7 +242,7 @@ class ServerGroupView(NodeView):
|
||||
|
||||
return jsonify(
|
||||
node=self.blueprint.generate_browser_node(
|
||||
"%d" % (sg.id), None,
|
||||
"%d" % (sg.id),None,
|
||||
sg.name,
|
||||
"icon-%s" % self.node_type,
|
||||
True,
|
||||
@@ -278,19 +293,29 @@ class ServerGroupView(NodeView):
|
||||
|
||||
if gid is None:
|
||||
groups = ServerGroup.query.filter_by(user_id=current_user.id)
|
||||
else:
|
||||
groups = ServerGroup.query.filter_by(user_id=current_user.id,
|
||||
id=gid).first()
|
||||
|
||||
for group in groups:
|
||||
nodes.append(
|
||||
self.blueprint.generate_browser_node(
|
||||
"%d" % (group.id), None,
|
||||
group.name,
|
||||
"icon-%s" % self.node_type,
|
||||
True,
|
||||
self.node_type
|
||||
for group in groups:
|
||||
nodes.append(
|
||||
self.blueprint.generate_browser_node(
|
||||
"%d" % (group.id), None,
|
||||
group.name,
|
||||
"icon-%s" % self.node_type,
|
||||
True,
|
||||
self.node_type
|
||||
)
|
||||
)
|
||||
else:
|
||||
group = ServerGroup.query.filter_by(user_id=current_user.id,
|
||||
id=gid).first()
|
||||
if not group:
|
||||
return gone(errormsg="Couldn't find the server-group!")
|
||||
|
||||
nodes = self.blueprint.generate_browser_node(
|
||||
"%d" % (group.id), None,
|
||||
group.name,
|
||||
"icon-%s" % self.node_type,
|
||||
True,
|
||||
self.node_type
|
||||
)
|
||||
|
||||
return make_json_response(data=nodes)
|
||||
|
||||
@@ -7,14 +7,13 @@
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
import json
|
||||
import traceback
|
||||
import simplejson as json
|
||||
|
||||
import pgadmin.browser.server_groups as sg
|
||||
from flask import render_template, request, make_response, jsonify, \
|
||||
current_app, url_for
|
||||
from flask.ext.babel import gettext
|
||||
from flask.ext.security import current_user
|
||||
from flask_babel import gettext
|
||||
from flask_security import current_user
|
||||
from pgadmin.browser.server_groups.servers.types import ServerType
|
||||
from pgadmin.browser.utils import PGChildNodeView
|
||||
from pgadmin.utils.ajax import make_json_response, bad_request, forbidden, \
|
||||
@@ -269,6 +268,12 @@ class ServerNode(PGChildNodeView):
|
||||
wal_pause=wal_paused
|
||||
)
|
||||
)
|
||||
|
||||
if not len(res):
|
||||
return gone(errormsg=gettext(
|
||||
'The specified server group with id# {0} could not be found.'
|
||||
))
|
||||
|
||||
return make_json_response(result=res)
|
||||
|
||||
def node(self, gid, sid):
|
||||
@@ -283,7 +288,7 @@ class ServerNode(PGChildNodeView):
|
||||
success=0,
|
||||
errormsg=gettext(
|
||||
gettext(
|
||||
"Couldn't find the server with id# %s!"
|
||||
"Couldn't find the server with id# {0}!"
|
||||
).format(sid)
|
||||
)
|
||||
)
|
||||
@@ -337,6 +342,7 @@ class ServerNode(PGChildNodeView):
|
||||
# TODO:: A server, which is connected, can not be deleted
|
||||
if servers is None:
|
||||
return make_json_response(
|
||||
status=410,
|
||||
success=0,
|
||||
errormsg=gettext(
|
||||
'The specified server could not be found.\n'
|
||||
@@ -355,14 +361,8 @@ class ServerNode(PGChildNodeView):
|
||||
success=0,
|
||||
errormsg=e.message)
|
||||
|
||||
try:
|
||||
info = traceback.format_exc()
|
||||
except Exception as e:
|
||||
current_app.logger.exception(e)
|
||||
info = str(e)
|
||||
|
||||
return make_json_response(success=1,
|
||||
info=info)
|
||||
info=gettext("Server deleted"))
|
||||
|
||||
def update(self, gid, sid):
|
||||
"""Update the server settings"""
|
||||
@@ -371,6 +371,7 @@ class ServerNode(PGChildNodeView):
|
||||
|
||||
if server is None:
|
||||
return make_json_response(
|
||||
status=410,
|
||||
success=0,
|
||||
errormsg=gettext("Could not find the required server.")
|
||||
)
|
||||
@@ -400,7 +401,9 @@ class ServerNode(PGChildNodeView):
|
||||
}
|
||||
|
||||
idx = 0
|
||||
data = request.form if request.form else json.loads(request.data.decode())
|
||||
data = request.form if request.form else json.loads(
|
||||
request.data, encoding='utf-8'
|
||||
)
|
||||
|
||||
from pgadmin.utils.driver import get_driver
|
||||
manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)
|
||||
@@ -444,9 +447,8 @@ class ServerNode(PGChildNodeView):
|
||||
if not conn.connected():
|
||||
manager.update(server)
|
||||
|
||||
return make_json_response(
|
||||
success=1,
|
||||
data=self.blueprint.generate_browser_node(
|
||||
return jsonify(
|
||||
node=self.blueprint.generate_browser_node(
|
||||
"%d" % (server.id), server.servergroup_id,
|
||||
server.name,
|
||||
"icon-server-not-connected" if not connected else
|
||||
@@ -507,6 +509,7 @@ class ServerNode(PGChildNodeView):
|
||||
|
||||
if server is None:
|
||||
return make_json_response(
|
||||
status=410,
|
||||
success=0,
|
||||
errormsg=gettext("Could not find the required server.")
|
||||
)
|
||||
@@ -554,7 +557,9 @@ class ServerNode(PGChildNodeView):
|
||||
u'role'
|
||||
]
|
||||
|
||||
data = request.form if request.form else json.loads(request.data.decode())
|
||||
data = request.form if request.form else json.loads(
|
||||
request.data, encoding='utf-8'
|
||||
)
|
||||
|
||||
for arg in required_args:
|
||||
if arg not in data:
|
||||
@@ -566,6 +571,8 @@ class ServerNode(PGChildNodeView):
|
||||
)
|
||||
)
|
||||
|
||||
server = None
|
||||
|
||||
try:
|
||||
server = Server(
|
||||
user_id=current_user.id,
|
||||
@@ -592,8 +599,10 @@ class ServerNode(PGChildNodeView):
|
||||
manager.update(server)
|
||||
conn = manager.connection()
|
||||
|
||||
have_password = False
|
||||
if 'password' in data and data["password"] != '':
|
||||
# login with password
|
||||
have_password = True
|
||||
password = data['password']
|
||||
password = encrypt(password, current_user.password)
|
||||
else:
|
||||
@@ -614,6 +623,10 @@ class ServerNode(PGChildNodeView):
|
||||
errormsg=gettext("Unable to connect to server:\n\n%s" % errmsg)
|
||||
)
|
||||
else:
|
||||
if 'save_password' in data and data['save_password'] and have_password:
|
||||
setattr(server, 'password', password)
|
||||
db.session.commit()
|
||||
|
||||
user = manager.user_info
|
||||
connected = True
|
||||
icon = "icon-pg"
|
||||
@@ -707,8 +720,16 @@ class ServerNode(PGChildNodeView):
|
||||
from pgadmin.utils.driver import get_driver
|
||||
manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)
|
||||
conn = manager.connection()
|
||||
res = conn.connected()
|
||||
|
||||
return make_json_response(data={'connected': conn.connected()})
|
||||
if res:
|
||||
from pgadmin.utils.exception import ConnectionLost
|
||||
try:
|
||||
conn.execute_scalar('SELECT 1')
|
||||
except ConnectionLost:
|
||||
res = False
|
||||
|
||||
return make_json_response(data={'connected': res})
|
||||
|
||||
def connect(self, gid, sid):
|
||||
"""
|
||||
@@ -740,14 +761,21 @@ class ServerNode(PGChildNodeView):
|
||||
if user is None:
|
||||
return unauthorized(gettext("Unauthorized request."))
|
||||
|
||||
data = request.form if request.form else json.loads(request.data) if \
|
||||
request.data else {}
|
||||
data = request.form if request.form else json.loads(
|
||||
request.data, encoding='utf-8'
|
||||
) if request.data else {}
|
||||
|
||||
password = None
|
||||
save_password = False
|
||||
|
||||
# Connect the Server
|
||||
from pgadmin.utils.driver import get_driver
|
||||
manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)
|
||||
conn = manager.connection()
|
||||
|
||||
if 'password' not in data:
|
||||
if server.password is None:
|
||||
conn_passwd = getattr(conn, 'password', None)
|
||||
if conn_passwd is None and server.password is None:
|
||||
# Return the password template in case password is not
|
||||
# provided, or password has not been saved earlier.
|
||||
return make_json_response(
|
||||
@@ -766,19 +794,15 @@ class ServerNode(PGChildNodeView):
|
||||
data['save_password'] if password and \
|
||||
'save_password' in data else False
|
||||
|
||||
# Encrypt the password before saving with user's login password key.
|
||||
try:
|
||||
password = encrypt(password, user.password) \
|
||||
if password is not None else server.password
|
||||
except Exception as e:
|
||||
current_app.logger.exception(e)
|
||||
return internal_server_error(errormsg=e.message)
|
||||
|
||||
# Connect the Server
|
||||
from pgadmin.utils.driver import get_driver
|
||||
manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)
|
||||
conn = manager.connection()
|
||||
# Encrypt the password before saving with user's login password key.
|
||||
try:
|
||||
password = encrypt(password, user.password) \
|
||||
if password is not None else server.password
|
||||
except Exception as e:
|
||||
current_app.logger.exception(e)
|
||||
return internal_server_error(errormsg=e.message)
|
||||
|
||||
status = True
|
||||
try:
|
||||
status, errmsg = conn.connect(
|
||||
password=password,
|
||||
@@ -786,13 +810,18 @@ class ServerNode(PGChildNodeView):
|
||||
)
|
||||
except Exception as e:
|
||||
current_app.logger.exception(e)
|
||||
# TODO::
|
||||
# Ask the password again (if existing password couldn't be
|
||||
# descrypted)
|
||||
if e.message:
|
||||
return internal_server_error(errormsg=e.message)
|
||||
else:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
return make_json_response(
|
||||
success=0,
|
||||
status=401,
|
||||
result=render_template(
|
||||
'servers/password.html',
|
||||
server_label=server.name,
|
||||
username=server.username,
|
||||
errmsg=e.message if e.message else str(e),
|
||||
_=gettext
|
||||
)
|
||||
)
|
||||
|
||||
if not status:
|
||||
current_app.logger.error(
|
||||
@@ -855,6 +884,7 @@ class ServerNode(PGChildNodeView):
|
||||
manager.server_type
|
||||
),
|
||||
'connected': True,
|
||||
'server_type': manager.server_type,
|
||||
'type': manager.server_type,
|
||||
'version': manager.version,
|
||||
'db': manager.db,
|
||||
@@ -969,7 +999,7 @@ class ServerNode(PGChildNodeView):
|
||||
sid: Server id
|
||||
"""
|
||||
try:
|
||||
data = json.loads(request.form['data'])
|
||||
data = json.loads(request.form['data'], encoding='utf-8')
|
||||
if data and ('password' not in data or
|
||||
data['password'] == '' or
|
||||
'newPassword' not in data or
|
||||
|
||||
@@ -9,13 +9,13 @@
|
||||
|
||||
"""Implements the Database Node"""
|
||||
|
||||
import json
|
||||
import simplejson as json
|
||||
import re
|
||||
from functools import wraps
|
||||
|
||||
import pgadmin.browser.server_groups.servers as servers
|
||||
from flask import render_template, make_response, current_app, request, jsonify
|
||||
from flask.ext.babel import gettext as _
|
||||
from flask_babel import gettext as _
|
||||
from pgadmin.browser.collection import CollectionNodeModule
|
||||
from pgadmin.browser.server_groups.servers.databases.utils import \
|
||||
parse_sec_labels_from_db, parse_variables_from_db
|
||||
@@ -24,7 +24,7 @@ from pgadmin.browser.server_groups.servers.utils import parse_priv_from_db, \
|
||||
from pgadmin.browser.utils import PGChildNodeView
|
||||
from pgadmin.utils.ajax import make_json_response, \
|
||||
make_response as ajax_response, internal_server_error, unauthorized
|
||||
from pgadmin.utils.ajax import precondition_required, gone
|
||||
from pgadmin.utils.ajax import gone
|
||||
from pgadmin.utils.driver import get_driver
|
||||
|
||||
from config import PG_DEFAULT_DRIVER
|
||||
@@ -99,6 +99,7 @@ class DatabaseView(PGChildNodeView):
|
||||
{'get': 'list', 'post': 'create'}
|
||||
],
|
||||
'nodes': [{'get': 'node'}, {'get': 'nodes'}],
|
||||
'get_databases': [{'get': 'get_databases'}, {'get': 'get_databases'}],
|
||||
'sql': [{'get': 'sql'}],
|
||||
'msql': [{'get': 'msql'}, {'get': 'msql'}],
|
||||
'stats': [{'get': 'statistics'}, {'get': 'statistics'}],
|
||||
@@ -126,17 +127,15 @@ class DatabaseView(PGChildNodeView):
|
||||
def wrapped(self, *args, **kwargs):
|
||||
|
||||
self.manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(kwargs['sid'])
|
||||
if self.manager is None:
|
||||
return gone(errormsg="Couldn't find the server.")
|
||||
|
||||
if action and action in ["drop"]:
|
||||
self.conn = self.manager.connection()
|
||||
elif 'did' in kwargs:
|
||||
self.conn = self.manager.connection(did=kwargs['did'])
|
||||
else:
|
||||
self.conn = self.manager.connection()
|
||||
# If DB not connected then return error to browser
|
||||
if not self.conn.connected():
|
||||
return precondition_required(
|
||||
_("Connection to the server has been lost!")
|
||||
)
|
||||
|
||||
ver = self.manager.version
|
||||
# we will set template path for sql scripts
|
||||
@@ -146,6 +145,7 @@ class DatabaseView(PGChildNodeView):
|
||||
self.template_path = 'databases/sql/9.2_plus'
|
||||
else:
|
||||
self.template_path = 'databases/sql/9.1_plus'
|
||||
|
||||
return f(self, *args, **kwargs)
|
||||
|
||||
return wrapped
|
||||
@@ -155,7 +155,9 @@ class DatabaseView(PGChildNodeView):
|
||||
@check_precondition(action="list")
|
||||
def list(self, gid, sid):
|
||||
last_system_oid = 0 if self.blueprint.show_system_objects else \
|
||||
(self.manager.db_info[self.manager.did])['datlastsysoid']
|
||||
(self.manager.db_info[self.manager.did])['datlastsysoid'] \
|
||||
if self.manager.db_info is not None and \
|
||||
self.manager.did in self.manager.db_info else 0
|
||||
SQL = render_template(
|
||||
"/".join([self.template_path, 'properties.sql']),
|
||||
conn=self.conn, last_system_oid=last_system_oid
|
||||
@@ -170,11 +172,14 @@ class DatabaseView(PGChildNodeView):
|
||||
status=200
|
||||
)
|
||||
|
||||
@check_precondition(action="nodes")
|
||||
def nodes(self, gid, sid):
|
||||
def get_nodes(self, gid, sid, show_system_templates=False):
|
||||
res = []
|
||||
last_system_oid = 0 if self.blueprint.show_system_objects else \
|
||||
(self.manager.db_info[self.manager.did])['datlastsysoid']
|
||||
last_system_oid = 0 if self.blueprint.show_system_objects or \
|
||||
show_system_templates else (
|
||||
(self.manager.db_info[self.manager.did])['datlastsysoid'] \
|
||||
if self.manager.db_info is not None and \
|
||||
self.manager.did in self.manager.db_info else 0
|
||||
)
|
||||
|
||||
SQL = render_template(
|
||||
"/".join([self.template_path, 'nodes.sql']),
|
||||
@@ -186,13 +191,16 @@ class DatabaseView(PGChildNodeView):
|
||||
return internal_server_error(errormsg=rset)
|
||||
|
||||
for row in rset['rows']:
|
||||
if self.manager.db == row['name']:
|
||||
dbname = row['name']
|
||||
if hasattr(str, 'decode'):
|
||||
dbname = dbname.decode('utf-8')
|
||||
if self.manager.db == dbname:
|
||||
connected = True
|
||||
canDisConn = False
|
||||
canDrop = canDisConn = False
|
||||
else:
|
||||
conn = self.manager.connection(row['name'])
|
||||
conn = self.manager.connection(dbname)
|
||||
connected = conn.connected()
|
||||
canDisConn = True
|
||||
canDrop = canDisConn = True
|
||||
|
||||
res.append(
|
||||
self.blueprint.generate_browser_node(
|
||||
@@ -205,10 +213,25 @@ class DatabaseView(PGChildNodeView):
|
||||
tablespace=row['spcname'],
|
||||
allowConn=row['datallowconn'],
|
||||
canCreate=row['cancreate'],
|
||||
canDisconn=canDisConn
|
||||
canDisconn=canDisConn,
|
||||
canDrop = canDrop
|
||||
)
|
||||
)
|
||||
|
||||
return res
|
||||
|
||||
@check_precondition(action="nodes")
|
||||
def nodes(self, gid, sid):
|
||||
res = self.get_nodes(gid, sid)
|
||||
|
||||
return make_json_response(
|
||||
data=res,
|
||||
status=200
|
||||
)
|
||||
|
||||
@check_precondition(action="get_databases")
|
||||
def get_databases(self, gid, sid):
|
||||
res = self.get_nodes(gid, sid, True)
|
||||
return make_json_response(
|
||||
data=res,
|
||||
status=200
|
||||
@@ -226,7 +249,10 @@ class DatabaseView(PGChildNodeView):
|
||||
return internal_server_error(errormsg=rset)
|
||||
|
||||
for row in rset['rows']:
|
||||
if self.manager.db == row['name']:
|
||||
db = row['name']
|
||||
if hasattr(str, 'decode'):
|
||||
db = db.decode('utf-8')
|
||||
if self.manager.db == db:
|
||||
connected = True
|
||||
else:
|
||||
conn = self.manager.connection(row['name'])
|
||||
@@ -256,6 +282,11 @@ class DatabaseView(PGChildNodeView):
|
||||
)
|
||||
status, res = self.conn.execute_dict(SQL)
|
||||
|
||||
if len(res['rows']) == 0:
|
||||
return gone(
|
||||
_("Couldnot find the database on the server.")
|
||||
)
|
||||
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
@@ -381,26 +412,22 @@ class DatabaseView(PGChildNodeView):
|
||||
This function to return list of avialable encodings
|
||||
"""
|
||||
res = [{'label': '', 'value': ''}]
|
||||
try:
|
||||
SQL = render_template(
|
||||
"/".join([self.template_path, 'get_encodings.sql'])
|
||||
)
|
||||
status, rset = self.conn.execute_dict(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
SQL = render_template(
|
||||
"/".join([self.template_path, 'get_encodings.sql'])
|
||||
)
|
||||
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['encoding'], 'value': row['encoding']}
|
||||
)
|
||||
|
||||
return make_json_response(
|
||||
data=res,
|
||||
status=200
|
||||
for row in rset['rows']:
|
||||
res.append(
|
||||
{'label': row['encoding'], 'value': row['encoding']}
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
return make_json_response(
|
||||
data=res,
|
||||
status=200
|
||||
)
|
||||
|
||||
@check_precondition(action="get_ctypes")
|
||||
def get_ctypes(self, gid, sid, did=None):
|
||||
@@ -413,25 +440,21 @@ class DatabaseView(PGChildNodeView):
|
||||
res.append(
|
||||
{'label': val, 'value': val}
|
||||
)
|
||||
try:
|
||||
SQL = render_template(
|
||||
"/".join([self.template_path, 'get_ctypes.sql'])
|
||||
)
|
||||
status, rset = self.conn.execute_dict(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
SQL = render_template(
|
||||
"/".join([self.template_path, 'get_ctypes.sql'])
|
||||
)
|
||||
status, rset = self.conn.execute_dict(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=rset)
|
||||
|
||||
for row in rset['rows']:
|
||||
if row['cname'] not in default_list:
|
||||
res.append({'label': row['cname'], 'value': row['cname']})
|
||||
for row in rset['rows']:
|
||||
if row['cname'] not in default_list:
|
||||
res.append({'label': row['cname'], 'value': row['cname']})
|
||||
|
||||
return make_json_response(
|
||||
data=res,
|
||||
status=200
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
return make_json_response(
|
||||
data=res,
|
||||
status=200
|
||||
)
|
||||
|
||||
@check_precondition(action="create")
|
||||
def create(self, gid, sid):
|
||||
@@ -440,7 +463,9 @@ class DatabaseView(PGChildNodeView):
|
||||
u'name'
|
||||
]
|
||||
|
||||
data = request.form if request.form else json.loads(request.data.decode())
|
||||
data = request.form if request.form else json.loads(
|
||||
request.data, encoding='utf-8'
|
||||
)
|
||||
|
||||
for arg in required_args:
|
||||
if arg not in data:
|
||||
@@ -451,72 +476,64 @@ class DatabaseView(PGChildNodeView):
|
||||
"Could not find the required parameter (%s)." % arg
|
||||
)
|
||||
)
|
||||
try:
|
||||
# The below SQL will execute CREATE DDL only
|
||||
SQL = render_template(
|
||||
"/".join([self.template_path, 'create.sql']),
|
||||
data=data, conn=self.conn
|
||||
)
|
||||
# The below SQL will execute CREATE DDL only
|
||||
SQL = render_template(
|
||||
"/".join([self.template_path, 'create.sql']),
|
||||
data=data, conn=self.conn
|
||||
)
|
||||
status, msg = self.conn.execute_scalar(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=msg)
|
||||
|
||||
if 'datacl' in data:
|
||||
data['datacl'] = parse_priv_to_db(data['datacl'], 'DATABASE')
|
||||
|
||||
# The below SQL will execute rest DMLs because we can not execute CREATE with any other
|
||||
SQL = render_template(
|
||||
"/".join([self.template_path, 'grant.sql']),
|
||||
data=data, conn=self.conn
|
||||
)
|
||||
SQL = SQL.strip('\n').strip(' ')
|
||||
if SQL and SQL != "":
|
||||
status, msg = self.conn.execute_scalar(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=msg)
|
||||
|
||||
if 'datacl' in data:
|
||||
data['datacl'] = parse_priv_to_db(data['datacl'], 'DATABASE')
|
||||
# We need oid of newly created database
|
||||
SQL = render_template(
|
||||
"/".join([self.template_path, 'properties.sql']),
|
||||
name=data['name'], conn=self.conn, last_system_oid=0
|
||||
)
|
||||
SQL = SQL.strip('\n').strip(' ')
|
||||
if SQL and SQL != "":
|
||||
status, res = self.conn.execute_dict(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
# The below SQL will execute rest DMLs because we can not execute CREATE with any other
|
||||
SQL = render_template(
|
||||
"/".join([self.template_path, 'grant.sql']),
|
||||
data=data, conn=self.conn
|
||||
)
|
||||
SQL = SQL.strip('\n').strip(' ')
|
||||
if SQL and SQL != "":
|
||||
status, msg = self.conn.execute_scalar(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=msg)
|
||||
|
||||
# We need oid of newly created database
|
||||
SQL = render_template(
|
||||
"/".join([self.template_path, 'properties.sql']),
|
||||
name=data['name'], conn=self.conn, last_system_oid=0
|
||||
)
|
||||
SQL = SQL.strip('\n').strip(' ')
|
||||
if SQL and SQL != "":
|
||||
status, res = self.conn.execute_dict(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
response = res['rows'][0]
|
||||
|
||||
return jsonify(
|
||||
node=self.blueprint.generate_browser_node(
|
||||
response['did'],
|
||||
sid,
|
||||
response['name'],
|
||||
icon="icon-database-not-connected",
|
||||
connected=False,
|
||||
tablespace=response['default_tablespace'],
|
||||
allowConn=True,
|
||||
canCreate=response['cancreate'],
|
||||
canDisconn=True
|
||||
)
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return make_json_response(
|
||||
status=410,
|
||||
success=0,
|
||||
errormsg=e.message
|
||||
response = res['rows'][0]
|
||||
|
||||
return jsonify(
|
||||
node=self.blueprint.generate_browser_node(
|
||||
response['did'],
|
||||
sid,
|
||||
response['name'],
|
||||
icon="icon-database-not-connected",
|
||||
connected=False,
|
||||
tablespace=response['default_tablespace'],
|
||||
allowConn=True,
|
||||
canCreate=response['cancreate'],
|
||||
canDisconn=True,
|
||||
canDrop=True
|
||||
)
|
||||
)
|
||||
|
||||
@check_precondition(action="update")
|
||||
def update(self, gid, sid, did):
|
||||
"""Update the database."""
|
||||
|
||||
data = request.form if request.form else json.loads(
|
||||
request.data.decode()
|
||||
request.data, encoding='utf-8'
|
||||
)
|
||||
info = "nothing to update."
|
||||
|
||||
if did is not None:
|
||||
# Fetch the name of database for comparison
|
||||
@@ -531,95 +548,82 @@ class DatabaseView(PGChildNodeView):
|
||||
|
||||
if len(rset['rows']) == 0:
|
||||
return gone(
|
||||
_("Couldnot find the database on the server.")
|
||||
_("Could not find the database on the server.")
|
||||
)
|
||||
|
||||
data['old_name'] = (rset['rows'][0])['name']
|
||||
if 'name' not in data:
|
||||
data['name'] = data['old_name']
|
||||
|
||||
try:
|
||||
status = self.manager.release(did=did)
|
||||
conn = self.manager.connection()
|
||||
for action in ["rename_database", "tablespace"]:
|
||||
SQL = self.get_offline_sql(gid, sid, data, did, action)
|
||||
SQL = SQL.strip('\n').strip(' ')
|
||||
if SQL and SQL != "":
|
||||
status, msg = conn.execute_scalar(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=msg)
|
||||
|
||||
info = "Database updated."
|
||||
|
||||
self.conn = self.manager.connection(database=data['name'], auto_reconnect=True)
|
||||
status, errmsg = self.conn.connect()
|
||||
|
||||
SQL = self.get_online_sql(gid, sid, data, did)
|
||||
status = self.manager.release(did=did)
|
||||
conn = self.manager.connection()
|
||||
for action in ["rename_database", "tablespace"]:
|
||||
SQL = self.get_offline_sql(gid, sid, data, did, action)
|
||||
SQL = SQL.strip('\n').strip(' ')
|
||||
if SQL and SQL != "":
|
||||
status, msg = self.conn.execute_scalar(SQL)
|
||||
status, msg = conn.execute_scalar(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=msg)
|
||||
|
||||
info = "Database updated."
|
||||
self.conn = self.manager.connection(database=data['name'], auto_reconnect=True)
|
||||
status, errmsg = self.conn.connect()
|
||||
|
||||
return make_json_response(
|
||||
success=1,
|
||||
info=info,
|
||||
data={
|
||||
'id': did,
|
||||
'sid': sid,
|
||||
'gid': gid,
|
||||
}
|
||||
)
|
||||
SQL = self.get_online_sql(gid, sid, data, did)
|
||||
SQL = SQL.strip('\n').strip(' ')
|
||||
if SQL and SQL != "":
|
||||
status, msg = self.conn.execute_scalar(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=msg)
|
||||
|
||||
except Exception as e:
|
||||
return make_json_response(
|
||||
status=410,
|
||||
success=0,
|
||||
errormsg=str(e)
|
||||
return jsonify(
|
||||
node=self.blueprint.generate_browser_node(
|
||||
did,
|
||||
sid,
|
||||
data['name'],
|
||||
"pg-icon-{0}".format(self.node_type)
|
||||
)
|
||||
)
|
||||
|
||||
@check_precondition(action="drop")
|
||||
def delete(self, gid, sid, did):
|
||||
"""Delete the database."""
|
||||
try:
|
||||
default_conn = self.manager.connection()
|
||||
default_conn = self.manager.connection()
|
||||
SQL = render_template(
|
||||
"/".join([self.template_path, 'delete.sql']),
|
||||
did=did, conn=self.conn
|
||||
)
|
||||
status, res = default_conn.execute_scalar(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
if res is None:
|
||||
return make_json_response(
|
||||
status=410,
|
||||
success=0,
|
||||
errormsg=_(
|
||||
'Error: Object not found.'
|
||||
),
|
||||
info=_(
|
||||
'The specified database could not be found.\n'
|
||||
)
|
||||
)
|
||||
else:
|
||||
|
||||
status = self.manager.release(did=did)
|
||||
|
||||
SQL = render_template(
|
||||
"/".join([self.template_path, 'delete.sql']),
|
||||
did=did, conn=self.conn
|
||||
datname=res, conn=self.conn
|
||||
)
|
||||
status, res = default_conn.execute_scalar(SQL)
|
||||
|
||||
status, msg = default_conn.execute_scalar(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
# reconnect if database drop failed.
|
||||
conn = self.manager.connection(did=did, auto_reconnect=True)
|
||||
status, errmsg = conn.connect()
|
||||
|
||||
if res is None:
|
||||
return make_json_response(
|
||||
success=0,
|
||||
errormsg=_(
|
||||
'The specified database could not be found.\n'
|
||||
)
|
||||
)
|
||||
else:
|
||||
return internal_server_error(errormsg=msg)
|
||||
|
||||
status = self.manager.release(did=did)
|
||||
|
||||
SQL = render_template(
|
||||
"/".join([self.template_path, 'delete.sql']),
|
||||
datname=res, conn=self.conn
|
||||
)
|
||||
|
||||
status, msg = default_conn.execute_scalar(SQL)
|
||||
if not status:
|
||||
# reconnect if database drop failed.
|
||||
conn = self.manager.connection(did=did, auto_reconnect=True)
|
||||
status, errmsg = conn.connect()
|
||||
return internal_server_error(errormsg=msg)
|
||||
|
||||
except Exception as e:
|
||||
return make_json_response(
|
||||
success=0,
|
||||
errormsg=str(e))
|
||||
|
||||
return make_json_response(success=1)
|
||||
|
||||
@@ -631,28 +635,21 @@ class DatabaseView(PGChildNodeView):
|
||||
data = {}
|
||||
for k, v in request.args.items():
|
||||
try:
|
||||
data[k] = json.loads(v)
|
||||
data[k] = json.loads(v, encoding='utf-8')
|
||||
except ValueError:
|
||||
data[k] = v
|
||||
try:
|
||||
status, res = self.get_sql(gid, sid, data, did)
|
||||
status, res = self.get_sql(gid, sid, data, did)
|
||||
|
||||
if not status:
|
||||
return res
|
||||
if not status:
|
||||
return res
|
||||
|
||||
res = re.sub('\n{2,}', '\n\n', res)
|
||||
SQL = res.strip('\n').strip(' ')
|
||||
res = re.sub('\n{2,}', '\n\n', res)
|
||||
SQL = res.strip('\n').strip(' ')
|
||||
|
||||
return make_json_response(
|
||||
data=SQL,
|
||||
status=200
|
||||
)
|
||||
except Exception as e:
|
||||
current_app.logger.exception(e)
|
||||
return make_json_response(
|
||||
data=_("-- modified SQL"),
|
||||
status=200
|
||||
)
|
||||
return make_json_response(
|
||||
data=SQL,
|
||||
status=200
|
||||
)
|
||||
|
||||
def get_sql(self, gid, sid, data, did=None):
|
||||
SQL = ''
|
||||
@@ -668,7 +665,7 @@ class DatabaseView(PGChildNodeView):
|
||||
return False, internal_server_error(errormsg=rset)
|
||||
|
||||
if len(rset['rows']) == 0:
|
||||
return False, gone(
|
||||
return gone(
|
||||
_("Could not find the database on the server.")
|
||||
)
|
||||
|
||||
@@ -703,7 +700,7 @@ class DatabaseView(PGChildNodeView):
|
||||
acls = render_template(
|
||||
"/".join([self.template_path, 'allowed_privs.json'])
|
||||
)
|
||||
acls = json.loads(acls)
|
||||
acls = json.loads(acls, encoding='utf-8')
|
||||
except Exception as e:
|
||||
current_app.logger.exception(e)
|
||||
|
||||
@@ -736,7 +733,7 @@ class DatabaseView(PGChildNodeView):
|
||||
acls = render_template(
|
||||
"/".join([self.template_path, 'allowed_privs.json'])
|
||||
)
|
||||
acls = json.loads(acls)
|
||||
acls = json.loads(acls, encoding='utf-8')
|
||||
except Exception as e:
|
||||
current_app.logger.exception(e)
|
||||
|
||||
@@ -791,7 +788,10 @@ class DatabaseView(PGChildNodeView):
|
||||
server.
|
||||
"""
|
||||
last_system_oid = 0 if self.blueprint.show_system_objects else \
|
||||
(self.manager.db_info[self.manager.did])['datlastsysoid']
|
||||
(self.manager.db_info[self.manager.did])['datlastsysoid'] \
|
||||
if self.manager.db_info is not None and \
|
||||
self.manager.did in self.manager.db_info else 0
|
||||
|
||||
status, res = self.conn.execute_dict(
|
||||
render_template(
|
||||
"/".join([self.template_path, 'stats.sql']),
|
||||
@@ -859,9 +859,20 @@ class DatabaseView(PGChildNodeView):
|
||||
frmtd_variables = parse_variables_from_db(res1['rows'])
|
||||
result.update(frmtd_variables)
|
||||
|
||||
sql_header = "-- Database: {0}\n\n-- ".format(result['name'])
|
||||
if hasattr(str, 'decode'):
|
||||
sql_header = sql_header.decode('utf-8')
|
||||
|
||||
sql_header += render_template(
|
||||
"/".join([self.template_path, 'delete.sql']),
|
||||
datname=result['name'], conn=self.conn
|
||||
)
|
||||
|
||||
SQL = self.get_new_sql(gid, sid, result, did)
|
||||
SQL = re.sub('\n{2,}', '\n\n', SQL)
|
||||
SQL = sql_header + '\n' + SQL
|
||||
SQL = SQL.strip('\n')
|
||||
|
||||
return ajax_response(response=SQL)
|
||||
|
||||
@check_precondition()
|
||||
|
||||
@@ -9,17 +9,16 @@
|
||||
|
||||
"""Implements Cast Node"""
|
||||
|
||||
import json
|
||||
import simplejson as json
|
||||
from functools import wraps
|
||||
|
||||
import pgadmin.browser.server_groups.servers.databases as databases
|
||||
from flask import render_template, make_response, request, jsonify
|
||||
from flask.ext.babel import gettext
|
||||
from flask_babel import gettext
|
||||
from pgadmin.browser.collection import CollectionNodeModule
|
||||
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.ajax import make_json_response, internal_server_error, \
|
||||
make_response as ajax_response, gone
|
||||
from pgadmin.utils.driver import get_driver
|
||||
|
||||
from config import PG_DEFAULT_DRIVER
|
||||
@@ -201,17 +200,8 @@ class CastView(PGChildNodeView):
|
||||
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!"
|
||||
)
|
||||
)
|
||||
ver = self.manager.version
|
||||
# we will set template path for sql scripts
|
||||
if ver >= 90100:
|
||||
self.template_path = 'cast/sql/9.1_plus'
|
||||
# Set template path for the SQL scripts
|
||||
self.template_path = 'cast/sql/9.1_plus'
|
||||
|
||||
return f(*args, **kwargs)
|
||||
|
||||
@@ -226,9 +216,13 @@ class CastView(PGChildNodeView):
|
||||
:param did: database id
|
||||
:return:
|
||||
"""
|
||||
last_system_oid = 0 if self.blueprint.show_system_objects else \
|
||||
(self.manager.db_info[did])['datlastsysoid'] \
|
||||
if self.manager.db_info is not None and \
|
||||
did in self.manager.db_info else 0
|
||||
sql = render_template(
|
||||
"/".join([self.template_path, 'properties.sql']),
|
||||
datlastsysoid=self.manager.db_info[did]['datlastsysoid'],
|
||||
datlastsysoid=last_system_oid,
|
||||
showsysobj=self.blueprint.show_system_objects
|
||||
)
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
@@ -255,9 +249,13 @@ class CastView(PGChildNodeView):
|
||||
:return:
|
||||
"""
|
||||
res = []
|
||||
last_system_oid = 0 if self.blueprint.show_system_objects else \
|
||||
(self.manager.db_info[did])['datlastsysoid'] \
|
||||
if self.manager.db_info is not None and \
|
||||
did in self.manager.db_info else 0
|
||||
sql = render_template(
|
||||
"/".join([self.template_path, 'nodes.sql']),
|
||||
datlastsysoid=self.manager.db_info[did]['datlastsysoid'],
|
||||
datlastsysoid=last_system_oid,
|
||||
showsysobj=self.blueprint.show_system_objects
|
||||
)
|
||||
status, rset = self.conn.execute_2darray(sql)
|
||||
@@ -280,7 +278,9 @@ class CastView(PGChildNodeView):
|
||||
|
||||
@check_precondition
|
||||
def node(self, gid, sid, did, cid):
|
||||
res = []
|
||||
"""
|
||||
This function will fetch properties of the cast node
|
||||
"""
|
||||
sql = render_template(
|
||||
"/".join([self.template_path, 'nodes.sql']),
|
||||
cid=cid
|
||||
@@ -290,18 +290,17 @@ class CastView(PGChildNodeView):
|
||||
return internal_server_error(errormsg=rset)
|
||||
|
||||
for row in rset['rows']:
|
||||
res.append(
|
||||
self.blueprint.generate_browser_node(
|
||||
return make_json_response(
|
||||
data=self.blueprint.generate_browser_node(
|
||||
row['oid'],
|
||||
did,
|
||||
row['name'],
|
||||
icon="icon-fts_template"
|
||||
))
|
||||
),
|
||||
status=200
|
||||
)
|
||||
|
||||
return make_json_response(
|
||||
data=res,
|
||||
status=200
|
||||
)
|
||||
return gone(errormsg=gettext("Could not find the specified cast."))
|
||||
|
||||
@check_precondition
|
||||
def properties(self, gid, sid, did, cid):
|
||||
@@ -313,17 +312,24 @@ class CastView(PGChildNodeView):
|
||||
:param cid: cast id
|
||||
:return:
|
||||
"""
|
||||
last_system_oid = (self.manager.db_info[did])['datlastsysoid'] if \
|
||||
self.manager.db_info is not None and \
|
||||
did in self.manager.db_info else 0
|
||||
sql = render_template(
|
||||
"/".join([self.template_path, 'properties.sql']),
|
||||
cid=cid,
|
||||
datlastsysoid=self.manager.db_info[did]['datlastsysoid'],
|
||||
datlastsysoid=last_system_oid,
|
||||
showsysobj=self.blueprint.show_system_objects
|
||||
)
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
result = res['rows'][0]
|
||||
|
||||
if len(res['rows']) == 0:
|
||||
return gone(
|
||||
gettext("Could not find the cast information.")
|
||||
)
|
||||
|
||||
return ajax_response(
|
||||
response=res['rows'][0],
|
||||
@@ -345,7 +351,9 @@ class CastView(PGChildNodeView):
|
||||
'trgtyp'
|
||||
]
|
||||
|
||||
data = request.form if request.form else json.loads(request.data.decode())
|
||||
data = request.form if request.form else json.loads(
|
||||
request.data, encoding='utf-8'
|
||||
)
|
||||
for arg in required_args:
|
||||
if arg not in data:
|
||||
return make_json_response(
|
||||
@@ -365,10 +373,14 @@ class CastView(PGChildNodeView):
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
# we need oid to to add object in tree at browser, below sql will gives the same
|
||||
last_system_oid = 0 if self.blueprint.show_system_objects else \
|
||||
(self.manager.db_info[did])['datlastsysoid'] \
|
||||
if self.manager.db_info is not None and \
|
||||
did in self.manager.db_info else 0
|
||||
sql = render_template("/".join([self.template_path, 'properties.sql']),
|
||||
srctyp=data['srctyp'],
|
||||
trgtyp=data['trgtyp'],
|
||||
datlastsysoid=self.manager.db_info[did]['datlastsysoid'],
|
||||
datlastsysoid=last_system_oid,
|
||||
showsysobj=self.blueprint.show_system_objects
|
||||
)
|
||||
status, cid = self.conn.execute_scalar(sql)
|
||||
@@ -396,35 +408,23 @@ class CastView(PGChildNodeView):
|
||||
:param gid: group id
|
||||
:return:
|
||||
"""
|
||||
data = request.form if request.form else json.loads(request.data.decode())
|
||||
sql = self.get_sql(gid, sid, did, data, cid)
|
||||
data = request.form if request.form else json.loads(
|
||||
request.data, encoding='utf-8'
|
||||
)
|
||||
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)
|
||||
sql, name = self.get_sql(gid, sid, did, data, cid)
|
||||
status, res = self.conn.execute_scalar(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
return make_json_response(
|
||||
success=1,
|
||||
info="Cast updated",
|
||||
data={
|
||||
'id': cid,
|
||||
'sid': sid,
|
||||
'gid': gid,
|
||||
'did': did
|
||||
}
|
||||
)
|
||||
else:
|
||||
return make_json_response(
|
||||
success=1,
|
||||
info="Nothing to update",
|
||||
data={
|
||||
'id': cid,
|
||||
'sid': sid,
|
||||
'gid': gid,
|
||||
'did': did
|
||||
}
|
||||
return jsonify(
|
||||
node=self.blueprint.generate_browser_node(
|
||||
cid,
|
||||
did,
|
||||
name,
|
||||
"icon-{0}".format(self.node_type)
|
||||
)
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
@@ -454,6 +454,18 @@ class CastView(PGChildNodeView):
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
if not res['rows']:
|
||||
return make_json_response(
|
||||
status=410,
|
||||
success=0,
|
||||
errormsg=gettext(
|
||||
'Error: Object not found.'
|
||||
),
|
||||
info=gettext(
|
||||
'The specified cast object could not be found.\n'
|
||||
)
|
||||
)
|
||||
|
||||
# drop cast
|
||||
result = res['rows'][0]
|
||||
sql = render_template("/".join([self.template_path, 'delete.sql']),
|
||||
@@ -490,17 +502,18 @@ class CastView(PGChildNodeView):
|
||||
:return:
|
||||
"""
|
||||
data = request.args
|
||||
sql = self.get_sql(gid, sid, did, data, cid)
|
||||
if isinstance(sql, str) and sql and sql.strip('\n') and sql.strip(' '):
|
||||
try:
|
||||
sql, name = self.get_sql(gid, sid, did, data, cid)
|
||||
sql = sql.strip('\n').strip(' ')
|
||||
if sql == '':
|
||||
sql = "--modified SQL"
|
||||
|
||||
return make_json_response(
|
||||
data=sql,
|
||||
status=200
|
||||
)
|
||||
else:
|
||||
return make_json_response(
|
||||
data="--modified SQL",
|
||||
status=200
|
||||
)
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
def get_sql(self, gid, sid, did, data, cid=None):
|
||||
"""
|
||||
@@ -512,31 +525,32 @@ class CastView(PGChildNodeView):
|
||||
:param data: model data
|
||||
:return:
|
||||
"""
|
||||
try:
|
||||
if cid is not None:
|
||||
sql = render_template("/".join([self.template_path, 'properties.sql']),
|
||||
cid=cid,
|
||||
datlastsysoid=self.manager.db_info[did]['datlastsysoid'],
|
||||
showsysobj=self.blueprint.show_system_objects)
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
if cid is not None:
|
||||
last_system_oid = 0 if self.blueprint.show_system_objects else \
|
||||
(self.manager.db_info[did])['datlastsysoid'] \
|
||||
if self.manager.db_info is not None and \
|
||||
did in self.manager.db_info else 0
|
||||
sql = render_template("/".join([self.template_path, 'properties.sql']),
|
||||
cid=cid,
|
||||
datlastsysoid=last_system_oid,
|
||||
showsysobj=self.blueprint.show_system_objects)
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
old_data = res['rows'][0]
|
||||
sql = render_template(
|
||||
"/".join([self.template_path, 'update.sql']),
|
||||
data=data, o_data=old_data
|
||||
)
|
||||
old_data = res['rows'][0]
|
||||
sql = render_template(
|
||||
"/".join([self.template_path, 'update.sql']),
|
||||
data=data, o_data=old_data
|
||||
)
|
||||
return str(sql), data['name'] if 'name' in data else old_data['name']
|
||||
else:
|
||||
if 'srctyp' in data and 'trgtyp' in data:
|
||||
sql = render_template("/".join([self.template_path, 'create.sql']), data=data, conn=self.conn)
|
||||
else:
|
||||
if 'srctyp' in data and 'trgtyp' in data:
|
||||
sql = render_template("/".join([self.template_path, 'create.sql']), data=data, conn=self.conn)
|
||||
else:
|
||||
sql = "-- incomplete definition"
|
||||
return str(sql)
|
||||
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
return "-- incomplete definition", None
|
||||
return str(sql), data['srctyp'] + "->" + data["trgtyp"]
|
||||
|
||||
@check_precondition
|
||||
def get_functions(self, gid, sid, did, cid=None):
|
||||
@@ -549,7 +563,10 @@ class CastView(PGChildNodeView):
|
||||
:return:
|
||||
"""
|
||||
res = []
|
||||
data = request.form if request.form else json.loads(request.data.decode())
|
||||
data = request.form if request.form else json.loads(
|
||||
request.data, encoding='utf-8'
|
||||
)
|
||||
|
||||
sql = render_template("/".join([self.template_path, 'functions.sql']),
|
||||
srctyp=data['srctyp'],
|
||||
trgtyp=data['trgtyp'])
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
# #################################################################
|
||||
#
|
||||
# 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 CastTestGenerator(BaseTestGenerator):
|
||||
|
||||
def runTest(self):
|
||||
return []
|
||||
@@ -0,0 +1,65 @@
|
||||
# #################################################################
|
||||
#
|
||||
# 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
|
||||
from regression import test_utils as utils
|
||||
from . import utils as cast_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.tests import \
|
||||
utils as database_utils
|
||||
from pgadmin.browser.server_groups.servers.tests import utils as server_utils
|
||||
|
||||
|
||||
class CastsAddTestCase(BaseTestGenerator):
|
||||
scenarios = [
|
||||
# Fetching default URL for cast node.
|
||||
('Check Cast Node', dict(url='/browser/cast/obj/'))
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
"""
|
||||
This function perform the following tasks:
|
||||
1. Add and connect to the test server(s)
|
||||
2. Add database(s) connected to server(s)
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
# 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)
|
||||
|
||||
def runTest(self):
|
||||
""" This function will add cast under database node. """
|
||||
|
||||
cast_utils.add_cast(self.tester)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
"""
|
||||
This function deletes the added cast, database, server and the
|
||||
'parent_id.pkl' file which is created in setUpClass.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
cast_utils.delete_cast(cls.tester)
|
||||
database_utils.delete_database(cls.tester)
|
||||
server_utils.delete_server(cls.tester)
|
||||
utils.delete_parent_id_file()
|
||||
@@ -0,0 +1,102 @@
|
||||
# # #################################################################
|
||||
# #
|
||||
# # 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
|
||||
from regression import test_utils as utils
|
||||
from regression.test_utils import get_ids
|
||||
from . import utils as cast_utils
|
||||
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 regression.test_setup import advanced_config_data
|
||||
import json
|
||||
|
||||
|
||||
class CastsDeleteTestCase(BaseTestGenerator):
|
||||
""" This class will fetch the cast node added under database node. """
|
||||
|
||||
scenarios = [
|
||||
# Fetching default URL for cast node.
|
||||
('Check Cast Node', dict(url='/browser/cast/obj/'))
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
"""
|
||||
This function perform the following tasks:
|
||||
1. Add and connect to the test server(s)
|
||||
2. Add database(s) connected to server(s)
|
||||
3. Add cast(s) to databases
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
# 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 cast(s) to database(s)
|
||||
cast_utils.add_cast(cls.tester)
|
||||
|
||||
def runTest(self):
|
||||
""" This function will delete added cast(s)."""
|
||||
|
||||
all_id = get_ids()
|
||||
server_ids = all_id["sid"]
|
||||
db_ids_dict = all_id["did"][0]
|
||||
cast_ids_dict = all_id["cid"][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 len(db_con) == 0:
|
||||
raise Exception("No database(s) to delete for server id %s"
|
||||
% server_id)
|
||||
cast_id = cast_ids_dict[server_id]
|
||||
cast_get_data = cast_utils.verify_cast(self.tester,
|
||||
utils.SERVER_GROUP,
|
||||
server_id,
|
||||
db_id, cast_id)
|
||||
|
||||
if cast_get_data.status_code == 200:
|
||||
|
||||
delete_response = self.tester.delete(
|
||||
self.url + str(utils.SERVER_GROUP) + '/' +
|
||||
str(server_id) + '/' + str(db_id) +
|
||||
'/' + str(cast_id),
|
||||
follow_redirects=True)
|
||||
response_data = json.loads(delete_response.data.decode('utf-8'))
|
||||
self.assertTrue(response_data['success'], 1)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
"""
|
||||
This function delete the added cast, database, server and the
|
||||
'parent_id.pkl' file which is created in setUpClass.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
database_utils.delete_database(cls.tester)
|
||||
server_utils.delete_server(cls.tester)
|
||||
utils.delete_parent_id_file()
|
||||
@@ -0,0 +1,87 @@
|
||||
# #################################################################
|
||||
#
|
||||
# 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
|
||||
from regression import test_utils as utils
|
||||
from regression.test_utils import get_ids
|
||||
from . import utils as cast_utils
|
||||
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
|
||||
|
||||
|
||||
class CastsGetTestCase(BaseTestGenerator):
|
||||
""" This class will fetch the cast node added under database node. """
|
||||
|
||||
scenarios = [
|
||||
# Fetching default URL for cast node.
|
||||
('Check Cast Node', dict(url='/browser/cast/obj/'))
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
"""
|
||||
This function used to add the sever, database, and cast
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
# 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)
|
||||
|
||||
cast_utils.add_cast(cls.tester)
|
||||
|
||||
def runTest(self):
|
||||
""" This function will get added cast."""
|
||||
|
||||
all_id = get_ids()
|
||||
server_ids = all_id["sid"]
|
||||
db_ids_dict = all_id["did"][0]
|
||||
cast_ids_dict = all_id["cid"][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 len(db_con) == 0:
|
||||
raise Exception("No database(s) to delete for server id %s"
|
||||
% server_id)
|
||||
cast_id = cast_ids_dict[server_id]
|
||||
cast_get_data = cast_utils.verify_cast(self.tester,
|
||||
utils.SERVER_GROUP,
|
||||
server_id,
|
||||
db_id, cast_id)
|
||||
self.assertEquals(cast_get_data.status_code, 200)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
"""
|
||||
This function deletes the added cast, database, server and the
|
||||
'parent_id.pkl' file which is created in setup() function.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
cast_utils.delete_cast(cls.tester)
|
||||
database_utils.delete_database(cls.tester)
|
||||
server_utils.delete_server(cls.tester)
|
||||
utils.delete_parent_id_file()
|
||||
@@ -0,0 +1,107 @@
|
||||
# #################################################################
|
||||
#
|
||||
# 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
|
||||
from regression import test_utils as utils
|
||||
from regression.test_utils import get_ids
|
||||
from . import utils as cast_utils
|
||||
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 regression.test_setup import advanced_config_data
|
||||
import json
|
||||
|
||||
|
||||
class CastsPutTestCase(BaseTestGenerator):
|
||||
""" This class will fetch the cast node added under database node. """
|
||||
|
||||
scenarios = [
|
||||
# Fetching default URL for cast node.
|
||||
('Check Cast Node', dict(url='/browser/cast/obj/'))
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
"""
|
||||
This function perform the following tasks:
|
||||
1. Add and connect to the test server(s)
|
||||
2. Add database(s) connected to server(s)
|
||||
3. Add cast(s) to databases
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
# 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)
|
||||
|
||||
cast_utils.add_cast(cls.tester)
|
||||
|
||||
def runTest(self):
|
||||
""" This function will update added cast."""
|
||||
|
||||
all_id = get_ids()
|
||||
server_ids = all_id["sid"]
|
||||
db_ids_dict = all_id["did"][0]
|
||||
cast_ids_dict = all_id["cid"][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 len(db_con) == 0:
|
||||
raise Exception("No database(s) to delete for server id %s"
|
||||
% server_id)
|
||||
cast_id = cast_ids_dict[server_id]
|
||||
cast_get_data = cast_utils.verify_cast(self.tester,
|
||||
utils.SERVER_GROUP,
|
||||
server_id,
|
||||
db_id, cast_id)
|
||||
|
||||
if cast_get_data.status_code == 200:
|
||||
|
||||
data = {
|
||||
"description": advanced_config_data["cast_update_data"]
|
||||
["comment"],
|
||||
"id": cast_id
|
||||
}
|
||||
|
||||
put_response = self.tester.put(
|
||||
self.url + str(utils.SERVER_GROUP) + '/' +
|
||||
str(server_id) + '/' + str(
|
||||
db_id) +
|
||||
'/' + str(cast_id),
|
||||
data=json.dumps(data),
|
||||
follow_redirects=True)
|
||||
self.assertEquals(put_response.status_code, 200)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
"""
|
||||
This function deletes the added cast, database, server and the
|
||||
'parent_id.pkl' file which is created in setUpClass.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
cast_utils.delete_cast(cls.tester)
|
||||
database_utils.delete_database(cls.tester)
|
||||
server_utils.delete_server(cls.tester)
|
||||
utils.delete_parent_id_file()
|
||||
@@ -0,0 +1,145 @@
|
||||
# #################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2016, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
# ##################################################################
|
||||
|
||||
import os
|
||||
import pickle
|
||||
import json
|
||||
from regression.test_setup import advanced_config_data, pickle_path
|
||||
from regression import test_utils as utils
|
||||
from pgadmin.browser.server_groups.servers.databases.tests import \
|
||||
utils as database_utils
|
||||
from pgadmin.browser.server_groups.servers.tests import utils as server_utils
|
||||
|
||||
CAST_URL = '/browser/cast/obj/'
|
||||
|
||||
|
||||
def get_cast_config_data(server_connect_data):
|
||||
|
||||
adv_config_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['casts_credentials']:
|
||||
if db_user == config_test_data['owner']:
|
||||
adv_config_data = config_test_data
|
||||
|
||||
data = {
|
||||
"castcontext": adv_config_data
|
||||
['cast_context'],
|
||||
"encoding": adv_config_data
|
||||
['encoding'],
|
||||
"name": adv_config_data
|
||||
['name'],
|
||||
"srctyp": adv_config_data
|
||||
['source_type'],
|
||||
"trgtyp": adv_config_data
|
||||
['target_type']
|
||||
}
|
||||
return data
|
||||
|
||||
|
||||
def add_cast(tester):
|
||||
"""
|
||||
This function add the cast in the existing database
|
||||
|
||||
:param tester: test object
|
||||
:type tester: flask test object
|
||||
:return:None
|
||||
"""
|
||||
|
||||
all_id = utils.get_ids()
|
||||
server_ids = all_id["sid"]
|
||||
db_ids_dict = all_id["did"][0]
|
||||
server_group = utils.config_data['server_group']
|
||||
|
||||
for server_id in server_ids:
|
||||
db_id = db_ids_dict[int(server_id)]
|
||||
db_con = database_utils.verify_database(tester, server_group,
|
||||
server_id, db_id)
|
||||
if db_con['data']['connected']:
|
||||
server_connect_response = server_utils.verify_server(
|
||||
tester, server_group, server_id)
|
||||
|
||||
data = get_cast_config_data(server_connect_response)
|
||||
|
||||
response = tester.post(CAST_URL + str(server_group) + '/' +
|
||||
str(server_id) + '/' + str(
|
||||
db_id) + '/',
|
||||
data=json.dumps(data),
|
||||
content_type='html/json')
|
||||
|
||||
assert response.status_code == 200
|
||||
response_data = json.loads(response.data.decode('utf-8'))
|
||||
write_cast_info(response_data, server_id)
|
||||
|
||||
|
||||
def write_cast_info(response_data, server_id):
|
||||
"""
|
||||
This function writes the server's details to file parent_id.pkl
|
||||
|
||||
:param response_data: server's data
|
||||
:type response_data: list of dictionary
|
||||
:param pickle_id_dict: contains ids of server,database,tables etc.
|
||||
:type pickle_id_dict: dict
|
||||
:return: None
|
||||
"""
|
||||
|
||||
cast_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 'cid' in pickle_id_dict:
|
||||
if pickle_id_dict['cid']:
|
||||
# Add the cast_id as value in dict
|
||||
pickle_id_dict["cid"][0].update({server_id: cast_id})
|
||||
else:
|
||||
# Create new dict with server_id and cast_id
|
||||
pickle_id_dict["cid"].append({server_id: cast_id})
|
||||
cast_output = open(pickle_path, 'wb')
|
||||
pickle.dump(pickle_id_dict, cast_output)
|
||||
cast_output.close()
|
||||
|
||||
|
||||
def verify_cast(tester, server_group, server_id, db_id, cast_id):
|
||||
|
||||
cast_response = tester.get(CAST_URL + str(server_group) + '/' +
|
||||
str(server_id) + '/' + str(db_id) +
|
||||
'/' + str(cast_id),
|
||||
content_type='html/json')
|
||||
return cast_response
|
||||
|
||||
|
||||
def delete_cast(tester):
|
||||
all_id = utils.get_ids()
|
||||
server_ids = all_id["sid"]
|
||||
db_ids_dict = all_id["did"][0]
|
||||
cast_ids_dict = all_id["cid"][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 len(db_con) == 0:
|
||||
raise Exception("No database(s) to delete for server id %s"
|
||||
% server_id)
|
||||
cast_id = cast_ids_dict[server_id]
|
||||
cast_get_data = verify_cast(tester, utils.SERVER_GROUP,
|
||||
server_id,
|
||||
db_id, cast_id)
|
||||
|
||||
if cast_get_data.status_code == 200:
|
||||
delete_response = tester.delete(
|
||||
CAST_URL + str(utils.SERVER_GROUP) + '/' +
|
||||
str(server_id) + '/' + str(db_id) +
|
||||
'/' + str(cast_id),
|
||||
follow_redirects=True)
|
||||
return delete_response
|
||||
@@ -7,19 +7,19 @@
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
import json
|
||||
import simplejson as json
|
||||
import re
|
||||
from functools import wraps
|
||||
|
||||
import pgadmin.browser.server_groups.servers.databases as database
|
||||
from flask import render_template, make_response, request, jsonify
|
||||
from flask.ext.babel import gettext
|
||||
from flask_babel import gettext
|
||||
from pgadmin.browser.collection import CollectionNodeModule
|
||||
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.ajax import make_json_response, internal_server_error, \
|
||||
make_response as ajax_response
|
||||
from pgadmin.utils.driver import get_driver
|
||||
from pgadmin.utils.ajax import gone
|
||||
|
||||
from config import PG_DEFAULT_DRIVER
|
||||
|
||||
@@ -188,22 +188,12 @@ class EventTriggerView(PGChildNodeView):
|
||||
|
||||
@wraps(f)
|
||||
def wrap(*args, **kwargs):
|
||||
# Here args[0] will hold self & kwargs will hold gid,sid,did
|
||||
|
||||
# Here - args[0] will always 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!"
|
||||
)
|
||||
)
|
||||
|
||||
ver = self.manager.version
|
||||
if ver >= 90300:
|
||||
self.template_path = 'event_triggers/sql/9.3_plus'
|
||||
self.template_path = 'event_triggers/sql/9.3_plus'
|
||||
|
||||
return f(*args, **kwargs)
|
||||
|
||||
@@ -269,6 +259,55 @@ class EventTriggerView(PGChildNodeView):
|
||||
status=200
|
||||
)
|
||||
|
||||
@check_precondition
|
||||
def node(self, gid, sid, did, etid):
|
||||
"""
|
||||
This function will fetch properties of trigger node.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
etid: Event trigger ID
|
||||
|
||||
Returns:
|
||||
Json object of trigger node
|
||||
"""
|
||||
sql = render_template("/".join([self.template_path, 'nodes.sql']),
|
||||
etid=etid)
|
||||
status, res = self.conn.execute_2darray(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
for row in res['rows']:
|
||||
return make_json_response(
|
||||
data=self.blueprint.generate_browser_node(
|
||||
row['oid'],
|
||||
did,
|
||||
row['name'],
|
||||
icon="icon-%s" % self.node_type
|
||||
),
|
||||
status=200
|
||||
)
|
||||
|
||||
return gone(gettext("Could not find the specified event trigger."))
|
||||
|
||||
def _formatter(self, result):
|
||||
"""
|
||||
This function is ued to parse security lables
|
||||
"""
|
||||
seclabels = []
|
||||
if 'seclabels' in result and result['seclabels'] is not None:
|
||||
for sec in result['seclabels']:
|
||||
sec = re.search(r'([^=]+)=(.*$)', sec)
|
||||
seclabels.append({
|
||||
'provider': sec.group(1),
|
||||
'label': sec.group(2)
|
||||
})
|
||||
|
||||
result['seclabels'] = seclabels
|
||||
return result
|
||||
|
||||
@check_precondition
|
||||
def properties(self, gid, sid, did, etid):
|
||||
"""
|
||||
@@ -289,17 +328,14 @@ class EventTriggerView(PGChildNodeView):
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
result = res['rows'][0]
|
||||
sec_labels = []
|
||||
if len(res['rows']) == 0:
|
||||
return gone(
|
||||
gettext("Couldnot find the event trigger information.")
|
||||
)
|
||||
|
||||
result = res['rows'][0]
|
||||
result = self._formatter(result)
|
||||
|
||||
if 'seclabels' in result and result['seclabels'] is not None:
|
||||
for sec in result['seclabels']:
|
||||
sec = re.search(r'([^=]+)=(.*$)', sec)
|
||||
sec_labels.append({
|
||||
'provider': sec.group(1),
|
||||
'securitylabel': sec.group(2)
|
||||
})
|
||||
result.update({"seclabels": sec_labels})
|
||||
return ajax_response(
|
||||
response=result,
|
||||
status=200
|
||||
@@ -319,7 +355,10 @@ class EventTriggerView(PGChildNodeView):
|
||||
Returns:
|
||||
|
||||
"""
|
||||
data = request.form if request.form else json.loads(request.data.decode())
|
||||
data = request.form if request.form else json.loads(
|
||||
request.data, encoding='utf-8'
|
||||
)
|
||||
|
||||
required_args = {
|
||||
'name': 'Name',
|
||||
'eventowner': 'Owner',
|
||||
@@ -382,7 +421,9 @@ class EventTriggerView(PGChildNodeView):
|
||||
Returns:
|
||||
|
||||
"""
|
||||
data = request.form if request.form else json.loads(request.data.decode())
|
||||
data = request.form if request.form else json.loads(
|
||||
request.data, encoding='utf-8'
|
||||
)
|
||||
|
||||
try:
|
||||
sql = self.get_sql(data, etid)
|
||||
@@ -444,6 +485,18 @@ class EventTriggerView(PGChildNodeView):
|
||||
if not status:
|
||||
return internal_server_error(errormsg=name)
|
||||
|
||||
if name is None:
|
||||
return make_json_response(
|
||||
status=410,
|
||||
success=0,
|
||||
errormsg=gettext(
|
||||
'Error: Object not found.'
|
||||
),
|
||||
info=gettext(
|
||||
'The specified event trigger could not be found.\n'
|
||||
)
|
||||
)
|
||||
|
||||
sql = render_template("/".join([self.template_path, 'delete.sql']), name=name, cascade=cascade)
|
||||
status, res = self.conn.execute_scalar(sql)
|
||||
if not status:
|
||||
@@ -481,13 +534,14 @@ class EventTriggerView(PGChildNodeView):
|
||||
data = {}
|
||||
for k, v in request.args.items():
|
||||
try:
|
||||
data[k] = json.loads(v)
|
||||
data[k] = json.loads(v, encoding='utf-8')
|
||||
except ValueError:
|
||||
data[k] = v
|
||||
try:
|
||||
sql = self.get_sql(data, etid)
|
||||
sql = sql.strip('\n').strip(' ')
|
||||
|
||||
if sql == '':
|
||||
sql = "--modified SQL"
|
||||
return make_json_response(
|
||||
data=sql,
|
||||
status=200
|
||||
@@ -516,7 +570,15 @@ class EventTriggerView(PGChildNodeView):
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
if len(res['rows']) == 0:
|
||||
return gone(
|
||||
gettext("Couldnot find the event trigger information.")
|
||||
)
|
||||
|
||||
old_data = res['rows'][0]
|
||||
old_data = self._formatter(old_data)
|
||||
|
||||
for arg in required_args:
|
||||
if arg not in data:
|
||||
data[arg] = old_data[arg]
|
||||
@@ -535,7 +597,7 @@ class EventTriggerView(PGChildNodeView):
|
||||
err.append(required_args.get(arg, arg))
|
||||
if err:
|
||||
return make_json_response(
|
||||
status=400,
|
||||
status=410,
|
||||
success=0,
|
||||
errormsg=gettext(
|
||||
"Could not find the required parameter %s." % err
|
||||
@@ -568,6 +630,7 @@ class EventTriggerView(PGChildNodeView):
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
result = res['rows'][0]
|
||||
result = self._formatter(result)
|
||||
|
||||
sql = render_template("/".join([self.template_path, 'create.sql']), data=result, conn=self.conn)
|
||||
sql += "\n\n"
|
||||
@@ -579,6 +642,8 @@ class EventTriggerView(PGChildNodeView):
|
||||
return internal_server_error(errormsg=db_name)
|
||||
|
||||
sql_header = "-- Event Trigger: {0} on database {1}\n\n-- ".format(result['name'], db_name)
|
||||
if hasattr(str, 'decode'):
|
||||
sql_header = sql_header.decode('utf-8')
|
||||
|
||||
sql_header += render_template(
|
||||
"/".join([self.template_path, 'delete.sql']),
|
||||
|
||||
@@ -2,35 +2,6 @@ define(
|
||||
['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'],
|
||||
function($, _, S, pgAdmin, pgBrowser, alertify) {
|
||||
|
||||
// Extend the browser's node model class to create a security model
|
||||
var SecurityLabelModel = pgAdmin.Browser.Node.Model.extend({
|
||||
defaults: {
|
||||
provider: undefined,
|
||||
securitylabel: undefined
|
||||
},
|
||||
// Define the schema for the Security Label
|
||||
schema: [
|
||||
{id: 'provider', label:'Provider', type:'text', group: null, editable: true},
|
||||
{id: 'securitylabel', label:'Security Label', type: 'text', group:null, extraHeaderClasses: 'cellwidth-40', editable: true},
|
||||
],
|
||||
validate: function() {
|
||||
// Clear any existing errors.
|
||||
|
||||
this.errorModel.clear()
|
||||
if (_.isUndefined(this.get('provider')) || String(this.get('provider')).replace(/^\s+|\s+$/g, '') == '') {
|
||||
var msg = '{{ _('Provider cannot be empty.') }}';
|
||||
this.errorModel.set('provider',msg);
|
||||
return msg;
|
||||
}
|
||||
if (_.isUndefined(this.get('securitylabel')) || String(this.get('securitylabel')).replace(/^\s+|\s+$/g, '') == '') {
|
||||
var msg = '{{ _('Security Label cannot be empty.') }}';
|
||||
this.errorModel.set('securitylabel',msg);
|
||||
return msg;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
// Extend the browser's collection class for event trigger collection
|
||||
if (!pgBrowser.Nodes['coll-event_trigger']) {
|
||||
var databases = pgAdmin.Browser.Nodes['coll-event_trigger'] =
|
||||
@@ -145,10 +116,11 @@ function($, _, S, pgAdmin, pgBrowser, alertify) {
|
||||
},{
|
||||
id: 'when', label:'{{ _('When') }}', type: 'multiline', group: "Definition",
|
||||
},{
|
||||
id: 'providers', label: 'Security Labels', type: 'collection', group: "Security Labels",
|
||||
model: SecurityLabelModel, control: 'unique-col-collection', mode: ['edit', 'create'],
|
||||
canAdd: true, canDelete: true, uniqueCol : ['provider'],
|
||||
columns: ['provider','securitylabel']
|
||||
id: 'seclabels', label: '{{ _('Security Labels') }}',
|
||||
model: pgBrowser.SecLabelModel, editable: false, type: 'collection',
|
||||
group: '{{ _('Security') }}', mode: ['edit', 'create'],
|
||||
min_version: 90200, canAdd: true,
|
||||
canEdit: false, canDelete: true, control: 'unique-col-collection'
|
||||
}
|
||||
],
|
||||
// event trigger model data validation.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{% import 'macros/security.macros' as SECLABLE %}
|
||||
{% import 'macros/security.macros' as SECLABEL %}
|
||||
{% if data %}
|
||||
{% if data.enabled and data.enabled != "O" %}
|
||||
ALTER EVENT TRIGGER {{ conn|qtIdent(data.name) }}
|
||||
@@ -15,10 +15,10 @@ ALTER EVENT TRIGGER {{ conn|qtIdent(data.name) }}
|
||||
COMMENT ON EVENT TRIGGER {{ conn|qtIdent(data.name) }}
|
||||
IS {{ data.comment|qtLiteral }};
|
||||
{% endif %}
|
||||
{% if data.providers and data.providers|length > 0 %}
|
||||
{% if data.seclabels and data.seclabels|length > 0 %}
|
||||
|
||||
{% for r in data.providers %}
|
||||
{{ SECLABLE.APPLY(conn, 'EVENT TRIGGER', data.name, r.provider, r.securitylabel) }}
|
||||
{% for r in data.seclabels %}
|
||||
{{ SECLABEL.APPLY(conn, 'EVENT TRIGGER', data.name, r.provider, r.label) }}
|
||||
{% endfor %}{% endif %}
|
||||
|
||||
ALTER EVENT TRIGGER {{ conn|qtIdent(data.name) }}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
SELECT e.oid, e.evtname AS name
|
||||
FROM pg_event_trigger e
|
||||
{% if etid %}
|
||||
WHERE e.oid={{etid}}::int
|
||||
{% endif %}
|
||||
ORDER BY e.evtname
|
||||
@@ -4,7 +4,7 @@ e.evtenabled AS enabled,
|
||||
e.evtfoid AS eventfuncoid, quote_ident(n.nspname) || '.' || e.evtfoid::regproc AS eventfunname,
|
||||
array_to_string(array(select quote_literal(x) from unnest(evttags) as t(x)), ', ') AS when,
|
||||
pg_catalog.obj_description(e.oid, 'pg_event_trigger') AS comment,
|
||||
(SELECT array_agg(provider || '=' || label) FROM pg_shseclabel sl1 WHERE sl1.objoid=e.oid) AS seclabels,
|
||||
(SELECT array_agg(provider || '=' || label) FROM pg_seclabel sl1 WHERE sl1.objoid=e.oid) AS seclabels,
|
||||
p.prosrc AS source, p.pronamespace AS schemaoid, l.lanname AS language
|
||||
FROM pg_event_trigger e
|
||||
LEFT OUTER JOIN pg_proc p ON p.oid=e.evtfoid
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{% import 'macros/security.macros' as SECLABLE %}
|
||||
{% import 'macros/security.macros' as SECLABEL %}
|
||||
{% if data %}
|
||||
{% if (data.eventfunname and data.eventfunname != o_data.eventfunname) or
|
||||
(data.eventname and data.eventname != o_data.eventname) or
|
||||
@@ -37,25 +37,25 @@ ALTER EVENT TRIGGER {{ conn|qtIdent(data.name) }}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if data.providers and
|
||||
data.providers|length > 0
|
||||
%}{% set seclabels = data.providers %}
|
||||
{% if data.seclabels and
|
||||
data.seclabels|length > 0
|
||||
%}{% set seclabels = data.seclabels %}
|
||||
{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %}
|
||||
|
||||
{% for r in seclabels.deleted %}
|
||||
{{ SECLABLE.DROP(conn, 'EVENT TRIGGER', data.name, r.provider) }}
|
||||
{{ SECLABEL.DROP(conn, 'EVENT TRIGGER', data.name, r.provider) }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if 'added' in seclabels and seclabels.added|length > 0 %}
|
||||
|
||||
{% for r in seclabels.added %}
|
||||
{{ SECLABLE.APPLY(conn, 'EVENT TRIGGER', data.name, r.provider, r.securitylabel) }}
|
||||
{{ SECLABEL.APPLY(conn, 'EVENT TRIGGER', data.name, r.provider, r.label) }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if 'changed' in seclabels and seclabels.changed|length > 0 %}
|
||||
|
||||
{% for r in seclabels.changed %}
|
||||
{{ SECLABLE.APPLY(conn, 'EVENT TRIGGER', data.name, r.provider, r.securitylabel) }}
|
||||
{{ SECLABEL.APPLY(conn, 'EVENT TRIGGER', data.name, r.provider, r.label) }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
# #################################################################
|
||||
#
|
||||
# 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 EventTriggerGeneratorTestCase(BaseTestGenerator):
|
||||
|
||||
def runTest(self):
|
||||
return []
|
||||
@@ -0,0 +1,79 @@
|
||||
# #################################################################
|
||||
#
|
||||
# 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
|
||||
from regression import test_utils as utils
|
||||
from pgadmin.browser.server_groups.servers.databases.tests import \
|
||||
utils as database_utils
|
||||
from pgadmin.browser.server_groups.servers.tests import utils as server_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.schemas.tests import \
|
||||
utils as schema_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.schemas.functions.tests \
|
||||
import utils as func_utils
|
||||
from . import utils as event_trigger_utils
|
||||
|
||||
|
||||
class EventTriggerAddTestCase(BaseTestGenerator):
|
||||
""" This class will add new event trigger under schema node. """
|
||||
|
||||
scenarios = [
|
||||
# Fetching default URL for event trigger node.
|
||||
('Fetch Event Trigger Node URL',
|
||||
dict(url='/browser/event_trigger/obj/'))
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
"""
|
||||
This function perform the following tasks:
|
||||
1. Add and connect to the test server(s)
|
||||
2. Add database(s) connected to server(s)
|
||||
3. Add schemas to connected database(s)
|
||||
4. Add trigger function(s) to schema(s)
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
# Add the server
|
||||
server_utils.add_server(cls.tester)
|
||||
|
||||
# Connect to servers
|
||||
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 databases to connected servers
|
||||
database_utils.add_database(cls.tester, cls.server_connect_response,
|
||||
cls.server_ids)
|
||||
|
||||
schema_utils.add_schemas(cls.tester)
|
||||
|
||||
func_utils.add_trigger_function(cls.tester, cls.server_connect_response,
|
||||
cls.server_ids)
|
||||
|
||||
def runTest(self):
|
||||
""" This function will add event trigger under database node. """
|
||||
event_trigger_utils.add_event_trigger(self.tester)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
"""This function deletes the added schema, database, server and parent
|
||||
id file
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
event_trigger_utils.delete_event_trigger(cls.tester)
|
||||
func_utils.delete_trigger_function(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()
|
||||
@@ -0,0 +1,88 @@
|
||||
# #################################################################
|
||||
#
|
||||
# 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
|
||||
from regression import test_utils as utils
|
||||
from pgadmin.browser.server_groups.servers.databases.tests import \
|
||||
utils as database_utils
|
||||
from pgadmin.browser.server_groups.servers.tests import utils as server_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.schemas.tests import \
|
||||
utils as schema_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.schemas.functions.tests \
|
||||
import utils as func_utils
|
||||
from . import utils as event_trigger_utils
|
||||
import json
|
||||
|
||||
|
||||
class EventTriggerDeleteTestCase(BaseTestGenerator):
|
||||
""" This class will fetch added event trigger under database node. """
|
||||
|
||||
scenarios = [
|
||||
# Fetching default URL for event trigger node.
|
||||
('Fetch Event Trigger Node URL',
|
||||
dict(url='/browser/event_trigger/obj/'))
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
"""
|
||||
This function perform the following tasks:
|
||||
1. Add and connect to the test server(s)
|
||||
2. Add database(s) connected to server(s)
|
||||
3. Add schemas to connected database(s)
|
||||
4. Add trigger function(s) to schema(s)
|
||||
5. Add event trigger(s) to database(s)
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
# Add the server
|
||||
server_utils.add_server(cls.tester)
|
||||
|
||||
# Connect to servers
|
||||
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 databases to connected servers
|
||||
database_utils.add_database(cls.tester, cls.server_connect_response,
|
||||
cls.server_ids)
|
||||
|
||||
schema_utils.add_schemas(cls.tester)
|
||||
|
||||
func_utils.add_trigger_function(cls.tester, cls.server_connect_response,
|
||||
cls.server_ids)
|
||||
|
||||
event_trigger_utils.add_event_trigger(cls.tester)
|
||||
|
||||
def runTest(self):
|
||||
""" This function will delete event trigger under database node. """
|
||||
|
||||
del_response = event_trigger_utils.delete_event_trigger(self.tester)
|
||||
|
||||
del_respdata = json.loads(del_response.data.decode("utf-8"))
|
||||
|
||||
self.assertTrue(del_respdata['success'], 1)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
"""
|
||||
This function delete the added schema, database, server and parent
|
||||
id file
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
func_utils.delete_trigger_function(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()
|
||||
@@ -0,0 +1,97 @@
|
||||
# #################################################################
|
||||
#
|
||||
# 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
|
||||
from regression import test_utils as utils
|
||||
from pgadmin.browser.server_groups.servers.databases.tests import \
|
||||
utils as database_utils
|
||||
from pgadmin.browser.server_groups.servers.tests import utils as server_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.schemas.tests import\
|
||||
utils as schema_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.schemas.functions.tests \
|
||||
import utils as func_utils
|
||||
from . import utils as event_trigger_utils
|
||||
|
||||
|
||||
class EventTriggerGetTestCase(BaseTestGenerator):
|
||||
""" This class will fetch added event trigger under schema node. """
|
||||
|
||||
scenarios = [
|
||||
# Fetching default URL for event trigger node.
|
||||
('Fetch Event Trigger Node URL',
|
||||
dict(url='/browser/event_trigger/obj/'))
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
"""
|
||||
This function perform the following tasks:
|
||||
1. Add and connect to the test server(s)
|
||||
2. Add database(s) connected to server(s)
|
||||
3. Add schemas to connected database(s)
|
||||
4. Add trigger function(s) to schema(s)
|
||||
5. Add event trigger(s) to database(s)
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
# Add the server
|
||||
server_utils.add_server(cls.tester)
|
||||
|
||||
# Connect to servers
|
||||
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 databases to connected servers
|
||||
database_utils.add_database(cls.tester, cls.server_connect_response,
|
||||
cls.server_ids)
|
||||
|
||||
schema_utils.add_schemas(cls.tester)
|
||||
|
||||
func_utils.add_trigger_function(cls.tester, cls.server_connect_response,
|
||||
cls.server_ids)
|
||||
|
||||
event_trigger_utils.add_event_trigger(cls.tester)
|
||||
|
||||
def runTest(self):
|
||||
""" This function will fetch event trigger under database node. """
|
||||
|
||||
all_id = utils.get_ids()
|
||||
server_ids = all_id["sid"]
|
||||
db_ids_dict = all_id["did"][0]
|
||||
event_trigger_ids_dict = all_id["etid"][0]
|
||||
|
||||
for server_id in server_ids:
|
||||
db_id = db_ids_dict[int(server_id)]
|
||||
event_trigger_id = event_trigger_ids_dict[server_id]
|
||||
|
||||
response = event_trigger_utils.verify_event_trigger(
|
||||
self.tester, utils.SERVER_GROUP, server_id, db_id,
|
||||
event_trigger_id)
|
||||
|
||||
self.assertEquals(response.status_code, 200)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
"""This function deletes the added schema, database, server and parent
|
||||
id file
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
event_trigger_utils.delete_event_trigger(cls.tester)
|
||||
func_utils.delete_trigger_function(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()
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
# #################################################################
|
||||
#
|
||||
# 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
|
||||
from regression import test_utils as utils
|
||||
from pgadmin.browser.server_groups.servers.databases.tests import \
|
||||
utils as database_utils
|
||||
from pgadmin.browser.server_groups.servers.tests import utils as server_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.schemas.tests import \
|
||||
utils as schema_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.schemas.functions.tests \
|
||||
import utils as func_utils
|
||||
from . import utils as event_trigger_utils
|
||||
import json
|
||||
from regression.test_setup import advanced_config_data
|
||||
|
||||
|
||||
class EventTriggerPutTestCase(BaseTestGenerator):
|
||||
""" This class will fetch added event trigger under database node. """
|
||||
|
||||
scenarios = [
|
||||
# Fetching default URL for event trigger node.
|
||||
('Fetch Event Trigger Node URL',
|
||||
dict(url='/browser/event_trigger/obj/'))
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
"""
|
||||
This function perform the following tasks:
|
||||
1. Add and connect to the test server(s)
|
||||
2. Add database(s) connected to server(s)
|
||||
3. Add schemas to connected database(s)
|
||||
4. Add trigger function(s) to schema(s)
|
||||
5. Add event trigger(s) to database(s)
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
# Add the server
|
||||
server_utils.add_server(cls.tester)
|
||||
|
||||
# Connect to servers
|
||||
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 databases to connected servers
|
||||
database_utils.add_database(cls.tester, cls.server_connect_response,
|
||||
cls.server_ids)
|
||||
|
||||
schema_utils.add_schemas(cls.tester)
|
||||
|
||||
func_utils.add_trigger_function(cls.tester, cls.server_connect_response,
|
||||
cls.server_ids)
|
||||
|
||||
event_trigger_utils.add_event_trigger(cls.tester)
|
||||
|
||||
def runTest(self):
|
||||
""" This function will update event trigger under database node. """
|
||||
|
||||
all_id = utils.get_ids()
|
||||
server_ids = all_id["sid"]
|
||||
db_ids_dict = all_id["did"][0]
|
||||
event_trigger_ids_dict = all_id["etid"][0]
|
||||
|
||||
for server_id in server_ids:
|
||||
db_id = db_ids_dict[int(server_id)]
|
||||
event_trigger_id = event_trigger_ids_dict[server_id]
|
||||
|
||||
response = event_trigger_utils.verify_event_trigger(
|
||||
self.tester, utils.SERVER_GROUP, server_id, db_id,
|
||||
event_trigger_id)
|
||||
|
||||
if response.status_code == 200:
|
||||
data = \
|
||||
{
|
||||
"comment":
|
||||
advanced_config_data['event_trigger_update_data']
|
||||
['comment'],
|
||||
"id": event_trigger_id
|
||||
}
|
||||
|
||||
put_response = self.tester.put(
|
||||
self.url + str(utils.SERVER_GROUP) + '/' +
|
||||
str(server_id) + '/' + str(db_id) +
|
||||
'/' + str(event_trigger_id),
|
||||
data=json.dumps(data),
|
||||
follow_redirects=True)
|
||||
|
||||
self.assertEquals(put_response.status_code, 200)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
"""This function deletes the added schema, database, server and parent
|
||||
id file
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
event_trigger_utils.delete_event_trigger(cls.tester)
|
||||
func_utils.delete_trigger_function(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()
|
||||
@@ -0,0 +1,148 @@
|
||||
# #################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2016, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
# ##################################################################
|
||||
|
||||
import os
|
||||
import pickle
|
||||
import json
|
||||
import uuid
|
||||
from regression.test_setup import advanced_config_data, pickle_path
|
||||
from regression import test_utils as utils
|
||||
from pgadmin.browser.server_groups.servers.databases.tests import \
|
||||
utils as database_utils
|
||||
from pgadmin.browser.server_groups.servers.tests import utils as server_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.schemas.functions.tests \
|
||||
import utils as func_utils
|
||||
|
||||
EVENT_TRIGGER_URL = '/browser/event_trigger/obj/'
|
||||
|
||||
|
||||
def get_event_trigger_config_data(schema_name, server_connect_data,
|
||||
trigger_func_name):
|
||||
adv_config_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['event_trigger_credentials']:
|
||||
if db_user == config_test_data['owner']:
|
||||
adv_config_data = config_test_data
|
||||
|
||||
data = {
|
||||
"enabled": adv_config_data['enable'],
|
||||
"eventfunname": "{0}.{1}".format(schema_name, trigger_func_name),
|
||||
"eventname": adv_config_data['event_name'],
|
||||
"eventowner": adv_config_data['owner'],
|
||||
"name": "event_trigger_{}".format(str(uuid.uuid4())[1:4]),
|
||||
"providers": adv_config_data['provider']
|
||||
}
|
||||
return data
|
||||
|
||||
|
||||
def add_event_trigger(tester):
|
||||
all_id = utils.get_ids()
|
||||
server_ids = all_id["sid"]
|
||||
db_ids_dict = all_id["did"][0]
|
||||
schema_info_dict = all_id["scid"][0]
|
||||
trigger_func_info_dict = all_id["tfnid"][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"]:
|
||||
server_connect_response = server_utils.verify_server(
|
||||
tester, utils.SERVER_GROUP, server_id)
|
||||
|
||||
schema_info = schema_info_dict[int(server_id)]
|
||||
trigger_func_list = trigger_func_info_dict[int(server_id)]
|
||||
trigger_func_info = \
|
||||
filter(lambda x: x[2] == "event_trigger", trigger_func_list)[0]
|
||||
trigger_func_name = trigger_func_info[1].replace("()", "")
|
||||
trigger_func_id = trigger_func_info[0]
|
||||
|
||||
trigger_func_response = \
|
||||
func_utils.verify_trigger_function(tester, server_id,
|
||||
db_id, schema_info[0],
|
||||
trigger_func_id)
|
||||
if trigger_func_response.status_code == 200:
|
||||
data = get_event_trigger_config_data(
|
||||
schema_info[1],
|
||||
server_connect_response, trigger_func_name)
|
||||
|
||||
response = tester.post(
|
||||
EVENT_TRIGGER_URL + str(utils.SERVER_GROUP) + '/' +
|
||||
str(server_id) + '/' + str(db_id) +
|
||||
'/', data=json.dumps(data),
|
||||
content_type='html/json')
|
||||
assert response.status_code == 200
|
||||
response_data = json.loads(response.data.decode('utf-8'))
|
||||
write_event_trigger_info(response_data, server_id)
|
||||
|
||||
|
||||
def write_event_trigger_info(response_data, server_id):
|
||||
"""
|
||||
This function writes the schema id into parent_id.pkl
|
||||
|
||||
:param response_data: extension add response data
|
||||
:type response_data: dict
|
||||
:param server_id: server id
|
||||
:type server_id: str
|
||||
:return: None
|
||||
"""
|
||||
|
||||
event_trigger_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 'etid' in pickle_id_dict:
|
||||
if pickle_id_dict['etid']:
|
||||
# Add the event_trigger_id as value in dict
|
||||
pickle_id_dict["etid"][0].update({server_id: event_trigger_id})
|
||||
else:
|
||||
# Create new dict with server_id and event_trigger_id
|
||||
pickle_id_dict["etid"].append({server_id: event_trigger_id})
|
||||
event_trigger_output = open(pickle_path, 'wb')
|
||||
pickle.dump(pickle_id_dict, event_trigger_output)
|
||||
event_trigger_output.close()
|
||||
|
||||
|
||||
def verify_event_trigger(tester, server_group, server_id, db_id,
|
||||
event_trigger_id):
|
||||
response = tester.get(EVENT_TRIGGER_URL + str(server_group) + '/' +
|
||||
str(server_id) + '/' + str(db_id) +
|
||||
'/' + str(event_trigger_id),
|
||||
content_type='html/json')
|
||||
return response
|
||||
|
||||
|
||||
def delete_event_trigger(tester):
|
||||
all_id = utils.get_ids()
|
||||
server_ids = all_id["sid"]
|
||||
db_ids_dict = all_id["did"][0]
|
||||
event_trigger_ids_dict = all_id["etid"][0]
|
||||
|
||||
for server_id in server_ids:
|
||||
db_id = db_ids_dict[int(server_id)]
|
||||
event_trigger_id = event_trigger_ids_dict[server_id]
|
||||
|
||||
response = verify_event_trigger(tester,
|
||||
utils.SERVER_GROUP,
|
||||
server_id,
|
||||
db_id,
|
||||
event_trigger_id)
|
||||
if response.status_code == 200:
|
||||
del_response = tester.delete(
|
||||
EVENT_TRIGGER_URL + str(utils.SERVER_GROUP) + '/' +
|
||||
str(server_id) + '/' +
|
||||
str(db_id) + '/' +
|
||||
str(event_trigger_id),
|
||||
follow_redirects=True)
|
||||
|
||||
return del_response
|
||||
@@ -9,17 +9,18 @@
|
||||
|
||||
""" Implements Extension Node """
|
||||
|
||||
import json
|
||||
import simplejson as json
|
||||
from functools import wraps
|
||||
|
||||
import pgadmin.browser.server_groups.servers.databases as databases
|
||||
from flask import render_template, make_response, request, jsonify
|
||||
from flask.ext.babel import gettext
|
||||
from flask_babel import gettext
|
||||
from pgadmin.browser.collection import CollectionNodeModule
|
||||
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.driver import get_driver
|
||||
from pgadmin.utils.ajax import gone
|
||||
|
||||
from config import PG_DEFAULT_DRIVER
|
||||
|
||||
@@ -191,6 +192,30 @@ class ExtensionView(PGChildNodeView):
|
||||
status=200
|
||||
)
|
||||
|
||||
@check_precondition
|
||||
def node(self, gid, sid, did, eid):
|
||||
"""
|
||||
This function will fetch the properties of extension
|
||||
"""
|
||||
SQL = render_template("/".join([self.template_path, 'properties.sql']),
|
||||
eid=eid)
|
||||
status, rset = self.conn.execute_2darray(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=rset)
|
||||
|
||||
for row in rset['rows']:
|
||||
return make_json_response(
|
||||
data=self.blueprint.generate_browser_node(
|
||||
row['eid'],
|
||||
did,
|
||||
row['name'],
|
||||
'icon-extension'
|
||||
),
|
||||
status=200
|
||||
)
|
||||
|
||||
return gone(gettext("Could not find the specified event trigger."))
|
||||
|
||||
@check_precondition
|
||||
def properties(self, gid, sid, did, eid):
|
||||
"""
|
||||
@@ -202,6 +227,11 @@ class ExtensionView(PGChildNodeView):
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
if len(res['rows']) == 0:
|
||||
return gone(
|
||||
gettext("Couldnot find the extension information.")
|
||||
)
|
||||
|
||||
return ajax_response(
|
||||
response=res['rows'][0],
|
||||
status=200
|
||||
@@ -216,8 +246,10 @@ class ExtensionView(PGChildNodeView):
|
||||
'name'
|
||||
]
|
||||
|
||||
data = request.form if request.form else \
|
||||
json.loads(request.data.decode())
|
||||
data = request.form if request.form else json.loads(
|
||||
request.data, encoding='utf-8'
|
||||
)
|
||||
|
||||
for arg in required_args:
|
||||
if arg not in data:
|
||||
return make_json_response(
|
||||
@@ -263,37 +295,25 @@ class ExtensionView(PGChildNodeView):
|
||||
"""
|
||||
This function will update an extension object
|
||||
"""
|
||||
data = request.form if request.form else \
|
||||
json.loads(request.data.decode())
|
||||
SQL = self.getSQL(gid, sid, data, did, eid)
|
||||
data = request.form if request.form else json.loads(
|
||||
request.data, encoding='utf-8'
|
||||
)
|
||||
|
||||
try:
|
||||
if SQL and isinstance(SQL, basestring) and \
|
||||
SQL.strip('\n') and SQL.strip(' '):
|
||||
status, res = self.conn.execute_dict(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
SQL, name = self.getSQL(gid, sid, data, did, eid)
|
||||
SQL = SQL.strip('\n').strip(' ')
|
||||
status, res = self.conn.execute_dict(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
return make_json_response(
|
||||
success=1,
|
||||
info="Extension updated",
|
||||
data={
|
||||
'id': eid,
|
||||
'sid': sid,
|
||||
'gid': gid
|
||||
}
|
||||
return jsonify(
|
||||
node=self.blueprint.generate_browser_node(
|
||||
eid,
|
||||
did,
|
||||
name,
|
||||
icon="icon-%s" % self.node_type
|
||||
)
|
||||
else:
|
||||
return make_json_response(
|
||||
success=1,
|
||||
info="Nothing to update",
|
||||
data={
|
||||
'id': did,
|
||||
'sid': sid,
|
||||
'gid': gid
|
||||
}
|
||||
)
|
||||
|
||||
)
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@@ -310,6 +330,19 @@ class ExtensionView(PGChildNodeView):
|
||||
status, name = self.conn.execute_scalar(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=name)
|
||||
|
||||
if name is None:
|
||||
return make_json_response(
|
||||
status=410,
|
||||
success=0,
|
||||
errormsg=gettext(
|
||||
'Error: Object not found.'
|
||||
),
|
||||
info=gettext(
|
||||
'The specified extension could not be found.\n'
|
||||
)
|
||||
)
|
||||
|
||||
# drop extension
|
||||
SQL = render_template("/".join(
|
||||
[self.template_path, 'delete.sql']
|
||||
@@ -337,18 +370,18 @@ class ExtensionView(PGChildNodeView):
|
||||
This function returns modified SQL
|
||||
"""
|
||||
data = request.args.copy()
|
||||
SQL = self.getSQL(gid, sid, data, did, eid)
|
||||
if SQL and isinstance(SQL, basestring) and SQL.strip('\n') \
|
||||
and SQL.strip(' '):
|
||||
try:
|
||||
SQL, name = self.getSQL(gid, sid, data, did, eid)
|
||||
SQL = SQL.strip('\n').strip(' ')
|
||||
if SQL == '':
|
||||
SQL = "--modified SQL"
|
||||
|
||||
return make_json_response(
|
||||
data=SQL,
|
||||
status=200
|
||||
)
|
||||
else:
|
||||
return make_json_response(
|
||||
data=gettext('-- Modified SQL --'),
|
||||
status=200
|
||||
)
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
def getSQL(self, gid, sid, data, did, eid=None):
|
||||
"""
|
||||
@@ -357,28 +390,33 @@ class ExtensionView(PGChildNodeView):
|
||||
required_args = [
|
||||
'name'
|
||||
]
|
||||
try:
|
||||
if eid is not None:
|
||||
SQL = render_template("/".join(
|
||||
[self.template_path, 'properties.sql']
|
||||
), eid=eid)
|
||||
status, res = self.conn.execute_dict(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
old_data = res['rows'][0]
|
||||
for arg in required_args:
|
||||
if arg not in data:
|
||||
data[arg] = old_data[arg]
|
||||
SQL = render_template("/".join(
|
||||
[self.template_path, 'update.sql']
|
||||
), data=data, o_data=old_data)
|
||||
else:
|
||||
SQL = render_template("/".join(
|
||||
[self.template_path, 'create.sql']
|
||||
), data=data)
|
||||
return SQL
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
if eid is not None:
|
||||
SQL = render_template("/".join(
|
||||
[self.template_path, 'properties.sql']
|
||||
), eid=eid)
|
||||
status, res = self.conn.execute_dict(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
if len(res['rows']) == 0:
|
||||
return gone(
|
||||
gettext("Couldnot find the extension information.")
|
||||
)
|
||||
|
||||
old_data = res['rows'][0]
|
||||
for arg in required_args:
|
||||
if arg not in data:
|
||||
data[arg] = old_data[arg]
|
||||
SQL = render_template("/".join(
|
||||
[self.template_path, 'update.sql']
|
||||
), data=data, o_data=old_data)
|
||||
return SQL, data['name'] if 'name' in data else old_data['name']
|
||||
else:
|
||||
SQL = render_template("/".join(
|
||||
[self.template_path, 'create.sql']
|
||||
), data=data)
|
||||
return SQL, data['name']
|
||||
|
||||
@check_precondition
|
||||
def avails(self, gid, sid, did):
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
# #################################################################
|
||||
#
|
||||
# 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 ExtensionGeneratorTestCase(BaseTestGenerator):
|
||||
|
||||
def runTest(self):
|
||||
return []
|
||||
@@ -0,0 +1,72 @@
|
||||
# #################################################################
|
||||
#
|
||||
# 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
|
||||
from regression import test_utils as utils
|
||||
from . import utils as extension_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.tests import \
|
||||
utils as database_utils
|
||||
from pgadmin.browser.server_groups.servers.tests import utils as server_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.schemas.tests import\
|
||||
utils as schema_utils
|
||||
|
||||
|
||||
|
||||
class ExtensionsAddTestCase(BaseTestGenerator):
|
||||
|
||||
scenarios = [
|
||||
# Fetching default URL for extension node.
|
||||
('Check Extension Node', dict(url='/browser/extension/obj/'))
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
"""
|
||||
This function perform the following tasks:
|
||||
1. Add and connect to the test server(s)
|
||||
2. Add database(s) connected to server(s)
|
||||
3. Add schemas to connected database(s)
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
# Add the server
|
||||
server_utils.add_server(cls.tester)
|
||||
|
||||
# Connect to servers
|
||||
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 databases to connected servers
|
||||
database_utils.add_database(cls.tester, cls.server_connect_response,
|
||||
cls.server_ids)
|
||||
|
||||
schema_utils.add_schemas(cls.tester)
|
||||
|
||||
def runTest(self):
|
||||
""" This function will add extension under 1st server of tree node. """
|
||||
|
||||
extension_utils.add_extensions(self.tester)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
"""This function deletes the added schema, database, server and parent
|
||||
id file
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
extension_utils.delete_extension(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()
|
||||
@@ -0,0 +1,74 @@
|
||||
# #################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2016, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
# ##################################################################
|
||||
|
||||
import json
|
||||
from pgadmin.utils.route import BaseTestGenerator
|
||||
from regression import test_utils as utils
|
||||
from pgadmin.browser.server_groups.servers.databases.schemas.tests import \
|
||||
utils as schema_utils
|
||||
from . import utils as extension_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.tests import \
|
||||
utils as database_utils
|
||||
from pgadmin.browser.server_groups.servers.tests import utils as server_utils
|
||||
|
||||
|
||||
class ExtensionsDeleteTestCase(BaseTestGenerator):
|
||||
|
||||
scenarios = [
|
||||
# Fetching default URL for extension node.
|
||||
('Check Extension Node', dict(url='/browser/extension/obj/'))
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
"""
|
||||
This function perform the following tasks:
|
||||
1. Add and connect to the test server(s)
|
||||
2. Add database(s) connected to server(s)
|
||||
3. Add schemas to connected database(s)
|
||||
4. Add extension(s) to schema(s)
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
# Add the server
|
||||
server_utils.add_server(cls.tester)
|
||||
|
||||
# Connect to servers
|
||||
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 databases to connected servers
|
||||
database_utils.add_database(cls.tester, cls.server_connect_response,
|
||||
cls.server_ids)
|
||||
|
||||
# Add schema(s) to database(s)
|
||||
schema_utils.add_schemas(cls.tester)
|
||||
|
||||
# Add extension(s) to schema(s)
|
||||
extension_utils.add_extensions(cls.tester)
|
||||
|
||||
def runTest(self):
|
||||
""" This function will add extension under 1st server of tree node. """
|
||||
|
||||
delete_respdata = extension_utils.delete_extension(self.tester)
|
||||
self.assertTrue(delete_respdata['success'], 1)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
"""This function deletes the added schema, database, server and parent
|
||||
id file
|
||||
"""
|
||||
|
||||
database_utils.delete_database(cls.tester)
|
||||
server_utils.delete_server(cls.tester)
|
||||
utils.delete_parent_id_file()
|
||||
@@ -0,0 +1,87 @@
|
||||
#################################################################
|
||||
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2016, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
|
||||
##################################################################
|
||||
|
||||
import json
|
||||
from pgadmin.utils.route import BaseTestGenerator
|
||||
from regression import test_utils as utils
|
||||
from pgadmin.browser.server_groups.servers.databases.schemas.tests import \
|
||||
utils as schema_utils
|
||||
from . import utils as extension_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.tests import \
|
||||
utils as database_utils
|
||||
from pgadmin.browser.server_groups.servers.tests import utils as server_utils
|
||||
|
||||
|
||||
class ExtensionsGetTestCase(BaseTestGenerator):
|
||||
|
||||
scenarios = [
|
||||
# Fetching default URL for extension node.
|
||||
('Check Extension Node', dict(url='/browser/extension/obj/'))
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
"""
|
||||
This function perform the following tasks:
|
||||
1. Add and connect to the test server(s)
|
||||
2. Add database(s) connected to server(s)
|
||||
3. Add schemas to connected database(s)
|
||||
4. Add extension(s) to schema(s)
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
# Add the server
|
||||
server_utils.add_server(cls.tester)
|
||||
|
||||
# Connect to servers
|
||||
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 databases to connected servers
|
||||
database_utils.add_database(cls.tester, cls.server_connect_response,
|
||||
cls.server_ids)
|
||||
|
||||
schema_utils.add_schemas(cls.tester)
|
||||
|
||||
extension_utils.add_extensions(cls.tester)
|
||||
|
||||
def runTest(self):
|
||||
""" This function will add extension under 1st server of tree node. """
|
||||
|
||||
all_id = utils.get_ids()
|
||||
server_ids = all_id["sid"]
|
||||
db_ids_dict = all_id["did"][0]
|
||||
extension_ids_dict = all_id["eid"][0]
|
||||
|
||||
for server_id in server_ids:
|
||||
db_id = db_ids_dict[int(server_id)]
|
||||
extension_id = extension_ids_dict[server_id]
|
||||
|
||||
response = extension_utils.verify_extensions(self.tester,
|
||||
utils.SERVER_GROUP,
|
||||
server_id, db_id,
|
||||
extension_id)
|
||||
|
||||
self.assertEquals(response.status_code, 200)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
"""This function deletes the added schema, database, server and parent
|
||||
id file
|
||||
"""
|
||||
|
||||
extension_utils.delete_extension(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()
|
||||
@@ -0,0 +1,104 @@
|
||||
# #################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2016, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
# ##################################################################
|
||||
|
||||
|
||||
import json
|
||||
from pgadmin.utils.route import BaseTestGenerator
|
||||
from regression import test_utils as utils
|
||||
from pgadmin.browser.server_groups.servers.databases.schemas.tests import \
|
||||
utils as schema_utils
|
||||
from . import utils as extension_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.tests import \
|
||||
utils as database_utils
|
||||
from pgadmin.browser.server_groups.servers.tests import utils as server_utils
|
||||
|
||||
|
||||
class ExtensionsPutTestCase(BaseTestGenerator):
|
||||
|
||||
scenarios = [
|
||||
# Fetching default URL for extension node.
|
||||
('Check Extension Node', dict(url='/browser/extension/obj/'))
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
"""
|
||||
This function perform the following tasks:
|
||||
1. Add and connect to the test server(s)
|
||||
2. Add database(s) connected to server(s)
|
||||
3. Add schemas to connected database(s)
|
||||
4. Add extension(s) to schema(s)
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
# Add the server
|
||||
server_utils.add_server(cls.tester)
|
||||
|
||||
# Connect to servers
|
||||
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 databases to connected servers
|
||||
database_utils.add_database(cls.tester, cls.server_connect_response,
|
||||
cls.server_ids)
|
||||
|
||||
schema_utils.add_schemas(cls.tester)
|
||||
|
||||
extension_utils.add_extensions(cls.tester)
|
||||
|
||||
def runTest(self):
|
||||
""" This function will add extension under 1st server of tree node. """
|
||||
|
||||
all_id = utils.get_ids()
|
||||
server_ids = all_id["sid"]
|
||||
db_ids_dict = all_id["did"][0]
|
||||
extension_ids_dict = all_id["eid"][0]
|
||||
schema_info_dict = all_id["scid"][0]
|
||||
|
||||
for server_id in server_ids:
|
||||
db_id = db_ids_dict[int(server_id)]
|
||||
extension_id = extension_ids_dict[server_id]
|
||||
|
||||
response = extension_utils.verify_extensions(self.tester,
|
||||
utils.SERVER_GROUP,
|
||||
server_id, db_id,
|
||||
extension_id)
|
||||
|
||||
if response.status_code == 200:
|
||||
schema_name = schema_info_dict[int(server_id)][1]
|
||||
|
||||
data = \
|
||||
{
|
||||
"id": extension_id,
|
||||
"schema": schema_name
|
||||
}
|
||||
|
||||
put_response = self.tester.put(
|
||||
self.url + str(utils.SERVER_GROUP) + '/' +
|
||||
str(server_id) + '/' + str(db_id) +
|
||||
'/' + str(extension_id),
|
||||
data=json.dumps(data),
|
||||
follow_redirects=True)
|
||||
|
||||
self.assertEquals(put_response.status_code, 200)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
"""This function deletes the added schema, database, server and parent
|
||||
id file
|
||||
"""
|
||||
extension_utils.delete_extension(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()
|
||||
@@ -0,0 +1,134 @@
|
||||
# #################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2016, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
# ##################################################################
|
||||
|
||||
|
||||
import os
|
||||
import pickle
|
||||
import json
|
||||
from regression.test_setup import advanced_config_data, pickle_path
|
||||
from regression import test_utils as utils
|
||||
from pgadmin.browser.server_groups.servers.databases.tests import \
|
||||
utils as database_utils
|
||||
from pgadmin.browser.server_groups.servers.tests import utils as server_utils
|
||||
|
||||
|
||||
EXTENSION_URL = '/browser/extension/obj/'
|
||||
|
||||
|
||||
def get_extension_config_data(schema_name, server_connect_data):
|
||||
adv_config_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['extension_credentials']:
|
||||
if db_user == config_test_data['owner']:
|
||||
adv_config_data = config_test_data
|
||||
|
||||
data = {
|
||||
"name": adv_config_data['name'],
|
||||
"relocatable": adv_config_data['relocate'],
|
||||
"schema": schema_name,
|
||||
"version": adv_config_data['version']
|
||||
}
|
||||
return data
|
||||
|
||||
|
||||
def write_extension_info(response_data, server_id):
|
||||
"""
|
||||
This function writes the schema id into parent_id.pkl
|
||||
|
||||
:param response_data: extension add response data
|
||||
:type response_data: dict
|
||||
:param server_id: server id
|
||||
:type server_id: str
|
||||
:return: None
|
||||
"""
|
||||
|
||||
extension_id = response_data['node']['_id']
|
||||
# schema_name = str(response_data['node']['label'])
|
||||
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 'eid' in pickle_id_dict:
|
||||
if pickle_id_dict['eid']:
|
||||
# Add the extension_id as value in dict
|
||||
pickle_id_dict["eid"][0].update({server_id: extension_id})
|
||||
else:
|
||||
# Create new dict with server_id and extension_id
|
||||
pickle_id_dict["eid"].append({server_id: extension_id})
|
||||
extension_output = open(pickle_path, 'wb')
|
||||
pickle.dump(pickle_id_dict, extension_output)
|
||||
extension_output.close()
|
||||
|
||||
|
||||
def add_extensions(tester):
|
||||
|
||||
all_id = utils.get_ids()
|
||||
server_ids = all_id["sid"]
|
||||
db_ids_dict = all_id["did"][0]
|
||||
schema_info_dict = all_id["scid"][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"]:
|
||||
server_connect_response = server_utils.verify_server(
|
||||
tester, utils.SERVER_GROUP, server_id)
|
||||
|
||||
schema_name = schema_info_dict[int(server_id)][1]
|
||||
data = get_extension_config_data(schema_name,
|
||||
server_connect_response)
|
||||
|
||||
response = tester.post(
|
||||
EXTENSION_URL + str(utils.SERVER_GROUP) + '/' +
|
||||
str(server_id) + '/' + str(
|
||||
db_id) + '/',
|
||||
data=json.dumps(data),
|
||||
content_type='html/json')
|
||||
assert response.status_code == 200
|
||||
response_data = json.loads(response.data.decode('utf-8'))
|
||||
write_extension_info(response_data, server_id)
|
||||
|
||||
|
||||
def verify_extensions(tester, server_group, server_id, db_id, extension_id):
|
||||
|
||||
response = tester.get(EXTENSION_URL + str(server_group) + '/' +
|
||||
str(server_id) + '/' + str(db_id) +
|
||||
'/' + str(extension_id),
|
||||
content_type='html/json')
|
||||
return response
|
||||
|
||||
|
||||
def delete_extension(tester):
|
||||
|
||||
all_id = utils.get_ids()
|
||||
server_ids = all_id["sid"]
|
||||
db_ids_dict = all_id["did"][0]
|
||||
extension_ids_dict = all_id["eid"][0]
|
||||
|
||||
for server_id in server_ids:
|
||||
db_id = db_ids_dict[int(server_id)]
|
||||
extension_id = extension_ids_dict[server_id]
|
||||
|
||||
response = verify_extensions(tester,
|
||||
utils.SERVER_GROUP,
|
||||
server_id, db_id,
|
||||
extension_id)
|
||||
if response.status_code == 200:
|
||||
delete_response = tester.delete(
|
||||
EXTENSION_URL + str(utils.SERVER_GROUP) + '/' +
|
||||
str(server_id) + '/' +
|
||||
str(db_id) + '/' +
|
||||
str(extension_id),
|
||||
follow_redirects=True)
|
||||
delete_respdata = json.loads(delete_response.data.decode())
|
||||
return delete_respdata
|
||||
@@ -9,20 +9,20 @@
|
||||
|
||||
"""Implements Foreign Data Wrapper Node"""
|
||||
|
||||
import json
|
||||
import simplejson as json
|
||||
from functools import wraps
|
||||
|
||||
import pgadmin.browser.server_groups.servers as servers
|
||||
from flask import render_template, make_response, request, jsonify
|
||||
from flask.ext.babel import gettext
|
||||
from flask_babel import gettext
|
||||
from pgadmin.browser.collection import CollectionNodeModule
|
||||
from pgadmin.browser.server_groups.servers.utils import parse_priv_from_db, \
|
||||
parse_priv_to_db
|
||||
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.ajax import make_json_response, internal_server_error, \
|
||||
make_response as ajax_response
|
||||
from pgadmin.utils.driver import get_driver
|
||||
from pgadmin.utils.ajax import gone
|
||||
|
||||
from config import PG_DEFAULT_DRIVER
|
||||
|
||||
@@ -211,17 +211,8 @@ class ForeignDataWrapperView(PGChildNodeView):
|
||||
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!"
|
||||
)
|
||||
)
|
||||
|
||||
ver = self.manager.version
|
||||
# we will set template path for sql scripts
|
||||
if ver >= 90300:
|
||||
# Set the template path for the SQL scripts
|
||||
if self.manager.version >= 90300:
|
||||
self.template_path = 'foreign_data_wrappers/sql/9.3_plus'
|
||||
else:
|
||||
self.template_path = 'foreign_data_wrappers/sql/9.1_plus'
|
||||
@@ -286,6 +277,36 @@ class ForeignDataWrapperView(PGChildNodeView):
|
||||
status=200
|
||||
)
|
||||
|
||||
@check_precondition
|
||||
def node(self, gid, sid, did, fid):
|
||||
"""
|
||||
This function will fetch properties of foreign data wrapper node.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
fid: Foreign data wrapper ID
|
||||
"""
|
||||
sql = render_template("/".join([self.template_path, 'properties.sql']),
|
||||
conn=self.conn, fid=fid)
|
||||
status, r_set = self.conn.execute_2darray(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=r_set)
|
||||
|
||||
for row in r_set['rows']:
|
||||
return make_json_response(
|
||||
data=self.blueprint.generate_browser_node(
|
||||
row['fdwoid'],
|
||||
did,
|
||||
row['name'],
|
||||
icon="icon-foreign_data_wrapper"
|
||||
),
|
||||
status=200
|
||||
)
|
||||
|
||||
return gone(gettext("Could not find the specified foreign data wrapper."))
|
||||
|
||||
def tokenize_options(self, option_value):
|
||||
"""
|
||||
This function will tokenize the string stored in database
|
||||
@@ -321,6 +342,11 @@ class ForeignDataWrapperView(PGChildNodeView):
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
if len(res['rows']) == 0:
|
||||
return gone(
|
||||
gettext("Couldnot find the foreign data wrapper information.")
|
||||
)
|
||||
|
||||
if res['rows'][0]['fdwoptions'] is not None:
|
||||
res['rows'][0]['fdwoptions'] = self.tokenize_options(res['rows'][0]['fdwoptions'])
|
||||
|
||||
@@ -355,7 +381,9 @@ class ForeignDataWrapperView(PGChildNodeView):
|
||||
'name'
|
||||
]
|
||||
|
||||
data = request.form if request.form else json.loads(request.data.decode())
|
||||
data = request.form if request.form else json.loads(
|
||||
request.data, encoding='utf-8'
|
||||
)
|
||||
for arg in required_args:
|
||||
if arg not in data:
|
||||
return make_json_response(
|
||||
@@ -422,36 +450,25 @@ class ForeignDataWrapperView(PGChildNodeView):
|
||||
did: Database ID
|
||||
fid: foreign data wrapper ID
|
||||
"""
|
||||
data = request.form if request.form else json.loads(request.data.decode())
|
||||
sql = self.get_sql(gid, sid, data, did, fid)
|
||||
data = request.form if request.form else json.loads(
|
||||
request.data, encoding='utf-8'
|
||||
)
|
||||
|
||||
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)
|
||||
sql, name = self.get_sql(gid, sid, data, did, fid)
|
||||
|
||||
return make_json_response(
|
||||
success=1,
|
||||
info="Foreign Data Wrapper updated",
|
||||
data={
|
||||
'id': fid,
|
||||
'did': did,
|
||||
'sid': sid,
|
||||
'gid': gid
|
||||
}
|
||||
)
|
||||
else:
|
||||
return make_json_response(
|
||||
success=1,
|
||||
info="Nothing to update",
|
||||
data={
|
||||
'id': fid,
|
||||
'did': did,
|
||||
'sid': sid,
|
||||
'gid': gid
|
||||
}
|
||||
)
|
||||
status, res = self.conn.execute_scalar(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
return jsonify(
|
||||
node=self.blueprint.generate_browser_node(
|
||||
fid,
|
||||
did,
|
||||
name,
|
||||
icon="icon-%s" % self.node_type
|
||||
)
|
||||
)
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@@ -478,6 +495,18 @@ class ForeignDataWrapperView(PGChildNodeView):
|
||||
status, name = self.conn.execute_scalar(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=name)
|
||||
|
||||
if name is None:
|
||||
return make_json_response(
|
||||
status=410,
|
||||
success=0,
|
||||
errormsg=gettext(
|
||||
'Error: Object not found.'
|
||||
),
|
||||
info=gettext(
|
||||
'The specified foreign data wrapper could not be found.\n'
|
||||
)
|
||||
)
|
||||
# drop foreign data wrapper node
|
||||
sql = render_template("/".join([self.template_path, 'delete.sql']), name=name, cascade=cascade,
|
||||
conn=self.conn)
|
||||
@@ -513,15 +542,21 @@ class ForeignDataWrapperView(PGChildNodeView):
|
||||
data = {}
|
||||
for k, v in request.args.items():
|
||||
try:
|
||||
data[k] = json.loads(v)
|
||||
data[k] = json.loads(v, encoding='utf-8')
|
||||
except ValueError:
|
||||
data[k] = v
|
||||
try:
|
||||
sql, name = self.get_sql(gid, sid, data, did, fid)
|
||||
|
||||
sql = self.get_sql(gid, sid, data, did, fid)
|
||||
if sql and sql.strip('\n') and sql.strip(' '):
|
||||
return make_json_response(data=sql, status=200)
|
||||
else:
|
||||
return make_json_response(data='-- Modified SQL --', status=200)
|
||||
if sql == '':
|
||||
sql = "--modified SQL"
|
||||
|
||||
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, did, fid=None):
|
||||
"""
|
||||
@@ -537,89 +572,94 @@ class ForeignDataWrapperView(PGChildNodeView):
|
||||
required_args = [
|
||||
'name'
|
||||
]
|
||||
try:
|
||||
if fid is not None:
|
||||
sql = render_template("/".join([self.template_path, 'properties.sql']), fid=fid, conn=self.conn)
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
if res['rows'][0]['fdwoptions'] is not None:
|
||||
res['rows'][0]['fdwoptions'] = self.tokenize_options(res['rows'][0]['fdwoptions'])
|
||||
if fid is not None:
|
||||
sql = render_template("/".join([self.template_path, 'properties.sql']), fid=fid, conn=self.conn)
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
for key in ['fdwacl']:
|
||||
if key in data and data[key] is not None:
|
||||
if 'added' in data[key]:
|
||||
data[key]['added'] = parse_priv_to_db(data[key]['added'], ['U'])
|
||||
if 'changed' in data[key]:
|
||||
data[key]['changed'] = parse_priv_to_db(data[key]['changed'], ['U'])
|
||||
if 'deleted' in data[key]:
|
||||
data[key]['deleted'] = parse_priv_to_db(data[key]['deleted'], ['U'])
|
||||
if len(res['rows']) == 0:
|
||||
return gone(
|
||||
gettext("Couldnot find the foreign data wrapper information.")
|
||||
)
|
||||
|
||||
old_data = res['rows'][0]
|
||||
for arg in required_args:
|
||||
if arg not in data:
|
||||
data[arg] = old_data[arg]
|
||||
if res['rows'][0]['fdwoptions'] is not None:
|
||||
res['rows'][0]['fdwoptions'] = self.tokenize_options(res['rows'][0]['fdwoptions'])
|
||||
|
||||
new_list_add = []
|
||||
new_list_change = []
|
||||
for key in ['fdwacl']:
|
||||
if key in data and data[key] is not None:
|
||||
if 'added' in data[key]:
|
||||
data[key]['added'] = parse_priv_to_db(data[key]['added'], ['U'])
|
||||
if 'changed' in data[key]:
|
||||
data[key]['changed'] = parse_priv_to_db(data[key]['changed'], ['U'])
|
||||
if 'deleted' in data[key]:
|
||||
data[key]['deleted'] = parse_priv_to_db(data[key]['deleted'], ['U'])
|
||||
|
||||
# Allow user to set the blank value in fdwvalue field in option model
|
||||
if 'fdwoptions' in data and 'added' in data['fdwoptions']:
|
||||
for item in data['fdwoptions']['added']:
|
||||
new_dict_add = {}
|
||||
if item['fdwoption']:
|
||||
if 'fdwvalue' in item and item['fdwvalue'] and item['fdwvalue'] != '':
|
||||
new_dict_add.update(item);
|
||||
else:
|
||||
new_dict_add.update({'fdwoption': item['fdwoption'], 'fdwvalue': ''})
|
||||
old_data = res['rows'][0]
|
||||
for arg in required_args:
|
||||
if arg not in data:
|
||||
data[arg] = old_data[arg]
|
||||
|
||||
new_list_add.append(new_dict_add)
|
||||
new_list_add = []
|
||||
new_list_change = []
|
||||
|
||||
data['fdwoptions']['added'] = new_list_add
|
||||
# Allow user to set the blank value in fdwvalue field in option model
|
||||
if 'fdwoptions' in data and 'added' in data['fdwoptions']:
|
||||
for item in data['fdwoptions']['added']:
|
||||
new_dict_add = {}
|
||||
if item['fdwoption']:
|
||||
if 'fdwvalue' in item and item['fdwvalue'] and item['fdwvalue'] != '':
|
||||
new_dict_add.update(item);
|
||||
else:
|
||||
new_dict_add.update({'fdwoption': item['fdwoption'], 'fdwvalue': ''})
|
||||
|
||||
# Allow user to set the blank value in fdwvalue field in option model
|
||||
if 'fdwoptions' in data and 'changed' in data['fdwoptions']:
|
||||
for item in data['fdwoptions']['changed']:
|
||||
new_dict_change = {}
|
||||
if item['fdwoption']:
|
||||
if 'fdwvalue' in item and item['fdwvalue'] and item['fdwvalue'] != '':
|
||||
new_dict_change.update(item);
|
||||
else:
|
||||
new_dict_change.update({'fdwoption': item['fdwoption'], 'fdwvalue': ''})
|
||||
new_list_add.append(new_dict_add)
|
||||
|
||||
new_list_change.append(new_dict_change)
|
||||
data['fdwoptions']['added'] = new_list_add
|
||||
|
||||
data['fdwoptions']['changed'] = new_list_change
|
||||
# Allow user to set the blank value in fdwvalue field in option model
|
||||
if 'fdwoptions' in data and 'changed' in data['fdwoptions']:
|
||||
for item in data['fdwoptions']['changed']:
|
||||
new_dict_change = {}
|
||||
if item['fdwoption']:
|
||||
if 'fdwvalue' in item and item['fdwvalue'] and item['fdwvalue'] != '':
|
||||
new_dict_change.update(item);
|
||||
else:
|
||||
new_dict_change.update({'fdwoption': item['fdwoption'], 'fdwvalue': ''})
|
||||
|
||||
sql = render_template("/".join([self.template_path, 'update.sql']), data=data, o_data=old_data,
|
||||
conn=self.conn)
|
||||
else:
|
||||
for key in ['fdwacl']:
|
||||
if key in data and data[key] is not None:
|
||||
data[key] = parse_priv_to_db(data[key], ['U'])
|
||||
new_list_change.append(new_dict_change)
|
||||
|
||||
new_list = []
|
||||
data['fdwoptions']['changed'] = new_list_change
|
||||
|
||||
# Allow user to set the blank value in fdwvalue field in option model
|
||||
if 'fdwoptions' in data:
|
||||
for item in data['fdwoptions']:
|
||||
new_dict = {}
|
||||
if item['fdwoption']:
|
||||
if 'fdwvalue' in item and item['fdwvalue'] and item['fdwvalue'] != '':
|
||||
new_dict.update(item);
|
||||
else:
|
||||
new_dict.update({'fdwoption': item['fdwoption'], 'fdwvalue': ''})
|
||||
sql = render_template("/".join([self.template_path, 'update.sql']), data=data, o_data=old_data,
|
||||
conn=self.conn)
|
||||
return sql, data['name'] if 'name' in data else old_data['name']
|
||||
else:
|
||||
for key in ['fdwacl']:
|
||||
if key in data and data[key] is not None:
|
||||
data[key] = parse_priv_to_db(data[key], ['U'])
|
||||
|
||||
new_list.append(new_dict)
|
||||
new_list = []
|
||||
|
||||
data['fdwoptions'] = new_list
|
||||
# Allow user to set the blank value in fdwvalue field in option model
|
||||
if 'fdwoptions' in data:
|
||||
for item in data['fdwoptions']:
|
||||
new_dict = {}
|
||||
if item['fdwoption']:
|
||||
if 'fdwvalue' in item and item['fdwvalue'] and item['fdwvalue'] != '':
|
||||
new_dict.update(item);
|
||||
else:
|
||||
new_dict.update({'fdwoption': item['fdwoption'], 'fdwvalue': ''})
|
||||
|
||||
new_list.append(new_dict)
|
||||
|
||||
data['fdwoptions'] = new_list
|
||||
|
||||
sql = render_template("/".join([self.template_path, 'create.sql']), data=data, conn=self.conn)
|
||||
sql += "\n"
|
||||
return sql, data['name']
|
||||
|
||||
sql = render_template("/".join([self.template_path, 'create.sql']), data=data, conn=self.conn)
|
||||
sql += "\n"
|
||||
return sql
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def sql(self, gid, sid, did, fid):
|
||||
@@ -666,6 +706,9 @@ class ForeignDataWrapperView(PGChildNodeView):
|
||||
|
||||
""".format(res['rows'][0]['name'])
|
||||
|
||||
if hasattr(str, 'decode'):
|
||||
sql_header = sql_header.decode('utf-8')
|
||||
|
||||
sql = sql_header + sql
|
||||
|
||||
return ajax_response(response=sql)
|
||||
@@ -689,7 +732,7 @@ class ForeignDataWrapperView(PGChildNodeView):
|
||||
return internal_server_error(errormsg=r_set)
|
||||
|
||||
for row in r_set['rows']:
|
||||
res.append({'label': row['fdwvalue'], 'value': row['fdwvalue']})
|
||||
res.append({'label': row['schema_prefix_fdw_val'], 'value': row['schema_prefix_fdw_val']})
|
||||
|
||||
return make_json_response(data=res, status=200)
|
||||
|
||||
@@ -715,7 +758,7 @@ class ForeignDataWrapperView(PGChildNodeView):
|
||||
return internal_server_error(errormsg=r_set)
|
||||
|
||||
for row in r_set['rows']:
|
||||
res.append({'label': row['fdwhan'], 'value': row['fdwhan']})
|
||||
res.append({'label': row['schema_prefix_fdw_hand'], 'value': row['schema_prefix_fdw_hand']})
|
||||
|
||||
return make_json_response(
|
||||
data=res,
|
||||
|
||||
@@ -9,20 +9,20 @@
|
||||
|
||||
"""Implements Foreign Server Node"""
|
||||
|
||||
import json
|
||||
import simplejson as json
|
||||
from functools import wraps
|
||||
|
||||
import pgadmin.browser.server_groups.servers.databases as databases
|
||||
from flask import render_template, make_response, request, jsonify
|
||||
from flask.ext.babel import gettext
|
||||
from flask_babel import gettext
|
||||
from pgadmin.browser.collection import CollectionNodeModule
|
||||
from pgadmin.browser.server_groups.servers.utils import parse_priv_from_db, \
|
||||
parse_priv_to_db
|
||||
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.ajax import make_json_response, internal_server_error, \
|
||||
make_response as ajax_response
|
||||
from pgadmin.utils.driver import get_driver
|
||||
from pgadmin.utils.ajax import gone
|
||||
|
||||
from config import PG_DEFAULT_DRIVER
|
||||
|
||||
@@ -204,18 +204,8 @@ class ForeignServerView(PGChildNodeView):
|
||||
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!"
|
||||
)
|
||||
)
|
||||
|
||||
ver = self.manager.version
|
||||
# we will set template path for sql scripts
|
||||
if ver >= 90300:
|
||||
# Set the template path for the SQL scripts
|
||||
if self.manager.version >= 90300:
|
||||
self.template_path = 'foreign_servers/sql/9.3_plus'
|
||||
else:
|
||||
self.template_path = 'foreign_servers/sql/9.1_plus'
|
||||
@@ -281,6 +271,41 @@ class ForeignServerView(PGChildNodeView):
|
||||
status=200
|
||||
)
|
||||
|
||||
@check_precondition
|
||||
def node(self, gid, sid, did, fid, fsid):
|
||||
"""
|
||||
This function will fetch properites foreign server node.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
fid: Foreign data wrapper ID
|
||||
fsid: Foreign server ID
|
||||
"""
|
||||
|
||||
res = []
|
||||
sql = render_template("/".join([self.template_path, 'properties.sql']),
|
||||
fsid=fsid, conn=self.conn)
|
||||
status, r_set = self.conn.execute_2darray(sql)
|
||||
|
||||
if not status:
|
||||
return internal_server_error(errormsg=r_set)
|
||||
|
||||
for row in r_set['rows']:
|
||||
|
||||
return make_json_response(
|
||||
data=self.blueprint.generate_browser_node(
|
||||
row['fsrvid'],
|
||||
fid,
|
||||
row['name'],
|
||||
icon="icon-foreign_server"
|
||||
),
|
||||
status=200
|
||||
)
|
||||
|
||||
return gone(gettext("Could not find the specified foreign server."))
|
||||
|
||||
def tokenizeOptions(self, option_value):
|
||||
"""
|
||||
This function will tokenize the string stored in database
|
||||
@@ -319,6 +344,11 @@ class ForeignServerView(PGChildNodeView):
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
if len(res['rows']) == 0:
|
||||
return gone(
|
||||
gettext("Couldnot find the foreign server information.")
|
||||
)
|
||||
|
||||
if res['rows'][0]['fsrvoptions'] is not None:
|
||||
res['rows'][0]['fsrvoptions'] = self.tokenizeOptions(res['rows'][0]['fsrvoptions'])
|
||||
|
||||
@@ -356,7 +386,9 @@ class ForeignServerView(PGChildNodeView):
|
||||
'name'
|
||||
]
|
||||
|
||||
data = request.form if request.form else json.loads(request.data.decode())
|
||||
data = request.form if request.form else json.loads(
|
||||
request.data, encoding='utf-8'
|
||||
)
|
||||
for arg in required_args:
|
||||
if arg not in data:
|
||||
return make_json_response(
|
||||
@@ -428,37 +460,25 @@ class ForeignServerView(PGChildNodeView):
|
||||
fsid: foreign server ID
|
||||
"""
|
||||
|
||||
data = request.form if request.form else json.loads(request.data.decode())
|
||||
sql = self.get_sql(gid, sid, data, did, fid, fsid)
|
||||
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)
|
||||
data = request.form if request.form else json.loads(
|
||||
request.data, encoding='utf-8'
|
||||
)
|
||||
|
||||
return make_json_response(
|
||||
success=1,
|
||||
info="Foreign server updated",
|
||||
data={
|
||||
'id': fsid,
|
||||
'fid': fid,
|
||||
'did': did,
|
||||
'sid': sid,
|
||||
'gid': gid
|
||||
}
|
||||
)
|
||||
else:
|
||||
return make_json_response(
|
||||
success=1,
|
||||
info="Nothing to update",
|
||||
data={
|
||||
'id': fsid,
|
||||
'fid': fid,
|
||||
'did': did,
|
||||
'sid': sid,
|
||||
'gid': gid
|
||||
}
|
||||
try:
|
||||
sql, name = self.get_sql(gid, sid, data, did, fid, fsid)
|
||||
sql = sql.strip('\n').strip(' ')
|
||||
status, res = self.conn.execute_scalar(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
return jsonify(
|
||||
node=self.blueprint.generate_browser_node(
|
||||
fsid,
|
||||
fid,
|
||||
name,
|
||||
icon="icon-%s" % self.node_type
|
||||
)
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
@@ -488,6 +508,19 @@ class ForeignServerView(PGChildNodeView):
|
||||
status, name = self.conn.execute_scalar(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=name)
|
||||
|
||||
if name is None:
|
||||
return make_json_response(
|
||||
status=410,
|
||||
success=0,
|
||||
errormsg=gettext(
|
||||
'Error: Object not found.'
|
||||
),
|
||||
info=gettext(
|
||||
'The specified foreign server could not be found.\n'
|
||||
)
|
||||
)
|
||||
|
||||
# drop foreign server
|
||||
sql = render_template("/".join([self.template_path, 'delete.sql']), name=name, cascade=cascade,
|
||||
conn=self.conn)
|
||||
@@ -526,21 +559,21 @@ class ForeignServerView(PGChildNodeView):
|
||||
data = {}
|
||||
for k, v in request.args.items():
|
||||
try:
|
||||
data[k] = json.loads(v)
|
||||
data[k] = json.loads(v, encoding='utf-8')
|
||||
except ValueError:
|
||||
data[k] = v
|
||||
try:
|
||||
sql, name = self.get_sql(gid, sid, data, did, fid, fsid)
|
||||
if sql == '':
|
||||
sql = "--modified SQL"
|
||||
|
||||
sql = self.get_sql(gid, sid, data, did, fid, fsid)
|
||||
if sql and sql.strip('\n') and sql.strip(' '):
|
||||
return make_json_response(
|
||||
data=sql,
|
||||
status=200
|
||||
)
|
||||
else:
|
||||
return make_json_response(
|
||||
data='-- Modified SQL --',
|
||||
status=200
|
||||
)
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
def get_sql(self, gid, sid, data, did, fid, fsid=None):
|
||||
"""
|
||||
@@ -558,96 +591,96 @@ class ForeignServerView(PGChildNodeView):
|
||||
required_args = [
|
||||
'name'
|
||||
]
|
||||
try:
|
||||
if fsid is not None:
|
||||
sql = render_template("/".join([self.template_path, 'properties.sql']), fsid=fsid, conn=self.conn)
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
if res['rows'][0]['fsrvoptions'] is not None:
|
||||
res['rows'][0]['fsrvoptions'] = self.tokenizeOptions(res['rows'][0]['fsrvoptions'])
|
||||
if fsid is not None:
|
||||
sql = render_template("/".join([self.template_path, 'properties.sql']), fsid=fsid, conn=self.conn)
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
for key in ['fsrvacl']:
|
||||
if key in data and data[key] is not None:
|
||||
if 'added' in data[key]:
|
||||
data[key]['added'] = parse_priv_to_db(data[key]['added'], ['U'])
|
||||
if 'changed' in data[key]:
|
||||
data[key]['changed'] = parse_priv_to_db(data[key]['changed'], ['U'])
|
||||
if 'deleted' in data[key]:
|
||||
data[key]['deleted'] = parse_priv_to_db(data[key]['deleted'], ['U'])
|
||||
if res['rows'][0]['fsrvoptions'] is not None:
|
||||
res['rows'][0]['fsrvoptions'] = self.tokenizeOptions(res['rows'][0]['fsrvoptions'])
|
||||
|
||||
old_data = res['rows'][0]
|
||||
for arg in required_args:
|
||||
if arg not in data:
|
||||
data[arg] = old_data[arg]
|
||||
for key in ['fsrvacl']:
|
||||
if key in data and data[key] is not None:
|
||||
if 'added' in data[key]:
|
||||
data[key]['added'] = parse_priv_to_db(data[key]['added'], ['U'])
|
||||
if 'changed' in data[key]:
|
||||
data[key]['changed'] = parse_priv_to_db(data[key]['changed'], ['U'])
|
||||
if 'deleted' in data[key]:
|
||||
data[key]['deleted'] = parse_priv_to_db(data[key]['deleted'], ['U'])
|
||||
|
||||
new_list_add = []
|
||||
new_list_change = []
|
||||
old_data = res['rows'][0]
|
||||
for arg in required_args:
|
||||
if arg not in data:
|
||||
data[arg] = old_data[arg]
|
||||
|
||||
# Allow user to set the blank value in fsrvvalue field in option model
|
||||
if 'fsrvoptions' in data and 'added' in data['fsrvoptions']:
|
||||
for item in data['fsrvoptions']['added']:
|
||||
new_dict_add = {}
|
||||
if item['fsrvoption']:
|
||||
if 'fsrvvalue' in item and item['fsrvvalue'] and item['fsrvvalue'] != '':
|
||||
new_dict_add.update(item);
|
||||
else:
|
||||
new_dict_add.update({'fsrvoption': item['fsrvoption'], 'fsrvvalue': ''})
|
||||
new_list_add = []
|
||||
new_list_change = []
|
||||
|
||||
new_list_add.append(new_dict_add)
|
||||
# Allow user to set the blank value in fsrvvalue field in option model
|
||||
if 'fsrvoptions' in data and 'added' in data['fsrvoptions']:
|
||||
for item in data['fsrvoptions']['added']:
|
||||
new_dict_add = {}
|
||||
if item['fsrvoption']:
|
||||
if 'fsrvvalue' in item and item['fsrvvalue'] and item['fsrvvalue'] != '':
|
||||
new_dict_add.update(item);
|
||||
else:
|
||||
new_dict_add.update({'fsrvoption': item['fsrvoption'], 'fsrvvalue': ''})
|
||||
|
||||
data['fsrvoptions']['added'] = new_list_add
|
||||
new_list_add.append(new_dict_add)
|
||||
|
||||
# Allow user to set the blank value in fsrvvalue field in option model
|
||||
if 'fsrvoptions' in data and 'changed' in data['fsrvoptions']:
|
||||
for item in data['fsrvoptions']['changed']:
|
||||
new_dict_change = {}
|
||||
if item['fsrvoption']:
|
||||
if 'fsrvvalue' in item and item['fsrvvalue'] and item['fsrvvalue'] != '':
|
||||
new_dict_change.update(item);
|
||||
else:
|
||||
new_dict_change.update({'fsrvoption': item['fsrvoption'], 'fsrvvalue': ''})
|
||||
data['fsrvoptions']['added'] = new_list_add
|
||||
|
||||
new_list_change.append(new_dict_change)
|
||||
# Allow user to set the blank value in fsrvvalue field in option model
|
||||
if 'fsrvoptions' in data and 'changed' in data['fsrvoptions']:
|
||||
for item in data['fsrvoptions']['changed']:
|
||||
new_dict_change = {}
|
||||
if item['fsrvoption']:
|
||||
if 'fsrvvalue' in item and item['fsrvvalue'] and item['fsrvvalue'] != '':
|
||||
new_dict_change.update(item);
|
||||
else:
|
||||
new_dict_change.update({'fsrvoption': item['fsrvoption'], 'fsrvvalue': ''})
|
||||
|
||||
data['fsrvoptions']['changed'] = new_list_change
|
||||
new_list_change.append(new_dict_change)
|
||||
|
||||
sql = render_template("/".join([self.template_path, 'update.sql']), data=data, o_data=old_data,
|
||||
conn=self.conn)
|
||||
else:
|
||||
sql = render_template("/".join([self.template_path, 'properties.sql']), fdwid=fid, conn=self.conn)
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
data['fsrvoptions']['changed'] = new_list_change
|
||||
|
||||
fdw_data = res['rows'][0]
|
||||
sql = render_template("/".join([self.template_path, 'update.sql']), data=data, o_data=old_data,
|
||||
conn=self.conn)
|
||||
return sql, data['name'] if 'name' in data else old_data['name']
|
||||
else:
|
||||
sql = render_template("/".join([self.template_path, 'properties.sql']), fdwid=fid, conn=self.conn)
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
for key in ['fsrvacl']:
|
||||
if key in data and data[key] is not None:
|
||||
data[key] = parse_priv_to_db(data[key], ['U'])
|
||||
fdw_data = res['rows'][0]
|
||||
|
||||
new_list = []
|
||||
for key in ['fsrvacl']:
|
||||
if key in data and data[key] is not None:
|
||||
data[key] = parse_priv_to_db(data[key], ['U'])
|
||||
|
||||
if 'fsrvoptions' in data:
|
||||
for item in data['fsrvoptions']:
|
||||
new_dict = {}
|
||||
if item['fsrvoption']:
|
||||
if 'fsrvvalue' in item and item['fsrvvalue'] and item['fsrvvalue'] != '':
|
||||
new_dict.update(item);
|
||||
else:
|
||||
new_dict.update({'fsrvoption': item['fsrvoption'], 'fsrvvalue': ''})
|
||||
new_list = []
|
||||
|
||||
new_list.append(new_dict)
|
||||
if 'fsrvoptions' in data:
|
||||
for item in data['fsrvoptions']:
|
||||
new_dict = {}
|
||||
if item['fsrvoption']:
|
||||
if 'fsrvvalue' in item and item['fsrvvalue'] and item['fsrvvalue'] != '':
|
||||
new_dict.update(item);
|
||||
else:
|
||||
new_dict.update({'fsrvoption': item['fsrvoption'], 'fsrvvalue': ''})
|
||||
|
||||
data['fsrvoptions'] = new_list
|
||||
new_list.append(new_dict)
|
||||
|
||||
data['fsrvoptions'] = new_list
|
||||
|
||||
sql = render_template("/".join([self.template_path, 'create.sql']), data=data, fdwdata=fdw_data,
|
||||
conn=self.conn)
|
||||
sql += "\n"
|
||||
return sql, data['name']
|
||||
|
||||
sql = render_template("/".join([self.template_path, 'create.sql']), data=data, fdwdata=fdw_data,
|
||||
conn=self.conn)
|
||||
sql += "\n"
|
||||
return sql
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def sql(self, gid, sid, did, fid, fsid):
|
||||
@@ -703,6 +736,8 @@ class ForeignServerView(PGChildNodeView):
|
||||
-- DROP SERVER {0}
|
||||
|
||||
""".format(res['rows'][0]['name'])
|
||||
if hasattr(str, 'decode'):
|
||||
sql_header = sql_header.decode('utf-8')
|
||||
|
||||
sql = sql_header + sql
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
SELECT 'fsrvacl' as deftype, COALESCE(gt.rolname, 'public') grantee, g.rolname grantor, array_agg(privilege_type) as privileges, array_agg(is_grantable) as grantable
|
||||
SELECT 'fsrvacl' as deftype, COALESCE(gt.rolname, 'PUBLIC') grantee, g.rolname grantor, array_agg(privilege_type) as privileges, array_agg(is_grantable) as grantable
|
||||
FROM
|
||||
(SELECT
|
||||
d.grantee, d.grantor, d.is_grantable,
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
{% if fsid %}
|
||||
WITH umapData AS
|
||||
(
|
||||
SELECT u.oid AS um_oid, CASE WHEN u.umuser = 0::oid THEN 'public'::name ELSE a.rolname END AS name,
|
||||
SELECT u.oid AS um_oid, CASE WHEN u.umuser = 0::oid THEN 'PUBLIC'::name ELSE a.rolname END AS name,
|
||||
array_to_string(u.umoptions, ',') AS umoptions FROM pg_user_mapping u
|
||||
LEFT JOIN pg_authid a ON a.oid = u.umuser WHERE u.umserver = {{ fsid }}::OID
|
||||
)
|
||||
|
||||
SELECT um.um_oid, name, dep.deptype FROM umapData um
|
||||
LEFT JOIN pg_depend dep ON dep.objid=um.um_oid
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
@@ -1,4 +1,4 @@
|
||||
SELECT 'fsrvacl' as deftype, COALESCE(gt.rolname, 'public') grantee, g.rolname grantor, array_agg(privilege_type) as privileges, array_agg(is_grantable) as grantable
|
||||
SELECT 'fsrvacl' as deftype, COALESCE(gt.rolname, 'PUBLIC') grantee, g.rolname grantor, array_agg(privilege_type) as privileges, array_agg(is_grantable) as grantable
|
||||
FROM
|
||||
(SELECT
|
||||
d.grantee, d.grantor, d.is_grantable,
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
{% if fsid %}
|
||||
WITH umapData AS
|
||||
(
|
||||
SELECT u.oid AS um_oid, CASE WHEN u.umuser = 0::oid THEN 'public'::name ELSE a.rolname END AS name,
|
||||
SELECT u.oid AS um_oid, CASE WHEN u.umuser = 0::oid THEN 'PUBLIC'::name ELSE a.rolname END AS name,
|
||||
array_to_string(u.umoptions, ',') AS umoptions FROM pg_user_mapping u
|
||||
LEFT JOIN pg_authid a ON a.oid = u.umuser WHERE u.umserver = {{ fsid }}::OID
|
||||
)
|
||||
|
||||
SELECT um.um_oid, name, dep.deptype FROM umapData um
|
||||
LEFT JOIN pg_depend dep ON dep.objid=um.um_oid
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
@@ -0,0 +1,16 @@
|
||||
# #################################################################
|
||||
#
|
||||
# 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 ForeignServersGeneratorTestCase(BaseTestGenerator):
|
||||
|
||||
def runTest(self):
|
||||
return []
|
||||
@@ -0,0 +1,88 @@
|
||||
# #################################################################
|
||||
#
|
||||
# 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
|
||||
from regression import test_utils as utils
|
||||
from pgadmin.browser.server_groups.servers.databases.extensions.tests import\
|
||||
utils as extension_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.schemas.tests import \
|
||||
utils as schema_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.tests import \
|
||||
utils as database_utils
|
||||
from pgadmin.browser.server_groups.servers.tests import utils as server_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.foreign_data_wrappers.tests \
|
||||
import utils as fdw_utils
|
||||
from . import utils as fsrv_utils
|
||||
|
||||
|
||||
class ForeignServerAddTestCase(BaseTestGenerator):
|
||||
"""
|
||||
This class will add foreign server under database node.
|
||||
"""
|
||||
|
||||
scenarios = [
|
||||
# Fetching default URL for foreign server node.
|
||||
('Check FSRV Node', dict(url='/browser/foreign_server/obj/'))
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
""""
|
||||
This function perform the following tasks:
|
||||
1. Add and connect to the test server(s)
|
||||
2. Add database(s) connected to server(s)
|
||||
3. Add schemas to connected database(s)
|
||||
4. Add extension(s) to schema(s)
|
||||
5. Add foreign data wrapper(s) to extension(s)
|
||||
|
||||
:return: None"
|
||||
"""
|
||||
|
||||
# Add the server(s)
|
||||
server_utils.add_server(cls.tester)
|
||||
|
||||
# Connect to server(s)
|
||||
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(s) to connected server(s)
|
||||
database_utils.add_database(cls.tester, cls.server_connect_response,
|
||||
cls.server_ids)
|
||||
|
||||
# Add schema(s) under connected database(s)
|
||||
schema_utils.add_schemas(cls.tester)
|
||||
|
||||
# Add extension(s) to schema(s)
|
||||
extension_utils.add_extensions(cls.tester)
|
||||
|
||||
# Add foreign data wrapper(s) to extension(s)
|
||||
fdw_utils.add_fdw(cls.tester)
|
||||
|
||||
def runTest(self):
|
||||
""" This function will add foreign server under database node. """
|
||||
|
||||
fsrv_utils.add_fsrv(self.tester)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
"""This function deletes the added schema, database, server and parent
|
||||
id file
|
||||
|
||||
:return: None
|
||||
"""
|
||||
fsrv_utils.delete_fsrv(cls.tester)
|
||||
fdw_utils.delete_fdw(cls.tester)
|
||||
extension_utils.delete_extension(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()
|
||||
@@ -0,0 +1,94 @@
|
||||
# #################################################################
|
||||
#
|
||||
# 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
|
||||
from regression import test_utils as utils
|
||||
from pgadmin.browser.server_groups.servers.databases.extensions.tests import\
|
||||
utils as extension_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.schemas.tests import \
|
||||
utils as schema_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.tests import \
|
||||
utils as database_utils
|
||||
from pgadmin.browser.server_groups.servers.tests import utils as server_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.foreign_data_wrappers.tests\
|
||||
import utils as fdw_utils
|
||||
from . import utils as fsrv_utils
|
||||
|
||||
|
||||
class ForeignServerDeleteTestCase(BaseTestGenerator):
|
||||
"""
|
||||
This class will add foreign server under FDW node.
|
||||
"""
|
||||
|
||||
scenarios = [
|
||||
# Fetching default URL for foreign server node.
|
||||
('Check FSRV Node', dict(url='/browser/foreign_server/obj/'))
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
"""
|
||||
This function perform the following tasks:
|
||||
1. Add and connect to the test server(s)
|
||||
2. Add database(s) connected to server(s)
|
||||
3. Add schemas to connected database(s)
|
||||
4. Add extension(s) to schema(s)
|
||||
5. Add foreign data wrapper(s) to extension(s)
|
||||
6. Add foreign server(s) to foreign data wrapper(s)
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
# Add the server
|
||||
server_utils.add_server(cls.tester)
|
||||
|
||||
# Connect to servers
|
||||
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 databases to connected servers
|
||||
database_utils.add_database(cls.tester, cls.server_connect_response,
|
||||
cls.server_ids)
|
||||
|
||||
# Add schema(s) under connected database(s)
|
||||
schema_utils.add_schemas(cls.tester)
|
||||
|
||||
# Add extension(s) to schema(s)
|
||||
extension_utils.add_extensions(cls.tester)
|
||||
|
||||
# Add foreign data wrapper(s) to extension(s)
|
||||
fdw_utils.add_fdw(cls.tester)
|
||||
|
||||
# Add foreign server(s) to foreign data wrapper
|
||||
fsrv_utils.add_fsrv(cls.tester)
|
||||
|
||||
#
|
||||
def runTest(self):
|
||||
""" This function will delete foreign server under FDW node. """
|
||||
|
||||
delete_respdata = fsrv_utils.delete_fsrv(self.tester)
|
||||
self.assertTrue(delete_respdata['success'], 1)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
"""This function deletes the added schema, database, server and parent
|
||||
id file
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
fdw_utils.delete_fdw(cls.tester)
|
||||
extension_utils.delete_extension(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()
|
||||
@@ -0,0 +1,110 @@
|
||||
# #################################################################
|
||||
#
|
||||
# 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
|
||||
from regression import test_utils as utils
|
||||
from pgadmin.browser.server_groups.servers.databases.extensions.tests import\
|
||||
utils as extension_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.schemas.tests import \
|
||||
utils as schema_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.tests import \
|
||||
utils as database_utils
|
||||
from pgadmin.browser.server_groups.servers.tests import utils as server_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.foreign_data_wrappers.tests\
|
||||
import utils as fdw_utils
|
||||
from . import utils as fsrv_utils
|
||||
|
||||
class ForeignServerGetTestCase(BaseTestGenerator):
|
||||
"""
|
||||
This class will add foreign server under FDW node.
|
||||
"""
|
||||
|
||||
scenarios = [
|
||||
# Fetching default URL for foreign server node.
|
||||
('Check FSRV Node', dict(url='/browser/foreign_server/obj/'))
|
||||
]
|
||||
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
"""
|
||||
This function perform the following tasks:
|
||||
1. Add and connect to the test server(s)
|
||||
2. Add database(s) connected to server(s)
|
||||
3. Add schemas to connected database(s)
|
||||
4. Add extension(s) to schema(s)
|
||||
5. Add foreign data wrapper(s) to extension(s)
|
||||
6. Add foreign server(s) to foreign data wrapper(s)
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
# Add the server
|
||||
server_utils.add_server(cls.tester)
|
||||
|
||||
# Connect to servers
|
||||
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 databases to connected servers
|
||||
database_utils.add_database(cls.tester, cls.server_connect_response,
|
||||
cls.server_ids)
|
||||
|
||||
# Add schema(s) under connected database(s)
|
||||
schema_utils.add_schemas(cls.tester)
|
||||
|
||||
# Add extension(s) to schema(s)
|
||||
extension_utils.add_extensions(cls.tester)
|
||||
|
||||
# Add foreign data wrapper(s) to extension(s)
|
||||
fdw_utils.add_fdw(cls.tester)
|
||||
|
||||
# Add foreign server(s) to foreign data wrapper
|
||||
fsrv_utils.add_fsrv(cls.tester)
|
||||
|
||||
#
|
||||
def runTest(self):
|
||||
""" This function will fetch foreign server under FDW node. """
|
||||
|
||||
all_id = utils.get_ids()
|
||||
server_ids = all_id["sid"]
|
||||
db_ids_dict = all_id["did"][0]
|
||||
fdw_ids_dict = all_id["fid"][0]
|
||||
fsrv_ids_dict = all_id["fsid"][0]
|
||||
|
||||
for server_id in server_ids:
|
||||
db_id = db_ids_dict[int(server_id)]
|
||||
fdw_id = fdw_ids_dict[server_id]
|
||||
fsrv_id = fsrv_ids_dict[server_id]
|
||||
|
||||
response = fsrv_utils.verify_fsrv(self.tester, utils.SERVER_GROUP,
|
||||
server_id, db_id,
|
||||
fdw_id, fsrv_id)
|
||||
|
||||
self.assertEquals(response.status_code, 200)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
"""This function deletes the added schema, database, server and parent
|
||||
id file
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
fsrv_utils.delete_fsrv(cls.tester)
|
||||
fdw_utils.delete_fdw(cls.tester)
|
||||
extension_utils.delete_extension(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()
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
# #################################################################
|
||||
#
|
||||
# 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
|
||||
from regression import test_utils as utils
|
||||
from pgadmin.browser.server_groups.servers.databases.extensions.tests import\
|
||||
utils as extension_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.schemas.tests import \
|
||||
utils as schema_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.tests import \
|
||||
utils as database_utils
|
||||
from pgadmin.browser.server_groups.servers.tests import utils as server_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.foreign_data_wrappers.tests\
|
||||
import utils as fdw_utils
|
||||
from . import utils as fsrv_utils
|
||||
from regression.test_setup import advanced_config_data
|
||||
import json
|
||||
|
||||
class ForeignServerPutTestCase(BaseTestGenerator):
|
||||
"""
|
||||
This class will add foreign server under FDW node.
|
||||
"""
|
||||
|
||||
scenarios = [
|
||||
# Fetching default URL for foreign server node.
|
||||
('Check FSRV Node', dict(url='/browser/foreign_server/obj/'))
|
||||
]
|
||||
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
"""
|
||||
This function perform the following tasks:
|
||||
1. Add and connect to the test server(s)
|
||||
2. Add database(s) connected to server(s)
|
||||
3. Add schemas to connected database(s)
|
||||
4. Add extension(s) to schema(s)
|
||||
5. Add foreign data wrapper(s) to extension(s)
|
||||
6. Add foreign server(s) to foreign data wrapper(s)
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
# Add the server
|
||||
server_utils.add_server(cls.tester)
|
||||
|
||||
# Connect to servers
|
||||
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 databases to connected servers
|
||||
database_utils.add_database(cls.tester, cls.server_connect_response,
|
||||
cls.server_ids)
|
||||
|
||||
# Add schema(s) under connected database(s)
|
||||
schema_utils.add_schemas(cls.tester)
|
||||
|
||||
# Add extension(s) to schema(s)
|
||||
extension_utils.add_extensions(cls.tester)
|
||||
|
||||
# Add foreign data wrapper(s) to extension(s)
|
||||
fdw_utils.add_fdw(cls.tester)
|
||||
|
||||
# Add foreign server(s) to foreign data wrapper
|
||||
fsrv_utils.add_fsrv(cls.tester)
|
||||
|
||||
#
|
||||
def runTest(self):
|
||||
""" This function will update foreign server under FDW node. """
|
||||
|
||||
all_id = utils.get_ids()
|
||||
server_ids = all_id["sid"]
|
||||
db_ids_dict = all_id["did"][0]
|
||||
fdw_ids_dict = all_id["fid"][0]
|
||||
fsrv_ids_dict = all_id["fsid"][0]
|
||||
|
||||
for server_id in server_ids:
|
||||
db_id = db_ids_dict[int(server_id)]
|
||||
fdw_id = fdw_ids_dict[server_id]
|
||||
fsrv_id = fsrv_ids_dict[server_id]
|
||||
|
||||
response = fsrv_utils.verify_fsrv(self.tester, utils.SERVER_GROUP,
|
||||
server_id, db_id,
|
||||
fdw_id, fsrv_id)
|
||||
|
||||
if response.status_code == 200:
|
||||
data = \
|
||||
{
|
||||
"description": advanced_config_data['FSRV_update_data']
|
||||
['comment'],
|
||||
"id": fsrv_id
|
||||
}
|
||||
|
||||
put_response = self.tester.put(
|
||||
self.url + str(utils.SERVER_GROUP) + '/' +
|
||||
str(server_id) + '/' + str(db_id) +
|
||||
'/' + str(fdw_id) + '/' +
|
||||
str(fsrv_id), data=json.dumps(data),
|
||||
follow_redirects=True)
|
||||
|
||||
self.assertEquals(put_response.status_code, 200)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
"""
|
||||
This function deletes the added foreign server(s) ,
|
||||
foreign data wrapper(s), extension(s), schema(s), database(s), server(s)
|
||||
and parent id file
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
fsrv_utils.delete_fsrv(cls.tester)
|
||||
fdw_utils.delete_fdw(cls.tester)
|
||||
extension_utils.delete_extension(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()
|
||||
@@ -0,0 +1,149 @@
|
||||
# #################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2016, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
# ##################################################################
|
||||
|
||||
import os
|
||||
import pickle
|
||||
import json
|
||||
from regression.test_setup import advanced_config_data, pickle_path
|
||||
from regression import test_utils as utils
|
||||
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
|
||||
import uuid
|
||||
from pgadmin.browser.server_groups.servers.databases.foreign_data_wrappers.tests \
|
||||
import utils as fdw_utils
|
||||
|
||||
|
||||
FSRV_URL = '/browser/foreign_server/obj/'
|
||||
|
||||
|
||||
def get_fsrv_config_data(server_connect_data):
|
||||
adv_config_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['fsrv_credentials']:
|
||||
if db_user == config_test_data['owner']:
|
||||
adv_config_data = config_test_data
|
||||
|
||||
data = {
|
||||
"fsrvacl": adv_config_data['fsrv_acl'],
|
||||
"fsrvoptions": adv_config_data['fsrv_options'],
|
||||
"fsrvowner": adv_config_data['owner'],
|
||||
"name": "fsrv_{}".format(str(uuid.uuid4())[1:4])
|
||||
}
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def add_fsrv(tester):
|
||||
|
||||
all_id = utils.get_ids()
|
||||
server_ids = all_id["sid"]
|
||||
db_ids_dict = all_id["did"][0]
|
||||
fdw_ids_dict = all_id["fid"][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']:
|
||||
server_connect_response = server_utils.verify_server(
|
||||
tester, utils.SERVER_GROUP, server_id)
|
||||
|
||||
fdw_id = fdw_ids_dict[server_id]
|
||||
|
||||
response = fdw_utils.verify_fdws(tester,
|
||||
utils.SERVER_GROUP,
|
||||
server_id, db_id,
|
||||
fdw_id)
|
||||
|
||||
if response.status_code == 200:
|
||||
data = get_fsrv_config_data(server_connect_response)
|
||||
|
||||
response = tester.post(
|
||||
FSRV_URL + str(utils.SERVER_GROUP) + '/' +
|
||||
str(server_id) + '/' + str(
|
||||
db_id) +
|
||||
'/' + str(fdw_id) + '/',
|
||||
data=json.dumps(data),
|
||||
content_type='html/json')
|
||||
|
||||
assert response.status_code == 200
|
||||
|
||||
response_data = json.loads(response.data.decode())
|
||||
write_fsrv_info(response_data, server_id)
|
||||
|
||||
|
||||
def write_fsrv_info(response_data, server_id):
|
||||
"""
|
||||
This function writes the schema id into parent_id.pkl
|
||||
|
||||
:param response_data: foreign server add response data
|
||||
:type response_data: dict
|
||||
:param server_id: server id
|
||||
:type server_id: str
|
||||
:return: None
|
||||
"""
|
||||
|
||||
fsrv_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 'fsid' in pickle_id_dict:
|
||||
if pickle_id_dict['fsid']:
|
||||
# Add the FSRV_id as value in dict
|
||||
pickle_id_dict["fsid"][0].update({server_id: fsrv_id})
|
||||
else:
|
||||
# Create new dict with server_id and fsrv_id
|
||||
pickle_id_dict["fsid"].append({server_id: fsrv_id})
|
||||
fsrv_output = open(pickle_path, 'wb')
|
||||
pickle.dump(pickle_id_dict, fsrv_output)
|
||||
fsrv_output.close()
|
||||
|
||||
|
||||
def verify_fsrv(tester, server_group, server_id, db_id, fdw_id, fsrv_id):
|
||||
response = tester.get(FSRV_URL + str(server_group) + '/' +
|
||||
str(server_id) + '/' + str(db_id) +
|
||||
'/' + str(fdw_id) + '/' + str(fsrv_id),
|
||||
content_type='html/json')
|
||||
return response
|
||||
|
||||
|
||||
def delete_fsrv(tester):
|
||||
all_id = utils.get_ids()
|
||||
server_ids = all_id["sid"]
|
||||
db_ids_dict = all_id["did"][0]
|
||||
fdw_ids_dict = all_id["fid"][0]
|
||||
fsrv_ids_dict = all_id["fsid"][0]
|
||||
|
||||
for server_id in server_ids:
|
||||
db_id = db_ids_dict[int(server_id)]
|
||||
fdw_id = fdw_ids_dict[server_id]
|
||||
fsrv_id = fsrv_ids_dict[server_id]
|
||||
|
||||
response = verify_fsrv(tester, utils.SERVER_GROUP,
|
||||
server_id, db_id,
|
||||
fdw_id, fsrv_id)
|
||||
|
||||
if response.status_code == 200:
|
||||
delete_response = tester.delete(
|
||||
FSRV_URL + str(utils.SERVER_GROUP) +
|
||||
'/' + str(server_id) + '/' +
|
||||
str(db_id) + '/' +
|
||||
str(fdw_id) + '/' +
|
||||
str(fsrv_id),
|
||||
follow_redirects=True)
|
||||
|
||||
delete_respdata = json.loads(delete_response.data.decode())
|
||||
return delete_respdata
|
||||
@@ -9,18 +9,18 @@
|
||||
|
||||
"""Implements User Mapping Node"""
|
||||
|
||||
import json
|
||||
import simplejson as json
|
||||
from functools import wraps
|
||||
|
||||
import pgadmin.browser.server_groups.servers as servers
|
||||
from flask import render_template, make_response, request, jsonify
|
||||
from flask.ext.babel import gettext
|
||||
from flask_babel import gettext
|
||||
from pgadmin.browser.collection import CollectionNodeModule
|
||||
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.ajax import make_json_response, internal_server_error, \
|
||||
make_response as ajax_response
|
||||
from pgadmin.utils.driver import get_driver
|
||||
from pgadmin.utils.ajax import gone
|
||||
|
||||
from config import PG_DEFAULT_DRIVER
|
||||
|
||||
@@ -219,21 +219,9 @@ class UserMappingView(PGChildNodeView):
|
||||
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!"
|
||||
)
|
||||
)
|
||||
|
||||
# Set the template path for the SQL scripts
|
||||
self.template_path = 'user_mappings/sql/9.1_plus'
|
||||
|
||||
ver = self.manager.version
|
||||
# we will set template path for sql scripts
|
||||
if ver >= 90100:
|
||||
self.template_path = 'user_mappings/sql/9.1_plus'
|
||||
|
||||
return f(*args, **kwargs)
|
||||
|
||||
return wrap
|
||||
@@ -297,6 +285,39 @@ class UserMappingView(PGChildNodeView):
|
||||
status=200
|
||||
)
|
||||
|
||||
@check_precondition
|
||||
def node(self, gid, sid, did, fid, fsid, umid):
|
||||
"""
|
||||
This function will fetch properties of user mapping node.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
fid: Foreign data wrapper ID
|
||||
fsid: Foreign server ID
|
||||
umid: User mapping ID
|
||||
"""
|
||||
sql = render_template("/".join([self.template_path, 'properties.sql']),
|
||||
conn=self.conn, umid=umid)
|
||||
status, r_set = self.conn.execute_2darray(sql)
|
||||
|
||||
if not status:
|
||||
return internal_server_error(errormsg=r_set)
|
||||
|
||||
for row in r_set['rows']:
|
||||
return make_json_response(
|
||||
data=self.blueprint.generate_browser_node(
|
||||
row['um_oid'],
|
||||
fsid,
|
||||
row['name'],
|
||||
icon="icon-user_mapping"
|
||||
),
|
||||
status=200
|
||||
)
|
||||
|
||||
return gone(gettext("Could not find the specified user mapping."))
|
||||
|
||||
def tokenizeOptions(self, option_value):
|
||||
"""
|
||||
This function will tokenize the string stored in database
|
||||
@@ -336,8 +357,10 @@ class UserMappingView(PGChildNodeView):
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
if res['rows'][0]['name'] == 'public':
|
||||
res['rows'][0]['name'] = 'PUBLIC'
|
||||
if len(res['rows']) == 0:
|
||||
return gone(
|
||||
gettext("Couldnot find the user mapping information.")
|
||||
)
|
||||
|
||||
if res['rows'][0]['umoptions'] is not None:
|
||||
res['rows'][0]['umoptions'] = self.tokenizeOptions(res['rows'][0]['umoptions'])
|
||||
@@ -364,7 +387,9 @@ class UserMappingView(PGChildNodeView):
|
||||
'name'
|
||||
]
|
||||
|
||||
data = request.form if request.form else json.loads(request.data.decode())
|
||||
data = request.form if request.form else json.loads(
|
||||
request.data, encoding='utf-8'
|
||||
)
|
||||
for arg in required_args:
|
||||
if arg not in data:
|
||||
return make_json_response(
|
||||
@@ -438,39 +463,24 @@ class UserMappingView(PGChildNodeView):
|
||||
umid: User mapping ID
|
||||
"""
|
||||
|
||||
data = request.form if request.form else json.loads(request.data.decode())
|
||||
sql = self.get_sql(gid, sid, data, did, fid, fsid, umid)
|
||||
data = request.form if request.form else json.loads(
|
||||
request.data, encoding='utf-8'
|
||||
)
|
||||
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)
|
||||
sql, name = self.get_sql(gid, sid, data, did, fid, fsid, umid)
|
||||
sql = sql.strip('\n').strip(' ')
|
||||
status, res = self.conn.execute_scalar(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
return make_json_response(
|
||||
success=1,
|
||||
info="User Mapping updated",
|
||||
data={
|
||||
'id': umid,
|
||||
'fsid': fsid,
|
||||
'fid': fid,
|
||||
'did': did,
|
||||
'sid': sid,
|
||||
'gid': gid
|
||||
}
|
||||
)
|
||||
else:
|
||||
return make_json_response(
|
||||
success=1,
|
||||
info="Nothing to update",
|
||||
data={
|
||||
'id': umid,
|
||||
'fsid': fsid,
|
||||
'fid': fid,
|
||||
'did': did,
|
||||
'sid': sid,
|
||||
'gid': gid
|
||||
}
|
||||
return jsonify(
|
||||
node=self.blueprint.generate_browser_node(
|
||||
umid,
|
||||
fsid,
|
||||
name,
|
||||
icon="icon-%s" % self.node_type
|
||||
)
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
@@ -502,11 +512,32 @@ class UserMappingView(PGChildNodeView):
|
||||
if not status:
|
||||
return internal_server_error(errormsg=name)
|
||||
|
||||
if name is None:
|
||||
return make_json_response(
|
||||
status=410,
|
||||
success=0,
|
||||
errormsg=gettext(
|
||||
'Error: Object not found.'
|
||||
),
|
||||
info=gettext(
|
||||
'The specified foreign server could not be found.\n'
|
||||
)
|
||||
)
|
||||
|
||||
sql = render_template("/".join([self.template_path, 'properties.sql']), umid=umid, conn=self.conn)
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
if not res['rows']:
|
||||
return make_json_response(
|
||||
status=410,
|
||||
success=0,
|
||||
errormsg=gettext(
|
||||
'The specified user mapping could not be found.\n'
|
||||
)
|
||||
)
|
||||
|
||||
data = res['rows'][0]
|
||||
|
||||
# drop user mapping
|
||||
@@ -549,21 +580,20 @@ class UserMappingView(PGChildNodeView):
|
||||
data = {}
|
||||
for k, v in request.args.items():
|
||||
try:
|
||||
data[k] = json.loads(v)
|
||||
data[k] = json.loads(v, encoding='utf-8')
|
||||
except ValueError:
|
||||
data[k] = v
|
||||
try:
|
||||
sql, name = self.get_sql(gid, sid, data, did, fid, fsid, umid)
|
||||
if sql == '':
|
||||
sql = "--modified SQL"
|
||||
|
||||
sql = self.get_sql(gid, sid, data, did, fid, fsid, umid)
|
||||
if sql and sql.strip('\n') and sql.strip(' '):
|
||||
return make_json_response(
|
||||
data=sql,
|
||||
status=200
|
||||
)
|
||||
else:
|
||||
return make_json_response(
|
||||
data='-- Modified SQL --',
|
||||
status=200
|
||||
)
|
||||
)
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
def get_sql(self, gid, sid, data, did, fid, fsid, umid=None):
|
||||
"""
|
||||
@@ -582,93 +612,92 @@ class UserMappingView(PGChildNodeView):
|
||||
required_args = [
|
||||
'name'
|
||||
]
|
||||
try:
|
||||
if umid is not None:
|
||||
sql = render_template("/".join([self.template_path, 'properties.sql']), umid=umid, conn=self.conn)
|
||||
|
||||
if umid is not None:
|
||||
sql = render_template("/".join([self.template_path, 'properties.sql']), umid=umid, conn=self.conn)
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
if res['rows'][0]['umoptions'] is not None:
|
||||
res['rows'][0]['umoptions'] = self.tokenizeOptions(res['rows'][0]['umoptions'])
|
||||
|
||||
old_data = res['rows'][0]
|
||||
|
||||
sql = render_template("/".join([self.template_path, 'properties.sql']), fserid=fsid, conn=self.conn)
|
||||
status, res1 = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res1)
|
||||
|
||||
fdw_data = res1['rows'][0]
|
||||
|
||||
for arg in required_args:
|
||||
if arg not in data:
|
||||
data[arg] = old_data[arg]
|
||||
|
||||
new_list_add = []
|
||||
new_list_change = []
|
||||
|
||||
# Allow user to set the blank value in fdwvalue field in option model
|
||||
if 'umoptions' in data and 'added' in data['umoptions']:
|
||||
for item in data['umoptions']['added']:
|
||||
new_dict_add = {}
|
||||
if item['umoption']:
|
||||
if 'umvalue' in item and item['umvalue'] and item['umvalue'] != '':
|
||||
new_dict_add.update(item);
|
||||
else:
|
||||
new_dict_add.update({'umoption': item['umoption'], 'umvalue': ''})
|
||||
|
||||
new_list_add.append(new_dict_add)
|
||||
|
||||
data['umoptions']['added'] = new_list_add
|
||||
|
||||
# Allow user to set the blank value in fdwvalue field in option model
|
||||
if 'umoptions' in data and 'changed' in data['umoptions']:
|
||||
for item in data['umoptions']['changed']:
|
||||
new_dict_change = {}
|
||||
if item['umoption']:
|
||||
if 'umvalue' in item and item['umvalue'] and item['umvalue'] != '':
|
||||
new_dict_change.update(item);
|
||||
else:
|
||||
new_dict_change.update({'umoption': item['umoption'], 'umvalue': ''})
|
||||
|
||||
new_list_change.append(new_dict_change)
|
||||
|
||||
data['umoptions']['changed'] = new_list_change
|
||||
|
||||
sql = render_template("/".join([self.template_path, 'update.sql']), data=data, o_data=old_data,
|
||||
fdwdata=fdw_data, conn=self.conn)
|
||||
return sql, data['name'] if 'name' in data else old_data['name']
|
||||
else:
|
||||
sql = render_template("/".join([self.template_path, 'properties.sql']), fserid=fsid, conn=self.conn)
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
fdw_data = res['rows'][0]
|
||||
|
||||
if res['rows'][0]['umoptions'] is not None:
|
||||
res['rows'][0]['umoptions'] = self.tokenizeOptions(res['rows'][0]['umoptions'])
|
||||
new_list = []
|
||||
|
||||
old_data = res['rows'][0]
|
||||
if 'umoptions' in data:
|
||||
for item in data['umoptions']:
|
||||
new_dict = {}
|
||||
if item['umoption']:
|
||||
if 'umvalue' in item and item['umvalue'] \
|
||||
and item['umvalue'] != '':
|
||||
new_dict.update(item);
|
||||
else:
|
||||
new_dict.update(
|
||||
{'umoption': item['umoption'],
|
||||
'umvalue': ''}
|
||||
)
|
||||
new_list.append(new_dict)
|
||||
|
||||
sql = render_template("/".join([self.template_path, 'properties.sql']), fserid=fsid, conn=self.conn)
|
||||
status, res1 = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res1)
|
||||
data['umoptions'] = new_list
|
||||
|
||||
fdw_data = res1['rows'][0]
|
||||
|
||||
for arg in required_args:
|
||||
if arg not in data:
|
||||
data[arg] = old_data[arg]
|
||||
|
||||
new_list_add = []
|
||||
new_list_change = []
|
||||
|
||||
# Allow user to set the blank value in fdwvalue field in option model
|
||||
if 'umoptions' in data and 'added' in data['umoptions']:
|
||||
for item in data['umoptions']['added']:
|
||||
new_dict_add = {}
|
||||
if item['umoption']:
|
||||
if 'umvalue' in item and item['umvalue'] and item['umvalue'] != '':
|
||||
new_dict_add.update(item);
|
||||
else:
|
||||
new_dict_add.update({'umoption': item['umoption'], 'umvalue': ''})
|
||||
|
||||
new_list_add.append(new_dict_add)
|
||||
|
||||
data['umoptions']['added'] = new_list_add
|
||||
|
||||
# Allow user to set the blank value in fdwvalue field in option model
|
||||
if 'umoptions' in data and 'changed' in data['umoptions']:
|
||||
for item in data['umoptions']['changed']:
|
||||
new_dict_change = {}
|
||||
if item['umoption']:
|
||||
if 'umvalue' in item and item['umvalue'] and item['umvalue'] != '':
|
||||
new_dict_change.update(item);
|
||||
else:
|
||||
new_dict_change.update({'umoption': item['umoption'], 'umvalue': ''})
|
||||
|
||||
new_list_change.append(new_dict_change)
|
||||
|
||||
data['umoptions']['changed'] = new_list_change
|
||||
|
||||
sql = render_template("/".join([self.template_path, 'update.sql']), data=data, o_data=old_data,
|
||||
fdwdata=fdw_data, conn=self.conn)
|
||||
else:
|
||||
sql = render_template("/".join([self.template_path, 'properties.sql']), fserid=fsid, conn=self.conn)
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
fdw_data = res['rows'][0]
|
||||
|
||||
new_list = []
|
||||
|
||||
if 'umoptions' in data:
|
||||
for item in data['umoptions']:
|
||||
new_dict = {}
|
||||
if item['umoption']:
|
||||
if 'umvalue' in item and item['umvalue'] \
|
||||
and item['umvalue'] != '':
|
||||
new_dict.update(item);
|
||||
else:
|
||||
new_dict.update(
|
||||
{'umoption': item['umoption'],
|
||||
'umvalue': ''}
|
||||
)
|
||||
new_list.append(new_dict)
|
||||
|
||||
data['umoptions'] = new_list
|
||||
|
||||
sql = render_template("/".join([self.template_path, 'create.sql']), data=data, fdwdata=fdw_data,
|
||||
conn=self.conn)
|
||||
sql += "\n"
|
||||
return sql
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
sql = render_template("/".join([self.template_path, 'create.sql']), data=data, fdwdata=fdw_data,
|
||||
conn=self.conn)
|
||||
sql += "\n"
|
||||
return sql, data['name']
|
||||
|
||||
@check_precondition
|
||||
def sql(self, gid, sid, did, fid, fsid, umid):
|
||||
@@ -709,6 +738,8 @@ class UserMappingView(PGChildNodeView):
|
||||
-- DROP USER MAPPING FOR {0} SERVER {1}
|
||||
|
||||
""".format(res['rows'][0]['name'], fdw_data['name'])
|
||||
if hasattr(str, 'decode'):
|
||||
sql_header = sql_header.decode('utf-8')
|
||||
|
||||
sql = sql_header + sql
|
||||
|
||||
|
||||
@@ -5,5 +5,5 @@ SELECT srvname as name FROM pg_foreign_server srv LEFT OUTER JOIN pg_foreign_dat
|
||||
{% endif %}
|
||||
{# ============= Drop/Delete cascade user mapping ============= #}
|
||||
{% if name and data %}
|
||||
DROP USER MAPPING FOR {{ conn|qtIdent(data.name) }} SERVER {{ conn|qtIdent(name) }}
|
||||
DROP USER MAPPING FOR {% if data.name == "CURRENT_USER" or data.name == "PUBLIC" %}{{ data.name }}{% else %}{{ conn|qtIdent(data.name) }}{% endif %} SERVER {{ conn|qtIdent(name) }}
|
||||
{% endif %}
|
||||
@@ -8,16 +8,16 @@ WHERE srv.oid = {{fserid}}::int
|
||||
{% if fsid or umid or fdwdata or data %}
|
||||
WITH umapData AS
|
||||
(
|
||||
SELECT u.oid AS um_oid, CASE WHEN u.umuser = 0::oid THEN 'public'::name ELSE a.rolname END AS name,
|
||||
SELECT u.oid AS um_oid, CASE WHEN u.umuser = 0::oid THEN 'PUBLIC'::name ELSE a.rolname END AS name,
|
||||
array_to_string(u.umoptions, ',') AS umoptions FROM pg_user_mapping u
|
||||
LEFT JOIN pg_authid a ON a.oid = u.umuser {% if fsid %} WHERE u.umserver = {{fsid}}::int {% endif %} {% if umid %} WHERE u.oid= {{umid}}::int {% endif %}
|
||||
)
|
||||
SELECT * FROM umapData
|
||||
{% if data %}
|
||||
WHERE {% if data.name == "CURRENT_USER" %} name = {{data.name}} {% elif data.name == "PUBLIC" %} name = {{data.name.lower()|qtLiteral}} {% else %} name = {{data.name|qtLiteral}} {% endif %}
|
||||
WHERE {% if data.name == "CURRENT_USER" %} name = {{data.name}} {% elif data.name == "PUBLIC" %} name = {{data.name|qtLiteral}} {% else %} name = {{data.name|qtLiteral}} {% endif %}
|
||||
{% endif %}
|
||||
{% if fdwdata %}
|
||||
WHERE fdw.fdwname = {{fdwdata.name|qtLiteral}}::text
|
||||
{% endif %}
|
||||
ORDER BY 2;
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
@@ -0,0 +1,16 @@
|
||||
# #################################################################
|
||||
#
|
||||
# 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 UserMappingGeneratorTestCase(BaseTestGenerator):
|
||||
|
||||
def runTest(self):
|
||||
return []
|
||||
@@ -0,0 +1,98 @@
|
||||
# #################################################################
|
||||
#
|
||||
# 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
|
||||
from regression import test_utils as utils
|
||||
from pgadmin.browser.server_groups.servers.databases.extensions.tests import\
|
||||
utils as extension_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.schemas.tests import \
|
||||
utils as schema_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.tests import \
|
||||
utils as database_utils
|
||||
from pgadmin.browser.server_groups.servers.tests import utils as server_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.foreign_data_wrappers.tests\
|
||||
import utils as fdw_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.foreign_data_wrappers.\
|
||||
foreign_servers.tests import utils as fsrv_utils
|
||||
from . import utils as um_utils
|
||||
|
||||
|
||||
class UserMappingAddTestCase(BaseTestGenerator):
|
||||
"""
|
||||
This class will add user mapping under foreign server node.
|
||||
"""
|
||||
|
||||
scenarios = [
|
||||
# Fetching default URL for user mapping node.
|
||||
('Check user mapping Node', dict(url='/browser/user_mapping/obj/'))
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
"""
|
||||
This function perform the following tasks:
|
||||
1. Add and connect to the test server(s)
|
||||
2. Add database(s) connected to server(s)
|
||||
3. Add schemas to connected database(s)
|
||||
4. Add extension(s) to schema(s)
|
||||
5. Add foreign data wrapper(s) to extension(s)
|
||||
6. Add foreign server(s) to foreign data wrapper(s)
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
# Add the server
|
||||
server_utils.add_server(cls.tester)
|
||||
|
||||
# Connect to servers
|
||||
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 databases to connected servers
|
||||
database_utils.add_database(cls.tester, cls.server_connect_response,
|
||||
cls.server_ids)
|
||||
|
||||
# Add schema(s) under connected database(s)
|
||||
schema_utils.add_schemas(cls.tester)
|
||||
|
||||
# Add extension(s) to schema(s)
|
||||
extension_utils.add_extensions(cls.tester)
|
||||
|
||||
# Add foreign data wrapper(s) to extension(s)
|
||||
fdw_utils.add_fdw(cls.tester)
|
||||
|
||||
# Add foreign server(s) to foreign data wrapper
|
||||
fsrv_utils.add_fsrv(cls.tester)
|
||||
|
||||
def runTest(self):
|
||||
""" This function will add user mapping under foreign server node. """
|
||||
|
||||
um_utils.add_um(self.tester)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
"""
|
||||
This function deletes the added user mapping(s), foreign server(s),
|
||||
foreign data wrapper(s), extension(s), schema(s), database(s),
|
||||
server(s) and parent id file
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
um_utils.delete_um(cls.tester)
|
||||
fsrv_utils.delete_fsrv(cls.tester)
|
||||
fdw_utils.delete_fdw(cls.tester)
|
||||
extension_utils.delete_extension(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()
|
||||
@@ -0,0 +1,102 @@
|
||||
# #################################################################
|
||||
#
|
||||
# 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
|
||||
from regression import test_utils as utils
|
||||
from pgadmin.browser.server_groups.servers.databases.extensions.tests import\
|
||||
utils as extension_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.schemas.tests import \
|
||||
utils as schema_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.tests import \
|
||||
utils as database_utils
|
||||
from pgadmin.browser.server_groups.servers.tests import utils as server_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.foreign_data_wrappers.tests \
|
||||
import utils as fdw_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.foreign_data_wrappers.\
|
||||
foreign_servers.tests import utils as fsrv_utils
|
||||
from . import utils as um_utils
|
||||
|
||||
|
||||
class UserMappingDeleteTestCase(BaseTestGenerator):
|
||||
"""
|
||||
This class will delete user mapping under foreign server node.
|
||||
"""
|
||||
|
||||
scenarios = [
|
||||
# Fetching default URL for user mapping node.
|
||||
('Check user mapping Node', dict(url='/browser/user_mapping/obj/'))
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
"""
|
||||
This function perform the following tasks:
|
||||
1. Add and connect to the test server(s)
|
||||
2. Add database(s) connected to server(s)
|
||||
3. Add schemas to connected database(s)
|
||||
4. Add extension(s) to schema(s)
|
||||
5. Add foreign data wrapper(s) to extension(s)
|
||||
6. Add foreign server(s) to foreign data wrapper(s)
|
||||
7. Add user mapping(s) to foreign server(s)
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
# Add the server
|
||||
server_utils.add_server(cls.tester)
|
||||
|
||||
# Connect to servers
|
||||
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 databases to connected servers
|
||||
database_utils.add_database(cls.tester, cls.server_connect_response,
|
||||
cls.server_ids)
|
||||
|
||||
# Add schema(s) under connected database(s)
|
||||
schema_utils.add_schemas(cls.tester)
|
||||
|
||||
# Add extension(s) to schema(s)
|
||||
extension_utils.add_extensions(cls.tester)
|
||||
|
||||
# Add foreign data wrapper(s) to extension(s)
|
||||
fdw_utils.add_fdw(cls.tester)
|
||||
|
||||
# Add foreign server(s) to foreign data wrapper
|
||||
fsrv_utils.add_fsrv(cls.tester)
|
||||
|
||||
# Add user mapping(s) to foreign server(s)
|
||||
um_utils.add_um(cls.tester)
|
||||
|
||||
def runTest(self):
|
||||
""" This function delete user mapping under foreign server node. """
|
||||
|
||||
delete_respdata = um_utils.delete_um(self.tester)
|
||||
self.assertTrue(delete_respdata['success'], 1)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
"""
|
||||
This function deletes the added foreign server(s) ,
|
||||
foreign data wrapper(s), extension(s), schema(s), database(s),
|
||||
server(s) and parent id file
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
fsrv_utils.delete_fsrv(cls.tester)
|
||||
fdw_utils.delete_fdw(cls.tester)
|
||||
extension_utils.delete_extension(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()
|
||||
@@ -0,0 +1,120 @@
|
||||
# #################################################################
|
||||
#
|
||||
# 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
|
||||
from regression import test_utils as utils
|
||||
from pgadmin.browser.server_groups.servers.databases.extensions.tests import\
|
||||
utils as extension_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.schemas.tests import \
|
||||
utils as schema_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.tests import \
|
||||
utils as database_utils
|
||||
from pgadmin.browser.server_groups.servers.tests import utils as server_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.foreign_data_wrappers.tests\
|
||||
import utils as fdw_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.foreign_data_wrappers.\
|
||||
foreign_servers.tests import utils as fsrv_utils
|
||||
from . import utils as um_utils
|
||||
|
||||
|
||||
class UserMappingGetTestCase(BaseTestGenerator):
|
||||
"""
|
||||
This class will add user mapping under foreign server node.
|
||||
"""
|
||||
|
||||
scenarios = [
|
||||
# Fetching default URL for user mapping node.
|
||||
('Check user mapping Node', dict(url='/browser/user_mapping/obj/'))
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
"""
|
||||
This function perform the following tasks:
|
||||
1. Add and connect to the test server(s)
|
||||
2. Add database(s) connected to server(s)
|
||||
3. Add schemas to connected database(s)
|
||||
4. Add extension(s) to schema(s)
|
||||
5. Add foreign data wrapper(s) to extension(s)
|
||||
6. Add foreign server(s) to foreign data wrapper(s)
|
||||
7. Add user mapping(s) to foreign server(s)
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
# Add the server
|
||||
server_utils.add_server(cls.tester)
|
||||
|
||||
# Connect to servers
|
||||
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 databases to connected servers
|
||||
database_utils.add_database(cls.tester, cls.server_connect_response,
|
||||
cls.server_ids)
|
||||
|
||||
# Add schema(s) under connected database(s)
|
||||
schema_utils.add_schemas(cls.tester)
|
||||
|
||||
# Add extension(s) to schema(s)
|
||||
extension_utils.add_extensions(cls.tester)
|
||||
|
||||
# Add foreign data wrapper(s) to extension(s)
|
||||
fdw_utils.add_fdw(cls.tester)
|
||||
|
||||
# Add foreign server(s) to foreign data wrapper
|
||||
fsrv_utils.add_fsrv(cls.tester)
|
||||
|
||||
# Add user mapping(s) to foreign server(s)
|
||||
um_utils.add_um(cls.tester)
|
||||
|
||||
def runTest(self):
|
||||
""" This function will fetch user mapping added to foreign server
|
||||
node. """
|
||||
|
||||
all_id = utils.get_ids()
|
||||
server_ids = all_id["sid"]
|
||||
db_ids_dict = all_id["did"][0]
|
||||
fdw_ids_dict = all_id["fid"][0]
|
||||
fsrv_ids_dict = all_id["fsid"][0]
|
||||
um_ids_dict = all_id["umid"][0]
|
||||
|
||||
for server_id in server_ids:
|
||||
db_id = db_ids_dict[int(server_id)]
|
||||
fdw_id = fdw_ids_dict[server_id]
|
||||
fsrv_id = fsrv_ids_dict[server_id]
|
||||
um_id = um_ids_dict[server_id]
|
||||
|
||||
response = um_utils.verify_um(self.tester, utils.SERVER_GROUP,
|
||||
server_id, db_id,
|
||||
fdw_id, fsrv_id, um_id)
|
||||
|
||||
self.assertEquals(response.status_code, 200)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
"""
|
||||
This function delete the added foreign server(s) ,
|
||||
foreign data wrapper(s), extension(s), schema(s), database(s),
|
||||
server(s) and parent id file
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
um_utils.delete_um(cls.tester)
|
||||
fsrv_utils.delete_fsrv(cls.tester)
|
||||
fdw_utils.delete_fdw(cls.tester)
|
||||
extension_utils.delete_extension(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()
|
||||
@@ -0,0 +1,138 @@
|
||||
# #################################################################
|
||||
#
|
||||
# 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
|
||||
from regression import test_utils as utils
|
||||
from pgadmin.browser.server_groups.servers.databases.extensions.tests import\
|
||||
utils as extension_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.schemas.tests import \
|
||||
utils as schema_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.tests import \
|
||||
utils as database_utils
|
||||
from pgadmin.browser.server_groups.servers.tests import utils as server_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.foreign_data_wrappers.tests \
|
||||
import utils as fdw_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.foreign_data_wrappers.\
|
||||
foreign_servers.tests import utils as fsrv_utils
|
||||
from . import utils as um_utils
|
||||
from regression.test_setup import advanced_config_data
|
||||
import json
|
||||
|
||||
|
||||
class UserMappingPutTestCase(BaseTestGenerator):
|
||||
"""
|
||||
This class will update user mapping under foreign server node.
|
||||
"""
|
||||
|
||||
scenarios = [
|
||||
# Fetching default URL for user mapping node.
|
||||
('Check user mapping Node', dict(url='/browser/user_mapping/obj/'))
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
"""
|
||||
This function perform the following tasks:
|
||||
1. Add and connect to the test server(s)
|
||||
2. Add database(s) connected to server(s)
|
||||
3. Add schemas to connected database(s)
|
||||
4. Add extension(s) to schema(s)
|
||||
5. Add foreign data wrapper(s) to extension(s)
|
||||
6. Add foreign server(s) to foreign data wrapper(s)
|
||||
7. Add user mapping(s) to foreign server(s)
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
# Add the server
|
||||
server_utils.add_server(cls.tester)
|
||||
|
||||
# Connect to servers
|
||||
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 databases to connected servers
|
||||
database_utils.add_database(cls.tester, cls.server_connect_response,
|
||||
cls.server_ids)
|
||||
|
||||
# Add schema(s) under connected database(s)
|
||||
schema_utils.add_schemas(cls.tester)
|
||||
|
||||
# Add extension(s) to schema(s)
|
||||
extension_utils.add_extensions(cls.tester)
|
||||
|
||||
# Add foreign data wrapper(s) to extension(s)
|
||||
fdw_utils.add_fdw(cls.tester)
|
||||
|
||||
# Add foreign server(s) to foreign data wrapper
|
||||
fsrv_utils.add_fsrv(cls.tester)
|
||||
|
||||
# Add user mapping(s) to foreign server(s)
|
||||
um_utils.add_um(cls.tester)
|
||||
|
||||
def runTest(self):
|
||||
""" This function update user mapping under foreign server node. """
|
||||
|
||||
all_id = utils.get_ids()
|
||||
server_ids = all_id["sid"]
|
||||
db_ids_dict = all_id["did"][0]
|
||||
fdw_ids_dict = all_id["fid"][0]
|
||||
fsrv_ids_dict = all_id["fsid"][0]
|
||||
um_ids_dict = all_id["umid"][0]
|
||||
|
||||
for server_id in server_ids:
|
||||
db_id = db_ids_dict[int(server_id)]
|
||||
fdw_id = fdw_ids_dict[server_id]
|
||||
fsrv_id = fsrv_ids_dict[server_id]
|
||||
um_id = um_ids_dict[server_id]
|
||||
|
||||
response = um_utils.verify_um(self.tester, utils.SERVER_GROUP,
|
||||
server_id, db_id,
|
||||
fdw_id, fsrv_id, um_id)
|
||||
if response.status_code == 200:
|
||||
|
||||
data = \
|
||||
{
|
||||
"id": um_id,
|
||||
"umoptions":
|
||||
advanced_config_data['user_mapping_update_data']
|
||||
['options']
|
||||
}
|
||||
|
||||
put_response = self.tester.put(
|
||||
self.url + str(utils.SERVER_GROUP) + '/' +
|
||||
str(server_id) + '/' + str(db_id) +
|
||||
'/' + str(fdw_id) + '/' +
|
||||
str(fsrv_id) + '/' + str(um_id),
|
||||
data=json.dumps(data),
|
||||
follow_redirects=True)
|
||||
|
||||
self.assertEquals(put_response.status_code, 200)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
"""
|
||||
This function deletes the added foreign server(s) ,
|
||||
foreign data wrapper(s), extension(s), schema(s), database(s),
|
||||
server(s) and parent id file
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
um_utils.delete_um(cls.tester)
|
||||
fsrv_utils.delete_fsrv(cls.tester)
|
||||
fdw_utils.delete_fdw(cls.tester)
|
||||
extension_utils.delete_extension(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()
|
||||
@@ -0,0 +1,156 @@
|
||||
# #################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2016, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
# ##################################################################
|
||||
|
||||
import os
|
||||
import pickle
|
||||
import json
|
||||
from regression.test_setup import advanced_config_data, pickle_path
|
||||
from regression import test_utils as utils
|
||||
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.foreign_data_wrappers.tests \
|
||||
import utils as fdw_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.foreign_data_wrappers.\
|
||||
foreign_servers.tests import utils as fsrv_utils
|
||||
|
||||
|
||||
UM_URL = '/browser/user_mapping/obj/'
|
||||
|
||||
|
||||
def get_um_config_data(server_connect_data):
|
||||
adv_config_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['user_mapping_credentials']:
|
||||
if db_user == config_test_data['owner']:
|
||||
adv_config_data = config_test_data
|
||||
|
||||
data = {
|
||||
"name": adv_config_data['name'],
|
||||
"um_options": adv_config_data['option'],
|
||||
"umoptions": adv_config_data['options']
|
||||
}
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def add_um(tester):
|
||||
|
||||
all_id = utils.get_ids()
|
||||
server_ids = all_id["sid"]
|
||||
db_ids_dict = all_id["did"][0]
|
||||
fdw_ids_dict = all_id["fid"][0]
|
||||
fsrv_ids_dict = all_id["fsid"][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']:
|
||||
server_connect_response = server_utils.verify_server(
|
||||
tester, utils.SERVER_GROUP, server_id)
|
||||
|
||||
fdw_id = fdw_ids_dict[server_id]
|
||||
|
||||
fdw_response = fdw_utils.verify_fdws(tester,
|
||||
utils.SERVER_GROUP,
|
||||
server_id, db_id,
|
||||
fdw_id)
|
||||
|
||||
fsrv_id = fsrv_ids_dict[server_id]
|
||||
|
||||
fsrv_response = fsrv_utils.verify_fsrv(tester, utils.SERVER_GROUP,
|
||||
server_id, db_id,
|
||||
fdw_id, fsrv_id)
|
||||
|
||||
if fsrv_response.status_code == 200:
|
||||
data = get_um_config_data(server_connect_response)
|
||||
|
||||
response = tester.post(
|
||||
UM_URL + str(utils.SERVER_GROUP) + '/' +
|
||||
str(server_id) + '/' + str(
|
||||
db_id) +
|
||||
'/' + str(fdw_id) + '/' + str(fsrv_id) + '/',
|
||||
data=json.dumps(data),
|
||||
content_type='html/json')
|
||||
|
||||
assert response.status_code == 200
|
||||
|
||||
response_data = json.loads(response.data.decode())
|
||||
write_um_info(response_data, server_id)
|
||||
|
||||
|
||||
def write_um_info(response_data, server_id):
|
||||
"""
|
||||
This function writes the schema id into parent_id.pkl
|
||||
|
||||
:param response_data: foreign server add response data
|
||||
:type response_data: dict
|
||||
:param server_id: server id
|
||||
:type server_id: str
|
||||
:return: None
|
||||
"""
|
||||
|
||||
um_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 'umid' in pickle_id_dict:
|
||||
if pickle_id_dict['umid']:
|
||||
# Add the umid as value in dict
|
||||
pickle_id_dict["umid"][0].update({server_id: um_id})
|
||||
else:
|
||||
# Create new dict with server_id and umid
|
||||
pickle_id_dict["umid"].append({server_id: um_id})
|
||||
fsrv_output = open(pickle_path, 'wb')
|
||||
pickle.dump(pickle_id_dict, fsrv_output)
|
||||
fsrv_output.close()
|
||||
|
||||
|
||||
def verify_um(tester, server_group, server_id, db_id, fdw_id, fsrv_id, um_id):
|
||||
response = tester.get(UM_URL + str(server_group) + '/' +
|
||||
str(server_id) + '/' + str(db_id) +
|
||||
'/' + str(fdw_id) + '/' + str(fsrv_id) + '/' + str(
|
||||
um_id),
|
||||
content_type='html/json')
|
||||
return response
|
||||
|
||||
|
||||
def delete_um(tester):
|
||||
all_id = utils.get_ids()
|
||||
server_ids = all_id["sid"]
|
||||
db_ids_dict = all_id["did"][0]
|
||||
fdw_ids_dict = all_id["fid"][0]
|
||||
fsrv_ids_dict = all_id["fsid"][0]
|
||||
um_ids_dict = all_id["umid"][0]
|
||||
|
||||
for server_id in server_ids:
|
||||
db_id = db_ids_dict[int(server_id)]
|
||||
fdw_id = fdw_ids_dict[server_id]
|
||||
fsrv_id = fsrv_ids_dict[server_id]
|
||||
um_id = um_ids_dict[server_id]
|
||||
|
||||
response = verify_um(tester, utils.SERVER_GROUP,
|
||||
server_id, db_id,
|
||||
fdw_id, fsrv_id, um_id)
|
||||
if response.status_code == 200:
|
||||
delete_response = tester.delete(
|
||||
UM_URL + str(utils.SERVER_GROUP) + '/' +
|
||||
str(server_id) + '/' + str(db_id) +
|
||||
'/' + str(fdw_id) + '/' +
|
||||
str(fsrv_id) + '/' + str(um_id),
|
||||
follow_redirects=True)
|
||||
delete_respdata = json.loads(delete_response.data.decode())
|
||||
return delete_respdata
|
||||
@@ -1,4 +1,4 @@
|
||||
SELECT 'fdwacl' as deftype, COALESCE(gt.rolname, 'public') grantee, g.rolname grantor, array_agg(privilege_type) as privileges, array_agg(is_grantable) as grantable
|
||||
SELECT 'fdwacl' as deftype, COALESCE(gt.rolname, 'PUBLIC') grantee, g.rolname grantor, array_agg(privilege_type) as privileges, array_agg(is_grantable) as grantable
|
||||
FROM
|
||||
(SELECT
|
||||
d.grantee, d.grantor, d.is_grantable,
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
{% if data.name %}
|
||||
CREATE FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }}{% if data.fdwvalue %}
|
||||
|
||||
VALIDATOR {{ conn|qtIdent(data.fdwvalue) }}{%endif%}{% if data.fdwhan %}
|
||||
VALIDATOR {{ data.fdwvalue }}{%endif%}{% if data.fdwhan %}
|
||||
|
||||
HANDLER {{ conn|qtIdent(data.fdwhan) }}{% endif %}{% if data.fdwoptions %}
|
||||
HANDLER {{ data.fdwhan }}{% endif %}{% if data.fdwoptions %}
|
||||
|
||||
{% set addAlter = "False" %}
|
||||
{% for variable in data.fdwoptions %}
|
||||
|
||||
@@ -1,2 +1,5 @@
|
||||
{# ============= Get the handlers of foreign data wrapper ============= #}
|
||||
SELECT proname as fdwhan FROM pg_proc p JOIN pg_namespace nsp ON nsp.oid=pronamespace WHERE pronargs=0 AND prorettype=3115;
|
||||
SELECT nspname, proname as fdwhan,
|
||||
quote_ident(nspname)||'.'||quote_ident(proname) AS schema_prefix_fdw_hand
|
||||
FROM pg_proc p JOIN pg_namespace nsp ON nsp.oid=pronamespace
|
||||
WHERE pronargs=0 AND prorettype=3115;
|
||||
@@ -1,9 +1,13 @@
|
||||
{# ============= Get all the properties of foreign data wrapper ============= #}
|
||||
SELECT fdw.oid as fdwoid, fdwname as name, fdwhandler, fdwvalidator, vh.proname as fdwhan, vp.proname as fdwvalue, description,
|
||||
array_to_string(fdwoptions, ',') AS fdwoptions, pg_get_userbyid(fdwowner) as fdwowner, array_to_string(fdwacl::text[], ', ') as acl
|
||||
SELECT fdw.oid as fdwoid, fdwname as name, fdwhandler, fdwvalidator, description,
|
||||
array_to_string(fdwoptions, ',') AS fdwoptions, pg_get_userbyid(fdwowner) as fdwowner, array_to_string(fdwacl::text[], ', ') as acl,
|
||||
quote_ident(vp_nsp.nspname)||'.'||quote_ident(vp.proname) AS fdwvalue,
|
||||
quote_ident(vh_nsp.nspname)||'.'||quote_ident(vh.proname) AS fdwhan
|
||||
FROM pg_foreign_data_wrapper fdw
|
||||
LEFT OUTER JOIN pg_proc vh on vh.oid=fdwhandler
|
||||
LEFT OUTER JOIN pg_proc vp on vp.oid=fdwvalidator
|
||||
LEFT OUTER JOIN pg_namespace vh_nsp ON vh_nsp.oid=vh.pronamespace
|
||||
LEFT OUTER JOIN pg_namespace vp_nsp ON vp_nsp.oid=vp.pronamespace
|
||||
LEFT OUTER JOIN pg_description des ON (des.objoid=fdw.oid AND des.objsubid=0 AND des.classoid='pg_foreign_data_wrapper'::regclass)
|
||||
{% if fid %}
|
||||
WHERE fdw.oid={{fid}}::int
|
||||
@@ -11,4 +15,4 @@ WHERE fdw.oid={{fid}}::int
|
||||
{% if fname %}
|
||||
WHERE fdw.fdwname={{ fname|qtLiteral }}::text
|
||||
{% endif %}
|
||||
ORDER BY fdwname
|
||||
ORDER BY fdwname
|
||||
@@ -15,7 +15,7 @@ ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }}
|
||||
{# ============= Update foreign data wrapper validator ============= #}
|
||||
{% if data.fdwvalue and data.fdwvalue != o_data.fdwvalue %}
|
||||
ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }}
|
||||
VALIDATOR {{ conn|qtIdent(data.fdwvalue) }};
|
||||
VALIDATOR {{ data.fdwvalue }};
|
||||
|
||||
{% endif %}
|
||||
{% if data.fdwvalue == '' and data.fdwvalue != o_data.fdwvalue %}
|
||||
@@ -26,7 +26,7 @@ ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }}
|
||||
{# ============= Update foreign data wrapper handler ============= #}
|
||||
{% if data.fdwhan and data.fdwhan != o_data.fdwhan %}
|
||||
ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }}
|
||||
HANDLER {{ conn|qtIdent(data.fdwhan) }};
|
||||
HANDLER {{ data.fdwhan }};
|
||||
|
||||
{% endif %}
|
||||
{% if data.fdwhan == '' and data.fdwhan != o_data.fdwhan %}
|
||||
|
||||
@@ -1,2 +1,5 @@
|
||||
{# ============= Get the validators of foreign data wrapper ============= #}
|
||||
SELECT proname as fdwvalue FROM pg_proc p JOIN pg_namespace nsp ON nsp.oid=pronamespace WHERE proargtypes[0]=1009 AND proargtypes[1]=26;
|
||||
SELECT nspname, proname as fdwvalue,
|
||||
quote_ident(nspname)||'.'||quote_ident(proname) AS schema_prefix_fdw_val
|
||||
FROM pg_proc p JOIN pg_namespace nsp ON nsp.oid=pronamespace
|
||||
WHERE proargtypes[0]=1009 AND proargtypes[1]=26;
|
||||
@@ -1,4 +1,4 @@
|
||||
SELECT 'fdwacl' as deftype, COALESCE(gt.rolname, 'public') grantee, g.rolname grantor, array_agg(privilege_type) as privileges, array_agg(is_grantable) as grantable
|
||||
SELECT 'fdwacl' as deftype, COALESCE(gt.rolname, 'PUBLIC') grantee, g.rolname grantor, array_agg(privilege_type) as privileges, array_agg(is_grantable) as grantable
|
||||
FROM
|
||||
(SELECT
|
||||
d.grantee, d.grantor, d.is_grantable,
|
||||
@@ -18,4 +18,4 @@ FROM
|
||||
) d
|
||||
LEFT JOIN pg_catalog.pg_roles g ON (d.grantor = g.oid)
|
||||
LEFT JOIN pg_catalog.pg_roles gt ON (d.grantee = gt.oid)
|
||||
GROUP BY g.rolname, gt.rolname
|
||||
GROUP BY g.rolname, gt.rolname
|
||||
@@ -3,9 +3,9 @@
|
||||
{% if data.name %}
|
||||
CREATE FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }}{% if data.fdwvalue %}
|
||||
|
||||
VALIDATOR {{ conn|qtIdent(data.fdwvalue) }}{%endif%}{% if data.fdwhan %}
|
||||
VALIDATOR {{ data.fdwvalue }}{%endif%}{% if data.fdwhan %}
|
||||
|
||||
HANDLER {{ conn|qtIdent(data.fdwhan) }}{% endif %}{% if data.fdwoptions %}
|
||||
HANDLER {{ data.fdwhan }}{% endif %}{% if data.fdwoptions %}
|
||||
|
||||
{% set addAlter = "False" %}
|
||||
{% for variable in data.fdwoptions %}
|
||||
|
||||
@@ -1,2 +1,5 @@
|
||||
{# ============= Get the handlers of foreign data wrapper ============= #}
|
||||
SELECT proname as fdwhan FROM pg_proc p JOIN pg_namespace nsp ON nsp.oid=pronamespace WHERE pronargs=0 AND prorettype=3115;
|
||||
SELECT nspname, proname as fdwhan,
|
||||
quote_ident(nspname)||'.'||quote_ident(proname) AS schema_prefix_fdw_hand
|
||||
FROM pg_proc p JOIN pg_namespace nsp ON nsp.oid=pronamespace
|
||||
WHERE pronargs=0 AND prorettype=3115;
|
||||
@@ -1,9 +1,13 @@
|
||||
{# ============= Get all the properties of foreign data wrapper ============= #}
|
||||
SELECT fdw.oid as fdwoid, fdwname as name, fdwhandler, fdwvalidator, vh.proname as fdwhan, vp.proname as fdwvalue, description,
|
||||
array_to_string(fdwoptions, ',') AS fdwoptions, pg_get_userbyid(fdwowner) as fdwowner, array_to_string(fdwacl::text[], ', ') as acl
|
||||
SELECT fdw.oid as fdwoid, fdwname as name, fdwhandler, fdwvalidator, description,
|
||||
array_to_string(fdwoptions, ',') AS fdwoptions, pg_get_userbyid(fdwowner) as fdwowner, array_to_string(fdwacl::text[], ', ') as acl,
|
||||
quote_ident(vp_nsp.nspname)||'.'||quote_ident(vp.proname) AS fdwvalue,
|
||||
quote_ident(vh_nsp.nspname)||'.'||quote_ident(vh.proname) AS fdwhan
|
||||
FROM pg_foreign_data_wrapper fdw
|
||||
LEFT OUTER JOIN pg_proc vh on vh.oid=fdwhandler
|
||||
LEFT OUTER JOIN pg_proc vp on vp.oid=fdwvalidator
|
||||
LEFT OUTER JOIN pg_namespace vh_nsp ON vh_nsp.oid=vh.pronamespace
|
||||
LEFT OUTER JOIN pg_namespace vp_nsp ON vp_nsp.oid=vp.pronamespace
|
||||
LEFT OUTER JOIN pg_description des ON (des.objoid=fdw.oid AND des.objsubid=0 AND des.classoid='pg_foreign_data_wrapper'::regclass)
|
||||
{% if fid %}
|
||||
WHERE fdw.oid={{fid}}::int
|
||||
|
||||
@@ -15,7 +15,7 @@ ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }}
|
||||
{# ============= Update foreign data wrapper validator ============= #}
|
||||
{% if data.fdwvalue and data.fdwvalue != o_data.fdwvalue %}
|
||||
ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }}
|
||||
VALIDATOR {{ conn|qtIdent(data.fdwvalue) }};
|
||||
VALIDATOR {{ data.fdwvalue }};
|
||||
|
||||
{% endif %}
|
||||
{% if data.fdwvalue == '' and data.fdwvalue != o_data.fdwvalue %}
|
||||
@@ -26,7 +26,7 @@ ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }}
|
||||
{# ============= Update foreign data wrapper handler ============= #}
|
||||
{% if data.fdwhan and data.fdwhan != o_data.fdwhan %}
|
||||
ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }}
|
||||
HANDLER {{ conn|qtIdent(data.fdwhan) }};
|
||||
HANDLER {{ data.fdwhan }};
|
||||
|
||||
{% endif %}
|
||||
{% if data.fdwhan == '' and data.fdwhan != o_data.fdwhan %}
|
||||
|
||||
@@ -1,2 +1,5 @@
|
||||
{# ============= Get the validators of foreign data wrapper ============= #}
|
||||
SELECT proname as fdwvalue FROM pg_proc p JOIN pg_namespace nsp ON nsp.oid=pronamespace WHERE proargtypes[0]=1009 AND proargtypes[1]=26;
|
||||
SELECT nspname, proname as fdwvalue,
|
||||
quote_ident(nspname)||'.'||quote_ident(proname) AS schema_prefix_fdw_val
|
||||
FROM pg_proc p JOIN pg_namespace nsp ON nsp.oid=pronamespace
|
||||
WHERE proargtypes[0]=1009 AND proargtypes[1]=26;
|
||||
@@ -0,0 +1,16 @@
|
||||
# #################################################################
|
||||
#
|
||||
# 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 FDWGeneratorTestCase(BaseTestGenerator):
|
||||
|
||||
def runTest(self):
|
||||
return []
|
||||
@@ -0,0 +1,75 @@
|
||||
# #################################################################
|
||||
#
|
||||
# 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
|
||||
from regression import test_utils as utils
|
||||
from pgadmin.browser.server_groups.servers.databases.extensions.tests import\
|
||||
utils as extension_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.schemas.tests import \
|
||||
utils as schema_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.tests import \
|
||||
utils as database_utils
|
||||
from pgadmin.browser.server_groups.servers.tests import utils as server_utils
|
||||
from . import utils as fdw_utils
|
||||
|
||||
|
||||
class FDWDAddTestCase(BaseTestGenerator):
|
||||
""" This class will add foreign data wrappers under database node. """
|
||||
|
||||
scenarios = [
|
||||
# Fetching default URL for foreign_data_wrapper node.
|
||||
('Check FDW Node',
|
||||
dict(url='/browser/foreign_data_wrapper/obj/'))
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
"""
|
||||
This function perform the following tasks:
|
||||
1. Add and connect to the test server(s)
|
||||
2. Add database(s) connected to server(s)
|
||||
3. Add schema(s) to connected database(s)
|
||||
4. Add extension(s) to schema(s)
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
# Add the server
|
||||
server_utils.add_server(cls.tester)
|
||||
|
||||
# Connect to servers
|
||||
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 databases to connected servers
|
||||
database_utils.add_database(cls.tester, cls.server_connect_response,
|
||||
cls.server_ids)
|
||||
schema_utils.add_schemas(cls.tester)
|
||||
|
||||
extension_utils.add_extensions(cls.tester)
|
||||
|
||||
def runTest(self):
|
||||
""" This function will add extension under 1st server of tree node. """
|
||||
fdw_utils.add_fdw(self.tester)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
"""This function deletes the added schema, database, server and parent
|
||||
id file
|
||||
"""
|
||||
|
||||
fdw_utils.delete_fdw(cls.tester)
|
||||
extension_utils.delete_extension(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()
|
||||
@@ -0,0 +1,79 @@
|
||||
#################################################################
|
||||
#
|
||||
# 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
|
||||
from regression import test_utils as utils
|
||||
from pgadmin.browser.server_groups.servers.databases.extensions.tests import\
|
||||
utils as extension_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.schemas.tests import \
|
||||
utils as schema_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.tests import \
|
||||
utils as database_utils
|
||||
from pgadmin.browser.server_groups.servers.tests import utils as server_utils
|
||||
from . import utils as fdw_utils
|
||||
import json
|
||||
|
||||
|
||||
class FDWDDeleteTestCase(BaseTestGenerator):
|
||||
""" This class will delete foreign data wrappers under database node. """
|
||||
|
||||
scenarios = [
|
||||
# Fetching default URL for foreign_data_wrapper node.
|
||||
('Check FDW Node',
|
||||
dict(url='/browser/foreign_data_wrapper/obj/'))
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
"""
|
||||
This function perform the following tasks:
|
||||
1. Add and connect to the test server(s)
|
||||
2. Add database(s) connected to server(s)
|
||||
3. Add schemas to connected database(s)
|
||||
4. Add extension(s) to schema(s)
|
||||
5. Add foreign data wrapper(s) to extension(s)
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
# Add the server
|
||||
server_utils.add_server(cls.tester)
|
||||
|
||||
# Connect to servers
|
||||
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 databases to connected servers
|
||||
database_utils.add_database(cls.tester, cls.server_connect_response,
|
||||
cls.server_ids)
|
||||
schema_utils.add_schemas(cls.tester)
|
||||
|
||||
extension_utils.add_extensions(cls.tester)
|
||||
|
||||
fdw_utils.add_fdw(cls.tester)
|
||||
|
||||
def runTest(self):
|
||||
""" This function will delete added FDW. """
|
||||
|
||||
delete_respdata = fdw_utils.delete_fdw(self.tester)
|
||||
self.assertTrue(delete_respdata['success'], 1)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
"""This function deletes the added schema, database, server and parent
|
||||
id file
|
||||
"""
|
||||
extension_utils.delete_extension(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()
|
||||
@@ -0,0 +1,83 @@
|
||||
# #################################################################
|
||||
#
|
||||
# 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
|
||||
from regression import test_utils as utils
|
||||
from pgadmin.browser.server_groups.servers.databases.extensions.tests import\
|
||||
utils as extension_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.schemas.tests import \
|
||||
utils as schema_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.tests import \
|
||||
utils as database_utils
|
||||
from pgadmin.browser.server_groups.servers.tests import utils as server_utils
|
||||
from . import utils as fdw_utils
|
||||
|
||||
|
||||
class FDWDGetTestCase(BaseTestGenerator):
|
||||
""" This class will add foreign data wrappers under database node. """
|
||||
|
||||
scenarios = [
|
||||
# Fetching default URL for foreign_data_wrapper node.
|
||||
('Check FDW Node',
|
||||
dict(url='/browser/foreign_data_wrapper/obj/'))
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
"""This function use to add/connect the servers and create databases"""
|
||||
|
||||
# Add the server
|
||||
server_utils.add_server(cls.tester)
|
||||
|
||||
# Connect to servers
|
||||
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 databases to connected servers
|
||||
database_utils.add_database(cls.tester, cls.server_connect_response,
|
||||
cls.server_ids)
|
||||
schema_utils.add_schemas(cls.tester)
|
||||
|
||||
extension_utils.add_extensions(cls.tester)
|
||||
|
||||
fdw_utils.add_fdw(cls.tester)
|
||||
|
||||
def runTest(self):
|
||||
""" This function will get added FDW. """
|
||||
|
||||
all_id = utils.get_ids()
|
||||
server_ids = all_id["sid"]
|
||||
db_ids_dict = all_id["did"][0]
|
||||
fdw_ids_dict = all_id["fid"][0]
|
||||
|
||||
for server_id in server_ids:
|
||||
db_id = db_ids_dict[int(server_id)]
|
||||
fdw_id = fdw_ids_dict[server_id]
|
||||
|
||||
response = fdw_utils.verify_fdws(self.tester,
|
||||
utils.SERVER_GROUP,
|
||||
server_id, db_id,
|
||||
fdw_id)
|
||||
|
||||
self.assertEquals(response.status_code, 200)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
"""This function deletes the added schema, database, server and parent
|
||||
id file
|
||||
"""
|
||||
fdw_utils.delete_fdw(cls.tester)
|
||||
extension_utils.delete_extension(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()
|
||||
@@ -0,0 +1,101 @@
|
||||
# #################################################################
|
||||
#
|
||||
# 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
|
||||
from regression import test_utils as utils
|
||||
from pgadmin.browser.server_groups.servers.databases.extensions.tests import\
|
||||
utils as extension_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.schemas.tests import \
|
||||
utils as schema_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.tests import \
|
||||
utils as database_utils
|
||||
from pgadmin.browser.server_groups.servers.tests import utils as server_utils
|
||||
from . import utils as fdw_utils
|
||||
from regression.test_setup import advanced_config_data
|
||||
import json
|
||||
|
||||
|
||||
class FDWDPutTestCase(BaseTestGenerator):
|
||||
""" This class will add foreign data wrappers under database node. """
|
||||
|
||||
scenarios = [
|
||||
# Fetching default URL for foreign_data_wrapper node.
|
||||
('Check FDW Node',
|
||||
dict(url='/browser/foreign_data_wrapper/obj/'))
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
"""This function use to add/connect the servers and create databases"""
|
||||
|
||||
# Add the server
|
||||
server_utils.add_server(cls.tester)
|
||||
|
||||
# Connect to servers
|
||||
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 databases to connected servers
|
||||
database_utils.add_database(cls.tester, cls.server_connect_response,
|
||||
cls.server_ids)
|
||||
schema_utils.add_schemas(cls.tester)
|
||||
|
||||
extension_utils.add_extensions(cls.tester)
|
||||
|
||||
fdw_utils.add_fdw(cls.tester)
|
||||
|
||||
def runTest(self):
|
||||
""" This function will update added FDW. """
|
||||
|
||||
all_id = utils.get_ids()
|
||||
server_ids = all_id["sid"]
|
||||
db_ids_dict = all_id["did"][0]
|
||||
fdw_ids_dict = all_id["fid"][0]
|
||||
|
||||
for server_id in server_ids:
|
||||
db_id = db_ids_dict[int(server_id)]
|
||||
fdw_id = fdw_ids_dict[server_id]
|
||||
|
||||
response = fdw_utils.verify_fdws(self.tester,
|
||||
utils.SERVER_GROUP,
|
||||
server_id, db_id,
|
||||
fdw_id)
|
||||
|
||||
if response.status_code == 200:
|
||||
data = \
|
||||
{
|
||||
"description": advanced_config_data['fdw_update_data']
|
||||
['comment'],
|
||||
"id": fdw_id
|
||||
}
|
||||
|
||||
put_response = self.tester.put(
|
||||
self.url + str(utils.SERVER_GROUP) + '/' +
|
||||
str(server_id) + '/' +
|
||||
str(db_id) + '/' + str(fdw_id),
|
||||
data=json.dumps(data),
|
||||
follow_redirects=True)
|
||||
|
||||
self.assertEquals(put_response.status_code, 200)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
"""
|
||||
This function deletes the added schema, database, server and parent
|
||||
id file
|
||||
"""
|
||||
fdw_utils.delete_fdw(cls.tester)
|
||||
extension_utils.delete_extension(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()
|
||||
@@ -0,0 +1,133 @@
|
||||
# #################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2016, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
# ##################################################################
|
||||
|
||||
import os
|
||||
import pickle
|
||||
import json
|
||||
from regression.test_setup import advanced_config_data, pickle_path
|
||||
from regression import test_utils as utils
|
||||
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
|
||||
import uuid
|
||||
|
||||
FDW_URL = '/browser/foreign_data_wrapper/obj/'
|
||||
|
||||
|
||||
def get_fdw_config_data(schema_name, server_connect_data):
|
||||
adv_config_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['fdw_credentials']:
|
||||
if db_user == config_test_data['owner']:
|
||||
adv_config_data = config_test_data
|
||||
|
||||
data = {
|
||||
"fdwacl": adv_config_data['acl'],
|
||||
"fdwhan": "{0}.{1}".format(schema_name, adv_config_data['handler']),
|
||||
"fdwoptions": adv_config_data['options'],
|
||||
"fdwowner": adv_config_data['owner'],
|
||||
"fdwvalue": "{0}.{1}".format(schema_name, adv_config_data['validator']),
|
||||
"name": "fdw_{}".format(str(uuid.uuid4())[1:4])
|
||||
}
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def add_fdw(tester):
|
||||
|
||||
all_id = utils.get_ids()
|
||||
server_ids = all_id["sid"]
|
||||
db_ids_dict = all_id["did"][0]
|
||||
schema_info_dict = all_id["scid"][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"]:
|
||||
server_connect_response = server_utils.verify_server(
|
||||
tester, utils.SERVER_GROUP, server_id)
|
||||
|
||||
schema_name = schema_info_dict[int(server_id)][1]
|
||||
data = get_fdw_config_data(schema_name,
|
||||
server_connect_response)
|
||||
|
||||
response = tester.post(FDW_URL + str(utils.SERVER_GROUP) + '/' +
|
||||
str(server_id) + '/' + str(db_id) + '/',
|
||||
data=json.dumps(data),
|
||||
content_type='html/json')
|
||||
assert response.status_code == 200
|
||||
response_data = json.loads(response.data.decode('utf-8'))
|
||||
write_fdw_info(response_data, server_id)
|
||||
|
||||
|
||||
def write_fdw_info(response_data, server_id):
|
||||
"""
|
||||
This function writes the sequence id into parent_id.pkl
|
||||
|
||||
:param response_data: FDW add response data
|
||||
:type response_data: dict
|
||||
:param server_id: server id
|
||||
:type server_id: str
|
||||
:return: None
|
||||
"""
|
||||
|
||||
fdw_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 'fid' in pickle_id_dict:
|
||||
if pickle_id_dict['fid']:
|
||||
# Add the FDW_id as value in dict
|
||||
pickle_id_dict["fid"][0].update({server_id: fdw_id})
|
||||
else:
|
||||
# Create new dict with server_id and FDW_id
|
||||
pickle_id_dict["fid"].append({server_id: fdw_id})
|
||||
fdw_output = open(pickle_path, 'wb')
|
||||
pickle.dump(pickle_id_dict, fdw_output)
|
||||
fdw_output.close()
|
||||
|
||||
|
||||
def verify_fdws(tester, server_group, server_id, db_id, fdw_id):
|
||||
response = tester.get(FDW_URL + str(server_group) + '/' +
|
||||
str(server_id) + '/' + str(db_id) +
|
||||
'/' + str(fdw_id),
|
||||
content_type='html/json')
|
||||
return response
|
||||
|
||||
|
||||
def delete_fdw(tester):
|
||||
all_id = utils.get_ids()
|
||||
server_ids = all_id["sid"]
|
||||
db_ids_dict = all_id["did"][0]
|
||||
fdw_ids_dict = all_id["fid"][0]
|
||||
|
||||
for server_id in server_ids:
|
||||
db_id = db_ids_dict[int(server_id)]
|
||||
fdw_id = fdw_ids_dict[server_id]
|
||||
|
||||
response = verify_fdws(tester,
|
||||
utils.SERVER_GROUP,
|
||||
server_id, db_id,
|
||||
fdw_id)
|
||||
|
||||
if response.status_code == 200:
|
||||
delete_response = tester.delete(
|
||||
FDW_URL + str(utils.SERVER_GROUP) + '/' +
|
||||
str(server_id) + '/' +
|
||||
str(db_id) + '/' + str(fdw_id),
|
||||
follow_redirects=True)
|
||||
|
||||
delete_respdata = json.loads(delete_response.data.decode())
|
||||
|
||||
return delete_respdata
|
||||
@@ -9,20 +9,20 @@
|
||||
|
||||
"""Implements Language Node"""
|
||||
|
||||
import json
|
||||
import simplejson as json
|
||||
from functools import wraps
|
||||
|
||||
import pgadmin.browser.server_groups.servers.databases as databases
|
||||
from flask import render_template, make_response, request
|
||||
from flask.ext.babel import gettext
|
||||
from flask import render_template, make_response, request, jsonify
|
||||
from flask_babel import gettext
|
||||
from pgadmin.browser.collection import CollectionNodeModule
|
||||
from pgadmin.browser.server_groups.servers.utils import parse_priv_from_db, \
|
||||
parse_priv_to_db
|
||||
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.ajax import make_json_response, internal_server_error, \
|
||||
make_response as ajax_response
|
||||
from pgadmin.utils.driver import get_driver
|
||||
from pgadmin.utils.ajax import gone
|
||||
|
||||
from config import PG_DEFAULT_DRIVER
|
||||
|
||||
@@ -133,6 +133,12 @@ class LanguageView(PGChildNodeView):
|
||||
* update(gid, sid, did, lid)
|
||||
- This function will update the data for the selected language node
|
||||
|
||||
* create(gid, sid, did)
|
||||
- This function will create the new language node
|
||||
|
||||
* delete(gid, sid, did, lid)
|
||||
- This function will delete the selected language node
|
||||
|
||||
* msql(gid, sid, did, lid)
|
||||
- This function is used to return modified SQL for the selected language node
|
||||
|
||||
@@ -142,6 +148,9 @@ class LanguageView(PGChildNodeView):
|
||||
* get_functions(gid, sid, did)
|
||||
- This function returns the handler and inline functions for the selected language node
|
||||
|
||||
* get_templates(gid, sid, did)
|
||||
- This function returns language templates.
|
||||
|
||||
* sql(gid, sid, did, lid):
|
||||
- This function will generate sql to show it in sql pane for the selected language node.
|
||||
|
||||
@@ -165,8 +174,8 @@ class LanguageView(PGChildNodeView):
|
||||
|
||||
operations = dict({
|
||||
'obj': [
|
||||
{'get': 'properties', 'put': 'update'},
|
||||
{'get': 'list'}
|
||||
{'get': 'properties', 'delete': 'delete', 'put': 'update'},
|
||||
{'get': 'list', 'post': 'create'}
|
||||
],
|
||||
'nodes': [{'get': 'node'}, {'get': 'nodes'}],
|
||||
'sql': [{'get': 'sql'}],
|
||||
@@ -175,7 +184,9 @@ class LanguageView(PGChildNodeView):
|
||||
'dependency': [{'get': 'dependencies'}],
|
||||
'dependent': [{'get': 'dependents'}],
|
||||
'module.js': [{}, {}, {'get': 'module_js'}],
|
||||
'get_functions': [{}, {'get': 'get_functions'}]
|
||||
'get_functions': [{}, {'get': 'get_functions'}],
|
||||
'get_templates': [{}, {'get': 'get_templates'}],
|
||||
'delete': [{'delete': 'delete'}]
|
||||
})
|
||||
|
||||
def _init_(self, **kwargs):
|
||||
@@ -218,18 +229,8 @@ class LanguageView(PGChildNodeView):
|
||||
self.driver = get_driver(PG_DEFAULT_DRIVER)
|
||||
self.manager = self.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!"
|
||||
)
|
||||
)
|
||||
|
||||
ver = self.manager.version
|
||||
# we will set template path for sql scripts
|
||||
if ver >= 90300:
|
||||
# Set the template path for the SQL scripts
|
||||
if self.manager.version >= 90300:
|
||||
self.template_path = 'languages/sql/9.3_plus'
|
||||
else:
|
||||
self.template_path = 'languages/sql/9.1_plus'
|
||||
@@ -289,6 +290,36 @@ class LanguageView(PGChildNodeView):
|
||||
status=200
|
||||
)
|
||||
|
||||
@check_precondition
|
||||
def node(self, gid, sid, did, lid):
|
||||
"""
|
||||
This function will fetch properties of the language nodes.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
lid: Language ID
|
||||
"""
|
||||
sql = render_template("/".join([self.template_path, 'properties.sql']),
|
||||
lid=lid)
|
||||
status, result = self.conn.execute_2darray(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=result)
|
||||
|
||||
for row in result['rows']:
|
||||
return make_json_response(
|
||||
data=self.blueprint.generate_browser_node(
|
||||
row['oid'],
|
||||
did,
|
||||
row['name'],
|
||||
icon="icon-language"
|
||||
),
|
||||
status=200
|
||||
)
|
||||
|
||||
return gone(gettext("Could not find the specified language."))
|
||||
|
||||
@check_precondition
|
||||
def properties(self, gid, sid, did, lid):
|
||||
"""
|
||||
@@ -306,6 +337,11 @@ class LanguageView(PGChildNodeView):
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
if len(res['rows']) == 0:
|
||||
return gone(
|
||||
gettext("Couldnot find the language information.")
|
||||
)
|
||||
|
||||
sql = render_template("/".join([self.template_path, 'acl.sql']), lid=lid)
|
||||
status, result = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
@@ -314,7 +350,7 @@ class LanguageView(PGChildNodeView):
|
||||
# if no acl found then by default add public
|
||||
if res['rows'][0]['acl'] is None:
|
||||
res['rows'][0]['lanacl'] = dict()
|
||||
res['rows'][0]['lanacl']['grantee'] = 'public'
|
||||
res['rows'][0]['lanacl']['grantee'] = 'PUBLIC'
|
||||
res['rows'][0]['lanacl']['grantor'] = res['rows'][0]['lanowner']
|
||||
res['rows'][0]['lanacl']['privileges'] = [{'privilege_type': 'U', 'privilege': True, 'with_grant': False}]
|
||||
else:
|
||||
@@ -325,6 +361,18 @@ class LanguageView(PGChildNodeView):
|
||||
else:
|
||||
res['rows'][0][row['deftype']] = [priv]
|
||||
|
||||
seclabels = []
|
||||
if 'seclabels' in res['rows'][0] and res['rows'][0]['seclabels'] is not None:
|
||||
import re
|
||||
for sec in res['rows'][0]['seclabels']:
|
||||
sec = re.search(r'([^=]+)=(.*$)', sec)
|
||||
seclabels.append({
|
||||
'provider': sec.group(1),
|
||||
'label': sec.group(2)
|
||||
})
|
||||
|
||||
res['rows'][0]['seclabels'] = seclabels
|
||||
|
||||
return ajax_response(
|
||||
response=res['rows'][0],
|
||||
status=200
|
||||
@@ -341,37 +389,129 @@ class LanguageView(PGChildNodeView):
|
||||
did: Database ID
|
||||
lid: Language ID
|
||||
"""
|
||||
data = request.form if request.form else json.loads(request.data.decode())
|
||||
sql = self.get_sql(data, lid)
|
||||
data = request.form if request.form else json.loads(
|
||||
request.data, encoding='utf-8'
|
||||
)
|
||||
|
||||
try:
|
||||
if sql and sql.strip('\n') and sql.strip(' '):
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
sql, name = self.get_sql(data, lid)
|
||||
sql = sql.strip('\n').strip(' ')
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
return make_json_response(
|
||||
success=1,
|
||||
info="Language updated",
|
||||
data={
|
||||
'id': lid,
|
||||
'did': did,
|
||||
'sid': sid,
|
||||
'gid': gid
|
||||
}
|
||||
return jsonify(
|
||||
node=self.blueprint.generate_browser_node(
|
||||
lid,
|
||||
did,
|
||||
name,
|
||||
icon="icon-%s" % self.node_type
|
||||
)
|
||||
else:
|
||||
)
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def create(self, gid, sid, did):
|
||||
"""
|
||||
This function will create the language object
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
"""
|
||||
required_args = [
|
||||
'name'
|
||||
]
|
||||
|
||||
data = request.form if request.form else json.loads(
|
||||
request.data, encoding='utf-8'
|
||||
)
|
||||
for arg in required_args:
|
||||
if arg not in data:
|
||||
return make_json_response(
|
||||
success=1,
|
||||
info="Nothing to update",
|
||||
data={
|
||||
'id': lid,
|
||||
'did': did,
|
||||
'sid': sid,
|
||||
'gid': gid
|
||||
}
|
||||
status=410,
|
||||
success=0,
|
||||
errormsg=gettext(
|
||||
"Could not find the required parameter (%s)." % arg
|
||||
)
|
||||
)
|
||||
|
||||
try:
|
||||
if 'lanacl' in data:
|
||||
data['lanacl'] = parse_priv_to_db(data['lanacl'], ['U'])
|
||||
|
||||
sql = render_template("/".join([self.template_path, 'create.sql']),
|
||||
data=data, conn=self.conn)
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
sql = render_template("/".join([self.template_path, 'properties.sql']),
|
||||
lanname=data['name'], conn=self.conn)
|
||||
|
||||
status, r_set = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=r_set)
|
||||
|
||||
for row in r_set['rows']:
|
||||
return jsonify(
|
||||
node=self.blueprint.generate_browser_node(
|
||||
row['oid'],
|
||||
did,
|
||||
row['name'],
|
||||
icon='icon-language'
|
||||
)
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def delete(self, gid, sid, did, lid):
|
||||
"""
|
||||
This function will drop the language object
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
lid: Language ID
|
||||
"""
|
||||
if self.cmd == 'delete':
|
||||
# This is a cascade operation
|
||||
cascade = True
|
||||
else:
|
||||
cascade = False
|
||||
|
||||
try:
|
||||
# Get name for language from lid
|
||||
sql = render_template("/".join([self.template_path, 'delete.sql']), lid=lid, conn=self.conn)
|
||||
status, lname = self.conn.execute_scalar(sql)
|
||||
|
||||
if not status:
|
||||
return internal_server_error(errormsg=lname)
|
||||
|
||||
# drop language
|
||||
sql = render_template("/".join([self.template_path, 'delete.sql']), lname=lname,
|
||||
cascade=cascade, 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("Language dropped"),
|
||||
data={
|
||||
'id': lid,
|
||||
'did': did,
|
||||
'sid': sid,
|
||||
'gid': gid,
|
||||
}
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@@ -389,21 +529,20 @@ class LanguageView(PGChildNodeView):
|
||||
data = {}
|
||||
for k, v in request.args.items():
|
||||
try:
|
||||
data[k] = json.loads(v)
|
||||
data[k] = json.loads(v, encoding='utf-8')
|
||||
except ValueError:
|
||||
data[k] = v
|
||||
try:
|
||||
sql, name = self.get_sql(data, lid)
|
||||
if sql == '':
|
||||
sql = "--modified SQL"
|
||||
|
||||
sql = self.get_sql(data, lid)
|
||||
if sql and sql.strip('\n') and sql.strip(' '):
|
||||
return make_json_response(
|
||||
data=sql,
|
||||
status=200
|
||||
)
|
||||
else:
|
||||
return make_json_response(
|
||||
data='-- Modified SQL --',
|
||||
status=200
|
||||
)
|
||||
)
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
def get_sql(self, data, lid=None):
|
||||
"""
|
||||
@@ -416,32 +555,43 @@ class LanguageView(PGChildNodeView):
|
||||
required_args = [
|
||||
'name', 'lanowner', 'description'
|
||||
]
|
||||
try:
|
||||
sql = ''
|
||||
if lid is not None:
|
||||
sql = render_template("/".join([self.template_path, 'properties.sql']), lid=lid)
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
for key in ['lanacl']:
|
||||
if key in data and data[key] is not None:
|
||||
if 'added' in data[key]:
|
||||
data[key]['added'] = parse_priv_to_db(data[key]['added'])
|
||||
if 'changed' in data[key]:
|
||||
data[key]['changed'] = parse_priv_to_db(data[key]['changed'])
|
||||
if 'deleted' in data[key]:
|
||||
data[key]['deleted'] = parse_priv_to_db(data[key]['deleted'])
|
||||
if lid is not None:
|
||||
sql = render_template("/".join([self.template_path, 'properties.sql']), lid=lid)
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
if len(res['rows']) == 0:
|
||||
return gone(
|
||||
gettext("Couldnot find the language information.")
|
||||
)
|
||||
|
||||
for key in ['lanacl']:
|
||||
if key in data and data[key] is not None:
|
||||
if 'added' in data[key]:
|
||||
data[key]['added'] = parse_priv_to_db(data[key]['added'], ["U"])
|
||||
if 'changed' in data[key]:
|
||||
data[key]['changed'] = parse_priv_to_db(data[key]['changed'], ["U"])
|
||||
if 'deleted' in data[key]:
|
||||
data[key]['deleted'] = parse_priv_to_db(data[key]['deleted'], ["U"])
|
||||
|
||||
old_data = res['rows'][0]
|
||||
for arg in required_args:
|
||||
if arg not in data:
|
||||
data[arg] = old_data[arg]
|
||||
sql = render_template("/".join([self.template_path, 'update.sql']), data=data,
|
||||
o_data=old_data, conn=self.conn)
|
||||
return sql.strip('\n'), data['name'] if 'name' in data else old_data['name']
|
||||
else:
|
||||
|
||||
if 'lanacl' in data:
|
||||
data['lanacl'] = parse_priv_to_db(data['lanacl'], ["U"])
|
||||
|
||||
sql = render_template("/".join([self.template_path, 'create.sql']),
|
||||
data=data, conn=self.conn)
|
||||
return sql.strip('\n'), data['name']
|
||||
|
||||
old_data = res['rows'][0]
|
||||
for arg in required_args:
|
||||
if arg not in data:
|
||||
data[arg] = old_data[arg]
|
||||
sql = render_template("/".join([self.template_path, 'update.sql']), data=data,
|
||||
o_data=old_data, conn=self.conn)
|
||||
return sql
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def get_functions(self, gid, sid, did):
|
||||
@@ -462,6 +612,25 @@ class LanguageView(PGChildNodeView):
|
||||
status=200
|
||||
)
|
||||
|
||||
@check_precondition
|
||||
def get_templates(self, gid, sid, did):
|
||||
"""
|
||||
This function returns the language template.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
"""
|
||||
sql = render_template("/".join([self.template_path, 'templates.sql']))
|
||||
status, result = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=result)
|
||||
return make_json_response(
|
||||
data=result['rows'],
|
||||
status=200
|
||||
)
|
||||
|
||||
@check_precondition
|
||||
def sql(self, gid, sid, did, lid):
|
||||
"""
|
||||
@@ -480,9 +649,33 @@ class LanguageView(PGChildNodeView):
|
||||
|
||||
# Making copy of output for future use
|
||||
old_data = dict(res['rows'][0])
|
||||
|
||||
sql = render_template("/".join([self.template_path, 'acl.sql']), lid=lid)
|
||||
status, result = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=result)
|
||||
|
||||
for row in result['rows']:
|
||||
priv = parse_priv_from_db(row)
|
||||
if row['deftype'] in old_data:
|
||||
old_data[row['deftype']].append(priv)
|
||||
else:
|
||||
old_data[row['deftype']] = [priv]
|
||||
|
||||
seclabels = []
|
||||
if 'seclabels' in old_data and old_data['seclabels'] is not None:
|
||||
import re
|
||||
for sec in old_data['seclabels']:
|
||||
sec = re.search(r'([^=]+)=(.*$)', sec)
|
||||
seclabels.append({
|
||||
'provider': sec.group(1),
|
||||
'label': sec.group(2)
|
||||
})
|
||||
|
||||
old_data['seclabels'] = seclabels
|
||||
sql = render_template("/".join([self.template_path, 'sqlpane.sql']), data=old_data, conn=self.conn)
|
||||
|
||||
return ajax_response(response=sql)
|
||||
return ajax_response(response=sql.strip('\n'))
|
||||
|
||||
@check_precondition
|
||||
def dependents(self, gid, sid, did, lid):
|
||||
|
||||
@@ -23,6 +23,8 @@ define(
|
||||
sqlCreateHelp: 'sql-createlanguage.html',
|
||||
label: '{{ _('Language') }}',
|
||||
hasSQL: true,
|
||||
canDrop: true,
|
||||
canDropCascade: true,
|
||||
hasDepends: true,
|
||||
Init: function() {
|
||||
|
||||
@@ -31,6 +33,25 @@ define(
|
||||
return;
|
||||
|
||||
this.initialized = true;
|
||||
|
||||
|
||||
// Add context menus for language
|
||||
pgBrowser.add_menus([{
|
||||
name: 'create_language_on_database', node: 'database', module: this,
|
||||
applies: ['object', 'context'], callback: 'show_obj_properties',
|
||||
category: 'create', priority: 4, label: '{{ _('Language...') }}',
|
||||
icon: 'wcTabIcon icon-language', data: {action: 'create'}
|
||||
},{
|
||||
name: 'create_language_on_coll', node: 'coll-language', module: this,
|
||||
applies: ['object', 'context'], callback: 'show_obj_properties',
|
||||
category: 'create', priority: 4, label: '{{ _('Language...') }}',
|
||||
icon: 'wcTabIcon icon-language', data: {action: 'create'}
|
||||
},{
|
||||
name: 'create_language', node: 'language', module: this,
|
||||
applies: ['object', 'context'], callback: 'show_obj_properties',
|
||||
category: 'create', priority: 4, label: '{{ _('Language...') }}',
|
||||
icon: 'wcTabIcon icon-language', data: {action: 'create'}
|
||||
}]);
|
||||
},
|
||||
|
||||
// Define the model for language node
|
||||
@@ -40,20 +61,59 @@ define(
|
||||
lanowner: undefined,
|
||||
comment: undefined,
|
||||
lanacl: [],
|
||||
seclabels:[]
|
||||
seclabels:[],
|
||||
trusted: true,
|
||||
lanproc: undefined,
|
||||
laninl: undefined,
|
||||
lanval: undefined,
|
||||
is_template: false,
|
||||
template_list: []
|
||||
},
|
||||
|
||||
// Default values!
|
||||
initialize: function(attrs, args) {
|
||||
var isNew = (_.size(attrs) === 0);
|
||||
if (isNew) {
|
||||
var userInfo = pgBrowser.serverInfo[args.node_info.server._id].user;
|
||||
|
||||
this.set({'lanowner': userInfo.name}, {silent: true});
|
||||
}
|
||||
pgBrowser.Node.Model.prototype.initialize.apply(this, arguments);
|
||||
},
|
||||
|
||||
// Define the schema for the language node
|
||||
schema: [{
|
||||
id: 'name', label: '{{ _('Name') }}', cell: 'string',
|
||||
type: 'text',
|
||||
id: 'name', label: '{{ _('Name') }}', type: 'text',
|
||||
control: 'node-ajax-options', mode: ['properties', 'create', 'edit'],
|
||||
url:'get_templates', select2: { allowClear: false, tags: true, multiple: false },
|
||||
transform: function(data, cell) {
|
||||
var res = [],
|
||||
control = cell || this,
|
||||
label = control.model.get('name');
|
||||
|
||||
if (!control.model.isNew()) {
|
||||
res.push({label: label, value: label});
|
||||
}
|
||||
else {
|
||||
var tmp_list = [];
|
||||
if (data && _.isArray(data)) {
|
||||
_.each(data, function(d) {
|
||||
res.push({label: d.tmplname, value: d.tmplname});
|
||||
tmp_list.push(d.tmplname);
|
||||
})
|
||||
}
|
||||
this.model.set({'template_list': tmp_list});
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
},{
|
||||
id: 'oid', label:'{{ _('OID') }}', cell: 'string', mode: ['properties'],
|
||||
type: 'text', disabled: true
|
||||
},{
|
||||
id: 'lanowner', label:'{{ _('Owner') }}', type: 'text',
|
||||
control: Backform.NodeListByNameControl, node: 'role',
|
||||
mode: ['edit', 'properties'], select2: { allowClear: false }
|
||||
mode: ['edit', 'properties', 'create'], select2: { allowClear: false }
|
||||
},{
|
||||
id: 'acl', label: '{{ _('Privileges') }}', type: 'text',
|
||||
group: '{{ _('Security') }}', mode: ['properties'], disabled: true
|
||||
@@ -67,13 +127,22 @@ define(
|
||||
'onColor': 'success', 'offColor': 'primary',
|
||||
'size': 'small'
|
||||
},
|
||||
group: 'Definition', mode: ['edit', 'properties'],
|
||||
group: 'Definition', mode: ['edit', 'properties', 'create'], deps: ['name'],
|
||||
disabled: function(m) {
|
||||
return !(m.isNew());
|
||||
if (m.isNew()) {
|
||||
if (m.get('template_list').indexOf(m.get('name')) == -1) {
|
||||
m.set({'is_template': false});
|
||||
return false;
|
||||
}
|
||||
else
|
||||
m.set({'is_template': true});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
},{
|
||||
id: 'lanproc', label:'{{ _('Handler Function') }}', type: 'text', control: 'node-ajax-options',
|
||||
group: 'Definition', mode: ['edit', 'properties'], url:'get_functions',
|
||||
group: 'Definition', mode: ['edit', 'properties', 'create'], url:'get_functions',
|
||||
deps: ['name'], first_empty: false,
|
||||
/* This function is used to populate the handler function
|
||||
* for the selected language node. It will check if the property
|
||||
* type is 'handler' then push the data into the result array.
|
||||
@@ -81,19 +150,24 @@ define(
|
||||
transform: function(data) {
|
||||
var res = [];
|
||||
if (data && _.isArray(data)) {
|
||||
_.each(data, function(d) {
|
||||
if (d.prop_type == 'handler') {
|
||||
res.push({label: d.label, value: d.label});
|
||||
}
|
||||
})
|
||||
_.each(data, function(d) {
|
||||
if (d.prop_type == 'handler') {
|
||||
res.push({label: d.label, value: d.label});
|
||||
}
|
||||
})
|
||||
}
|
||||
return res;
|
||||
}, disabled: function(m) {
|
||||
return !(m.isNew());
|
||||
if (m.isNew()) {
|
||||
if (m.get('template_list').indexOf(m.get('name')) == -1)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
},{
|
||||
id: 'laninl', label:'{{ _('Inline Function') }}', type: 'text', control: 'node-ajax-options',
|
||||
group: 'Definition', mode: ['edit', 'properties'], url:'get_functions',
|
||||
group: 'Definition', mode: ['edit', 'properties', 'create'], url:'get_functions',
|
||||
deps: ['name'], first_empty: false,
|
||||
/* This function is used to populate the inline function
|
||||
* for the selected language node. It will check if the property
|
||||
* type is 'inline' then push the data into the result array.
|
||||
@@ -101,19 +175,24 @@ define(
|
||||
transform: function(data) {
|
||||
var res = [];
|
||||
if (data && _.isArray(data)) {
|
||||
_.each(data, function(d) {
|
||||
if (d.prop_type == 'inline') {
|
||||
res.push({label: d.label, value: d.label});
|
||||
}
|
||||
})
|
||||
_.each(data, function(d) {
|
||||
if (d.prop_type == 'inline') {
|
||||
res.push({label: d.label, value: d.label});
|
||||
}
|
||||
})
|
||||
}
|
||||
return res;
|
||||
}, disabled: function(m) {
|
||||
return !(m.isNew());
|
||||
if (m.isNew()) {
|
||||
if (m.get('template_list').indexOf(m.get('name')) == -1)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
},{
|
||||
id: 'lanval', label:'{{ _('Validator Function') }}', type: 'text', control: 'node-ajax-options',
|
||||
group: 'Definition', mode: ['edit', 'properties'], url:'get_functions',
|
||||
group: 'Definition', mode: ['edit', 'properties', 'create'], url:'get_functions',
|
||||
deps: ['name'],
|
||||
/* This function is used to populate the validator function
|
||||
* for the selected language node. It will check if the property
|
||||
* type is 'validator' then push the data into the result array.
|
||||
@@ -129,16 +208,20 @@ define(
|
||||
}
|
||||
return res;
|
||||
}, disabled: function(m) {
|
||||
return !(m.isNew());
|
||||
if (m.isNew()) {
|
||||
if (m.get('template_list').indexOf(m.get('name')) == -1)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}, pgBrowser.SecurityGroupUnderSchema, {
|
||||
id: 'lanacl', label: '{{ _('Privileges') }}', type: 'collection',
|
||||
group: 'security', control: 'unique-col-collection', mode: ['edit'],
|
||||
group: 'security', control: 'unique-col-collection', mode: ['edit', 'create'],
|
||||
model: pgBrowser.Node.PrivilegeRoleModel.extend({
|
||||
privileges: ['U']
|
||||
}), canAdd: true, canDelete: true, uniqueCol : ['grantee']
|
||||
},{
|
||||
id: 'seclabels', label: '{{ _('Security Labels') }}', mode: ['edit'],
|
||||
id: 'seclabels', label: '{{ _('Security Labels') }}', mode: ['edit', 'create'],
|
||||
model: pgBrowser.SecLabelModel, editable: false,
|
||||
type: 'collection', group: 'security', min_version: 90200,
|
||||
canAdd: true, canEdit: false, canDelete: true,
|
||||
@@ -160,6 +243,20 @@ define(
|
||||
} else {
|
||||
this.errorModel.unset('name');
|
||||
}
|
||||
|
||||
// If predefined template is selected then no need to validate it.
|
||||
if (!this.get('is_template')) {
|
||||
var handler_func = this.get('lanproc');
|
||||
if (_.isUndefined(handler_func) || _.isNull(handler_func) ||
|
||||
String(handler_func).replace(/^\s+|\s+$/g, '') == '') {
|
||||
var msg = '{{ _('Handler Function cannot be empty') }}';
|
||||
this.errorModel.set('lanproc', msg);
|
||||
return msg;
|
||||
} else {
|
||||
this.errorModel.unset('lanproc');
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
})
|
||||
|
||||