diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/static/js/mview.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/static/js/mview.js
index ad5d7b2ec..ce36dde36 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/static/js/mview.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/static/js/mview.js
@@ -7,6 +7,11 @@
//
//////////////////////////////////////////////////////////////
+import MViewSchema from './mview.ui';
+import { getNodeListByName } from '../../../../../../../static/js/node_ajax';
+import { getNodePrivilegeRoleSchema } from '../../../../../static/js/privilege.ui';
+import { getNodeVacuumSettingsSchema } from '../../../../../static/js/vacuum.ui';
+
define('pgadmin.node.mview', [
'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
'sources/pgadmin', 'pgadmin.alertifyjs', 'pgadmin.browser',
@@ -122,7 +127,23 @@ define('pgadmin.node.mview', [
icon: 'fa fa-sync-alt',
}]);
},
-
+ getSchema: function(treeNodeInfo, itemNodeData) {
+ return new MViewSchema(
+ (privileges)=>getNodePrivilegeRoleSchema('', treeNodeInfo, itemNodeData, privileges),
+ ()=>getNodeVacuumSettingsSchema(this, treeNodeInfo, itemNodeData),
+ {
+ role: ()=>getNodeListByName('role', treeNodeInfo, itemNodeData),
+ schema: ()=>getNodeListByName('schema', treeNodeInfo, itemNodeData, {cacheLevel: 'database'}),
+ spcname: ()=>getNodeListByName('tablespace', treeNodeInfo, itemNodeData, {}, (m)=> {
+ return (m.label != 'pg_global');
+ }),
+ },
+ {
+ owner: pgBrowser.serverInfo[treeNodeInfo.server._id].user.name,
+ schema: treeNodeInfo.schema.label
+ }
+ );
+ },
/**
Define model for the view node and specify the
properties of the model in schema.
@@ -142,9 +163,6 @@ define('pgadmin.node.mview', [
},
defaults: {
spcname: undefined,
- toast_autovacuum_enabled: 'x',
- autovacuum_enabled: 'x',
- warn_text: undefined,
},
schema: [{
id: 'name', label: gettext('Name'), cell: 'string',
@@ -156,81 +174,9 @@ define('pgadmin.node.mview', [
id: 'owner', label: gettext('Owner'), cell: 'string',
control: 'node-list-by-name', select2: { allowClear: false },
node: 'role', disabled: 'inSchema',
- },{
- id: 'schema', label: gettext('Schema'), cell: 'string', first_empty: false,
- control: 'node-list-by-name', type: 'text', cache_level: 'database',
- node: 'schema', mode: ['create', 'edit'], cache_node: 'database',
- disabled: 'inSchema', select2: { allowClear: false },
- },{
- id: 'system_view', label: gettext('System materialized view?'), cell: 'string',
- type: 'switch', mode: ['properties'],
- }, pgBrowser.SecurityGroupSchema, {
- id: 'acl', label: gettext('Privileges'),
- mode: ['properties'], type: 'text', group: gettext('Security'),
},{
id: 'comment', label: gettext('Comment'), cell: 'string',
type: 'multiline',
- },{
- id: 'definition', label: gettext('Definition'), cell: 'string',
- type: 'text', mode: ['create', 'edit'], group: gettext('Definition'),
- tabPanelCodeClass: 'sql-code-control',
- control: Backform.SqlCodeControl.extend({
- onChange: function() {
- Backform.SqlCodeControl.prototype.onChange.apply(this, arguments);
- if(this.model && this.model.changed) {
- if(this.model.origSessAttrs && (this.model.changed.definition != this.model.origSessAttrs.definition)) {
- this.model.warn_text = gettext(
- 'Updating the definition will drop and re-create the materialized view. It may result in loss of information about its dependent objects.'
- ) + '
' + gettext('Do you want to continue?') +
- '';
- }
- else {
- this.model.warn_text = undefined;
- }
- }
- else {
- this.model.warn_text = undefined;
- }
- },
- }),
- },{
- id: 'with_data', label: gettext('With data?'),
- group: gettext('Storage'), mode: ['edit', 'create'],
- type: 'switch',
- },{
- id: 'spcname', label: gettext('Tablespace'), cell: 'string',
- type: 'text', group: gettext('Storage'), first_empty: false,
- control: 'node-list-by-name', node: 'tablespace', select2: { allowClear: false },
- filter: function(m) {
- return (m.label != 'pg_global');
- },
- },{
- id: 'fillfactor', label: gettext('Fill factor'),
- group: gettext('Storage'), mode: ['edit', 'create'],
- type: 'int', min: 10, max: 100,
- },{
- id: 'vacuum_settings_str', label: gettext('Storage settings'),
- type: 'multiline', group: gettext('Storage'), mode: ['properties'],
- },{
- type: 'nested', control: 'tab', id: 'materialization',
- label: gettext('Parameter'), mode: ['edit', 'create'],
- group: gettext('Parameter'),
- schema: Backform.VacuumSettingsSchema,
- },{
- // Add Privilege Control
- id: 'datacl', label: gettext('Privileges'), type: 'collection',
- model: pgBrowser.Node.PrivilegeRoleModel.extend({
- privileges: ['a', 'r', 'w', 'd', 'D', 'x', 't'],
- }), uniqueCol : ['grantee'], editable: false,
- group: 'security', canAdd: true, canDelete: true,
- mode: ['edit', 'create'], control: 'unique-col-collection',
- },{
- // Add Security Labels Control
- id: 'seclabels', label: gettext('Security labels'),
- model: pgBrowser.SecLabelModel, editable: false, type: 'collection',
- canEdit: false, group: 'security', canDelete: true,
- mode: ['edit', 'create'], canAdd: true,
- control: 'unique-col-collection', uniqueCol : ['provider'],
}],
sessChanged: function() {
/* If only custom autovacuum option is enabled the check if the options table is also changed. */
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/static/js/mview.ui.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/static/js/mview.ui.js
new file mode 100644
index 000000000..f7a52d7ac
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/static/js/mview.ui.js
@@ -0,0 +1,179 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2021, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import gettext from 'sources/gettext';
+import BaseUISchema from 'sources/SchemaView/base_schema.ui';
+import SecLabelSchema from '../../../../../static/js/sec_label.ui';
+import { isEmptyString } from 'sources/validators';
+
+
+export default class MViewSchema extends BaseUISchema {
+ constructor(getPrivilegeRoleSchema, getVacuumSettingsSchema, fieldOptions={}, initValues) {
+ super({
+ spcname: undefined,
+ toast_autovacuum_enabled: 'x',
+ autovacuum_enabled: 'x',
+ warn_text: undefined,
+ ...initValues
+ });
+ this.getPrivilegeRoleSchema = getPrivilegeRoleSchema;
+ this.getVacuumSettingsSchema = getVacuumSettingsSchema;
+ this.fieldOptions = {
+ role: [],
+ schema: [],
+ spcname: [],
+ nodeInfo: null,
+ ...fieldOptions,
+ };
+
+ this.nodeInfo = this.fieldOptions.nodeInfo;
+ }
+
+ get idAttribute() {
+ return 'oid';
+ }
+
+ inSchema() {
+ if(this.nodeInfo && 'catalog' in this.nodeInfo)
+ {
+ return true;
+ }
+ return false;
+ }
+
+
+ get baseFields() {
+ let obj = this;
+ return [
+ {
+ id: 'name', label: gettext('Name'), cell: 'text',
+ type: 'text', disabled: obj.inSchema, noEmpty: true,
+ },{
+ id: 'oid', label: gettext('OID'), cell: 'text',
+ type: 'text', mode: ['properties'],
+ },{
+ id: 'owner', label: gettext('Owner'),
+ type: 'select', cell: 'text',
+ options: obj.fieldOptions.role, controlProps: { allowClear: false },
+ disabled: obj.inSchema,
+ },{
+ id: 'schema', label: gettext('Schema'), cell: 'text',
+ type: 'select', options: obj.fieldOptions.schema, mode: ['create', 'edit'],
+ cache_node: 'database', disabled: obj.inSchema,
+ controlProps: {
+ allowClear: false,
+ first_empty: false
+ },
+ },{
+ id: 'system_view', label: gettext('System materialized view?'), cell: 'text',
+ type: 'switch', mode: ['properties'],
+ },
+ {
+ id: 'acl', label: gettext('Privileges'),
+ mode: ['properties'], type: 'text', group: gettext('Security'),
+ },{
+ id: 'comment', label: gettext('Comment'), cell: 'text',
+ type: 'multiline',
+ },{
+ id: 'definition', label: gettext('Definition'), cell: 'text',
+ type: 'sql', mode: ['create', 'edit'], group: gettext('Definition'),
+ isFullTab: true, controlProps: { readOnly: this.nodeInfo && 'catalog' in this.nodeInfo ? true: false },
+ },{
+ id: 'with_data', label: gettext('With data?'),
+ group: gettext('Storage'), mode: ['edit', 'create'],
+ type: 'switch',
+ },{
+ id: 'spcname', label: gettext('Tablespace'), cell: 'text',
+ type: 'select', group: gettext('Storage'),
+ options: obj.fieldOptions.spcname,
+ controlProps: {
+ allowClear: false,
+ first_empty: false,
+ },
+ },{
+ id: 'fillfactor', label: gettext('Fill factor'),
+ group: gettext('Storage'), mode: ['edit', 'create'],
+ noEmpty: false, type: 'int', controlProps: {min: 10, max: 100}
+ },{
+ id: 'vacuum_settings_str', label: gettext('Storage settings'),
+ type: 'multiline', group: gettext('Storage'), mode: ['properties'],
+ },
+ {
+ type: 'nested-tab', group: gettext('Parameter'), mode: ['create', 'edit'],
+ schema: this.getVacuumSettingsSchema(),
+ },
+ /*{
+ type: 'nested', control: 'tab', id: 'materialization',
+ label: gettext('Parameter'), mode: ['edit', 'create'],
+ group: gettext('Parameter'),
+ schema: Backform.VacuumSettingsSchema,
+ },
+ {
+ // Add Privilege Control
+ id: 'datacl', label: gettext('Privileges'), type: 'collection',
+ model: pgBrowser.Node.PrivilegeRoleModel.extend({
+ privileges: ['a', 'r', 'w', 'd', 'D', 'x', 't'],
+ }), uniqueCol : ['grantee'], editable: false,
+ group: 'security', canAdd: true, canDelete: true,
+ mode: ['edit', 'create'], control: 'unique-col-collection',
+ },*/
+ {
+ id: 'datacl', label: gettext('Privileges'), type: 'collection',
+ schema: this.getPrivilegeRoleSchema(['U']),
+ uniqueCol : ['grantee'],
+ editable: false,
+ group: gettext('Security'), mode: ['edit', 'create'],
+ canAdd: true, canDelete: true,
+ },
+ {
+ // Add Security Labels Control
+ id: 'seclabels', label: gettext('Security labels'),
+ schema: new SecLabelSchema(),
+ editable: false, type: 'collection',
+ canEdit: false, group: gettext('Security'), canDelete: true,
+ mode: ['edit', 'create'], canAdd: true,
+ control: 'unique-col-collection',
+ uniqueCol : ['provider'],
+ }
+ ];
+ }
+
+ validate(state, setError) {
+ let errmsg = null;
+ let obj = this;
+
+ if (isEmptyString(state.service)) {
+
+ /* mview definition validation*/
+ if (isEmptyString(state.definition)) {
+ errmsg = gettext('Please enter view definition.');
+ setError('definition', errmsg);
+ return true;
+ } else {
+ errmsg = null;
+ setError('definition', errmsg);
+ }
+
+ if (state.definition) {
+ obj.warningText = null;
+ if (obj.origData.oid !== undefined && state.definition !== obj.origData.definition) {
+ obj.warningText = gettext(
+ 'Updating the definition will drop and re-create the materialized view. It may result in loss of information about its dependent objects.'
+ ) + '
' + gettext('Do you want to continue?') + '';
+ }
+ }
+ return false;
+ } else {
+ errmsg = null;
+ _.each(['definition'], (item) => {
+ setError(item, errmsg);
+ });
+ }
+ }
+}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/static/js/view.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/static/js/view.js
index 2f196ef63..4b38ff7fe 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/static/js/view.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/static/js/view.js
@@ -104,7 +104,7 @@ define('pgadmin.node.view', [
},
{
owner: pgBrowser.serverInfo[treeNodeInfo.server._id].user.name,
- schema: treeNodeInfo.schema.label
+ schema: treeNodeInfo.schema ? treeNodeInfo.schema.label : ''
}
);
},
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/static/js/view.ui.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/static/js/view.ui.js
index 136d68d0d..89cdf8c23 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/static/js/view.ui.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/static/js/view.ui.js
@@ -36,7 +36,7 @@ export default class ViewSchema extends BaseUISchema {
}
notInSchema() {
- if(this.node_info && 'catalog' in this.node_info) {
+ if(this.nodeInfo && 'catalog' in this.nodeInfo) {
return true;
}
return false;
@@ -96,7 +96,7 @@ export default class ViewSchema extends BaseUISchema {
id: 'definition', label: gettext('Code'), cell: 'text',
type: 'sql', mode: ['create', 'edit'], group: gettext('Code'),
isFullTab: true,
- disabled: obj.notInSchema,
+ controlProps: { readOnly: obj.nodeInfo && 'catalog' in obj.nodeInfo ? true: false },
},
{
@@ -142,7 +142,7 @@ export default class ViewSchema extends BaseUISchema {
state.definition !== obj.origData.definition
)) {
obj.warningText = null;
- return true;
+ return false;
}
let old_def = obj.origData.definition &&
@@ -169,7 +169,7 @@ export default class ViewSchema extends BaseUISchema {
} else {
obj.warningText = null;
}
- return true;
+ return false;
}
} else {
diff --git a/web/regression/javascript/schema_ui_files/mview.ui.spec.js b/web/regression/javascript/schema_ui_files/mview.ui.spec.js
new file mode 100644
index 000000000..66a9b48c0
--- /dev/null
+++ b/web/regression/javascript/schema_ui_files/mview.ui.spec.js
@@ -0,0 +1,135 @@
+/////////////////////////////////////////////////////////////
+//
+// 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 MViewSchema from '../../../pgadmin/browser/server_groups/servers/databases/schemas/views/static/js/mview.ui';
+
+
+class MockSchema extends BaseUISchema {
+ get baseFields() {
+ return [];
+ }
+}
+
+describe('MaterializedViewSchema', ()=>{
+ let mount;
+ let schemaObj = new MViewSchema(
+ ()=>new MockSchema(),
+ ()=>new MockSchema(),
+ {
+ role: ()=>[],
+ schema: ()=>[],
+ spcname: ()=>[],
+ },
+ {
+ owner: '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.definition = null;
+ schemaObj.validate(state, setError);
+ expect(setError).toHaveBeenCalledWith('definition', 'Please enter view definition.');
+
+ state.definition = 'SELECT 1;';
+ schemaObj.validate(state, setError);
+ expect(setError).toHaveBeenCalledWith('definition', null);
+
+ state.definition = 'SELECT 1';
+ schemaObj.validate(state, setError);
+ expect(setError).toHaveBeenCalledWith('definition', null);
+
+ state.service = 'Test';
+ state.definition = 'SELECT 1';
+ schemaObj.validate(state, setError);
+ expect(setError).toHaveBeenCalledWith('definition', null);
+
+ });
+});
+
diff --git a/web/regression/javascript/schema_ui_files/view.ui.spec.js b/web/regression/javascript/schema_ui_files/view.ui.spec.js
index d801b1c0b..244a55390 100644
--- a/web/regression/javascript/schema_ui_files/view.ui.spec.js
+++ b/web/regression/javascript/schema_ui_files/view.ui.spec.js
@@ -15,7 +15,7 @@ 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 ViewSchema from '../../../pgadmin/browser/server_groups/servers/databases/schemas/views/static/js/view.ui.js';
+import ViewSchema from '../../../pgadmin/browser/server_groups/servers/databases/schemas/views/static/js/view.ui';
class MockSchema extends BaseUISchema {