Port Publications node to react. Fixes #6610

This commit is contained in:
Pradip Parkale 2021-07-21 17:00:36 +05:30 committed by Akshay Joshi
parent be04bea2b6
commit c474ca2c5f
7 changed files with 323 additions and 174 deletions

View File

@ -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

View File

@ -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'];

View File

@ -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),
},
];
}
}

View File

@ -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 ###}

View File

@ -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 ###}

View File

@ -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(
<MappedFormControl
key={field.id}
viewHelperProps={viewHelperProps}
state={origData}
name={field.id}
value={origData[field.id]}
readonly={readonly}
disabled={disabled}
visible={_visible}
{...field}
className={classes.controlRow}
/>
);
if(field && field.type === 'nested-fieldset') {
tabs[group].push(
<FieldSetView
key={`nested${tabs[group].length}`}
value={origData}
viewHelperProps={viewHelperProps}
schema={field.schema}
accessPath={[]}
formErr={{}}
controlClassName={classes.controlRow}
{...field} />
);
}else{
tabs[group].push(
<MappedFormControl
key={field.id}
viewHelperProps={viewHelperProps}
state={origData}
name={field.id}
value={origData[field.id]}
readonly={readonly}
disabled={disabled}
visible={_visible}
{...field}
className={classes.controlRow}
/>
);
}
}
});

View File

@ -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(<SchemaView
formType='dialog'
schema={schemaObj}
viewHelperProps={{
mode: 'create',
}}
onSave={()=>{}}
onClose={()=>{}}
onHelp={()=>{}}
onEdit={()=>{}}
onDataChange={()=>{}}
confirmOnCloseReset={false}
hasSQL={false}
disableSqlHelp={false}
/>);
});
it('edit', ()=>{
mount(<SchemaView
formType='dialog'
schema={schemaObj}
getInitData={getInitData}
viewHelperProps={{
mode: 'create',
}}
onSave={()=>{}}
onClose={()=>{}}
onHelp={()=>{}}
onEdit={()=>{}}
onDataChange={()=>{}}
confirmOnCloseReset={false}
hasSQL={false}
disableSqlHelp={false}
/>);
});
it('properties', ()=>{
mount(<SchemaView
formType='tab'
schema={schemaObj}
getInitData={getInitData}
viewHelperProps={{
mode: 'properties',
}}
onHelp={()=>{}}
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});
});
});