diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/static/js/synonym.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/static/js/synonym.js index 3ce6129b1..36749506f 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/static/js/synonym.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/static/js/synonym.js @@ -7,6 +7,9 @@ // ////////////////////////////////////////////////////////////// +import { getNodeAjaxOptions, getNodeListByName } from '../../../../../../../static/js/node_ajax'; +import SynonymSchema from './synonym.ui'; + define('pgadmin.node.synonym', [ 'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'sources/pgadmin', 'pgadmin.browser', 'pgadmin.alertifyjs', @@ -68,6 +71,35 @@ define('pgadmin.node.synonym', [ ]); }, + + getSchema: function(treeNodeInfo, itemNodeData) { + return new SynonymSchema( + { + role: ()=>getNodeListByName('role', treeNodeInfo, itemNodeData), + schema: ()=>getNodeListByName('schema', treeNodeInfo, itemNodeData, { + cacheLevel: 'database', + cacheNode: 'database' + }), + synobjschema: ()=>getNodeListByName('schema', treeNodeInfo, itemNodeData, {}, (m)=>{ + // Exclude PPAS catalogs + var exclude_catalogs = ['pg_catalog', 'sys', 'dbo', 'pgagent', 'information_schema', 'dbms_job_procedure']; + return m && _.indexOf(exclude_catalogs, m.label) == -1; + }), + getTargetObjectOptions: (targettype, synobjschema) => + { + return getNodeAjaxOptions('get_target_objects', this, treeNodeInfo, + itemNodeData, {urlParams: {'trgTyp' : targettype, 'trgSchema' : synobjschema}, useCache: false}); + }, + }, + treeNodeInfo, + { + owner: pgBrowser.serverInfo[treeNodeInfo.server._id].user.name, + schema: itemNodeData.label, + synobjschema: itemNodeData.label, + } + ); + }, + model: pgAdmin.Browser.Node.Model.extend({ isNew: function() { return !this.fetchFromServer; @@ -104,123 +136,8 @@ define('pgadmin.node.synonym', [ type: 'text', mode: ['properties', 'create', 'edit'], readonly: true , control: 'node-list-by-name', node: 'role', visible: false, - },{ - id: 'schema', label: gettext('Schema'), cell: 'string', - type: 'text', mode: ['properties', 'create', 'edit'], - readonly: function(m) { return !m.isNew(); }, node: 'schema', - control: 'node-list-by-name', cache_node: 'database', - cache_level: 'database', - },{ - id: 'targettype', label: gettext('Target type'), cell: 'string', - disabled: 'inSchema', group: gettext('Definition'), - select2: { width: '50%', allowClear: false }, - options: function() { - return [ - {label: gettext('Function'), value: 'f'}, - {label: gettext('Package'), value: 'P'}, - {label: gettext('Procedure'), value: 'p'}, - {label: gettext('Public Synonym'), value: 's'}, - {label: gettext('Sequence'), value: 'S'}, - {label: gettext('Table'), value: 'r'}, - {label: gettext('View'), value: 'v'}, - ]; - }, - control: 'select2', - },{ - id: 'synobjschema', label: gettext('Target schema'), cell: 'string', - type: 'text', mode: ['properties', 'create', 'edit'], - group: gettext('Definition'), deps: ['targettype'], - select2: { allowClear: false }, control: 'node-list-by-name', - node: 'schema', filter: function(d) { - // Exclude PPAS catalogs - var exclude_catalogs = ['pg_catalog', 'sys', 'dbo', - 'pgagent', 'information_schema', - 'dbms_job_procedure']; - return d && _.indexOf(exclude_catalogs, d.label) == -1; - }, - disabled: function(m) { - // If tagetType is synonym then disable it - if(!m.inSchema.apply(this, [m])) { - var is_synonym = (m.get('targettype') == 's'); - if(is_synonym) { - m.set('synobjschema', 'public', {silent: true}); - return true; - } else { - return false; - } - } - return true; - }, - },{ - id: 'synobjname', label: gettext('Target object'), cell: 'string', - type: 'text', group: gettext('Definition'), - deps: ['targettype', 'synobjschema'], - control: 'node-ajax-options', - options: function(control) { - var trgTyp = control.model.get('targettype'); - var trgSchema = control.model.get('synobjschema'); - var res = []; - var node = control.field.get('schema_node'), - _url = node.generate_url.apply( - node, [ - null, 'get_target_objects', control.field.get('node_data'), false, - control.field.get('node_info') ]); - $.ajax({ - type: 'GET', - timeout: 30000, - url: _url, - cache: false, - async: false, - data: {'trgTyp' : trgTyp, 'trgSchema' : trgSchema}, - }) - // On success return function list from server - .done(function(result) { - res = result.data; - return res; - }) - // On failure show error appropriate error message to user - .fail(function(xhr, status, error) { - alertify.pgRespErrorNotify(xhr, error); - }); - return res; - }, - disabled: function(m) { - if (this.node_info && 'catalog' in this.node_info) { - return true; - } - // Check the changed attributes if targettype or synobjschema - // is changed then reset the target object - if ('changed' in m && !('name' in m.changed) && - ('targettype' in m.changed || 'synobjschema' in m.changed)) { - m.set('synobjname', undefined); - } + }], - return false; - }, - },{ - id: 'is_sys_obj', label: gettext('System synonym?'), - cell:'boolean', type: 'switch', mode: ['properties'], - }, - ], - validate: function() { - var msg = undefined; - this.errorModel.clear(); - - if (_.isUndefined(this.get('name')) - || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') { - msg = gettext('Name cannot be empty.'); - this.errorModel.set('name', msg); - } else if (_.isUndefined(this.get('synobjschema')) - || String(this.get('synobjschema')).replace(/^\s+|\s+$/g, '') == '') { - msg = gettext('Target schema cannot be empty.'); - this.errorModel.set('synobjschema', msg); - } else if (_.isUndefined(this.get('synobjname')) - || String(this.get('synobjname')).replace(/^\s+|\s+$/g, '') == '') { - msg = gettext('Target object cannot be empty.'); - this.errorModel.set('synobjname', msg); - } - return null; - }, // We will disable everything if we are under catalog node inSchema: function() { if(this.node_info && 'catalog' in this.node_info) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/static/js/synonym.ui.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/static/js/synonym.ui.js new file mode 100644 index 000000000..cd54bfd16 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/synonyms/static/js/synonym.ui.js @@ -0,0 +1,146 @@ +///////////////////////////////////////////////////////////// +// +// 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 { emptyValidator } from 'sources/validators'; + +export default class SynonymSchema extends BaseUISchema { + constructor(fieldOptions={}, nodeInfo, initValues) { + super({ + targettype: 'r', + ...initValues, + }); + this.fieldOptions = fieldOptions; + this.nodeInfo = nodeInfo; + } + + get idAttribute() { + return 'oid'; + } + + get baseFields() { + let obj = this; + return [ + { + id: 'name', label: gettext('Name'), cell: 'text', + type: 'text', mode: ['properties', 'create', 'edit'], + readonly: function(state) { return !obj.isNew(state); }, + }, { + id: 'oid', label: gettext('OID'), cell: 'text', + type: 'text', mode: ['properties'], + }, { + id: 'owner', label: gettext('Owner'), + options: this.fieldOptions.role, + controlProps: { allowClear: false, editable: false}, + type: 'select', mode: ['properties', 'create', 'edit'], + readonly: true , visible: false, + }, { + id: 'schema', label: gettext('Schema'), + type: 'select', mode: ['properties', 'create', 'edit'], + options: this.fieldOptions.schema, + controlProps: { allowClear: false, editable: false }, + readonly: function(state) { return !obj.isNew(state); }, + }, { + id: 'targettype', label: gettext('Target type'), + readonly: this.inSchema, group: gettext('Definition'), + controlProps: { allowClear: false }, + type: 'select', + options: [ + {label: gettext('Function'), value: 'f'}, + {label: gettext('Package'), value: 'P'}, + {label: gettext('Procedure'), value: 'p'}, + {label: gettext('Public Synonym'), value: 's'}, + {label: gettext('Sequence'), value: 'S'}, + {label: gettext('Table'), value: 'r'}, + {label: gettext('View'), value: 'v'} + ] + }, { + id: 'synobjschema', label: gettext('Target schema'), + type: 'select', mode: ['properties', 'create', 'edit'], + group: gettext('Definition'), deps: ['targettype'], + controlProps: { allowClear: false}, + options: this.fieldOptions.synobjschema, + readonly: function(state) { + // If tagetType is synonym then disable it + if(!obj.inSchema()) { + if(state.targettype == 's') { + return true; + } else { + return false; + } + } + return true; + }, + depChange: function(state) { + if(!obj.inSchema() && state.targettype == 's') { + return { synobjschema: 'public'}; + } + } + }, { + id: 'synobjname', label: gettext('Target object'), + group: gettext('Definition'), + deps: ['targettype', 'synobjschema'], + type: (state)=>{ + let fetchOptionsBasis = state.targettype + state.synobjschema; + return { + type: 'select', + options: ()=>obj.fieldOptions.getTargetObjectOptions(state.targettype, state.synobjschema), + optionsReloadBasis: fetchOptionsBasis, + }; + }, + readonly: function() { + if(!obj.inSchema()) { + return false; + } + return true; + } + }, { + id: 'is_sys_obj', label: gettext('System synonym?'), + cell:'boolean', type: 'switch', mode: ['properties'], + } + ]; + } + + validate(state, setError) { + let errmsg = null; + + errmsg = emptyValidator('Name', state.name); + if (errmsg) { + setError('name', errmsg); + return true; + } else { + setError('name', errmsg); + } + + errmsg = emptyValidator('Target schema', state.synobjschema); + if (errmsg) { + setError('synobjschema', errmsg); + return true; + } else { + setError('synobjschema', errmsg); + } + + errmsg = emptyValidator('Target object', state.synobjname); + if (errmsg) { + setError('synobjname', errmsg); + return true; + } else { + setError('synobjname', errmsg); + } + } + + inSchema() { + if(this.nodeInfo && 'catalog' in this.nodeInfo) + { + return true; + } + return false; + } +} diff --git a/web/pgadmin/browser/static/js/node_ajax.js b/web/pgadmin/browser/static/js/node_ajax.js index c2d390f3c..7594a0813 100644 --- a/web/pgadmin/browser/static/js/node_ajax.js +++ b/web/pgadmin/browser/static/js/node_ajax.js @@ -64,6 +64,7 @@ export function getNodeAjaxOptions(url, nodeObj, treeNodeInfo, itemNodeData, par let otherParams = { urlWithId: false, jumpAfterNode: null, + useCache: true, ...params }; return new Promise((resolve, reject)=>{ @@ -91,7 +92,7 @@ export function getNodeAjaxOptions(url, nodeObj, treeNodeInfo, itemNodeData, par }) .then((res)=>{ data = res.data.data; - cacheNode.cache(nodeObj.type + '#' + url, treeNodeInfo, cacheLevel, data); + otherParams.useCache && cacheNode.cache(nodeObj.type + '#' + url, treeNodeInfo, cacheLevel, data); resolve(transform(data)); }) .catch((err)=>{ diff --git a/web/pgadmin/static/js/SchemaView/FormView.jsx b/web/pgadmin/static/js/SchemaView/FormView.jsx index 58b752de4..020e54efe 100644 --- a/web/pgadmin/static/js/SchemaView/FormView.jsx +++ b/web/pgadmin/static/js/SchemaView/FormView.jsx @@ -116,9 +116,7 @@ export default function FormView({ visible = _.isUndefined(visible) ? true : visible; let _visible = true; - if(visible) { - _visible = evalFunc(schema, visible, value); - } + _visible = evalFunc(schema, visible, value); _visible = _visible && verInLimit; disabled = evalFunc(schema, disabled, value); diff --git a/web/pgadmin/static/js/SchemaView/index.jsx b/web/pgadmin/static/js/SchemaView/index.jsx index 4cbdbc705..b66e2f9be 100644 --- a/web/pgadmin/static/js/SchemaView/index.jsx +++ b/web/pgadmin/static/js/SchemaView/index.jsx @@ -597,6 +597,7 @@ function SchemaPropertiesView({ { + let mount; + let schemaObj = new SynonymSchema( + { + role: ()=>[], + schema: ()=>[], + synobjschema: ()=>[], + getTargetObjectOptions: ()=>[], + }, + [], + { + owner: 'postgres', + schema: 'public', + synobjschema: 'public', + } + ); + 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.name = null; + schemaObj.validate(state, setError); + expect(setError).toHaveBeenCalledWith('name', '\'Name\' cannot be empty.'); + + state.name = 'my_syn'; + state.synobjschema = null; + schemaObj.validate(state, setError); + expect(setError).toHaveBeenCalledWith('synobjschema', '\'Target schema\' cannot be empty.'); + + state.synobjschema = 'public'; + state.synobjname = null; + schemaObj.validate(state, setError); + expect(setError).toHaveBeenCalledWith('synobjname', '\'Target object\' cannot be empty.'); + }); +}); +