- Add VaccumSettings schema. - Allow collection to have fixed rows. - Changes in data change comparison and add state utils context. - Fixed jasmine test cases.
parent
eb48765a5a
commit
377fe80046
|
@ -0,0 +1,179 @@
|
||||||
|
import gettext from 'sources/gettext';
|
||||||
|
import BaseUISchema from 'sources/SchemaView/base_schema.ui';
|
||||||
|
import { getNodeAjaxOptions } from '../../../../static/js/node_ajax';
|
||||||
|
|
||||||
|
export function getNodeVacuumSettingsSchema(nodeObj, treeNodeInfo, itemNodeData) {
|
||||||
|
let tableVacuumRows = ()=>getNodeAjaxOptions('get_table_vacuum', nodeObj, treeNodeInfo, itemNodeData, {noCache: true});
|
||||||
|
let toastTableVacuumRows = ()=>getNodeAjaxOptions('get_toast_table_vacuum', nodeObj, treeNodeInfo, itemNodeData, {noCache: true});
|
||||||
|
return new VacuumSettingsSchema(tableVacuumRows, toastTableVacuumRows, treeNodeInfo);
|
||||||
|
}
|
||||||
|
export class VacuumTableSchema extends BaseUISchema {
|
||||||
|
constructor(valueDep) {
|
||||||
|
super();
|
||||||
|
this.valueDep = valueDep;
|
||||||
|
}
|
||||||
|
|
||||||
|
get baseFields() {
|
||||||
|
let obj = this;
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
id: 'label', name: 'label', label: gettext('Label'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'value', name: 'value', label: gettext('Value'),
|
||||||
|
type: 'text', deps: [[this.valueDep]],
|
||||||
|
editable: function() {
|
||||||
|
return obj.top.sessData[this.valueDep];
|
||||||
|
},
|
||||||
|
cell: (state)=>{
|
||||||
|
switch(state.column_type) {
|
||||||
|
case 'integer':
|
||||||
|
return {cell: 'int'};
|
||||||
|
case 'number':
|
||||||
|
return {cell: 'numeric', controlProps: {decimals: 5}};
|
||||||
|
case 'string':
|
||||||
|
return {cell: 'text'};
|
||||||
|
default:
|
||||||
|
return {cell: ''};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'setting', name: 'setting', label: gettext('Default'),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class VacuumSettingsSchema extends BaseUISchema {
|
||||||
|
constructor(tableVars, toastTableVars, nodeInfo) {
|
||||||
|
super({
|
||||||
|
vacuum_table: [],
|
||||||
|
vacuum_toast: [],
|
||||||
|
});
|
||||||
|
this.tableVars = tableVars;
|
||||||
|
this.toastTableVars = toastTableVars;
|
||||||
|
this.nodeInfo = nodeInfo;
|
||||||
|
|
||||||
|
this.vacuumTableObj = new VacuumTableSchema('autovacuum_custom');
|
||||||
|
this.vacuumToastTableObj = new VacuumTableSchema('toast_autovacuum');
|
||||||
|
}
|
||||||
|
|
||||||
|
inSchemaCheck() {
|
||||||
|
if(this.nodeInfo && 'catalog' in this.nodeInfo)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
get baseFields() {
|
||||||
|
var obj = this;
|
||||||
|
return [{
|
||||||
|
id: 'autovacuum_custom', label: gettext('Custom auto-vacuum?'),
|
||||||
|
group: gettext('Table'), mode: ['edit', 'create'],
|
||||||
|
type: 'switch', disabled: function(state) {
|
||||||
|
if(state.is_partitioned) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// If table is partitioned table then disabled it.
|
||||||
|
if(state.top && state.is_partitioned) {
|
||||||
|
// We also need to unset rest of all
|
||||||
|
state.autovacuum_custom = false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(obj.inSchemaCheck)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
depChange(state) {
|
||||||
|
if(state.is_partitioned) {
|
||||||
|
return {autovacuum_custom: false};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'autovacuum_enabled', label: gettext('Autovacuum Enabled?'),
|
||||||
|
group: gettext('Table'), mode: ['edit', 'create'], type: 'toggle',
|
||||||
|
options: [
|
||||||
|
{'label': gettext('Not set'), 'value': 'x'},
|
||||||
|
{'label': gettext('Yes'), 'value': 't'},
|
||||||
|
{'label': gettext('No'), 'value': 'f'},
|
||||||
|
],
|
||||||
|
deps: ['autovacuum_custom'],
|
||||||
|
disabled: function(state) {
|
||||||
|
if(obj.inSchemaCheck && state.autovacuum_custom) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
depChange: function(state) {
|
||||||
|
if(obj.inSchemaCheck && state.autovacuum_custom) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return {autovacuum_enabled: 'x'};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'vacuum_table', label: '', editable: false, type: 'collection',
|
||||||
|
canEdit: false, canAdd: false, canDelete: false, group: gettext('Table'),
|
||||||
|
fixedRows: this.tableVars,
|
||||||
|
schema: this.vacuumTableObj,
|
||||||
|
mode: ['edit', 'create'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'toast_autovacuum', label: gettext('Custom auto-vacuum?'),
|
||||||
|
group: gettext('TOAST table'), mode: ['edit', 'create'],
|
||||||
|
type: 'switch',
|
||||||
|
disabled: function(state) {
|
||||||
|
// We need to check additional condition to toggle enable/disable
|
||||||
|
// for table auto-vacuum
|
||||||
|
if(obj.inSchemaCheck && (obj.isNew() || (state.toast_autovacuum_enabled || state.hastoasttable))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'toast_autovacuum_enabled', label: gettext('Autovacuum Enabled?'),
|
||||||
|
group: gettext('TOAST table'), mode: ['edit', 'create'],
|
||||||
|
type: 'toggle',
|
||||||
|
options: [
|
||||||
|
{'label': gettext('Not set'), 'value': 'x'},
|
||||||
|
{'label': gettext('Yes'), 'value': 't'},
|
||||||
|
{'label': gettext('No'), 'value': 'f'},
|
||||||
|
],
|
||||||
|
deps:['toast_autovacuum'],
|
||||||
|
disabled: function(state) {
|
||||||
|
if(obj.inSchemaCheck && state.toast_autovacuum) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
depChange: function(state) {
|
||||||
|
if(obj.inSchemaCheck && state.toast_autovacuum) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(obj.isNew() || state.hastoasttable) {
|
||||||
|
return {toast_autovacuum_enabled: 'x'};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'vacuum_toast', label: '',
|
||||||
|
type: 'collection',
|
||||||
|
fixedRows: this.toastTableVars,
|
||||||
|
editable: function(state) {
|
||||||
|
return state.isNew();
|
||||||
|
},
|
||||||
|
canEdit: false, canAdd: false, canDelete: false, group: gettext('TOAST table'),
|
||||||
|
schema: this.vacuumToastTableObj,
|
||||||
|
mode: ['properties', 'edit', 'create'], deps: ['toast_autovacuum'],
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
}
|
|
@ -89,13 +89,14 @@ export function getNodeAjaxOptions(url, nodeObj, treeNodeInfo, itemNodeData, par
|
||||||
if (_.isUndefined(data) || _.isNull(data)) {
|
if (_.isUndefined(data) || _.isNull(data)) {
|
||||||
api.get(fullUrl, {
|
api.get(fullUrl, {
|
||||||
params: otherParams.urlParams,
|
params: otherParams.urlParams,
|
||||||
})
|
}).then((res)=>{
|
||||||
.then((res)=>{
|
data = res.data;
|
||||||
|
if(res.data.data) {
|
||||||
data = res.data.data;
|
data = res.data.data;
|
||||||
|
}
|
||||||
otherParams.useCache && cacheNode.cache(nodeObj.type + '#' + url, treeNodeInfo, cacheLevel, data);
|
otherParams.useCache && cacheNode.cache(nodeObj.type + '#' + url, treeNodeInfo, cacheLevel, data);
|
||||||
resolve(transform(data));
|
resolve(transform(data));
|
||||||
})
|
}).catch((err)=>{
|
||||||
.catch((err)=>{
|
|
||||||
reject(err);
|
reject(err);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
/* The DataGridView component is based on react-table component */
|
/* The DataGridView component is based on react-table component */
|
||||||
|
|
||||||
import React, { useCallback, useContext, useMemo, useRef, useState } from 'react';
|
import React, { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { Box } from '@material-ui/core';
|
import { Box } from '@material-ui/core';
|
||||||
import { makeStyles } from '@material-ui/core/styles';
|
import { makeStyles } from '@material-ui/core/styles';
|
||||||
import { PgIconButton } from '../components/Buttons';
|
import { PgIconButton } from '../components/Buttons';
|
||||||
|
@ -23,7 +23,7 @@ import PropTypes from 'prop-types';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
|
||||||
import gettext from 'sources/gettext';
|
import gettext from 'sources/gettext';
|
||||||
import { SCHEMA_STATE_ACTIONS } from '.';
|
import { SCHEMA_STATE_ACTIONS, StateUtilsContext } from '.';
|
||||||
import FormView from './FormView';
|
import FormView from './FormView';
|
||||||
import { confirmDeleteRow } from '../helpers/legacyConnector';
|
import { confirmDeleteRow } from '../helpers/legacyConnector';
|
||||||
import CustomPropTypes from 'sources/custom_prop_types';
|
import CustomPropTypes from 'sources/custom_prop_types';
|
||||||
|
@ -78,7 +78,6 @@ const useStyles = makeStyles((theme)=>({
|
||||||
...theme.mixins.panelBorder.bottom,
|
...theme.mixins.panelBorder.bottom,
|
||||||
...theme.mixins.panelBorder.right,
|
...theme.mixins.panelBorder.right,
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
textAlign: 'center'
|
|
||||||
},
|
},
|
||||||
tableCellHeader: {
|
tableCellHeader: {
|
||||||
fontWeight: theme.typography.fontWeightBold,
|
fontWeight: theme.typography.fontWeightBold,
|
||||||
|
@ -87,6 +86,7 @@ const useStyles = makeStyles((theme)=>({
|
||||||
},
|
},
|
||||||
btnCell: {
|
btnCell: {
|
||||||
padding: theme.spacing(0.5, 0),
|
padding: theme.spacing(0.5, 0),
|
||||||
|
textAlign: 'center',
|
||||||
},
|
},
|
||||||
resizer: {
|
resizer: {
|
||||||
display: 'inline-block',
|
display: 'inline-block',
|
||||||
|
@ -202,8 +202,10 @@ function DataTableRow({row, totalRows, isResizing, schema, schemaRef, accessPath
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function DataGridView({
|
export default function DataGridView({
|
||||||
value, viewHelperProps, formErr, schema, accessPath, dataDispatch, containerClassName, ...props}) {
|
value, viewHelperProps, formErr, schema, accessPath, dataDispatch, containerClassName,
|
||||||
|
fixedRows, ...props}) {
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
|
const stateUtils = useContext(StateUtilsContext);
|
||||||
|
|
||||||
/* Using ref so that schema variable is not frozen in columns closure */
|
/* Using ref so that schema variable is not frozen in columns closure */
|
||||||
const schemaRef = useRef(schema);
|
const schemaRef = useRef(schema);
|
||||||
|
@ -386,6 +388,23 @@ export default function DataGridView({
|
||||||
...tablePlugins,
|
...tablePlugins,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
useEffect(()=>{
|
||||||
|
let rowsPromise = fixedRows, umounted=false;
|
||||||
|
if(typeof rowsPromise === 'function') {
|
||||||
|
rowsPromise = rowsPromise();
|
||||||
|
}
|
||||||
|
if(rowsPromise) {
|
||||||
|
Promise.resolve(rowsPromise)
|
||||||
|
.then((res)=>{
|
||||||
|
/* If component unmounted, dont update state */
|
||||||
|
if(!umounted) {
|
||||||
|
stateUtils.initOrigData(accessPath, res);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return ()=>umounted=true;
|
||||||
|
}, []);
|
||||||
|
|
||||||
const isResizing = _.flatMap(headerGroups, headerGroup => headerGroup.headers.map(col=>col.isResizing)).includes(true);
|
const isResizing = _.flatMap(headerGroups, headerGroup => headerGroup.headers.map(col=>col.isResizing)).includes(true);
|
||||||
|
|
||||||
if(!props.visible) {
|
if(!props.visible) {
|
||||||
|
@ -395,12 +414,12 @@ export default function DataGridView({
|
||||||
return (
|
return (
|
||||||
<Box className={containerClassName}>
|
<Box className={containerClassName}>
|
||||||
<Box className={classes.grid}>
|
<Box className={classes.grid}>
|
||||||
<Box className={classes.gridHeader}>
|
{(props.label || props.canAdd) && <Box className={classes.gridHeader}>
|
||||||
<Box className={classes.gridHeaderText}>{props.label}</Box>
|
<Box className={classes.gridHeaderText}>{props.label}</Box>
|
||||||
<Box className={classes.gridControls}>
|
<Box className={classes.gridControls}>
|
||||||
{props.canAdd && <PgIconButton data-test="add-row" title={gettext('Add row')} onClick={onAddClick} icon={<AddIcon />} className={classes.gridControlsButton} />}
|
{props.canAdd && <PgIconButton data-test="add-row" title={gettext('Add row')} onClick={onAddClick} icon={<AddIcon />} className={classes.gridControlsButton} />}
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>}
|
||||||
<div {...getTableProps()} className={classes.table}>
|
<div {...getTableProps()} className={classes.table}>
|
||||||
<DataTableHeader headerGroups={headerGroups} />
|
<DataTableHeader headerGroups={headerGroups} />
|
||||||
<div {...getTableBodyProps()}>
|
<div {...getTableBodyProps()}>
|
||||||
|
@ -432,6 +451,7 @@ DataGridView.propTypes = {
|
||||||
accessPath: PropTypes.array.isRequired,
|
accessPath: PropTypes.array.isRequired,
|
||||||
dataDispatch: PropTypes.func.isRequired,
|
dataDispatch: PropTypes.func.isRequired,
|
||||||
containerClassName: PropTypes.oneOfType([PropTypes.object, PropTypes.string]),
|
containerClassName: PropTypes.oneOfType([PropTypes.object, PropTypes.string]),
|
||||||
|
fixedRows: PropTypes.oneOfType([PropTypes.array, PropTypes.instanceOf(Promise), PropTypes.func]),
|
||||||
columns: PropTypes.array,
|
columns: PropTypes.array,
|
||||||
canEdit: PropTypes.bool,
|
canEdit: PropTypes.bool,
|
||||||
canAdd: PropTypes.bool,
|
canAdd: PropTypes.bool,
|
||||||
|
|
|
@ -217,6 +217,11 @@ export default function FormView({
|
||||||
|
|
||||||
depsMap.push(canAdd, canEdit, canDelete, visible);
|
depsMap.push(canAdd, canEdit, canDelete, visible);
|
||||||
|
|
||||||
|
if(!_.isUndefined(field.fixedRows)) {
|
||||||
|
canAdd = false;
|
||||||
|
canDelete = false;
|
||||||
|
}
|
||||||
|
|
||||||
tabs[group].push(
|
tabs[group].push(
|
||||||
<DataGridView key={field.id} value={value[field.id]} viewHelperProps={viewHelperProps} formErr={formErr}
|
<DataGridView key={field.id} value={value[field.id]} viewHelperProps={viewHelperProps} formErr={formErr}
|
||||||
schema={field.schema} accessPath={accessPath.concat(field.id)} dataDispatch={dataDispatch} containerClassName={classes.controlRow}
|
schema={field.schema} accessPath={accessPath.concat(field.id)} dataDispatch={dataDispatch} containerClassName={classes.controlRow}
|
||||||
|
|
|
@ -92,7 +92,7 @@ function MappedFormControlBase({type, value, id, onChange, className, visible, i
|
||||||
case 'sql':
|
case 'sql':
|
||||||
return <FormInputSQL name={name} value={value} onChange={onSqlChange} className={className} noLabel={noLabel} {...props} />;
|
return <FormInputSQL name={name} value={value} onChange={onSqlChange} className={className} noLabel={noLabel} {...props} />;
|
||||||
default:
|
default:
|
||||||
return <></>;
|
return <span>{value}</span>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,7 +120,7 @@ function MappedCellControlBase({cell, value, id, optionsLoaded, onCellChange, vi
|
||||||
value = e.target.value;
|
value = e.target.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
onCellChange(value);
|
onCellChange && onCellChange(value);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const onIntChange = useCallback((e) => {
|
const onIntChange = useCallback((e) => {
|
||||||
|
@ -179,7 +179,7 @@ function MappedCellControlBase({cell, value, id, optionsLoaded, onCellChange, vi
|
||||||
case 'privilege':
|
case 'privilege':
|
||||||
return <Privilege name={name} value={value} onChange={onTextChange} {...props}/>;
|
return <Privilege name={name} value={value} onChange={onTextChange} {...props}/>;
|
||||||
default:
|
default:
|
||||||
return <></>;
|
return <span>{value}</span>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -65,11 +65,13 @@ const useDialogStyles = makeStyles((theme)=>({
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
export const StateUtilsContext = React.createContext();
|
||||||
|
|
||||||
function getForQueryParams(data) {
|
function getForQueryParams(data) {
|
||||||
let retData = {...data};
|
let retData = {...data};
|
||||||
Object.keys(retData).forEach((key)=>{
|
Object.keys(retData).forEach((key)=>{
|
||||||
let value = retData[key];
|
let value = retData[key];
|
||||||
if(_.isArray(value) || _.isObject(value)) {
|
if(_.isObject(value)) {
|
||||||
retData[key] = JSON.stringify(value);
|
retData[key] = JSON.stringify(value);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -79,6 +81,38 @@ function getForQueryParams(data) {
|
||||||
/* Compare the sessData with schema.origData
|
/* Compare the sessData with schema.origData
|
||||||
schema.origData is set to incoming or default data
|
schema.origData is set to incoming or default data
|
||||||
*/
|
*/
|
||||||
|
function isValueEqual(val1, val2) {
|
||||||
|
let attrDefined = !_.isUndefined(val1) && !_.isUndefined(val2) && !_.isNull(val1) && !_.isNull(val2);
|
||||||
|
|
||||||
|
/* If the orig value was null and new one is empty string, then its a "no change" */
|
||||||
|
/* If the orig value and new value are of different datatype but of same value(numeric) "no change" */
|
||||||
|
/* If the orig value is undefined or null and new value is boolean false "no change" */
|
||||||
|
if ((_.isEqual(val1, val2)
|
||||||
|
|| ((val1 === null || _.isUndefined(val1)) && !val2)
|
||||||
|
|| (attrDefined ? _.isEqual(val1.toString(), val2.toString()) : false
|
||||||
|
))) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function objectComparator(obj1, obj2) {
|
||||||
|
for(const key of _.union(Object.keys(obj1), Object.keys(obj2))) {
|
||||||
|
let equal = isValueEqual(obj1[key], obj2[key]);
|
||||||
|
if(equal) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const diffArrayOptions = {
|
||||||
|
compareFunction: objectComparator,
|
||||||
|
};
|
||||||
|
|
||||||
function getChangedData(topSchema, mode, sessData, stringify=false) {
|
function getChangedData(topSchema, mode, sessData, stringify=false) {
|
||||||
let changedData = {};
|
let changedData = {};
|
||||||
let isEdit = mode === 'edit';
|
let isEdit = mode === 'edit';
|
||||||
|
@ -87,15 +121,8 @@ function getChangedData(topSchema, mode, sessData, stringify=false) {
|
||||||
const attrChanged = (currPath, change, force=false)=>{
|
const attrChanged = (currPath, change, force=false)=>{
|
||||||
let origVal = _.get(topSchema.origData, currPath);
|
let origVal = _.get(topSchema.origData, currPath);
|
||||||
let sessVal = _.get(sessData, currPath);
|
let sessVal = _.get(sessData, currPath);
|
||||||
let attrDefined = !_.isUndefined(origVal) && !_.isUndefined(sessVal) && !_.isNull(origVal) && !_.isNull(sessVal);
|
|
||||||
|
|
||||||
/* If the orig value was null and new one is empty string, then its a "no change" */
|
if(isValueEqual(origVal, sessVal) && !force) {
|
||||||
/* If the orig value and new value are of different datatype but of same value(numeric) "no change" */
|
|
||||||
/* If the orig value is undefined or null and new value is boolean false "no change" */
|
|
||||||
if ((_.isEqual(origVal, sessVal)
|
|
||||||
|| ((origVal === null || _.isUndefined(origVal)) && !sessVal)
|
|
||||||
|| (attrDefined ? _.isEqual(origVal.toString(), sessVal.toString()) : false))
|
|
||||||
&& !force) {
|
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
change = change || _.get(sessData, currPath);
|
change = change || _.get(sessData, currPath);
|
||||||
|
@ -146,8 +173,22 @@ function getChangedData(topSchema, mode, sessData, stringify=false) {
|
||||||
}
|
}
|
||||||
} else if(!isEdit) {
|
} else if(!isEdit) {
|
||||||
if(field.type === 'collection') {
|
if(field.type === 'collection') {
|
||||||
|
/* For fixed rows, check the updated changes */
|
||||||
|
if(!_.isUndefined(field.fixedRows)) {
|
||||||
|
const changeDiff = diffArray(
|
||||||
|
_.get(topSchema.origData, currPath) || [],
|
||||||
|
_.get(sessData, currPath) || [],
|
||||||
|
'cid',
|
||||||
|
diffArrayOptions
|
||||||
|
);
|
||||||
|
if(changeDiff.updated.length > 0) {
|
||||||
|
let change = cleanCid(_.get(sessData, currPath));
|
||||||
|
attrChanged(currPath, change, true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
let change = cleanCid(_.get(sessData, currPath));
|
let change = cleanCid(_.get(sessData, currPath));
|
||||||
attrChanged(currPath, change);
|
attrChanged(currPath, change);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
attrChanged(currPath);
|
attrChanged(currPath);
|
||||||
}
|
}
|
||||||
|
@ -320,11 +361,16 @@ function cleanCid(coll) {
|
||||||
return coll.map((o)=>_.pickBy(o, (v, k)=>k!='cid'));
|
return coll.map((o)=>_.pickBy(o, (v, k)=>k!='cid'));
|
||||||
}
|
}
|
||||||
|
|
||||||
function prepareData(origData) {
|
function prepareData(val) {
|
||||||
_.forIn(origData, function (val) {
|
if(_.isPlainObject(val)) {
|
||||||
if (_.isArray(val)) {
|
_.forIn(val, function (el) {
|
||||||
val.forEach(function(el) {
|
|
||||||
if (_.isObject(el)) {
|
if (_.isObject(el)) {
|
||||||
|
prepareData(el);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if(_.isArray(val)) {
|
||||||
|
val.forEach(function(el) {
|
||||||
|
if (_.isPlainObject(el)) {
|
||||||
/* The each row in collection need to have an id to identify them uniquely
|
/* The each row in collection need to have an id to identify them uniquely
|
||||||
This helps in easily getting what has changed */
|
This helps in easily getting what has changed */
|
||||||
/* Nested collection rows may or may not have idAttribute.
|
/* Nested collection rows may or may not have idAttribute.
|
||||||
|
@ -336,11 +382,7 @@ function prepareData(origData) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (_.isObject(val)) {
|
return val;
|
||||||
prepareData(val);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return origData;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If its the dialog */
|
/* If its the dialog */
|
||||||
|
@ -560,8 +602,24 @@ function SchemaDialogView({
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const stateUtils = useMemo(()=>({
|
||||||
|
dataDispatch: sessDispatchWithListener,
|
||||||
|
initOrigData: (path, value)=>{
|
||||||
|
if(path) {
|
||||||
|
let data = prepareData(value);
|
||||||
|
_.set(schema.origData, path, data);
|
||||||
|
sessDispatchWithListener({
|
||||||
|
type: SCHEMA_STATE_ACTIONS.SET_VALUE,
|
||||||
|
path: path,
|
||||||
|
value: data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}), []);
|
||||||
|
|
||||||
/* I am Groot */
|
/* I am Groot */
|
||||||
return (
|
return (
|
||||||
|
<StateUtilsContext.Provider value={stateUtils}>
|
||||||
<DepListenerContext.Provider value={depListenerObj.current}>
|
<DepListenerContext.Provider value={depListenerObj.current}>
|
||||||
<Box className={classes.root}>
|
<Box className={classes.root}>
|
||||||
<Box className={classes.form}>
|
<Box className={classes.form}>
|
||||||
|
@ -592,6 +650,7 @@ function SchemaDialogView({
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</DepListenerContext.Provider>
|
</DepListenerContext.Provider>
|
||||||
|
</StateUtilsContext.Provider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -204,8 +204,6 @@ describe('SchemaView', ()=>{
|
||||||
ctrl.find('ForwardRef(Tab)[label="SQL"]').find('button').simulate('click');
|
ctrl.find('ForwardRef(Tab)[label="SQL"]').find('button').simulate('click');
|
||||||
setTimeout(()=>{
|
setTimeout(()=>{
|
||||||
ctrl.update();
|
ctrl.update();
|
||||||
/* Dont show error message */
|
|
||||||
expect(ctrl.find('FormFooterMessage').prop('message')).toBe('');
|
|
||||||
expect(ctrl.find('CodeMirror').prop('value')).toBe('-- No updates.');
|
expect(ctrl.find('CodeMirror').prop('value')).toBe('-- No updates.');
|
||||||
done();
|
done();
|
||||||
}, 0);
|
}, 0);
|
||||||
|
|
|
@ -115,6 +115,7 @@ describe('LanguageSchema', ()=>{
|
||||||
let setError = jasmine.createSpy('setError');
|
let setError = jasmine.createSpy('setError');
|
||||||
|
|
||||||
state.lanproc = '';
|
state.lanproc = '';
|
||||||
|
state.isTemplate = true;
|
||||||
schemaObj.validate(state, setError);
|
schemaObj.validate(state, setError);
|
||||||
expect(setError).toHaveBeenCalledWith('lanproc', 'Handler function cannot be empty.');
|
expect(setError).toHaveBeenCalledWith('lanproc', 'Handler function cannot be empty.');
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue