diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/sequences/static/js/sequence.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/sequences/static/js/sequence.js index 8b1b8083a..9c6418cb3 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/sequences/static/js/sequence.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/sequences/static/js/sequence.js @@ -7,6 +7,10 @@ // ////////////////////////////////////////////////////////////// +import { getNodeListByName } from '../../../../../../../static/js/node_ajax'; +import { getNodePrivilegeRoleSchema } from '../../../../../static/js/privilege.ui'; +import SequenceSchema from './sequence.ui'; + define('pgadmin.node.sequence', [ 'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'sources/pgadmin', 'pgadmin.browser', 'pgadmin.backform', @@ -72,26 +76,30 @@ define('pgadmin.node.sequence', [ ]); }, + + getSchema: function(treeNodeInfo, itemNodeData) { + return new SequenceSchema( + (privileges)=>getNodePrivilegeRoleSchema(this, treeNodeInfo, itemNodeData, privileges), + { + role: ()=>getNodeListByName('role', treeNodeInfo, itemNodeData), + schema: ()=>getNodeListByName('schema', treeNodeInfo, itemNodeData, {}, (m)=>{ + // If schema name start with pg_* then we need to exclude them + if (m.label.match(/^pg_/)) { + return false; + } + return true; + }), + }, + { + seqowner: pgBrowser.serverInfo[treeNodeInfo.server._id].user.name, + schema: itemNodeData.label, + } + ); + }, + // Define the model for sequence node. model: pgBrowser.Node.Model.extend({ idAttribute: 'oid', - defaults: { - name: undefined, - oid: undefined, - seqowner: undefined, - schema: undefined, - is_sys_obj: undefined, - comment: undefined, - increment: undefined, - start: undefined, - current_value: undefined, - minimum: undefined, - maximum: undefined, - cache: undefined, - cycled: undefined, - relacl: [], - securities: [], - }, // Default values! initialize: function(attrs, args) { @@ -118,176 +126,10 @@ define('pgadmin.node.sequence', [ id: 'seqowner', label: gettext('Owner'), cell: 'string', type: 'text', mode: ['properties', 'create', 'edit'], node: 'role', control: Backform.NodeListByNameControl, - },{ - id: 'schema', label: gettext('Schema'), cell: 'string', - control: 'node-list-by-name', node: 'schema', - type: 'text', mode: ['create', 'edit'], filter: function(d) { - // If schema name start with pg_* then we need to exclude them - if(d && d.label.match(/^pg_/)) - { - return false; - } - return true; - }, cache_node: 'database', cache_level: 'database', - },{ - id: 'is_sys_obj', label: gettext('System sequence?'), - cell:'boolean', type: 'switch', mode: ['properties'], },{ id: 'comment', label: gettext('Comment'), type: 'multiline', mode: ['properties', 'create', 'edit'], - },{ - id: 'current_value', label: gettext('Current value'), type: 'int', - mode: ['properties', 'edit'], group: gettext('Definition'), - },{ - id: 'increment', label: gettext('Increment'), type: 'int', - mode: ['properties', 'create', 'edit'], group: gettext('Definition'), - },{ - id: 'start', label: gettext('Start'), type: 'int', - mode: ['properties', 'create', 'edit'], group: gettext('Definition'), - },{ - id: 'minimum', label: gettext('Minimum'), type: 'int', - mode: ['properties', 'create', 'edit'], group: gettext('Definition'), - },{ - id: 'maximum', label: gettext('Maximum'), type: 'int', - mode: ['properties', 'create', 'edit'], group: gettext('Definition'), - },{ - id: 'cache', label: gettext('Cache'), type: 'int', - mode: ['properties', 'create', 'edit'], group: gettext('Definition'), - min: 1, - },{ - id: 'cycled', label: gettext('Cycled'), type: 'switch', - mode: ['properties', 'create', 'edit'], group: gettext('Definition'), - }, pgBrowser.SecurityGroupSchema,{ - id: 'acl', label: gettext('Privileges'), type: 'text', - group: gettext('Security'), mode: ['properties'], - },{ - id: 'relacl', label: gettext('Privileges'), group: 'security', - model: pgBrowser.Node.PrivilegeRoleModel.extend({ - privileges: ['r', 'w', 'U'], - }), uniqueCol : ['grantee', 'grantor'], mode: ['edit', 'create'], - editable: false, type: 'collection', canAdd: true, canDelete: true, - control: 'unique-col-collection', - },{ - id: 'securities', label: gettext('Security labels'), canAdd: true, - model: pgBrowser.SecLabelModel, editable: false, - type: 'collection', canEdit: false, group: 'security', - mode: ['edit', 'create'], canDelete: true, - control: 'unique-col-collection', - min_version: 90200, uniqueCol : ['provider'], }], - /* validate function is used to validate the input given by - * the user. In case of error, message will be displayed on - * the GUI for the respective control. - */ - validate: function() { - var msg = undefined, - minimum = this.get('minimum'), - maximum = this.get('maximum'), - start = this.get('start'); - - if (_.isUndefined(this.get('name')) - || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') { - msg = gettext('Name cannot be empty.'); - this.errorModel.set('name', msg); - return msg; - } else { - this.errorModel.unset('name'); - } - - if (_.isUndefined(this.get('seqowner')) - || String(this.get('seqowner')).replace(/^\s+|\s+$/g, '') == '') { - msg = gettext('Owner cannot be empty.'); - this.errorModel.set('seqowner', msg); - return msg; - } else { - this.errorModel.unset('seqowner'); - } - - if (_.isUndefined(this.get('schema')) - || String(this.get('schema')).replace(/^\s+|\s+$/g, '') == '') { - msg = gettext('Schema cannot be empty.'); - this.errorModel.set('schema', msg); - return msg; - } else { - this.errorModel.unset('schema'); - } - - if (!this.isNew()) { - if (_.isUndefined(this.get('current_value')) - || String(this.get('current_value')).replace(/^\s+|\s+$/g, '') == '') { - msg = gettext('Current value cannot be empty.'); - this.errorModel.set('current_value', msg); - return msg; - } else { - this.errorModel.unset('current_value'); - } - - if (_.isUndefined(this.get('increment')) - || String(this.get('increment')).replace(/^\s+|\s+$/g, '') == '') { - msg = gettext('Increment value cannot be empty.'); - this.errorModel.set('increment', msg); - return msg; - } else { - this.errorModel.unset('increment'); - } - - if (_.isUndefined(this.get('minimum')) - || String(this.get('minimum')).replace(/^\s+|\s+$/g, '') == '') { - msg = gettext('Minimum value cannot be empty.'); - this.errorModel.set('minimum', msg); - return msg; - } else { - this.errorModel.unset('minimum'); - } - - if (_.isUndefined(this.get('maximum')) - || String(this.get('maximum')).replace(/^\s+|\s+$/g, '') == '') { - msg = gettext('Maximum value cannot be empty.'); - this.errorModel.set('maximum', msg); - return msg; - } else { - this.errorModel.unset('maximum'); - } - - if (_.isUndefined(this.get('cache')) - || String(this.get('cache')).replace(/^\s+|\s+$/g, '') == '') { - msg = gettext('Cache value cannot be empty.'); - this.errorModel.set('cache', msg); - return msg; - } else { - this.errorModel.unset('cache'); - } - } - var min_lt = gettext('Minimum value must be less than maximum value.'), - start_lt = gettext('Start value cannot be less than minimum value.'), - start_gt = gettext('Start value cannot be greater than maximum value.'); - - if (_.isEmpty(minimum) || _.isEmpty(maximum)) - return null; - - if ((minimum == 0 && maximum == 0) || - (parseInt(minimum, 10) >= parseInt(maximum, 10))) { - this.errorModel.set('minimum', min_lt); - return min_lt; - } else { - this.errorModel.unset('minimum'); - } - - if (start && minimum && parseInt(start) < parseInt(minimum)) { - this.errorModel.set('start', start_lt); - return start_lt; - } else { - this.errorModel.unset('start'); - } - - if (start && maximum && parseInt(start) > parseInt(maximum)) { - this.errorModel.set('start', start_gt); - return start_gt; - } else { - this.errorModel.unset('start'); - } - return null; - }, }), }); } diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/sequences/static/js/sequence.ui.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/sequences/static/js/sequence.ui.js new file mode 100644 index 000000000..d9f0f7bb8 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/sequences/static/js/sequence.ui.js @@ -0,0 +1,211 @@ +///////////////////////////////////////////////////////////// +// +// pgAdmin 4 - PostgreSQL Tools +// +// Copyright (C) 2013 - 2021, The pgAdmin Development Team +// This software is released under the PostgreSQL Licence +// +////////////////////////////////////////////////////////////// + +import gettext from 'sources/gettext'; +import BaseUISchema from 'sources/SchemaView/base_schema.ui'; +import SecLabelSchema from '../../../../../static/js/sec_label.ui'; +import { emptyValidator, isEmptyString } from '../../../../../../../../static/js/validators'; + +export default class SequenceSchema extends BaseUISchema { + constructor(getPrivilegeRoleSchema, fieldOptions={}, initValues) { + super({ + name: undefined, + oid: undefined, + seqowner: undefined, + schema: undefined, + is_sys_obj: undefined, + comment: undefined, + increment: undefined, + start: undefined, + current_value: undefined, + minimum: undefined, + maximum: undefined, + cache: undefined, + cycled: undefined, + relacl: [], + securities: [], + ...initValues, + }); + this.getPrivilegeRoleSchema = getPrivilegeRoleSchema; + this.fieldOptions = { + role: [], + schema: [], + ...fieldOptions, + }; + } + + get idAttribute() { + return 'oid'; + } + + get baseFields() { + 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: 'seqowner', label: gettext('Owner'), + editable: false, type: 'select', options: this.fieldOptions.role, + controlProps: { allowClear: false } + }, { + id: 'schema', label: gettext('Schema'), + editable: false, type: 'select', options: this.fieldOptions.schema, + controlProps: { allowClear: false }, + mode: ['create', 'edit'], + cache_node: 'database', cache_level: 'database', + }, { + id: 'is_sys_obj', label: gettext('System sequence?'), + cell:'boolean', type: 'switch', mode: ['properties'], + }, { + id: 'comment', label: gettext('Comment'), type: 'multiline', + mode: ['properties', 'create', 'edit'], + }, { + id: 'current_value', label: gettext('Current value'), type: 'int', + mode: ['properties', 'edit'], group: gettext('Definition'), + }, { + id: 'increment', label: gettext('Increment'), type: 'int', + mode: ['properties', 'create', 'edit'], group: gettext('Definition'), + }, { + id: 'start', label: gettext('Start'), type: 'int', + mode: ['properties', 'create', 'edit'], group: gettext('Definition'), + }, { + id: 'minimum', label: gettext('Minimum'), type: 'int', + mode: ['properties', 'create', 'edit'], group: gettext('Definition'), + }, { + id: 'maximum', label: gettext('Maximum'), type: 'int', + mode: ['properties', 'create', 'edit'], group: gettext('Definition'), + }, { + id: 'cache', label: gettext('Cache'), type: 'int', + mode: ['properties', 'create', 'edit'], group: gettext('Definition'), + min: 1, + }, { + id: 'cycled', label: gettext('Cycled'), type: 'switch', + mode: ['properties', 'create', 'edit'], group: gettext('Definition'), + }, { + id: 'acl', label: gettext('Privileges'), type: 'text', + group: gettext('Security'), mode: ['properties'], + }, { + id: 'relacl', label: gettext('Privileges'), group: gettext('Security'), type: 'collection', + schema: this.getPrivilegeRoleSchema(['r', 'w', 'U']), + uniqueCol : ['grantee', 'grantor'], mode: ['edit', 'create'], + canAdd: true, canDelete: true, + }, { + id: 'securities', label: gettext('Security labels'), type: 'collection', + editable: false, group: gettext('Security'), + schema: new SecLabelSchema(), + mode: ['edit', 'create'], + canAdd: true, canEdit: false, canDelete: true, + uniqueCol : ['provider'], + min_version: 90200, + } + ]; + } + /* validate function is used to validate the input given by + * the user. In case of error, message will be displayed on + * the GUI for the respective control. + */ + validate(state, setError) { + let errmsg = null, + minimum = state.minimum, + maximum = state.maximum, + start = state.start; + + errmsg = emptyValidator('Owner', state.seqowner); + if (errmsg) { + setError('seqowner', errmsg); + return true; + } else { + setError('seqowner', errmsg); + } + + errmsg = emptyValidator('Schema', state.schema); + if (errmsg) { + setError('schema', errmsg); + return true; + } else { + setError('schema', errmsg); + } + + if (!this.isNew(state)) { + errmsg = emptyValidator('Current value', state.current_value); + if (errmsg) { + setError('current_value', errmsg); + return true; + } else { + setError('current_value', errmsg); + } + + errmsg = emptyValidator('Increment value', state.increment); + if (errmsg) { + setError('increment', errmsg); + return true; + } else { + setError('increment', errmsg); + } + + errmsg = emptyValidator('Minimum value', state.minimum); + if (errmsg) { + setError('minimum', errmsg); + return true; + } else { + setError('minimum', errmsg); + } + + errmsg = emptyValidator('Maximum value', state.maximum); + if (errmsg) { + setError('maximum', errmsg); + return true; + } else { + setError('maximum', errmsg); + } + + errmsg = emptyValidator('Cache value', state.cache); + if (errmsg) { + setError('cache', errmsg); + return true; + } else { + setError('cache', errmsg); + } + } + + var min_lt = gettext('Minimum value must be less than maximum value.'), + start_lt = gettext('Start value cannot be less than minimum value.'), + start_gt = gettext('Start value cannot be greater than maximum value.'); + + if (isEmptyString(minimum) || isEmptyString(maximum)) + return null; + + if ((minimum == 0 && maximum == 0) || + (parseInt(minimum, 10) >= parseInt(maximum, 10))) { + setError('minimum', min_lt); + return true; + } else { + setError('minimum', null); + } + + if (start && minimum && parseInt(start) < parseInt(minimum)) { + setError('start', start_lt); + return true; + } else { + setError('start', null); + } + + if (start && maximum && parseInt(start) > parseInt(maximum)) { + setError('start', start_gt); + return true; + } else { + setError('start', null); + } + return null; + } +} diff --git a/web/regression/javascript/schema_ui_files/sequence.ui.spec.js b/web/regression/javascript/schema_ui_files/sequence.ui.spec.js new file mode 100644 index 000000000..e60ab5112 --- /dev/null +++ b/web/regression/javascript/schema_ui_files/sequence.ui.spec.js @@ -0,0 +1,165 @@ +///////////////////////////////////////////////////////////// +// +// 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 SequenceSchema from '../../../pgadmin/browser/server_groups/servers/databases/schemas/sequences/static/js/sequence.ui'; + +class MockSchema extends BaseUISchema { + get baseFields() { + return []; + } +} + +describe('SequenceSchema', ()=>{ + let mount; + let schemaObj = new SequenceSchema( + ()=>new MockSchema(), + { + role: ()=>[], + }, + { + seqowner: 'postgres', + schema: '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.seqowner = null; + schemaObj.validate(state, setError); + expect(setError).toHaveBeenCalledWith('seqowner', '\'Owner\' cannot be empty.'); + + state.seqowner = 'postgres'; + state.schema = null; + schemaObj.validate(state, setError); + expect(setError).toHaveBeenCalledWith('schema', '\'Schema\' cannot be empty.'); + + state.schema = 'public'; + state.oid = 12345; + state.current_value = null; + schemaObj.validate(state, setError); + expect(setError).toHaveBeenCalledWith('current_value', '\'Current value\' cannot be empty.'); + + state.current_value = 10; + state.increment = null; + schemaObj.validate(state, setError); + expect(setError).toHaveBeenCalledWith('increment', '\'Increment value\' cannot be empty.'); + + + state.increment = 1; + state.minimum = null; + schemaObj.validate(state, setError); + expect(setError).toHaveBeenCalledWith('minimum', '\'Minimum value\' cannot be empty.'); + + state.minimum = 5; + state.maximum = null; + schemaObj.validate(state, setError); + expect(setError).toHaveBeenCalledWith('maximum', '\'Maximum value\' cannot be empty.'); + + state.maximum = 200; + state.cache = null; + schemaObj.validate(state, setError); + expect(setError).toHaveBeenCalledWith('cache', '\'Cache value\' cannot be empty.'); + + state.cache = 1; + state.minimum = 10; + state.maximum = 5; + schemaObj.validate(state, setError); + expect(setError).toHaveBeenCalledWith('minimum', 'Minimum value must be less than maximum value.'); + + state.start = 5; + state.minimum = 10; + state.maximum = 50; + schemaObj.validate(state, setError); + expect(setError).toHaveBeenCalledWith('start', 'Start value cannot be less than minimum value.'); + + state.start = 500; + schemaObj.validate(state, setError); + expect(setError).toHaveBeenCalledWith('start', 'Start value cannot be greater than maximum value.'); + }); +}); +