From c474ca2c5f4c540c505b996f67e1a7bf48a7c7b7 Mon Sep 17 00:00:00 2001 From: Pradip Parkale Date: Wed, 21 Jul 2021 17:00:36 +0530 Subject: [PATCH] Port Publications node to react. Fixes #6610 --- .../databases/publications/__init__.py | 40 ----- .../publications/static/js/publication.js | 134 ++------------- .../publications/static/js/publication.ui.js | 157 ++++++++++++++++++ .../publications/sql/13_plus/update.sql | 2 +- .../publications/sql/default/update.sql | 2 +- web/pgadmin/static/js/SchemaView/index.jsx | 43 +++-- .../schema_ui_files/publication.ui.spec.js | 119 +++++++++++++ 7 files changed, 323 insertions(+), 174 deletions(-) create mode 100644 web/pgadmin/browser/server_groups/servers/databases/publications/static/js/publication.ui.js create mode 100644 web/regression/javascript/schema_ui_files/publication.ui.spec.js diff --git a/web/pgadmin/browser/server_groups/servers/databases/publications/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/publications/__init__.py index e0d0c0477..d395195d7 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/publications/__init__.py +++ b/web/pgadmin/browser/server_groups/servers/databases/publications/__init__.py @@ -236,20 +236,6 @@ class PublicationView(PGChildNodeView, SchemaDiffObjectCompare): return wrap - @staticmethod - def _parser_data_input_from_client(data): - """ - - :param data: - :return: data - """ - - if 'pubtable' in data and data['pubtable'] != '': - data['pubtable'] = json.loads( - data['pubtable'], encoding='utf-8' - ) - return data - @check_precondition def list(self, gid, sid, did): """ @@ -438,10 +424,6 @@ class PublicationView(PGChildNodeView, SchemaDiffObjectCompare): ) try: - data = self._parser_data_input_from_client(data) - - # unquote the table data - data = self.unquote_the_table(data) sql, name = self.get_sql(data, pbid) @@ -464,22 +446,6 @@ class PublicationView(PGChildNodeView, SchemaDiffObjectCompare): except Exception as e: return internal_server_error(errormsg=str(e)) - def unquote_the_table(self, data): - """ - This function unquote the table value - :param data: - :return: data - """ - pubtable = [] - - # Unquote the values - if 'pubtable' in data: - for table in data['pubtable']: - pubtable.append(unquote(table)) - data['pubtable'] = pubtable - - return data - @check_precondition def create(self, gid, sid, did): """ @@ -508,10 +474,6 @@ class PublicationView(PGChildNodeView, SchemaDiffObjectCompare): ) try: - data = self._parser_data_input_from_client(data) - - # unquote the table data - data = self.unquote_the_table(data) sql = render_template("/".join([self.template_path, self._CREATE_SQL]), @@ -621,8 +583,6 @@ class PublicationView(PGChildNodeView, SchemaDiffObjectCompare): except ValueError: data[k] = v try: - # unquote the table data - data = self.unquote_the_table(data) sql, name = self.get_sql(data, pbid) # Most probably this is due to error diff --git a/web/pgadmin/browser/server_groups/servers/databases/publications/static/js/publication.js b/web/pgadmin/browser/server_groups/servers/databases/publications/static/js/publication.js index 4fe459717..30b2705a9 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/publications/static/js/publication.js +++ b/web/pgadmin/browser/server_groups/servers/databases/publications/static/js/publication.js @@ -7,6 +7,9 @@ // ////////////////////////////////////////////////////////////// +import { getNodeAjaxOptions, getNodeListByName } from '../../../../../../static/js/node_ajax'; +import PublicationSchema from './publication.ui'; + define('pgadmin.node.publication', [ 'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'sources/pgadmin', 'pgadmin.browser', 'pgadmin.backform', @@ -70,18 +73,6 @@ define('pgadmin.node.publication', [ // Define the model for publication node model: pgBrowser.Node.Model.extend({ idAttribute: 'oid', - defaults: { - name: undefined, - pubowner: undefined, - pubtable: undefined, - all_table: undefined, - evnt_insert:true, - evnt_delete:true, - evnt_update:true, - evnt_truncate:true, - only_table: undefined, - publish_via_partition_root: false, - }, // Default values! initialize: function(attrs, args) { @@ -123,104 +114,12 @@ define('pgadmin.node.publication', [ group: gettext('Definition'), mode: ['edit', 'properties', 'create'], deps: ['name'], readonly: function(m) {return !m.isNew();}, }, - { - id: 'only_table', label: gettext('Only table?'), type: 'switch', - group: gettext('Definition'), mode: ['edit', 'create'], - deps: ['name', 'pubtable', 'all_table'], readonly: 'isTable', - helpMessage: gettext('If ONLY is specified before the table name, only that table is added to the publication. If ONLY is not specified, the table and all its descendant tables (if any) are added.'), - }, - { - id: 'pubtable', label: gettext('Tables'), type: 'array', - select2: { allowClear: true, multiple: true }, - control: 'node-ajax-options', url:'get_tables', - group: gettext('Definition'), mode: ['edit', 'create'], - deps: ['all_table'], disabled: 'isAllTable', - }, { id: 'pubtable', label: gettext('Tables'), type: 'text', group: gettext('Definition'), mode: ['properties'], }, - { - type: 'nested', control: 'fieldset', mode: ['create','edit', 'properties'], - label: gettext('With'), group: gettext('Definition'), contentClass: 'row', - schema:[{ - id: 'evnt_insert', label: gettext('INSERT'), - type: 'switch', mode: ['create','edit', 'properties'], - group: gettext('With'), - 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', - },{ - id: 'evnt_update', label: gettext('UPDATE'), - type: 'switch', mode: ['create','edit', 'properties'], - group: gettext('With'), - 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', - },{ - id: 'evnt_delete', label: gettext('DELETE'), - type: 'switch', mode: ['create','edit', 'properties'], - group: gettext('With'), - 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', - },{ - id: 'evnt_truncate', label: gettext('TRUNCATE'), - type: 'switch', group: gettext('With'), - 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', - visible: function(m) { - if(!_.isUndefined(m.node_info) && !_.isUndefined(m.node_info.server) - && !_.isUndefined(m.node_info.server.version) && - m.node_info.server.version >= 110000) - return true; - return false; - }, - - },{ - id: 'publish_via_partition_root', label: gettext('Publish via root?'), - type: 'switch', group: gettext('With'), - 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', - visible: function(m) { - if(!_.isUndefined(m.node_info) && !_.isUndefined(m.node_info.server) - && !_.isUndefined(m.node_info.server.version) && - m.node_info.server.version >= 130000) - return true; - return false; - }, - - }], - }, ], - isAllTable: function(m) { - var all_table = m.get('all_table'); - if(all_table){ - setTimeout( function() { - m.set('pubtable', ''); - }, 10); - return true; - } - return false; - }, - isTable: function(m) { - var all_table = m.get('all_table'), - table = m.get('pubtable'); - if(all_table){ - setTimeout( function() { - m.set('only_table', false); - }, 10); - return true; - } - - if (!_.isUndefined(table) && table.length > 0 && m._changing && !_.isEqual(m.origSessAttrs['pubtable'], m.changed['pubtable']) && 'pubtable' in m.changed){ - return false; - } - return true; - }, /* validate function is used to validate the input given by * the user. In case of error, message will be displayed on @@ -233,20 +132,6 @@ define('pgadmin.node.publication', [ return pgBrowser.DataModel.prototype.sessChanged.apply(this); }, - validate: function() { - var name = this.get('name'), - msg; - - if (_.isUndefined(name) || _.isNull(name) || - String(name).replace(/^\s+|\s+$/g, '') == '') { - msg = gettext('Name cannot be empty.'); - this.errorModel.set('name', msg); - return msg; - } else { - this.errorModel.unset('name'); - } - return null; - }, canCreate: function(itemData, item) { var treeData = this.getTreeNodeHierarchy(item), @@ -261,6 +146,19 @@ define('pgadmin.node.publication', [ }, }), + + getSchema: function(treeNodeInfo, itemNodeData){ + let schema = new PublicationSchema( + { + publicationTable: ()=>getNodeAjaxOptions('get_tables', this, treeNodeInfo, itemNodeData), + role:()=>getNodeListByName('role', treeNodeInfo, itemNodeData), + },{ + node_info: treeNodeInfo.server, + }, + ); + return schema; + }, + }); } return pgBrowser.Nodes['coll-publication']; diff --git a/web/pgadmin/browser/server_groups/servers/databases/publications/static/js/publication.ui.js b/web/pgadmin/browser/server_groups/servers/databases/publications/static/js/publication.ui.js new file mode 100644 index 000000000..51cf9b169 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/publications/static/js/publication.ui.js @@ -0,0 +1,157 @@ +///////////////////////////////////////////////////////////// +// +// pgAdmin 4 - PostgreSQL Tools +// +// Copyright (C) 2013 - 2021, The pgAdmin Development Team +// This software is released under the PostgreSQL Licence +// +////////////////////////////////////////////////////////////// +import gettext from 'sources/gettext'; +import BaseUISchema from 'sources/SchemaView/base_schema.ui'; + + +export class DefaultWithSchema extends BaseUISchema { + constructor(node_info) { + super(); + this.node_info = node_info; + } + + get baseFields() { + return[{ + id: 'evnt_insert', label: gettext('INSERT'), + type: 'switch', mode: ['create','edit', 'properties'], + group: gettext('With'), + },{ + id: 'evnt_update', label: gettext('UPDATE'), + type: 'switch', mode: ['create','edit', 'properties'], + group: gettext('With'), + },{ + id: 'evnt_delete', label: gettext('DELETE'), + type: 'switch', mode: ['create','edit', 'properties'], + group: gettext('With'), + },{ + id: 'evnt_truncate', label: gettext('TRUNCATE'), + type: 'switch', group: gettext('With'), + visible: function() { + if(!_.isUndefined(this.node_info['node_info']) && !_.isUndefined(this.node_info['node_info']) + && !_.isUndefined(this.node_info['node_info'].version) && + this.node_info['node_info'].version >= 110000) + return true; + return false; + }, + + },{ + id: 'publish_via_partition_root', label: gettext('Publish via root?'), + type: 'switch', group: gettext('With'), + visible: function() { + if(!_.isUndefined(this.node_info['node_info']) && !_.isUndefined(this.node_info['node_info']) + && !_.isUndefined(this.node_info['node_info'].version) && + this.node_info['node_info'].version >= 130000) + return true; + return false; + }, + }]; + } +} + +export default class PublicationSchema extends BaseUISchema { + constructor(fieldOptions={}, node_info, initValues) { + super({ + name: undefined, + pubowner: (node_info) ? node_info['node_info'].user.name: undefined, + pubtable: undefined, + all_table: undefined, + evnt_insert:true, + evnt_delete:true, + evnt_update:true, + evnt_truncate:true, + only_table: undefined, + publish_via_partition_root: false, + ...initValues, + }); + + this.fieldOptions = { + role: [], + publicationTable: [], + ...fieldOptions, + }; + this.node_info = node_info; + } + get idAttribute() { + return 'oid'; + } + + isAllTable(state) { + var allTable = state.all_table; + if(allTable){ + state.pubtable = ''; + return true; + } + return false; + } + + isTable(state) { + var allTable = state.all_table, + table = state.pubtable; + if(allTable){ + state.only_table = false; + return true; + } + if (!_.isUndefined(table) && table.length > 0){ + return false; + } + return true; + } + + get baseFields() { + let obj = this; + return [{ + id: 'name', label: gettext('Name'), type: 'text', + mode: ['properties', 'create', 'edit'], noEmpty: true, + visible: function() { + if(!_.isUndefined(this.node_info['node_info']) + && !_.isUndefined(this.node_info['node_info'].version) + && this.node_info['node_info'].version >= 100000) { + return true; + } + return false; + }, + },{ + id: 'oid', label: gettext('OID'), cell: 'string', mode: ['properties'], + type: 'text', + },{ + id: 'pubowner', label: gettext('Owner'), type: 'select', + options: this.fieldOptions.role, + disabled: () => { + if (obj.isNew()) + return true; + return false; + }, + mode: ['edit', 'properties', 'create'], controlProps: { allowClear: false}, + },{ + id: 'all_table', label: gettext('All tables?'), type: 'switch', + group: gettext('Definition'), mode: ['edit', 'properties', 'create'], deps: ['name'], + readonly: (state) => {return !obj.isNew(state);}, + },{ + id: 'only_table', label: gettext('Only table?'), type: 'switch', + group: gettext('Definition'), mode: ['edit', 'create'], + deps: ['name', 'pubtable', 'all_table'], readonly: obj.isTable, + helpMessage: gettext('If ONLY is specified before the table name, only that table is added to the publication. If ONLY is not specified, the table and all its descendant tables (if any) are added.'), + },{ + id: 'pubtable', label: gettext('Tables'), type: 'select', + controlProps: { allowClear: true, multiple: true, creatable: true }, + options: this.fieldOptions.publicationTable, + group: gettext('Definition'), mode: ['edit', 'create'], + deps: ['all_table'], disabled: obj.isAllTable, + },{ + id: 'pubtable', label: gettext('Tables'), type: 'text', group: gettext('Definition'), + mode: ['properties'], + },{ + type: 'nested-fieldset', mode: ['create','edit', 'properties'], + label: gettext('With'), group: gettext('Definition'), + schema : new DefaultWithSchema(this.node_info), + }, + ]; + } +} + diff --git a/web/pgadmin/browser/server_groups/servers/databases/publications/templates/publications/sql/13_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/publications/templates/publications/sql/13_plus/update.sql index 58542ea4b..4995d92b7 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/publications/templates/publications/sql/13_plus/update.sql +++ b/web/pgadmin/browser/server_groups/servers/databases/publications/templates/publications/sql/13_plus/update.sql @@ -10,7 +10,7 @@ {### Alter publication owner ###} {% if data.pubowner %} ALTER PUBLICATION {{ conn|qtIdent(o_data.name) }} - OWNER TO {{ data.pubowner }}; + OWNER TO {{ conn|qtIdent(data.pubowner) }}; {% endif %} {### Alter publication event ###} diff --git a/web/pgadmin/browser/server_groups/servers/databases/publications/templates/publications/sql/default/update.sql b/web/pgadmin/browser/server_groups/servers/databases/publications/templates/publications/sql/default/update.sql index 5966191ed..0593ec409 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/publications/templates/publications/sql/default/update.sql +++ b/web/pgadmin/browser/server_groups/servers/databases/publications/templates/publications/sql/default/update.sql @@ -10,7 +10,7 @@ {### Alter publication owner ###} {% if data.pubowner %} ALTER PUBLICATION {{ conn|qtIdent(o_data.name) }} - OWNER TO {{ data.pubowner }}; + OWNER TO {{ conn|qtIdent(data.pubowner) }}; {% endif %} {### Alter publication event ###} diff --git a/web/pgadmin/static/js/SchemaView/index.jsx b/web/pgadmin/static/js/SchemaView/index.jsx index 43ddb986c..246c36a29 100644 --- a/web/pgadmin/static/js/SchemaView/index.jsx +++ b/web/pgadmin/static/js/SchemaView/index.jsx @@ -35,6 +35,7 @@ import PropTypes from 'prop-types'; import CustomPropTypes from '../custom_prop_types'; import { parseApiError } from '../api_instance'; import DepListener, {DepListenerContext} from './DepListener'; +import FieldSetView from './FieldSetView'; const useDialogStyles = makeStyles((theme)=>({ root: { @@ -665,20 +666,34 @@ function SchemaPropertiesView({ readonly = true; if(_visible && verInLimit) { if(!tabs[group]) tabs[group] = []; - tabs[group].push( - - ); + if(field && field.type === 'nested-fieldset') { + tabs[group].push( + + ); + }else{ + tabs[group].push( + + ); + } } }); diff --git a/web/regression/javascript/schema_ui_files/publication.ui.spec.js b/web/regression/javascript/schema_ui_files/publication.ui.spec.js new file mode 100644 index 000000000..7071979b4 --- /dev/null +++ b/web/regression/javascript/schema_ui_files/publication.ui.spec.js @@ -0,0 +1,119 @@ +///////////////////////////////////////////////////////////// +// +// pgAdmin 4 - PostgreSQL Tools +// +// Copyright (C) 2013 - 2021, The pgAdmin Development Team +// This software is released under the PostgreSQL Licence +// +////////////////////////////////////////////////////////////// + +import jasmineEnzyme from 'jasmine-enzyme'; +import React from 'react'; +import '../helper/enzyme.helper'; +import { createMount } from '@material-ui/core/test-utils'; +import pgAdmin from 'sources/pgadmin'; +import {messages} from '../fake_messages'; +import SchemaView from '../../../pgadmin/static/js/SchemaView'; +import PublicationSchema from '../../../pgadmin/browser/server_groups/servers/databases/publications/static/js/publication.ui'; + + + +describe('PublicationSchema', ()=>{ + let mount; + let schemaObj = new PublicationSchema( + { + publicationTable: ()=>[], + role: ()=>[], + }, + { + node_info: { + connected: true, + user: {id: 10, name: 'postgres', is_superuser: true, can_create_role: true, can_create_db: true}, + user_id: 1, + user_name: 'postgres', + version: 130005, + }, + }, + ); + let getInitData = ()=>Promise.resolve({}); + + /* Use createMount so that material ui components gets the required context */ + /* https://material-ui.com/guides/testing/#api */ + beforeAll(()=>{ + mount = createMount(); + }); + + afterAll(() => { + mount.cleanUp(); + }); + + beforeEach(()=>{ + jasmineEnzyme(); + /* messages used by validators */ + pgAdmin.Browser = pgAdmin.Browser || {}; + pgAdmin.Browser.messages = pgAdmin.Browser.messages || messages; + pgAdmin.Browser.utils = pgAdmin.Browser.utils || {}; + }); + + it('create', ()=>{ + mount({}} + onClose={()=>{}} + onHelp={()=>{}} + onEdit={()=>{}} + onDataChange={()=>{}} + confirmOnCloseReset={false} + hasSQL={false} + disableSqlHelp={false} + />); + }); + + it('edit', ()=>{ + mount({}} + onClose={()=>{}} + onHelp={()=>{}} + onEdit={()=>{}} + onDataChange={()=>{}} + confirmOnCloseReset={false} + hasSQL={false} + disableSqlHelp={false} + />); + }); + + it('properties', ()=>{ + mount({}} + onEdit={()=>{}} + />); + }); + + it('pubtable disabled', ()=>{ + let disabled = _.find(schemaObj.fields, (f)=>f.id=='pubtable').disabled; + disabled({all_table: true}); + }); + + it('only_table readonly', ()=>{ + let readonly = _.find(schemaObj.fields, (f)=>f.id=='only_table').readonly; + readonly({all_table: true}); + }); + +}); +