Port Domain and Domain Constraints node to react. Fixes #6330

This commit is contained in:
Akshay Joshi
2021-07-23 18:19:10 +05:30
parent 438d591d5b
commit 6d18842dd3
9 changed files with 642 additions and 245 deletions

View File

@@ -7,6 +7,8 @@
//
//////////////////////////////////////////////////////////////
import DomainConstraintSchema from './domain_constraints.ui';
// Domain Constraint Module: Collection and Node
define('pgadmin.node.domain_constraints', [
'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
@@ -69,47 +71,19 @@ define('pgadmin.node.domain_constraints', [
},
canDrop: schemaChildTreeNode.isTreeItemOfChildOfSchema,
getSchema: function() {
return new DomainConstraintSchema();
},
model: pgAdmin.Browser.Node.Model.extend({
idAttribute: 'oid',
defaults: {
name: undefined,
oid: undefined,
description: undefined,
consrc: undefined,
connoinherit: undefined,
convalidated: true,
},
// Domain Constraint Schema
schema: [{
id: 'name', label: gettext('Name'), type:'text', cell:'string',
},{
id: 'oid', label: gettext('OID'), cell: 'string',
type: 'text' , mode: ['properties'],
},{
id: 'is_sys_obj', label: gettext('System domain constraint?'),
cell:'boolean', type: 'switch', mode: ['properties'],
},{
id: 'description', label: gettext('Comment'), type: 'multiline', cell:
'string', mode: ['properties', 'create', 'edit'], min_version: 90500,
},{
id: 'consrc', label: gettext('Check'), type: 'multiline', cel:
'string', group: gettext('Definition'), mode: ['properties',
'create', 'edit'], readonly: function(m) { return !m.isNew(); },
},{
id: 'connoinherit', label: gettext('No inherit?'), type:
'switch', cell: 'boolean', group: gettext('Definition'), mode:
['properties', 'create', 'edit'],
visible: false,
},{
id: 'convalidated', label: gettext('Validate?'), type: 'switch', cell:
'boolean', group: gettext('Definition'), min_version: 90200,
disabled: function(m) {
if (!m.isNew() && m.get('convalidated')) {
return true;
}
return false;
},
mode: ['properties', 'create', 'edit'],
}],
// Client Side Validation
validate: function() {

View File

@@ -0,0 +1,62 @@
/////////////////////////////////////////////////////////////
//
// 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 default class DomainConstraintSchema extends BaseUISchema {
constructor(initValues) {
super({
name: undefined,
oid: undefined,
description: undefined,
consrc: undefined,
convalidated: true,
...initValues,
});
}
get idAttribute() {
return 'oid';
}
get baseFields() {
let obj = this;
return [
{
id: 'name', label: gettext('Name'), type:'text', cell:'text',
noEmpty: true,
}, {
id: 'oid', label: gettext('OID'), cell: 'text',
type: 'text' , mode: ['properties'],
}, {
id: 'is_sys_obj', label: gettext('System domain constraint?'),
cell:'boolean', type: 'switch', mode: ['properties'],
}, {
id: 'description', label: gettext('Comment'), type: 'multiline', cell:
'text', mode: ['properties', 'create', 'edit'], min_version: 90500,
}, {
id: 'consrc', label: gettext('Check'), type: 'multiline',
group: gettext('Definition'), mode: ['properties', 'create', 'edit'],
readonly: function(state) {return !obj.isNew(state); },
noEmpty: true,
}, {
id: 'convalidated', label: gettext('Validate?'), type: 'switch',
cell:'boolean', group: gettext('Definition'), min_version: 90200,
mode: ['properties', 'create', 'edit'],
readonly: function(state) {
if (!obj.isNew(state) && obj._origData.convalidated) {
return true;
}
return false;
}
}
];
}
}

View File

@@ -7,6 +7,9 @@
//
//////////////////////////////////////////////////////////////
import { getNodeAjaxOptions, getNodeListByName } from '../../../../../../../static/js/node_ajax';
import DomainSchema from './domain.ui';
// Domain Module: Collection and Node.
define('pgadmin.node.domain', [
'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'backbone',
@@ -31,65 +34,6 @@ define('pgadmin.node.domain', [
});
}
// Constraint Model
var ConstraintModel = pgBrowser.Node.Model.extend({
idAttribute: 'conoid',
initialize: function(attrs) {
if (_.size(attrs) !== 0) {
this.convalidated_default = this.get('convalidated');
}
pgBrowser.Node.Model.prototype.initialize.apply(this, arguments);
},
defaults: {
conoid: undefined,
conname: undefined,
consrc: undefined,
convalidated: true,
},
convalidated_default: true,
schema: [{
id: 'conoid', type: 'text', cell: 'string', visible: false,
},{
id: 'conname', label: gettext('Name'), type: 'text', cell: 'string',
cellHeaderClasses: 'width_percent_40',
editable: function(m) {
if (_.isUndefined(m.isNew)) { return true; }
if (!m.isNew()) {
var server = this.get('node_info').server;
if (server.version < 90200) { return false;
}
}
return true;
},
},{
id: 'consrc', label: gettext('Check'), type: 'multiline',
cell: Backgrid.Extension.TextareaCell, group: gettext('Definition'),
cellHeaderClasses: 'width_percent_60', editable: function(m) {
return _.isUndefined(m.isNew) ? true : m.isNew();
},
},{
id: 'convalidated', label: gettext('Validate?'), type: 'switch', cell:
'boolean', group: gettext('Definition'),
editable: function(m) {
var server = this.get('node_info').server;
if (server.version < 90200) { return false;
}
if (_.isUndefined(m.isNew)) { return true; }
if (!m.isNew()) {
if(m.get('convalidated') && m.convalidated_default) {
return false;
}
return true;
}
return true;
},
}],
toJSON: Backbone.Model.prototype.toJSON,
validate: function() {
return null;
},
});
// Domain Node
if (!pgBrowser.Nodes['domain']) {
pgBrowser.Nodes['domain'] = schemaChild.SchemaChildNode.extend({
@@ -130,6 +74,31 @@ define('pgadmin.node.domain', [
]);
},
getSchema: function(treeNodeInfo, itemNodeData) {
return new DomainSchema(
{
role: ()=>getNodeListByName('role', treeNodeInfo, itemNodeData),
schema: ()=>getNodeListByName('schema', treeNodeInfo, itemNodeData, {
cacheLevel: 'database',
cacheNode: 'database'
}),
basetype: ()=>getNodeAjaxOptions('get_types', this, treeNodeInfo, itemNodeData, {
cacheNode: 'type'
}),
collation: ()=>getNodeAjaxOptions('get_collations', this, treeNodeInfo, itemNodeData, {
cacheLevel: 'database',
cacheNode: 'schema'
}),
},
{
owner: pgBrowser.serverInfo[treeNodeInfo.server._id].user.name,
schema: itemNodeData.label,
basensp: itemNodeData.label,
}
);
},
// Domain Node Model
model: pgBrowser.Node.Model.extend({
idAttribute: 'oid',
@@ -146,23 +115,6 @@ define('pgadmin.node.domain', [
}
pgBrowser.Node.Model.prototype.initialize.apply(this, arguments);
},
defaults: {
name: undefined,
oid: undefined,
owner: undefined,
basensp: undefined,
description: undefined,
basetype: undefined,
typlen: undefined,
precision: undefined,
typdefault: undefined,
typnotnull: undefined,
sysdomain: undefined,
collname: undefined,
constraints: [],
seclabels: [],
},
type_options: undefined,
// Domain Schema
schema: [{
id: 'name', label: gettext('Name'), cell: 'string',
@@ -173,141 +125,10 @@ define('pgadmin.node.domain', [
},{
id: 'owner', label: gettext('Owner'), cell: 'string', control: Backform.NodeListByNameControl,
node: 'role', type: 'text', mode: ['edit', 'create', 'properties'],
},{
id: 'basensp', label: gettext('Schema'), cell: 'node-list-by-name',
control: 'node-list-by-name', cache_level: 'database', type: 'text',
node: 'schema', mode: ['create', 'edit'],
},{
id: 'sysdomain', label: gettext('System domain?'), cell: 'boolean',
type: 'switch', mode: ['properties'],
},{
id: 'description', label: gettext('Comment'), cell: 'string',
type: 'multiline',
},{
id: 'basetype', label: gettext('Base type'), cell: 'string',
control: 'node-ajax-options', type: 'text', url: 'get_types',
mode:['properties', 'create', 'edit'], group: gettext('Definition'),
first_empty: true, cache_node: 'type',
readonly: function(m) {
return !m.isNew();
},
transform: function(d) {
this.model.type_options = d;
return d;
},
},{
id: 'typlen', label: gettext('Length'), cell: 'string',
type: 'text', group: gettext('Definition'), deps: ['basetype'],
readonly: function(m) {return !m.isNew();},
disabled: function(m) {
// We will store type from selected from combobox
var of_type = m.get('basetype');
if(m.type_options) {
// iterating over all the types
_.each(m.type_options, function(o) {
// if type from selected from combobox matches in options
if ( of_type == o.value ) {
// if length is allowed for selected type
if(o.length)
{
// set the values in model
m.set('is_tlength', true, {silent: true});
m.set('min_val', o.min_val, {silent: true});
m.set('max_val', o.max_val, {silent: true});
}
else
m.set('is_tlength', false, {silent: true});
}
});
!m.get('is_tlength') && setTimeout(function() {
if(m.get('typlen')) {
m.set('typlen', null);
}
},10);
}
return !m.get('is_tlength');
},
},{
id: 'precision', label: gettext('Precision'), cell: 'string',
type: 'text', group: gettext('Definition'), deps: ['basetype'],
readonly: function(m) {return !m.isNew();},
disabled: function(m) {
// We will store type from selected from combobox
var of_type = m.get('basetype');
if(m.type_options) {
// iterating over all the types
_.each(m.type_options, function(o) {
// if type from selected from combobox matches in options
if ( of_type == o.value ) {
// if precession is allowed for selected type
if(o.precision)
{
// set the values in model
m.set('is_precision', true, {silent: true});
m.set('min_val', o.min_val, {silent: true});
m.set('max_val', o.max_val, {silent: true});
}
else
m.set('is_precision', false, {silent: true});
}
});
!m.get('is_precision') && setTimeout(function() {
if(m.get('precision')) {
m.set('precision', null);
}
},10);
}
return !m.get('is_precision');
},
},{
id: 'typdefault', label: gettext('Default'), cell: 'string',
type: 'text', group: gettext('Definition'),
placeholder: gettext('Enter an expression or a value.'),
},{
id: 'typnotnull', label: gettext('Not NULL?'), cell: 'boolean',
type: 'switch', group: gettext('Definition'),
},{
id: 'collname', label: gettext('Collation'), cell: 'string',
control: 'node-ajax-options', type: 'text', url: 'get_collations',
group: gettext('Definition'), cache_level: 'database',
cache_node: 'schema', readonly: function(m) {
return !m.isNew();
},
},{
id: 'constraints', label: gettext('Constraints'), cell: 'string',
type: 'collection', group: gettext('Constraints'), mode: ['edit', 'create'],
model: ConstraintModel, canAdd: true, canDelete: true,
canEdit: false, columns: ['conname','consrc', 'convalidated'],
},
pgBrowser.SecurityGroupSchema,
{
id: 'seclabels', label: gettext('Security labels'),
model: pgBrowser.SecLabelModel, type: 'collection',
group: 'security', mode: ['edit', 'create'],
min_version: 90100, canAdd: true,
canEdit: false, canDelete: true,
control: 'unique-col-collection', uniqueCol : ['provider'],
}],
validate: function() { // Client Side Validation
var err = {},
errmsg;
if (_.isUndefined(this.get('name')) || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') {
err['name'] = gettext('Name cannot be empty.');
errmsg = err['name'];
}
if (_.isUndefined(this.get('basetype')) || String(this.get('basetype')).replace(/^\s+|\s+$/g, '') == '') {
err['basetype'] = gettext('Base Type cannot be empty.');
errmsg = errmsg || err['basetype'];
}
this.errorModel.clear().set(err);
return errmsg;
},
}),
});

View File

@@ -0,0 +1,227 @@
/////////////////////////////////////////////////////////////
//
// 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';
import SecLabelSchema from '../../../../../static/js/sec_label.ui';
import { isEmptyString } from 'sources/validators';
import _ from 'lodash';
export class DomainConstSchema extends BaseUISchema {
constructor() {
super({
conoid: undefined,
conname: undefined,
consrc: undefined,
convalidated: true,
});
}
get idAttribute() {
return 'conoid';
}
get baseFields() {
let obj = this;
return [
{
id: 'conname', label: gettext('Name'), cell: 'text', type: 'text',
}, {
id: 'consrc', label: gettext('Check'), cell: 'text', type: 'text',
editable: function(state) {return obj.isNew(state);},
}, {
id: 'convalidated', label: gettext('Validate?'), cell: 'checkbox',
type: 'checkbox',
readonly: function(state) {
let currCon = _.find(obj.top.origData.constraints, (con)=>con.conoid == state.conoid);
if (!obj.isNew(state) && currCon.convalidated) {
return true;
}
return false;
},
}
];
}
validate(state, setError) {
if (isEmptyString(state.conname)) {
setError('conname', 'Constraint Name cannot be empty.');
return true;
} else {
setError('conname', null);
}
if (isEmptyString(state.consrc)) {
setError('consrc', 'Constraint Check cannot be empty.');
return true;
} else {
setError('consrc', null);
}
}
}
export default class DomainSchema extends BaseUISchema {
constructor(fieldOptions={}, initValues) {
super({
name: undefined,
oid: undefined,
owner: undefined,
basensp: undefined,
description: undefined,
basetype: undefined,
typlen: undefined,
precision: undefined,
typdefault: undefined,
typnotnull: undefined,
sysdomain: undefined,
collname: undefined,
constraints: [],
seclabels: [],
...initValues,
});
this.fieldOptions = {
role: [],
schema: [],
basetype: [],
collation: [],
...fieldOptions,
};
}
get idAttribute() {
return 'oid';
}
get baseFields() {
let obj = this;
return [
{
id: 'name', label: gettext('Name'), cell: 'text',
type: 'text', mode: ['properties', 'create', 'edit'],
noEmpty: true,
}, {
id: 'oid', label: gettext('OID'), cell: 'text',
type: 'text' , mode: ['properties'],
}, {
id: 'owner', label: gettext('Owner'),
editable: false, type: 'select', options: this.fieldOptions.role,
controlProps: { allowClear: false },
}, {
id: 'basensp', label: gettext('Schema'),
editable: false, type: 'select', options: this.fieldOptions.schema,
controlProps: { allowClear: false },
mode: ['create', 'edit'],
}, {
id: 'sysdomain', label: gettext('System domain?'), cell: 'boolean',
type: 'switch', mode: ['properties'],
}, {
id: 'description', label: gettext('Comment'), cell: 'text',
type: 'multiline',
}, {
id: 'basetype', label: gettext('Base type'),
type: 'select', options: this.fieldOptions.basetype,
optionsLoaded: (options) => { obj.type_options = options; },
mode:['properties', 'create', 'edit'], group: gettext('Definition'),
readonly: function(state) {return !obj.isNew(state);}, noEmpty: true,
}, {
id: 'typlen', label: gettext('Length'), cell: 'text',
type: 'text', group: gettext('Definition'), deps: ['basetype'],
readonly: function(state) {return !obj.isNew(state);},
disabled: function(state) {
// We will store type from selected from combobox
var of_type = state.basetype;
if(obj.type_options) {
// iterating over all the types
_.each(obj.type_options, function(o) {
// if type from selected from combobox matches in options
if ( of_type == o.value ) {
// if length is allowed for selected type
if(o.length) {
// set the values in model
state.is_tlength = true;
state.min_val = o.min_val;
state.max_val = o.max_val;
}
else
state.is_tlength = false;
}
});
if(!state.is_tlength) {
if(state.typlen) {
state.typlen = null;
}
}
}
return !state.is_tlength;
},
}, {
id: 'precision', label: gettext('Precision'), cell: 'text',
type: 'text', group: gettext('Definition'), deps: ['basetype'],
readonly: function(state) {return !obj.isNew(state);},
disabled: function(state) {
// We will store type from selected from combobox
var of_type = state.basetype;
if(obj.type_options) {
// iterating over all the types
_.each(obj.type_options, function(o) {
// if type from selected from combobox matches in options
if ( of_type == o.value ) {
// if precession is allowed for selected type
if(o.precision)
{
// set the values in model
state.is_precision = true;
state.min_val = o.min_val;
state.max_val = o.max_val;
}
else
state.is_precision = false;
}
});
if (!state.is_precision) {
if(state.precision) {
state.precision = null;
}
}
}
return !state.is_precision;
},
}, {
id: 'typdefault', label: gettext('Default'), cell: 'text',
type: 'text', group: gettext('Definition'),
placeholder: gettext('Enter an expression or a value.'),
}, {
id: 'typnotnull', label: gettext('Not NULL?'), cell: 'boolean',
type: 'switch', group: gettext('Definition'),
}, {
id: 'collname', label: gettext('Collation'), cell: 'text',
type: 'select', group: gettext('Definition'),
options: this.fieldOptions.collation,
readonly: function(state) {return !obj.isNew(state);},
}, {
id: 'constraints', label: gettext('Constraints'), type: 'collection',
schema: new DomainConstSchema(),
editable: false, group: gettext('Constraints'),
mode: ['edit', 'create'],
canAdd: true, canEdit: false, canDelete: true,
uniqueCol : ['conname'],
}, {
id: 'seclabels', label: gettext('Security labels'), type: 'collection',
schema: new SecLabelSchema(),
editable: false, group: gettext('Security'),
mode: ['edit', 'create'],
canAdd: true, canEdit: false, canDelete: true,
uniqueCol : ['provider'],
min_version: 90200,
}
];
}
}

View File

@@ -781,9 +781,7 @@ class FtsDictionaryView(PGChildNodeView, SchemaDiffObjectCompare):
if not status:
return internal_server_error(errormsg=rset)
# Empty set is added before actual list as initially it will be visible
# at template control while creating a new FTS Dictionary
res = [{'label': '', 'value': ''}]
res = []
for row in rset['rows']:
if row['nspname'] != "pg_catalog":
row['tmplname'] = self.qtIdent(