Port Foreign Table node to react. Fixes #6678.

pull/58/head
Nikhil Mohite 2021-08-23 16:31:13 +05:30 committed by Akshay Joshi
parent 261cec1d20
commit 42eac6f846
9 changed files with 1065 additions and 173 deletions

View File

@ -338,6 +338,8 @@ class ForeignTableView(PGChildNodeView, DataTypeReader,
if not isinstance(req[key], list) and req[key]:
data[key] = json.loads(req[key], encoding='utf-8')
elif req[key]:
data[key] = req[key]
if key == 'inherits':
# Convert Table ids from unicode/string to int

View File

@ -6,6 +6,10 @@
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
import { getNodeListByName, getNodeAjaxOptions } from '../../../../../../../static/js/node_ajax';
import { getNodeVariableSchema } from '../../../../../static/js/variable.ui';
import { getNodePrivilegeRoleSchema } from '../../../../../static/js/privilege.ui';
import ForeignTableSchema from './foreign_table.ui';
/* Create and Register Foreign Table Collection and Node. */
define('pgadmin.node.foreign_table', [
@ -90,7 +94,8 @@ define('pgadmin.node.foreign_table', [
self.model.type_options = d;
return d;
},
},{
},
{
id: 'typlen', label: gettext('Length'),
cell: 'string', group: gettext('Definition'),
type: 'int', deps: ['datatype'],
@ -147,7 +152,8 @@ define('pgadmin.node.foreign_table', [
return true;
},
cellHeaderClasses: 'width_percent_10',
},{
},
{
id: 'precision', label: gettext('Precision'),
type: 'int', deps: ['datatype'],
cell: 'string', group: gettext('Definition'),
@ -200,7 +206,8 @@ define('pgadmin.node.foreign_table', [
}
return true;
}, cellHeaderClasses: 'width_percent_10',
},{
},
{
id: 'typdefault', label: gettext('Default'), type: 'text',
cell: 'string', min_version: 90300, group: gettext('Definition'),
placeholder: gettext('Enter an expression or a value.'),
@ -215,11 +222,13 @@ define('pgadmin.node.foreign_table', [
}
return true;
},
},{
},
{
id: 'attnotnull', label: gettext('Not NULL?'),
cell: 'boolean',type: 'switch', editable: 'is_editable_column',
cellHeaderClasses: 'width_percent_10', group: gettext('Definition'),
},{
},
{
id: 'attstattarget', label: gettext('Statistics'), min_version: 90200,
cell: 'integer', type: 'int', group: gettext('Definition'),
editable: function(m) {
@ -230,7 +239,8 @@ define('pgadmin.node.foreign_table', [
return (_.isUndefined(m.get('inheritedid')) || _.isNull(m.get('inheritedid'))
|| _.isUndefined(m.get('inheritedfrom')) || _.isNull(m.get('inheritedfrom'))) ? true : false;
}, cellHeaderClasses: 'width_percent_10',
},{
},
{
id: 'collname', label: gettext('Collation'), cell: 'node-ajax-options',
control: 'node-ajax-options', type: 'text', url: 'get_collations',
min_version: 90300, editable: function(m) {
@ -239,13 +249,16 @@ define('pgadmin.node.foreign_table', [
|| _.isUndefined(m.get('inheritedfrom')) || _.isNull(m.get('inheritedfrom'))) ? true : false;
},
cellHeaderClasses: 'width_percent_20', group: gettext('Definition'),
},{
},
{
id: 'attnum', cell: 'string',type: 'text', visible: false,
},{
},
{
id: 'inheritedfrom', label: gettext('Inherited From'), cell: 'string',
type: 'text', visible: false, mode: ['properties', 'edit'],
cellHeaderClasses: 'width_percent_10',
},{
},
{
id: 'coloptions', label: gettext('Options'), cell: 'string',
type: 'collection', group: gettext('Options'), mode: ['edit', 'create'],
model: ColumnOptionsModel, canAdd: true, canDelete: true, canEdit: false,
@ -490,6 +503,7 @@ define('pgadmin.node.foreign_table', [
collection_type: 'coll-foreign_table',
hasSQL: true,
hasDepends: true,
width: pgBrowser.stdW.md + 'px',
hasScriptTypes: ['create', 'select', 'insert', 'update', 'delete'],
parent_type: ['schema'],
Init: function() {
@ -521,6 +535,32 @@ define('pgadmin.node.foreign_table', [
]);
},
getSchema: function(treeNodeInfo, itemNodeData) {
return new ForeignTableSchema(
(privileges)=>getNodePrivilegeRoleSchema('', treeNodeInfo, itemNodeData, privileges),
()=>getNodeVariableSchema(this, treeNodeInfo, itemNodeData, false, false),
(params)=>{
return getNodeAjaxOptions('get_columns', pgBrowser.Nodes['foreign_table'], treeNodeInfo, itemNodeData, {urlParams: params, useCache:false});
},
{
role: ()=>getNodeListByName('role', treeNodeInfo, itemNodeData),
schema: ()=>getNodeListByName('schema', treeNodeInfo, itemNodeData, {cacheLevel: 'database'}),
foreignServers: ()=>getNodeAjaxOptions('get_foreign_servers', this, treeNodeInfo, itemNodeData, {cacheLevel: 'database'}, (res) => {
return _.reject(res, function(o) {
return o.label == '' || o.label == null;
});
}),
tables: ()=>getNodeAjaxOptions('get_tables', this, treeNodeInfo, itemNodeData, {cacheLevel: 'database'}),
nodeInfo: treeNodeInfo,
nodeData: itemNodeData,
pgBrowser: pgBrowser
},
{
owner: pgBrowser.serverInfo[treeNodeInfo.server._id].user.name,
basensp: treeNodeInfo.schema ? treeNodeInfo.schema.label : ''
}
);
},
model: pgBrowser.Node.Model.extend({
idAttribute: 'oid',
initialize: function(attrs, args) {

View File

@ -0,0 +1,545 @@
/////////////////////////////////////////////////////////////
//
// 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 SecLabelSchema from '../../../../../static/js/sec_label.ui';
import BaseUISchema from 'sources/SchemaView/base_schema.ui';
import OptionsSchema from '../../../../../static/js/options.ui';
import { isEmptyString } from 'sources/validators';
import _ from 'lodash';
import { getNodePrivilegeRoleSchema } from '../../../../../static/js/privilege.ui';
import { getNodeAjaxOptions } from '../../../../../../../static/js/node_ajax';
export default class ForeignTableSchema extends BaseUISchema {
constructor(getPrivilegeRoleSchema, getVariableSchema, getColumns, fieldOptions={}, initValues) {
super({
name: undefined,
oid: undefined,
owner: undefined,
basensp: undefined,
is_sys_obj: undefined,
description: undefined,
ftsrvname: undefined,
strftoptions: undefined,
inherits: [],
columns: [],
ftoptions: [],
relacl: [],
stracl: [],
seclabels: [],
...initValues
});
this.getPrivilegeRoleSchema = getPrivilegeRoleSchema;
this.getVariableSchema = getVariableSchema;
this.getColumns = getColumns;
this.inheritedTableList = [];
this.fieldOptions = {
role: [],
schema: [],
foreignServers: [],
tables: [],
nodeInfo: null,
...fieldOptions,
};
}
get idAttribute() {
return 'oid';
}
canEditDeleteRowColumns(colstate) {
if (!isEmptyString(colstate.inheritedfrom)) {
return false;
}
return true;
}
getTableOid(tabId) {
// Here we will fetch the table oid from table name
// iterate over list to find table oid
for(const t of this.inheritedTableList) {
if(t.value === tabId) {
return t.value;
}
}
return;
}
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'), cell: 'text',
type: 'select', controlProps: { allowClear: false },
options: obj.fieldOptions.role
},{
id: 'basensp', label: gettext('Schema'), cell: 'text',
type: 'select', mode:['create', 'edit'],
options: obj.fieldOptions.schema
},{
id: 'is_sys_obj', label: gettext('System foreign table?'),
cell:'boolean', type: 'switch', mode: ['properties'],
},{
id: 'description', label: gettext('Comment'), cell: 'text',
type: 'multiline',
},{
id: 'ftsrvname', label: gettext('Foreign server'), cell: 'text',
type: 'select', group: gettext('Definition'),
options: obj.fieldOptions.foreignServers,
readonly: (state) => { return !obj.isNew(state); },
},{
id: 'inherits', label: gettext('Inherits'), group: gettext('Definition'),
type: 'select', min_version: 90500, controlProps: {multiple: true},
options: obj.fieldOptions.tables,
optionsLoaded: (res)=>obj.inheritedTableList=res,
deferredDepChange: (state, source, topState, actionObj)=>{
return new Promise((resolve)=>{
// current table list and previous table list
let newColInherits = state.inherits || [];
let oldColInherits = actionObj.oldState.inherits || [];
var tabName = undefined;
let tabColsResponse;
// Add columns logic
// If new table is added in list
if(newColInherits.length > 1 && newColInherits.length > oldColInherits.length) {
// Find newly added table from current list
tabName = _.difference(newColInherits, oldColInherits);
tabColsResponse = obj.getColumns({attrelid: this.getTableOid(tabName[0])});
} else if (newColInherits.length == 1) {
// First table added
tabColsResponse = obj.getColumns({attrelid: this.getTableOid(newColInherits[0])});
}
if(tabColsResponse) {
tabColsResponse.then((res)=>{
resolve((state)=>{
let finalCols = res.map((col)=> col);
finalCols = [...state.columns, ...finalCols];
return {
adding_inherit_cols: false,
columns: finalCols,
};
});
});
}
// Remove columns logic
let removeOid;
if(newColInherits.length > 0 && newColInherits.length < oldColInherits.length) {
// Find deleted table from previous list
tabName = _.difference(oldColInherits, newColInherits);
removeOid = this.getTableOid(tabName[0]);
} else if (oldColInherits.length === 1 && newColInherits.length < 1) {
// We got last table from list
tabName = oldColInherits[0];
removeOid = this.getTableOid(tabName);
}
if(removeOid) {
resolve((state)=>{
let finalCols = state.columns;
_.remove(state.columns, (col)=>col.inheritedid==removeOid);
return {
adding_inherit_cols: false,
columns: finalCols
};
});
}
});
},
},
{
id: 'columns', label: gettext('Columns'), cell: 'text',
type: 'collection', group: gettext('Columns'), mode: ['edit', 'create'],
schema: new getNodeColumnSchema(obj.fieldOptions.nodeInfo, obj.fieldOptions.nodeData, obj.fieldOptions.pgBrowser),
canAdd: true, canDelete: true, canEdit: true, columns: ['attname', 'datatype', 'inheritedfrom'],
// For each row edit/delete button enable/disable
canEditRow: this.canEditDeleteRowColumns,
canDeleteRow: this.canEditDeleteRowColumns,
},
{
id: 'constraints', label: gettext('Constraints'), cell: 'text',
type: 'collection', group: gettext('Constraints'), mode: ['edit', 'create'],
schema: new CheckConstraintSchema(),
canAdd: true, canDelete: true, columns: ['conname','consrc', 'connoinherit', 'convalidated'],
canEdit: true,
canDeleteRow: function(state) {
return (state.conislocal == true || _.isUndefined(state.conislocal)) ? true : false;
},
canEditRow: function(state) {
return obj.isNew(state);
}
},
{
id: 'strftoptions', label: gettext('Options'), cell: 'text',
type: 'text', group: gettext('Definition'), mode: ['properties'],
},
{
id: 'fdwoptions', label: gettext('Options'), type: 'collection',
schema: new OptionsSchema('fdwoption', 'fdwvalue'),
group: gettext('Options'),
mode: ['edit', 'create'],
canAdd: true, canDelete: true, uniqueCol : ['fdwoption'],
},
{
id: 'relacl', label: gettext('Privileges'), cell: 'text',
type: 'text', group: gettext('Security'),
mode: ['properties'], min_version: 90200,
},
{
id: 'acl', label: gettext('Privileges'), type: 'collection',
schema: this.getPrivilegeRoleSchema(['a','r','w','x']),
uniqueCol : ['grantee', 'grantor'],
editable: false,
group: gettext('Security'), mode: ['edit', 'create'],
canAdd: true, canDelete: true,
min_version: 90200
},
{
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: 90100,
disabled: obj.inCatalog()
}
];
}
validate(state, setError) {
let errmsg = null;
if (isEmptyString(state.service)) {
/* code validation*/
if (isEmptyString(state.ftsrvname)) {
errmsg = gettext('Foreign server cannot be empty.');
setError('ftsrvname', errmsg);
return true;
} else {
errmsg = null;
setError('ftsrvname', errmsg);
}
}
}
}
export function getNodeColumnSchema(treeNodeInfo, itemNodeData, pgBrowser) {
return new ColumnSchema(
{},
(privileges)=>getNodePrivilegeRoleSchema(this, treeNodeInfo, itemNodeData, privileges),
treeNodeInfo,
()=>getNodeAjaxOptions('get_types', pgBrowser.Nodes['table'], treeNodeInfo, itemNodeData, {
cacheLevel: 'table',
}),
()=>getNodeAjaxOptions('get_collations', pgBrowser.Nodes['collation'], treeNodeInfo, itemNodeData),
);
}
export class ColumnSchema extends BaseUISchema {
constructor(initValues, getPrivilegeRoleSchema, nodeInfo, datatypeOptions, collspcnameOptions) {
super({
attname: undefined,
datatype: undefined,
typlen: undefined,
precision: undefined,
typdefault: undefined,
attnotnull: undefined,
collname: undefined,
attnum: undefined,
inheritedfrom: undefined,
inheritedid: undefined,
attstattarget: undefined,
coloptions: [],
});
this.getPrivilegeRoleSchema = getPrivilegeRoleSchema;
this.nodeInfo = nodeInfo;
this.datatypeOptions = datatypeOptions;
this.collspcnameOptions = collspcnameOptions;
this.datatypes = [];
}
get idAttribute() {
return 'attnum';
}
editable_check_for_column(state) {
return (_.isUndefined(state.inheritedid) || _.isNull(state.inheritedid) || _.isUndefined(state.inheritedfrom) || _.isNull(state.inheritedfrom)) ? true : false;
}
get baseFields() {
let obj = this;
return [
{
id: 'attname', label: gettext('Name'), cell: 'text',
type: 'text', editable: obj.editable_check_for_column, noEmpty: true,
minWidth: 115,
},
{
id: 'datatype', label: gettext('Data type'), minWidth: 150,
group: gettext('Definition'), noEmpty: true,
editable: obj.editable_check_for_column,
options: obj.datatypeOptions,
optionsLoaded: (options)=>{
obj.datatypes = options;
obj.type_options = options;
},
cell: 'select',
controlProps: {
allowClear: false,
},
type: 'select'
},
{
id: 'inheritedfrom', label: gettext('Inherited From'), cell: 'label',
type: 'label', readonly: true, editable: false, mode: ['properties', 'edit'],
},
{
id: 'attnum', label: gettext('Position'), cell: 'text',
type: 'text', disabled: obj.inCatalog(), mode: ['properties'],
},
{
id: 'typlen', label: gettext('Length'), cell: 'int',
deps: ['datatype'], type: 'int', group: gettext('Definition'), width: 120, minWidth: 120,
disabled: (state) => {
var val = state.typlen;
// We will store type from selected from combobox
if(!(_.isUndefined(state.inheritedid)
|| _.isNull(state.inheritedid)
|| _.isUndefined(state.inheritedfrom)
|| _.isNull(state.inheritedfrom))) {
if (!_.isUndefined(val)) {
state.typlen = undefined;
}
return true;
}
var of_type = state.datatype,
has_length = false;
if(obj.type_options) {
state.is_tlength = false;
// 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
has_length = true;
state.is_tlength = true;
state.min_val = o.min_val;
state.max_val = o.max_val;
}
}
});
if (!has_length && !_.isUndefined(val)) {
state.typlen = undefined;
}
return !(state.is_tlength);
}
if (!has_length && !_.isUndefined(val)) {
state.typlen = undefined;
}
return true;
},
},
{
id: 'precision', label: gettext('Precision'), cell: 'int', minWidth: 60,
deps: ['datatype'], type: 'int', group: gettext('Definition'),
disabled: (state) => {
var val = state.precision;
if(!(_.isUndefined(state.inheritedid)
|| _.isNull(state.inheritedid)
|| _.isUndefined(state.inheritedfrom)
|| _.isNull(state.inheritedfrom))) {
if (!_.isUndefined(val)) {
state.precision = undefined;
}
return true;
}
var of_type = state.datatype,
has_precision = false;
if(obj.type_options) {
state.is_precision = false;
// 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)
{
has_precision = true;
// set the values in model
state.is_precision = true;
state.min_val = o.min_val;
state.max_val = o.max_val;
}
}
});
if (!has_precision && !_.isUndefined(val)) {
state.precision = undefined;
}
return !(state.is_precision);
}
if (!has_precision && !_.isUndefined(val)) {
state.precision = undefined;
}
return true;
},
},
{
id: 'typdefault', label: gettext('Default'), cell: 'text',
type: 'text', group: gettext('Definition'),
placeholder: gettext('Enter an expression or a value.'),
editable: (state) => {
if(!(_.isUndefined(state.inheritedid)
|| _.isNull(state.inheritedid)
|| _.isUndefined(state.inheritedfrom)
|| _.isNull(state.inheritedfrom))) { return false; }
if (obj.nodeInfo.server.version < 90300){
return false;
}
return true;
},
},
{
id: 'attnotnull', label: gettext('Not NULL?'), cell: 'switch',
type: 'switch', minWidth: 80,
group: gettext('Definition'), editable: obj.editable_check_for_column,
},
{
id: 'attstattarget', label: gettext('Statistics'), cell: 'text',
type: 'text', disabled: (state) => {
if (obj.isNew()) {
return false;
}
if (obj.nodeInfo.server.version < 90200) {
return false;
}
return (_.isUndefined(state.inheritedid) || _.isNull(state.inheritedid) ||
_.isUndefined(state.inheritedfrom) || _.isNull(state.inheritedfrom)) ? true : false;
}, mode: ['properties', 'edit'],
group: gettext('Definition'),
},
{
id: 'collname', label: gettext('Collation'), cell: 'select',
type: 'select', group: gettext('Definition'),
deps: ['datatype'], options: obj.collspcnameOptions,
disabled: (state)=>{
if (!(_.isUndefined(obj.isNew)) && !obj.isNew(state)) { return false; }
return (_.isUndefined(state.inheritedid) || _.isNull(state.inheritedid) ||
_.isUndefined(state.inheritedfrom) || _.isNull(state.inheritedfrom)) ? true : false;
}
},
{
id: 'coloptions', label: gettext('Options'), type: 'collection',
group: gettext('Options'),
schema: new OptionsSchema('option', 'value'),
uniqueCol : ['option'], mode: ['edit', 'create'],
canAdd: true, canEdit: false, canDelete: true,
}
];
}
}
export class CheckConstraintSchema extends BaseUISchema {
constructor() {
super({
name: undefined,
oid: undefined,
description: undefined,
consrc: undefined,
connoinherit: undefined,
convalidated: true,
});
this.convalidated_default = true;
}
get idAttribute() {
return 'conoid';
}
isReadonly(state) {
return !this.isNew(state);
}
get baseFields() {
let obj = this;
return [{
id: 'conname', label: gettext('Name'), type:'text', cell:'text',
mode: ['properties', 'create', 'edit'],
editable: (state) => {
return _.isUndefined(obj.isNew) ? true : obj.isNew(state);
}, noEmpty: true, readonly: obj.isReadonly
},{
id: 'consrc', label: gettext('Check'), type: 'multiline', cell: 'text',
mode: ['properties', 'create', 'edit'],
editable: (state) => {
return _.isUndefined(obj.isNew) ? true : obj.isNew(state);
}, noEmpty: true, readonly: obj.isReadonly
},{
id: 'connoinherit', label: gettext('No inherit?'), type: 'switch', cell: 'switch',
mode: ['properties', 'create', 'edit'],
deps: [['is_partitioned']],
editable: (state) => {
return _.isUndefined(obj.isNew) ? true : obj.isNew(state);
}, readonly: obj.isReadonly
},{
id: 'convalidated', label: gettext('Validate?'), type: 'switch', cell: 'switch',
readonly: obj.isReadonly,
editable: (state) => {
if (_.isUndefined(obj.isNew)) { return true; }
if (!obj.isNew(state)) {
if(state.convalidated && obj.convalidated_default) {
return false;
}
return true;
}
return true;
},
mode: ['properties', 'create', 'edit'],
}];
}
}

View File

@ -139,139 +139,9 @@ define('pgadmin.node.trigger_function', [
id: 'funcowner', label: gettext('Owner'), cell: 'string',
control: Backform.NodeListByNameControl, node: 'role', type:
'text', disabled: 'isDisabled', readonly: 'isReadonly',
},{
id: 'pronamespace', label: gettext('Schema'), cell: 'string',
control: 'node-list-by-id', type: 'text', cache_level: 'database',
node: 'schema', disabled: 'isDisabled', readonly: 'isReadonly',
mode: ['create', 'edit'],
},{
id: 'sysfunc', label: gettext('System trigger function?'),
cell:'boolean', type: 'switch',
mode: ['properties'], visible: 'isVisible',
},{
id: 'sysproc', label: gettext('System procedure?'),
cell:'boolean', type: 'switch',
mode: ['properties'], visible: 'isVisible',
},{
id: 'description', label: gettext('Comment'), cell: 'string',
type: 'multiline', disabled: 'isDisabled', readonly: 'isReadonly',
},{
id: 'pronargs', label: gettext('Argument count'), cell: 'string',
type: 'text', group: gettext('Definition'), mode: ['properties'],
},{
id: 'proargs', label: gettext('Arguments'), cell: 'string',
type: 'text', group: gettext('Definition'), mode: ['properties', 'edit'],
disabled: 'isDisabled', readonly: 'isReadonly',
},{
id: 'proargtypenames', label: gettext('Signature arguments'), cell:
'string', type: 'text', group: gettext('Definition'), mode: ['properties'],
disabled: 'isDisabled', readonly: 'isReadonly',
},{
id: 'prorettypename', label: gettext('Return type'), cell: 'string',
control: 'select2', type: 'text', group: gettext('Definition'),
disabled: 'isDisabled', readonly: 'isReadonly', first_empty: true,
select2: { width: '100%', allowClear: false },
mode: ['create'], visible: 'isVisible', options: [
{label: gettext('trigger'), value: 'trigger'},
{label: gettext('event_trigger'), value: 'event_trigger'},
],
},{
id: 'prorettypename', label: gettext('Return type'), cell: 'string',
type: 'text', group: gettext('Definition'),
mode: ['properties', 'edit'], disabled: 'isDisabled', readonly: 'isReadonly',
visible: 'isVisible',
}, {
id: 'lanname', label: gettext('Language'), cell: 'string',
control: 'node-ajax-options', type: 'text', group: gettext('Definition'),
url: 'get_languages', disabled: 'isDisabled', readonly: 'isReadonly',
transform: function(d) {
return _.reject(d, function(o) {
return o.label == 'sql' || o.label == 'edbspl';
});
}, select2: { allowClear: false },
},{
id: 'prosrc', label: gettext('Code'), cell: 'string',
type: 'text', mode: ['properties', 'create', 'edit'],
group: gettext('Code'), deps: ['lanname'],
tabPanelCodeClass: 'sql-code-control',
control: Backform.SqlCodeControl,
visible: function(m) {
if (m.get('lanname') == 'c') {
return false;
}
return true;
}, disabled: 'isDisabled', readonly: 'isReadonly',
},{
id: 'probin', label: gettext('Object file'), cell: 'string',
type: 'text', group: gettext('Definition'), deps: ['lanname'], visible:
function(m) {
if (m.get('lanname') == 'c') { return true; }
return false;
}, disabled: 'isDisabled', readonly: 'isReadonly',
},{
id: 'prosrc_c', label: gettext('Link symbol'), cell: 'string',
type: 'text', group: gettext('Definition'), deps: ['lanname'], visible:
function(m) {
if (m.get('lanname') == 'c') { return true; }
return false;
}, disabled: 'isDisabled', readonly: 'isReadonly',
},{
id: 'provolatile', label: gettext('Volatility'), cell: 'string',
control: 'node-ajax-options', type: 'text', group: gettext('Options'),
options:[
{'label': 'VOLATILE', 'value': 'v'},
{'label': 'STABLE', 'value': 's'},
{'label': 'IMMUTABLE', 'value': 'i'},
], disabled: 'isDisabled', readonly: 'isReadonly', select2: { allowClear: false },
},{
id: 'proretset', label: gettext('Returns a set?'), type: 'switch',
group: gettext('Options'), disabled: 'isDisabled', readonly: 'isReadonly',
visible: 'isVisible',
},{
id: 'proisstrict', label: gettext('Strict?'), type: 'switch',
disabled: 'isDisabled', readonly: 'isReadonly', group: gettext('Options'),
},{
id: 'prosecdef', label: gettext('Security of definer?'),
group: gettext('Options'), cell:'boolean', type: 'switch',
disabled: 'isDisabled', readonly: 'isReadonly',
},{
id: 'proiswindow', label: gettext('Window?'),
group: gettext('Options'), cell:'boolean', type: 'switch',
disabled: 'isDisabled', readonly: 'isReadonly', visible: 'isVisible',
},{
id: 'procost', label: gettext('Estimated cost'), type: 'text',
group: gettext('Options'), disabled: 'isDisabled', readonly: 'isReadonly',
},{
id: 'prorows', label: gettext('Estimated rows'), type: 'text',
group: gettext('Options'),
disabled: 'isDisabled', readonly: 'isReadonly',
deps: ['proretset'], visible: 'isVisible',
},{
id: 'proleakproof', label: gettext('Leak proof?'),
group: gettext('Options'), cell:'boolean', type: 'switch', min_version: 90200,
disabled: 'isDisabled', readonly: 'isReadonly',
}, pgBrowser.SecurityGroupSchema, {
id: 'proacl', label: gettext('Privileges'), mode: ['properties'],
group: gettext('Security'), type: 'text',
},{
id: 'variables', label: '', type: 'collection',
group: gettext('Parameters'), control: 'variable-collection',
model: pgBrowser.Node.VariableModel,
mode: ['edit', 'create'], canAdd: 'canVarAdd', canEdit: false,
canDelete: true, disabled: 'isDisabled', readonly: 'isReadonly',
},{
id: 'acl', label: gettext('Privileges'), editable: false,
type: 'collection', group: 'security', mode: ['edit', 'create'],
model: pgBrowser.Node.PrivilegeRoleModel.extend({
privileges: ['X'],
}), uniqueCol : ['grantee', 'grantor'], disabled: 'isDisabled', readonly: 'isReadonly',
canAdd: true, canDelete: true, control: 'unique-col-collection',
},{
id: 'seclabels', label: gettext('Security labels'), canEdit: true,
model: pgBrowser.SecLabelModel, type: 'collection',
min_version: 90100, group: 'security', mode: ['edit', 'create'],
canDelete: true, control: 'unique-col-collection', canAdd: true,
uniqueCol : ['provider'], disabled: 'isDisabled', readonly: 'isReadonly',
}],
validate: function(keys)
{

View File

@ -118,10 +118,6 @@ export default class TriggerFunctionSchema extends BaseUISchema {
id: 'sysfunc', label: gettext('System trigger function?'),
cell:'boolean', type: 'switch',
mode: ['properties'], visible: obj.isVisible
},{
id: 'sysproc', label: gettext('System procedure?'),
cell:'boolean', type: 'switch',
mode: ['properties'], visible: obj.isVisible
},{
id: 'description', label: gettext('Comment'), cell: 'string',
type: 'multiline', disabled: obj.isDisabled, readonly: obj.isReadonly,

View File

@ -51,7 +51,7 @@ export class EventSchema extends BaseUISchema {
id: 'evnt_insert', label: gettext('INSERT'),
type: 'switch', mode: ['create','edit', 'properties'],
group: gettext('Events'),
disabled: (state) => {
readonly: (state) => {
var evn_insert = state.evnt_insert;
if (!_.isUndefined(evn_insert) && obj.nodeInfo && obj.nodeInfo.server.server_type == 'ppas' && obj.isNew(state))
return false;
@ -61,7 +61,7 @@ export class EventSchema extends BaseUISchema {
id: 'evnt_update', label: gettext('UPDATE'),
type: 'switch', mode: ['create','edit', 'properties'],
group: gettext('Events'),
disabled: (state) => {
readonly: (state) => {
var evn_update = state.evnt_update;
if (!_.isUndefined(evn_update) && obj.nodeInfo && obj.nodeInfo.server.server_type == 'ppas' && obj.isNew(state))
return false;
@ -71,7 +71,7 @@ export class EventSchema extends BaseUISchema {
id: 'evnt_delete', label: gettext('DELETE'),
type: 'switch', mode: ['create','edit', 'properties'],
group: gettext('Events'),
disabled: (state) => {
readonly: (state) => {
var evn_delete = state.evnt_delete;
if (!_.isUndefined(evn_delete) && obj.nodeInfo && obj.nodeInfo.server.server_type == 'ppas' && obj.isNew(state))
return false;
@ -80,7 +80,7 @@ export class EventSchema extends BaseUISchema {
},{
id: 'evnt_truncate', label: gettext('TRUNCATE'),
type: 'switch', group: gettext('Events'), deps: ['is_row_trigger', 'is_constraint_trigger'],
disabled: (state) => {
readonly: (state) => {
var is_constraint_trigger = state.is_constraint_trigger,
is_row_trigger = state.is_row_trigger,
server_type = obj.nodeInfo ? obj.nodeInfo.server.server_type: null;
@ -219,7 +219,7 @@ export default class TriggerSchema extends BaseUISchema {
type: 'switch', group: gettext('Definition'),
mode: ['create','edit', 'properties'],
deps: ['is_constraint_trigger'],
disabled: (state) => {
readonly: (state) => {
// Disabled if table is a partitioned table.
if (!obj.isNew())
return true;
@ -475,3 +475,4 @@ export default class TriggerSchema extends BaseUISchema {
}
}
}

View File

@ -0,0 +1,462 @@
/////////////////////////////////////////////////////////////
//
// 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 BaseUISchema from 'sources/SchemaView/base_schema.ui';
import ForeignTableSchema, { ColumnSchema, CheckConstraintSchema } from '../../../pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/static/js/foreign_table.ui';
class MockSchema extends BaseUISchema {
get baseFields() {
return [];
}
}
describe('ForeignTableSchema', ()=>{
let mount;
let schemaObj = new ForeignTableSchema(
()=>new MockSchema(),
()=>new MockSchema(),
()=>new MockSchema(),
{
role: [],
schema: [],
foreignServers: [],
tables: [],
nodeData: {},
pgBrowser: {},
nodeInfo: {
schema: {},
server: {user: {name:'postgres', id:0}, server_type: 'pg', version: 90400},
table: {}
}
}
);
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: 'edit',
}}
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('validate', ()=>{
let state = {};
let setError = jasmine.createSpy('setError');
state.ftsrvname = null;
schemaObj.validate(state, setError);
expect(setError).toHaveBeenCalledWith('ftsrvname', 'Foreign server cannot be empty.');
state.ftsrvname = 'public';
schemaObj.validate(state, setError);
expect(setError).toHaveBeenCalledWith('ftsrvname', null);
});
it('column canEditRow', ()=>{
let state = {};
let canEditRow = _.find(schemaObj.fields, (f)=>f.id=='columns').canEditRow;
let status = canEditRow(state);
expect(status).toBe(true);
let colstate = { inheritedfrom: ['public'] };
status = canEditRow(colstate);
expect(status).toBe(false);
});
it('constraints canDeleteRow', ()=>{
let state = {};
let canEditRow = _.find(schemaObj.fields, (f)=>f.id=='constraints').canDeleteRow;
let status = canEditRow(state);
expect(status).toBe(true);
let colstate = { conislocal: true };
status = canEditRow(colstate);
expect(status).toBe(true);
});
it('constraints canEditRow', ()=>{
let state = {};
let canEditRow = _.find(schemaObj.fields, (f)=>f.id=='constraints').canEditRow;
let status = canEditRow(state);
expect(status).toBe(true);
});
/*it('inherits deferredDepChange', ()=>{
let state = {columns: []};
let deferredDepChange = _.find(schemaObj.fields, (f)=>f.id=='inherits').deferredDepChange;
let status = deferredDepChange(state, {}, {}, {});
expect(status).toEqual(Promise.reject());
});*/
});
describe('ForeignTableColumnSchema', ()=>{
let mount;
let schemaObj = new ColumnSchema(
{},
()=>new MockSchema(),
{
schema: {},
server: {user: {name:'postgres', id:0}, server_type: 'pg', version: 90400},
table: {}
},
[{is_collatable: false, label: '"char"', length: true, max_val: 0, min_val: 0, precision: true, typval: ' '}],
()=>[],
);
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('properties', ()=>{
mount(<SchemaView
formType='tab'
schema={schemaObj}
getInitData={getInitData}
viewHelperProps={{
mode: 'properties',
}}
onHelp={()=>{}}
onEdit={()=>{}}
/>);
});
it('edit', ()=>{
mount(<SchemaView
formType='dialog'
schema={schemaObj}
getInitData={getInitData}
viewHelperProps={{
mode: 'edit',
}}
onSave={()=>{}}
onClose={()=>{}}
onHelp={()=>{}}
onEdit={()=>{}}
onDataChange={()=>{}}
confirmOnCloseReset={false}
hasSQL={false}
disableSqlHelp={false}
/>);
});
it('column editable', ()=>{
let state = {};
let editable = _.find(schemaObj.fields, (f)=>f.id=='attname').editable;
let status = editable(state);
expect(status).toBe(true);
});
it('typdefault editable', ()=>{
let state = {};
let editable = _.find(schemaObj.fields, (f)=>f.id=='typdefault').editable;
let status = editable(state);
expect(status).toBe(true);
});
it('typdefault_edit', ()=>{
let defaultSchemaObj = new ForeignTableSchema(
()=>new MockSchema(),
()=>new MockSchema(),
()=>new MockSchema(),
{
role: [],
schema: [],
foreignServers: [],
tables: [],
nodeData: {},
pgBrowser: {},
nodeInfo: {
schema: {},
server: {user: {name:'postgres', id:0}, server_type: 'pg', version: 90000},
table: {}
}
}
);
let initData = ()=>Promise.resolve({typlen: 1, inheritedid: 1, inheritedfrom: 'public'});
mount(<SchemaView
formType='dialog'
schema={defaultSchemaObj}
getInitData={initData}
viewHelperProps={{
mode: 'edit',
}}
onSave={()=>{}}
onClose={()=>{}}
onHelp={()=>{}}
onEdit={()=>{}}
onDataChange={()=>{}}
confirmOnCloseReset={false}
hasSQL={false}
disableSqlHelp={false}
/>);
});
it('attstattarget', ()=>{
let defaultSchemaObj = new ForeignTableSchema(
()=>new MockSchema(),
()=>new MockSchema(),
()=>new MockSchema(),
{
role: [],
schema: [],
foreignServers: [],
tables: [],
nodeData: {},
pgBrowser: {},
nodeInfo: {
schema: {},
server: {user: {name:'postgres', id:0}, server_type: 'pg', version: 90000},
table: {}
}
}
);
let initData = ()=>Promise.resolve({
precision: null,
typlen: 1,
inheritedid: 1,
inheritedfrom: 'public',
});
mount(<SchemaView
formType='dialog'
schema={defaultSchemaObj}
getInitData={initData}
viewHelperProps={{
mode: 'edit',
}}
onSave={()=>{}}
onClose={()=>{}}
onHelp={()=>{}}
onEdit={()=>{}}
onDataChange={()=>{}}
confirmOnCloseReset={false}
hasSQL={false}
disableSqlHelp={false}
/>);
});
});
describe('ForeignTableCheckConstraint', ()=>{
let mount;
let schemaObj = new CheckConstraintSchema();
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('properties', ()=>{
mount(<SchemaView
formType='tab'
schema={schemaObj}
getInitData={getInitData}
viewHelperProps={{
mode: 'properties',
}}
onHelp={()=>{}}
onEdit={()=>{}}
/>);
});
it('edit', ()=>{
mount(<SchemaView
formType='dialog'
schema={schemaObj}
getInitData={getInitData}
viewHelperProps={{
mode: 'edit',
}}
onSave={()=>{}}
onClose={()=>{}}
onHelp={()=>{}}
onEdit={()=>{}}
onDataChange={()=>{}}
confirmOnCloseReset={false}
hasSQL={false}
disableSqlHelp={false}
/>);
});
it('conname editable', ()=>{
let state = {};
let editable = _.find(schemaObj.fields, (f)=>f.id=='conname').editable;
let status = editable(state);
expect(status).toBe(true);
});
it('consrc editable', ()=>{
let state = {};
let editable = _.find(schemaObj.fields, (f)=>f.id=='consrc').editable;
let status = editable(state);
expect(status).toBe(true);
});
it('connoinherit editable', ()=>{
let state = {};
let editable = _.find(schemaObj.fields, (f)=>f.id=='connoinherit').editable;
let status = editable(state);
expect(status).toBe(true);
});
it('convalidated editable', ()=>{
let state = {};
let editable = _.find(schemaObj.fields, (f)=>f.id=='convalidated').editable;
let status = editable(state);
expect(status).toBe(true);
spyOn(schemaObj, 'isNew').and.returnValue(false);
editable = _.find(schemaObj.fields, (f)=>f.id=='convalidated').editable;
status = editable(state);
expect(status).toBe(true);
});
});

View File

@ -17,7 +17,7 @@ import SchemaView from '../../../pgadmin/static/js/SchemaView';
import MembershipSchema, {getMembershipSchema} from '../../../pgadmin/browser/server_groups/servers/static/js/membership.ui';
import * as nodeAjax from '../../../pgadmin/browser/static/js/node_ajax';
describe('PrivilegeSchema', ()=>{
describe('MembershipSchema', ()=>{
let mount;
let schemaObj = new MembershipSchema(
()=>[]);

View File

@ -351,28 +351,4 @@ describe('TriggerEventsSchema', ()=>{
schemaObj.validate(state, setError);
expect(setError).toHaveBeenCalledWith('evnt_insert', null);
});
//spyOn(schemaObj, 'isNew).and.returnValue(true);
/*it('evnt_insert disabled', ()=>{
let disabled = _.find(schemaObj.fields, (f)=>f.id=='evnt_insert').disabled;
disabled({evnt_insert : true});
});
it('evnt_update disabled', ()=>{
let disabled = _.find(schemaObj.fields, (f)=>f.id=='evnt_update').disabled;
disabled({evnt_update : true});
});
it('evnt_delete disabled', ()=>{
let disabled = _.find(schemaObj.fields, (f)=>f.id=='evnt_delete').disabled;
disabled({evnt_delete : true});
});
it('evnt_truncate disabled', ()=>{
getInitData = ()=>Promise.resolve({is_constraint_trigger: true});
let disabled = _.find(schemaObj.fields, (f)=>f.id=='evnt_truncate').disabled;
disabled({evnt_truncate : true});
});*/
});