diff --git a/docs/en_US/images/backup_disable.png b/docs/en_US/images/backup_disable.png index 4a81f3334..1df41fa08 100644 Binary files a/docs/en_US/images/backup_disable.png and b/docs/en_US/images/backup_disable.png differ diff --git a/docs/en_US/images/backup_do_not_save.png b/docs/en_US/images/backup_do_not_save.png index 0652eb7fb..7bb25b998 100644 Binary files a/docs/en_US/images/backup_do_not_save.png and b/docs/en_US/images/backup_do_not_save.png differ diff --git a/docs/en_US/images/backup_general.png b/docs/en_US/images/backup_general.png index 15cec6126..7db9f8140 100644 Binary files a/docs/en_US/images/backup_general.png and b/docs/en_US/images/backup_general.png differ diff --git a/docs/en_US/images/backup_globals_general.png b/docs/en_US/images/backup_globals_general.png index ad4cdd597..3d9b21c78 100644 Binary files a/docs/en_US/images/backup_globals_general.png and b/docs/en_US/images/backup_globals_general.png differ diff --git a/docs/en_US/images/backup_miscellaneous.png b/docs/en_US/images/backup_miscellaneous.png index 14f49e98d..78f184c31 100644 Binary files a/docs/en_US/images/backup_miscellaneous.png and b/docs/en_US/images/backup_miscellaneous.png differ diff --git a/docs/en_US/images/backup_objects.png b/docs/en_US/images/backup_objects.png index de6778abb..91526a739 100644 Binary files a/docs/en_US/images/backup_objects.png and b/docs/en_US/images/backup_objects.png differ diff --git a/docs/en_US/images/backup_queries.png b/docs/en_US/images/backup_queries.png index 3e03322a1..d590644d1 100644 Binary files a/docs/en_US/images/backup_queries.png and b/docs/en_US/images/backup_queries.png differ diff --git a/docs/en_US/images/backup_sections.png b/docs/en_US/images/backup_sections.png index 5671ecb1f..26f152515 100644 Binary files a/docs/en_US/images/backup_sections.png and b/docs/en_US/images/backup_sections.png differ diff --git a/docs/en_US/images/backup_server_disable.png b/docs/en_US/images/backup_server_disable.png index 334acf252..1df41fa08 100644 Binary files a/docs/en_US/images/backup_server_disable.png and b/docs/en_US/images/backup_server_disable.png differ diff --git a/docs/en_US/images/backup_server_do_not_save.png b/docs/en_US/images/backup_server_do_not_save.png index c26a9416d..e7d758eb0 100644 Binary files a/docs/en_US/images/backup_server_do_not_save.png and b/docs/en_US/images/backup_server_do_not_save.png differ diff --git a/docs/en_US/images/backup_server_general.png b/docs/en_US/images/backup_server_general.png index fe1eae575..e3702ba16 100644 Binary files a/docs/en_US/images/backup_server_general.png and b/docs/en_US/images/backup_server_general.png differ diff --git a/docs/en_US/images/backup_server_miscellaneous.png b/docs/en_US/images/backup_server_miscellaneous.png index 4f05b7135..944071bc1 100644 Binary files a/docs/en_US/images/backup_server_miscellaneous.png and b/docs/en_US/images/backup_server_miscellaneous.png differ diff --git a/docs/en_US/images/backup_server_objects.png b/docs/en_US/images/backup_server_objects.png index f31efe35b..c6d44e949 100644 Binary files a/docs/en_US/images/backup_server_objects.png and b/docs/en_US/images/backup_server_objects.png differ diff --git a/docs/en_US/images/backup_server_queries.png b/docs/en_US/images/backup_server_queries.png index 289c7b5de..4f7cece19 100644 Binary files a/docs/en_US/images/backup_server_queries.png and b/docs/en_US/images/backup_server_queries.png differ diff --git a/docs/en_US/release_notes_6_3.rst b/docs/en_US/release_notes_6_3.rst index 89086123b..4c7396961 100644 --- a/docs/en_US/release_notes_6_3.rst +++ b/docs/en_US/release_notes_6_3.rst @@ -13,6 +13,7 @@ New features Housekeeping ************ +| `Issue #6984 `_ - Port Backup Global, Backup Server, and Backup object dialog in React. Bug fixes ********* diff --git a/web/pgadmin/browser/server_groups/servers/databases/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/__init__.py index 4b1d7308d..04b93ad75 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/__init__.py +++ b/web/pgadmin/browser/server_groups/servers/databases/__init__.py @@ -537,7 +537,7 @@ class DatabaseView(PGChildNodeView): """ This function to return list of avialable encodings """ - res = [{'label': '', 'value': ''}] + res = [] SQL = render_template( "/".join([self.template_path, 'get_encodings.sql']) ) diff --git a/web/pgadmin/browser/static/js/node.js b/web/pgadmin/browser/static/js/node.js index 650d6418b..84ece42a8 100644 --- a/web/pgadmin/browser/static/js/node.js +++ b/web/pgadmin/browser/static/js/node.js @@ -497,6 +497,72 @@ define('pgadmin.browser.node', [ return null; }, + addUtilityPanel: function() { + var body = window.document.body, + el = document.createElement('div'); + + body.insertBefore(el, body.firstChild); + + var new_panel = pgBrowser.docker.addPanel( + 'utility_props', window.wcDocker.DOCK.FLOAT, undefined, { + w: (screen.width < 700 ? + screen.width * 0.95 : screen.width * 0.5), + h: (screen.height < 500 ? + screen.height * 0.95 : screen.height * 0.5), + x: (screen.width < 700 ? '2%' : '25%'), + y: (screen.height < 500 ? '2%' : '25%'), + } + ); + /*set movable false to prevent dialog from docking, + by setting this we can able to move the dialog but can't dock it + in to the frame. e.g: can't dock it in to properties and other tabs. */ + setTimeout(function() { + new_panel.moveable(false); + }, 0); + + body.removeChild(el); + + return new_panel; + }, + registerUtilityPanel: function() { + var w = pgBrowser.docker, + p = w.findPanels('utility_props'); + + if (p && p.length == 1) + return; + + var events = {}; + events[wcDocker.EVENT.RESIZE_ENDED] = function() { + var $container = this.$container.find('.obj_properties').first(), + v = $container.data('obj-view'); + + if (v && v.model && v.model) { + v.model.trigger( + 'pg-browser-resized', { + 'view': v, + 'panel': this, + 'container': $container, + }); + + } + }; + + p = new pgBrowser.Panel({ + name: 'utility_props', + showTitle: true, + isCloseable: true, + isPrivate: true, + isLayoutMember: false, + canMaximise: true, + elContainer: true, + content: '
' + gettext('Please wait while we fetch information ...') + '
', + onCreate: function(myPanel, $container) { + $container.addClass('pg-no-overflow'); + }, + events: events, + }); + p.load(pgBrowser.docker); + }, register_node_panel: function() { var w = pgBrowser.docker, p = w.findPanels('node_props'); diff --git a/web/pgadmin/browser/static/js/utility_view.jsx b/web/pgadmin/browser/static/js/utility_view.jsx new file mode 100644 index 000000000..c7ee8fd2f --- /dev/null +++ b/web/pgadmin/browser/static/js/utility_view.jsx @@ -0,0 +1,108 @@ +///////////////////////////////////////////////////////////// +// +// pgAdmin 4 - PostgreSQL Tools +// +// Copyright (C) 2013 - 2021, The pgAdmin Development Team +// This software is released under the PostgreSQL Licence +// +////////////////////////////////////////////////////////////// + +import React from 'react'; +import ReactDOM from 'react-dom'; +import pgAdmin from 'sources/pgadmin'; +import getApiInstance from 'sources/api_instance'; +import {getHelpUrl, getEPASHelpUrl} from 'pgadmin.help'; +import SchemaView from 'sources/SchemaView'; +import 'wcdocker'; + +/* The entry point for rendering React based view in properties, called in node.js */ +export function getUtilityView(schema, treeNodeInfo, actionType, formType, container, containerPanel, onSave, extraData, saveBtnName, urlBase, sqlHelpUrl, helpUrl) { + let serverInfo = treeNodeInfo && ('server' in treeNodeInfo) && + pgAdmin.Browser.serverInfo && pgAdmin.Browser.serverInfo[treeNodeInfo.server._id]; + let inCatalog = treeNodeInfo && ('catalog' in treeNodeInfo); + const api = getApiInstance(); + const url = ()=>{ + return urlBase; + }; + const confirmOnReset = pgAdmin.Browser.get_preferences_for_module('browser').confirm_on_properties_close; + + /* on save button callback, promise required */ + const onSaveClick = (isNew, data)=>new Promise((resolve, reject)=>{ + return api({ + url: url(), + method: isNew ? 'POST' : 'PUT', + data: Object.assign({}, data, extraData), + }).then((res)=>{ + /* Don't warn the user before closing dialog */ + resolve(res.data); + onSave && onSave(res.data, containerPanel); + containerPanel.close(); + }).catch((err)=>{ + reject(err); + }); + }); + + /* Callback for help button */ + const onHelp = (isSqlHelp=false, isNew=false)=>{ + if(isSqlHelp) { + let server = treeNodeInfo.server; + let url = pgAdmin.Browser.utils.pg_help_path; + let fullUrl = ''; + + if (server.server_type == 'ppas') { + fullUrl = getEPASHelpUrl(server.version); + } else { + if (sqlHelpUrl == '') { + fullUrl = getHelpUrl(url, sqlHelpUrl, server.version); + } else if (sqlHelpUrl != '') { + fullUrl = getHelpUrl(url, sqlHelpUrl, server.version); + } else { + if (isNew) { + fullUrl = getHelpUrl(url, sqlHelpUrl, server.version); + } else { + fullUrl = getHelpUrl(url, sqlHelpUrl, server.version); + } + } + } + + window.open(fullUrl, 'postgres_help'); + } else { + window.open(helpUrl, 'pgadmin_help'); + } + }; + + /* All other useful details can go with this object */ + const viewHelperProps = { + mode: actionType, + serverInfo: serverInfo ? { + type: serverInfo.server_type, + version: serverInfo.version, + }: undefined, + inCatalog: inCatalog, + }; + + let _schema = schema; + + /* Fire at will, mount the DOM */ + ReactDOM.render( + containerPanel.close()} + onHelp={onHelp} + onDataChange={()=>{ + }} + confirmOnCloseReset={confirmOnReset} + hasSQL={false} + disableSqlHelp={sqlHelpUrl == undefined || sqlHelpUrl == ''} + disableDialogHelp={helpUrl == undefined || helpUrl == ''} + />, container); +} + +/* When switching from normal node to collection node, clean up the React mounted DOM */ +export function removeNodeView(container) { + ReactDOM.unmountComponentAtNode(container); +} diff --git a/web/pgadmin/static/js/SchemaView/index.jsx b/web/pgadmin/static/js/SchemaView/index.jsx index 5f3833d54..02b5b2f7e 100644 --- a/web/pgadmin/static/js/SchemaView/index.jsx +++ b/web/pgadmin/static/js/SchemaView/index.jsx @@ -706,7 +706,7 @@ function SchemaDialogView({ {gettext('Reset')} } disabled={!dirty || saving || Boolean(formErr.name) || !formReady}> - {gettext('Save')} + {props.customSaveBtnName ? gettext(props.customSaveBtnName) : gettext('Save')} } @@ -739,6 +739,7 @@ SchemaDialogView.propTypes = { disableDialogHelp: PropTypes.bool, showFooter: PropTypes.bool, resetKey: PropTypes.any, + customSaveBtnName: PropTypes.string, }; const usePropsStyles = makeStyles((theme)=>({ diff --git a/web/pgadmin/static/js/alertify/dialog_factory.js b/web/pgadmin/static/js/alertify/dialog_factory.js index 8453ac9d1..e37b33fb1 100644 --- a/web/pgadmin/static/js/alertify/dialog_factory.js +++ b/web/pgadmin/static/js/alertify/dialog_factory.js @@ -7,7 +7,6 @@ // ////////////////////////////////////////////////////////////// -import * as BackupDialog from '../../../tools/backup/static/js/backup_dialog_wrapper'; import {RestoreDialogWrapper} from '../../../tools/restore/static/js/restore_dialog_wrapper'; import SearchObjectsDialogWrapper from '../../../tools/search_objects/static/js/search_objects_dialog_wrapper'; @@ -28,8 +27,6 @@ export class DialogFactory { return this.createRestoreDialog(dialogTitle, typeOfDialog); } else if (typeOfDialog === 'search_objects') { return this.createSearchObjectsDialog(dialogTitle, typeOfDialog); - } else { - return this.createBackupDialog(dialogTitle, typeOfDialog); } } @@ -43,16 +40,6 @@ export class DialogFactory { this.backform); } - createBackupDialog(dialogTitle, typeOfDialog) { - return new BackupDialog.BackupDialogWrapper( - this.dialogContainerSelector, dialogTitle, typeOfDialog, - this.jquery, - this.pgBrowser, - this.alertify, - this.dialogModel, - this.backform); - } - createSearchObjectsDialog(dialogTitle, typeOfDialog) { return new SearchObjectsDialogWrapper( this.dialogContainerSelector, dialogTitle, typeOfDialog, diff --git a/web/pgadmin/tools/backup/static/js/backup.js b/web/pgadmin/tools/backup/static/js/backup.js index cdbc44d9d..ffe4ec287 100644 --- a/web/pgadmin/tools/backup/static/js/backup.js +++ b/web/pgadmin/tools/backup/static/js/backup.js @@ -6,18 +6,20 @@ // This software is released under the PostgreSQL Licence // ////////////////////////////////////////////////////////////// - +import {getUtilityView, removeNodeView} from '../../../../browser/static/js/utility_view'; +import { getNodeListByName, getNodeAjaxOptions } from '../../../../browser/static/js/node_ajax'; +import BackupSchema, {getSectionSchema, getTypeObjSchema, getSaveOptSchema, getQueryOptionSchema, getDisabledOptionSchema, getMiscellaneousSchema} from './backup.ui'; +import BackupGlobalSchema, {getMiscellaneousSchema as getMiscellaneousGlobalSchema} from './backupGlobal.ui'; // Backup dialog define([ - 'sources/gettext', 'sources/url_for', 'jquery', 'underscore', + 'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'sources/pgadmin', 'pgadmin.alertifyjs', 'backbone', 'pgadmin.backgrid', 'pgadmin.backform', 'pgadmin.browser', 'sources/utils', 'tools/backup/static/js/menu_utils', - 'tools/backup/static/js/backup_dialog', 'sources/nodes/supported_database_node', ], function( - gettext, url_for, $, _, alertify, Backbone, Backgrid, Backform, pgBrowser, - commonUtils, menuUtils, globalBackupDialog, supportedNodes + gettext, url_for, $, _, pgAdmin, alertify, Backbone, Backgrid, Backform, pgBrowser, + commonUtils, menuUtils, supportedNodes ) { // if module is already initialized, refer to that. @@ -43,524 +45,6 @@ define([ with schema. */ - //Backup Model (Server Node) - var BackupModel = Backbone.Model.extend({ - idAttribute: 'id', - defaults: { - file: undefined, - role: undefined, - dqoute: false, - verbose: true, - type: undefined, - /* global */ - }, - schema: [{ - id: 'file', - label: gettext('Filename'), - type: 'text', - disabled: false, - control: Backform.FileControl, - dialog_type: 'create_file', - supp_types: ['*', 'sql', 'backup'], - }, { - id: 'role', - label: gettext('Role name'), - control: 'node-list-by-name', - node: 'role', - select2: { - allowClear: false, - }, - }, { - type: 'nested', - control: 'fieldset', - label: gettext('Miscellaneous'), - contentClass: 'row', - schema: [{ - id: 'verbose', - label: gettext('Verbose messages'), - type: 'switch', - disabled: false, - group: gettext('Miscellaneous'), - }, { - id: 'dqoute', - label: gettext('Force double quote on identifiers'), - type: 'switch', - disabled: false, - group: gettext('Miscellaneous'), - controlLabelClassName: 'control-label pg-el-sm-6 pg-el-12', - controlsClassName: 'pgadmin-controls pg-el-sm-6 pg-el-12', - }], - }, { - id: 'globals_note', - label: gettext('Note'), - text: gettext('Only objects global to the entire database will be backed up, in PLAIN format'), - type: 'note', - }, {}], - validate: function() { - // TODO: HOW TO VALIDATE ??? - return null; - }, - }); - - //Backup Model (Objects like Database/Schema/Table) - var BackupObjectModel = Backbone.Model.extend({ - idAttribute: 'id', - defaults: { - file: undefined, - role: undefined, - format: 'custom', - verbose: true, - blobs: true, - encoding: undefined, - schemas: [], - tables: [], - database: undefined, - }, - schema: [{ - id: 'file', - label: gettext('Filename'), - type: 'text', - disabled: false, - control: Backform.FileControl.extend({ - render: function() { - var attributes = this.model.toJSON(); - if (attributes.format == 'directory') { - this.field.attributes.dialog_type = 'select_folder'; - } - else { - this.field.attributes.dialog_type = 'create_file'; - } - - Backform.InputControl.prototype.render.apply(this, arguments); - return this; - }, - }), - dialog_type: 'create_file', - supp_types: ['*', 'sql', 'backup'], - deps: ['format'], - }, { - id: 'format', - label: gettext('Format'), - type: 'text', - disabled: false, - control: 'select2', - select2: { - allowClear: false, - width: '100%', - }, - options: [{ - label: gettext('Custom'), - value: 'custom', - }, - { - label: gettext('Tar'), - value: 'tar', - }, - { - label: gettext('Plain'), - value: 'plain', - }, - { - label: gettext('Directory'), - value: 'directory', - }, - ], - visible: function(m) { - if (!_.isUndefined(m.get('type')) && m.get('type') === 'server') { - setTimeout(function() { m.set('format', 'plain'); }, 10); - return false; - } - return true; - }, - }, { - id: 'ratio', - label: gettext('Compression ratio'), - type: 'int', - min: 0, - max: 9, - deps: ['format'], - disabled: function(m) { - return (m.get('format') === 'tar'); - }, - visible: function(m) { - if (!_.isUndefined(m.get('type')) && m.get('type') === 'server') - return false; - return true; - }, - }, { - id: 'encoding', - label: gettext('Encoding'), - type: 'text', - disabled: false, - node: 'database', - control: 'node-ajax-options', - url: 'get_encodings', - visible: function(m) { - if (!_.isUndefined(m.get('type')) && m.get('type') === 'server') { - var t = pgBrowser.tree, - i = t.selected(), - d = i ? t.itemData(i) : undefined; - return _.isUndefined(d) ? false : d.version >= 110000; - } - return true; - }, - }, { - id: 'no_of_jobs', - label: gettext('Number of jobs'), - type: 'int', - deps: ['format'], - disabled: function(m) { - return (m.get('format') !== 'directory'); - }, - visible: function(m) { - if (!_.isUndefined(m.get('type')) && m.get('type') === 'server') - return false; - return true; - }, - }, { - id: 'role', - label: gettext('Role name'), - control: 'node-list-by-name', - node: 'role', - select2: { - allowClear: false, - }, - }, { - id: 'server_note', - label: gettext('Note'), - text: gettext('The backup format will be PLAIN'), - type: 'note', - visible: function(m) { - return m.get('type') === 'server'; - }, - }, { - type: 'nested', - control: 'fieldset', - label: gettext('Sections'), - group: gettext('Dump options'), - contentClass: 'row', - schema: [{ - id: 'pre_data', - label: gettext('Pre-data'), - type: 'switch', - extraToggleClasses: 'pg-el-sm-6', - controlLabelClassName: 'control-label pg-el-sm-5 pg-el-12', - controlsClassName: 'pgadmin-controls pg-el-sm-7 pg-el-12', - group: gettext('Sections'), - deps: ['only_data', 'only_schema'], - disabled: function(m) { - return m.get('only_data') || - m.get('only_schema'); - }, - }, { - id: 'data', - label: gettext('Data'), - type: 'switch', - extraToggleClasses: 'pg-el-sm-6', - controlLabelClassName: 'control-label pg-el-sm-5 pg-el-12', - controlsClassName: 'pgadmin-controls pg-el-sm-7 pg-el-12', - group: gettext('Sections'), - deps: ['only_data', 'only_schema'], - disabled: function(m) { - return m.get('only_data') || - m.get('only_schema'); - }, - }, { - id: 'post_data', - label: gettext('Post-data'), - type: 'switch', - extraToggleClasses: 'pg-el-sm-6', - controlLabelClassName: 'control-label pg-el-sm-5 pg-el-12', - controlsClassName: 'pgadmin-controls pg-el-sm-7 pg-el-12', - group: gettext('Sections'), - deps: ['only_data', 'only_schema'], - disabled: function(m) { - return m.get('only_data') || - m.get('only_schema'); - }, - }], - visible: function(m) { - if (!_.isUndefined(m.get('type')) && m.get('type') === 'server') - return false; - return true; - }, - }, { - type: 'nested', - control: 'fieldset', - label: gettext('Type of objects'), - group: gettext('Dump options'), - contentClass: 'row', - schema: [{ - id: 'only_data', - label: gettext('Only data'), - type: 'switch', - extraToggleClasses: 'pg-el-sm-6', - controlLabelClassName: 'control-label pg-el-sm-5 pg-el-12', - controlsClassName: 'pgadmin-controls pg-el-sm-7 pg-el-12', - group: gettext('Type of objects'), - deps: ['pre_data', 'data', 'post_data', 'only_schema'], - disabled: function(m) { - return m.get('pre_data') || - m.get('data') || - m.get('post_data') || - m.get('only_schema'); - }, - }, { - id: 'only_schema', - label: gettext('Only schema'), - type: 'switch', - extraToggleClasses: 'pg-el-sm-6', - controlLabelClassName: 'control-label pg-el-sm-5 pg-el-12', - controlsClassName: 'pgadmin-controls pg-el-sm-7 pg-el-12', - group: gettext('Type of objects'), - deps: ['pre_data', 'data', 'post_data', 'only_data'], - disabled: function(m) { - return m.get('pre_data') || - m.get('data') || - m.get('post_data') || - m.get('only_data'); - }, - }, { - id: 'blobs', - label: gettext('Blobs'), - type: 'switch', - extraToggleClasses: 'pg-el-sm-6', - controlLabelClassName: 'control-label pg-el-sm-5 pg-el-12', - controlsClassName: 'pgadmin-controls pg-el-sm-7 pg-el-12', - disabled: false, - group: gettext('Type of objects'), - visible: function(m) { - if (!_.isUndefined(m.get('type')) && m.get('type') === 'server') { - setTimeout(function() { m.set('blobs', false); }, 10); - return false; - } - return true; - }, - }], - }, { - type: 'nested', - control: 'fieldset', - label: gettext('Do not save'), - group: gettext('Dump options'), - contentClass: 'row', - schema: [{ - id: 'dns_owner', - label: gettext('Owner'), - type: 'switch', - extraToggleClasses: 'pg-el-sm-6', - controlLabelClassName: 'control-label pg-el-sm-5 pg-el-12', - controlsClassName: 'pgadmin-controls pg-el-sm-7 pg-el-12', - disabled: false, - group: gettext('Do not save'), - }, { - id: 'dns_privilege', - label: gettext('Privilege'), - type: 'switch', - extraToggleClasses: 'pg-el-sm-6', - controlLabelClassName: 'control-label pg-el-sm-5 pg-el-12', - controlsClassName: 'pgadmin-controls pg-el-sm-7 pg-el-12', - disabled: false, - group: gettext('Do not save'), - }, { - id: 'dns_tablespace', - label: gettext('Tablespace'), - type: 'switch', - extraToggleClasses: 'pg-el-sm-6', - controlLabelClassName: 'control-label pg-el-sm-5 pg-el-12', - controlsClassName: 'pgadmin-controls pg-el-sm-7 pg-el-12', - disabled: false, - group: gettext('Do not save'), - }, { - id: 'dns_unlogged_tbl_data', - label: gettext('Unlogged table data'), - type: 'switch', - extraToggleClasses: 'pg-el-sm-6', - controlLabelClassName: 'control-label pg-el-sm-5 pg-el-12', - controlsClassName: 'pgadmin-controls pg-el-sm-7 pg-el-12', - disabled: false, - group: gettext('Do not save'), - }, { - id: 'no_comments', - label: gettext('Comments'), - type: 'switch', - extraToggleClasses: 'pg-el-sm-6', - controlLabelClassName: 'control-label pg-el-sm-5 pg-el-12', - controlsClassName: 'pgadmin-controls pg-el-sm-7 pg-el-12', - disabled: false, - group: gettext('Do not save'), - visible: function() { - var t = pgBrowser.tree, - i = t.selected(), - s = _.isUndefined(i) ? undefined : t.getTreeNodeHierarchy(i)['server']; - - return _.isUndefined(s) ? false : s.version >= 110000; - }, - }], - }, { - type: 'nested', - control: 'fieldset', - label: gettext('Queries'), - group: gettext('Dump options'), - contentClass: 'row', - schema: [{ - id: 'use_column_inserts', - label: gettext('Use Column Inserts'), - type: 'switch', - extraToggleClasses: 'pg-el-sm-6', - controlLabelClassName: 'control-label pg-el-sm-5 pg-el-12', - controlsClassName: 'pgadmin-controls pg-el-sm-7 pg-el-12', - disabled: false, - group: gettext('Queries'), - }, { - id: 'use_insert_commands', - label: gettext('Use Insert Commands'), - type: 'switch', - extraToggleClasses: 'pg-el-sm-6', - controlLabelClassName: 'control-label pg-el-sm-5 pg-el-12', - controlsClassName: 'pgadmin-controls pg-el-sm-7 pg-el-12', - disabled: false, - group: gettext('Queries'), - }, { - id: 'include_create_database', - label: gettext('Include CREATE DATABASE statement'), - type: 'switch', - extraToggleClasses: 'pg-el-sm-6', - controlLabelClassName: 'control-label pg-el-sm-5 pg-el-12', - controlsClassName: 'pgadmin-controls pg-el-sm-7 pg-el-12', - disabled: false, - group: gettext('Queries'), - visible: function(m) { - if (!_.isUndefined(m.get('type')) && m.get('type') === 'server') - return false; - return true; - }, - }, { - id: 'include_drop_database', - label: gettext('Include DROP DATABASE statement'), - type: 'switch', - extraToggleClasses: 'pg-el-sm-6', - controlLabelClassName: 'control-label pg-el-sm-5 pg-el-12', - controlsClassName: 'pgadmin-controls pg-el-sm-7 pg-el-12', - group: gettext('Queries'), - deps: ['only_data'], - disabled: function(m) { - if (m.get('only_data')) { - setTimeout(function() { m.set('include_drop_database', false); }, 10); - return true; - } - return false; - }, - }, { - id: 'load_via_partition_root', - label: gettext('Load Via Partition Root'), - type: 'switch', - extraToggleClasses: 'pg-el-sm-6', - controlLabelClassName: 'control-label pg-el-sm-5 pg-el-12', - controlsClassName: 'pgadmin-controls pg-el-sm-7 pg-el-12', - disabled: false, - group: gettext('Queries'), - visible: function(m) { - if (!_.isUndefined(m.get('type')) && m.get('type') === 'server') - return false; - - var t = pgBrowser.tree, - i = t.selected(), - s = _.isUndefined(i) ? undefined : t.getTreeNodeHierarchy(i)['server']; - - return _.isUndefined(s) ? false : s.version >= 110000; - }, - }], - }, { - type: 'nested', - control: 'fieldset', - label: gettext('Disable'), - group: gettext('Dump options'), - contentClass: 'row', - schema: [{ - id: 'disable_trigger', - label: gettext('Trigger'), - type: 'switch', - extraToggleClasses: 'pg-el-sm-6', - controlLabelClassName: 'control-label pg-el-sm-5 pg-el-12', - controlsClassName: 'pgadmin-controls pg-el-sm-7 pg-el-12', - group: gettext('Disable'), - deps: ['only_data'], - disabled: function(m) { - return !(m.get('only_data')); - }, - }, { - id: 'disable_quoting', - label: gettext('$ quoting'), - type: 'switch', - extraToggleClasses: 'pg-el-sm-6', - controlLabelClassName: 'control-label pg-el-sm-5 pg-el-12', - controlsClassName: 'pgadmin-controls pg-el-sm-7 pg-el-12', - disabled: false, - group: gettext('Disable'), - }], - }, { - type: 'nested', - control: 'fieldset', - label: gettext('Miscellaneous'), - group: gettext('Dump options'), - contentClass: 'row', - schema: [{ - id: 'with_oids', - label: gettext('With OID(s)'), - type: 'switch', - extraToggleClasses: 'pg-el-sm-6', - controlLabelClassName: 'control-label pg-el-sm-5 pg-el-12', - controlsClassName: 'pgadmin-controls pg-el-sm-7 pg-el-12', - deps: ['use_column_inserts', 'use_insert_commands'], - group: gettext('Miscellaneous'), - disabled: function(m) { - var t = pgBrowser.tree, - i = t.selected(), - s = _.isUndefined(i) ? undefined : t.getTreeNodeHierarchy(i)['server']; - - if (!_.isUndefined(s) && s.version >= 120000) - return true; - - if (m.get('use_column_inserts') || m.get('use_insert_commands')) { - setTimeout(function() { m.set('with_oids', false); }, 10); - return true; - } - return false; - }, - }, { - id: 'verbose', - label: gettext('Verbose messages'), - type: 'switch', - extraToggleClasses: 'pg-el-sm-6', - controlLabelClassName: 'control-label pg-el-sm-5 pg-el-12', - controlsClassName: 'pgadmin-controls pg-el-sm-7 pg-el-12', - disabled: false, - group: gettext('Miscellaneous'), - }, { - id: 'dqoute', - label: gettext('Force double quote on identifiers'), - type: 'switch', - extraToggleClasses: 'pg-el-sm-6', - controlLabelClassName: 'control-label pg-el-sm-5 pg-el-12', - controlsClassName: 'pgadmin-controls pg-el-sm-7 pg-el-12', - disabled: false, - group: gettext('Miscellaneous'), - }, { - id: 'use_set_session_auth', - label: gettext('Use SET SESSION AUTHORIZATION'), - type: 'switch', - extraToggleClasses: 'pg-el-sm-6', - controlLabelClassName: 'control-label pg-el-sm-5 pg-el-12', - controlsClassName: 'pgadmin-controls pg-el-sm-7 pg-el-12', - disabled: false, - group: gettext('Miscellaneous'), - }], - }], - validate: function() { - return null; - }, - }); - // Create an Object Backup of pgBrowser class pgBrowser.Backup = { init: function() { @@ -574,7 +58,7 @@ define([ name: 'backup_global', module: this, applies: ['tools'], - callback: 'start_backup_global', + callback: 'startBackupGlobal', priority: 12, label: gettext('Backup Globals...'), icon: 'fa fa-save', @@ -586,7 +70,7 @@ define([ name: 'backup_server', module: this, applies: ['tools'], - callback: 'start_backup_server', + callback: 'startBackupServer', priority: 12, label: gettext('Backup Server...'), icon: 'fa fa-save', @@ -599,7 +83,7 @@ define([ module: this, node: 'server', applies: ['context'], - callback: 'start_backup_global', + callback: 'startBackupGlobal', priority: 12, label: gettext('Backup Globals...'), icon: 'fa fa-save', @@ -612,7 +96,7 @@ define([ module: this, node: 'server', applies: ['context'], - callback: 'start_backup_server', + callback: 'startBackupServer', priority: 12, label: gettext('Backup Server...'), icon: 'fa fa-save', @@ -624,7 +108,7 @@ define([ name: 'backup_object', module: this, applies: ['tools'], - callback: 'backup_objects', + callback: 'backupObjects', priority: 11, label: gettext('Backup...'), icon: 'fa fa-save', @@ -642,7 +126,7 @@ define([ node: menuUtils.backupSupportedNodes[idx], module: this, applies: ['context'], - callback: 'backup_objects', + callback: 'backupObjects', priority: 11, label: gettext('Backup...'), icon: 'fa fa-save', @@ -655,34 +139,161 @@ define([ pgBrowser.add_menus(menus); return this; }, - start_backup_global: function(action, item) { - let dialog = new globalBackupDialog.BackupDialog( - pgBrowser, - $, - alertify, - BackupModel - ); - dialog.draw(action, item, {'globals': true}, pgBrowser.stdW.calc(pgBrowser.stdW.md), pgBrowser.stdH.calc(pgBrowser.stdH.md)); + startBackupGlobal: function(action, treeItem) { + pgBrowser.Node.registerUtilityPanel(); + var panel = pgBrowser.Node.addUtilityPanel(); + var tree = pgBrowser.tree, + i = treeItem || tree.selected(), + data = i ? tree.itemData(i) : undefined, + j = panel.$container.find('.obj_properties').first(); + + var schema = this.getGlobalUISchema(treeItem); + panel.title('Backup Globals'); + panel.focus(); + var typeOfDialog = 'globals'; + var serverIdentifier = this.retrieveServerIdentifier(); + + var extraData = this.setExtraParameters(typeOfDialog); + this.showBackupDialog(schema, treeItem, j, data, panel, typeOfDialog, serverIdentifier, extraData); }, - start_backup_server: function(action, item) { - let dialog = new globalBackupDialog.BackupDialog( - pgBrowser, - $, - alertify, - BackupObjectModel - ); - dialog.draw(action, item, {'server': true}, pgBrowser.stdW.calc(pgBrowser.stdW.md), pgBrowser.stdH.calc(pgBrowser.stdH.md)); + startBackupServer: function(action, treeItem) { + pgBrowser.Node.registerUtilityPanel(); + var panel = pgBrowser.Node.addUtilityPanel(); + var tree = pgBrowser.tree, + i = treeItem || tree.selected(), + data = i ? tree.itemData(i) : undefined, + j = panel.$container.find('.obj_properties').first(); + + var schema = this.getUISchema(treeItem, 'server'); + panel.title(gettext('Backup Server')); + panel.focus(); + var typeOfDialog = 'server'; + var serverIdentifier = this.retrieveServerIdentifier(); + + var extraData = this.setExtraParameters(typeOfDialog); + this.showBackupDialog(schema, treeItem, j, data, panel, typeOfDialog, serverIdentifier, extraData); + }, + saveCallBack: function(data, dialog) { + pgBrowser.Events.trigger('pgadmin-bgprocess:created', dialog); + }, + showBackupDialog: function(schema, item, j, data, panel, typeOfDialog, serverIdentifier, extraData) { + if(schema) { + let treeNodeInfo = pgBrowser.tree.getTreeNodeHierarchy(item); + removeNodeView(j[0]); + + let urlShortcut = 'backup.create_server_job'; + if (typeOfDialog === 'backup_objects') { + urlShortcut = 'backup.create_object_job'; + } + const baseUrl = url_for(urlShortcut, { + 'sid': serverIdentifier, + }); + var sqlHelpUrl = 'backup.html'; + var helpUrl = url_for('help.static', { + 'filename': this.getHelpFile(typeOfDialog), + }); + getUtilityView( + schema, treeNodeInfo, 'create', 'dialog', j[0], panel, this.saveCallBack, extraData, 'Backup', baseUrl, sqlHelpUrl, helpUrl); + } }, // Callback to draw Backup Dialog for objects - backup_objects: function(action, treeItem) { - let dialog = new globalBackupDialog.BackupDialog( - pgBrowser, - $, - alertify, - BackupObjectModel - ); - dialog.draw(action, treeItem, null, pgBrowser.stdW.calc(pgBrowser.stdW.md), pgBrowser.stdH.calc(pgBrowser.stdH.md)); + backupObjects: function(action, treeItem) { + pgBrowser.Node.registerUtilityPanel(); + var panel = pgBrowser.Node.addUtilityPanel(); + var tree = pgBrowser.tree, + i = treeItem || tree.selected(), + data = i ? tree.itemData(i) : undefined, + j = panel.$container.find('.obj_properties').first(); + + var schema = this.getUISchema(treeItem, 'backup_objects'); + panel.title(`Backup (${pgBrowser.Nodes[data._type].label}: ${data.label})`); + panel.focus(); + + var typeOfDialog = 'backup_objects'; + var serverIdentifier = this.retrieveServerIdentifier(); + + var extraData = this.setExtraParameters(typeOfDialog); + this.showBackupDialog(schema, treeItem, j, data, panel, typeOfDialog, serverIdentifier, extraData); + }, + getUISchema: function(treeItem, backupType) { + let treeNodeInfo = pgBrowser.tree.getTreeNodeHierarchy(treeItem); + const selectedNode = pgBrowser.tree.selected(); + let itemNodeData = pgBrowser.tree.findNodeByDomElement(selectedNode).getData(); + return new BackupSchema( + ()=> getSectionSchema(), + ()=> getTypeObjSchema({backupType: backupType}), + ()=> getSaveOptSchema({nodeInfo: treeNodeInfo}), + ()=> getQueryOptionSchema({nodeInfo: treeNodeInfo, backupType: backupType}), + ()=> getDisabledOptionSchema({nodeInfo: treeNodeInfo}), + ()=> getMiscellaneousSchema({nodeInfo: treeNodeInfo}), + { + role: ()=>getNodeListByName('role', treeNodeInfo, itemNodeData), + encoding: ()=>getNodeAjaxOptions('get_encodings', pgBrowser.Nodes['database'], treeNodeInfo, itemNodeData, { + cacheNode: 'database', + cacheLevel: 'server', + }), + }, + treeNodeInfo, + pgBrowser, + backupType + ); + }, + getGlobalUISchema: function(treeItem) { + let treeNodeInfo = pgBrowser.tree.getTreeNodeHierarchy(treeItem); + const selectedNode = pgBrowser.tree.selected(); + let itemNodeData = pgBrowser.tree.findNodeByDomElement(selectedNode).getData(); + return new BackupGlobalSchema( + ()=> getMiscellaneousGlobalSchema(), + { + role: ()=>getNodeListByName('role', treeNodeInfo, itemNodeData), + } + ); + }, + retrieveServerIdentifier() { + const selectedNode = pgBrowser.tree.selected(); + + var node = pgBrowser.tree.findNodeByDomElement(selectedNode); + const treeInfo = pgBrowser.tree.getTreeNodeHierarchy(node); + return treeInfo.server._id; + }, + setExtraParameters(typeOfDialog) { + var extraData = {}; + const selectedNode = pgBrowser.tree.selected(); + var selectedTreeNode = pgBrowser.tree.findNodeByDomElement(selectedNode); + const treeInfo = pgBrowser.tree.getTreeNodeHierarchy(selectedTreeNode); + if (typeOfDialog === 'backup_objects') { + + extraData['database'] = treeInfo.database._label; + + const nodeData = selectedTreeNode.getData(); + if (nodeData._type === 'schema') { + extraData['schemas'] = [nodeData._label]; + } + + if (nodeData._type === 'table' || nodeData._type === 'partition') { + extraData['tables'] = [[treeInfo.schema._label, nodeData._label]]; + } + + /*(if (_.isEmpty(this.view.model.get('ratio'))) { + this.view.model.unset('ratio'); + }*/ + } else if(typeOfDialog === 'server') { + extraData['type'] = 'server'; + } else if(typeOfDialog === 'globals') { + extraData['type'] = 'globals'; + } + + return extraData; + }, + getHelpFile: function (dialog_type) { + if (dialog_type == 'globals') { + return 'backup_globals_dialog.html'; + } else if (dialog_type == 'server') { + return 'backup_server_dialog.html'; + } + return 'backup_dialog.html'; + } }; return pgBrowser.Backup; }); diff --git a/web/pgadmin/tools/backup/static/js/backup.ui.js b/web/pgadmin/tools/backup/static/js/backup.ui.js new file mode 100644 index 000000000..62f2c1476 --- /dev/null +++ b/web/pgadmin/tools/backup/static/js/backup.ui.js @@ -0,0 +1,560 @@ +import gettext from 'sources/gettext'; +import BaseUISchema from 'sources/SchemaView/base_schema.ui'; +import { isEmptyString } from 'sources/validators'; + +export class SectionSchema extends BaseUISchema { + constructor(fieldOptions={}, initValues) { + super({ + ...initValues, + }); + + this.fieldOptions = { + ...fieldOptions, + }; + } + + get idAttribute() { + return 'id'; + } + + + get baseFields() { + return [ + { + id: 'pre_data', + label: gettext('Pre-data'), + type: 'switch', + group: gettext('Sections'), + deps: ['only_data', 'only_schema'], + disabled: function(state) { + return state.only_data || + state.only_schema; + }, + }, { + id: 'data', + label: gettext('Data'), + type: 'switch', + group: gettext('Sections'), + deps: ['only_data', 'only_schema'], + disabled: function(state) { + return state.only_data || + state.only_schema; + }, + }, { + id: 'post_data', + label: gettext('Post-data'), + type: 'switch', + group: gettext('Sections'), + deps: ['only_data', 'only_schema'], + disabled: function(state) { + return state.only_data || + state.only_schema; + }, + } + ]; + } +} + +export function getSectionSchema() { + return new SectionSchema(); +} + +export class TypeObjSchema extends BaseUISchema { + constructor(fieldOptions={}) { + super(); + + this.fieldOptions = { + backupType: null, + ...fieldOptions, + }; + + this.backupType = this.fieldOptions.backupType; + } + + get idAttribute() { + return 'id'; + } + + + get baseFields() { + let obj = this; + return [{ + id: 'only_data', + label: gettext('Only data'), + type: 'switch', + group: gettext('Type of objects'), + deps: ['pre_data', 'data', 'post_data', 'only_schema'], + disabled: function(state) { + return state.pre_data || + state.data || + state.post_data || + state.only_schema; + }, + }, { + id: 'only_schema', + label: gettext('Only schema'), + type: 'switch', + group: gettext('Type of objects'), + deps: ['pre_data', 'data', 'post_data', 'only_data'], + disabled: function(state) { + return state.pre_data || + state.data || + state.post_data || + state.only_data; + }, + }, { + id: 'blobs', + label: gettext('Blobs'), + type: 'switch', + group: gettext('Type of objects'), + visible: function(state) { + if (!_.isUndefined(obj.backupType) && obj.backupType === 'server') { + state.blobs = false; + return false; + } + return true; + }, + }]; + } +} + +export function getTypeObjSchema(fieldOptions) { + return new TypeObjSchema(fieldOptions); +} + +export class SaveOptSchema extends BaseUISchema { + constructor(fieldOptions={}, initValues) { + super({ + id: null, + ...initValues, + }); + + this.fieldOptions = { + nodeInfo: null, + ...fieldOptions, + }; + } + + get idAttribute() { + return 'id'; + } + + + get baseFields() { + let obj = this; + return [{ + id: 'dns_owner', + label: gettext('Owner'), + type: 'switch', + disabled: false, + group: gettext('Do not save'), + }, { + id: 'dns_privilege', + label: gettext('Privilege'), + type: 'switch', + disabled: false, + group: gettext('Do not save'), + }, { + id: 'dns_tablespace', + label: gettext('Tablespace'), + type: 'switch', + disabled: false, + group: gettext('Do not save'), + }, { + id: 'dns_unlogged_tbl_data', + label: gettext('Unlogged table data'), + type: 'switch', + disabled: false, + group: gettext('Do not save'), + }, { + id: 'no_comments', + label: gettext('Comments'), + type: 'switch', + disabled: false, + group: gettext('Do not save'), + visible: function() { + var serverInfo = _.isUndefined(obj.fieldOptions.nodeInfo) ? undefined : obj.fieldOptions.nodeInfo.server; + + return _.isUndefined(serverInfo) ? false : serverInfo.version >= 110000; + }, + }]; + } +} + +export function getSaveOptSchema(fieldOptions) { + return new SaveOptSchema(fieldOptions); +} + +export class QueryOptionSchema extends BaseUISchema { + constructor(fieldOptions={}, initValues) { + super({ + id: null, + ...initValues, + }); + + this.fieldOptions = { + nodeInfo: null, + backupType: null, + ...fieldOptions, + }; + this.backupType = fieldOptions.backupType; + } + + get idAttribute() { + return 'id'; + } + + + get baseFields() { + let obj = this; + return [{ + id: 'use_column_inserts', + label: gettext('Use Column Inserts'), + type: 'switch', + disabled: false, + group: gettext('Queries'), + }, { + id: 'use_insert_commands', + label: gettext('Use Insert Commands'), + type: 'switch', + disabled: false, + group: gettext('Queries'), + }, { + id: 'include_create_database', + label: gettext('Include CREATE DATABASE statement'), + type: 'switch', + disabled: false, + group: gettext('Queries'), + visible: function() { + if (!_.isUndefined(obj.backupType) && obj.backupType === 'server') + return false; + return true; + }, + }, { + id: 'include_drop_database', + label: gettext('Include DROP DATABASE statement'), + type: 'switch', + group: gettext('Queries'), + deps: ['only_data'], + disabled: function(state) { + if (state.only_data) { + state.include_drop_database = false; + return true; + } + return false; + }, + }, { + id: 'load_via_partition_root', + label: gettext('Load Via Partition Root'), + type: 'switch', + disabled: false, + group: gettext('Queries'), + visible: function() { + if (!_.isUndefined(obj.backupType) && obj.backupType === 'server') + return false; + + var serverInfo = _.isUndefined(obj.fieldOptions.nodeInfo) ? undefined : obj.fieldOptions.nodeInfo.server; + + return _.isUndefined(serverInfo) ? false : serverInfo.version >= 110000; + }, + }]; + } +} + +export function getQueryOptionSchema(fieldOptions) { + return new QueryOptionSchema(fieldOptions); +} + +export class DisabledOptionSchema extends BaseUISchema { + constructor(fieldOptions={}, initValues) { + super({ + id: null, + ...initValues, + }); + + this.fieldOptions = { + nodeInfo: null, + ...fieldOptions, + }; + } + + get idAttribute() { + return 'id'; + } + + + get baseFields() { + return [{ + id: 'disable_trigger', + label: gettext('Trigger'), + type: 'switch', + group: gettext('Disable'), + deps: ['only_data'], + disabled: function(state) { + return !(state.only_data); + }, + }, { + id: 'disable_quoting', + label: gettext('$ quoting'), + type: 'switch', + disabled: false, + group: gettext('Disable'), + }]; + } +} + +export function getDisabledOptionSchema(fieldOptions) { + return new DisabledOptionSchema(fieldOptions); +} + +export class MiscellaneousSchema extends BaseUISchema { + constructor(fieldOptions={}, initValues) { + super({ + id: null, + verbose: true, + ...initValues, + }); + + this.fieldOptions = { + nodeInfo: null, + ...fieldOptions, + }; + } + + get idAttribute() { + return 'id'; + } + + + get baseFields() { + let obj = this; + return [{ + id: 'with_oids', + label: gettext('With OID(s)'), + type: 'switch', + deps: ['use_column_inserts', 'use_insert_commands'], + group: gettext('Miscellaneous'), + disabled: function(state) { + var serverInfo = _.isUndefined(obj.fieldOptions.nodeInfo) ? undefined : obj.fieldOptions.nodeInfo.server; + + if (!_.isUndefined(serverInfo) && serverInfo.version >= 120000) + return true; + + if (state.use_column_inserts || state.use_insert_commands) { + state.with_oids = false; + return true; + } + return false; + }, + }, { + id: 'verbose', + label: gettext('Verbose messages'), + type: 'switch', + disabled: false, + group: gettext('Miscellaneous'), + }, { + id: 'dqoute', + label: gettext('Force double quote on identifiers'), + type: 'switch', + disabled: false, + group: gettext('Miscellaneous'), + }, { + id: 'use_set_session_auth', + label: gettext('Use SET SESSION AUTHORIZATION'), + type: 'switch', + disabled: false, + group: gettext('Miscellaneous'), + }]; + } +} + +export function getMiscellaneousSchema(fieldOptions) { + return new MiscellaneousSchema(fieldOptions); +} + +export default class BackupSchema extends BaseUISchema { + constructor(getSectionSchema, getTypeObjSchema, getSaveOptSchema, getQueryOptionSchema, getDisabledOptionSchema, getMiscellaneousSchema, fieldOptions = {}, treeNodeInfo, pgBrowser, backupType) { + super({ + file: undefined, + format: 'custom', + id: null, + blobs: true, + verbose: true, + }); + + this.fieldOptions = { + encoding: null, + role: null, + ...fieldOptions, + }; + + this.treeNodeInfo = treeNodeInfo; + this.pgBrowser = pgBrowser; + this.backupType = backupType; + this.getSectionSchema = getSectionSchema; + this.getTypeObjSchema = getTypeObjSchema; + this.getSaveOptSchema = getSaveOptSchema; + this.getQueryOptionSchema = getQueryOptionSchema; + this.getDisabledOptionSchema = getDisabledOptionSchema; + this.getMiscellaneousSchema = getMiscellaneousSchema; + } + + get idAttribute() { + return 'id'; + } + + get baseFields() { + var obj = this; + return [{ + id: 'file', + label: gettext('Filename'), + type: 'file', + disabled: false, + controlProps: { + dialogType: 'create_file', + supportedTypes: ['*', 'sql', 'backup'], + dialogTitle: 'Select file', + }, + deps: ['format'], + }, { + id: 'format', + label: gettext('Format'), + type: 'select', + disabled: false, + controlProps: { allowClear: false, width: '100%' }, + options: [ + { + label: gettext('Custom'), + value: 'custom', + }, + { + label: gettext('Tar'), + value: 'tar', + }, + { + label: gettext('Plain'), + value: 'plain', + }, + { + label: gettext('Directory'), + value: 'directory', + }, + ], + visible: function(state) { + if (!_.isUndefined(obj.backupType) && obj.backupType === 'server') { + state.format = 'plain'; + return false; + } + return true; + }, + }, { + id: 'ratio', + label: gettext('Compression ratio'), + type: 'int', + min: 0, + max: 9, + deps: ['format'], + disabled: function(state) { + return (state.format === 'tar'); + }, + visible: function() { + if (!_.isUndefined(obj.backupType) && obj.backupType === 'server') + return false; + return true; + }, + }, { + id: 'encoding', + label: gettext('Encoding'), + type: 'select', + disabled: false, + options: obj.fieldOptions.encoding, + visible: function() { + if (!_.isUndefined(obj.backupType) && obj.backupType === 'server') { + var dbNode = obj.pgBrowser.serverInfo[obj.treeNodeInfo.server._id]; + return _.isUndefined(dbNode) ? false : dbNode.version >= 110000; + } + return true; + }, + }, { + id: 'no_of_jobs', + label: gettext('Number of jobs'), + type: 'int', + deps: ['format'], + disabled: function(state) { + return (state.format !== 'directory'); + }, + visible: function() { + if (!_.isUndefined(obj.backupType) && obj.backupType === 'server') + return false; + return true; + }, + }, { + id: 'role', + label: gettext('Role name'), + type: 'select', + options: obj.fieldOptions.role, + controlProps: { + allowClear: false, + }, + }, { + id: 'server_note', + label: gettext('Note'), + text: gettext('The backup format will be PLAIN'), + type: 'note', + visible: function() { + return obj.backupType === 'server'; + }, + }, { + type: 'nested-fieldset', + label: gettext('Sections'), + group: gettext('Dump options'), + schema:new getSectionSchema(), + visible: function() { + if (!_.isUndefined(obj.backupType) && obj.backupType === 'server') + return false; + return true; + }, + }, { + type: 'nested-fieldset', + label: gettext('Type of objects'), + group: gettext('Dump options'), + schema: obj.getTypeObjSchema() + }, { + type: 'nested-fieldset', + label: gettext('Do not save'), + group: gettext('Dump options'), + schema: obj.getSaveOptSchema(), + }, { + type: 'nested-fieldset', + label: gettext('Queries'), + group: gettext('Dump options'), + schema: obj.getQueryOptionSchema(), + }, { + type: 'nested-fieldset', + label: gettext('Disable'), + group: gettext('Dump options'), + schema: obj.getDisabledOptionSchema(), + }, { + type: 'nested-fieldset', + label: gettext('Miscellaneous'), + group: gettext('Dump options'), + schema: obj.getMiscellaneousSchema(), + }]; + } + + validate(state, setError) { + if (isEmptyString(state.service)) { + let errmsg = null; + /* events validation*/ + if (!state.file) { + errmsg = gettext('Please provide a filename.'); + setError('file', errmsg); + return true; + } else { + errmsg = null; + setError('file', errmsg); + } + } + } + +} diff --git a/web/pgadmin/tools/backup/static/js/backupGlobal.ui.js b/web/pgadmin/tools/backup/static/js/backupGlobal.ui.js new file mode 100644 index 000000000..4fdb4d4c0 --- /dev/null +++ b/web/pgadmin/tools/backup/static/js/backupGlobal.ui.js @@ -0,0 +1,108 @@ +import gettext from 'sources/gettext'; +import BaseUISchema from 'sources/SchemaView/base_schema.ui'; +import { isEmptyString } from 'sources/validators'; + +export class MiscellaneousSchema extends BaseUISchema { + constructor(fieldOptions={}) { + super(); + + this.fieldOptions = { + ...fieldOptions, + }; + } + + get idAttribute() { + return 'id'; + } + + + get baseFields() { + return [{ + id: 'verbose', + label: gettext('Verbose messages'), + type: 'switch', + disabled: false, + group: gettext('Miscellaneous'), + }, { + id: 'dqoute', + label: gettext('Force double quote on identifiers'), + type: 'switch', + disabled: false, + group: gettext('Miscellaneous') + }]; + } +} + +export function getMiscellaneousSchema() { + return new MiscellaneousSchema(); +} + +export default class BackupGlobalSchema extends BaseUISchema { + constructor(getMiscellaneousSchema, fieldOptions = {}) { + super({ + id: null, + verbose: true, + }); + + this.fieldOptions = { + role: null, + ...fieldOptions, + }; + + this.getMiscellaneousSchema = getMiscellaneousSchema; + } + + get idAttribute() { + return 'id'; + } + + get baseFields() { + var obj = this; + return [{ + id: 'file', + label: gettext('Filename'), + type: 'file', + disabled: false, + controlProps: { + dialogType: 'create_file', + supportedTypes: ['*', 'sql', 'backup'], + dialogTitle: 'Select file', + }, + dialog_title: 'Select file', + }, { + id: 'role', + label: gettext('Role name'), + type: 'select', + options: obj.fieldOptions.role, + controlProps: { + allowClear: false, + }, + }, { + type: 'nested-fieldset', + label: gettext('Miscellaneous'), + contentClass: 'row', + schema: obj.getMiscellaneousSchema(), + }, { + id: 'globals_note', + label: gettext('Note'), + text: gettext('Only objects global to the entire database will be backed up, in PLAIN format'), + type: 'note', + }]; + } + + validate(state, setError) { + if (isEmptyString(state.service)) { + let errmsg = null; + /* validation */ + if (!state.file) { + errmsg = gettext('Please provide a filename.'); + setError('file', errmsg); + return true; + } else { + errmsg = null; + setError('file', errmsg); + } + } + } + +} diff --git a/web/pgadmin/tools/backup/static/js/backup_dialog.js b/web/pgadmin/tools/backup/static/js/backup_dialog.js deleted file mode 100644 index fb18ffd41..000000000 --- a/web/pgadmin/tools/backup/static/js/backup_dialog.js +++ /dev/null @@ -1,103 +0,0 @@ -///////////////////////////////////////////////////////////// -// -// pgAdmin 4 - PostgreSQL Tools -// -// Copyright (C) 2013 - 2021, The pgAdmin Development Team -// This software is released under the PostgreSQL Licence -// -////////////////////////////////////////////////////////////// - -import gettext from '../../../../static/js/gettext'; -import Backform from '../../../../static/js/backform.pgadmin'; -import {Dialog} from '../../../../static/js/alertify/dialog'; -import url_for from 'sources/url_for'; -import axios from 'axios/index'; -import {retrieveAncestorOfTypeServer} from 'sources/tree/tree_utils'; -import {hasBinariesConfiguration} from 'sources/utils'; - -export class BackupDialog extends Dialog { - constructor(pgBrowser, $, alertify, BackupModel, backform = Backform) { - super(gettext('Backup Error'), - '
', - pgBrowser, $, alertify, BackupModel, backform - ); - } - - url_for_utility_exists(id, params){ - return url_for('backup.utility_exists', { - 'sid': id, - 'backup_obj_type': params == null ? 'objects' : 'servers', - }); - } - - draw(action, aciTreeItem, params, width=0, height=0) { - const serverInformation = retrieveAncestorOfTypeServer(this.pgBrowser, aciTreeItem, gettext('Backup Error'), this.alertify); - - if (!serverInformation) { - return; - } - - if (!hasBinariesConfiguration(this.pgBrowser, serverInformation, this.alertify)) { - return; - } - - var sid = serverInformation._type == 'database' ? serverInformation._pid : serverInformation._id; - const baseUrl = this.url_for_utility_exists(sid, params); - // Check pg_dump or pg_dumpall utility exists or not. - let that = this; - axios.get( - baseUrl - ).then(function(res) { - if (!res.data.success) { - that.alertify.alert( - gettext('Utility not found'), - res.data.errormsg - ); - return; - } - const typeOfDialog = BackupDialog.typeOfDialog(params); - - if (!that.canExecuteOnCurrentDatabase(aciTreeItem)) { - return; - } - const dialog = that.createOrGetDialog( - BackupDialog.dialogTitle(typeOfDialog), - typeOfDialog - ); - dialog(true).resizeTo(width, height); - }).catch(function() { - that.alertify.alert( - gettext('Utility not found'), - gettext('Failed to fetch Utility information') - ); - return; - }); - } - - static typeOfDialog(params) { - if (params === null) { - return 'backup_objects'; - } - let typeOfDialog = 'server'; - if (!_.isUndefined(params['globals']) && params['globals']) { - typeOfDialog = 'globals'; - } - return typeOfDialog; - } - - static dialogTitle(typeOfDialog) { - if (typeOfDialog === 'backup_objects') { - return null; - } - return ((typeOfDialog === 'globals') ? - gettext('Backup Globals...') : - gettext('Backup Server...')); - } - - dialogName(typeOfDialog) { - if (typeOfDialog === 'backup_objects') { - return typeOfDialog; - } - return 'BackupDialog_' + typeOfDialog; - } -} diff --git a/web/pgadmin/tools/backup/static/js/backup_dialog_wrapper.js b/web/pgadmin/tools/backup/static/js/backup_dialog_wrapper.js deleted file mode 100644 index b45cb8008..000000000 --- a/web/pgadmin/tools/backup/static/js/backup_dialog_wrapper.js +++ /dev/null @@ -1,317 +0,0 @@ -///////////////////////////////////////////////////////////// -// -// pgAdmin 4 - PostgreSQL Tools -// -// Copyright (C) 2013 - 2021, The pgAdmin Development Team -// This software is released under the PostgreSQL Licence -// -////////////////////////////////////////////////////////////// - -import axios from 'axios/index'; -import gettext from '../../../../static/js/gettext'; -import url_for from '../../../../static/js/url_for'; -import _ from 'underscore'; -import {DialogWrapper} from '../../../../static/js/alertify/dialog_wrapper'; -import {fetch_ticket_lifetime} from '../../../../authenticate/static/js/kerberos'; -import userInfo from 'pgadmin.user_management.current_user'; -import pgConst from 'pgadmin.browser.constants'; - -export class BackupDialogWrapper extends DialogWrapper { - constructor(dialogContainerSelector, dialogTitle, typeOfDialog, - jquery, pgBrowser, alertify, dialogModel, backform) { - super(dialogContainerSelector, dialogTitle, jquery, - pgBrowser, alertify, dialogModel, backform); - this.typeOfDialog = typeOfDialog; - } - - main(title) { - this.set('title', title); - } - - setup() { - let get_help_file = function (dialog_type) { - if (dialog_type == 'globals') { - return 'backup_globals_dialog.html'; - } else if (dialog_type == 'server') { - return 'backup_server_dialog.html'; - } - return 'backup_dialog.html'; - }; - return { - buttons: [{ - text: '', - className: 'btn btn-primary-icon pull-left fa fa-info pg-alertify-icon-button', - attrs: { - name: 'object_help', - type: 'button', - url: 'backup.html', - label: gettext('Backup'), - 'aria-label': gettext('Backup'), - }, - }, { - text: '', - key: 112, - className: 'btn btn-primary-icon pull-left fa fa-question pg-alertify-icon-button', - attrs: { - name: 'dialog_help', - type: 'button', - label: gettext('Help'), - 'aria-label': gettext('Help'), - url: url_for('help.static', { - 'filename': get_help_file(this.typeOfDialog), - }), - }, - }, { - text: gettext('Cancel'), - key: 27, - className: 'btn btn-secondary fa fa-lg fa-times pg-alertify-button', - 'data-btn-name': 'cancel', - }, { - text: gettext('Backup'), - key: 13, - className: 'btn btn-primary fa fa-lg fa-save pg-alertify-button', - 'data-btn-name': 'backup', - }], - // Set options for dialog - options: { - title: this.dialogTitle, - //disable both padding and overflow control. - padding: !1, - overflow: !1, - model: 0, - resizable: true, - maximizable: true, - pinnable: false, - closableByDimmer: false, - modal: false, - }, - }; - } - - prepare() { - this.disableBackupButton(); - - const $container = this.jquery(this.dialogContainerSelector); - const selectedTreeNode = this.getSelectedNode(); - const selectedTreeNodeData = this.getSelectedNodeData(selectedTreeNode); - if (!selectedTreeNodeData) { - return; - } - - const node = this.pgBrowser.Nodes[selectedTreeNodeData._type]; - if (this.dialogTitle === null) { - let title = gettext('Backup (%s: %s)', node.label, selectedTreeNodeData.label); - this.main(title); - } - - const treeInfo = this.pgBrowser.tree.getTreeNodeHierarchy(selectedTreeNode); - const dialog = this.createDialog(node, treeInfo, this.typeOfDialog, $container); - this.addAlertifyClassToBackupNodeChildNodes(); - dialog.render(); - const statusBar = this.jquery( - '
' + - ' ' + - '
').appendTo($container); - - this.elements.content.appendChild($container.get(0)); - - this.focusOnDialog(this); - this.setListenersForFilenameChanges(statusBar); - } - - callback(event) { - const selectedTreeNode = this.getSelectedNode(); - const selectedTreeNodeData = this.getSelectedNodeData(selectedTreeNode); - const node = selectedTreeNodeData && this.pgBrowser.Nodes[selectedTreeNodeData._type]; - - if (this.wasHelpButtonPressed(event)) { - event.cancel = true; - this.pgBrowser.showHelp( - event.button.element.name, - event.button.element.getAttribute('url'), - node, - selectedTreeNode.getHtmlIdentifier() - ); - return; - } - - if (this.wasBackupButtonPressed(event)) { - - if (!selectedTreeNodeData) - return; - - const serverIdentifier = this.retrieveServerIdentifier(node, selectedTreeNode); - - const dialog = this; - let urlShortcut = 'backup.create_server_job'; - if (this.typeOfDialog === 'backup_objects') { - urlShortcut = 'backup.create_object_job'; - } - const baseUrl = url_for(urlShortcut, { - 'sid': serverIdentifier, - }); - - const treeInfo = this.pgBrowser.tree.getTreeNodeHierarchy(selectedTreeNode); - - this.setExtraParameters(selectedTreeNode, treeInfo); - let backupDate = this.view.model.toJSON(); - - if(userInfo['current_auth_source'] == pgConst['KERBEROS'] && treeInfo.server.gss_authenticated && (backupDate.type == 'globals' || backupDate.type == 'server')) { - let newPromise = fetch_ticket_lifetime(); - newPromise.then( - function(lifetime) { - if (lifetime < 1800 && lifetime > 0) { - dialog.alertify.warning( - 'You have '+ (Math.round(parseInt(lifetime)/60)).toString() +' minutes left on your ticket - if the dump takes longer than that, it may fail."' - ); - } - }, - function() { - dialog.alertify.warning( - gettext('Please renew your kerberos ticket, it has been expired.') - ); - } - ); - } - - axios.post( - baseUrl, - backupDate - ).then(function (res) { - if (res.data.success) { - dialog.alertify.success(gettext('Backup job created.'), 5); - dialog.pgBrowser.Events.trigger('pgadmin-bgprocess:created', dialog); - } else { - dialog.alertify.alert( - gettext('Backup job creation failed.'), - res.data.errormsg - ); - } - }).catch(function (error) { - try { - const err = error.response.data; - dialog.alertify.alert( - gettext('Backup job failed.'), - err.errormsg - ); - } catch (e) { - console.warn(e.stack || e); - } - }); - } - } - - addAlertifyClassToBackupNodeChildNodes() { - this.jquery(this.elements.body.childNodes[0]).addClass( - 'alertify_tools_dialog_properties obj_properties' - ); - } - - getSelectedNode() { - const tree = this.pgBrowser.tree; - const selectedNode = tree.selected(); - if (selectedNode) { - return tree.findNodeByDomElement(selectedNode); - } else { - return undefined; - } - } - - disableBackupButton() { - this.__internal.buttons[3].element.disabled = true; - } - - enableBackupButton() { - this.__internal.buttons[3].element.disabled = false; - } - - createDialog(node, treeInfo, typeOfDialog, $container) { - let attributes = {}; - if (typeOfDialog !== 'backup_objects') { - attributes['type'] = typeOfDialog; - } - // Instance of backbone model - const newModel = new this.dialogModel(attributes, { - node_info: treeInfo, - }); - const fields = this.backform.generateViewSchema( - treeInfo, newModel, 'create', node, treeInfo.server, true - ); - - return this.view = new this.backform.Dialog({ - el: $container, - model: newModel, - schema: fields, - }); - } - - retrieveServerIdentifier(node, selectedTreeNode) { - const treeInfo = this.pgBrowser.tree.getTreeNodeHierarchy(selectedTreeNode); - return treeInfo.server._id; - } - - setListenersForFilenameChanges(statusBar) { - const self = this; - - this.view.model.on('change', function () { - const ctx = this; - var errmsg; - - const showError = function(errorField, errormsg) { - ctx.errorModel.set(errorField, errormsg); - statusBar.removeClass('d-none'); - statusBar.find('.alert-text').html(errormsg); - self.elements.dialog.querySelector('.close-error').addEventListener('click', ()=>{ - statusBar.addClass('d-none'); - ctx.errorModel.set(errorField, errormsg); - }); - }; - - if (!_.isUndefined(this.get('file')) && this.get('file') !== '') { - this.errorModel.clear(); - statusBar.addClass('d-none'); - self.enableBackupButton(); - } else { - self.disableBackupButton(); - errmsg = gettext('Please provide a filename'); - showError('file', errmsg); - } - }); - } - - setExtraParameters(selectedTreeNode, treeInfo) { - if (this.typeOfDialog === 'backup_objects') { - - this.view.model.set('database', treeInfo.database._label); - - const nodeData = selectedTreeNode.getData(); - if (nodeData._type === 'schema') { - this.view.model.set('schemas', [nodeData._label]); - } - - if (nodeData._type === 'table' || nodeData._type === 'partition') { - this.view.model.set('tables', [ - [treeInfo.schema._label, nodeData._label], - ]); - } - - if (_.isEmpty(this.view.model.get('ratio'))) { - this.view.model.unset('ratio'); - } - } - } - - wasBackupButtonPressed(event) { - return event.button['data-btn-name'] === 'backup'; - } -} diff --git a/web/regression/javascript/backup/backup_dialog_spec.js b/web/regression/javascript/backup/backup_dialog_spec.js deleted file mode 100644 index 2ac646760..000000000 --- a/web/regression/javascript/backup/backup_dialog_spec.js +++ /dev/null @@ -1,303 +0,0 @@ -///////////////////////////////////////////////////////////// -// -// pgAdmin 4 - PostgreSQL Tools -// -// Copyright (C) 2013 - 2021, The pgAdmin Development Team -// This software is released under the PostgreSQL Licence -// -////////////////////////////////////////////////////////////// -import {BackupDialog} from '../../../pgadmin/tools/backup/static/js/backup_dialog'; -import {TreeFake} from '../tree/tree_fake'; -import MockAdapter from 'axios-mock-adapter'; -import axios from 'axios/index'; - -const context = describe; - -describe('BackupDialog', () => { - let backupDialog; - let pgBrowser; - let jquerySpy; - let alertifySpy; - let backupModelSpy; - - beforeEach(() => { - pgBrowser = { - Nodes: { - server: { - hasId: true, - label: 'server', - getTreeNodeHierarchy: jasmine.createSpy('server.getTreeNodeHierarchy'), - }, - database: { - hasId: true, - label: 'database', - getTreeNodeHierarchy: jasmine.createSpy('db.getTreeNodeHierarchy'), - }, - schema: { - hasId: true, - label: 'schema', - getTreeNodeHierarchy: jasmine.createSpy('db.getTreeNodeHierarchy'), - }, - }, - stdW: { - sm: 500, - md: 700, - lg: 900, - default: 500, - }, - stdH: { - sm: 200, - md: 400, - lg: 550, - default: 550, - }, - }; - pgBrowser.tree = new TreeFake(pgBrowser); - pgBrowser.Nodes.server.hasId = true; - pgBrowser.Nodes.database.hasId = true; - jquerySpy = jasmine.createSpy('jquerySpy'); - backupModelSpy = jasmine.createSpy('backupModelSpy'); - - const hierarchy = { - children: [ - { - id: 'root', - children: [ - { - id: 'serverTreeNode', - data: { - _id: 10, - _type: 'server', - label: 'some-tree-label', - server_type: 'pg', - version: 100000, - }, - children: [ - { - id: 'some_database', - data: { - _type: 'database', - _id: 11, - label: 'some_database', - _label: 'some_database_label', - }, - }, { - id: 'database_with_equal_in_name', - data: { - _type: 'database', - label: 'some_database', - _label: '=some_database_label', - }, - }, - ], - }, - { - id: 'serverTreeNodeWrongPath', - data: { - _id: 12, - _type: 'server', - label: 'some-tree-label', - server_type: 'pg', - version: 90600, - }, - }, - { - id: 'ppasServer', - data: { - _id: 13, - _type: 'server', - label: 'some-tree-label', - server_type: 'ppas', - version: 130000, - }, - }, - { - id: 'ppasServerTreeNodeWrongPath', - data: { - _id: 14, - _type: 'server', - label: 'some-tree-label', - server_type: 'ppas', - version: 90600, - }, - }, - ], - }, - ], - }; - - pgBrowser.tree = TreeFake.build(hierarchy, pgBrowser); - }); - - describe('#draw', () => { - let networkMock; - beforeEach(() => { - networkMock = new MockAdapter(axios); - alertifySpy = jasmine.createSpyObj('alertify', ['alert', 'dialog']); - alertifySpy['backup_objects'] = jasmine.createSpy('backup_objects'); - backupDialog = new BackupDialog( - pgBrowser, - jquerySpy, - alertifySpy, - backupModelSpy - ); - - pgBrowser.get_preference = jasmine.createSpy('get_preferences'); - }); - - afterEach(() => { - networkMock.restore(); - }); - - context('there are no ancestors of the type server', () => { - it('does not create a dialog', () => { - pgBrowser.tree.selectNode([{id: 'root'}]); - backupDialog.draw(null, null, null); - expect(alertifySpy['backup_objects']).not.toHaveBeenCalled(); - }); - - it('display an alert with a Backup Error', () => { - backupDialog.draw(null, [{id: 'root'}], null); - expect(alertifySpy.alert).toHaveBeenCalledWith( - 'Backup Error', - 'Please select server or child node from the browser tree.' - ); - }); - }); - - context('there is an ancestor of the type server', () => { - context('no preference can be found', () => { - beforeEach(() => { - pgBrowser.get_preference.and.returnValue(undefined); - }); - - context('server is a PostgreSQL server', () => { - it('display an alert with "Preferences Error"', () => { - backupDialog.draw(null, [{id: 'serverTreeNode'}], null); - expect(alertifySpy.alert).toHaveBeenCalledWith( - 'Preferences Error', - 'Failed to load preference pg_bin_dir of module paths' - ); - }); - }); - - context('server is a EPAS server', () => { - it('display an alert with "Preferences Error"', () => { - backupDialog.draw(null, [{id: 'ppasServer'}], null); - expect(alertifySpy.alert).toHaveBeenCalledWith( - 'Preferences Error', - 'Failed to load preference ppas_bin_dir of module paths' - ); - }); - }); - }); - - context('preference can be found for PostgreSQL Server', () => { - context('binary folder is not configured', () => { - beforeEach(() => { - pgBrowser.get_preference.and.returnValue({value: '[{\"serverType\":\"PostgreSQL 9.6\",\"binaryPath\":null,\"isDefault\":false,\"version\":\"90600\",\"next_major_version\":\"100000\"},{\"serverType\":\"PostgreSQL 10\",\"binaryPath\":\"/Library/PostgreSQL/10/bin\",\"isDefault\":false,\"version\":\"100000\",\"next_major_version\":\"110000\"},{\"serverType\":\"PostgreSQL 11\",\"binaryPath\":\"/Library/PostgreSQL/11/bin\",\"isDefault\":false,\"version\":\"110000\",\"next_major_version\":\"120000\"},{\"serverType\":\"PostgreSQL 12\",\"binaryPath\":\"/Library/PostgreSQL/12/bin\",\"isDefault\":false,\"version\":\"120000\",\"next_major_version\":\"130000\"},{\"serverType\":\"PostgreSQL 13\",\"binaryPath\":null,\"isDefault\":false,\"version\":\"130000\",\"next_major_version\":\"140000\"}]'}); - }); - - context('server is a PostgreSQL server', () => { - it('display an alert with "Configuration required"', () => { - backupDialog.draw(null, [{id: 'serverTreeNodeWrongPath'}], null); - expect(alertifySpy.alert).toHaveBeenCalledWith( - 'Configuration required', - 'Please configure the PostgreSQL Binary Path in the Preferences dialog.' - ); - }); - }); - }); - - context('binary folder is configured', () => { - let backupDialogResizeToSpy; - beforeEach(() => { - backupDialogResizeToSpy = jasmine.createSpyObj('backupDialogResizeToSpy', ['resizeTo']); - alertifySpy['backup_objects'].and - .returnValue(backupDialogResizeToSpy); - pgBrowser.get_preference.and.returnValue({value: '[{\"serverType\":\"PostgreSQL 9.6\",\"binaryPath\":null,\"isDefault\":false,\"version\":\"90600\",\"next_major_version\":\"100000\"},{\"serverType\":\"PostgreSQL 10\",\"binaryPath\":\"/Library/PostgreSQL/10/bin\",\"isDefault\":true,\"version\":\"100000\",\"next_major_version\":\"110000\"},{\"serverType\":\"PostgreSQL 11\",\"binaryPath\":\"/Library/PostgreSQL/11/bin\",\"isDefault\":false,\"version\":\"110000\",\"next_major_version\":\"120000\"},{\"serverType\":\"PostgreSQL 12\",\"binaryPath\":\"/Library/PostgreSQL/12/bin\",\"isDefault\":false,\"version\":\"120000\",\"next_major_version\":\"130000\"},{\"serverType\":\"PostgreSQL 13\",\"binaryPath\":null,\"isDefault\":false,\"version\":\"130000\",\"next_major_version\":\"140000\"}]'}); - spyOn(backupDialog, 'url_for_utility_exists').and.returnValue('/backup/utility_exists/10/objects'); - networkMock.onGet('/backup/utility_exists/10/objects').reply(200, {'success': 1}); - }); - - it('displays the dialog when binary path is for correct server version', (done) => { - backupDialog.draw(null, [{id: 'serverTreeNode'}], null, pgBrowser.stdW.md, pgBrowser.stdH.md); - setTimeout(() => { - expect(alertifySpy['backup_objects']).toHaveBeenCalledWith(true); - expect(backupDialogResizeToSpy.resizeTo).toHaveBeenCalledWith(pgBrowser.stdW.md, pgBrowser.stdH.md); - done(); - }, 0); - }); - - it('displays the dialog when default binary path is specified', (done) => { - backupDialog.draw(null, [{id: 'serverTreeNodeWrongPath'}], null, pgBrowser.stdW.md, pgBrowser.stdH.md); - setTimeout(() => { - expect(alertifySpy['backup_objects']).toHaveBeenCalledWith(true); - expect(backupDialogResizeToSpy.resizeTo).toHaveBeenCalledWith(pgBrowser.stdW.md, pgBrowser.stdH.md); - done(); - }, 0); - }); - - context('database label contain "="', () => { - it('should create alert dialog with backup error', (done) => { - backupDialog.draw(null, [{id: 'database_with_equal_in_name'}], null); - setTimeout(() => { - expect(alertifySpy.alert).toHaveBeenCalledWith('Backup Error', - 'Databases with = symbols in the name cannot be backed up or restored using this utility.'); - done(); - }, 0); - }); - }); - }); - }); - - context('preference can be found for EPAS server', () => { - context('binary folder is not configured', () => { - beforeEach(() => { - pgBrowser.get_preference.and.returnValue({value: '[{\"serverType\":\"EDB Advanced Server 9.6\",\"binaryPath\":\"\",\"isDefault\":false,\"version\":\"90600\",\"next_major_version\":\"100000\"},{\"serverType\":\"EDB Advanced Server 10\",\"binaryPath\":null,\"isDefault\":false,\"version\":\"100000\",\"next_major_version\":\"110000\"},{\"serverType\":\"EDB Advanced Server 11\",\"binaryPath\":\"/Library/EPAS/11/bin/\",\"isDefault\":false,\"version\":\"110000\",\"next_major_version\":\"120000\"},{\"serverType\":\"EDB Advanced Server 12\",\"binaryPath\":null,\"isDefault\":false,\"version\":\"120000\",\"next_major_version\":\"130000\"},{\"serverType\":\"EDB Advanced Server 13\",\"binaryPath\":\"/Library/EPAS/13/bin/\",\"isDefault\":false,\"version\":\"130000\",\"next_major_version\":\"140000\"}]'}); - }); - - context('server is a EPAS server', () => { - it('display an alert with "Configuration required"', () => { - backupDialog.draw(null, [{id: 'ppasServerTreeNodeWrongPath'}], null); - expect(alertifySpy.alert).toHaveBeenCalledWith( - 'Configuration required', - 'Please configure the EDB Advanced Server Binary Path in the Preferences dialog.' - ); - }); - }); - }); - - context('binary folder is configured', () => { - let backupDialogResizeToSpy; - beforeEach(() => { - backupDialogResizeToSpy = jasmine.createSpyObj('backupDialogResizeToSpy', ['resizeTo']); - alertifySpy['backup_objects'].and - .returnValue(backupDialogResizeToSpy); - pgBrowser.get_preference.and.returnValue({value: '[{\"serverType\":\"EDB Advanced Server 9.6\",\"binaryPath\":\"\",\"isDefault\":false,\"version\":\"90600\",\"next_major_version\":\"100000\"},{\"serverType\":\"EDB Advanced Server 10\",\"binaryPath\":null,\"isDefault\":false,\"version\":\"100000\",\"next_major_version\":\"110000\"},{\"serverType\":\"EDB Advanced Server 11\",\"binaryPath\":\"/Library/EPAS/11/bin/\",\"isDefault\":false,\"version\":\"110000\",\"next_major_version\":\"120000\"},{\"serverType\":\"EDB Advanced Server 12\",\"binaryPath\":null,\"isDefault\":false,\"version\":\"120000\",\"next_major_version\":\"130000\"},{\"serverType\":\"EDB Advanced Server 13\",\"binaryPath\":\"/Library/EPAS/13/bin/\",\"isDefault\":true,\"version\":\"130000\",\"next_major_version\":\"140000\"}]'}); - spyOn(backupDialog, 'url_for_utility_exists').and.returnValue('/backup/utility_exists/10/objects'); - networkMock.onGet('/backup/utility_exists/10/objects').reply(200, {'success': 1}); - }); - - it('displays the dialog when binary path is for correct server version', (done) => { - backupDialog.draw(null, [{id: 'ppasServer'}], null, pgBrowser.stdW.md, pgBrowser.stdH.md); - setTimeout(() => { - expect(alertifySpy['backup_objects']).toHaveBeenCalledWith(true); - expect(backupDialogResizeToSpy.resizeTo).toHaveBeenCalledWith(pgBrowser.stdW.md, pgBrowser.stdH.md); - done(); - }, 0); - }); - - it('displays the dialog when default binary path is specified', (done) => { - backupDialog.draw(null, [{id: 'ppasServerTreeNodeWrongPath'}], null, pgBrowser.stdW.md, pgBrowser.stdH.md); - setTimeout(() => { - expect(alertifySpy['backup_objects']).toHaveBeenCalledWith(true); - expect(backupDialogResizeToSpy.resizeTo).toHaveBeenCalledWith(pgBrowser.stdW.md, pgBrowser.stdH.md); - done(); - }, 0); - }); - }); - }); - }); - }); -}); diff --git a/web/regression/javascript/backup/backup_dialog_wrapper_spec.js b/web/regression/javascript/backup/backup_dialog_wrapper_spec.js deleted file mode 100644 index e3c91ecea..000000000 --- a/web/regression/javascript/backup/backup_dialog_wrapper_spec.js +++ /dev/null @@ -1,682 +0,0 @@ -///////////////////////////////////////////////////////////// -// -// pgAdmin 4 - PostgreSQL Tools -// -// Copyright (C) 2013 - 2021, The pgAdmin Development Team -// This software is released under the PostgreSQL Licence -// -////////////////////////////////////////////////////////////// - -import {TreeFake} from '../tree/tree_fake'; -import {BackupDialogWrapper} from '../../../pgadmin/tools/backup/static/js/backup_dialog_wrapper'; -import axios from 'axios/index'; -import MockAdapter from 'axios-mock-adapter'; -import {FakeModel} from '../fake_model'; -import {TreeNode} from '../../../pgadmin/static/js/tree/tree_nodes'; - -let context = describe; - -describe('BackupDialogWrapper', () => { - let jquerySpy; - let pgBrowser; - let alertifySpy; - let dialogModelKlassSpy; - let backform; - let generatedBackupModel; - let backupDialogWrapper; - let noDataNode; - let serverTreeNode; - let databaseTreeNode; - let viewSchema; - let backupJQueryContainerSpy; - let backupNodeChildNodeSpy; - let backupNode; - - beforeEach(() => { - pgBrowser = { - Nodes: { - server: { - hasId: true, - getTreeNodeHierarchy: jasmine.createSpy('getTreeNodeHierarchy'), - }, - database: { - hasId: true, - }, - }, - keyboardNavigation: jasmine.createSpyObj('keyboardNavigation', ['getDialogTabNavigator']), - }; - - pgBrowser.tree = new TreeFake(pgBrowser); - - noDataNode = pgBrowser.tree.addNewNode('level1.1', undefined, [{id: 'level1'}]); - serverTreeNode = pgBrowser.tree.addNewNode('level2.1', { - _type: 'server', - _id: 10, - label: 'some-tree-label', - }, [{id: 'level2.1'}]); - databaseTreeNode = new TreeNode('database-tree-node', { - _type: 'database', - _label: 'some-database-label', - }, [{id: 'database-tree-node'}]); - pgBrowser.tree.addChild(serverTreeNode, databaseTreeNode); - - jquerySpy = jasmine.createSpy('jquerySpy'); - backupNode = { - __internal: { - buttons: [{}, {}, {}, { - element: { - disabled: false, - }, - }], - }, - elements: { - body: { - childNodes: [ - {}, - ], - }, - content: jasmine.createSpyObj('content', ['appendChild', 'attr']), - }, - }; - - backupJQueryContainerSpy = jasmine.createSpyObj('backupJQueryContainer', ['get', 'attr']); - backupJQueryContainerSpy.get.and.returnValue(backupJQueryContainerSpy); - - generatedBackupModel = {}; - dialogModelKlassSpy = jasmine.createSpy('dialogModelKlass'); - dialogModelKlassSpy.and.returnValue(generatedBackupModel); - - viewSchema = {}; - backform = jasmine.createSpyObj('backform', ['generateViewSchema', 'Dialog']); - backform.generateViewSchema.and.returnValue(viewSchema); - - backupNodeChildNodeSpy = jasmine.createSpyObj('something', ['addClass']); - jquerySpy.and.callFake((selector) => { - if (selector === '
') { - return backupJQueryContainerSpy; - } else if (selector === backupNode.elements.body.childNodes[0]) { - return backupNodeChildNodeSpy; - } else { - return jasmine.createSpyObj('obj', ['appendTo']); - } - }); - alertifySpy = jasmine.createSpyObj('alertify', ['alert', 'dialog']); - - }); - - describe('#prepare', () => { - beforeEach(() => { - backupDialogWrapper = new BackupDialogWrapper( - '
', - 'backupDialogTitle', - 'backup', - jquerySpy, - pgBrowser, - alertifySpy, - dialogModelKlassSpy, - backform - ); - backupDialogWrapper = Object.assign(backupDialogWrapper, backupNode); - }); - - context('no tree element is selected', () => { - it('does not create a backform dialog', () => { - backupDialogWrapper.prepare(); - expect(backform.Dialog).not.toHaveBeenCalled(); - }); - - it('disables the button "submit button" until a filename is selected', () => { - backupDialogWrapper.prepare(); - expect(backupDialogWrapper.__internal.buttons[3].element.disabled).toBe(true); - }); - }); - - context('selected tree node has no data', () => { - beforeEach(() => { - pgBrowser.tree.selectNode(noDataNode.domNode); - }); - - it('does not create a backform dialog', () => { - backupDialogWrapper.prepare(); - expect(backform.Dialog).not.toHaveBeenCalled(); - }); - - it('disables the button "submit button" until a filename is selected', () => { - backupDialogWrapper.prepare(); - expect(backupDialogWrapper.__internal.buttons[3].element.disabled).toBe(true); - }); - }); - - context('tree element is selected', () => { - let treeHierarchyInformation; - let dialogSpy; - - beforeEach(() => { - treeHierarchyInformation = { - server: { - _type: 'server', - _id: 10, - priority: 0, - label: 'some-tree-label', - }, - }; - pgBrowser.tree.selectNode(serverTreeNode.domNode); - pgBrowser.Nodes['server'].getTreeNodeHierarchy.and - .returnValue(treeHierarchyInformation); - dialogSpy = jasmine.createSpyObj('newView', ['render']); - dialogSpy.$el = jasmine.createSpyObj('$el', ['find', 'attr']); - dialogSpy.model = jasmine.createSpyObj('model', ['on']); - dialogSpy.$el.find.and.returnValue([]); - - backform.Dialog.and.returnValue(dialogSpy); - }); - - it('creates a backform dialog and displays it', () => { - backupDialogWrapper.prepare(); - expect(backform.Dialog).toHaveBeenCalledWith({ - el: backupJQueryContainerSpy, - model: generatedBackupModel, - schema: viewSchema, - }); - - expect(dialogSpy.render).toHaveBeenCalled(); - }); - - - it('add alertify classes to restore node childnode', () => { - backupDialogWrapper.prepare(); - expect(backupNodeChildNodeSpy.addClass) - .toHaveBeenCalledWith('alertify_tools_dialog_properties obj_properties'); - }); - - it('disables the button submit button until a filename is selected', () => { - backupDialogWrapper.prepare(); - expect(backupNode.__internal.buttons[3].element.disabled).toBe(true); - }); - - it('generates a new backup model', () => { - backupDialogWrapper.prepare(); - expect(dialogModelKlassSpy).toHaveBeenCalledWith( - {type: 'backup'}, - {node_info: treeHierarchyInformation} - ); - }); - - it('add the new dialog to the backup node HTML', () => { - backupDialogWrapper.prepare(); - expect(backupNode.elements.content.appendChild).toHaveBeenCalledWith(backupJQueryContainerSpy); - }); - }); - }); - - describe('onButtonClicked', () => { - let networkMock; - beforeEach(() => { - networkMock = new MockAdapter(axios); - backupDialogWrapper = new BackupDialogWrapper( - '
', - 'backupDialogTitle', - 'backup', - jquerySpy, - pgBrowser, - alertifySpy, - dialogModelKlassSpy, - backform - ); - - backupDialogWrapper = Object.assign(backupDialogWrapper, backupNode); - }); - - afterEach(() => { - networkMock.restore(); - }); - - context('dialog help button was pressed', () => { - let networkCalled; - beforeEach(() => { - networkCalled = false; - networkMock.onAny(/.*/).reply(() => { - networkCalled = true; - return [200, {}]; - }); - pgBrowser.tree.selectNode(serverTreeNode.domNode); - pgBrowser.showHelp = jasmine.createSpy('showHelp'); - - const event = { - button: { - element: { - name: 'dialog_help', - getAttribute: (attributeName) => { - if (attributeName === 'url') { - return 'http://someurl'; - } - }, - }, - }, - }; - backupDialogWrapper.callback(event); - }); - - it('displays help for dialog', () => { - expect(pgBrowser.showHelp).toHaveBeenCalledWith( - 'dialog_help', - 'http://someurl', - pgBrowser.Nodes['server'], - serverTreeNode.getHtmlIdentifier() - ); - }); - - it('does not start the backup', () => { - expect(networkCalled).toBe(false); - }); - }); - - context('object help button was pressed', () => { - let networkCalled; - beforeEach(() => { - networkCalled = false; - networkMock.onAny(/.*/).reply(() => { - networkCalled = true; - return [200, {}]; - }); - pgBrowser.tree.selectNode(serverTreeNode.domNode); - pgBrowser.showHelp = jasmine.createSpy('showHelp'); - - const event = { - button: { - element: { - name: 'object_help', - getAttribute: (attributeName) => { - if (attributeName === 'url') { - return 'http://someurl'; - } - }, - }, - }, - }; - backupDialogWrapper.callback(event); - }); - - it('displays help for dialog', () => { - expect(pgBrowser.showHelp).toHaveBeenCalledWith( - 'object_help', - 'http://someurl', - pgBrowser.Nodes['server'], - serverTreeNode.getHtmlIdentifier() - ); - }); - - it('does not start the backup', () => { - expect(networkCalled).toBe(false); - }); - }); - - context('backup button was pressed', () => { - context('no tree node is selected', () => { - it('does not start the backup', () => { - let networkCalled = false; - networkMock.onAny(/.*/).reply(() => { - networkCalled = true; - return [200, {}]; - }); - - let event = { - button: { - 'data-btn-name': 'backup', - element: { - getAttribute: () => { - return 'http://someurl'; - }, - }, - }, - }; - - backupDialogWrapper.callback(event); - expect(networkCalled).toBe(false); - }); - }); - - context('tree node has no data', () => { - it('does not start the backup', () => { - pgBrowser.tree.selectNode(noDataNode.domNode); - - let networkCalled = false; - networkMock.onAny(/.*/).reply(() => { - networkCalled = true; - return [200, {}]; - }); - - let event = { - button: { - 'data-btn-name': 'backup', - element: { - getAttribute: () => { - return 'http://someurl'; - }, - }, - }, - }; - - backupDialogWrapper.callback(event); - expect(networkCalled).toBe(false); - }); - }); - - context('tree node has data', () => { - context('when dialog type is global', () => { - let event; - beforeEach(() => { - pgBrowser.tree.selectNode(serverTreeNode.domNode); - - backupDialogWrapper.view = { - model: new FakeModel(), - }; - - event = { - button: { - 'data-btn-name': 'backup', - element: { - getAttribute: () => { - return 'http://someurl'; - }, - }, - }, - }; - }); - - context('when the backup job is created successfully', () => { - let dataSentToServer; - beforeEach(() => { - pgBrowser.Events = jasmine.createSpyObj('Events', ['trigger']); - alertifySpy.success = jasmine.createSpy('success'); - networkMock.onPost('/backup/job/10').reply((request) => { - dataSentToServer = request.data; - return [200, {'success': 1}]; - }); - - }); - - it('creates a success alert box', (done) => { - backupDialogWrapper.callback(event); - setTimeout(() => { - expect(alertifySpy.success).toHaveBeenCalledWith( - 'Backup job created.', - 5 - ); - done(); - }, 0); - }); - - it('trigger an event to background process', (done) => { - backupDialogWrapper.callback(event); - - setTimeout(() => { - expect(pgBrowser.Events.trigger).toHaveBeenCalledWith( - 'pgadmin-bgprocess:created', - backupDialogWrapper - ); - done(); - }, 0); - }); - - it('send the correct paramenters to the backend', (done) => { - backupDialogWrapper.callback(event); - setTimeout(() => { - expect(JSON.parse(dataSentToServer)).toEqual( - {} - ); - done(); - }, 0); - }); - }); - - context('when creating backup job fails', () => { - it('creates an alert box', (done) => { - alertifySpy.alert = jasmine.createSpy('alert'); - networkMock.onPost('/backup/job/10').reply(() => { - return [400, { - errormsg: 'some-error-message', - }]; - }); - - backupDialogWrapper.callback(event); - setTimeout( () => { - expect(alertifySpy.alert).toHaveBeenCalledWith( - 'Backup job failed.', - 'some-error-message' - ); - done(); - }, 0); - - }); - }); - }); - - context('when dialog type is object', () => { - let event; - beforeEach(() => { - backupDialogWrapper = new BackupDialogWrapper( - '
', - 'backupDialogTitle', - 'backup_objects', - jquerySpy, - pgBrowser, - alertifySpy, - dialogModelKlassSpy, - backform - ); - - pgBrowser.tree.selectNode(databaseTreeNode.domNode); - - backupDialogWrapper.view = { - model: new FakeModel(), - }; - - event = { - button: { - 'data-btn-name': 'backup', - element: { - getAttribute: () => { - return 'http://someurl'; - }, - }, - }, - }; - }); - - context('when the backup job is created successfully', () => { - let dataSentToServer; - beforeEach(() => { - pgBrowser.Events = jasmine.createSpyObj('Events', ['trigger']); - alertifySpy.success = jasmine.createSpy('success'); - - networkMock.onPost('/backup/job/10/object').reply((request) => { - dataSentToServer = request.data; - return [200, {'success': 1}]; - }); - }); - - it('creates a success alert box', (done) => { - backupDialogWrapper.callback(event); - setTimeout(() => { - expect(alertifySpy.success).toHaveBeenCalledWith( - 'Backup job created.', - 5 - ); - done(); - }, 0); - }); - - it('trigger an event to background process', (done) => { - backupDialogWrapper.callback(event); - - setTimeout(() => { - expect(pgBrowser.Events.trigger).toHaveBeenCalledWith( - 'pgadmin-bgprocess:created', - backupDialogWrapper - ); - done(); - }, 0); - }); - - it('send the correct parameters to the backend', (done) => { - backupDialogWrapper.callback(event); - setTimeout(() => { - expect(JSON.parse(dataSentToServer)).toEqual( - {database: 'some-database-label'} - ); - done(); - }, 0); - }); - }); - - context('when creating backup job fails', () => { - it('creates an alert box', (done) => { - alertifySpy.alert = jasmine.createSpy('alert'); - networkMock.onPost('/backup/job/10/object').reply(() => { - return [400, { - errormsg: 'some-error-message', - }]; - }); - - backupDialogWrapper.callback(event); - setTimeout(() => { - expect(alertifySpy.alert).toHaveBeenCalledWith( - 'Backup job failed.', - 'some-error-message' - ); - done(); - }, 0); - }); - }); - }); - }); - }); - }); - - xdescribe('#setExtraParameters', () => { - let selectedTreeNode; - let treeInfo; - let model; - - context('when dialog type is global', () => { - beforeEach(() => { - backupDialogWrapper = new BackupDialogWrapper( - '
', - 'backupDialogTitle', - 'backup', - jquerySpy, - pgBrowser, - alertifySpy, - dialogModelKlassSpy, - backform - ); - - treeInfo = {}; - model = new FakeModel(); - backupDialogWrapper.view = { - model: model, - }; - }); - - - it('sets nothing on the view model', () => { - backupDialogWrapper.setExtraParameters(selectedTreeNode, treeInfo); - expect(model.toJSON()).toEqual({}); - }); - }); - - context('when dialog type is object', () => { - beforeEach(() => { - backupDialogWrapper = new BackupDialogWrapper( - '
', - 'backupDialogTitle', - 'backup_objects', - jquerySpy, - pgBrowser, - alertifySpy, - dialogModelKlassSpy, - backform - ); - - treeInfo = { - database: { - _label: 'some-database-label', - }, - schema: { - _label: 'some-treeinfo-label', - }, - }; - - model = new FakeModel(); - selectedTreeNode = new TreeNode('some-selected-node', - {_type: 'some-type', _label: 'some-selected-label'}, - []); - backupDialogWrapper.view = { - model: model, - }; - }); - - it('sets the database label on the model', () => { - backupDialogWrapper.setExtraParameters(selectedTreeNode, treeInfo); - expect(model.toJSON()).toEqual({ - 'database': 'some-database-label', - }); - }); - - context('when the selected is a schema type', () => { - beforeEach(() => { - selectedTreeNode = new TreeNode('some-selected-node', - {_type: 'schema', _label: 'some-schema-label'}, - []); - }); - - it('sets the schema label on the model', () => { - backupDialogWrapper.setExtraParameters(selectedTreeNode, treeInfo); - expect(model.toJSON()).toEqual({ - 'database': 'some-database-label', - 'schemas': ['some-schema-label'], - }); - }); - }); - - context('when the selected is a table type', () => { - beforeEach(() => { - selectedTreeNode = new TreeNode('some-selected-node', - {_type: 'table', _label: 'some-table-label'}, - []); - }); - - it('sets the schema label on the model', () => { - backupDialogWrapper.setExtraParameters(selectedTreeNode, treeInfo); - expect(model.toJSON()).toEqual({ - 'database': 'some-database-label', - 'tables': [['some-treeinfo-label', 'some-table-label']], - }); - }); - }); - - context('when the model has no ratio value', () => { - beforeEach(() => { - model.set('ratio', ''); - }); - - it('sets clears the ratio value', () => { - backupDialogWrapper.setExtraParameters(selectedTreeNode, treeInfo); - expect(model.get('ratio')).toBeUndefined(); - }); - }); - - context('when the model has a valid ratio value', () => { - beforeEach(() => { - model.set('ratio', '0.25'); - }); - - it('sets clears the ratio value', () => { - backupDialogWrapper.setExtraParameters(selectedTreeNode, treeInfo); - expect(model.get('ratio')).toEqual('0.25'); - }); - }); - }); - }); -}); diff --git a/web/regression/javascript/backup/global_server_backup_dialog_spec.js b/web/regression/javascript/backup/global_server_backup_dialog_spec.js deleted file mode 100644 index b16dfa33e..000000000 --- a/web/regression/javascript/backup/global_server_backup_dialog_spec.js +++ /dev/null @@ -1,298 +0,0 @@ -///////////////////////////////////////////////////////////// -// -// pgAdmin 4 - PostgreSQL Tools -// -// Copyright (C) 2013 - 2021, The pgAdmin Development Team -// This software is released under the PostgreSQL Licence -// -////////////////////////////////////////////////////////////// -import {BackupDialog} from '../../../pgadmin/tools/backup/static/js/backup_dialog'; -import {TreeFake} from '../tree/tree_fake'; -import MockAdapter from 'axios-mock-adapter'; -import axios from 'axios/index'; - -const context = describe; - -describe('GlobalServerBackupDialog', () => { - let backupDialog; - let pgBrowser; - let jquerySpy; - let alertifySpy; - let backupModelSpy; - - let rootNode; - let serverTreeNode, serverTreeNodeWrongPath; - let ppasServerTreeNode, ppasServerTreeNodeWrongPath; - - beforeEach(() => { - pgBrowser = { - Nodes: { - server: jasmine.createSpyObj('Node[server]', ['getTreeNodeHierarchy']), - }, - stdW: { - sm: 500, - md: 700, - lg: 900, - default: 500, - }, - stdH: { - sm: 200, - md: 400, - lg: 550, - default: 550, - }, - }; - pgBrowser.tree = new TreeFake(pgBrowser); - pgBrowser.Nodes.server.hasId = true; - jquerySpy = jasmine.createSpy('jquerySpy'); - backupModelSpy = jasmine.createSpy('backupModelSpy'); - - rootNode = pgBrowser.tree.addNewNode('level1', {}, undefined, []); - serverTreeNode = pgBrowser.tree.addNewNode('level1.1', { - _type: 'server', - _id: 10, - server_type: 'pg', - version: 100000, - }, undefined, ['level1']); - serverTreeNodeWrongPath = pgBrowser.tree.addNewNode('level1.2', { - _type: 'server', - _id: 11, - server_type: 'pg', - version: 90600, - }, undefined, ['level1']); - ppasServerTreeNode = pgBrowser.tree.addNewNode('level1.3', { - _type: 'server', - server_type: 'ppas', - version: 130000, - }, undefined, ['level1']); - ppasServerTreeNodeWrongPath = pgBrowser.tree.addNewNode('level1.4', { - _type: 'server', - server_type: 'ppas', - version: 90600, - }, undefined, ['level1']); - pgBrowser.tree.addNewNode('level3', {}, undefined, ['level1', 'level1.2', 'level1.3', 'level1.4']); - pgBrowser.tree.addNewNode('level3.1', undefined, undefined, ['level1', 'level1.2', 'level3']); - }); - - describe('#draw', () => { - let networkMock; - beforeEach(() => { - networkMock = new MockAdapter(axios); - alertifySpy = jasmine.createSpyObj('alertify', ['alert', 'dialog']); - alertifySpy['BackupDialog_globals'] = jasmine.createSpy('BackupDialog_globals'); - alertifySpy['BackupDialog_server'] = jasmine.createSpy('BackupDialog_server'); - backupDialog = new BackupDialog( - pgBrowser, - jquerySpy, - alertifySpy, - backupModelSpy - ); - - pgBrowser.get_preference = jasmine.createSpy('get_preferences'); - }); - - afterEach(() => { - networkMock.restore(); - }); - - context('there are no ancestors of the type server', () => { - it('does not create a dialog', () => { - pgBrowser.tree.selectNode([{id: 'level1'}]); - backupDialog.draw(null, null, null); - expect(alertifySpy['BackupDialog_globals']).not.toHaveBeenCalled(); - expect(alertifySpy['BackupDialog_server']).not.toHaveBeenCalled(); - }); - - it('display an alert with a Backup Error', () => { - backupDialog.draw(null, [rootNode], null); - expect(alertifySpy.alert).toHaveBeenCalledWith( - 'Backup Error', - 'Please select server or child node from the browser tree.' - ); - }); - }); - - context('there is an ancestor of the type server', () => { - context('no preference can be found', () => { - beforeEach(() => { - pgBrowser.get_preference.and.returnValue(undefined); - }); - - context('server is a PostgreSQL server', () => { - it('display an alert with "Preferences Error"', () => { - backupDialog.draw(null, [serverTreeNode], null); - expect(alertifySpy.alert).toHaveBeenCalledWith( - 'Preferences Error', - 'Failed to load preference pg_bin_dir of module paths' - ); - }); - }); - - context('server is a EPAS server', () => { - it('display an alert with "Preferences Error"', () => { - backupDialog.draw(null, [ppasServerTreeNode], null); - expect(alertifySpy.alert).toHaveBeenCalledWith( - 'Preferences Error', - 'Failed to load preference ppas_bin_dir of module paths' - ); - }); - }); - }); - - context('preference can be found for PostgreSQL Server', () => { - context('binary folder is not configured', () => { - beforeEach(() => { - pgBrowser.get_preference.and.returnValue({value: '[{\"serverType\":\"PostgreSQL 9.6\",\"binaryPath\":null,\"isDefault\":false,\"version\":\"90600\",\"next_major_version\":\"100000\"},{\"serverType\":\"PostgreSQL 10\",\"binaryPath\":\"/Library/PostgreSQL/10/bin\",\"isDefault\":false,\"version\":\"100000\",\"next_major_version\":\"110000\"},{\"serverType\":\"PostgreSQL 11\",\"binaryPath\":\"/Library/PostgreSQL/11/bin\",\"isDefault\":false,\"version\":\"110000\",\"next_major_version\":\"120000\"},{\"serverType\":\"PostgreSQL 12\",\"binaryPath\":\"/Library/PostgreSQL/12/bin\",\"isDefault\":false,\"version\":\"120000\",\"next_major_version\":\"130000\"},{\"serverType\":\"PostgreSQL 13\",\"binaryPath\":null,\"isDefault\":false,\"version\":\"130000\",\"next_major_version\":\"140000\"}]'}); - }); - - context('server is a PostgreSQL server', () => { - it('display an alert with "Configuration required"', () => { - backupDialog.draw(null, [serverTreeNodeWrongPath], null); - expect(alertifySpy.alert).toHaveBeenCalledWith( - 'Configuration required', - 'Please configure the PostgreSQL Binary Path in the Preferences dialog.' - ); - }); - }); - }); - - context('binary folder is configured', () => { - let globalResizeToSpy; - let serverResizeToSpy; - beforeEach(() => { - globalResizeToSpy = jasmine.createSpyObj('globals', ['resizeTo']); - alertifySpy['BackupDialog_globals'].and - .returnValue(globalResizeToSpy); - serverResizeToSpy = jasmine.createSpyObj('server', ['resizeTo']); - alertifySpy['BackupDialog_server'].and - .returnValue(serverResizeToSpy); - pgBrowser.get_preference.and.returnValue({value: '[{\"serverType\":\"PostgreSQL 9.6\",\"binaryPath\":null,\"isDefault\":false,\"version\":\"90600\",\"next_major_version\":\"100000\"},{\"serverType\":\"PostgreSQL 10\",\"binaryPath\":\"/Library/PostgreSQL/10/bin\",\"isDefault\":true,\"version\":\"100000\",\"next_major_version\":\"110000\"},{\"serverType\":\"PostgreSQL 11\",\"binaryPath\":\"/Library/PostgreSQL/11/bin\",\"isDefault\":false,\"version\":\"110000\",\"next_major_version\":\"120000\"},{\"serverType\":\"PostgreSQL 12\",\"binaryPath\":\"/Library/PostgreSQL/12/bin\",\"isDefault\":false,\"version\":\"120000\",\"next_major_version\":\"130000\"},{\"serverType\":\"PostgreSQL 13\",\"binaryPath\":null,\"isDefault\":false,\"version\":\"130000\",\"next_major_version\":\"140000\"}]'}); - spyOn(backupDialog, 'url_for_utility_exists').and.returnValue('/backup/utility_exists/10/servers'); - networkMock.onGet('/backup/utility_exists/10/servers').reply(200, {'success': 1}); - }); - - context('dialog for global backup ', () => { - it('displays the dialog when binary path is for correct server version', (done) => { - backupDialog.draw(null, [serverTreeNode], {globals: true}, pgBrowser.stdW.md, pgBrowser.stdH.md); - setTimeout(() => { - expect(alertifySpy['BackupDialog_globals']).toHaveBeenCalledWith(true); - expect(globalResizeToSpy.resizeTo).toHaveBeenCalledWith(pgBrowser.stdW.md, pgBrowser.stdH.md); - done(); - }, 0); - }); - }); - - context('dialog for server backup', () => { - it('displays the dialog when binary path is for correct server version', (done) => { - backupDialog.draw(null, [serverTreeNode], {server: true}, pgBrowser.stdW.md, pgBrowser.stdH.md); - setTimeout(() => { - expect(alertifySpy['BackupDialog_server']).toHaveBeenCalledWith(true); - expect(serverResizeToSpy.resizeTo).toHaveBeenCalledWith(pgBrowser.stdW.md, pgBrowser.stdH.md); - done(); - }, 0); - }); - }); - - context('dialog for global backup ', () => { - it('displays the dialog when default binary path is specified', (done) => { - backupDialog.draw(null, [serverTreeNodeWrongPath], {globals: true}, pgBrowser.stdW.md, pgBrowser.stdH.md); - setTimeout(() => { - expect(alertifySpy['BackupDialog_globals']).toHaveBeenCalledWith(true); - expect(globalResizeToSpy.resizeTo).toHaveBeenCalledWith(pgBrowser.stdW.md, pgBrowser.stdH.md); - done(); - }, 0); - }); - }); - - context('dialog for server backup', () => { - it('displays the dialog when default binary path is specified', (done) => { - backupDialog.draw(null, [serverTreeNodeWrongPath], {server: true}, pgBrowser.stdW.md, pgBrowser.stdH.md); - setTimeout(() => { - expect(alertifySpy['BackupDialog_server']).toHaveBeenCalledWith(true); - expect(serverResizeToSpy.resizeTo).toHaveBeenCalledWith(pgBrowser.stdW.md, pgBrowser.stdH.md); - done(); - }, 0); - }); - }); - }); - }); - context('preference can be found for EPAS Server', () => { - context('binary folder is not configured', () => { - beforeEach(() => { - pgBrowser.get_preference.and.returnValue({value: '[{\"serverType\":\"EDB Advanced Server 9.6\",\"binaryPath\":\"\",\"isDefault\":false,\"version\":\"90600\",\"next_major_version\":\"100000\"},{\"serverType\":\"EDB Advanced Server 10\",\"binaryPath\":null,\"isDefault\":false,\"version\":\"100000\",\"next_major_version\":\"110000\"},{\"serverType\":\"EDB Advanced Server 11\",\"binaryPath\":\"/Library/EPAS/11/bin/\",\"isDefault\":false,\"version\":\"110000\",\"next_major_version\":\"120000\"},{\"serverType\":\"EDB Advanced Server 12\",\"binaryPath\":null,\"isDefault\":false,\"version\":\"120000\",\"next_major_version\":\"130000\"},{\"serverType\":\"EDB Advanced Server 13\",\"binaryPath\":\"/Library/EPAS/13/bin/\",\"isDefault\":false,\"version\":\"130000\",\"next_major_version\":\"140000\"}]'}); - }); - - context('server is a EPAS server', () => { - it('display an alert with "Configuration required"', () => { - backupDialog.draw(null, [ppasServerTreeNodeWrongPath], null); - expect(alertifySpy.alert).toHaveBeenCalledWith( - 'Configuration required', - 'Please configure the EDB Advanced Server Binary Path in the Preferences dialog.' - ); - }); - }); - }); - - context('binary folder is configured', () => { - let globalResizeToSpy; - let serverResizeToSpy; - beforeEach(() => { - globalResizeToSpy = jasmine.createSpyObj('globals', ['resizeTo']); - alertifySpy['BackupDialog_globals'].and - .returnValue(globalResizeToSpy); - serverResizeToSpy = jasmine.createSpyObj('server', ['resizeTo']); - alertifySpy['BackupDialog_server'].and - .returnValue(serverResizeToSpy); - pgBrowser.get_preference.and.returnValue({value: '[{\"serverType\":\"EDB Advanced Server 9.6\",\"binaryPath\":\"\",\"isDefault\":false,\"version\":\"90600\",\"next_major_version\":\"100000\"},{\"serverType\":\"EDB Advanced Server 10\",\"binaryPath\":null,\"isDefault\":false,\"version\":\"100000\",\"next_major_version\":\"110000\"},{\"serverType\":\"EDB Advanced Server 11\",\"binaryPath\":\"/Library/EPAS/11/bin/\",\"isDefault\":false,\"version\":\"110000\",\"next_major_version\":\"120000\"},{\"serverType\":\"EDB Advanced Server 12\",\"binaryPath\":null,\"isDefault\":false,\"version\":\"120000\",\"next_major_version\":\"130000\"},{\"serverType\":\"EDB Advanced Server 13\",\"binaryPath\":\"/Library/EPAS/13/bin/\",\"isDefault\":true,\"version\":\"130000\",\"next_major_version\":\"140000\"}]'}); - spyOn(backupDialog, 'url_for_utility_exists').and.returnValue('/backup/utility_exists/10/servers'); - networkMock.onGet('/backup/utility_exists/10/servers').reply(200, {'success': 1}); - }); - - context('dialog for global backup ', () => { - it('displays the dialog when binary path is for correct server version', (done) => { - backupDialog.draw(null, [ppasServerTreeNode], {globals: true}, pgBrowser.stdW.md, pgBrowser.stdH.md); - setTimeout(() => { - expect(alertifySpy['BackupDialog_globals']).toHaveBeenCalledWith(true); - expect(globalResizeToSpy.resizeTo).toHaveBeenCalledWith(pgBrowser.stdW.md, pgBrowser.stdH.md); - done(); - }, 0); - }); - }); - - context('dialog for server backup', () => { - it('displays the dialog when binary path is for correct server version', (done) => { - backupDialog.draw(null, [ppasServerTreeNode], {server: true}, pgBrowser.stdW.md, pgBrowser.stdH.md); - setTimeout(() => { - expect(alertifySpy['BackupDialog_server']).toHaveBeenCalledWith(true); - expect(serverResizeToSpy.resizeTo).toHaveBeenCalledWith(pgBrowser.stdW.md, pgBrowser.stdH.md); - done(); - }, 0); - }); - }); - - context('dialog for global backup ', () => { - it('displays the dialog when default binary path is specified', (done) => { - backupDialog.draw(null, [ppasServerTreeNodeWrongPath], {globals: true}, pgBrowser.stdW.md, pgBrowser.stdH.md); - setTimeout(() => { - expect(alertifySpy['BackupDialog_globals']).toHaveBeenCalledWith(true); - expect(globalResizeToSpy.resizeTo).toHaveBeenCalledWith(pgBrowser.stdW.md, pgBrowser.stdH.md); - done(); - }, 0); - }); - }); - - context('dialog for server backup', () => { - it('displays the dialog when default binary path is specified', (done) => { - backupDialog.draw(null, [ppasServerTreeNodeWrongPath], {server: true}, pgBrowser.stdW.md, pgBrowser.stdH.md); - setTimeout(() => { - expect(alertifySpy['BackupDialog_server']).toHaveBeenCalledWith(true); - expect(serverResizeToSpy.resizeTo).toHaveBeenCalledWith(pgBrowser.stdW.md, pgBrowser.stdH.md); - done(); - }, 0); - }); - }); - }); - }); - }); - }); -}); diff --git a/web/regression/javascript/schema_ui_files/backup.ui.spec.js b/web/regression/javascript/schema_ui_files/backup.ui.spec.js new file mode 100644 index 000000000..b6e904282 --- /dev/null +++ b/web/regression/javascript/schema_ui_files/backup.ui.spec.js @@ -0,0 +1,96 @@ +///////////////////////////////////////////////////////////// +// +// pgAdmin 4 - PostgreSQL Tools +// +// Copyright (C) 2013 - 2021, The pgAdmin Development Team +// This software is released under the PostgreSQL Licence +// +////////////////////////////////////////////////////////////// + +import React from 'react'; +import '../helper/enzyme.helper'; +import { createMount } from '@material-ui/core/test-utils'; +import pgAdmin from 'sources/pgadmin'; +import SchemaView from '../../../pgadmin/static/js/SchemaView'; +import BackupSchema, {getSectionSchema, getTypeObjSchema, getSaveOptSchema, getQueryOptionSchema, getDisabledOptionSchema, getMiscellaneousSchema} from '../../../pgadmin/tools/backup/static/js/backup.ui'; + + +describe('BackupSchema', ()=>{ + let mount; + beforeAll(()=>{ + mount = createMount(); + }); + + afterAll(() => { + mount.cleanUp(); + }); + let backupSchemaObj = new BackupSchema( + ()=> getSectionSchema(), + ()=> getTypeObjSchema(), + ()=> getSaveOptSchema({nodeInfo: {server: {version: 11000}}}), + ()=> getQueryOptionSchema({nodeInfo: {server: {version: 11000}}}), + ()=> getDisabledOptionSchema({nodeInfo: {server: {version: 11000}}}), + ()=> getMiscellaneousSchema({nodeInfo: {server: {version: 11000}}}), + { + role: ()=>[], + encoding: ()=>[], + }, + {server: {version: 11000}}, + pgAdmin.pgBrowser, + 'backup_objects' + ); + + it('create object backup', ()=>{ + mount({}} + onClose={()=>{}} + onHelp={()=>{}} + onDataChange={()=>{}} + confirmOnCloseReset={false} + hasSQL={false} + disableSqlHelp={false} + disableDialogHelp={false} + />); + }); + + + let backupServerSchemaObj = new BackupSchema( + ()=> getSectionSchema(), + ()=> getTypeObjSchema(), + ()=> getSaveOptSchema({nodeInfo: {server: {version: 11000}}}), + ()=> getQueryOptionSchema({nodeInfo: {server: {version: 11000}}}), + ()=> getDisabledOptionSchema({nodeInfo: {server: {version: 11000}}}), + ()=> getMiscellaneousSchema({nodeInfo: {server: {version: 11000}}}), + { + role: ()=>[], + encoding: ()=>[], + }, + {server: {version: 11000}}, + {serverInfo: {}}, + 'server' + ); + + it('create server backup', ()=>{ + mount({}} + onClose={()=>{}} + onHelp={()=>{}} + onDataChange={()=>{}} + confirmOnCloseReset={false} + hasSQL={false} + disableSqlHelp={false} + disableDialogHelp={false} + />); + }); +}); + diff --git a/web/regression/javascript/schema_ui_files/backupGlobal.ui.spec.js b/web/regression/javascript/schema_ui_files/backupGlobal.ui.spec.js new file mode 100644 index 000000000..7fa9088d6 --- /dev/null +++ b/web/regression/javascript/schema_ui_files/backupGlobal.ui.spec.js @@ -0,0 +1,51 @@ +///////////////////////////////////////////////////////////// +// +// pgAdmin 4 - PostgreSQL Tools +// +// Copyright (C) 2013 - 2021, The pgAdmin Development Team +// This software is released under the PostgreSQL Licence +// +////////////////////////////////////////////////////////////// + +import React from 'react'; +import '../helper/enzyme.helper'; +import { createMount } from '@material-ui/core/test-utils'; +import SchemaView from '../../../pgadmin/static/js/SchemaView'; +import BackupGlobalSchema, {getMiscellaneousSchema} from '../../../pgadmin/tools/backup/static/js/backupGlobal.ui'; + + +describe('BackupGlobalSchema', ()=>{ + let mount; + beforeAll(()=>{ + mount = createMount(); + }); + + afterAll(() => { + mount.cleanUp(); + }); + let backupGlobalSchemaObj = new BackupGlobalSchema( + ()=> getMiscellaneousSchema(), + { + role: ()=>[], + } + ); + + it('create', ()=>{ + mount({}} + onClose={()=>{}} + onHelp={()=>{}} + onDataChange={()=>{}} + confirmOnCloseReset={false} + hasSQL={false} + disableSqlHelp={false} + disableDialogHelp={false} + />); + }); +}); +