Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
99c688f894 | ||
|
|
f4761f55f7 | ||
|
|
e18a8bf620 | ||
|
|
7c6fdcb12e | ||
|
|
f88c89f3eb | ||
|
|
5949696ded | ||
|
|
f724578463 | ||
|
|
2324337149 | ||
|
|
2dd009eb7c | ||
|
|
92bb931b0a | ||
|
|
eeee45e57c | ||
|
|
10ba029980 | ||
|
|
fd65f1b644 | ||
|
|
9f6ddd3cf9 | ||
|
|
673b5ea5a1 | ||
|
|
84c024f6fa | ||
|
|
3e0ade5a59 | ||
|
|
db23330939 | ||
|
|
bb24c445e6 | ||
|
|
b44b06591b | ||
|
|
b475be93fa | ||
|
|
3e47abed8f | ||
|
|
f0185fbe7f | ||
|
|
5f689a56dd | ||
|
|
0d1e3f36e1 | ||
|
|
29ccdb3242 | ||
|
|
0546e79aed | ||
|
|
5ee403fec7 | ||
|
|
5a3fa59741 | ||
|
|
60497df04b | ||
|
|
f143aa05a3 | ||
|
|
ee0687ecd3 | ||
|
|
e612140473 | ||
|
|
e384c9665a | ||
|
|
30d2d1b23e | ||
|
|
ed2a73f7ff | ||
|
|
b5e5f8ac4c | ||
|
|
102e0a9839 | ||
|
|
edec9adbfb | ||
|
|
c9345ad618 | ||
|
|
fc4b419987 | ||
|
|
475e3689f7 | ||
|
|
c90508cece | ||
|
|
a85d907351 | ||
|
|
9f31ec115a | ||
|
|
fbbbfe22dd | ||
|
|
26b279cc8a | ||
|
|
3ac271e702 | ||
|
|
4ae9132b59 | ||
|
|
3fa48f26b8 | ||
|
|
00e17ef152 | ||
|
|
ace73ebb60 | ||
|
|
5931162556 | ||
|
|
053b1e3d69 | ||
|
|
082fb165d3 | ||
|
|
a02ebe192b | ||
|
|
8ab78dc40d | ||
|
|
6ecd7b20f7 | ||
|
|
48a86f6c38 | ||
|
|
b742487dc3 | ||
|
|
089f890f2e | ||
|
|
1f0a2fd150 | ||
|
|
715839d295 | ||
|
|
1bcf343ceb | ||
|
|
e99fc02f9e | ||
|
|
c0a1429664 | ||
|
|
cc1ce09b73 | ||
|
|
25074e46b4 | ||
|
|
cee0945a78 | ||
|
|
24b0c2e919 | ||
|
|
c56aef335b | ||
|
|
097b630738 | ||
|
|
f351b10ed0 | ||
|
|
1a02d13a28 | ||
|
|
bece59d4b3 | ||
|
|
30398219f4 | ||
|
|
8545d846c0 | ||
|
|
6d8c4bdb82 | ||
|
|
496acf5598 | ||
|
|
36228fece7 | ||
|
|
794c7cb335 | ||
|
|
9c9766ca0a | ||
|
|
7374997425 | ||
|
|
134e651989 |
No files matched your search
@@ -17,7 +17,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Build the container
|
||||
run: docker build .
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install platform dependencies
|
||||
run: |
|
||||
@@ -33,7 +33,7 @@ jobs:
|
||||
make docs
|
||||
|
||||
- name: Archive HTML
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: pgadmin4-html-build-output
|
||||
path: docs/en_US/_build/html/*
|
||||
@@ -44,7 +44,7 @@ jobs:
|
||||
make docs-epub
|
||||
|
||||
- name: Archive ePub
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: pgadmin4-epub-build-output
|
||||
path: docs/en_US/_build/epub/pgAdmin4.epub
|
||||
@@ -55,7 +55,7 @@ jobs:
|
||||
make docs-pdf
|
||||
|
||||
- name: Archive PDF
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: pgadmin4-pdf-build-output
|
||||
path: docs/en_US/_build/latex/pgadmin4.pdf
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Upgrade yarn
|
||||
run: |
|
||||
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install platform dependencies
|
||||
run: |
|
||||
@@ -33,7 +33,7 @@ jobs:
|
||||
make pip
|
||||
|
||||
- name: Archive Python wheel
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: pgadmin4-python-build-output
|
||||
path: dist/*
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install Python dependencies
|
||||
run: |
|
||||
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install platform dependencies
|
||||
run: |
|
||||
@@ -33,7 +33,7 @@ jobs:
|
||||
make src
|
||||
|
||||
- name: Archive source tarball
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: pgadmin4-tarball-build-output
|
||||
path: dist/*
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install platform dependencies
|
||||
run: |
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup the EDB APT repo on Linux
|
||||
run: |
|
||||
@@ -157,21 +157,21 @@ jobs:
|
||||
|
||||
- name: Archive server log
|
||||
if: success() || failure()
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: server-log-pg${{ matrix.pgver }}
|
||||
path: var/pgadmin4.log
|
||||
|
||||
- name: Archive regression log
|
||||
if: success() || failure()
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: regression-log-pg${{ matrix.pgver }}
|
||||
path: web/regression/regression.log
|
||||
|
||||
- name: Archive screenshots
|
||||
if: success() || failure()
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: screenshots-pg${{ matrix.pgver }}
|
||||
path: web/regression/screenshots
|
||||
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup the PGDG APT repo
|
||||
run: |
|
||||
@@ -160,21 +160,21 @@ jobs:
|
||||
|
||||
- name: Archive server log
|
||||
if: success() || failure()
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: server-log-pg${{ matrix.pgver }}
|
||||
path: var/pgadmin4.log
|
||||
|
||||
- name: Archive regression log
|
||||
if: success() || failure()
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: regression-log-pg${{ matrix.pgver }}
|
||||
path: web/regression/regression.log
|
||||
|
||||
- name: Archive screenshots
|
||||
if: success() || failure()
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: screenshots-pg${{ matrix.pgver }}
|
||||
path: web/regression/screenshots
|
||||
|
||||
@@ -22,11 +22,11 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '18.x'
|
||||
node-version: '20.x'
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Upgrade yarn
|
||||
run: |
|
||||
|
||||
@@ -37,7 +37,7 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup the EDB APT repo on Linux
|
||||
if: ${{ matrix.os == 'ubuntu-22.04' }}
|
||||
@@ -78,6 +78,12 @@ jobs:
|
||||
icacls "C:\EPAS\${{ matrix.pgver }}\tablespaces" /grant "NETWORK SERVICE":(OI)(CI)F /T
|
||||
shell: cmd
|
||||
|
||||
- name: Add edb_job_scheduler into config file.
|
||||
if: ${{ matrix.os == 'ubuntu-22.04' && matrix.pgver == 16 }}
|
||||
run: |
|
||||
sudo sed -i "s/shared_preload_libraries = '/shared_preload_libraries = '\$libdir\/edb_job_scheduler,/g" /etc/edb-as/${{ matrix.pgver }}/main/postgresql.conf
|
||||
sudo su -c "echo \"edb_job_scheduler.database_list = 'test_dbms_job_scheduler'\" >> /etc/edb-as/${{ matrix.pgver }}/main/postgresql.conf"
|
||||
|
||||
- name: Start PostgreSQL on Linux
|
||||
if: ${{ matrix.os == 'ubuntu-22.04' }}
|
||||
run: |
|
||||
@@ -272,14 +278,14 @@ jobs:
|
||||
|
||||
- name: Archive server log
|
||||
if: success() || failure()
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: server-log-epas${{ matrix.pgver }}
|
||||
name: server-log-epas-${{ matrix.os }}-pg${{ matrix.pgver }}
|
||||
path: var/pgadmin4.log
|
||||
|
||||
- name: Archive regression log
|
||||
if: success() || failure()
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: regression-log-epas${{ matrix.pgver }}
|
||||
name: regression-log-epas-${{ matrix.os }}-pg${{ matrix.pgver }}
|
||||
path: web/regression/regression.log
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup the PGDG APT repo on Linux
|
||||
if: ${{ matrix.os == 'ubuntu-latest' }}
|
||||
@@ -291,14 +291,14 @@ jobs:
|
||||
|
||||
- name: Archive server log
|
||||
if: success() || failure()
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: server-log-${{ matrix.os }}-pg${{ matrix.pgver }}
|
||||
path: var/pgadmin4.log
|
||||
|
||||
- name: Archive regression log
|
||||
if: success() || failure()
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: regression-log-${{ matrix.os }}-pg${{ matrix.pgver }}
|
||||
path: web/regression/regression.log
|
||||
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
|
||||
@@ -200,8 +200,8 @@ RUN apk add \
|
||||
/venv/bin/python3 -m pip install --no-cache-dir gunicorn==20.1.0 && \
|
||||
find / -type d -name '__pycache__' -exec rm -rf {} + && \
|
||||
useradd -r -u 5050 -g root -s /sbin/nologin pgadmin && \
|
||||
mkdir -p /var/lib/pgadmin && \
|
||||
chown pgadmin:root /var/lib/pgadmin && \
|
||||
mkdir -p /run/pgadmin /var/lib/pgadmin && \
|
||||
chown pgadmin:root /run/pgadmin /var/lib/pgadmin && \
|
||||
chmod g=u /var/lib/pgadmin && \
|
||||
touch /pgadmin4/config_distro.py && \
|
||||
chown pgadmin:root /pgadmin4/config_distro.py && \
|
||||
|
||||
@@ -200,11 +200,29 @@ tab related to tables that should be included in the backup.
|
||||
contents of the table that they have access to. This option is relevant
|
||||
only when dumping the contents of a table which has row security.
|
||||
|
||||
* Move the switch next to *With OIDs* towards right position to include object
|
||||
identifiers as part of the table data for each table.
|
||||
* Use the *Include table(s) and Children* field to dump the tables and any partitions
|
||||
or inheritance child tables of the tables matching the pattern.
|
||||
**Note:** This option is visible only for database server greater than or
|
||||
equal to 16.
|
||||
|
||||
* The Exclude patterns field is utilized to exclude patterns in the following manner:
|
||||
|
||||
* Use the *Exclude table data* field to not dump data for any tables
|
||||
matching the table pattern.
|
||||
* Use the *Table(s)* field to not dump the tables matching the
|
||||
pattern. Multiple patterns can be given separated by space.
|
||||
|
||||
* Use the *Table(s) data* field to not dump data for any tables
|
||||
matching the pattern. Multiple patterns can be given separated by
|
||||
spaces.
|
||||
|
||||
* Use the *Table(s) and children* field to not dump the tables and any
|
||||
partitions or inheritance child tables of the tables matching the pattern.
|
||||
**Note:** This option is visible only for database server greater than or
|
||||
equal to 16.
|
||||
|
||||
* Use the *Table(s) data and children* field to not dump data for the
|
||||
tables and any partitions or inheritance child tables of the tables matching
|
||||
the table pattern. **Note:** This option is visible only for database server
|
||||
greater than or equal to 16.
|
||||
|
||||
Click the *Options* tab to continue. Use the fields in the *Options*
|
||||
tab to provide other backup options.
|
||||
|
||||
@@ -136,9 +136,6 @@ tab related to tables that should be included in the backup.
|
||||
partition itself. **Note:** This option is visible only for database server
|
||||
greater than or equal to 11.
|
||||
|
||||
* Move the switch next to *With OIDs* towards right position to include object
|
||||
identifiers as part of the table data for each table.
|
||||
|
||||
Click the *Options* tab to continue. Use the fields in the *Options*
|
||||
tab to provide other backup options.
|
||||
|
||||
|
||||
@@ -50,6 +50,16 @@ settings:
|
||||
* Use the *LC_CTYPE* field to specify a locale with specified character
|
||||
classification. The locale must be applicable to the current database encoding.
|
||||
(See CREATE DATABASE for details.)
|
||||
* Use the drop-down listbox next to *Locale Provider* to select a locale services associated
|
||||
with the collation. Possible values are: icu, libc. libc is the default.
|
||||
* Move the switch next to *Deterministic* to *YES* to specify whether the collation should use
|
||||
deterministic comparisons. By default, this option is set to true. In a
|
||||
deterministic comparison, strings that are not byte-wise equal are considered
|
||||
unequal, even if they are considered logically equal in the comparison.
|
||||
* Use the *Rules* field to specify a rules for customizing the behavior of the collation.
|
||||
It includes considerations such as character ordering, case sensitivity, and accent
|
||||
sensitivity.
|
||||
* Use the *Version* field to specify version string to store with the collation object.
|
||||
|
||||
|
||||
Click the *SQL* tab to continue.
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
.. _dbms_job:
|
||||
|
||||
*****************
|
||||
`DBMS Job`:index:
|
||||
*****************
|
||||
|
||||
Use the *DBMS Job* dialog to create a DBMS Job.
|
||||
|
||||
.. image:: images/dbms_job_general.png
|
||||
:alt: DBMS Job dialog general tab
|
||||
:align: center
|
||||
|
||||
Use the fields in the *General* tab to create job:
|
||||
|
||||
* Use the *Name* field to add a descriptive name for the job. The name will
|
||||
be displayed in the *pgAdmin* object explorer.
|
||||
* Use the *Enabled?* switch to indicate that job should be enabled or disabled.
|
||||
* Use the *Job Type* field to select the type of the job. Type could be SELF-CONTAINED or PRE-DEFINED.
|
||||
If the Job Type is Self-Contained you need to specify the action and repeat interval in the Action and Repeat tabs respectively.
|
||||
If the Job Type is Pre-Defined you need to specify the existing Program and Schedule names in the Pre-Defined tab.
|
||||
* Store notes about the job in the *Comment* field.
|
||||
|
||||
Click the *Action* tab to continue.
|
||||
|
||||
.. image:: images/dbms_job_action.png
|
||||
:alt: DBMS Job dialog action tab
|
||||
:align: center
|
||||
|
||||
Use the *Action* tab to select the action for the job. This tab is only enabled when the job type is 'SELF-CONTAINED'.
|
||||
|
||||
* Use the *Type* field to select the type of the job. Type could be PLSQL BLOCK or STORED PROCEDURE.
|
||||
* Use the *Procedure* field to select an existing procedure that executes when the job is invoked.
|
||||
* *Number of Arguments* field is read-only and indicates the quantity of arguments necessary for the chosen procedure.
|
||||
|
||||
Click the *Code* tab to continue.
|
||||
|
||||
.. image:: images/dbms_job_code.png
|
||||
:alt: DBMS Job dialog code tab
|
||||
:align: center
|
||||
|
||||
* Use the *Code* field to write the code that executes when the job is invoked.
|
||||
This tab is only enabled when the job type is 'SELF-CONTAINED' and type of the action is set to 'PLSQL BLOCK'.
|
||||
|
||||
|
||||
Click the *Arguments* tab to continue.
|
||||
|
||||
.. image:: images/dbms_job_arguments.png
|
||||
:alt: DBMS Job dialog arguments tab
|
||||
:align: center
|
||||
|
||||
* *Arguments* tab outlines the arguments required by the selected procedure in the 'Action' tab. This tab is only enabled when the job type is 'SELF-CONTAINED'.
|
||||
|
||||
|
||||
Click the *Repeat* tab to continue.
|
||||
|
||||
.. image:: images/dbms_job_repeat.png
|
||||
:alt: DBMS Job dialog repeat tab
|
||||
:align: center
|
||||
|
||||
Use the *Repeat* tab to select the repeat interval for the job. This tab is only enabled when the job type is 'SELF-CONTAINED'.
|
||||
|
||||
* Use the calendar selector in the *Start* field to specify the starting date
|
||||
and time for the job.
|
||||
* Use the calendar selector in the *End* field to specify the ending date and
|
||||
time for the job.
|
||||
* Use the *Frequency* field to select the frequency. Frequency is one of the following:
|
||||
YEARLY, MONTHLY, WEEKLY, DAILY, HOURLY, MINUTELY.
|
||||
* Use the *Date* field to select the date on which job will execute.Date is YYYYMMDD.
|
||||
* Use the *Months* field to select the months in which the job will execute.
|
||||
* Use the *Week Days* field to select the days on which the job will execute.
|
||||
* Use the *Month Days* field to select the numeric days on which the job will
|
||||
execute.
|
||||
* Use the *Hours* field to select the hour at which the job will execute.
|
||||
* Use the *Minutes* field to select the minute at which the job will execute.
|
||||
|
||||
Click the *Pre-Defined* tab to continue.
|
||||
|
||||
.. image:: images/dbms_job_predefined.png
|
||||
:alt: DBMS Job dialog predefined tab
|
||||
:align: center
|
||||
|
||||
Use the *Pre-Defined* tab to select the existing program and schedule to create the job.
|
||||
This tab is only enabled when the job type is 'PRE-DEFINED'.
|
||||
|
||||
* Use the *Program Name* field to select the existing program.
|
||||
* Use the *Schedule Name* field to select an existing schedule.
|
||||
|
||||
|
||||
Click the *SQL* tab to continue.
|
||||
|
||||
Your entries in the *DBMS Job* dialog generate a SQL command (see an example below).
|
||||
Use the *SQL* tab for review; revisit or switch tabs to make any changes to the
|
||||
SQL command.
|
||||
|
||||
**Example**
|
||||
|
||||
The following is an example of the sql command generated by user selections in
|
||||
the *DBMS Job* dialog:
|
||||
|
||||
.. image:: images/dbms_job_sql.png
|
||||
:alt: DBMS Job dialog sql tab
|
||||
:align: center
|
||||
|
||||
* Click the *Info* button (i) to access online help.
|
||||
* Click the *Help* button (?) to access dialog help.
|
||||
* Click the *Save* button to save work.
|
||||
* Click the *Close* button to exit without saving work.
|
||||
* Click the *Reset* button to restore configuration parameters.
|
||||
@@ -0,0 +1,45 @@
|
||||
.. _dbms_job_scheduler:
|
||||
|
||||
********************************
|
||||
`Using EDB Job Scheduler`:index:
|
||||
********************************
|
||||
|
||||
In the past versions of EPAS, DBMS_SCHEDULER or DBMS_JOBS required the configuration
|
||||
of pgAgent, an essential service for their functionality. Maintaining pgAgent in a
|
||||
production environment is cumbersome. The need for correct configuration, regular updates,
|
||||
and ensuring the service’s health added complexity.
|
||||
|
||||
EPAS 16 revolutionizes job scheduling by eliminating the need for the pgAgent component.
|
||||
The new version introduces EDB Job Scheduler which is an extension that runs the job scheduler
|
||||
as a background process for the DBMS_SCHEDULER and DBMS_JOB packages.
|
||||
|
||||
The EDB Job Scheduler has a scheduler process that starts when the database cluster starts.
|
||||
To start the scheduler process, load the EDB Job Scheduler extension using the **shared_preload_libraries**
|
||||
parameter. After you load the extension, create the extension **'edb_job_scheduler'** and **'dbms_scheduler'** using
|
||||
the CREATE EXTENSION command. The database in which you're creating the extension must be listed in the
|
||||
**edb_job_scheduler.database_list** parameter.
|
||||
|
||||
Instructions for configuring the EDB Job Scheduler can be found in the
|
||||
`Configuring EDB Job Scheduler <https://www.enterprisedb.com/docs/pg_extensions/edb_job_scheduler/configuring/>`_.
|
||||
|
||||
.. image:: images/dbms_job_scheduler.png
|
||||
:alt: DBMS Job Scheduler Object Browser
|
||||
:align: center
|
||||
|
||||
Check the status of all the jobs
|
||||
********************************
|
||||
|
||||
To check the running status of all the jobs select the 'DBMS Job Scheduler' collection node from the object
|
||||
explorer and select the Properties tab.
|
||||
|
||||
.. image:: images/dbms_job_details.png
|
||||
:alt: DBMS Job Details
|
||||
:align: center
|
||||
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
dbms_job
|
||||
dbms_program
|
||||
dbms_schedule
|
||||
@@ -0,0 +1,71 @@
|
||||
.. _dbms_program:
|
||||
|
||||
*********************
|
||||
`DBMS Program`:index:
|
||||
*********************
|
||||
|
||||
Use the *DBMS Program* dialog to create a DBMS Program.
|
||||
|
||||
.. image:: images/dbms_program_general.png
|
||||
:alt: DBMS Program dialog general tab
|
||||
:align: center
|
||||
|
||||
Use the fields in the *General* tab to create program:
|
||||
|
||||
* Use the *Name* field to add a descriptive name for the program. The name will
|
||||
be displayed in the *pgAdmin* object explorer.
|
||||
* Use the *Enabled?* switch to indicate that program should be enabled or disabled.
|
||||
* Store notes about the program in the *Comment* field.
|
||||
|
||||
Click the *Action* tab to continue.
|
||||
|
||||
.. image:: images/dbms_program_action.png
|
||||
:alt: DBMS Program dialog action tab
|
||||
:align: center
|
||||
|
||||
Use the *Action* tab to select the action for the program:
|
||||
|
||||
* Use the *Type* field to select the type of the program. Type could be PLSQL BLOCK or STORED PROCEDURE.
|
||||
* Use the *Procedure* field to select an existing procedure that executes when the program is invoked.
|
||||
* *Number of Arguments* field is read-only and indicates the quantity of arguments necessary for the chosen procedure.
|
||||
|
||||
Click the *Code* tab to continue.
|
||||
|
||||
.. image:: images/dbms_program_code.png
|
||||
:alt: DBMS Program dialog code tab
|
||||
:align: center
|
||||
|
||||
* Use the *Code* field to write the code that executes when the program is invoked.
|
||||
This tab is only enabled when the type of the program is set to 'PLSQL BLOCK'.
|
||||
|
||||
|
||||
Click the *Arguments* tab to continue.
|
||||
|
||||
.. image:: images/dbms_program_arguments.png
|
||||
:alt: DBMS Program dialog arguments tab
|
||||
:align: center
|
||||
|
||||
* *Arguments* tab is a read-only section that outlines the arguments required by the selected procedure in the 'Action' tab.
|
||||
|
||||
|
||||
Click the *SQL* tab to continue.
|
||||
|
||||
Your entries in the *DBMS Program* dialog generate a SQL command (see an example below).
|
||||
Use the *SQL* tab for review; revisit or switch tabs to make any changes to the
|
||||
SQL command.
|
||||
|
||||
**Example**
|
||||
|
||||
The following is an example of the sql command generated by user selections in
|
||||
the *DBMS Program* dialog:
|
||||
|
||||
.. image:: images/dbms_program_sql.png
|
||||
:alt: DBMS Program dialog sql tab
|
||||
:align: center
|
||||
|
||||
* Click the *Info* button (i) to access online help.
|
||||
* Click the *Help* button (?) to access dialog help.
|
||||
* Click the *Save* button to save work.
|
||||
* Click the *Close* button to exit without saving work.
|
||||
* Click the *Reset* button to restore configuration parameters.
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
.. _dbms_schedule:
|
||||
|
||||
**********************
|
||||
`DBMS Schedule`:index:
|
||||
**********************
|
||||
|
||||
Use the *DBMS Schedule* dialog to create a DBMS Schedule.
|
||||
|
||||
.. image:: images/dbms_schedule_general.png
|
||||
:alt: DBMS Schedule dialog general tab
|
||||
:align: center
|
||||
|
||||
Use the fields in the *General* tab to create schedule:
|
||||
|
||||
* Use the *Name* field to add a descriptive name for the schedule. The name will
|
||||
be displayed in the *pgAdmin* object explorer.
|
||||
* Store notes about the schedule in the *Comment* field.
|
||||
|
||||
Click the *Repeat* tab to continue.
|
||||
|
||||
.. image:: images/dbms_schedule_repeat.png
|
||||
:alt: DBMS Schedule dialog repeat tab
|
||||
:align: center
|
||||
|
||||
Use the *Repeat* tab to select the repeat interval for the schedule:
|
||||
|
||||
* Use the calendar selector in the *Start* field to specify the starting date
|
||||
and time for the schedule.
|
||||
* Use the calendar selector in the *End* field to specify the ending date and
|
||||
time for the schedule.
|
||||
* Use the *Frequency* field to select the frequency. Frequency is one of the following:
|
||||
YEARLY, MONTHLY, WEEKLY, DAILY, HOURLY, MINUTELY.
|
||||
* Use the *Date* field to select the date on which schedule will execute.Date is YYYYMMDD.
|
||||
* Use the *Months* field to select the months in which the schedule will execute.
|
||||
* Use the *Week Days* field to select the days on which the schedule will execute.
|
||||
* Use the *Month Days* field to select the numeric days on which the schedule will
|
||||
execute.
|
||||
* Use the *Hours* field to select the hour at which the schedule will execute.
|
||||
* Use the *Minutes* field to select the minute at which the schedule will execute.
|
||||
|
||||
|
||||
Click the *SQL* tab to continue.
|
||||
|
||||
Your entries in the *DBMS Schedule* dialog generate a SQL command (see an example below).
|
||||
Use the *SQL* tab for review; revisit or switch tabs to make any changes to the
|
||||
SQL command.
|
||||
|
||||
**Example**
|
||||
|
||||
The following is an example of the sql command generated by user selections in
|
||||
the *DBMS Schedule* dialog:
|
||||
|
||||
.. image:: images/dbms_schedule_sql.png
|
||||
:alt: DBMS Schedule dialog sql tab
|
||||
:align: center
|
||||
|
||||
* Click the *Info* button (i) to access online help.
|
||||
* Click the *Help* button (?) to access dialog help.
|
||||
* Click the *Save* button to save work.
|
||||
* Click the *Close* button to exit without saving work.
|
||||
* Click the *Reset* button to restore configuration parameters.
|
||||
|
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 81 KiB After Width: | Height: | Size: 125 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 57 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 212 KiB |
|
After Width: | Height: | Size: 111 KiB |
|
After Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 98 KiB |
|
After Width: | Height: | Size: 82 KiB |
|
After Width: | Height: | Size: 71 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 45 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
After Width: | Height: | Size: 115 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 94 KiB |
|
After Width: | Height: | Size: 74 KiB |
|
After Width: | Height: | Size: 102 KiB |
|
After Width: | Height: | Size: 73 KiB |
@@ -18,4 +18,5 @@ database, right-click on the *Databases* node, and select *Create Database...*
|
||||
resource_group_dialog
|
||||
role_dialog
|
||||
tablespace_dialog
|
||||
replica_nodes_dialog
|
||||
role_reassign_dialog
|
||||
@@ -18,6 +18,7 @@ node, and select *Create Cast...*
|
||||
:maxdepth: 1
|
||||
|
||||
cast_dialog
|
||||
dbms_job_scheduler
|
||||
collation_dialog
|
||||
domain_dialog
|
||||
domain_constraint_dialog
|
||||
|
||||
@@ -12,6 +12,8 @@ notes for it.
|
||||
:maxdepth: 1
|
||||
|
||||
|
||||
release_notes_8_6
|
||||
release_notes_8_5
|
||||
release_notes_8_4
|
||||
release_notes_8_3
|
||||
release_notes_8_2
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
***********
|
||||
Version 8.5
|
||||
***********
|
||||
|
||||
Release date: 2024-04-04
|
||||
|
||||
This release contains a number of bug fixes and new features since the release of pgAdmin 4 v8.5.
|
||||
|
||||
Supported Database Servers
|
||||
**************************
|
||||
**PostgreSQL**: 12, 13, 14, 15, and 16
|
||||
|
||||
**EDB Advanced Server**: 12, 13, 14, 15, and 16
|
||||
|
||||
Bundled PostgreSQL Utilities
|
||||
****************************
|
||||
**psql**, **pg_dump**, **pg_dumpall**, **pg_restore**: 16.1
|
||||
|
||||
|
||||
New features
|
||||
************
|
||||
|
||||
| `Issue #5611 <https://github.com/pgadmin-org/pgadmin4/issues/5611>`_ - Added support for provider, deterministic, version and RULES parameter while creating collation.
|
||||
| `Issue #7098 <https://github.com/pgadmin-org/pgadmin4/issues/7098>`_ - Added support for EDB Job Scheduler.
|
||||
| `Issue #7216 <https://github.com/pgadmin-org/pgadmin4/issues/7216>`_ - Added support for viewing Log Streaming replication Based Clusters.
|
||||
| `Issue #7221 <https://github.com/pgadmin-org/pgadmin4/issues/7221>`_ - Added support for UNIX socket in entrypoint.sh for Docker implementation.
|
||||
|
||||
Housekeeping
|
||||
************
|
||||
|
||||
|
||||
Bug fixes
|
||||
*********
|
||||
|
||||
| `Issue #4413 <https://github.com/pgadmin-org/pgadmin4/issues/4413>`_ - Fixed an issue in Schema Diff where Columns with sequences get altered unnecessarily.
|
||||
| `Issue #7111 <https://github.com/pgadmin-org/pgadmin4/issues/7111>`_ - Fixed an issue where user was unable to share a newly registered server.
|
||||
| `Issue #7116 <https://github.com/pgadmin-org/pgadmin4/issues/7116>`_ - Bug fixes and improvements in pgAdmin CLI.
|
||||
| `Issue #7139 <https://github.com/pgadmin-org/pgadmin4/issues/7139>`_ - Fixed an issue where the 'Save Password' option for SSH tunneling password was consistently disabled.
|
||||
| `Issue #7165 <https://github.com/pgadmin-org/pgadmin4/issues/7165>`_ - Fixed schema diff wrong query generation for table, foreign table and sequence.
|
||||
| `Issue #7210 <https://github.com/pgadmin-org/pgadmin4/issues/7210>`_ - Fixed an issue where table properties were not updating from properties dialog.
|
||||
| `Issue #7218 <https://github.com/pgadmin-org/pgadmin4/issues/7218>`_ - Fixed an issue where maximize icon is missing from query tool panel.
|
||||
| `Issue #7229 <https://github.com/pgadmin-org/pgadmin4/issues/7229>`_ - Fix an issue in table dialog where changing column name was not syncing table constraints appropriately.
|
||||
| `Issue #7248 <https://github.com/pgadmin-org/pgadmin4/issues/7248>`_ - Fixed rollback and commit button activation on execute button click.
|
||||
| `Issue #7255 <https://github.com/pgadmin-org/pgadmin4/issues/7255>`_ - Fixed an issue where taking backup of a shared server was using server owner's user name.
|
||||
| `Issue #7262 <https://github.com/pgadmin-org/pgadmin4/issues/7262>`_ - Fix an issue in editor where replace option in query tool edit menu is not working on non-Mac OS.
|
||||
| `Issue #7268 <https://github.com/pgadmin-org/pgadmin4/issues/7268>`_ - Fix an issue in editor where Format SQL shortcut and multiline selection are not working.
|
||||
| `Issue #7269 <https://github.com/pgadmin-org/pgadmin4/issues/7269>`_ - Fix an issue in editor where "Use Spaces?" Preference of Editor is not working.
|
||||
| `Issue #7271 <https://github.com/pgadmin-org/pgadmin4/issues/7271>`_ - Fixed an issue where Triggers, Rules, Indexes were absent from the Schema Diff when comparing views.
|
||||
| `Issue #7277 <https://github.com/pgadmin-org/pgadmin4/issues/7277>`_ - Fix an issue in query tool where toggle case of selected text loses selection.
|
||||
| `Issue #7299 <https://github.com/pgadmin-org/pgadmin4/issues/7299>`_ - Fix query tool autocomplete results when cursor is in between the SQL query.
|
||||
| `Issue #7305 <https://github.com/pgadmin-org/pgadmin4/issues/7305>`_ - Fix an issue in query tool where custom keyboard shortcuts are not working for some.
|
||||
| `Issue #7304 <https://github.com/pgadmin-org/pgadmin4/issues/7304>`_ - Fixed the issue where the update-user CLI command doesn't change the password.
|
||||
| `Issue #7308 <https://github.com/pgadmin-org/pgadmin4/issues/7308>`_ - Fixed issue related to email authentication of Two-factor authentication.
|
||||
| `Issue #7326 <https://github.com/pgadmin-org/pgadmin4/issues/7326>`_ - Fixed a remote code execution issue in the validate binary path (CVE-2024-3116).
|
||||
@@ -0,0 +1,45 @@
|
||||
***********
|
||||
Version 8.6
|
||||
***********
|
||||
|
||||
Release date: 2024-05-02
|
||||
|
||||
This release contains a number of bug fixes and new features since the release of pgAdmin 4 v8.5.
|
||||
|
||||
Supported Database Servers
|
||||
**************************
|
||||
**PostgreSQL**: 12, 13, 14, 15, and 16
|
||||
|
||||
**EDB Advanced Server**: 12, 13, 14, 15, and 16
|
||||
|
||||
Bundled PostgreSQL Utilities
|
||||
****************************
|
||||
**psql**, **pg_dump**, **pg_dumpall**, **pg_restore**: 16.1
|
||||
|
||||
|
||||
New features
|
||||
************
|
||||
|
||||
| `Issue #6728 <https://github.com/pgadmin-org/pgadmin4/issues/6728>`_ - Added the new columns "last_seq_scan" and "last_idx_scan" from the pg_stat_all_tables and pg_stat_all_indexes tables respectively to the Statistics tab.
|
||||
| `Issue #7163 <https://github.com/pgadmin-org/pgadmin4/issues/7163>`_ - Added support for excluding multiple tables while taking Backup.
|
||||
|
||||
Housekeeping
|
||||
************
|
||||
|
||||
| `Issue #7213 <https://github.com/pgadmin-org/pgadmin4/issues/7213>`_ - Update UI library MUI from v4 to v5, more details `here <https://github.com/pgadmin-org/pgadmin4/commit/102e0a983956be57fdb63abb356b5b8fcb8b74ba>`_
|
||||
| `Issue #7362 <https://github.com/pgadmin-org/pgadmin4/issues/7362>`_ - Upgraded Flask, Flask-Security-Too, Werkzeug, and other dependencies, ensuring compatibility with Python 3.7.
|
||||
|
||||
Bug fixes
|
||||
*********
|
||||
|
||||
| `Issue #2410 <https://github.com/pgadmin-org/pgadmin4/issues/2410>`_ - Fixed all input boxes in pgAdmin to show browser auto-fill only where it is relevant.
|
||||
| `Issue #7173 <https://github.com/pgadmin-org/pgadmin4/issues/7173>`_ - Install dbus-python, an in-direct dependency of the Keyring package as a system package for Debian platforms.
|
||||
| `Issue #7275 <https://github.com/pgadmin-org/pgadmin4/issues/7275>`_ - Fixed an issue where debugger was not scrolling automatically on stepping.
|
||||
| `Issue #7282 <https://github.com/pgadmin-org/pgadmin4/issues/7282>`_ - Fixed an XSS vulnerability in the /settings/store endpoint (CVE-2024-4216).
|
||||
| `Issue #7294 <https://github.com/pgadmin-org/pgadmin4/issues/7294>`_ - Fixed an issue where double dollar quoted code is treated as string in syntax highlighter.
|
||||
| `Issue #7317 <https://github.com/pgadmin-org/pgadmin4/issues/7317>`_ - Fixed an issue where pressing backspace should remove the spaces and not the entire tab width, on enabling 'Use spaces?' in the preferences.
|
||||
| `Issue #7334 <https://github.com/pgadmin-org/pgadmin4/issues/7334>`_ - Fixed an issue where incorrect select/exec scripts were generated for functions/procedures.
|
||||
| `Issue #7372 <https://github.com/pgadmin-org/pgadmin4/issues/7372>`_ - Fixed an issue where connection to the database is not automatically re-established after connectivity drop.
|
||||
| `Issue #7384 <https://github.com/pgadmin-org/pgadmin4/issues/7384>`_ - Fixed an issue when closing the view data second tab; it raises the error that the 'ViewCommand' object has no attribute 'auto_commit'.
|
||||
| `Issue #7390 <https://github.com/pgadmin-org/pgadmin4/issues/7390>`_ - Fixed violates check constraint issue when creating a pgAgent schedule.
|
||||
| `Issue #7425 <https://github.com/pgadmin-org/pgadmin4/issues/7425>`_ - Fixed Multi-Factor Authentication bypass vulnerability (CVE-2024-4215).
|
||||
@@ -0,0 +1,46 @@
|
||||
.. _replica_nodes_dialog:
|
||||
|
||||
****************************
|
||||
`Replica Node Dialog`:index:
|
||||
****************************
|
||||
|
||||
Use The *Replica Node* dialog to view a standby instance being replicated
|
||||
using log based streaming replication. Streaming replication allows a standby
|
||||
server to stay more up-to-date than is possible with file-based log shipping.
|
||||
The standby connects to the primary, which streams WAL records to the standby as
|
||||
they're generated, without waiting for the WAL file to be filled.
|
||||
|
||||
The *Replica Node* dialog organizes the information through the following tabs:
|
||||
*General*, *Replication Slot*
|
||||
|
||||
.. image:: images/replica_nodes_general.png
|
||||
:alt: Replica Node dialog general tab
|
||||
:align: center
|
||||
|
||||
* The *PID* field is the process ID of a WAL sender process.
|
||||
* The *Username* field is the name of the user logged into this WAL sender process.
|
||||
* The *App Name* field is the name of the application that is connected to this WAL sender.
|
||||
* The *Client Address* field is the IP address of the client connected to this WAL sender.
|
||||
If this field is null, it indicates that the client is connected via a Unix socket on the server machine.
|
||||
* The *Client Hostname* field is the host name of the connected client, as reported by a reverse DNS lookup
|
||||
of client_addr.This field will only be non-null for IP connections, and only when log_hostname is enabled.
|
||||
* The *Client Port* field is the TCP port number that the client is using for communication with
|
||||
this WAL sender, or -1 if a Unix socket is used.
|
||||
* The *State* field is the current WAL sender state.
|
||||
|
||||
Click the *Replication Slot* tab to continue.
|
||||
|
||||
.. image:: images/replica_nodes_replication.png
|
||||
:alt: Replica Node dialog replication slot tab
|
||||
:align: center
|
||||
|
||||
* The *Slot Name* field is a unique, cluster-wide identifier for the replication slot.
|
||||
* The *Slot Type* field is the slot type - physical or logical
|
||||
* The *Active* field is True if this slot is currently actively being used.
|
||||
|
||||
Other buttons:
|
||||
|
||||
* Click the *Info* button (i) to access online help.
|
||||
* Click the *Save* button to save work.
|
||||
* Click the *Close* button to exit without saving work.
|
||||
* Click the *Reset* button to restore configuration parameters.
|
||||
@@ -50,7 +50,7 @@ Version: ${APP_LONG_VERSION}
|
||||
Architecture: ${OS_ARCH}
|
||||
Section: database
|
||||
Priority: optional
|
||||
Depends: ${PYTHON_BINARY}, libpq5 (>= 11.0), libgssapi-krb5-2
|
||||
Depends: ${PYTHON_BINARY}, libpq5 (>= 11.0), libgssapi-krb5-2, python-dbus | python3-dbus
|
||||
Recommends: postgresql-client | postgresql-client-15 | postgresql-client-14 | postgresql-client-13 | postgresql-client-12 | postgresql-client-11 | postgresql-client-10
|
||||
Maintainer: pgAdmin Development Team <pgadmin-hackers@postgresql.org>
|
||||
Description: The core server package for pgAdmin. pgAdmin is the most popular and feature rich Open Source administration and development platform for PostgreSQL, the most advanced Open Source database in the world.
|
||||
@@ -73,7 +73,7 @@ Version: ${APP_LONG_VERSION}
|
||||
Architecture: ${OS_ARCH}
|
||||
Section: database
|
||||
Priority: optional
|
||||
Depends: ${APP_NAME}-server (= ${APP_LONG_VERSION}), libatomic1, xdg-utils
|
||||
Depends: ${APP_NAME}-server (= ${APP_LONG_VERSION}), libatomic1, xdg-utils, python-dbus | python3-dbus
|
||||
Maintainer: pgAdmin Development Team <pgadmin-hackers@postgresql.org>
|
||||
Description: The desktop user interface for pgAdmin. pgAdmin is the most popular and feature rich Open Source administration and development platform for PostgreSQL, the most advanced Open Source database in the world.
|
||||
EOF
|
||||
|
||||
@@ -101,8 +101,18 @@ TIMEOUT=$(cd /pgadmin4 && /venv/bin/python3 -c 'import config; print(config.SESS
|
||||
# NOTE: currently pgadmin can run only with 1 worker due to sessions implementation
|
||||
# Using --threads to have multi-threaded single-process worker
|
||||
|
||||
if [ -n "${PGADMIN_ENABLE_TLS}" ]; then
|
||||
exec /venv/bin/gunicorn --limit-request-line "${GUNICORN_LIMIT_REQUEST_LINE:-8190}" --timeout "${TIMEOUT}" --bind "${PGADMIN_LISTEN_ADDRESS:-[::]}:${PGADMIN_LISTEN_PORT:-443}" -w 1 --threads "${GUNICORN_THREADS:-25}" --access-logfile "${GUNICORN_ACCESS_LOGFILE:--}" --keyfile /certs/server.key --certfile /certs/server.cert -c gunicorn_config.py run_pgadmin:app
|
||||
if [ -n "${PGADMIN_ENABLE_SOCK}" ]; then
|
||||
BIND_ADDRESS="unix:/run/pgadmin/pgadmin.sock"
|
||||
else
|
||||
exec /venv/bin/gunicorn --limit-request-line "${GUNICORN_LIMIT_REQUEST_LINE:-8190}" --timeout "${TIMEOUT}" --bind "${PGADMIN_LISTEN_ADDRESS:-[::]}:${PGADMIN_LISTEN_PORT:-80}" -w 1 --threads "${GUNICORN_THREADS:-25}" --access-logfile "${GUNICORN_ACCESS_LOGFILE:--}" -c gunicorn_config.py run_pgadmin:app
|
||||
if [ -n "${PGADMIN_ENABLE_TLS}" ]; then
|
||||
BIND_ADDRESS="${PGADMIN_LISTEN_ADDRESS:-[::]}:${PGADMIN_LISTEN_PORT:-443}"
|
||||
else
|
||||
BIND_ADDRESS="${PGADMIN_LISTEN_ADDRESS:-[::]}:${PGADMIN_LISTEN_PORT:-80}"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -n "${PGADMIN_ENABLE_TLS}" ]; then
|
||||
exec /venv/bin/gunicorn --limit-request-line "${GUNICORN_LIMIT_REQUEST_LINE:-8190}" --timeout "${TIMEOUT}" --bind "${BIND_ADDRESS}" -w 1 --threads "${GUNICORN_THREADS:-25}" --access-logfile "${GUNICORN_ACCESS_LOGFILE:--}" --keyfile /certs/server.key --certfile /certs/server.cert -c gunicorn_config.py run_pgadmin:app
|
||||
else
|
||||
exec /venv/bin/gunicorn --limit-request-line "${GUNICORN_LIMIT_REQUEST_LINE:-8190}" --timeout "${TIMEOUT}" --bind "${BIND_ADDRESS}" -w 1 --threads "${GUNICORN_THREADS:-25}" --access-logfile "${GUNICORN_ACCESS_LOGFILE:--}" -c gunicorn_config.py run_pgadmin:app
|
||||
fi
|
||||
@@ -8,9 +8,8 @@
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
Flask==2.3.*; python_version > '3.7'
|
||||
Flask==3.0.*; python_version > '3.7'
|
||||
Flask==2.2.*; python_version <= '3.7'
|
||||
Flask-Gravatar==0.*
|
||||
Flask-Login==0.*
|
||||
Flask-Mail==0.*
|
||||
Flask-Migrate==4.*
|
||||
@@ -23,13 +22,13 @@ Flask-Compress==1.*
|
||||
Flask-Paranoid==0.*
|
||||
Flask-Babel==4.0.*; python_version > '3.7'
|
||||
Flask-Babel==3.1.*; python_version <= '3.7'
|
||||
Flask-Security-Too==5.3.*; python_version > '3.7'
|
||||
Flask-Security-Too==5.4.*; python_version > '3.7'
|
||||
Flask-Security-Too==5.1.*; python_version <= '3.7'
|
||||
Flask-SocketIO==5.3.*
|
||||
WTForms==3.1.*; python_version > '3.7'
|
||||
WTForms==3.0.*; python_version <= '3.7'
|
||||
passlib==1.*
|
||||
pytz==2023.*
|
||||
pytz==2024.*
|
||||
speaklater3==1.*
|
||||
sqlparse==0.*
|
||||
psutil==5.9.*
|
||||
@@ -45,7 +44,8 @@ eventlet==0.34.2
|
||||
httpagentparser==1.9.*
|
||||
user-agents==2.2.0
|
||||
pywinpty==2.0.*; sys_platform=="win32"
|
||||
Authlib==1.2.*
|
||||
Authlib==1.3.*; python_version > '3.7'
|
||||
Authlib==1.2.*; python_version <= '3.7'
|
||||
pyotp==2.*
|
||||
qrcode==7.*
|
||||
boto3==1.33.*
|
||||
@@ -55,11 +55,12 @@ azure-mgmt-resource==23.0.1
|
||||
azure-mgmt-subscription==3.1.1
|
||||
azure-identity==1.15.0
|
||||
google-api-python-client==2.*
|
||||
google-auth-oauthlib==1.1.0
|
||||
google-auth-oauthlib==1.2.0
|
||||
keyring==24.*; python_version > '3.7'
|
||||
keyring==23.*; python_version <= '3.7'
|
||||
Werkzeug==2.3.*; python_version > '3.7'
|
||||
Werkzeug==3.0.*; python_version > '3.7'
|
||||
Werkzeug==2.2.3; python_version <= '3.7'
|
||||
typer[all]==0.9.*
|
||||
typer[all]==0.12.*
|
||||
setuptools==69.*; python_version >= '3.12'
|
||||
jsonformatter~=0.3.2
|
||||
libgravatar==1.0.*
|
||||
@@ -23,14 +23,15 @@
|
||||
"256": "assets/pgAdmin4.png"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.5.1",
|
||||
"axios": "^1.6.8",
|
||||
"bootstrap": "^4.5.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^8.50.0",
|
||||
"eslint": "^9.0.0",
|
||||
"nw": "0.77.0"
|
||||
},
|
||||
"scripts": {
|
||||
"linter": "yarn eslint --no-eslintrc -c .eslintrc.js --ext .js ."
|
||||
}
|
||||
},
|
||||
"packageManager": "yarn@3.6.4"
|
||||
}
|
||||
@@ -30,38 +30,38 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@eslint/eslintrc@npm:^2.1.2":
|
||||
version: 2.1.2
|
||||
resolution: "@eslint/eslintrc@npm:2.1.2"
|
||||
"@eslint/eslintrc@npm:^3.0.2":
|
||||
version: 3.0.2
|
||||
resolution: "@eslint/eslintrc@npm:3.0.2"
|
||||
dependencies:
|
||||
ajv: ^6.12.4
|
||||
debug: ^4.3.2
|
||||
espree: ^9.6.0
|
||||
globals: ^13.19.0
|
||||
espree: ^10.0.1
|
||||
globals: ^14.0.0
|
||||
ignore: ^5.2.0
|
||||
import-fresh: ^3.2.1
|
||||
js-yaml: ^4.1.0
|
||||
minimatch: ^3.1.2
|
||||
strip-json-comments: ^3.1.1
|
||||
checksum: bc742a1e3b361f06fedb4afb6bf32cbd27171292ef7924f61c62f2aed73048367bcc7ac68f98c06d4245cd3fabc43270f844e3c1699936d4734b3ac5398814a7
|
||||
checksum: 05bf516b60fbb1c1bdc264e081118b2172c5feb071cd665976482c5614b8e7950991175fea3ca6b1f482ced7cb0d0aa34ceab3a508d6bf1ff17b4efc0911e293
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@eslint/js@npm:8.52.0":
|
||||
version: 8.52.0
|
||||
resolution: "@eslint/js@npm:8.52.0"
|
||||
checksum: 490893b8091a66415f4ac98b963d23eb287264ea3bd6af7ec788f0570705cf64fd6ab84b717785980f55e39d08ff5c7fde6d8e4391ccb507169370ce3a6d091a
|
||||
"@eslint/js@npm:9.0.0":
|
||||
version: 9.0.0
|
||||
resolution: "@eslint/js@npm:9.0.0"
|
||||
checksum: bd8692d68fc79306ad44999fdec9c1ab39c72c3947034f5a251a9148bc175f1d831b54cbbe522d69d88d9d4f1b9e85a1495513553b8528fd8b107d77eadb713f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@humanwhocodes/config-array@npm:^0.11.13":
|
||||
version: 0.11.13
|
||||
resolution: "@humanwhocodes/config-array@npm:0.11.13"
|
||||
"@humanwhocodes/config-array@npm:^0.12.3":
|
||||
version: 0.12.3
|
||||
resolution: "@humanwhocodes/config-array@npm:0.12.3"
|
||||
dependencies:
|
||||
"@humanwhocodes/object-schema": ^2.0.1
|
||||
debug: ^4.1.1
|
||||
"@humanwhocodes/object-schema": ^2.0.3
|
||||
debug: ^4.3.1
|
||||
minimatch: ^3.0.5
|
||||
checksum: f8ea57b0d7ed7f2d64cd3944654976829d9da91c04d9c860e18804729a33f7681f78166ef4c761850b8c324d362f7d53f14c5c44907a6b38b32c703ff85e4805
|
||||
checksum: 4212cf12567538675f45f09dae9715978fac805fbc6f194ce31e62dbbe01ca286b87478463c042883bf4451998be737012dd0882530f55ad0c0e901d963b5123
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -72,10 +72,10 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@humanwhocodes/object-schema@npm:^2.0.1":
|
||||
version: 2.0.1
|
||||
resolution: "@humanwhocodes/object-schema@npm:2.0.1"
|
||||
checksum: 24929487b1ed48795d2f08346a0116cc5ee4634848bce64161fb947109352c562310fd159fc64dda0e8b853307f5794605191a9547f7341158559ca3c8262a45
|
||||
"@humanwhocodes/object-schema@npm:^2.0.3":
|
||||
version: 2.0.3
|
||||
resolution: "@humanwhocodes/object-schema@npm:2.0.3"
|
||||
checksum: d3b78f6c5831888c6ecc899df0d03bcc25d46f3ad26a11d7ea52944dc36a35ef543fad965322174238d677a43d5c694434f6607532cff7077062513ad7022631
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -116,27 +116,20 @@ __metadata:
|
||||
linkType: hard
|
||||
|
||||
"@types/node@npm:*":
|
||||
version: 20.8.10
|
||||
resolution: "@types/node@npm:20.8.10"
|
||||
version: 20.12.7
|
||||
resolution: "@types/node@npm:20.12.7"
|
||||
dependencies:
|
||||
undici-types: ~5.26.4
|
||||
checksum: 7c61190e43e8074a1b571e52ff14c880bc67a0447f2fe5ed0e1a023eb8a23d5f815658edb98890f7578afe0f090433c4a635c7c87311762544e20dd78723e515
|
||||
checksum: 7cc979f7e2ca9a339ec71318c3901b9978555257929ef3666987f3e447123bc6dc92afcc89f6347e09e07d602fde7d51bcddea626c23aa2bb74aeaacfd1e1686
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/responselike@npm:^1.0.0":
|
||||
version: 1.0.2
|
||||
resolution: "@types/responselike@npm:1.0.2"
|
||||
version: 1.0.3
|
||||
resolution: "@types/responselike@npm:1.0.3"
|
||||
dependencies:
|
||||
"@types/node": "*"
|
||||
checksum: ff1767e947eb7d49849e4566040453efcd894888e85b398f7f8cb731552f303f26aceda573b680a142b77ec5fb6c79535d9c6d047d9f936c386dbf3863d2ae17
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@ungap/structured-clone@npm:^1.2.0":
|
||||
version: 1.2.0
|
||||
resolution: "@ungap/structured-clone@npm:1.2.0"
|
||||
checksum: 4f656b7b4672f2ce6e272f2427d8b0824ed11546a601d8d5412b9d7704e83db38a8d9f402ecdf2b9063fc164af842ad0ec4a55819f621ed7e7ea4d1efcc74524
|
||||
checksum: 6ac4b35723429b11b117e813c7acc42c3af8b5554caaf1fc750404c1ae59f9b7376bc69b9e9e194a5a97357a597c2228b7173d317320f0360d617b6425212f58
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -149,12 +142,12 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"acorn@npm:^8.9.0":
|
||||
version: 8.11.2
|
||||
resolution: "acorn@npm:8.11.2"
|
||||
"acorn@npm:^8.11.3":
|
||||
version: 8.11.3
|
||||
resolution: "acorn@npm:8.11.3"
|
||||
bin:
|
||||
acorn: bin/acorn
|
||||
checksum: 818450408684da89423e3daae24e4dc9b68692db8ab49ea4569c7c5abb7a3f23669438bf129cc81dfdada95e1c9b944ee1bfca2c57a05a4dc73834a612fbf6a7
|
||||
checksum: 76d8e7d559512566b43ab4aadc374f11f563f0a9e21626dd59cb2888444e9445923ae9f3699972767f18af61df89cd89f5eaaf772d1327b055b45cb829b4a88c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -214,14 +207,14 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"axios@npm:^1.5.1":
|
||||
version: 1.6.0
|
||||
resolution: "axios@npm:1.6.0"
|
||||
"axios@npm:^1.6.8":
|
||||
version: 1.6.8
|
||||
resolution: "axios@npm:1.6.8"
|
||||
dependencies:
|
||||
follow-redirects: ^1.15.0
|
||||
follow-redirects: ^1.15.6
|
||||
form-data: ^4.0.0
|
||||
proxy-from-env: ^1.1.0
|
||||
checksum: c7c9f2ae9e0b9bad7d6f9a4dff030930b12ee667dedf54c3c776714f91681feb743c509ac0796ae5c01e12c4ab4a2bee74905068dd200fbc1ab86f9814578fb0
|
||||
checksum: bf007fa4b207d102459300698620b3b0873503c6d47bf5a8f6e43c0c64c90035a4f698b55027ca1958f61ab43723df2781c38a99711848d232cad7accbcdfcdd
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -460,7 +453,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"debug@npm:^4.1.1, debug@npm:^4.3.2":
|
||||
"debug@npm:^4.3.1, debug@npm:^4.3.2":
|
||||
version: 4.3.4
|
||||
resolution: "debug@npm:4.3.4"
|
||||
dependencies:
|
||||
@@ -556,15 +549,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"doctrine@npm:^3.0.0":
|
||||
version: 3.0.0
|
||||
resolution: "doctrine@npm:3.0.0"
|
||||
dependencies:
|
||||
esutils: ^2.0.2
|
||||
checksum: fd7673ca77fe26cd5cba38d816bc72d641f500f1f9b25b83e8ce28827fe2da7ad583a8da26ab6af85f834138cf8dae9f69b0cd6ab925f52ddab1754db44d99ce
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"download@npm:^5.0.3":
|
||||
version: 5.0.3
|
||||
resolution: "download@npm:5.0.3"
|
||||
@@ -610,57 +594,60 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"eslint-scope@npm:^7.2.2":
|
||||
version: 7.2.2
|
||||
resolution: "eslint-scope@npm:7.2.2"
|
||||
"eslint-scope@npm:^8.0.1":
|
||||
version: 8.0.1
|
||||
resolution: "eslint-scope@npm:8.0.1"
|
||||
dependencies:
|
||||
esrecurse: ^4.3.0
|
||||
estraverse: ^5.2.0
|
||||
checksum: ec97dbf5fb04b94e8f4c5a91a7f0a6dd3c55e46bfc7bbcd0e3138c3a76977570e02ed89a1810c778dcd72072ff0e9621ba1379b4babe53921d71e2e4486fda3e
|
||||
checksum: 67a5a39312dadb8c9a677df0f2e8add8daf15280b08bfe07f898d5347ee2d7cd2a1f5c2760f34e46e8f5f13f7192f47c2c10abe676bfa4173ae5539365551940
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"eslint-visitor-keys@npm:^3.3.0, eslint-visitor-keys@npm:^3.4.1, eslint-visitor-keys@npm:^3.4.3":
|
||||
"eslint-visitor-keys@npm:^3.3.0":
|
||||
version: 3.4.3
|
||||
resolution: "eslint-visitor-keys@npm:3.4.3"
|
||||
checksum: 36e9ef87fca698b6fd7ca5ca35d7b2b6eeaaf106572e2f7fd31c12d3bfdaccdb587bba6d3621067e5aece31c8c3a348b93922ab8f7b2cbc6aaab5e1d89040c60
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"eslint@npm:^8.50.0":
|
||||
version: 8.52.0
|
||||
resolution: "eslint@npm:8.52.0"
|
||||
"eslint-visitor-keys@npm:^4.0.0":
|
||||
version: 4.0.0
|
||||
resolution: "eslint-visitor-keys@npm:4.0.0"
|
||||
checksum: 5c09f89cf29d87cdbfbac38802a880d3c2e65f8cb61c689888346758f1e24a4c7f6caefeac9474dfa52058a99920623599bdb00516976a30134abeba91275aa2
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"eslint@npm:^9.0.0":
|
||||
version: 9.0.0
|
||||
resolution: "eslint@npm:9.0.0"
|
||||
dependencies:
|
||||
"@eslint-community/eslint-utils": ^4.2.0
|
||||
"@eslint-community/regexpp": ^4.6.1
|
||||
"@eslint/eslintrc": ^2.1.2
|
||||
"@eslint/js": 8.52.0
|
||||
"@humanwhocodes/config-array": ^0.11.13
|
||||
"@eslint/eslintrc": ^3.0.2
|
||||
"@eslint/js": 9.0.0
|
||||
"@humanwhocodes/config-array": ^0.12.3
|
||||
"@humanwhocodes/module-importer": ^1.0.1
|
||||
"@nodelib/fs.walk": ^1.2.8
|
||||
"@ungap/structured-clone": ^1.2.0
|
||||
ajv: ^6.12.4
|
||||
chalk: ^4.0.0
|
||||
cross-spawn: ^7.0.2
|
||||
debug: ^4.3.2
|
||||
doctrine: ^3.0.0
|
||||
escape-string-regexp: ^4.0.0
|
||||
eslint-scope: ^7.2.2
|
||||
eslint-visitor-keys: ^3.4.3
|
||||
espree: ^9.6.1
|
||||
eslint-scope: ^8.0.1
|
||||
eslint-visitor-keys: ^4.0.0
|
||||
espree: ^10.0.1
|
||||
esquery: ^1.4.2
|
||||
esutils: ^2.0.2
|
||||
fast-deep-equal: ^3.1.3
|
||||
file-entry-cache: ^6.0.1
|
||||
file-entry-cache: ^8.0.0
|
||||
find-up: ^5.0.0
|
||||
glob-parent: ^6.0.2
|
||||
globals: ^13.19.0
|
||||
graphemer: ^1.4.0
|
||||
ignore: ^5.2.0
|
||||
imurmurhash: ^0.1.4
|
||||
is-glob: ^4.0.0
|
||||
is-path-inside: ^3.0.3
|
||||
js-yaml: ^4.1.0
|
||||
json-stable-stringify-without-jsonify: ^1.0.1
|
||||
levn: ^0.4.1
|
||||
lodash.merge: ^4.6.2
|
||||
@@ -671,18 +658,18 @@ __metadata:
|
||||
text-table: ^0.2.0
|
||||
bin:
|
||||
eslint: bin/eslint.js
|
||||
checksum: fd22d1e9bd7090e31b00cbc7a3b98f3b76020a4c4641f987ae7d0c8f52e1b88c3b268bdfdabac2e1a93513e5d11339b718ff45cbff48a44c35d7e52feba510ed
|
||||
checksum: 3f20468cfd0f5affadf0f307e3af5b8f9ed19feaabccc3b4ed7548d40727d3f5dea4138db1ec287ca5a6152e164a051450604d47186b5fb28bb158a40937cf60
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"espree@npm:^9.6.0, espree@npm:^9.6.1":
|
||||
version: 9.6.1
|
||||
resolution: "espree@npm:9.6.1"
|
||||
"espree@npm:^10.0.1":
|
||||
version: 10.0.1
|
||||
resolution: "espree@npm:10.0.1"
|
||||
dependencies:
|
||||
acorn: ^8.9.0
|
||||
acorn: ^8.11.3
|
||||
acorn-jsx: ^5.3.2
|
||||
eslint-visitor-keys: ^3.4.1
|
||||
checksum: eb8c149c7a2a77b3f33a5af80c10875c3abd65450f60b8af6db1bfcfa8f101e21c1e56a561c6dc13b848e18148d43469e7cd208506238554fb5395a9ea5a1ab9
|
||||
eslint-visitor-keys: ^4.0.0
|
||||
checksum: 62c9242a84c6741cebd35ede6574131d0419be7e5559566403e384087d99c4ddb2ced44e32acd44a4c3d8a8a84997cf8d78810c4e46b3fe25a804f1a92dc6b9d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -740,11 +727,11 @@ __metadata:
|
||||
linkType: hard
|
||||
|
||||
"fastq@npm:^1.6.0":
|
||||
version: 1.15.0
|
||||
resolution: "fastq@npm:1.15.0"
|
||||
version: 1.17.1
|
||||
resolution: "fastq@npm:1.17.1"
|
||||
dependencies:
|
||||
reusify: ^1.0.4
|
||||
checksum: 0170e6bfcd5d57a70412440b8ef600da6de3b2a6c5966aeaf0a852d542daff506a0ee92d6de7679d1de82e644bce69d7a574a6c93f0b03964b5337eed75ada1a
|
||||
checksum: a8c5b26788d5a1763f88bae56a8ddeee579f935a831c5fe7a8268cea5b0a91fbfe705f612209e02d639b881d7b48e461a50da4a10cfaa40da5ca7cc9da098d88
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -757,12 +744,12 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"file-entry-cache@npm:^6.0.1":
|
||||
version: 6.0.1
|
||||
resolution: "file-entry-cache@npm:6.0.1"
|
||||
"file-entry-cache@npm:^8.0.0":
|
||||
version: 8.0.0
|
||||
resolution: "file-entry-cache@npm:8.0.0"
|
||||
dependencies:
|
||||
flat-cache: ^3.0.4
|
||||
checksum: f49701feaa6314c8127c3c2f6173cfefff17612f5ed2daaafc6da13b5c91fd43e3b2a58fd0d63f9f94478a501b167615931e7200e31485e320f74a33885a9c74
|
||||
flat-cache: ^4.0.0
|
||||
checksum: f67802d3334809048c69b3d458f672e1b6d26daefda701761c81f203b80149c35dea04d78ea4238969dd617678e530876722a0634c43031a0957f10cc3ed190f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -822,31 +809,30 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"flat-cache@npm:^3.0.4":
|
||||
version: 3.1.1
|
||||
resolution: "flat-cache@npm:3.1.1"
|
||||
"flat-cache@npm:^4.0.0":
|
||||
version: 4.0.1
|
||||
resolution: "flat-cache@npm:4.0.1"
|
||||
dependencies:
|
||||
flatted: ^3.2.9
|
||||
keyv: ^4.5.3
|
||||
rimraf: ^3.0.2
|
||||
checksum: 4958cfe0f46acf84953d4e16676ef5f0d38eab3a92d532a1e8d5f88f11eea8b36d5d598070ff2aeae15f1fde18f8d7d089eefaf9db10b5a587cc1c9072325c7a
|
||||
keyv: ^4.5.4
|
||||
checksum: 899fc86bf6df093547d76e7bfaeb900824b869d7d457d02e9b8aae24836f0a99fbad79328cfd6415ee8908f180699bf259dc7614f793447cb14f707caf5996f6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"flatted@npm:^3.2.9":
|
||||
version: 3.2.9
|
||||
resolution: "flatted@npm:3.2.9"
|
||||
checksum: f14167fbe26a9d20f6fca8d998e8f1f41df72c8e81f9f2c9d61ed2bea058248f5e1cbd05e7f88c0e5087a6a0b822a1e5e2b446e879f3cfbe0b07ba2d7f80b026
|
||||
version: 3.3.1
|
||||
resolution: "flatted@npm:3.3.1"
|
||||
checksum: 85ae7181650bb728c221e7644cbc9f4bf28bc556f2fc89bb21266962bdf0ce1029cc7acc44bb646cd469d9baac7c317f64e841c4c4c00516afa97320cdac7f94
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"follow-redirects@npm:^1.15.0":
|
||||
version: 1.15.3
|
||||
resolution: "follow-redirects@npm:1.15.3"
|
||||
"follow-redirects@npm:^1.15.6":
|
||||
version: 1.15.6
|
||||
resolution: "follow-redirects@npm:1.15.6"
|
||||
peerDependenciesMeta:
|
||||
debug:
|
||||
optional: true
|
||||
checksum: 584da22ec5420c837bd096559ebfb8fe69d82512d5585004e36a3b4a6ef6d5905780e0c74508c7b72f907d1fa2b7bd339e613859e9c304d0dc96af2027fd0231
|
||||
checksum: a62c378dfc8c00f60b9c80cab158ba54e99ba0239a5dd7c81245e5a5b39d10f0c35e249c3379eae719ff0285fff88c365dd446fab19dee771f1d76252df1bbf5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -924,12 +910,10 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"globals@npm:^13.19.0":
|
||||
version: 13.23.0
|
||||
resolution: "globals@npm:13.23.0"
|
||||
dependencies:
|
||||
type-fest: ^0.20.2
|
||||
checksum: 194c97cf8d1ef6ba59417234c2386549c4103b6e5f24b1ff1952de61a4753e5d2069435ba629de711a6480b1b1d114a98e2ab27f85e966d5a10c319c3bbd3dc3
|
||||
"globals@npm:^14.0.0":
|
||||
version: 14.0.0
|
||||
resolution: "globals@npm:14.0.0"
|
||||
checksum: 534b8216736a5425737f59f6e6a5c7f386254560c9f41d24a9227d60ee3ad4a9e82c5b85def0e212e9d92162f83a92544be4c7fd4c902cb913736c10e08237ac
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -1006,9 +990,9 @@ __metadata:
|
||||
linkType: hard
|
||||
|
||||
"ignore@npm:^5.2.0":
|
||||
version: 5.2.4
|
||||
resolution: "ignore@npm:5.2.4"
|
||||
checksum: 3d4c309c6006e2621659311783eaea7ebcd41fe4ca1d78c91c473157ad6666a57a2df790fe0d07a12300d9aac2888204d7be8d59f9aaf665b1c7fcdb432517ef
|
||||
version: 5.3.1
|
||||
resolution: "ignore@npm:5.3.1"
|
||||
checksum: 71d7bb4c1dbe020f915fd881108cbe85a0db3d636a0ea3ba911393c53946711d13a9b1143c7e70db06d571a5822c0a324a6bcde5c9904e7ca5047f01f1bf8cd3
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -1183,7 +1167,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"keyv@npm:^4.5.3":
|
||||
"keyv@npm:^4.5.4":
|
||||
version: 4.5.4
|
||||
resolution: "keyv@npm:4.5.4"
|
||||
dependencies:
|
||||
@@ -1441,9 +1425,9 @@ __metadata:
|
||||
version: 0.0.0-use.local
|
||||
resolution: "pgadmin4@workspace:."
|
||||
dependencies:
|
||||
axios: ^1.5.1
|
||||
axios: ^1.6.8
|
||||
bootstrap: ^4.5.3
|
||||
eslint: ^8.50.0
|
||||
eslint: ^9.0.0
|
||||
nw: 0.77.0
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
@@ -1574,17 +1558,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"rimraf@npm:^3.0.2":
|
||||
version: 3.0.2
|
||||
resolution: "rimraf@npm:3.0.2"
|
||||
dependencies:
|
||||
glob: ^7.1.3
|
||||
bin:
|
||||
rimraf: bin.js
|
||||
checksum: 87f4164e396f0171b0a3386cc1877a817f572148ee13a7e113b238e48e8a9f2f31d009a92ec38a591ff1567d9662c6b67fd8818a2dbbaed74bc26a87a2a4a9a0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"run-parallel@npm:^1.1.9":
|
||||
version: 1.2.0
|
||||
resolution: "run-parallel@npm:1.2.0"
|
||||
@@ -1794,13 +1767,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"type-fest@npm:^0.20.2":
|
||||
version: 0.20.2
|
||||
resolution: "type-fest@npm:0.20.2"
|
||||
checksum: 4fb3272df21ad1c552486f8a2f8e115c09a521ad7a8db3d56d53718d0c907b62c6e9141ba5f584af3f6830d0872c521357e512381f24f7c44acae583ad517d73
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"unbzip2-stream@npm:^1.0.9":
|
||||
version: 1.4.3
|
||||
resolution: "unbzip2-stream@npm:1.4.3"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
requests>=2.21.0
|
||||
requests[security]>=2.21.0
|
||||
safety>=1.9.0
|
||||
Sphinx==7.2.6
|
||||
Sphinx==7.3.5
|
||||
sphinxcontrib-youtube==1.4.1
|
||||
@@ -469,6 +469,26 @@ DEFAULT_BINARY_PATHS = {
|
||||
"ppas-16": ""
|
||||
}
|
||||
|
||||
##########################################################################
|
||||
|
||||
# Admin can specify fixed binary paths to prevent users from changing.
|
||||
# It will take precedence over DEFAULT_BINARY_PATHS.
|
||||
|
||||
FIXED_BINARY_PATHS = {
|
||||
"pg": "",
|
||||
"pg-12": "",
|
||||
"pg-13": "",
|
||||
"pg-14": "",
|
||||
"pg-15": "",
|
||||
"pg-16": "",
|
||||
"ppas": "",
|
||||
"ppas-12": "",
|
||||
"ppas-13": "",
|
||||
"ppas-14": "",
|
||||
"ppas-15": "",
|
||||
"ppas-16": ""
|
||||
}
|
||||
|
||||
##########################################################################
|
||||
# Test settings - used primarily by the regression suite, not for users
|
||||
##########################################################################
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"@babel/preset-typescript": "^7.22.5",
|
||||
"@emotion/core": "^10.0.14",
|
||||
"@emotion/memoize": "^0.7.5",
|
||||
"@emotion/react": "^11.1.5",
|
||||
"@emotion/react": "^11.11.3",
|
||||
"@emotion/styled": "^11.11.0",
|
||||
"@emotion/utils": "^1.0.0",
|
||||
"@svgr/webpack": "^8.1.0",
|
||||
@@ -56,16 +56,13 @@
|
||||
"process": "^0.11.10",
|
||||
"prop-types": "^15.7.2",
|
||||
"resize-observer-polyfill": "^1.5.1",
|
||||
"sass": "^1.24.4",
|
||||
"sass-loader": "^11.0.0",
|
||||
"sass-resources-loader": "^2.2.1",
|
||||
"shim-loader": "^1.0.1",
|
||||
"style-loader": "^3.3.2",
|
||||
"stylis": "^4.0.7",
|
||||
"svgo": "^2.7.0",
|
||||
"svgo-loader": "^2.2.0",
|
||||
"terser-webpack-plugin": "^5.1.1",
|
||||
"typescript": "^3.2.2",
|
||||
"typescript": "^4.9.5",
|
||||
"url-loader": "^4.1.1",
|
||||
"webfonts-loader": "^8.0.1",
|
||||
"webpack": "^5.88.2",
|
||||
@@ -76,25 +73,26 @@
|
||||
"dependencies": {
|
||||
"@babel/plugin-proposal-class-properties": "^7.10.4",
|
||||
"@babel/preset-react": "^7.12.13",
|
||||
"@codemirror/lang-sql": "^6.5.5",
|
||||
"@date-io/core": "^1.3.6",
|
||||
"@date-io/date-fns": "1.x",
|
||||
"@codemirror/lang-sql": "^6.6.3",
|
||||
"@date-io/core": "^3.0.0",
|
||||
"@date-io/date-fns": "3.x",
|
||||
"@emotion/sheet": "^1.0.1",
|
||||
"@fortawesome/fontawesome-free": "latest",
|
||||
"@material-ui/core": "4.12.4",
|
||||
"@material-ui/icons": "^4.11.2",
|
||||
"@material-ui/lab": "4.0.0-alpha.61",
|
||||
"@material-ui/pickers": "3.3.10",
|
||||
"@mui/icons-material": "^5.15.10",
|
||||
"@mui/lab": "^5.0.0-alpha.165",
|
||||
"@mui/material": "^5.15.10",
|
||||
"@mui/styles": "^5.15.10",
|
||||
"@mui/x-date-pickers": "^6.19.7",
|
||||
"@projectstorm/react-diagrams": "^6.6.1",
|
||||
"@simonwep/pickr": "^1.5.1",
|
||||
"@szhsin/react-menu": "^2.2.0",
|
||||
"@types/classnames": "^2.2.6",
|
||||
"@types/react": "^16.7.18",
|
||||
"@types/react-dom": "^17.0.11",
|
||||
"@types/react": "^17.0.80",
|
||||
"@types/react-dom": "^17.0.25",
|
||||
"ajv": "^8.8.2",
|
||||
"anti-trojan-source": "^1.4.0",
|
||||
"aspen-decorations": "^1.0.2",
|
||||
"axios": "^1.4.0",
|
||||
"axios": "^1.6.8",
|
||||
"babelify": "~10.0.0",
|
||||
"bignumber.js": "^9.0.1",
|
||||
"brace": "^0.11.1",
|
||||
@@ -107,7 +105,7 @@
|
||||
"convert-units": "^2.3.4",
|
||||
"cssnano": "^5.0.2",
|
||||
"dagre": "^0.8.4",
|
||||
"date-fns": "^2.24.0",
|
||||
"date-fns": "^3.6.0",
|
||||
"diff-arrays-of-objects": "^1.1.8",
|
||||
"hotkeys-js": "^3.13.3",
|
||||
"html-to-image": "^1.11.11",
|
||||
@@ -122,11 +120,10 @@
|
||||
"moment": "^2.29.4",
|
||||
"moment-timezone": "^0.5.34",
|
||||
"notificar": "^1.0.1",
|
||||
"notistack": "^1.0.10",
|
||||
"notistack": "^2.0.8",
|
||||
"path-fx": "^2.0.0",
|
||||
"pathfinding": "^0.4.18",
|
||||
"paths-js": "^0.4.9",
|
||||
"popper.js": "^1.16.1",
|
||||
"postcss": "^8.4.31",
|
||||
"raf": "^3.4.1",
|
||||
"rc-dock": "^3.2.9",
|
||||
@@ -172,7 +169,7 @@
|
||||
"bundle:watch": "yarn run linter && yarn run webpacker:watch",
|
||||
"bundle:dev": "yarn run linter && yarn run webpacker",
|
||||
"bundle:analyze": "cross-env NODE_ENV=production ANALYZE=true yarn run bundle:dev",
|
||||
"bundle": "cross-env NODE_ENV=production NODE_OPTIONS=--max-old-space-size=2048 yarn run bundle:dev",
|
||||
"bundle": "cross-env NODE_ENV=production NODE_OPTIONS=--max-old-space-size=3072 yarn run bundle:dev",
|
||||
"test:js-once": "yarn run linter && yarn run jest --maxWorkers=50%",
|
||||
"test:js": "yarn run test:js-once --watch",
|
||||
"test:js-file": "yarn run test:js-once -t",
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"dark": {
|
||||
"disp_name": "dark_(Beta)",
|
||||
"cssfile": "pgadmin.theme.dark",
|
||||
"preview_img": "dark_preview.png"
|
||||
}
|
||||
}
|
||||
@@ -505,7 +505,9 @@ def create_app(app_name=None):
|
||||
'WTF_CSRF_TIME_LIMIT': getattr(config, 'CSRF_TIME_LIMIT', None),
|
||||
'WTF_CSRF_METHODS': ['GET', 'POST', 'PUT', 'DELETE'],
|
||||
# Disable deliverable check for email addresss
|
||||
'SECURITY_EMAIL_VALIDATOR_ARGS': config.SECURITY_EMAIL_VALIDATOR_ARGS
|
||||
'SECURITY_EMAIL_VALIDATOR_ARGS': config.SECURITY_EMAIL_VALIDATOR_ARGS,
|
||||
# Disable CSRF for unauthenticated endpoints
|
||||
'SECURITY_CSRF_IGNORE_UNAUTH_ENDPOINTS': True
|
||||
}))
|
||||
|
||||
app.config.update(dict({
|
||||
@@ -519,7 +521,12 @@ def create_app(app_name=None):
|
||||
security.init_app(app, user_datastore)
|
||||
|
||||
# register custom unauthorised handler.
|
||||
app.login_manager.unauthorized_handler(pga_unauthorised)
|
||||
if sys.version_info < (3, 8):
|
||||
app.login_manager.unauthorized_handler(pga_unauthorised)
|
||||
else:
|
||||
# Flask-Security-Too > 5.4.* requires custom unauth handeler
|
||||
# to be registeres with it.
|
||||
security.unauthn_handler(pga_unauthorised)
|
||||
|
||||
# Set the permanent session lifetime to the specified value in config file.
|
||||
app.permanent_session_lifetime = timedelta(
|
||||
@@ -890,9 +897,6 @@ def create_app(app_name=None):
|
||||
from flask_compress import Compress
|
||||
Compress(app)
|
||||
|
||||
from pgadmin.misc.themes import themes
|
||||
themes(app)
|
||||
|
||||
@app.context_processor
|
||||
def inject_blueprint():
|
||||
"""
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
|
||||
from flask import Response, render_template, request
|
||||
from flask_babel import gettext
|
||||
from flask_security import current_user, login_required
|
||||
from flask_security import current_user
|
||||
from pgadmin.user_login_check import pga_login_required
|
||||
from pgadmin.utils import PgAdminModule
|
||||
from pgadmin.utils.menu import MenuItem
|
||||
from pgadmin.utils.constants import MIMETYPE_APP_JS
|
||||
@@ -53,7 +54,7 @@ blueprint = AboutModule(MODULE_NAME, __name__, static_url_path='')
|
||||
# A test page
|
||||
##########################################################################
|
||||
@blueprint.route("/", endpoint='index')
|
||||
@login_required
|
||||
@pga_login_required
|
||||
def index():
|
||||
"""Render the about box."""
|
||||
info = {}
|
||||
@@ -138,7 +139,7 @@ def detect_browser(request):
|
||||
|
||||
|
||||
@blueprint.route("/about.js")
|
||||
@login_required
|
||||
@pga_login_required
|
||||
def script():
|
||||
"""render the required javascript"""
|
||||
return Response(
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
import gettext from 'sources/gettext';
|
||||
import url_for from 'sources/url_for';
|
||||
import React, { useEffect, useState, useRef } from 'react';
|
||||
import { Box, Grid, InputLabel } from '@material-ui/core';
|
||||
import { Box, Grid, InputLabel } from '@mui/material';
|
||||
import { DefaultButton } from '../../../static/js/components/Buttons';
|
||||
import { makeStyles } from '@material-ui/styles';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import { InputText } from '../../../static/js/components/FormComponents';
|
||||
import getApiInstance from '../../../static/js/api_instance';
|
||||
import { copyToClipboard } from '../../../static/js/clipboard';
|
||||
@@ -141,11 +141,11 @@ export default function AboutComponent() {
|
||||
}}>{copyText}</DefaultButton>
|
||||
</Box>
|
||||
<Box flexGrow="1" paddingTop="1px">
|
||||
<InputText style={{height: '100%'}} controlProps={{multiline: true}} inputStyle={{resize: 'none'}}
|
||||
<InputText style={{height: '100%'}} controlProps={{multiline: true, rows: 8}} inputStyle={{resize: 'none'}}
|
||||
value={aboutData.settings}/>
|
||||
</Box>
|
||||
</Box>
|
||||
}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -23,12 +23,10 @@ from flask_security.utils import logout_user, config_value
|
||||
from flask_login import current_user
|
||||
from flask_socketio import disconnect, ConnectionRefusedError
|
||||
|
||||
|
||||
from pgadmin.model import db, User
|
||||
from pgadmin.utils import PgAdminModule, get_safe_post_login_redirect, \
|
||||
get_safe_post_logout_redirect
|
||||
from pgadmin.utils.constants import KERBEROS, INTERNAL, OAUTH2, LDAP,\
|
||||
MessageType
|
||||
import pgadmin.utils as pga_utils
|
||||
from pgadmin.authenticate.registry import AuthSourceRegistry
|
||||
|
||||
MODULE_NAME = 'authenticate'
|
||||
@@ -84,7 +82,7 @@ def socket_login_required(f):
|
||||
return wrapped
|
||||
|
||||
|
||||
class AuthenticateModule(PgAdminModule):
|
||||
class AuthenticateModule(pga_utils.PgAdminModule):
|
||||
def get_exposed_url_endpoints(self):
|
||||
return ['authenticate.login']
|
||||
|
||||
@@ -135,7 +133,7 @@ def _login():
|
||||
'Administrator.'),
|
||||
MessageType.WARNING)
|
||||
logout_user()
|
||||
return redirect(get_safe_post_logout_redirect())
|
||||
return redirect(pga_utils.get_safe_post_logout_redirect())
|
||||
|
||||
# Validate the user
|
||||
if not auth_obj.validate():
|
||||
@@ -161,7 +159,7 @@ def _login():
|
||||
flash_login_attempt_error = None
|
||||
flash(error, MessageType.WARNING)
|
||||
|
||||
return redirect(get_safe_post_logout_redirect())
|
||||
return redirect(pga_utils.get_safe_post_logout_redirect())
|
||||
|
||||
# Authenticate the user
|
||||
status, msg = auth_obj.authenticate()
|
||||
@@ -177,7 +175,7 @@ def _login():
|
||||
'authenticate.kerberos_login'), url_for('browser.index')))
|
||||
|
||||
flash(msg, MessageType.ERROR)
|
||||
return redirect(get_safe_post_logout_redirect())
|
||||
return redirect(pga_utils.get_safe_post_logout_redirect())
|
||||
|
||||
session['auth_source_manager'] = current_auth_obj
|
||||
|
||||
@@ -187,7 +185,7 @@ def _login():
|
||||
|
||||
if 'auth_obj' in session:
|
||||
session.pop('auth_obj')
|
||||
return redirect(get_safe_post_login_redirect())
|
||||
return redirect(pga_utils.get_safe_post_login_redirect())
|
||||
|
||||
elif isinstance(msg, Response):
|
||||
return msg
|
||||
|
||||
@@ -18,7 +18,7 @@ from flask import request, Response, session,\
|
||||
current_app, render_template, flash, url_for
|
||||
from flask_security.views import _security
|
||||
from flask_security.utils import logout_user
|
||||
from flask_security import login_required
|
||||
from pgadmin.user_login_check import pga_login_required
|
||||
|
||||
import config
|
||||
from pgadmin.model import User
|
||||
@@ -97,7 +97,7 @@ def init_app(app):
|
||||
@blueprint.route("/update_ticket",
|
||||
endpoint="update_ticket", methods=["GET"])
|
||||
@pgCSRFProtect.exempt
|
||||
@login_required
|
||||
@pga_login_required
|
||||
def kerberos_update_ticket():
|
||||
"""
|
||||
Update the kerberos ticket.
|
||||
@@ -127,7 +127,7 @@ def init_app(app):
|
||||
@blueprint.route("/validate_ticket",
|
||||
endpoint="validate_ticket", methods=["GET"])
|
||||
@pgCSRFProtect.exempt
|
||||
@login_required
|
||||
@pga_login_required
|
||||
def kerberos_validate_ticket():
|
||||
"""
|
||||
Return the kerberos ticket lifetime left after getting the
|
||||
|
||||
@@ -227,7 +227,7 @@ class EmailAuthentication(BaseMFAuth):
|
||||
def _registration_view_after_code_sent(self, _form_data):
|
||||
|
||||
session['mfa_email_id'] = _form_data.get('send_to', None)
|
||||
success, _, message = _send_code_to_email(
|
||||
success, http_code, message = _send_code_to_email(
|
||||
session['mfa_email_id']
|
||||
)
|
||||
|
||||
@@ -239,7 +239,8 @@ class EmailAuthentication(BaseMFAuth):
|
||||
label=email_authentication_label(),
|
||||
auth_method=EMAIL_AUTH_METHOD,
|
||||
message=message,
|
||||
otp_placeholder=_("Enter code here")
|
||||
otp_placeholder=_("Enter code here"),
|
||||
http_code=http_code,
|
||||
)
|
||||
|
||||
def registration_view(self, _form_data):
|
||||
|
||||
@@ -53,7 +53,6 @@ def init_dummy_auth_class():
|
||||
def test_create_dummy_app(name=__name__):
|
||||
import os
|
||||
import pgadmin
|
||||
from pgadmin.misc.themes import themes
|
||||
|
||||
def index():
|
||||
return Response("<html><body>logged in</body></html>")
|
||||
@@ -88,8 +87,6 @@ def test_create_dummy_app(name=__name__):
|
||||
|
||||
app.register_logout_hook = types.MethodType(__dummy_logout_hook, app)
|
||||
|
||||
themes(app)
|
||||
|
||||
return app
|
||||
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ from functools import wraps
|
||||
|
||||
from flask import url_for, session, request, redirect
|
||||
from flask_login.utils import login_url
|
||||
from flask_security import current_user
|
||||
from flask_security import current_user, login_required
|
||||
|
||||
import config
|
||||
from pgadmin.model import UserMFA, db
|
||||
@@ -279,7 +279,7 @@ def mfa_required(wrapped):
|
||||
registration_url = url_for('mfa.register')
|
||||
|
||||
if next_url.startswith(registration_url):
|
||||
return url('browser.index')
|
||||
return url_for('browser.index')
|
||||
|
||||
return next_url
|
||||
|
||||
@@ -290,8 +290,8 @@ def mfa_required(wrapped):
|
||||
return redirect(login_url("mfa.register", next_url=get_next_url()))
|
||||
|
||||
@wraps(wrapped)
|
||||
@login_required
|
||||
def inner(*args, **kwargs):
|
||||
|
||||
def execute_func():
|
||||
session['mfa_authenticated'] = True
|
||||
return wrapped(*args, **kwargs)
|
||||
|
||||
@@ -69,7 +69,6 @@ def init_app(app):
|
||||
methods=['GET', 'POST'])
|
||||
@pgCSRFProtect.exempt
|
||||
def oauth_logout():
|
||||
id_token = session['oauth2_token']['id_token']
|
||||
logout_url = None
|
||||
if 'oauth2_logout_url' in session:
|
||||
logout_url = session['oauth2_logout_url']
|
||||
@@ -81,6 +80,7 @@ def init_app(app):
|
||||
|
||||
logout_user()
|
||||
if logout_url:
|
||||
id_token = session['oauth2_token'].get('id_token')
|
||||
return redirect(logout_url.format(
|
||||
redirect_uri=request.url_root,
|
||||
id_token=id_token))
|
||||
|
||||
@@ -43,7 +43,7 @@ function fetch_ticket_lifetime () {
|
||||
if (ticket_lifetime > 0) {
|
||||
return Promise.resolve(ticket_lifetime);
|
||||
} else {
|
||||
return Promise.reject();
|
||||
return Promise.reject(new Error(null));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -27,8 +27,8 @@ import keyring
|
||||
from flask import current_app, render_template, url_for, make_response, \
|
||||
flash, Response, request, after_this_request, redirect, session
|
||||
from flask_babel import gettext
|
||||
from flask_gravatar import Gravatar
|
||||
from flask_login import current_user, login_required
|
||||
from libgravatar import Gravatar
|
||||
from flask_security import current_user
|
||||
from flask_login.utils import login_url
|
||||
from flask_security.changeable import send_password_changed_notice
|
||||
from flask_security.decorators import anonymous_user_required
|
||||
@@ -64,6 +64,8 @@ from pgadmin.utils.constants import MIMETYPE_APP_JS, PGADMIN_NODE,\
|
||||
from pgadmin.authenticate import AuthSourceManager
|
||||
from pgadmin.utils.exception import CryptKeyMissing
|
||||
|
||||
from pgadmin.user_login_check import pga_login_required
|
||||
|
||||
try:
|
||||
from flask_security.views import default_render_json
|
||||
except ImportError as e:
|
||||
@@ -360,24 +362,26 @@ def _get_supported_browser():
|
||||
return browser_name, browser_known, version
|
||||
|
||||
|
||||
@blueprint.add_app_template_filter
|
||||
def gravatar(username):
|
||||
"""
|
||||
This function adds a template filter which
|
||||
returns gravatar image for user.
|
||||
:return: gravatar image
|
||||
"""
|
||||
g = Gravatar(username)
|
||||
return g.get_image(
|
||||
size=100,
|
||||
rating='g',
|
||||
default='retro'
|
||||
)
|
||||
|
||||
|
||||
@blueprint.route("/")
|
||||
@pgCSRFProtect.exempt
|
||||
@login_required
|
||||
@mfa_required
|
||||
@pga_login_required
|
||||
def index():
|
||||
"""Render and process the main browser window."""
|
||||
# Register Gravatar module with the app only if required
|
||||
if config.SHOW_GRAVATAR_IMAGE:
|
||||
Gravatar(
|
||||
current_app,
|
||||
size=100,
|
||||
rating='g',
|
||||
default='retro',
|
||||
force_default=False,
|
||||
force_lower=True,
|
||||
use_ssl=True,
|
||||
base_url=None
|
||||
)
|
||||
|
||||
# Check the browser is a supported version
|
||||
# NOTE: If the checks here are updated, make sure the supported versions
|
||||
@@ -468,16 +472,19 @@ def get_shared_storage_list():
|
||||
|
||||
@blueprint.route("/js/utils.js")
|
||||
@pgCSRFProtect.exempt
|
||||
@login_required
|
||||
@pga_login_required
|
||||
def utils():
|
||||
layout = get_setting('Browser/Layout', default='')
|
||||
snippets = []
|
||||
|
||||
prefs = Preferences.module('paths')
|
||||
|
||||
pg_help_path_pref = prefs.preference('pg_help_path')
|
||||
pg_help_path = pg_help_path_pref.get()
|
||||
|
||||
# Added to have theme value available at app start page loading
|
||||
prefs = Preferences.module('misc')
|
||||
theme = prefs.preference('theme').get()
|
||||
|
||||
# Get sqleditor options
|
||||
prefs = Preferences.module('sqleditor')
|
||||
|
||||
@@ -539,6 +546,7 @@ def utils():
|
||||
render_template(
|
||||
'browser/js/utils.js',
|
||||
layout=layout,
|
||||
theme=theme,
|
||||
jssnippets=snippets,
|
||||
pg_help_path=pg_help_path,
|
||||
editor_tab_size=editor_tab_size,
|
||||
@@ -585,7 +593,7 @@ def exposed_urls():
|
||||
|
||||
@blueprint.route("/js/error.js")
|
||||
@pgCSRFProtect.exempt
|
||||
@login_required
|
||||
@pga_login_required
|
||||
def error_js():
|
||||
return make_response(
|
||||
render_template('browser/js/error.js', _=gettext),
|
||||
@@ -602,7 +610,7 @@ def messages_js():
|
||||
|
||||
@blueprint.route("/browser.css")
|
||||
@pgCSRFProtect.exempt
|
||||
@login_required
|
||||
@pga_login_required
|
||||
def browser_css():
|
||||
"""Render and return CSS snippets from the nodes and modules."""
|
||||
snippets = []
|
||||
@@ -617,7 +625,7 @@ def browser_css():
|
||||
|
||||
|
||||
@blueprint.route("/nodes/", endpoint="nodes")
|
||||
@login_required
|
||||
@pga_login_required
|
||||
def get_nodes():
|
||||
"""Build a list of treeview nodes from the child nodes."""
|
||||
nodes = []
|
||||
@@ -924,7 +932,7 @@ if hasattr(config, 'SECURITY_CHANGEABLE') and config.SECURITY_CHANGEABLE:
|
||||
@blueprint.route("/change_password", endpoint="change_password",
|
||||
methods=['GET', 'POST'])
|
||||
@pgCSRFProtect.exempt
|
||||
@login_required
|
||||
@pga_login_required
|
||||
def change_password():
|
||||
"""View function which handles a change password request."""
|
||||
|
||||
|
||||
@@ -14,7 +14,8 @@ from abc import ABCMeta, abstractmethod
|
||||
|
||||
from flask import request, jsonify, render_template
|
||||
from flask_babel import gettext
|
||||
from flask_security import current_user, login_required
|
||||
from flask_security import current_user
|
||||
from pgadmin.user_login_check import pga_login_required
|
||||
from pgadmin.browser import BrowserPluginModule
|
||||
from pgadmin.browser.utils import NodeView
|
||||
from pgadmin.utils.ajax import make_json_response, gone, \
|
||||
@@ -162,7 +163,7 @@ class ServerGroupView(NodeView):
|
||||
parent_ids = []
|
||||
ids = [{'type': 'int', 'id': 'gid'}]
|
||||
|
||||
@login_required
|
||||
@pga_login_required
|
||||
def list(self):
|
||||
res = []
|
||||
|
||||
@@ -176,7 +177,7 @@ class ServerGroupView(NodeView):
|
||||
|
||||
return ajax_response(response=res, status=200)
|
||||
|
||||
@login_required
|
||||
@pga_login_required
|
||||
def delete(self, gid):
|
||||
"""Delete a server group node in the settings database"""
|
||||
|
||||
@@ -232,7 +233,7 @@ class ServerGroupView(NodeView):
|
||||
|
||||
return make_json_response(result=request.form)
|
||||
|
||||
@login_required
|
||||
@pga_login_required
|
||||
def update(self, gid):
|
||||
"""Update the server-group properties"""
|
||||
|
||||
@@ -281,7 +282,7 @@ class ServerGroupView(NodeView):
|
||||
)
|
||||
)
|
||||
|
||||
@login_required
|
||||
@pga_login_required
|
||||
def properties(self, gid):
|
||||
"""Update the server-group properties"""
|
||||
|
||||
@@ -299,7 +300,7 @@ class ServerGroupView(NodeView):
|
||||
status=200
|
||||
)
|
||||
|
||||
@login_required
|
||||
@pga_login_required
|
||||
def create(self):
|
||||
"""Creates new server-group """
|
||||
data = request.form if request.form else json.loads(
|
||||
@@ -349,23 +350,23 @@ class ServerGroupView(NodeView):
|
||||
success=0,
|
||||
errormsg=gettext('No server group name was specified'))
|
||||
|
||||
@login_required
|
||||
@pga_login_required
|
||||
def sql(self, gid):
|
||||
return make_json_response(status=422)
|
||||
|
||||
@login_required
|
||||
@pga_login_required
|
||||
def modified_sql(self, gid):
|
||||
return make_json_response(status=422)
|
||||
|
||||
@login_required
|
||||
@pga_login_required
|
||||
def statistics(self, gid):
|
||||
return make_json_response(status=422)
|
||||
|
||||
@login_required
|
||||
@pga_login_required
|
||||
def dependencies(self, gid):
|
||||
return make_json_response(status=422)
|
||||
|
||||
@login_required
|
||||
@pga_login_required
|
||||
def dependents(self, gid):
|
||||
return make_json_response(status=422)
|
||||
|
||||
@@ -394,7 +395,7 @@ class ServerGroupView(NodeView):
|
||||
groups.append(group)
|
||||
return groups
|
||||
|
||||
@login_required
|
||||
@pga_login_required
|
||||
def nodes(self, gid=None):
|
||||
"""Return a JSON document listing the server groups for the user"""
|
||||
nodes = []
|
||||
|
||||
@@ -13,8 +13,10 @@ import pgadmin.browser.server_groups as sg
|
||||
from flask import render_template, request, make_response, jsonify, \
|
||||
current_app, url_for, session
|
||||
from flask_babel import gettext
|
||||
from flask_security import current_user, login_required
|
||||
from flask_security import current_user
|
||||
from pgadmin.user_login_check import pga_login_required
|
||||
from psycopg.conninfo import make_conninfo, conninfo_to_dict
|
||||
|
||||
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, \
|
||||
@@ -30,7 +32,8 @@ from pgadmin.utils.driver import get_driver
|
||||
from pgadmin.utils.master_password import get_crypt_key
|
||||
from pgadmin.utils.exception import CryptKeyMissing
|
||||
from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry
|
||||
from pgadmin.browser.server_groups.servers.utils import is_valid_ipaddress
|
||||
from pgadmin.browser.server_groups.servers.utils import \
|
||||
is_valid_ipaddress, get_replication_type
|
||||
from pgadmin.utils.constants import UNAUTH_REQ, MIMETYPE_APP_JS, \
|
||||
SERVER_CONNECTION_CLOSED
|
||||
from sqlalchemy import or_
|
||||
@@ -216,7 +219,7 @@ class ServerModule(sg.ServerGroupPluginModule):
|
||||
|
||||
return servers
|
||||
|
||||
@login_required
|
||||
@pga_login_required
|
||||
def get_nodes(self, gid):
|
||||
"""Return a JSON document listing the server groups for the user"""
|
||||
|
||||
@@ -343,6 +346,9 @@ class ServerModule(sg.ServerGroupPluginModule):
|
||||
from .tablespaces import blueprint as module
|
||||
self.submodules.append(module)
|
||||
|
||||
from .replica_nodes import blueprint as module
|
||||
self.submodules.append(module)
|
||||
|
||||
super().register(app, options)
|
||||
|
||||
# We do not have any preferences for server node.
|
||||
@@ -469,7 +475,7 @@ class ServerNode(PGChildNodeView):
|
||||
}],
|
||||
'check_pgpass': [{'get': 'check_pgpass'}],
|
||||
'clear_saved_password': [{'put': 'clear_saved_password'}],
|
||||
'clear_sshtunnel_password': [{'put': 'clear_sshtunnel_password'}]
|
||||
'clear_sshtunnel_password': [{'put': 'clear_sshtunnel_password'}],
|
||||
})
|
||||
SSL_MODES = ['prefer', 'require', 'verify-ca', 'verify-full']
|
||||
|
||||
@@ -559,7 +565,7 @@ class ServerNode(PGChildNodeView):
|
||||
|
||||
data['connection_params'] = existing_conn_params
|
||||
|
||||
@login_required
|
||||
@pga_login_required
|
||||
def nodes(self, gid):
|
||||
res = []
|
||||
"""
|
||||
@@ -645,7 +651,7 @@ class ServerNode(PGChildNodeView):
|
||||
|
||||
return make_json_response(result=res)
|
||||
|
||||
@login_required
|
||||
@pga_login_required
|
||||
def node(self, gid, sid):
|
||||
"""Return a JSON document listing the server groups for the user"""
|
||||
server = Server.query.filter_by(id=sid).first()
|
||||
@@ -728,7 +734,7 @@ class ServerNode(PGChildNodeView):
|
||||
success=0,
|
||||
errormsg=e.message)
|
||||
|
||||
@login_required
|
||||
@pga_login_required
|
||||
def delete(self, gid, sid):
|
||||
"""Delete a server node in the settings database."""
|
||||
servers = Server.query.filter_by(user_id=current_user.id, id=sid)
|
||||
@@ -778,7 +784,7 @@ class ServerNode(PGChildNodeView):
|
||||
return make_json_response(success=1,
|
||||
info=gettext("Server deleted"))
|
||||
|
||||
@login_required
|
||||
@pga_login_required
|
||||
def update(self, gid, sid):
|
||||
"""Update the server settings"""
|
||||
server = Server.query.filter_by(id=sid).first()
|
||||
@@ -983,7 +989,7 @@ class ServerNode(PGChildNodeView):
|
||||
).format(disp_lbl[arg])
|
||||
)
|
||||
|
||||
@login_required
|
||||
@pga_login_required
|
||||
def list(self, gid):
|
||||
"""
|
||||
Return list of attributes of all servers.
|
||||
@@ -1030,7 +1036,7 @@ class ServerNode(PGChildNodeView):
|
||||
response=res
|
||||
)
|
||||
|
||||
@login_required
|
||||
@pga_login_required
|
||||
def properties(self, gid, sid):
|
||||
"""Return list of attributes of a server"""
|
||||
|
||||
@@ -1144,12 +1150,12 @@ class ServerNode(PGChildNodeView):
|
||||
con_info_ord = OrderedDict([('host', server.host),
|
||||
('port', server.port),
|
||||
('dbname', db_name),
|
||||
('user', server.username)])
|
||||
('user', manager.user)])
|
||||
con_info_ord.update(con_info)
|
||||
display_conn_string = make_conninfo(**con_info_ord)
|
||||
return display_conn_string
|
||||
|
||||
@login_required
|
||||
@pga_login_required
|
||||
def create(self, gid):
|
||||
"""Add a server node to the settings database"""
|
||||
required_args = ['name', 'db']
|
||||
@@ -1247,6 +1253,8 @@ class ServerNode(PGChildNodeView):
|
||||
connected = False
|
||||
user = None
|
||||
manager = None
|
||||
replication_type = None
|
||||
tunnel_password_saved = False
|
||||
|
||||
if 'connect_now' in data and data['connect_now']:
|
||||
manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(
|
||||
@@ -1323,7 +1331,10 @@ class ServerNode(PGChildNodeView):
|
||||
KEY_RING_TUNNEL_FORMAT.format(server.name,
|
||||
server.id),
|
||||
tunnel_password)
|
||||
tunnel_password_saved = True
|
||||
|
||||
replication_type = get_replication_type(conn,
|
||||
manager.version)
|
||||
user = manager.user_info
|
||||
connected = True
|
||||
|
||||
@@ -1337,6 +1348,7 @@ class ServerNode(PGChildNodeView):
|
||||
username=server.username,
|
||||
user=user,
|
||||
connected=connected,
|
||||
replication_type=replication_type,
|
||||
shared=server.shared,
|
||||
server_type=manager.server_type
|
||||
if manager and manager.server_type
|
||||
@@ -1348,7 +1360,8 @@ class ServerNode(PGChildNodeView):
|
||||
gss_authenticated=manager.gss_authenticated if
|
||||
manager and manager.gss_authenticated else False,
|
||||
is_password_saved=bool(server.save_password),
|
||||
is_tunnel_password_saved=bool(server.tunnel_password)
|
||||
is_tunnel_password_saved=tunnel_password_saved,
|
||||
user_id=server.user_id
|
||||
)
|
||||
)
|
||||
|
||||
@@ -1364,15 +1377,15 @@ class ServerNode(PGChildNodeView):
|
||||
errormsg=str(e)
|
||||
)
|
||||
|
||||
@login_required
|
||||
@pga_login_required
|
||||
def sql(self, gid, sid):
|
||||
return make_json_response(data='')
|
||||
|
||||
@login_required
|
||||
@pga_login_required
|
||||
def modified_sql(self, gid, sid):
|
||||
return make_json_response(data='')
|
||||
|
||||
@login_required
|
||||
@pga_login_required
|
||||
def statistics(self, gid, sid):
|
||||
manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)
|
||||
conn = manager.connection()
|
||||
@@ -1396,11 +1409,11 @@ class ServerNode(PGChildNodeView):
|
||||
)
|
||||
)
|
||||
|
||||
@login_required
|
||||
@pga_login_required
|
||||
def dependencies(self, gid, sid):
|
||||
return make_json_response(data='')
|
||||
|
||||
@login_required
|
||||
@pga_login_required
|
||||
def dependents(self, gid, sid):
|
||||
return make_json_response(data='')
|
||||
|
||||
@@ -1427,6 +1440,7 @@ class ServerNode(PGChildNodeView):
|
||||
in_recovery = None
|
||||
wal_paused = None
|
||||
errmsg = None
|
||||
replication_type = None
|
||||
if connected:
|
||||
status, result, in_recovery, wal_paused =\
|
||||
recovery_state(conn, manager.version)
|
||||
@@ -1436,10 +1450,13 @@ class ServerNode(PGChildNodeView):
|
||||
manager.release()
|
||||
errmsg = "{0} : {1}".format(server.name, result)
|
||||
|
||||
replication_type = get_replication_type(conn, manager.version)
|
||||
|
||||
return make_json_response(
|
||||
data={
|
||||
'icon': server_icon_and_background(connected, manager, server),
|
||||
'connected': connected,
|
||||
'replication_type': replication_type,
|
||||
'in_recovery': in_recovery,
|
||||
'wal_pause': wal_paused,
|
||||
'server_type': manager.server_type if connected else "pg",
|
||||
@@ -1709,6 +1726,8 @@ class ServerNode(PGChildNodeView):
|
||||
_, _, in_recovery, wal_paused =\
|
||||
recovery_state(conn, manager.version)
|
||||
|
||||
replication_type = get_replication_type(conn, manager.version)
|
||||
|
||||
return make_json_response(
|
||||
success=1,
|
||||
info=gettext("Server connected."),
|
||||
@@ -1716,6 +1735,7 @@ class ServerNode(PGChildNodeView):
|
||||
'icon': server_icon_and_background(True, manager, server),
|
||||
'connected': True,
|
||||
'server_type': manager.server_type,
|
||||
'replication_type': replication_type,
|
||||
'type': manager.server_type,
|
||||
'version': manager.version,
|
||||
'db': manager.db,
|
||||
@@ -2146,8 +2166,8 @@ class ServerNode(PGChildNodeView):
|
||||
session['allow_save_password'] else False,
|
||||
"allow_save_tunnel_password":
|
||||
True if config.ALLOW_SAVE_TUNNEL_PASSWORD and
|
||||
'allow_save_tunnel_password' in session and
|
||||
session['allow_save_tunnel_password'] else False
|
||||
'allow_save_password' in session and
|
||||
session['allow_save_password'] else False
|
||||
}
|
||||
return make_json_response(
|
||||
success=0,
|
||||
|
||||
@@ -128,6 +128,9 @@ class DatabaseModule(CollectionNodeModule):
|
||||
from .subscriptions import blueprint as module
|
||||
self.submodules.append(module)
|
||||
|
||||
from .dbms_job_scheduler import blueprint as module
|
||||
self.submodules.append(module)
|
||||
|
||||
super().register(app, options)
|
||||
|
||||
|
||||
|
||||
@@ -76,11 +76,10 @@ define('pgadmin.node.cast', [
|
||||
return new Promise((resolve, reject)=>{
|
||||
const api = getApiInstance();
|
||||
|
||||
let _url = pgBrowser.Nodes['cast'].generate_url.apply(
|
||||
pgBrowser.Nodes['cast'], [
|
||||
null, 'get_functions', itemNodeData, false,
|
||||
treeNodeInfo,
|
||||
]);
|
||||
let _url = pgBrowser.Nodes['cast'].generate_url(
|
||||
null, 'get_functions', itemNodeData, false,
|
||||
treeNodeInfo,
|
||||
);
|
||||
let data = {'srctyp' : srcTyp, 'trgtyp' : trgtyp};
|
||||
|
||||
if(srcTyp != undefined && srcTyp != '' &&
|
||||
|
||||
@@ -0,0 +1,280 @@
|
||||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2024, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
""" Implements DBMS Job Scheduler objects Node."""
|
||||
|
||||
from functools import wraps
|
||||
from flask import render_template
|
||||
from flask_babel import gettext
|
||||
from pgadmin.browser.collection import CollectionNodeModule
|
||||
from pgadmin.browser.server_groups.servers import databases
|
||||
from pgadmin.browser.utils import PGChildNodeView
|
||||
from pgadmin.utils.ajax import (make_json_response, internal_server_error,
|
||||
make_response as ajax_response)
|
||||
from pgadmin.utils.driver import get_driver
|
||||
from config import PG_DEFAULT_DRIVER
|
||||
from pgadmin.utils.constants import DBMS_JOB_SCHEDULER_ID
|
||||
|
||||
|
||||
class DBMSJobSchedulerModule(CollectionNodeModule):
|
||||
"""
|
||||
class DBMSJobSchedulerModule(CollectionNodeModule)
|
||||
|
||||
A module class for DBMS Job Scheduler objects node derived
|
||||
from CollectionNodeModule.
|
||||
|
||||
Methods:
|
||||
-------
|
||||
* __init__(*args, **kwargs)
|
||||
- Method is used to initialize the DBMS Job Scheduler objects, and it's
|
||||
base module.
|
||||
|
||||
* get_nodes(gid, sid, did, scid, coid)
|
||||
- Method is used to generate the browser collection node.
|
||||
|
||||
* script_load()
|
||||
- Load the module script for DBMS Job Scheduler objects, when any of the
|
||||
server node is initialized.
|
||||
|
||||
* backend_supported(manager, **kwargs)
|
||||
|
||||
* registert(self, app, options)
|
||||
- Override the default register function to automagically register
|
||||
sub-modules at once.
|
||||
"""
|
||||
_NODE_TYPE = 'dbms_job_scheduler'
|
||||
_COLLECTION_LABEL = gettext("DBMS Job Scheduler")
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""
|
||||
Method is used to initialize the DBMSJobSchedulerModule, and it's base
|
||||
module.
|
||||
|
||||
Args:
|
||||
*args:
|
||||
**kwargs:
|
||||
"""
|
||||
super().__init__(*args, **kwargs)
|
||||
self.min_ver = None
|
||||
self.max_ver = None
|
||||
|
||||
@property
|
||||
def node_icon(self):
|
||||
"""
|
||||
icon to be displayed for the browser nodes
|
||||
"""
|
||||
return 'icon-coll-dbms_job_scheduler'
|
||||
|
||||
def get_nodes(self, gid, sid, did):
|
||||
"""
|
||||
Generate the collection node
|
||||
"""
|
||||
if self.show_node:
|
||||
yield self.generate_browser_node(DBMS_JOB_SCHEDULER_ID, did,
|
||||
self._COLLECTION_LABEL, None)
|
||||
|
||||
@property
|
||||
def script_load(self):
|
||||
"""
|
||||
Load the module script for server, when any of the database node is
|
||||
initialized.
|
||||
"""
|
||||
return databases.DatabaseModule.node_type
|
||||
|
||||
def backend_supported(self, manager, **kwargs):
|
||||
"""
|
||||
Function is used to check the pre-requisite for this node.
|
||||
Args:
|
||||
manager:
|
||||
**kwargs:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
if hasattr(self, 'show_node') and not self.show_node:
|
||||
return False
|
||||
|
||||
# Get the connection for the respective database
|
||||
conn = manager.connection(did=kwargs['did'])
|
||||
# Checking whether both 'edb_job_scheduler' and 'dbms_scheduler'
|
||||
# extensions are created or not.
|
||||
status, res = conn.execute_scalar("""
|
||||
SELECT COUNT(*) FROM pg_extension WHERE extname IN (
|
||||
'edb_job_scheduler', 'dbms_scheduler') """)
|
||||
if status and int(res) == 2:
|
||||
# Get the list of databases specified for the edb_job_scheduler
|
||||
status, res = conn.execute_scalar("""
|
||||
SHOW edb_job_scheduler.database_list""")
|
||||
# If database is available in the specified list than return True.
|
||||
if status and res and conn.db in res:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
@property
|
||||
def module_use_template_javascript(self):
|
||||
"""
|
||||
Returns whether Jinja2 template is used for generating the javascript
|
||||
module.
|
||||
"""
|
||||
return False
|
||||
|
||||
def register(self, app, options):
|
||||
"""
|
||||
Override the default register function to automagically register
|
||||
sub-modules at once.
|
||||
"""
|
||||
from .dbms_jobs import blueprint as module
|
||||
self.submodules.append(module)
|
||||
|
||||
from .dbms_programs import blueprint as module
|
||||
self.submodules.append(module)
|
||||
|
||||
from .dbms_schedules import blueprint as module
|
||||
self.submodules.append(module)
|
||||
|
||||
super().register(app, options)
|
||||
|
||||
|
||||
blueprint = DBMSJobSchedulerModule(__name__)
|
||||
|
||||
|
||||
class DBMSJobSchedulerView(PGChildNodeView):
|
||||
"""
|
||||
class DBMSJobSchedulerView(PGChildNodeView)
|
||||
|
||||
A view class for cast node derived from PGChildNodeView. This class is
|
||||
responsible for all the stuff related to view like
|
||||
create/update/delete cast, showing properties of cast node,
|
||||
showing sql in sql pane.
|
||||
|
||||
Methods:
|
||||
-------
|
||||
* __init__(**kwargs)
|
||||
- Method is used to initialize the DBMSJobSchedulerView, and it's
|
||||
base view.
|
||||
|
||||
* check_precondition()
|
||||
- This function will behave as a decorator which will checks
|
||||
database connection before running view, it will also attach
|
||||
manager,conn & template_path properties to self
|
||||
|
||||
* nodes()
|
||||
- This function will use to create all the child node within that
|
||||
collection. Here it will create all the scheduler nodes.
|
||||
|
||||
* properties(gid, sid, did, jsid)
|
||||
- This function will show the properties of the selected job node
|
||||
|
||||
"""
|
||||
|
||||
node_type = blueprint.node_type
|
||||
BASE_TEMPLATE_PATH = 'dbms_job_scheduler/ppas/#{0}#'
|
||||
|
||||
parent_ids = [
|
||||
{'type': 'int', 'id': 'gid'},
|
||||
{'type': 'int', 'id': 'sid'},
|
||||
{'type': 'int', 'id': 'did'}
|
||||
]
|
||||
ids = [
|
||||
{'type': 'int', 'id': 'jsid'}
|
||||
]
|
||||
|
||||
operations = dict({
|
||||
'obj': [
|
||||
{'get': 'properties'},
|
||||
{'get': 'list'}
|
||||
],
|
||||
'children': [{
|
||||
'get': 'children'
|
||||
}],
|
||||
'nodes': [{'get': 'nodes'}, {'get': 'nodes'}]
|
||||
})
|
||||
|
||||
def _init_(self, **kwargs):
|
||||
self.conn = None
|
||||
self.template_path = None
|
||||
self.manager = None
|
||||
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def check_precondition(f):
|
||||
"""
|
||||
This function will behave as a decorator which will check the
|
||||
database connection before running view. It will also attach
|
||||
manager, conn & template_path properties to self
|
||||
"""
|
||||
|
||||
@wraps(f)
|
||||
def wrap(*args, **kwargs):
|
||||
self = args[0]
|
||||
self.driver = get_driver(PG_DEFAULT_DRIVER)
|
||||
self.manager = self.driver.connection_manager(kwargs['sid'])
|
||||
self.conn = self.manager.connection(did=kwargs['did'])
|
||||
# Set the template path for the SQL scripts
|
||||
self.template_path = self.BASE_TEMPLATE_PATH.format(
|
||||
self.manager.version)
|
||||
|
||||
# Here args[0] will hold self & kwargs will hold gid,sid,did
|
||||
return f(*args, **kwargs)
|
||||
return wrap
|
||||
|
||||
@check_precondition
|
||||
def nodes(self, gid, sid, did):
|
||||
"""
|
||||
This function will use to create all the child nodes within the
|
||||
collection.
|
||||
"""
|
||||
return make_json_response(
|
||||
data=[],
|
||||
status=200
|
||||
)
|
||||
|
||||
@check_precondition
|
||||
def list(self, gid, sid, did):
|
||||
"""
|
||||
This function will show the run details of all the jobs.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
"""
|
||||
try:
|
||||
sql = render_template(
|
||||
"/".join([self.template_path, 'get_job_run_details.sql']))
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
return ajax_response(
|
||||
response=res['rows'],
|
||||
status=200
|
||||
)
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def properties(self, gid, sid, did, jsid):
|
||||
"""
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
jsid: Job Scheduler ID
|
||||
"""
|
||||
return make_json_response(
|
||||
data=[],
|
||||
status=200
|
||||
)
|
||||
|
||||
|
||||
DBMSJobSchedulerView.register_node_view(blueprint)
|
||||
@@ -0,0 +1,785 @@
|
||||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2024, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
""" Implements DBMS Job objects Node."""
|
||||
|
||||
import json
|
||||
from functools import wraps
|
||||
|
||||
from flask import render_template, request, jsonify
|
||||
from flask_babel import gettext
|
||||
from pgadmin.browser.collection import CollectionNodeModule
|
||||
from pgadmin.browser.server_groups.servers import databases
|
||||
from pgadmin.browser.utils import PGChildNodeView
|
||||
from pgadmin.utils.ajax import make_json_response, gone, \
|
||||
make_response as ajax_response, internal_server_error, success_return
|
||||
from pgadmin.utils.driver import get_driver
|
||||
from config import PG_DEFAULT_DRIVER
|
||||
from pgadmin.utils.constants import DBMS_JOB_SCHEDULER_ID
|
||||
from pgadmin.browser.server_groups.servers.databases.schemas.functions.utils \
|
||||
import format_arguments_from_db
|
||||
from pgadmin.browser.server_groups.servers.databases.dbms_job_scheduler.utils \
|
||||
import (resolve_calendar_string, create_calendar_string,
|
||||
get_formatted_program_args)
|
||||
|
||||
|
||||
class DBMSJobModule(CollectionNodeModule):
|
||||
"""
|
||||
class DBMSJobModule(CollectionNodeModule)
|
||||
|
||||
A module class for DBMS Job objects node derived
|
||||
from CollectionNodeModule.
|
||||
|
||||
Methods:
|
||||
-------
|
||||
|
||||
* get_nodes(gid, sid, did)
|
||||
- Method is used to generate the browser collection node.
|
||||
|
||||
* script_load()
|
||||
- Load the module script for DBMS Job objects, when any of
|
||||
the server node is initialized.
|
||||
"""
|
||||
_NODE_TYPE = 'dbms_job'
|
||||
_COLLECTION_LABEL = gettext("DBMS Jobs")
|
||||
|
||||
@property
|
||||
def collection_icon(self):
|
||||
"""
|
||||
icon to be displayed for the browser collection node
|
||||
"""
|
||||
return 'icon-coll-pga_job'
|
||||
|
||||
@property
|
||||
def node_icon(self):
|
||||
"""
|
||||
icon to be displayed for the browser nodes
|
||||
"""
|
||||
return 'icon-pga_job'
|
||||
|
||||
def get_nodes(self, gid, sid, did, jsid):
|
||||
"""
|
||||
Generate the collection node
|
||||
"""
|
||||
if self.show_node:
|
||||
yield self.generate_browser_collection_node(did)
|
||||
|
||||
@property
|
||||
def node_inode(self):
|
||||
"""
|
||||
Override this property to make the node a leaf node.
|
||||
|
||||
Returns: False as this is the leaf node
|
||||
"""
|
||||
return False
|
||||
|
||||
@property
|
||||
def script_load(self):
|
||||
"""
|
||||
Load the module script for server, when any of the database node is
|
||||
initialized.
|
||||
"""
|
||||
return databases.DatabaseModule.node_type
|
||||
|
||||
@property
|
||||
def module_use_template_javascript(self):
|
||||
"""
|
||||
Returns whether Jinja2 template is used for generating the javascript
|
||||
module.
|
||||
"""
|
||||
return False
|
||||
|
||||
|
||||
blueprint = DBMSJobModule(__name__)
|
||||
|
||||
|
||||
class DBMSJobView(PGChildNodeView):
|
||||
"""
|
||||
class DBMSJobView(PGChildNodeView)
|
||||
|
||||
A view class for DBMSJob node derived from PGChildNodeView.
|
||||
This class is responsible for all the stuff related to view like
|
||||
updating job node, showing properties, showing sql in sql pane.
|
||||
|
||||
Methods:
|
||||
-------
|
||||
* __init__(**kwargs)
|
||||
- Method is used to initialize the DBMSJobView, and it's base view.
|
||||
|
||||
* check_precondition()
|
||||
- This function will behave as a decorator which will checks
|
||||
database connection before running view, it will also attaches
|
||||
manager,conn & template_path properties to self
|
||||
|
||||
* list()
|
||||
- This function is used to list all the job nodes within that
|
||||
collection.
|
||||
|
||||
* nodes()
|
||||
- This function will use to create all the child node within that
|
||||
collection. Here it will create all the job node.
|
||||
|
||||
* properties(gid, sid, did, jsid, jsjobid)
|
||||
- This function will show the properties of the selected job node
|
||||
|
||||
* create(gid, sid, did, jsid, jsjobid)
|
||||
- This function will create the new job object
|
||||
|
||||
* msql(gid, sid, did, jsid, jsjobid)
|
||||
- This function is used to return modified SQL for the
|
||||
selected job node
|
||||
|
||||
* sql(gid, sid, did, jsid, jsjobid)
|
||||
- Dummy response for sql panel
|
||||
|
||||
* delete(gid, sid, did, jsid, jsjobid)
|
||||
- Drops job
|
||||
"""
|
||||
|
||||
node_type = blueprint.node_type
|
||||
BASE_TEMPLATE_PATH = 'dbms_jobs/ppas/#{0}#'
|
||||
PROGRAM_TEMPLATE_PATH = 'dbms_programs/ppas/#{0}#'
|
||||
SCHEDULE_TEMPLATE_PATH = 'dbms_schedules/ppas/#{0}#'
|
||||
|
||||
parent_ids = [
|
||||
{'type': 'int', 'id': 'gid'},
|
||||
{'type': 'int', 'id': 'sid'},
|
||||
{'type': 'int', 'id': 'did'},
|
||||
{'type': 'int', 'id': 'jsid'}
|
||||
]
|
||||
ids = [
|
||||
{'type': 'int', 'id': 'jsjobid'}
|
||||
]
|
||||
|
||||
operations = dict({
|
||||
'obj': [
|
||||
{'get': 'properties', 'delete': 'delete', 'put': 'update'},
|
||||
{'get': 'list', 'post': 'create', 'delete': 'delete'}
|
||||
],
|
||||
'nodes': [{'get': 'nodes'}, {'get': 'nodes'}],
|
||||
'msql': [{'get': 'msql'}, {'get': 'msql'}],
|
||||
'sql': [{'get': 'sql'}],
|
||||
'get_procedures': [{}, {'get': 'get_procedures'}],
|
||||
'enable_disable': [{'put': 'enable_disable'}],
|
||||
'get_programs': [{}, {'get': 'get_programs'}],
|
||||
'get_schedules': [{}, {'get': 'get_schedules'}],
|
||||
'run_job': [{'put': 'run_job'}],
|
||||
})
|
||||
|
||||
def _init_(self, **kwargs):
|
||||
self.conn = None
|
||||
self.template_path = None
|
||||
self.pr_template_path = None
|
||||
self.sch_template_path = None
|
||||
self.manager = None
|
||||
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def check_precondition(f):
|
||||
"""
|
||||
This function will behave as a decorator which will check the
|
||||
database connection before running view. It will also attach
|
||||
manager, conn & template_path properties to self
|
||||
"""
|
||||
|
||||
@wraps(f)
|
||||
def wrap(*args, **kwargs):
|
||||
# Here args[0] will hold self & kwargs will hold gid,sid,did
|
||||
self = args[0]
|
||||
self.driver = get_driver(PG_DEFAULT_DRIVER)
|
||||
self.manager = self.driver.connection_manager(kwargs['sid'])
|
||||
self.conn = self.manager.connection(did=kwargs['did'])
|
||||
# Set the template path for the SQL scripts
|
||||
self.template_path = self.BASE_TEMPLATE_PATH.format(
|
||||
self.manager.version)
|
||||
self.pr_template_path = self.PROGRAM_TEMPLATE_PATH.format(
|
||||
self.manager.version)
|
||||
self.sch_template_path = self.SCHEDULE_TEMPLATE_PATH.format(
|
||||
self.manager.version)
|
||||
|
||||
return f(*args, **kwargs)
|
||||
|
||||
return wrap
|
||||
|
||||
@check_precondition
|
||||
def list(self, gid, sid, did, jsid):
|
||||
"""
|
||||
This function is used to list all the job nodes within
|
||||
that collection.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
jsid: Job Scheduler ID
|
||||
"""
|
||||
sql = render_template(
|
||||
"/".join([self.template_path, self._PROPERTIES_SQL]))
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
return ajax_response(
|
||||
response=res['rows'],
|
||||
status=200
|
||||
)
|
||||
|
||||
@check_precondition
|
||||
def nodes(self, gid, sid, did, jsid, jsjobid=None):
|
||||
"""
|
||||
This function is used to create all the child nodes within
|
||||
the collection. Here it will create all the job nodes.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
jsid: Job Scheduler ID
|
||||
"""
|
||||
res = []
|
||||
try:
|
||||
sql = render_template(
|
||||
"/".join([self.template_path, self._NODES_SQL]))
|
||||
|
||||
status, result = self.conn.execute_2darray(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=result)
|
||||
|
||||
if jsjobid is not None:
|
||||
if len(result['rows']) == 0:
|
||||
return gone(
|
||||
errormsg=gettext("Could not find the specified job.")
|
||||
)
|
||||
|
||||
row = result['rows'][0]
|
||||
return make_json_response(
|
||||
data=self.blueprint.generate_browser_node(
|
||||
row['jsjobid'],
|
||||
DBMS_JOB_SCHEDULER_ID,
|
||||
row['jsjobname'],
|
||||
is_enabled=row['jsjobenabled'],
|
||||
icon="icon-pga_job" if row['jsjobenabled'] else
|
||||
"icon-pga_job-disabled",
|
||||
description=row['jsjobdesc']
|
||||
)
|
||||
)
|
||||
|
||||
for row in result['rows']:
|
||||
res.append(
|
||||
self.blueprint.generate_browser_node(
|
||||
row['jsjobid'],
|
||||
DBMS_JOB_SCHEDULER_ID,
|
||||
row['jsjobname'],
|
||||
is_enabled=row['jsjobenabled'],
|
||||
icon="icon-pga_job" if row['jsjobenabled'] else
|
||||
"icon-pga_job-disabled",
|
||||
description=row['jsjobdesc']
|
||||
)
|
||||
)
|
||||
|
||||
return make_json_response(
|
||||
data=res,
|
||||
status=200
|
||||
)
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def properties(self, gid, sid, did, jsid, jsjobid):
|
||||
"""
|
||||
This function will show the properties of the selected job node.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
jsid: Job Scheduler ID
|
||||
jsjobid: Job ID
|
||||
"""
|
||||
try:
|
||||
status, data = self._fetch_properties(jsjobid)
|
||||
if not status:
|
||||
return data
|
||||
|
||||
return ajax_response(
|
||||
response=data,
|
||||
status=200
|
||||
)
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
def _fetch_properties(self, jsjobid):
|
||||
"""
|
||||
This function is used to fetch the properties.
|
||||
Args:
|
||||
jsjobid:
|
||||
"""
|
||||
sql = render_template(
|
||||
"/".join([self.template_path, self._PROPERTIES_SQL]),
|
||||
jsjobid=jsjobid
|
||||
)
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return False, internal_server_error(errormsg=res)
|
||||
|
||||
if len(res['rows']) == 0:
|
||||
return False, gone(
|
||||
errormsg=gettext("Could not find the specified job.")
|
||||
)
|
||||
|
||||
data = res['rows'][0]
|
||||
|
||||
# If 'jsjobscname' and 'jsjobprname' in data then set the jsjobtype
|
||||
if ('jsjobscname' in data and data['jsjobscname'] is not None and
|
||||
'jsjobprname' in data and data['jsjobprname'] is not None):
|
||||
data['jsjobtype'] = 'p'
|
||||
else:
|
||||
data['jsjobtype'] = 's'
|
||||
|
||||
# Resolve the repeat interval string
|
||||
if 'jsscrepeatint' in data:
|
||||
(freq, by_date, by_month, by_month_day, by_weekday, by_hour,
|
||||
by_minute) = resolve_calendar_string(
|
||||
data['jsscrepeatint'])
|
||||
|
||||
data['jsscfreq'] = freq
|
||||
data['jsscdate'] = by_date
|
||||
data['jsscmonths'] = by_month
|
||||
data['jsscmonthdays'] = by_month_day
|
||||
data['jsscweekdays'] = by_weekday
|
||||
data['jsschours'] = by_hour
|
||||
data['jsscminutes'] = by_minute
|
||||
|
||||
# Get Program's arguments
|
||||
sql = render_template(
|
||||
"/".join([self.pr_template_path, self._PROPERTIES_SQL]),
|
||||
jsprid=data['program_id']
|
||||
)
|
||||
status, res_program = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res_program)
|
||||
|
||||
# Update the data dictionary.
|
||||
if len(res_program['rows']) > 0:
|
||||
# Get the formatted program args
|
||||
get_formatted_program_args(self.pr_template_path, self.conn,
|
||||
res_program['rows'][0])
|
||||
# Get the job argument value
|
||||
self.get_job_args_value(self.template_path, self.conn,
|
||||
data['jsjobname'],
|
||||
res_program['rows'][0])
|
||||
data['jsprarguments'] = res_program['rows'][0]['jsprarguments'] \
|
||||
if 'jsprarguments' in res_program['rows'][0] else []
|
||||
|
||||
return True, data
|
||||
|
||||
@check_precondition
|
||||
def create(self, gid, sid, did, jsid):
|
||||
"""
|
||||
This function will create the job node.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
jsid: Job Scheduler ID
|
||||
"""
|
||||
data = json.loads(request.data)
|
||||
try:
|
||||
# Get the SQL
|
||||
sql, _, _ = self.get_sql(None, data)
|
||||
|
||||
status, res = self.conn.execute_void('BEGIN')
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
status, res = self.conn.execute_scalar(sql)
|
||||
if not status:
|
||||
if self.conn.connected():
|
||||
self.conn.execute_void('END')
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
self.conn.execute_void('END')
|
||||
|
||||
# Get the newly created job id
|
||||
sql = render_template(
|
||||
"/".join([self.template_path, 'get_job_id.sql']),
|
||||
job_name=data['jsjobname'], conn=self.conn
|
||||
)
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
if len(res['rows']) == 0:
|
||||
return gone(
|
||||
errormsg=gettext("Job creation failed.")
|
||||
)
|
||||
row = res['rows'][0]
|
||||
|
||||
return jsonify(
|
||||
node=self.blueprint.generate_browser_node(
|
||||
row['jsjobid'],
|
||||
DBMS_JOB_SCHEDULER_ID,
|
||||
row['jsjobname'],
|
||||
is_enabled=row['jsjobenabled'],
|
||||
icon="icon-pga_job" if row['jsjobenabled'] else
|
||||
"icon-pga_job-disabled"
|
||||
)
|
||||
)
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def update(self, gid, sid, did, jsid, jsjobid=None):
|
||||
"""
|
||||
This function will update job object
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
jsid: Job Scheduler ID
|
||||
"""
|
||||
data = request.form if request.form else json.loads(
|
||||
request.data
|
||||
)
|
||||
|
||||
try:
|
||||
sql, jobname, jobenabled = self.get_sql(jsjobid, data)
|
||||
|
||||
status, res = self.conn.execute_scalar(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
return jsonify(
|
||||
node=self.blueprint.generate_browser_node(
|
||||
jsjobid,
|
||||
DBMS_JOB_SCHEDULER_ID,
|
||||
jobname,
|
||||
icon="icon-pga_job" if jobenabled else
|
||||
"icon-pga_job-disabled"
|
||||
)
|
||||
)
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def delete(self, gid, sid, did, jsid, jsjobid=None):
|
||||
"""Delete the Job."""
|
||||
|
||||
if jsjobid is None:
|
||||
data = request.form if request.form else json.loads(
|
||||
request.data
|
||||
)
|
||||
else:
|
||||
data = {'ids': [jsjobid]}
|
||||
|
||||
try:
|
||||
for jsjobid in data['ids']:
|
||||
status, data = self._fetch_properties(jsjobid)
|
||||
if not status:
|
||||
return data
|
||||
|
||||
jsjobname = data['jsjobname']
|
||||
|
||||
status, res = self.conn.execute_void(
|
||||
render_template(
|
||||
"/".join([self.template_path, self._DELETE_SQL]),
|
||||
job_name=jsjobname, conn=self.conn
|
||||
)
|
||||
)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
return make_json_response(success=1)
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def msql(self, gid, sid, did, jsid, jsjobid=None):
|
||||
"""
|
||||
This function is used to return modified SQL for the
|
||||
selected job node.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
jsid: Job Scheduler ID
|
||||
jsjobid: Job ID (optional)
|
||||
"""
|
||||
data = {}
|
||||
for k, v in request.args.items():
|
||||
try:
|
||||
# comments should be taken as is because if user enters a
|
||||
# json comment it is parsed by loads which should not happen
|
||||
if k in ('jsjobdesc',):
|
||||
data[k] = v
|
||||
else:
|
||||
data[k] = json.loads(v)
|
||||
except ValueError:
|
||||
data[k] = v
|
||||
|
||||
sql, _, _ = self.get_sql(jsjobid, data)
|
||||
|
||||
return make_json_response(
|
||||
data=sql,
|
||||
status=200
|
||||
)
|
||||
|
||||
def get_sql(self, jsjobid, data):
|
||||
"""
|
||||
This function is used to get the SQL.
|
||||
"""
|
||||
sql = ''
|
||||
name = ''
|
||||
enabled = True
|
||||
if jsjobid is None:
|
||||
name = data['jsjobname']
|
||||
enabled = data['jsjobenabled']
|
||||
|
||||
# Create calendar string for repeat interval
|
||||
repeat_interval = create_calendar_string(
|
||||
data['jsscfreq'], data['jsscdate'], data['jsscmonths'],
|
||||
data['jsscmonthdays'], data['jsscweekdays'], data['jsschours'],
|
||||
data['jsscminutes'])
|
||||
|
||||
sql = render_template(
|
||||
"/".join([self.template_path, self._CREATE_SQL]),
|
||||
job_name=data['jsjobname'],
|
||||
internal_job_type=data['jsjobtype'],
|
||||
job_type=data['jsprtype'],
|
||||
job_action=data['jsprproc']
|
||||
if data['jsprtype'] == 'STORED_PROCEDURE' else
|
||||
data['jsprcode'],
|
||||
enabled=data['jsjobenabled'],
|
||||
comments=data['jsjobdesc'],
|
||||
number_of_arguments=data['jsprnoofargs'],
|
||||
start_date=data['jsscstart'],
|
||||
repeat_interval=repeat_interval,
|
||||
end_date=data['jsscend'],
|
||||
program_name=data['jsjobprname'],
|
||||
schedule_name=data['jsjobscname'],
|
||||
arguments=data['jsprarguments'] if 'jsprarguments' in data
|
||||
else [],
|
||||
conn=self.conn
|
||||
)
|
||||
elif jsjobid is not None and 'jsprarguments' in data:
|
||||
status, res = self._fetch_properties(jsjobid)
|
||||
if not status:
|
||||
return res
|
||||
|
||||
name = res['jsjobname']
|
||||
enabled = res['jsjobenabled']
|
||||
sql = render_template(
|
||||
"/".join([self.template_path, self._UPDATE_SQL]),
|
||||
job_name=res['jsjobname'],
|
||||
changed_value=data['jsprarguments']['changed'],
|
||||
conn=self.conn
|
||||
)
|
||||
|
||||
return sql, name, enabled
|
||||
|
||||
@check_precondition
|
||||
def sql(self, gid, sid, did, jsid, jsjobid):
|
||||
"""
|
||||
This function will generate sql for the sql panel
|
||||
"""
|
||||
try:
|
||||
status, data = self._fetch_properties(jsjobid)
|
||||
if not status:
|
||||
return ''
|
||||
|
||||
SQL = render_template(
|
||||
"/".join([self.template_path, self._CREATE_SQL]),
|
||||
display_comments=True,
|
||||
job_name=data['jsjobname'],
|
||||
internal_job_type=data['jsjobtype'],
|
||||
job_type=data['jsprtype'],
|
||||
job_action=data['jsprproc']
|
||||
if data['jsprtype'] == 'STORED_PROCEDURE' else
|
||||
data['jsprcode'],
|
||||
enabled=data['jsjobenabled'],
|
||||
comments=data['jsjobdesc'],
|
||||
number_of_arguments=data['jsprnoofargs'],
|
||||
start_date=data['jsscstart'],
|
||||
repeat_interval=data['jsscrepeatint'],
|
||||
end_date=data['jsscend'],
|
||||
program_name=data['jsjobprname'],
|
||||
schedule_name=data['jsjobscname'],
|
||||
arguments=data['jsprarguments'] if 'jsprarguments' in data
|
||||
else [],
|
||||
conn=self.conn
|
||||
)
|
||||
|
||||
return ajax_response(response=SQL)
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def get_procedures(self, gid, sid, did, jsid=None):
|
||||
"""
|
||||
This function will return procedure list
|
||||
:param gid: group id
|
||||
:param sid: server id
|
||||
:param did: database id
|
||||
:return:
|
||||
"""
|
||||
res = []
|
||||
sql = render_template("/".join([self.pr_template_path,
|
||||
'get_procedures.sql']),
|
||||
datlastsysoid=self._DATABASE_LAST_SYSTEM_OID)
|
||||
status, rset = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=rset)
|
||||
|
||||
for row in rset['rows']:
|
||||
# Get formatted Arguments
|
||||
frmtd_params, _ = format_arguments_from_db(
|
||||
self.template_path, self.conn, row)
|
||||
|
||||
res.append({'label': row['proc_name'],
|
||||
'value': row['proc_name'],
|
||||
'no_of_args': row['number_of_arguments'],
|
||||
'arguments': frmtd_params['arguments']
|
||||
})
|
||||
|
||||
return make_json_response(
|
||||
data=res,
|
||||
status=200
|
||||
)
|
||||
|
||||
@check_precondition
|
||||
def enable_disable(self, gid, sid, did, jsid, jsjobid=None):
|
||||
"""
|
||||
This function is used to enable/disable job.
|
||||
"""
|
||||
data = request.form if request.form else json.loads(
|
||||
request.data
|
||||
)
|
||||
|
||||
status, res = self.conn.execute_void(
|
||||
render_template(
|
||||
"/".join([self.pr_template_path, 'enable_disable.sql']),
|
||||
name=data['job_name'],
|
||||
is_enable=data['is_enable_job'], conn=self.conn
|
||||
)
|
||||
)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
return make_json_response(
|
||||
success=1,
|
||||
info=gettext("Job enabled") if data['is_enable_job'] else
|
||||
gettext('Job disabled'),
|
||||
data={
|
||||
'sid': sid,
|
||||
'did': did,
|
||||
'jsid': jsid,
|
||||
'jsjobid': jsjobid
|
||||
}
|
||||
)
|
||||
|
||||
@check_precondition
|
||||
def get_programs(self, gid, sid, did, jsid=None):
|
||||
"""
|
||||
This function will return procedure list
|
||||
:param gid: group id
|
||||
:param sid: server id
|
||||
:param did: database id
|
||||
:return:
|
||||
"""
|
||||
res = []
|
||||
sql = render_template("/".join([self.pr_template_path,
|
||||
self._NODES_SQL]),
|
||||
datlastsysoid=self._DATABASE_LAST_SYSTEM_OID)
|
||||
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['jsprname'],
|
||||
'value': row['jsprname']})
|
||||
|
||||
return make_json_response(
|
||||
data=res,
|
||||
status=200
|
||||
)
|
||||
|
||||
@check_precondition
|
||||
def get_schedules(self, gid, sid, did, jsid=None):
|
||||
"""
|
||||
This function will return procedure list
|
||||
:param gid: group id
|
||||
:param sid: server id
|
||||
:param did: database id
|
||||
:return:
|
||||
"""
|
||||
res = []
|
||||
sql = render_template("/".join([self.sch_template_path,
|
||||
self._NODES_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['jsscname'],
|
||||
'value': row['jsscname']
|
||||
})
|
||||
|
||||
return make_json_response(
|
||||
data=res,
|
||||
status=200
|
||||
)
|
||||
|
||||
@check_precondition
|
||||
def run_job(self, gid, sid, did, jsid, jsjobid=None):
|
||||
"""
|
||||
This function is used to run the job now.
|
||||
"""
|
||||
data = request.form if request.form else json.loads(
|
||||
request.data
|
||||
)
|
||||
|
||||
try:
|
||||
status, res = self.conn.execute_void(
|
||||
render_template(
|
||||
"/".join([self.template_path, 'run_job.sql']),
|
||||
job_name=data['job_name'], conn=self.conn
|
||||
)
|
||||
)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
return success_return(
|
||||
message=gettext("Started the Job execution.")
|
||||
)
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
def get_job_args_value(self, template_path, conn, jobname, data):
|
||||
"""
|
||||
This function is used to get the job arguments value.
|
||||
Args:
|
||||
template_path:
|
||||
conn:
|
||||
jobname:
|
||||
data:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
if 'jsprarguments' in data and len(data['jsprarguments']) > 0:
|
||||
for args in data['jsprarguments']:
|
||||
sql = render_template(
|
||||
"/".join([template_path, 'get_job_args_value.sql']),
|
||||
job_name=jobname,
|
||||
arg_name=args['argname'], conn=self.conn)
|
||||
status, res = conn.execute_scalar(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
args['argval'] = res
|
||||
|
||||
|
||||
DBMSJobView.register_node_view(blueprint)
|
||||
@@ -0,0 +1,234 @@
|
||||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2024, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import DBMSJobSchema from './dbms_job.ui';
|
||||
import { getNodeAjaxOptions } from '../../../../../../../static/js/node_ajax';
|
||||
import getApiInstance from '../../../../../../../../static/js/api_instance';
|
||||
|
||||
|
||||
define('pgadmin.node.dbms_job', [
|
||||
'sources/gettext', 'sources/url_for', 'sources/pgadmin',
|
||||
'pgadmin.browser', 'pgadmin.browser.collection',
|
||||
], function(gettext, url_for, pgAdmin, pgBrowser) {
|
||||
|
||||
if (!pgBrowser.Nodes['coll-dbms_job']) {
|
||||
pgBrowser.Nodes['coll-dbms_job'] =
|
||||
pgBrowser.Collection.extend({
|
||||
node: 'dbms_job',
|
||||
label: gettext('DBMS Jobs'),
|
||||
type: 'coll-dbms_job',
|
||||
columns: ['jsjobname', 'jsjobenabled', 'jsjobruncount', 'jsjobfailurecount', 'jsjobdesc'],
|
||||
hasSQL: false,
|
||||
hasDepends: false,
|
||||
hasStatistics: false,
|
||||
hasScriptTypes: [],
|
||||
canDrop: true,
|
||||
canDropCascade: false,
|
||||
});
|
||||
}
|
||||
|
||||
if (!pgBrowser.Nodes['dbms_job']) {
|
||||
pgAdmin.Browser.Nodes['dbms_job'] = pgAdmin.Browser.Node.extend({
|
||||
parent_type: 'dbms_job_scheduler',
|
||||
type: 'dbms_job',
|
||||
label: gettext('DBMS Job'),
|
||||
node_image: 'icon-pga_job',
|
||||
epasHelp: true,
|
||||
epasURL: 'https://www.enterprisedb.com/docs/epas/$VERSION$/epas_compat_bip_guide/03_built-in_packages/15_dbms_scheduler/02_create_job/',
|
||||
dialogHelp: url_for('help.static', {'filename': 'dbms_job.html'}),
|
||||
canDrop: true,
|
||||
hasSQL: true,
|
||||
hasDepends: false,
|
||||
hasStatistics: false,
|
||||
Init: function() {
|
||||
/* Avoid multiple registration of menus */
|
||||
if (this.initialized)
|
||||
return;
|
||||
|
||||
this.initialized = true;
|
||||
|
||||
pgBrowser.add_menus([{
|
||||
name: 'create_dbms_job_on_coll', node: 'coll-dbms_job', module: this,
|
||||
applies: ['object', 'context'], callback: 'show_obj_properties',
|
||||
category: 'create', priority: 4, label: gettext('DBMS Job...'),
|
||||
data: {action: 'create'},
|
||||
},{
|
||||
name: 'create_dbms_job', node: 'dbms_job', module: this,
|
||||
applies: ['object', 'context'], callback: 'show_obj_properties',
|
||||
category: 'create', priority: 4, label: gettext('DBMS Job...'),
|
||||
data: {action: 'create'},
|
||||
},{
|
||||
name: 'create_dbms_job', node: 'dbms_job_scheduler', module: this,
|
||||
applies: ['object', 'context'], callback: 'show_obj_properties',
|
||||
category: 'create', priority: 4, label: gettext('DBMS Job...'),
|
||||
data: {action: 'create'},
|
||||
}, {
|
||||
name: 'enable_job', node: 'dbms_job', module: this,
|
||||
applies: ['object', 'context'], callback: 'enable_job',
|
||||
priority: 4, label: gettext('Enable Job'),
|
||||
enable : 'is_enabled',data: {
|
||||
data_disabled: gettext('Job is already enabled.'),
|
||||
},
|
||||
}, {
|
||||
name: 'disable_job', node: 'dbms_job', module: this,
|
||||
applies: ['object', 'context'], callback: 'disable_job',
|
||||
priority: 4, label: gettext('Disable Job'),
|
||||
enable : 'is_disabled',data: {
|
||||
data_disabled: gettext('Job is already disabled.'),
|
||||
},
|
||||
}, {
|
||||
name: 'run_job', node: 'dbms_job', module: this,
|
||||
applies: ['object', 'context'], callback: 'run_job',
|
||||
priority: 4, label: gettext('Run Job'),
|
||||
enable : 'is_disabled', data: {
|
||||
data_disabled: gettext('Job is already disabled.'),
|
||||
}
|
||||
}
|
||||
]);
|
||||
},
|
||||
is_enabled: function(node) {
|
||||
return !node?.is_enabled;
|
||||
},
|
||||
is_disabled: function(node) {
|
||||
return node?.is_enabled;
|
||||
},
|
||||
callbacks: {
|
||||
enable_job: function(args, notify) {
|
||||
let input = args || {},
|
||||
obj = this,
|
||||
t = pgBrowser.tree,
|
||||
i = 'item' in input ? input.item : t.selected(),
|
||||
d = i ? t.itemData(i) : undefined;
|
||||
|
||||
if (d) {
|
||||
notify = notify || _.isUndefined(notify) || _.isNull(notify);
|
||||
let enable = function() {
|
||||
let data = d;
|
||||
getApiInstance().put(
|
||||
obj.generate_url(i, 'enable_disable', d, true),
|
||||
{'job_name': data.label, 'is_enable_job': true}
|
||||
).then(({data: res})=> {
|
||||
if (res.success == 1) {
|
||||
pgAdmin.Browser.notifier.success(res.info);
|
||||
t.removeIcon(i);
|
||||
data.icon = 'icon-pga_jobstep';
|
||||
data.is_enabled = true;
|
||||
t.addIcon(i, {icon: data.icon});
|
||||
t.updateAndReselectNode(i, data);
|
||||
}
|
||||
}).catch(function(error) {
|
||||
pgAdmin.Browser.notifier.pgRespErrorNotify(error);
|
||||
t.refresh(i);
|
||||
});
|
||||
};
|
||||
if (notify) {
|
||||
pgAdmin.Browser.notifier.confirm(
|
||||
gettext('Enable Job'),
|
||||
gettext('Are you sure you want to enable the job %s?', d.label),
|
||||
function() { enable(); },
|
||||
function() { return true;},
|
||||
);
|
||||
} else {
|
||||
enable();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
disable_job: function(args, notify) {
|
||||
let input = args || {},
|
||||
obj = this,
|
||||
t = pgBrowser.tree,
|
||||
i = 'item' in input ? input.item : t.selected(),
|
||||
d = i ? t.itemData(i) : undefined;
|
||||
|
||||
if (d) {
|
||||
notify = notify || _.isUndefined(notify) || _.isNull(notify);
|
||||
let disable = function() {
|
||||
let data = d;
|
||||
getApiInstance().put(
|
||||
obj.generate_url(i, 'enable_disable', d, true),
|
||||
{'job_name': data.label, 'is_enable_job': false}
|
||||
).then(({data: res})=> {
|
||||
if (res.success == 1) {
|
||||
pgAdmin.Browser.notifier.success(res.info);
|
||||
t.removeIcon(i);
|
||||
data.icon = 'icon-pga_jobstep-disabled';
|
||||
data.is_enabled = false;
|
||||
t.addIcon(i, {icon: data.icon});
|
||||
t.updateAndReselectNode(i, data);
|
||||
}
|
||||
}).catch(function(error) {
|
||||
pgAdmin.Browser.notifier.pgRespErrorNotify(error);
|
||||
t.refresh(i);
|
||||
});
|
||||
};
|
||||
if (notify) {
|
||||
pgAdmin.Browser.notifier.confirm(
|
||||
gettext('Disable Job'),
|
||||
gettext('Are you sure you want to disable the job %s?', d.label),
|
||||
function() { disable(); },
|
||||
function() { return true;},
|
||||
);
|
||||
} else {
|
||||
disable();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
run_job: function(args, notify) {
|
||||
let input = args || {},
|
||||
obj = this,
|
||||
t = pgBrowser.tree,
|
||||
i = 'item' in input ? input.item : t.selected(),
|
||||
d = i ? t.itemData(i) : undefined;
|
||||
|
||||
if (d) {
|
||||
notify = notify || _.isUndefined(notify) || _.isNull(notify);
|
||||
let run = function() {
|
||||
let data = d;
|
||||
getApiInstance().put(
|
||||
obj.generate_url(i, 'run_job', d, true),
|
||||
{'job_name': data.label}
|
||||
).then(({data: res})=> {
|
||||
if (res.success == 1) {
|
||||
pgAdmin.Browser.notifier.success(res.info);
|
||||
}
|
||||
}).catch(function(error) {
|
||||
pgAdmin.Browser.notifier.pgRespErrorNotify(error);
|
||||
});
|
||||
};
|
||||
if (notify) {
|
||||
pgAdmin.Browser.notifier.confirm(
|
||||
gettext('Run Job'),
|
||||
gettext('Are you sure you want to run the job %s now?', d.label),
|
||||
function() { run(); },
|
||||
function() { return true;},
|
||||
);
|
||||
} else {
|
||||
run();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
},
|
||||
getSchema: function(treeNodeInfo, itemNodeData) {
|
||||
return new DBMSJobSchema(
|
||||
{
|
||||
procedures: ()=>getNodeAjaxOptions('get_procedures', this, treeNodeInfo, itemNodeData),
|
||||
programs: ()=>getNodeAjaxOptions('get_programs', this, treeNodeInfo, itemNodeData),
|
||||
schedules: ()=>getNodeAjaxOptions('get_schedules', this, treeNodeInfo, itemNodeData)
|
||||
}
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return pgBrowser.Nodes['dbms_job'];
|
||||
});
|
||||
@@ -0,0 +1,198 @@
|
||||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2024, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import gettext from 'sources/gettext';
|
||||
import BaseUISchema from 'sources/SchemaView/base_schema.ui';
|
||||
import { isEmptyString } from 'sources/validators';
|
||||
import moment from 'moment';
|
||||
import { getActionSchema, getRepeatSchema } from '../../../static/js/dbms_job_scheduler_common.ui';
|
||||
|
||||
|
||||
export default class DBMSJobSchema extends BaseUISchema {
|
||||
constructor(fieldOptions={}) {
|
||||
super({
|
||||
jsjobid: null,
|
||||
jsjobname: '',
|
||||
jsjobenabled: true,
|
||||
jsjobdesc: '',
|
||||
jsjobtype: 's',
|
||||
jsjobruncount: 0,
|
||||
jsjobfailurecount: 0,
|
||||
// Program Args
|
||||
jsjobprname: '',
|
||||
jsprtype: 'PLSQL_BLOCK',
|
||||
jsprenabled: true,
|
||||
jsprnoofargs: 0,
|
||||
jsprproc: null,
|
||||
jsprcode: null,
|
||||
jsprarguments: [],
|
||||
// Schedule args
|
||||
jsjobscname: '',
|
||||
jsscstart: null,
|
||||
jsscend: null,
|
||||
jsscrepeatint: '',
|
||||
jsscfreq: null,
|
||||
jsscdate: null,
|
||||
jsscweekdays: null,
|
||||
jsscmonthdays: null,
|
||||
jsscmonths: null,
|
||||
jsschours: null,
|
||||
jsscminutes: null,
|
||||
});
|
||||
this.fieldOptions = {
|
||||
procedures: [],
|
||||
programs: [],
|
||||
schedules: [],
|
||||
...fieldOptions,
|
||||
};
|
||||
}
|
||||
|
||||
get idAttribute() {
|
||||
return 'jsjobid';
|
||||
}
|
||||
|
||||
get baseFields() {
|
||||
let obj = this;
|
||||
return [
|
||||
{
|
||||
id: 'jsjobid', label: gettext('ID'), type: 'int', mode: ['properties'],
|
||||
readonly: function(state) {return !obj.isNew(state); },
|
||||
}, {
|
||||
id: 'jsjobname', label: gettext('Name'), cell: 'text',
|
||||
editable: false, type: 'text', noEmpty: true,
|
||||
readonly: function(state) {return !obj.isNew(state); },
|
||||
}, {
|
||||
id: 'jsjobenabled', label: gettext('Enabled?'), type: 'switch', cell: 'switch',
|
||||
readonly: function(state) {return !obj.isNew(state); },
|
||||
}, {
|
||||
id: 'jsjobtype', label: gettext('Job Type'),
|
||||
type: ()=>{
|
||||
let options = [
|
||||
{'label': gettext('SELF-CONTAINED'), 'value': 's'},
|
||||
{'label': gettext('PRE-DEFINED'), 'value': 'p'},
|
||||
];
|
||||
return {
|
||||
type: 'toggle',
|
||||
options: options,
|
||||
};
|
||||
},
|
||||
readonly: function(state) {return !obj.isNew(state); },
|
||||
helpMessage: gettext('If the Job Type is Self-Contained you need to specify the action and repeat interval in the Action and Repeat tabs respectively. If the Job Type is Pre-Defined you need to specify the existing Program and Schedule names in the Pre-Defined tab.'),
|
||||
helpMessageMode: ['create'],
|
||||
}, {
|
||||
id: 'jsjobruncount', label: gettext('Run Count'), type: 'int',
|
||||
readonly: true, mode: ['edit', 'properties']
|
||||
}, {
|
||||
id: 'jsjobfailurecount', label: gettext('Failure Count'), type: 'int',
|
||||
readonly: true, mode: ['edit', 'properties']
|
||||
}, {
|
||||
id: 'jsjobdesc', label: gettext('Comment'), type: 'multiline',
|
||||
readonly: function(state) {return !obj.isNew(state); },
|
||||
},
|
||||
// Add the Action Schema
|
||||
...getActionSchema(obj, 'job'),
|
||||
// Add the Repeat Schema.
|
||||
...getRepeatSchema(obj, 'job'),
|
||||
{
|
||||
id: 'jsjobprname', label: gettext('Program Name'), type: 'select',
|
||||
controlProps: { allowClear: false}, group: gettext('Pre-Defined'),
|
||||
options: this.fieldOptions.programs,
|
||||
readonly: function(state) {
|
||||
return !obj.isNew(state) || state.jsjobtype == 's';
|
||||
},
|
||||
deps: ['jsjobtype'],
|
||||
depChange: (state) => {
|
||||
if (state.jsjobtype == 's') {
|
||||
return { jsjobprname: null };
|
||||
}
|
||||
}
|
||||
}, {
|
||||
id: 'jsjobscname', label: gettext('Schedule Name'), type: 'select',
|
||||
controlProps: { allowClear: false}, group: gettext('Pre-Defined'),
|
||||
options: this.fieldOptions.schedules,
|
||||
readonly: function(state) {
|
||||
return !obj.isNew(state) || state.jsjobtype == 's';
|
||||
},
|
||||
deps: ['jsjobtype'],
|
||||
depChange: (state) => {
|
||||
if (state.jsjobtype == 's') {
|
||||
return { jsjobscname: null };
|
||||
}
|
||||
}
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
validate(state, setError) {
|
||||
if (state.jsjobtype == 's' ) {
|
||||
if (isEmptyString(state.jsprtype)) {
|
||||
setError('jsprtype', gettext('Job Type cannot be empty.'));
|
||||
return true;
|
||||
} else {
|
||||
setError('jsprtype', null);
|
||||
}
|
||||
|
||||
if (state.jsprtype == 'PLSQL_BLOCK' && isEmptyString(state.jsprcode)) {
|
||||
setError('jsprcode', gettext('Code cannot be empty.'));
|
||||
return true;
|
||||
} else {
|
||||
setError('jsprcode', null);
|
||||
}
|
||||
|
||||
if (state.jsprtype == 'STORED_PROCEDURE' && isEmptyString(state.jsprproc)) {
|
||||
setError('jsprproc', gettext('Procedure cannot be empty.'));
|
||||
return true;
|
||||
} else {
|
||||
setError('jsprproc', null);
|
||||
}
|
||||
|
||||
if (isEmptyString(state.jsscstart) && isEmptyString(state.jsscfreq) &&
|
||||
isEmptyString(state.jsscmonths) && isEmptyString(state.jsscweekdays) &&
|
||||
isEmptyString(state.jsscmonthdays) && isEmptyString(state.jsschours) &&
|
||||
isEmptyString(state.jsscminutes) && isEmptyString(state.jsscdate)) {
|
||||
setError('jsscstart', gettext('Either Start time or Repeat interval must be specified.'));
|
||||
return true;
|
||||
} else {
|
||||
setError('jsscstart', null);
|
||||
}
|
||||
|
||||
if (!isEmptyString(state.jsscend)) {
|
||||
let start_time = state.jsscstart,
|
||||
end_time = state.jsscend,
|
||||
start_time_js = start_time.split(' '),
|
||||
end_time_js = end_time.split(' ');
|
||||
|
||||
start_time_js = moment(start_time_js[0] + ' ' + start_time_js[1]);
|
||||
end_time_js = moment(end_time_js[0] + ' ' + end_time_js[1]);
|
||||
|
||||
if(end_time_js.isBefore(start_time_js)) {
|
||||
setError('jsscend', gettext('Start time must be less than end time'));
|
||||
return true;
|
||||
} else {
|
||||
setError('jsscend', null);
|
||||
}
|
||||
} else {
|
||||
state.jsscend = null;
|
||||
}
|
||||
} else if (state.jsjobtype == 'p') {
|
||||
if (isEmptyString(state.jsjobprname)) {
|
||||
setError('jsjobprname', gettext('Pre-Defined program name cannot be empty.'));
|
||||
return true;
|
||||
} else {
|
||||
setError('jsjobprname', null);
|
||||
}
|
||||
if (isEmptyString(state.jsjobscname)) {
|
||||
setError('jsjobscname', gettext('Pre-Defined schedule name cannot be empty.'));
|
||||
return true;
|
||||
} else {
|
||||
setError('jsjobscname', null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
{% if display_comments %}
|
||||
-- DBMS Job: '{{ job_name }}'
|
||||
|
||||
-- EXEC dbms_scheduler.DROP_JOB('{{ job_name }}');
|
||||
|
||||
{% endif %}
|
||||
{% if internal_job_type is defined and internal_job_type == 's' %}
|
||||
EXEC dbms_scheduler.CREATE_JOB(
|
||||
job_name => {{ job_name|qtLiteral(conn) }},
|
||||
job_type => {{ job_type|qtLiteral(conn) }},
|
||||
job_action => {{ job_action|qtLiteral(conn) }},
|
||||
repeat_interval => {{ repeat_interval|qtLiteral(conn) }}{% if start_date or end_date or number_of_arguments or enabled or comments %},{% endif %}
|
||||
{% if start_date %}
|
||||
|
||||
start_date => {{ start_date|qtLiteral(conn) }}{% if end_date or number_of_arguments or enabled or comments %},{% endif %}
|
||||
{% endif %}
|
||||
{% if end_date %}
|
||||
|
||||
end_date => {{ end_date|qtLiteral(conn) }}{% if number_of_arguments or enabled or comments %},{% endif %}
|
||||
{% endif %}
|
||||
{% if number_of_arguments %}
|
||||
|
||||
number_of_arguments => {{ number_of_arguments }}{% if enabled or comments %},{% endif %}
|
||||
{% endif %}
|
||||
{% if enabled %}
|
||||
|
||||
enabled => {{ enabled }}{% if comments %},{% endif %}
|
||||
{% endif %}
|
||||
{% if comments %}
|
||||
|
||||
comments => {{ comments|qtLiteral(conn) }}
|
||||
{% endif %}
|
||||
);
|
||||
{% elif internal_job_type is defined and internal_job_type == 'p' %}
|
||||
EXEC dbms_scheduler.CREATE_JOB(
|
||||
job_name => {{ job_name|qtLiteral(conn) }},
|
||||
program_name => {{ program_name|qtLiteral(conn) }},
|
||||
schedule_name => {{ schedule_name|qtLiteral(conn) }}{% if enabled or comments %},{% endif %}
|
||||
{% if enabled %}
|
||||
|
||||
enabled => {{ enabled }}{% if comments %},{% endif %}
|
||||
{% endif %}
|
||||
{% if comments %}
|
||||
|
||||
comments => {{ comments|qtLiteral(conn) }}
|
||||
{% endif %}
|
||||
);
|
||||
{% endif %}
|
||||
|
||||
{% for args_list_item in arguments %}
|
||||
EXEC dbms_scheduler.SET_JOB_ARGUMENT_VALUE(
|
||||
job_name => {{ job_name|qtLiteral(conn) }},
|
||||
argument_name => {{ args_list_item.argname|qtLiteral(conn) }},
|
||||
{% if args_list_item.argval is defined and args_list_item.argval != '' %}
|
||||
argument_value => {{ args_list_item.argval|qtLiteral(conn) }}
|
||||
{% elif args_list_item.argdefval is defined %}
|
||||
argument_value => {{ args_list_item.argdefval|qtLiteral(conn) }}
|
||||
{% endif %}
|
||||
);
|
||||
|
||||
{% endfor %}
|
||||
@@ -0,0 +1,3 @@
|
||||
EXEC dbms_scheduler.DROP_JOB(
|
||||
{{ job_name|qtLiteral(conn) }}
|
||||
);
|
||||
@@ -0,0 +1,3 @@
|
||||
SELECT value
|
||||
FROM dba_scheduler_job_args
|
||||
WHERE job_name = {{ job_name|qtLiteral(conn) }} AND argument_name = {{ arg_name|qtLiteral(conn) }}
|
||||
@@ -0,0 +1,5 @@
|
||||
SELECT
|
||||
dsj_job_id AS jsjobid, dsj_job_name AS jsjobname,
|
||||
dsj_enabled AS jsjobenabled
|
||||
FROM sys.scheduler_0400_job
|
||||
WHERE dsj_job_name={{ job_name|qtLiteral(conn) }}
|
||||
@@ -0,0 +1,4 @@
|
||||
SELECT
|
||||
dsj_job_id as jsjobid, dsj_job_name as jsjobname,
|
||||
dsj_enabled as jsjobenabled, dsj_comments as jsjobdesc
|
||||
FROM sys.scheduler_0400_job;
|
||||
@@ -0,0 +1,14 @@
|
||||
SELECT
|
||||
job.dsj_job_id as jsjobid, job_name as jsjobname, program_name as jsjobprname, job_type as jsprtype,
|
||||
CASE WHEN job_type = 'PLSQL_BLOCK' THEN job_action ELSE '' END AS jsprcode,
|
||||
CASE WHEN job_type = 'STORED_PROCEDURE' THEN job_action ELSE '' END AS jsprproc,
|
||||
job_action, number_of_arguments as jsprnoofargs, schedule_name as jsjobscname,
|
||||
start_date as jsscstart, end_date as jsscend, repeat_interval as jsscrepeatint,
|
||||
enabled as jsjobenabled, comments as jsjobdesc,
|
||||
run_count as jsjobruncount, failure_count as jsjobfailurecount,
|
||||
job.dsj_program_id as program_id, job.dsj_schedule_id as schedule_id
|
||||
FROM sys.dba_scheduler_jobs jobv
|
||||
LEFT JOIN sys.scheduler_0400_job job ON jobv.job_name = job.dsj_job_name
|
||||
{% if jsjobid %}
|
||||
WHERE job.dsj_job_id={{jsjobid}}::oid
|
||||
{% endif %}
|
||||
@@ -0,0 +1,3 @@
|
||||
EXEC dbms_scheduler.RUN_JOB(
|
||||
{{ job_name|qtLiteral(conn) }}
|
||||
);
|
||||
@@ -0,0 +1,12 @@
|
||||
{% for chval in changed_value %}
|
||||
EXEC dbms_scheduler.SET_JOB_ARGUMENT_VALUE(
|
||||
job_name => {{ job_name|qtLiteral(conn) }},
|
||||
argument_name => {{ chval.argname|qtLiteral(conn) }},
|
||||
{% if chval.argval is defined and chval.argval != '' %}
|
||||
argument_value => {{ chval.argval|qtLiteral(conn) }}
|
||||
{% elif chval.argdefval is defined %}
|
||||
argument_value => {{ chval.argdefval|qtLiteral(conn) }}
|
||||
{% endif %}
|
||||
);
|
||||
|
||||
{% endfor %}
|
||||
@@ -0,0 +1,429 @@
|
||||
{
|
||||
"dbms_create_job": [
|
||||
{
|
||||
"name": "Create job when type is self contained and PLSQL_BLOCK",
|
||||
"url": "/browser/dbms_job/obj/",
|
||||
"is_positive_test": true,
|
||||
"test_data": {
|
||||
"jsjobid": null,
|
||||
"jsjobname": "job_self_with_psql",
|
||||
"jsjobenabled": true,
|
||||
"jsjobdesc": "This is a self contained psql job.",
|
||||
"jsjobtype": "s",
|
||||
"jsprtype": "PLSQL_BLOCK",
|
||||
"jsprnoofargs": 0,
|
||||
"jsprarguments": [],
|
||||
"jsprproc": null,
|
||||
"jsprcode": "BEGIN PERFORM 1; END;",
|
||||
"jsscstart": "2024-02-27 00:00:00 +05:30",
|
||||
"jsscend": "2054-02-28 00:00:00 +05:30",
|
||||
"jsscfreq": "YEARLY",
|
||||
"jsscdate": null,
|
||||
"jsscweekdays": ["7", "1", "2", "3", "4", "5", "6"],
|
||||
"jsscmonthdays": ["2", "8", "31", "27"],
|
||||
"jsscmonths": ["1", "5", "12"],
|
||||
"jsschours": ["05", "18", "22"],
|
||||
"jsscminutes": ["45", "37", "58"],
|
||||
"jsjobprname": "",
|
||||
"jsjobscname": ""
|
||||
},
|
||||
"mocking_required": false,
|
||||
"mock_data": {},
|
||||
"expected_data": {
|
||||
"status_code": 200,
|
||||
"error_msg": null,
|
||||
"test_result_data": {}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Create job when type is self contained and STORED_PROCEDURE without arguments",
|
||||
"url": "/browser/dbms_job/obj/",
|
||||
"is_positive_test": true,
|
||||
"test_data": {
|
||||
"jsjobid": null,
|
||||
"jsjobname": "job_self_with_proc_noargs",
|
||||
"jsjobenabled": true,
|
||||
"jsjobdesc": "This is a self contained stored procedure job with no args.",
|
||||
"jsjobtype": "s",
|
||||
"jsprtype": "STORED_PROCEDURE",
|
||||
"jsprnoofargs": 0,
|
||||
"jsprarguments": [],
|
||||
"jsprproc": "public.test_proc_without_args",
|
||||
"jsprcode": null,
|
||||
"jsscstart": "2024-02-27 00:00:00 +05:30",
|
||||
"jsscend": "2054-02-28 00:00:00 +05:30",
|
||||
"jsscfreq": "YEARLY",
|
||||
"jsscdate": "20250113",
|
||||
"jsscweekdays": [],
|
||||
"jsscmonthdays": [],
|
||||
"jsscmonths": [],
|
||||
"jsschours": [],
|
||||
"jsscminutes": [],
|
||||
"jsjobprname": "",
|
||||
"jsjobscname": ""
|
||||
},
|
||||
"mocking_required": false,
|
||||
"mock_data": {},
|
||||
"expected_data": {
|
||||
"status_code": 200,
|
||||
"error_msg": null,
|
||||
"test_result_data": {}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Create job when type is pre-defined and program is PLSQL",
|
||||
"url": "/browser/dbms_job/obj/",
|
||||
"is_positive_test": true,
|
||||
"test_data": {
|
||||
"jsjobid": null,
|
||||
"jsjobname": "job_pre_with_psql",
|
||||
"jsjobenabled": true,
|
||||
"jsjobdesc": "This is a pre-defined job with PLSQL program.",
|
||||
"jsjobtype": "p",
|
||||
"jsjobprname": "prg_with_psql",
|
||||
"jsjobscname": "yearly_sch",
|
||||
"jsprtype": null,
|
||||
"jsprnoofargs": 0,
|
||||
"jsprarguments": [],
|
||||
"jsprproc": null,
|
||||
"jsprcode": null,
|
||||
"jsscstart": null,
|
||||
"jsscend": null,
|
||||
"jsscfreq": null,
|
||||
"jsscdate": null,
|
||||
"jsscweekdays": null,
|
||||
"jsscmonthdays": null,
|
||||
"jsscmonths": null,
|
||||
"jsschours": null,
|
||||
"jsscminutes": null
|
||||
},
|
||||
"mocking_required": false,
|
||||
"mock_data": {},
|
||||
"expected_data": {
|
||||
"status_code": 200,
|
||||
"error_msg": null,
|
||||
"test_result_data": {}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Create job when type is pre-defined and program is Stored Procedure without args",
|
||||
"url": "/browser/dbms_job/obj/",
|
||||
"is_positive_test": true,
|
||||
"test_data": {
|
||||
"jsjobid": null,
|
||||
"jsjobname": "job_pre_with_proc_noargs",
|
||||
"jsjobenabled": true,
|
||||
"jsjobdesc": "This is a pre-defined job with Stored Procedure without args",
|
||||
"jsjobtype": "p",
|
||||
"jsjobprname": "prg_with_proc_noargs",
|
||||
"jsjobscname": "yearly_sch",
|
||||
"jsprtype": null,
|
||||
"jsprnoofargs": 0,
|
||||
"jsprarguments": [],
|
||||
"jsprproc": null,
|
||||
"jsprcode": null,
|
||||
"jsscstart": null,
|
||||
"jsscend": null,
|
||||
"jsscfreq": null,
|
||||
"jsscdate": null,
|
||||
"jsscweekdays": null,
|
||||
"jsscmonthdays": null,
|
||||
"jsscmonths": null,
|
||||
"jsschours": null,
|
||||
"jsscminutes": null
|
||||
},
|
||||
"mocking_required": false,
|
||||
"mock_data": {},
|
||||
"expected_data": {
|
||||
"status_code": 200,
|
||||
"error_msg": null,
|
||||
"test_result_data": {}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Create job when type is pre-defined and program is Stored Procedure with args",
|
||||
"url": "/browser/dbms_job/obj/",
|
||||
"is_positive_test": true,
|
||||
"test_data": {
|
||||
"jsjobid": null,
|
||||
"jsjobname": "job_pre_with_proc_args",
|
||||
"jsjobenabled": true,
|
||||
"jsjobdesc": "This is a pre-defined job with program is Stored Procedure with args.",
|
||||
"jsjobtype": "p",
|
||||
"jsjobprname": "prg_with_proc_args",
|
||||
"jsjobscname": "yearly_sch",
|
||||
"jsprtype": null,
|
||||
"jsprnoofargs": [],
|
||||
"jsprarguments": [],
|
||||
"jsprproc": null,
|
||||
"jsprcode": null,
|
||||
"jsscstart": null,
|
||||
"jsscend": null,
|
||||
"jsscfreq": null,
|
||||
"jsscdate": null,
|
||||
"jsscweekdays": null,
|
||||
"jsscmonthdays": null,
|
||||
"jsscmonths": null,
|
||||
"jsschours": null,
|
||||
"jsscminutes": null
|
||||
},
|
||||
"mocking_required": false,
|
||||
"mock_data": {},
|
||||
"expected_data": {
|
||||
"status_code": 200,
|
||||
"error_msg": null,
|
||||
"test_result_data": {}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Create job: while server is down",
|
||||
"url": "/browser/dbms_job/obj/",
|
||||
"is_positive_test": false,
|
||||
"test_data": {
|
||||
"jsjobid": null,
|
||||
"jsjobname": "job_with_psql",
|
||||
"jsjobenabled": true,
|
||||
"jsjobdesc": "This is a self contained psql job.",
|
||||
"jsjobtype": "s",
|
||||
"jsprtype": "PLSQL_BLOCK",
|
||||
"jsprnoofargs": 0,
|
||||
"jsprarguments": [],
|
||||
"jsprproc": null,
|
||||
"jsprcode": "BEGIN PERFORM 1; END;",
|
||||
"jsscstart": "2024-02-27 00:00:00 +05:30",
|
||||
"jsscend": "2054-02-28 00:00:00 +05:30",
|
||||
"jsscfreq": "YEARLY",
|
||||
"jsscdate": null,
|
||||
"jsscweekdays": ["7", "1", "2", "3", "4", "5", "6"],
|
||||
"jsscmonthdays": ["2", "8", "31", "27"],
|
||||
"jsscmonths": ["1", "5", "12"],
|
||||
"jsschours": ["05", "18", "22"],
|
||||
"jsscminutes": ["45", "37", "58"],
|
||||
"jsjobprname": "",
|
||||
"jsjobscname": ""
|
||||
},
|
||||
"mocking_required": true,
|
||||
"mock_data": {
|
||||
"function_name": "pgadmin.utils.driver.psycopg3.connection.Connection.execute_scalar",
|
||||
"return_value": "[(False,'Mocked Internal Server Error')]"
|
||||
},
|
||||
"expected_data": {
|
||||
"status_code": 500,
|
||||
"error_msg": "Mocked Internal Server Error",
|
||||
"test_result_data": {}
|
||||
}
|
||||
}
|
||||
],
|
||||
"dbms_update_job": [
|
||||
{
|
||||
"name": "Set job argument value",
|
||||
"url": "/browser/dbms_job/obj/",
|
||||
"is_positive_test": true,
|
||||
"test_data": {
|
||||
"jsjobname": "job_with_update_args",
|
||||
"jsprarguments": {
|
||||
"changed": [{"argid":0,"argtype":"bigint","argmode":"IN","argname":"salary","argdefval":"10000","argval":"5000"}]
|
||||
}
|
||||
},
|
||||
"mocking_required": false,
|
||||
"mock_data": {},
|
||||
"expected_data": {
|
||||
"status_code": 200,
|
||||
"error_msg": null,
|
||||
"test_result_data": {}
|
||||
}
|
||||
}
|
||||
],
|
||||
"dbms_delete_job": [
|
||||
{
|
||||
"name": "Delete job: With existing DBMS job.",
|
||||
"url": "/browser/dbms_job/obj/",
|
||||
"is_positive_test": true,
|
||||
"inventory_data": {},
|
||||
"test_data": {},
|
||||
"mocking_required": false,
|
||||
"mock_data": {},
|
||||
"expected_data": {
|
||||
"status_code": 200,
|
||||
"error_msg": null,
|
||||
"test_result_data": {}
|
||||
},
|
||||
"is_list": false
|
||||
},
|
||||
{
|
||||
"name": "Delete multiple jobs: With existing DBMS jobs.",
|
||||
"url": "/browser/dbms_job/obj/",
|
||||
"is_positive_test": true,
|
||||
"inventory_data": {},
|
||||
"test_data": {},
|
||||
"mocking_required": false,
|
||||
"mock_data": {},
|
||||
"expected_data": {
|
||||
"status_code": 200,
|
||||
"error_msg": null,
|
||||
"test_result_data": {}
|
||||
},
|
||||
"is_list": true
|
||||
}
|
||||
],
|
||||
"dbms_get_job": [
|
||||
{
|
||||
"name": "Get job: With existing DBMS job.",
|
||||
"url": "/browser/dbms_job/obj/",
|
||||
"is_positive_test": true,
|
||||
"inventory_data": {},
|
||||
"test_data": {},
|
||||
"mocking_required": false,
|
||||
"mock_data": {},
|
||||
"expected_data": {
|
||||
"status_code": 200,
|
||||
"error_msg": null,
|
||||
"test_result_data": {}
|
||||
},
|
||||
"is_list": false
|
||||
},
|
||||
{
|
||||
"name": "Get jobs: With multiple existing DBMS jobs.",
|
||||
"url": "/browser/dbms_job/obj/",
|
||||
"is_positive_test": true,
|
||||
"inventory_data": {},
|
||||
"test_data": {},
|
||||
"mocking_required": false,
|
||||
"mock_data": {},
|
||||
"expected_data": {
|
||||
"status_code": 200,
|
||||
"error_msg": null,
|
||||
"test_result_data": {}
|
||||
},
|
||||
"is_list": true
|
||||
},
|
||||
{
|
||||
"name": "Get job: while server down.",
|
||||
"url": "/browser/dbms_job/obj/",
|
||||
"is_positive_test": false,
|
||||
"inventory_data": {},
|
||||
"test_data": {},
|
||||
"mocking_required": true,
|
||||
"mock_data": {
|
||||
"function_name": "pgadmin.utils.driver.psycopg3.connection.Connection.execute_dict",
|
||||
"return_value": "(False,'Mocked Internal Server Error')"
|
||||
},
|
||||
"expected_data": {
|
||||
"status_code": 500,
|
||||
"error_msg": "Mocked Internal Server Error",
|
||||
"test_result_data": {}
|
||||
},
|
||||
"is_list": false
|
||||
}
|
||||
],
|
||||
"dbms_msql_job": [
|
||||
{
|
||||
"name": "Get job msql: For existing PLSQL job.",
|
||||
"url": "/browser/dbms_job/msql/",
|
||||
"is_positive_test": true,
|
||||
"inventory_data": {},
|
||||
"test_data": {
|
||||
"jsjobid": null,
|
||||
"jsjobname": "job_self_with_psql",
|
||||
"jsjobenabled": true,
|
||||
"jsjobdesc": "This is a self contained psql job.",
|
||||
"jsjobtype": "s",
|
||||
"jsprtype": "PLSQL_BLOCK",
|
||||
"jsprnoofargs": 0,
|
||||
"jsprarguments": [],
|
||||
"jsprproc": null,
|
||||
"jsprcode": "BEGIN PERFORM 1; END;",
|
||||
"jsscstart": "2024-02-27 00:00:00 +05:30",
|
||||
"jsscend": "2054-02-28 00:00:00 +05:30",
|
||||
"jsscfreq": "YEARLY",
|
||||
"jsscdate": null,
|
||||
"jsscweekdays": ["7", "1", "2", "3", "4", "5", "6"],
|
||||
"jsscmonthdays": ["2", "8", "31", "27"],
|
||||
"jsscmonths": ["1", "5", "12"],
|
||||
"jsschours": ["05", "18", "22"],
|
||||
"jsscminutes": ["45", "37", "58"],
|
||||
"jsjobprname": "",
|
||||
"jsjobscname": ""
|
||||
},
|
||||
"mocking_required": false,
|
||||
"mock_data": {},
|
||||
"expected_data": {
|
||||
"status_code": 200,
|
||||
"error_msg": null,
|
||||
"test_result_data": {}
|
||||
},
|
||||
"is_list": false
|
||||
},
|
||||
{
|
||||
"name": "Get job msql: For existing STORED_PROCEDURE job.",
|
||||
"url": "/browser/dbms_job/msql/",
|
||||
"is_positive_test": true,
|
||||
"inventory_data": {},
|
||||
"test_data": {
|
||||
"jsjobid": null,
|
||||
"jsjobname": "job_self_with_proc_noargs",
|
||||
"jsjobenabled": true,
|
||||
"jsjobdesc": "This is a self contained stored procedure job with no args.",
|
||||
"jsjobtype": "s",
|
||||
"jsprtype": "STORED_PROCEDURE",
|
||||
"jsprnoofargs": 0,
|
||||
"jsprarguments": [],
|
||||
"jsprproc": "public.test_proc_without_args",
|
||||
"jsprcode": null,
|
||||
"jsscstart": "2024-02-27 00:00:00 +05:30",
|
||||
"jsscend": "2054-02-28 00:00:00 +05:30",
|
||||
"jsscfreq": "YEARLY",
|
||||
"jsscdate": "20250113",
|
||||
"jsscweekdays": [],
|
||||
"jsscmonthdays": [],
|
||||
"jsscmonths": [],
|
||||
"jsschours": [],
|
||||
"jsscminutes": [],
|
||||
"jsjobprname": "",
|
||||
"jsjobscname": ""
|
||||
},
|
||||
"mocking_required": false,
|
||||
"mock_data": {},
|
||||
"expected_data": {
|
||||
"status_code": 200,
|
||||
"error_msg": null,
|
||||
"test_result_data": {}
|
||||
},
|
||||
"is_list": false
|
||||
}
|
||||
],
|
||||
"dbms_enable_job": [
|
||||
{
|
||||
"name": "Enable existing job",
|
||||
"url": "/browser/dbms_job/enable_disable/",
|
||||
"is_positive_test": true,
|
||||
"test_data": {
|
||||
"is_enable_job": true
|
||||
},
|
||||
"mocking_required": false,
|
||||
"mock_data": {},
|
||||
"expected_data": {
|
||||
"status_code": 200,
|
||||
"error_msg": null,
|
||||
"test_result_data": {}
|
||||
}
|
||||
}
|
||||
],
|
||||
"dbms_disable_job": [
|
||||
{
|
||||
"name": "Disable existing job",
|
||||
"url": "/browser/dbms_job/enable_disable/",
|
||||
"is_positive_test": true,
|
||||
"test_data": {
|
||||
"is_enable_job": false
|
||||
},
|
||||
"mocking_required": false,
|
||||
"mock_data": {},
|
||||
"expected_data": {
|
||||
"status_code": 200,
|
||||
"error_msg": null,
|
||||
"test_result_data": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2024, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
import os
|
||||
import json
|
||||
from unittest.mock import patch
|
||||
from pgadmin.utils.route import BaseTestGenerator
|
||||
from regression.python_test_utils import test_utils as utils
|
||||
from ...tests import utils as job_scheduler_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.tests import \
|
||||
utils as database_utils
|
||||
|
||||
|
||||
# Load test data from json file.
|
||||
CURRENT_PATH = os.path.dirname(os.path.realpath(__file__))
|
||||
with open(CURRENT_PATH + "/dbms_jobs_test_data.json") as data_file:
|
||||
test_cases = json.load(data_file)
|
||||
|
||||
|
||||
class DBMSAddJobTestCase(BaseTestGenerator):
|
||||
"""This class will test the add job in the DBMS Job API"""
|
||||
scenarios = utils.generate_scenarios("dbms_create_job",
|
||||
test_cases)
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
# Load test data
|
||||
self.data = self.test_data
|
||||
|
||||
if not job_scheduler_utils.is_supported_version(self):
|
||||
self.skipTest(job_scheduler_utils.SKIP_MSG)
|
||||
|
||||
# Create db
|
||||
self.db_name, self.db_id = job_scheduler_utils.create_test_database(
|
||||
self)
|
||||
db_con = database_utils.connect_database(self,
|
||||
utils.SERVER_GROUP,
|
||||
self.server_id,
|
||||
self.db_id)
|
||||
if db_con["info"] != "Database connected.":
|
||||
raise Exception("Could not connect to database.")
|
||||
|
||||
# Create extension required for job scheduler
|
||||
job_scheduler_utils.create_job_scheduler_extensions(self)
|
||||
|
||||
if not job_scheduler_utils.is_dbms_job_scheduler_present(self):
|
||||
self.skipTest(job_scheduler_utils.SKIP_MSG_EXTENSION)
|
||||
|
||||
# Create job schedule
|
||||
job_scheduler_utils.create_dbms_schedule(self, 'yearly_sch')
|
||||
job_scheduler_utils.create_dbms_program(self, 'prg_with_psql')
|
||||
job_scheduler_utils.create_dbms_program(
|
||||
self,'prg_with_proc_noargs', with_proc=True,
|
||||
proc_name='public.test_proc_without_args()')
|
||||
job_scheduler_utils.create_dbms_program(
|
||||
self,'prg_with_proc_args', with_proc=True,
|
||||
proc_name='public.test_proc_with_args(IN salary bigint DEFAULT '
|
||||
'10000, IN name character varying)')
|
||||
|
||||
def runTest(self):
|
||||
""" This function will add DBMS Job under test database. """
|
||||
if self.is_positive_test:
|
||||
response = job_scheduler_utils.api_create(self)
|
||||
|
||||
# Assert response
|
||||
utils.assert_status_code(self, response)
|
||||
|
||||
# Verify in backend
|
||||
response_data = json.loads(response.data)
|
||||
self.jobs_id = response_data['node']['_id']
|
||||
jobs_name = response_data['node']['label']
|
||||
is_present = job_scheduler_utils.verify_dbms_job(
|
||||
self, jobs_name)
|
||||
self.assertTrue(
|
||||
is_present,"DBMS job was not created successfully.")
|
||||
else:
|
||||
if self.mocking_required:
|
||||
with patch(self.mock_data["function_name"],
|
||||
side_effect=eval(self.mock_data["return_value"])):
|
||||
response = job_scheduler_utils.api_create(self)
|
||||
|
||||
# Assert response
|
||||
utils.assert_status_code(self, response)
|
||||
utils.assert_error_message(self, response)
|
||||
|
||||
def tearDown(self):
|
||||
"""This function will do the cleanup task."""
|
||||
job_scheduler_utils.clean_up(self)
|
||||
@@ -0,0 +1,98 @@
|
||||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2024, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
import uuid
|
||||
import os
|
||||
import json
|
||||
from pgadmin.utils.route import BaseTestGenerator
|
||||
from regression.python_test_utils import test_utils as utils
|
||||
from ...tests import utils as job_scheduler_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.tests import \
|
||||
utils as database_utils
|
||||
|
||||
|
||||
# Load test data from json file.
|
||||
CURRENT_PATH = os.path.dirname(os.path.realpath(__file__))
|
||||
with open(CURRENT_PATH + "/dbms_jobs_test_data.json") as data_file:
|
||||
test_cases = json.load(data_file)
|
||||
|
||||
|
||||
class DBMSDeleteJobTestCase(BaseTestGenerator):
|
||||
"""This class will test the delete job in the DBMS job API"""
|
||||
scenarios = utils.generate_scenarios("dbms_delete_job",
|
||||
test_cases)
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
# Load test data
|
||||
self.data = self.test_data
|
||||
|
||||
if not job_scheduler_utils.is_supported_version(self):
|
||||
self.skipTest(job_scheduler_utils.SKIP_MSG)
|
||||
|
||||
# Create db
|
||||
self.db_name, self.db_id = job_scheduler_utils.create_test_database(
|
||||
self)
|
||||
db_con = database_utils.connect_database(self,
|
||||
utils.SERVER_GROUP,
|
||||
self.server_id,
|
||||
self.db_id)
|
||||
if db_con["info"] != "Database connected.":
|
||||
raise Exception("Could not connect to database.")
|
||||
|
||||
# Create extension required for job scheduler
|
||||
job_scheduler_utils.create_job_scheduler_extensions(self)
|
||||
|
||||
if not job_scheduler_utils.is_dbms_job_scheduler_present(self):
|
||||
self.skipTest(job_scheduler_utils.SKIP_MSG_EXTENSION)
|
||||
|
||||
self.job_name = "test_job_delete%s" % str(uuid.uuid4())[1:8]
|
||||
self.job_id = job_scheduler_utils.create_dbms_job(
|
||||
self, self.job_name)
|
||||
|
||||
# multiple jobs
|
||||
if self.is_list:
|
||||
self.job_name2 = "test_job_delete%s" % str(uuid.uuid4())[1:8]
|
||||
self.job_id_2 = job_scheduler_utils.create_dbms_job(
|
||||
self, self.job_name2)
|
||||
|
||||
def runTest(self):
|
||||
"""
|
||||
This function will test delete DBMS job under test database.
|
||||
"""
|
||||
if self.is_list:
|
||||
self.data['ids'] = [self.job_id, self.job_id_2]
|
||||
response = job_scheduler_utils.api_delete(self, '')
|
||||
|
||||
# Assert response
|
||||
utils.assert_status_code(self, response)
|
||||
|
||||
is_present = job_scheduler_utils.verify_dbms_job(
|
||||
self, self.job_name)
|
||||
self.assertFalse(
|
||||
is_present, "DBMS job was not deleted successfully")
|
||||
|
||||
is_present = job_scheduler_utils.verify_dbms_job(
|
||||
self, self.job_name2)
|
||||
self.assertFalse(
|
||||
is_present, "DBMS job was not deleted successfully")
|
||||
else:
|
||||
response = job_scheduler_utils.api_delete(self)
|
||||
|
||||
# Assert response
|
||||
utils.assert_status_code(self, response)
|
||||
|
||||
is_present = job_scheduler_utils.verify_dbms_job(
|
||||
self, self.job_name)
|
||||
self.assertFalse(
|
||||
is_present, "DBMS job was not deleted successfully")
|
||||
|
||||
def tearDown(self):
|
||||
"""This function will do the cleanup task."""
|
||||
job_scheduler_utils.clean_up(self)
|
||||
@@ -0,0 +1,71 @@
|
||||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2024, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
import uuid
|
||||
import os
|
||||
import json
|
||||
from pgadmin.utils.route import BaseTestGenerator
|
||||
from regression.python_test_utils import test_utils as utils
|
||||
from ...tests import utils as job_scheduler_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.tests import \
|
||||
utils as database_utils
|
||||
|
||||
|
||||
# Load test data from json file.
|
||||
CURRENT_PATH = os.path.dirname(os.path.realpath(__file__))
|
||||
with open(CURRENT_PATH + "/dbms_jobs_test_data.json") as data_file:
|
||||
test_cases = json.load(data_file)
|
||||
|
||||
|
||||
class DBMSDisableJobTestCase(BaseTestGenerator):
|
||||
"""This class will test the add job in the DBMS job API"""
|
||||
scenarios = utils.generate_scenarios("dbms_disable_job",
|
||||
test_cases)
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
# Load test data
|
||||
self.data = self.test_data
|
||||
|
||||
if not job_scheduler_utils.is_supported_version(self):
|
||||
self.skipTest(job_scheduler_utils.SKIP_MSG)
|
||||
|
||||
# Create db
|
||||
self.db_name, self.db_id = job_scheduler_utils.create_test_database(
|
||||
self)
|
||||
db_con = database_utils.connect_database(self,
|
||||
utils.SERVER_GROUP,
|
||||
self.server_id,
|
||||
self.db_id)
|
||||
if db_con["info"] != "Database connected.":
|
||||
raise Exception("Could not connect to database.")
|
||||
|
||||
# Create extension required for job scheduler
|
||||
job_scheduler_utils.create_job_scheduler_extensions(self)
|
||||
|
||||
if not job_scheduler_utils.is_dbms_job_scheduler_present(self):
|
||||
self.skipTest(job_scheduler_utils.SKIP_MSG_EXTENSION)
|
||||
|
||||
self.job_name = "test_job_disable%s" % str(uuid.uuid4())[1:8]
|
||||
self.data['job_name'] = self.job_name
|
||||
self.job_id = job_scheduler_utils.create_dbms_job(
|
||||
self, self.job_name)
|
||||
|
||||
def runTest(self):
|
||||
""" This function will test DBMS job under test database."""
|
||||
response = job_scheduler_utils.api_put(self, self.job_id)
|
||||
|
||||
# Assert response
|
||||
utils.assert_status_code(self, response)
|
||||
|
||||
def tearDown(self):
|
||||
"""This function will do the cleanup task."""
|
||||
job_scheduler_utils.delete_dbms_job(self, self.job_name)
|
||||
|
||||
job_scheduler_utils.clean_up(self)
|
||||
@@ -0,0 +1,71 @@
|
||||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2024, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
import uuid
|
||||
import os
|
||||
import json
|
||||
from pgadmin.utils.route import BaseTestGenerator
|
||||
from regression.python_test_utils import test_utils as utils
|
||||
from ...tests import utils as job_scheduler_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.tests import \
|
||||
utils as database_utils
|
||||
|
||||
|
||||
# Load test data from json file.
|
||||
CURRENT_PATH = os.path.dirname(os.path.realpath(__file__))
|
||||
with open(CURRENT_PATH + "/dbms_jobs_test_data.json") as data_file:
|
||||
test_cases = json.load(data_file)
|
||||
|
||||
|
||||
class DBMSEnableJobTestCase(BaseTestGenerator):
|
||||
"""This class will test the enable job in the DBMS job API"""
|
||||
scenarios = utils.generate_scenarios("dbms_enable_job",
|
||||
test_cases)
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
# Load test data
|
||||
self.data = self.test_data
|
||||
|
||||
if not job_scheduler_utils.is_supported_version(self):
|
||||
self.skipTest(job_scheduler_utils.SKIP_MSG)
|
||||
|
||||
# Create db
|
||||
self.db_name, self.db_id = job_scheduler_utils.create_test_database(
|
||||
self)
|
||||
db_con = database_utils.connect_database(self,
|
||||
utils.SERVER_GROUP,
|
||||
self.server_id,
|
||||
self.db_id)
|
||||
if db_con["info"] != "Database connected.":
|
||||
raise Exception("Could not connect to database.")
|
||||
|
||||
# Create extension required for job scheduler
|
||||
job_scheduler_utils.create_job_scheduler_extensions(self)
|
||||
|
||||
if not job_scheduler_utils.is_dbms_job_scheduler_present(self):
|
||||
self.skipTest(job_scheduler_utils.SKIP_MSG_EXTENSION)
|
||||
|
||||
self.job_name = "test_job_enable%s" % str(uuid.uuid4())[1:8]
|
||||
self.data['job_name'] = self.job_name
|
||||
self.job_id = job_scheduler_utils.create_dbms_job(
|
||||
self, self.job_name, False)
|
||||
|
||||
def runTest(self):
|
||||
""" This function will test DBMS job under test database."""
|
||||
response = job_scheduler_utils.api_put(self, self.job_id)
|
||||
|
||||
# Assert response
|
||||
utils.assert_status_code(self, response)
|
||||
|
||||
def tearDown(self):
|
||||
"""This function will do the cleanup task."""
|
||||
job_scheduler_utils.delete_dbms_job(self, self.job_name)
|
||||
|
||||
job_scheduler_utils.clean_up(self)
|
||||
@@ -0,0 +1,92 @@
|
||||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2024, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
import uuid
|
||||
import os
|
||||
import json
|
||||
from unittest.mock import patch
|
||||
from pgadmin.utils.route import BaseTestGenerator
|
||||
from regression.python_test_utils import test_utils as utils
|
||||
from ...tests import utils as job_scheduler_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.tests import \
|
||||
utils as database_utils
|
||||
|
||||
|
||||
# Load test data from json file.
|
||||
CURRENT_PATH = os.path.dirname(os.path.realpath(__file__))
|
||||
with open(CURRENT_PATH + "/dbms_jobs_test_data.json") as data_file:
|
||||
test_cases = json.load(data_file)
|
||||
|
||||
|
||||
class DBMSGetJobTestCase(BaseTestGenerator):
|
||||
"""This class will test the add job in the DBMS job API"""
|
||||
scenarios = utils.generate_scenarios("dbms_get_job",
|
||||
test_cases)
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
# Load test data
|
||||
self.data = self.test_data
|
||||
|
||||
if not job_scheduler_utils.is_supported_version(self):
|
||||
self.skipTest(job_scheduler_utils.SKIP_MSG)
|
||||
|
||||
# Create db
|
||||
self.db_name, self.db_id = job_scheduler_utils.create_test_database(
|
||||
self)
|
||||
db_con = database_utils.connect_database(self,
|
||||
utils.SERVER_GROUP,
|
||||
self.server_id,
|
||||
self.db_id)
|
||||
if db_con["info"] != "Database connected.":
|
||||
raise Exception("Could not connect to database.")
|
||||
|
||||
# Create extension required for job scheduler
|
||||
job_scheduler_utils.create_job_scheduler_extensions(self)
|
||||
|
||||
if not job_scheduler_utils.is_dbms_job_scheduler_present(self):
|
||||
self.skipTest(job_scheduler_utils.SKIP_MSG_EXTENSION)
|
||||
|
||||
self.job_name = "test_job_get%s" % str(uuid.uuid4())[1:8]
|
||||
self.job_id = job_scheduler_utils.create_dbms_job(
|
||||
self, self.job_name)
|
||||
|
||||
# multiple jobs
|
||||
if self.is_list:
|
||||
self.job_name2 = "test_job_get%s" % str(uuid.uuid4())[1:8]
|
||||
self.job_id_2 = job_scheduler_utils.create_dbms_job(
|
||||
self, self.job_name2,)
|
||||
|
||||
def runTest(self):
|
||||
""" This function will test DBMS job under test database."""
|
||||
if self.is_positive_test:
|
||||
if self.is_list:
|
||||
response = job_scheduler_utils.api_get(self, '')
|
||||
else:
|
||||
response = job_scheduler_utils.api_get(self)
|
||||
|
||||
# Assert response
|
||||
utils.assert_status_code(self, response)
|
||||
else:
|
||||
if self.mocking_required:
|
||||
with patch(self.mock_data["function_name"],
|
||||
side_effect=[eval(self.mock_data["return_value"])]):
|
||||
response = job_scheduler_utils.api_get(self)
|
||||
|
||||
# Assert response
|
||||
utils.assert_status_code(self, response)
|
||||
utils.assert_error_message(self, response)
|
||||
|
||||
def tearDown(self):
|
||||
"""This function will do the cleanup task."""
|
||||
job_scheduler_utils.delete_dbms_job(self, self.job_name)
|
||||
if self.is_list:
|
||||
job_scheduler_utils.delete_dbms_job(self, self.job_name2)
|
||||
|
||||
job_scheduler_utils.clean_up(self)
|
||||
@@ -0,0 +1,65 @@
|
||||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2024, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
import os
|
||||
import json
|
||||
from pgadmin.utils.route import BaseTestGenerator
|
||||
from regression.python_test_utils import test_utils as utils
|
||||
from ...tests import utils as job_scheduler_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.tests import \
|
||||
utils as database_utils
|
||||
|
||||
|
||||
# Load test data from json file.
|
||||
CURRENT_PATH = os.path.dirname(os.path.realpath(__file__))
|
||||
with open(CURRENT_PATH + "/dbms_jobs_test_data.json") as data_file:
|
||||
test_cases = json.load(data_file)
|
||||
|
||||
|
||||
class DBMSGetMSQLJobTestCase(BaseTestGenerator):
|
||||
"""This class will test the add job in the DBMS job API"""
|
||||
scenarios = utils.generate_scenarios("dbms_msql_job",
|
||||
test_cases)
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
# Load test data
|
||||
self.data = self.test_data
|
||||
|
||||
if not job_scheduler_utils.is_supported_version(self):
|
||||
self.skipTest(job_scheduler_utils.SKIP_MSG)
|
||||
|
||||
# Create db
|
||||
self.db_name, self.db_id = job_scheduler_utils.create_test_database(
|
||||
self)
|
||||
db_con = database_utils.connect_database(self,
|
||||
utils.SERVER_GROUP,
|
||||
self.server_id,
|
||||
self.db_id)
|
||||
if db_con["info"] != "Database connected.":
|
||||
raise Exception("Could not connect to database.")
|
||||
|
||||
# Create extension required for job scheduler
|
||||
job_scheduler_utils.create_job_scheduler_extensions(self)
|
||||
|
||||
if not job_scheduler_utils.is_dbms_job_scheduler_present(self):
|
||||
self.skipTest(job_scheduler_utils.SKIP_MSG_EXTENSION)
|
||||
|
||||
def runTest(self):
|
||||
""" This function will add DBMS job under test database. """
|
||||
url_encode_data = self.data
|
||||
|
||||
response = job_scheduler_utils.api_get_msql(self, url_encode_data)
|
||||
|
||||
# Assert response
|
||||
utils.assert_status_code(self, response)
|
||||
|
||||
def tearDown(self):
|
||||
"""This function will do the cleanup task."""
|
||||
job_scheduler_utils.clean_up(self)
|
||||
@@ -0,0 +1,73 @@
|
||||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2024, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
import os
|
||||
import json
|
||||
from pgadmin.utils.route import BaseTestGenerator
|
||||
from regression.python_test_utils import test_utils as utils
|
||||
from ...tests import utils as job_scheduler_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.tests import \
|
||||
utils as database_utils
|
||||
|
||||
|
||||
# Load test data from json file.
|
||||
CURRENT_PATH = os.path.dirname(os.path.realpath(__file__))
|
||||
with open(CURRENT_PATH + "/dbms_jobs_test_data.json") as data_file:
|
||||
test_cases = json.load(data_file)
|
||||
|
||||
|
||||
class DBMSUpdateJobTestCase(BaseTestGenerator):
|
||||
"""This class will test the add job in the DBMS Job API"""
|
||||
scenarios = utils.generate_scenarios("dbms_update_job",
|
||||
test_cases)
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
# Load test data
|
||||
self.data = self.test_data
|
||||
|
||||
if not job_scheduler_utils.is_supported_version(self):
|
||||
self.skipTest(job_scheduler_utils.SKIP_MSG)
|
||||
|
||||
# Create db
|
||||
self.db_name, self.db_id = job_scheduler_utils.create_test_database(
|
||||
self)
|
||||
db_con = database_utils.connect_database(self,
|
||||
utils.SERVER_GROUP,
|
||||
self.server_id,
|
||||
self.db_id)
|
||||
if db_con["info"] != "Database connected.":
|
||||
raise Exception("Could not connect to database.")
|
||||
|
||||
# Create extension required for job scheduler
|
||||
job_scheduler_utils.create_job_scheduler_extensions(self)
|
||||
|
||||
if not job_scheduler_utils.is_dbms_job_scheduler_present(self):
|
||||
self.skipTest(job_scheduler_utils.SKIP_MSG_EXTENSION)
|
||||
|
||||
# Create job schedule
|
||||
job_scheduler_utils.create_dbms_schedule(self, 'yearly_sch')
|
||||
job_scheduler_utils.create_dbms_program(
|
||||
self,'prg_with_proc_args', with_proc=True,
|
||||
proc_name='public.test_proc_with_args()',
|
||||
define_args=True)
|
||||
self.job_id = job_scheduler_utils.create_dbms_job(
|
||||
self, self.data['jsjobname'], True,
|
||||
'prg_with_proc_args','yearly_sch')
|
||||
|
||||
def runTest(self):
|
||||
""" This function will update DBMS Job under test database. """
|
||||
response = job_scheduler_utils.api_put(self, self.job_id)
|
||||
|
||||
# Assert response
|
||||
utils.assert_status_code(self, response)
|
||||
|
||||
def tearDown(self):
|
||||
"""This function will do the cleanup task."""
|
||||
job_scheduler_utils.clean_up(self)
|
||||
@@ -0,0 +1,574 @@
|
||||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2024, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
""" Implements DBMS Program objects Node."""
|
||||
|
||||
import json
|
||||
from functools import wraps
|
||||
|
||||
from flask import render_template, request, jsonify
|
||||
from flask_babel import gettext
|
||||
from pgadmin.browser.collection import CollectionNodeModule
|
||||
from pgadmin.browser.server_groups.servers import databases
|
||||
from pgadmin.browser.utils import PGChildNodeView
|
||||
from pgadmin.utils.ajax import make_json_response, gone, \
|
||||
make_response as ajax_response, internal_server_error
|
||||
from pgadmin.utils.driver import get_driver
|
||||
from config import PG_DEFAULT_DRIVER
|
||||
from pgadmin.utils.constants import DBMS_JOB_SCHEDULER_ID
|
||||
from pgadmin.browser.server_groups.servers.databases.schemas.functions.utils \
|
||||
import format_arguments_from_db
|
||||
from pgadmin.browser.server_groups.servers.databases.dbms_job_scheduler.utils \
|
||||
import get_formatted_program_args
|
||||
|
||||
|
||||
class DBMSProgramModule(CollectionNodeModule):
|
||||
"""
|
||||
class DBMSProgramModule(CollectionNodeModule)
|
||||
|
||||
A module class for DBMS Program objects node derived
|
||||
from CollectionNodeModule.
|
||||
|
||||
Methods:
|
||||
-------
|
||||
|
||||
* get_nodes(gid, sid, did)
|
||||
- Method is used to generate the browser collection node.
|
||||
|
||||
* script_load()
|
||||
- Load the module script for DBMS Program objects, when any of
|
||||
the server node is initialized.
|
||||
"""
|
||||
_NODE_TYPE = 'dbms_program'
|
||||
_COLLECTION_LABEL = gettext("DBMS Programs")
|
||||
|
||||
@property
|
||||
def collection_icon(self):
|
||||
"""
|
||||
icon to be displayed for the browser collection node
|
||||
"""
|
||||
return 'icon-coll-pga_jobstep'
|
||||
|
||||
@property
|
||||
def node_icon(self):
|
||||
"""
|
||||
icon to be displayed for the browser nodes
|
||||
"""
|
||||
return 'icon-pga_jobstep'
|
||||
|
||||
def get_nodes(self, gid, sid, did, jsid):
|
||||
"""
|
||||
Generate the collection node
|
||||
"""
|
||||
if self.show_node:
|
||||
yield self.generate_browser_collection_node(did)
|
||||
|
||||
@property
|
||||
def node_inode(self):
|
||||
"""
|
||||
Override this property to make the node a leaf node.
|
||||
|
||||
Returns: False as this is the leaf node
|
||||
"""
|
||||
return False
|
||||
|
||||
@property
|
||||
def script_load(self):
|
||||
"""
|
||||
Load the module script for server, when any of the database node is
|
||||
initialized.
|
||||
"""
|
||||
return databases.DatabaseModule.node_type
|
||||
|
||||
@property
|
||||
def module_use_template_javascript(self):
|
||||
"""
|
||||
Returns whether Jinja2 template is used for generating the javascript
|
||||
module.
|
||||
"""
|
||||
return False
|
||||
|
||||
|
||||
blueprint = DBMSProgramModule(__name__)
|
||||
|
||||
|
||||
class DBMSProgramView(PGChildNodeView):
|
||||
"""
|
||||
class DBMSProgramView(PGChildNodeView)
|
||||
|
||||
A view class for DBMSProgram node derived from PGChildNodeView.
|
||||
This class is responsible for all the stuff related to view like
|
||||
updating program node, showing properties, showing sql in sql pane.
|
||||
|
||||
Methods:
|
||||
-------
|
||||
* __init__(**kwargs)
|
||||
- Method is used to initialize the DBMSProgramView, and it's base view.
|
||||
|
||||
* check_precondition()
|
||||
- This function will behave as a decorator which will checks
|
||||
database connection before running view, it will also attaches
|
||||
manager,conn & template_path properties to self
|
||||
|
||||
* list()
|
||||
- This function is used to list all the program nodes within that
|
||||
collection.
|
||||
|
||||
* nodes()
|
||||
- This function will use to create all the child node within that
|
||||
collection. Here it will create all the program node.
|
||||
|
||||
* properties(gid, sid, did, jsid, jsprid)
|
||||
- This function will show the properties of the selected program node
|
||||
|
||||
* create(gid, sid, did, jsid, jsprid)
|
||||
- This function will create the new program object
|
||||
|
||||
* msql(gid, sid, did, jsid, jsprid)
|
||||
- This function is used to return modified SQL for the
|
||||
selected program node
|
||||
|
||||
* sql(gid, sid, did, jsid, jsprid)
|
||||
- Dummy response for sql panel
|
||||
|
||||
* delete(gid, sid, did, jsid, jsprid)
|
||||
- Drops job program
|
||||
"""
|
||||
|
||||
node_type = blueprint.node_type
|
||||
BASE_TEMPLATE_PATH = 'dbms_programs/ppas/#{0}#'
|
||||
|
||||
parent_ids = [
|
||||
{'type': 'int', 'id': 'gid'},
|
||||
{'type': 'int', 'id': 'sid'},
|
||||
{'type': 'int', 'id': 'did'},
|
||||
{'type': 'int', 'id': 'jsid'}
|
||||
]
|
||||
ids = [
|
||||
{'type': 'int', 'id': 'jsprid'}
|
||||
]
|
||||
|
||||
operations = dict({
|
||||
'obj': [
|
||||
{'get': 'properties', 'delete': 'delete'},
|
||||
{'get': 'list', 'post': 'create', 'delete': 'delete'}
|
||||
],
|
||||
'nodes': [{'get': 'nodes'}, {'get': 'nodes'}],
|
||||
'msql': [{'get': 'msql'}, {'get': 'msql'}],
|
||||
'sql': [{'get': 'sql'}],
|
||||
'get_procedures': [{}, {'get': 'get_procedures'}],
|
||||
'enable_disable': [{'put': 'enable_disable'}],
|
||||
})
|
||||
|
||||
def _init_(self, **kwargs):
|
||||
self.conn = None
|
||||
self.template_path = None
|
||||
self.manager = None
|
||||
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def check_precondition(f):
|
||||
"""
|
||||
This function will behave as a decorator which will check the
|
||||
database connection before running view. It will also attach
|
||||
manager, conn & template_path properties to self
|
||||
"""
|
||||
|
||||
@wraps(f)
|
||||
def wrap(*args, **kwargs):
|
||||
# Here args[0] will hold self & kwargs will hold gid,sid,did
|
||||
self = args[0]
|
||||
self.driver = get_driver(PG_DEFAULT_DRIVER)
|
||||
self.manager = self.driver.connection_manager(kwargs['sid'])
|
||||
self.conn = self.manager.connection(did=kwargs['did'])
|
||||
# Set the template path for the SQL scripts
|
||||
self.template_path = self.BASE_TEMPLATE_PATH.format(
|
||||
self.manager.version)
|
||||
|
||||
return f(*args, **kwargs)
|
||||
|
||||
return wrap
|
||||
|
||||
@check_precondition
|
||||
def list(self, gid, sid, did, jsid):
|
||||
"""
|
||||
This function is used to list all the program nodes within
|
||||
that collection.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
jsid: Job Scheduler ID
|
||||
"""
|
||||
sql = render_template(
|
||||
"/".join([self.template_path, self._PROPERTIES_SQL]))
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
return ajax_response(
|
||||
response=res['rows'],
|
||||
status=200
|
||||
)
|
||||
|
||||
@check_precondition
|
||||
def nodes(self, gid, sid, did, jsid, jsprid=None):
|
||||
"""
|
||||
This function is used to create all the child nodes within
|
||||
the collection. Here it will create all the program nodes.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
jsid: Job Scheduler ID
|
||||
"""
|
||||
res = []
|
||||
try:
|
||||
sql = render_template(
|
||||
"/".join([self.template_path, self._NODES_SQL]))
|
||||
|
||||
status, result = self.conn.execute_2darray(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=result)
|
||||
|
||||
if jsprid is not None:
|
||||
if len(result['rows']) == 0:
|
||||
return gone(
|
||||
errormsg=gettext("Could not find the specified "
|
||||
"program."))
|
||||
|
||||
row = result['rows'][0]
|
||||
return make_json_response(
|
||||
data=self.blueprint.generate_browser_node(
|
||||
row['jsprid'],
|
||||
DBMS_JOB_SCHEDULER_ID,
|
||||
row['jsprname'],
|
||||
is_enabled=row['jsprenabled'],
|
||||
icon="icon-pga_jobstep" if row['jsprenabled'] else
|
||||
"icon-pga_jobstep-disabled",
|
||||
description=row['jsprdesc']
|
||||
)
|
||||
)
|
||||
|
||||
for row in result['rows']:
|
||||
res.append(
|
||||
self.blueprint.generate_browser_node(
|
||||
row['jsprid'],
|
||||
DBMS_JOB_SCHEDULER_ID,
|
||||
row['jsprname'],
|
||||
is_enabled=row['jsprenabled'],
|
||||
icon="icon-pga_jobstep" if row['jsprenabled'] else
|
||||
"icon-pga_jobstep-disabled",
|
||||
description=row['jsprdesc']
|
||||
)
|
||||
)
|
||||
|
||||
return make_json_response(
|
||||
data=res,
|
||||
status=200
|
||||
)
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def properties(self, gid, sid, did, jsid, jsprid):
|
||||
"""
|
||||
This function will show the properties of the selected program node.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
jsid: Job Scheduler ID
|
||||
jsprid: Job program ID
|
||||
"""
|
||||
try:
|
||||
sql = render_template(
|
||||
"/".join([self.template_path, self._PROPERTIES_SQL]),
|
||||
jsprid=jsprid
|
||||
)
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
if len(res['rows']) == 0:
|
||||
return gone(
|
||||
errormsg=gettext("Could not find the specified program.")
|
||||
)
|
||||
|
||||
data = res['rows'][0]
|
||||
# Get the formatted program args
|
||||
get_formatted_program_args(self.template_path, self.conn, data)
|
||||
|
||||
return ajax_response(
|
||||
response=data,
|
||||
status=200
|
||||
)
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def create(self, gid, sid, did, jsid):
|
||||
"""
|
||||
This function will update the data for the selected program node.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
jsid: Job Scheduler ID
|
||||
"""
|
||||
data = json.loads(request.data)
|
||||
try:
|
||||
sql = render_template(
|
||||
"/".join([self.template_path, self._CREATE_SQL]),
|
||||
program_name=data['jsprname'],
|
||||
program_type=data['jsprtype'],
|
||||
program_action=data['jsprproc']
|
||||
if data['jsprtype'] == 'STORED_PROCEDURE' else
|
||||
data['jsprcode'],
|
||||
number_of_arguments=data['jsprnoofargs'],
|
||||
enabled=data['jsprenabled'],
|
||||
comments=data['jsprdesc'],
|
||||
arguments=data['jsprarguments'],
|
||||
conn=self.conn
|
||||
)
|
||||
|
||||
status, res = self.conn.execute_void('BEGIN')
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
status, res = self.conn.execute_scalar(sql)
|
||||
if not status:
|
||||
if self.conn.connected():
|
||||
self.conn.execute_void('END')
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
self.conn.execute_void('END')
|
||||
|
||||
# Get the newly created program id
|
||||
sql = render_template(
|
||||
"/".join([self.template_path, 'get_program_id.sql']),
|
||||
jsprname=data['jsprname'], conn=self.conn
|
||||
)
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
if len(res['rows']) == 0:
|
||||
return gone(
|
||||
errormsg=gettext("Job program creation failed.")
|
||||
)
|
||||
row = res['rows'][0]
|
||||
|
||||
return jsonify(
|
||||
node=self.blueprint.generate_browser_node(
|
||||
row['jsprid'],
|
||||
DBMS_JOB_SCHEDULER_ID,
|
||||
row['jsprname'],
|
||||
is_enabled=row['jsprenabled'],
|
||||
icon="icon-pga_jobstep" if row['jsprenabled'] else
|
||||
"icon-pga_jobstep-disabled"
|
||||
)
|
||||
)
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def delete(self, gid, sid, did, jsid, jsprid=None):
|
||||
"""Delete the Job program."""
|
||||
|
||||
if jsprid is None:
|
||||
data = request.form if request.form else json.loads(
|
||||
request.data
|
||||
)
|
||||
else:
|
||||
data = {'ids': [jsprid]}
|
||||
|
||||
try:
|
||||
for jsprid in data['ids']:
|
||||
sql = render_template(
|
||||
"/".join([self.template_path, self._PROPERTIES_SQL]),
|
||||
jsprid=jsprid
|
||||
)
|
||||
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
jsprname = res['rows'][0]['jsprname']
|
||||
|
||||
status, res = self.conn.execute_void(
|
||||
render_template(
|
||||
"/".join([self.template_path, self._DELETE_SQL]),
|
||||
program_name=jsprname, conn=self.conn
|
||||
)
|
||||
)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
return make_json_response(success=1)
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def msql(self, gid, sid, did, jsid, jsprid=None):
|
||||
"""
|
||||
This function is used to return modified SQL for the
|
||||
selected program node.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
jsid: Job Scheduler ID
|
||||
jsprid: Job program ID (optional)
|
||||
"""
|
||||
data = {}
|
||||
for k, v in request.args.items():
|
||||
try:
|
||||
# comments should be taken as is because if user enters a
|
||||
# json comment it is parsed by loads which should not happen
|
||||
if k in ('jsprdesc',):
|
||||
data[k] = v
|
||||
else:
|
||||
data[k] = json.loads(v)
|
||||
except ValueError:
|
||||
data[k] = v
|
||||
|
||||
try:
|
||||
sql = render_template(
|
||||
"/".join([self.template_path, self._CREATE_SQL]),
|
||||
program_name=data['jsprname'],
|
||||
program_type=data['jsprtype'],
|
||||
program_action=data['jsprproc']
|
||||
if data['jsprtype'] == 'STORED_PROCEDURE' else
|
||||
data['jsprcode'],
|
||||
number_of_arguments=data['jsprnoofargs'],
|
||||
enabled=data['jsprenabled'],
|
||||
comments=data['jsprdesc'],
|
||||
arguments=data['jsprarguments'],
|
||||
conn=self.conn
|
||||
)
|
||||
|
||||
return make_json_response(
|
||||
data=sql,
|
||||
status=200
|
||||
)
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def sql(self, gid, sid, did, jsid, jsprid):
|
||||
"""
|
||||
This function will generate sql for the sql panel
|
||||
"""
|
||||
try:
|
||||
SQL = render_template("/".join(
|
||||
[self.template_path, self._PROPERTIES_SQL]
|
||||
), jsprid=jsprid)
|
||||
|
||||
status, res = self.conn.execute_dict(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
if len(res['rows']) == 0:
|
||||
return gone(
|
||||
gettext("Could not find the DBMS Schedule.")
|
||||
)
|
||||
|
||||
data = res['rows'][0]
|
||||
# Get the formatted program args
|
||||
get_formatted_program_args(self.template_path, self.conn, data)
|
||||
|
||||
SQL = render_template(
|
||||
"/".join([self.template_path, self._CREATE_SQL]),
|
||||
display_comments=True,
|
||||
program_name=data['jsprname'],
|
||||
program_type=data['jsprtype'],
|
||||
program_action=data['jsprproc']
|
||||
if data['jsprtype'] == 'STORED_PROCEDURE' else
|
||||
data['jsprcode'],
|
||||
number_of_arguments=data['jsprnoofargs'],
|
||||
enabled=data['jsprenabled'],
|
||||
comments=data['jsprdesc'],
|
||||
arguments=data['jsprarguments'] if 'jsprarguments' in data
|
||||
else [],
|
||||
conn=self.conn
|
||||
)
|
||||
|
||||
return ajax_response(response=SQL)
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def get_procedures(self, gid, sid, did, jsid=None):
|
||||
"""
|
||||
This function will return procedure list
|
||||
:param gid: group id
|
||||
:param sid: server id
|
||||
:param did: database id
|
||||
:return:
|
||||
"""
|
||||
res = []
|
||||
sql = render_template("/".join([self.template_path,
|
||||
'get_procedures.sql']),
|
||||
datlastsysoid=self._DATABASE_LAST_SYSTEM_OID)
|
||||
status, rset = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=rset)
|
||||
|
||||
for row in rset['rows']:
|
||||
# Get formatted Arguments
|
||||
frmtd_params, _ = format_arguments_from_db(
|
||||
self.template_path, self.conn, row)
|
||||
|
||||
res.append({'label': row['proc_name'],
|
||||
'value': row['proc_name'],
|
||||
'no_of_args': row['number_of_arguments'],
|
||||
'arguments': frmtd_params['arguments']
|
||||
})
|
||||
|
||||
return make_json_response(
|
||||
data=res,
|
||||
status=200
|
||||
)
|
||||
|
||||
@check_precondition
|
||||
def enable_disable(self, gid, sid, did, jsid, jsprid=None):
|
||||
"""
|
||||
This function is used to enable/disable program.
|
||||
"""
|
||||
data = request.form if request.form else json.loads(
|
||||
request.data
|
||||
)
|
||||
|
||||
status, res = self.conn.execute_void(
|
||||
render_template(
|
||||
"/".join([self.template_path, 'enable_disable.sql']),
|
||||
name=data['program_name'],
|
||||
is_enable=data['is_enable_program'], conn=self.conn
|
||||
)
|
||||
)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
return make_json_response(
|
||||
success=1,
|
||||
info=gettext("Program enabled") if data['is_enable_program'] else
|
||||
gettext('Program disabled'),
|
||||
data={
|
||||
'sid': sid,
|
||||
'did': did,
|
||||
'jsid': jsid,
|
||||
'jsprid': jsprid
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
DBMSProgramView.register_node_view(blueprint)
|
||||
@@ -0,0 +1,189 @@
|
||||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2024, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import DBMSProgramSchema from './dbms_program.ui';
|
||||
import { getNodeAjaxOptions } from '../../../../../../../static/js/node_ajax';
|
||||
import getApiInstance from '../../../../../../../../static/js/api_instance';
|
||||
|
||||
define('pgadmin.node.dbms_program', [
|
||||
'sources/gettext', 'sources/url_for', 'sources/pgadmin',
|
||||
'pgadmin.browser', 'pgadmin.browser.collection',
|
||||
], function(gettext, url_for, pgAdmin, pgBrowser) {
|
||||
|
||||
if (!pgBrowser.Nodes['coll-dbms_program']) {
|
||||
pgBrowser.Nodes['coll-dbms_program'] =
|
||||
pgBrowser.Collection.extend({
|
||||
node: 'dbms_program',
|
||||
label: gettext('DBMS Programs'),
|
||||
type: 'coll-dbms_program',
|
||||
columns: ['jsprname', 'jsprtype', 'jsprenabled', 'jsprdesc'],
|
||||
hasSQL: false,
|
||||
hasDepends: false,
|
||||
hasStatistics: false,
|
||||
hasScriptTypes: [],
|
||||
canDrop: true,
|
||||
canDropCascade: false,
|
||||
});
|
||||
}
|
||||
|
||||
if (!pgBrowser.Nodes['dbms_program']) {
|
||||
pgAdmin.Browser.Nodes['dbms_program'] = pgAdmin.Browser.Node.extend({
|
||||
parent_type: 'dbms_job_scheduler',
|
||||
type: 'dbms_program',
|
||||
label: gettext('DBMS Program'),
|
||||
node_image: 'icon-pga_jobstep',
|
||||
epasHelp: true,
|
||||
epasURL: 'https://www.enterprisedb.com/docs/epas/$VERSION$/epas_compat_bip_guide/03_built-in_packages/15_dbms_scheduler/03_create_program/',
|
||||
dialogHelp: url_for('help.static', {'filename': 'dbms_program.html'}),
|
||||
canDrop: true,
|
||||
hasSQL: true,
|
||||
hasDepends: false,
|
||||
hasStatistics: false,
|
||||
Init: function() {
|
||||
/* Avoid multiple registration of menus */
|
||||
if (this.initialized)
|
||||
return;
|
||||
|
||||
this.initialized = true;
|
||||
|
||||
pgBrowser.add_menus([{
|
||||
name: 'create_dbms_program_on_coll', node: 'coll-dbms_program', module: this,
|
||||
applies: ['object', 'context'], callback: 'show_obj_properties',
|
||||
category: 'create', priority: 4, label: gettext('DBMS Program...'),
|
||||
data: {action: 'create'},
|
||||
}, {
|
||||
name: 'create_dbms_program', node: 'dbms_program', module: this,
|
||||
applies: ['object', 'context'], callback: 'show_obj_properties',
|
||||
category: 'create', priority: 4, label: gettext('DBMS Program...'),
|
||||
data: {action: 'create'},
|
||||
}, {
|
||||
name: 'create_dbms_program', node: 'dbms_job_scheduler', module: this,
|
||||
applies: ['object', 'context'], callback: 'show_obj_properties',
|
||||
category: 'create', priority: 4, label: gettext('DBMS Program...'),
|
||||
data: {action: 'create'},
|
||||
}, {
|
||||
name: 'enable_program', node: 'dbms_program', module: this,
|
||||
applies: ['object', 'context'], callback: 'enable_program',
|
||||
priority: 4, label: gettext('Enable Program'),
|
||||
enable : 'is_enabled',data: {
|
||||
data_disabled: gettext('Program is already enabled.'),
|
||||
},
|
||||
}, {
|
||||
name: 'disable_program', node: 'dbms_program', module: this,
|
||||
applies: ['object', 'context'], callback: 'disable_program',
|
||||
priority: 4, label: gettext('Disable Program'),
|
||||
enable : 'is_disabled',data: {
|
||||
data_disabled: gettext('Program is already disabled.'),
|
||||
},
|
||||
}
|
||||
]);
|
||||
},
|
||||
is_enabled: function(node) {
|
||||
return !node?.is_enabled;
|
||||
},
|
||||
is_disabled: function(node) {
|
||||
return node?.is_enabled;
|
||||
},
|
||||
callbacks: {
|
||||
enable_program: function(args, notify) {
|
||||
let input = args || {},
|
||||
obj = this,
|
||||
t = pgBrowser.tree,
|
||||
i = 'item' in input ? input.item : t.selected(),
|
||||
d = i ? t.itemData(i) : undefined;
|
||||
|
||||
if (d) {
|
||||
notify = notify || _.isUndefined(notify) || _.isNull(notify);
|
||||
let enable = function() {
|
||||
let data = d;
|
||||
getApiInstance().put(
|
||||
obj.generate_url(i, 'enable_disable', d, true),
|
||||
{'program_name': data.label, 'is_enable_program': true}
|
||||
).then(({data: res})=> {
|
||||
if (res.success == 1) {
|
||||
pgAdmin.Browser.notifier.success(res.info);
|
||||
t.removeIcon(i);
|
||||
data.icon = 'icon-pga_jobstep';
|
||||
data.is_enabled = true;
|
||||
t.addIcon(i, {icon: data.icon});
|
||||
t.updateAndReselectNode(i, data);
|
||||
}
|
||||
}).catch(function(error) {
|
||||
pgAdmin.Browser.notifier.pgRespErrorNotify(error);
|
||||
t.refresh(i);
|
||||
});
|
||||
};
|
||||
if (notify) {
|
||||
pgAdmin.Browser.notifier.confirm(
|
||||
gettext('Enable Program'),
|
||||
gettext('Are you sure you want to enable the program %s?', d.label),
|
||||
function() { enable(); },
|
||||
function() { return true;},
|
||||
);
|
||||
} else {
|
||||
enable();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
disable_program: function(args, notify) {
|
||||
let input = args || {},
|
||||
obj = this,
|
||||
t = pgBrowser.tree,
|
||||
i = 'item' in input ? input.item : t.selected(),
|
||||
d = i ? t.itemData(i) : undefined;
|
||||
|
||||
if (d) {
|
||||
notify = notify || _.isUndefined(notify) || _.isNull(notify);
|
||||
let disable = function() {
|
||||
let data = d;
|
||||
getApiInstance().put(
|
||||
obj.generate_url(i, 'enable_disable', d, true),
|
||||
{'program_name': data.label, 'is_enable_program': false}
|
||||
).then(({data: res})=> {
|
||||
if (res.success == 1) {
|
||||
pgAdmin.Browser.notifier.success(res.info);
|
||||
t.removeIcon(i);
|
||||
data.icon = 'icon-pga_jobstep-disabled';
|
||||
data.is_enabled = false;
|
||||
t.addIcon(i, {icon: data.icon});
|
||||
t.updateAndReselectNode(i, data);
|
||||
}
|
||||
}).catch(function(error) {
|
||||
pgAdmin.Browser.notifier.pgRespErrorNotify(error);
|
||||
t.refresh(i);
|
||||
});
|
||||
};
|
||||
if (notify) {
|
||||
pgAdmin.Browser.notifier.confirm(
|
||||
gettext('Disable Program'),
|
||||
gettext('Are you sure you want to disable the program %s?', d.label),
|
||||
function() { disable(); },
|
||||
function() { return true;},
|
||||
);
|
||||
} else {
|
||||
disable();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
},
|
||||
getSchema: function(treeNodeInfo, itemNodeData) {
|
||||
return new DBMSProgramSchema(
|
||||
{
|
||||
procedures: ()=>getNodeAjaxOptions('get_procedures', this, treeNodeInfo, itemNodeData),
|
||||
}
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return pgBrowser.Nodes['dbms_program'];
|
||||
});
|
||||
@@ -0,0 +1,76 @@
|
||||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2024, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import gettext from 'sources/gettext';
|
||||
import BaseUISchema from 'sources/SchemaView/base_schema.ui';
|
||||
import { isEmptyString } from 'sources/validators';
|
||||
import { getActionSchema } from '../../../static/js/dbms_job_scheduler_common.ui';
|
||||
|
||||
export default class DBMSProgramSchema extends BaseUISchema {
|
||||
constructor(fieldOptions={}) {
|
||||
super({
|
||||
jsprid: null,
|
||||
jsprname: '',
|
||||
jsprtype: 'PLSQL_BLOCK',
|
||||
jsprenabled: true,
|
||||
jsprnoofargs: 0,
|
||||
jsprarguments: [],
|
||||
jsprdesc: '',
|
||||
jsprproc: null,
|
||||
jsprcode: null,
|
||||
});
|
||||
this.fieldOptions = {
|
||||
procedures: [],
|
||||
...fieldOptions,
|
||||
};
|
||||
}
|
||||
|
||||
get idAttribute() {
|
||||
return 'jsprid';
|
||||
}
|
||||
|
||||
get baseFields() {
|
||||
let obj = this;
|
||||
return [
|
||||
{
|
||||
id: 'jsprid', label: gettext('ID'), type: 'int', mode: ['properties'],
|
||||
readonly: function(state) {return !obj.isNew(state); },
|
||||
}, {
|
||||
id: 'jsprname', label: gettext('Name'), cell: 'text',
|
||||
type: 'text', noEmpty: true,
|
||||
readonly: function(state) {return !obj.isNew(state); },
|
||||
}, {
|
||||
id: 'jsprenabled', label: gettext('Enabled?'), type: 'switch', cell: 'switch',
|
||||
readonly: function(state) {return !obj.isNew(state); },
|
||||
},
|
||||
// Add the Action Schema
|
||||
...getActionSchema(obj, 'program'),
|
||||
{
|
||||
id: 'jsprdesc', label: gettext('Comment'), type: 'multiline',
|
||||
readonly: function(state) {return !obj.isNew(state); },
|
||||
}
|
||||
];
|
||||
}
|
||||
validate(state, setError) {
|
||||
/* code validation*/
|
||||
if (state.jsprtype == 'PLSQL_BLOCK' && isEmptyString(state.jsprcode)) {
|
||||
setError('jsprcode', gettext('Code cannot be empty.'));
|
||||
return true;
|
||||
} else {
|
||||
setError('jsprcode', null);
|
||||
}
|
||||
|
||||
if (state.jsprtype == 'STORED_PROCEDURE' && isEmptyString(state.jsprproc)) {
|
||||
setError('jsprproc', gettext('Procedure cannot be empty.'));
|
||||
return true;
|
||||
} else {
|
||||
setError('jsprproc', null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
{% if display_comments %}
|
||||
-- DBMS Program: '{{ program_name }}'
|
||||
|
||||
-- EXEC dbms_scheduler.DROP_PROGRAM('{{ program_name }}');
|
||||
|
||||
{% endif %}
|
||||
EXEC dbms_scheduler.CREATE_PROGRAM(
|
||||
program_name => {{ program_name|qtLiteral(conn) }},
|
||||
program_type => {{ program_type|qtLiteral(conn) }},
|
||||
program_action => {{ program_action|qtLiteral(conn) }}{% if number_of_arguments or enabled or comments %},{% endif %}
|
||||
{% if number_of_arguments %}
|
||||
|
||||
number_of_arguments => {{ number_of_arguments }}{% if enabled or comments %},{% endif %}
|
||||
{% endif %}
|
||||
{% if enabled %}
|
||||
|
||||
enabled => {{ enabled }}{% if comments %},{% endif %}
|
||||
{% endif %}
|
||||
{% if comments %}
|
||||
|
||||
comments => {{ comments|qtLiteral(conn) }}
|
||||
{% endif %}
|
||||
);
|
||||
|
||||
{% for args_list_item in arguments %}
|
||||
EXEC dbms_scheduler.DEFINE_PROGRAM_ARGUMENT(
|
||||
program_name => {{ program_name|qtLiteral(conn) }},
|
||||
argument_position => {{ args_list_item['argid'] }},
|
||||
argument_name => {{ args_list_item['argname']|qtLiteral(conn) }},
|
||||
argument_type => {{ args_list_item['argtype']|qtLiteral(conn) }},
|
||||
default_value => {{ args_list_item['argdefval']|qtLiteral(conn) }}
|
||||
);
|
||||
|
||||
{% endfor %}
|
||||
@@ -0,0 +1,3 @@
|
||||
EXEC dbms_scheduler.DROP_PROGRAM(
|
||||
{{ program_name|qtLiteral(conn) }}
|
||||
);
|
||||