diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/__init__.py index bd67aea7c..bd8290d4b 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/__init__.py +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/__init__.py @@ -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 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/static/js/foreign_table.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/static/js/foreign_table.js index 425198d6e..de6d2f965 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/static/js/foreign_table.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/static/js/foreign_table.js @@ -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) { diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/static/js/foreign_table.ui.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/static/js/foreign_table.ui.js new file mode 100644 index 000000000..a17a7c149 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/static/js/foreign_table.ui.js @@ -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'], + }]; + } +} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/static/js/trigger_function.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/static/js/trigger_function.js index 84e1b03aa..e70b17956 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/static/js/trigger_function.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/static/js/trigger_function.js @@ -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) { diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/static/js/trigger_function.ui.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/static/js/trigger_function.ui.js index 0257ce8b0..33b2e6721 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/static/js/trigger_function.ui.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/static/js/trigger_function.ui.js @@ -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, diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/triggers/static/js/trigger.ui.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/triggers/static/js/trigger.ui.js index 7ab1f1bfb..f5e9565f9 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/triggers/static/js/trigger.ui.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/triggers/static/js/trigger.ui.js @@ -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 { } } } + diff --git a/web/regression/javascript/schema_ui_files/foreign_table.ui.spec.js b/web/regression/javascript/schema_ui_files/foreign_table.ui.spec.js new file mode 100644 index 000000000..9c0bffd10 --- /dev/null +++ b/web/regression/javascript/schema_ui_files/foreign_table.ui.spec.js @@ -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({}} + 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('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({}} + onClose={()=>{}} + onHelp={()=>{}} + onEdit={()=>{}} + onDataChange={()=>{}} + confirmOnCloseReset={false} + hasSQL={false} + disableSqlHelp={false} + />); + }); + + it('properties', ()=>{ + mount({}} + onEdit={()=>{}} + />); + }); + + it('edit', ()=>{ + mount({}} + 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({}} + 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({}} + 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({}} + onClose={()=>{}} + onHelp={()=>{}} + onEdit={()=>{}} + onDataChange={()=>{}} + confirmOnCloseReset={false} + hasSQL={false} + disableSqlHelp={false} + />); + }); + + it('properties', ()=>{ + mount({}} + onEdit={()=>{}} + />); + }); + + it('edit', ()=>{ + mount({}} + 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); + }); +}); + diff --git a/web/regression/javascript/schema_ui_files/membership.ui.spec.js b/web/regression/javascript/schema_ui_files/membership.ui.spec.js index e07ea80e0..e420c0961 100644 --- a/web/regression/javascript/schema_ui_files/membership.ui.spec.js +++ b/web/regression/javascript/schema_ui_files/membership.ui.spec.js @@ -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( ()=>[]); diff --git a/web/regression/javascript/schema_ui_files/trigger.ui.spec.js b/web/regression/javascript/schema_ui_files/trigger.ui.spec.js index c437b33ff..da9532922 100644 --- a/web/regression/javascript/schema_ui_files/trigger.ui.spec.js +++ b/web/regression/javascript/schema_ui_files/trigger.ui.spec.js @@ -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}); - });*/ - });