mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2024-11-28 03:23:52 -06:00
Fixed an issue introduced due to reloading of the tree due to changes in particular preferences (#7942)
* Fix an issue - when the object browser tree is being recreated due to reloading for changes in some of the preferences. Tree object returns object from the previous instance as 'selected', but - it does not have the 'treeNodeInfo' available. In this special case - we would consider that there is no node selected at that particular moment, and pass information accordingly. * Fixed 'New Connection' dialog issue after connecting a disconnected server. * Disable the 'Add' button in the GridHeader with form, when 'canAdd' flag is set to false. * Convert the access path to string array for correct comparision. * Check the access path type before comparison. When language is 'c', set the 'code' block read-only. * Enabled 'Strict' control for EPAS >= 95
This commit is contained in:
parent
100f59f78b
commit
b0cd028ff8
@ -16,15 +16,15 @@ class TokenHeaderSchema extends BaseUISchema {
|
||||
constructor(tokenOptions) {
|
||||
super({
|
||||
token: undefined,
|
||||
isNew: false,
|
||||
isNew: true,
|
||||
});
|
||||
|
||||
this.tokenOptions = tokenOptions;
|
||||
}
|
||||
|
||||
set isNewFTSConf(flag) {
|
||||
if (!this.state) return;
|
||||
this.state.data = {...this.state.data, isNew: flag};
|
||||
if (this.state)
|
||||
this.state.data = {...this.state.data, isNew: flag};
|
||||
}
|
||||
|
||||
getNewData(data) {
|
||||
@ -35,16 +35,17 @@ class TokenHeaderSchema extends BaseUISchema {
|
||||
}
|
||||
|
||||
get baseFields() {
|
||||
let obj = this;
|
||||
return [{
|
||||
id: 'token', label: gettext('Tokens'), deps: ['isNew'],
|
||||
type: () => ({
|
||||
type: 'select',
|
||||
options: this.tokenOptions,
|
||||
}),
|
||||
disabled: function() { return obj.isNewFTSConf; }
|
||||
}, {
|
||||
id: 'isNew', visible: false, type: 'text',
|
||||
disabled: function() {
|
||||
return this.state ? this.state.data.isNew : true;
|
||||
}
|
||||
},{
|
||||
id: 'isNew', visible: false, type: 'text', exclude: true,
|
||||
}];
|
||||
}
|
||||
}
|
||||
@ -160,12 +161,13 @@ export default class FTSConfigurationSchema extends BaseUISchema {
|
||||
}, {
|
||||
id: 'tokens', label: '', type: 'collection',
|
||||
group: gettext('Tokens'), mode: ['create','edit'],
|
||||
editable: false, schema: this.tokColumnSchema,
|
||||
schema: this.tokColumnSchema,
|
||||
headerSchema: this.tokHeaderSchema,
|
||||
headerFormVisible: true,
|
||||
GridHeader: DataGridFormHeader,
|
||||
uniqueCol : ['token'],
|
||||
canAdd: true, canEdit: false, canDelete: true,
|
||||
canAdd: (state, helpderProps) => (helpderProps.mode !== 'create'),
|
||||
canDelete: true,
|
||||
}
|
||||
];
|
||||
}
|
||||
|
@ -159,33 +159,21 @@ export default class FunctionSchema extends BaseUISchema {
|
||||
}
|
||||
}
|
||||
|
||||
isGreaterThan95(state){
|
||||
if (
|
||||
isLessThan95ORNonSPL(state) {
|
||||
return (
|
||||
this.inCatalog() ||
|
||||
this.node_info['node_info'].server.version < 90500 ||
|
||||
this.node_info['node_info']['server'].server_type != 'ppas' ||
|
||||
state.lanname != 'edbspl'
|
||||
) {
|
||||
state.provolatile = null;
|
||||
state.proisstrict = false;
|
||||
state.procost = null;
|
||||
state.proleakproof = false;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
isGreaterThan96(state){
|
||||
if (
|
||||
isLessThan96ORNonSPL(state){
|
||||
return (
|
||||
this.node_info['node_info'].server.version < 90600 ||
|
||||
this.node_info['node_info']['server'].server_type != 'ppas' ||
|
||||
state.lanname != 'edbspl'
|
||||
) {
|
||||
state.proparallel = null;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -210,7 +198,7 @@ export default class FunctionSchema extends BaseUISchema {
|
||||
if (this.type !== 'procedure') {
|
||||
obj.inCatalog(state);
|
||||
} else {
|
||||
obj.isGreaterThan95(state);
|
||||
obj.isLessThan95ORNonSPL(state);
|
||||
}
|
||||
},
|
||||
noEmpty: true,
|
||||
@ -328,7 +316,7 @@ export default class FunctionSchema extends BaseUISchema {
|
||||
{'label': 'VOLATILE', 'value': 'v'},
|
||||
{'label': 'STABLE', 'value': 's'},
|
||||
{'label': 'IMMUTABLE', 'value': 'i'},
|
||||
], disabled: (this.type !== 'procedure') ? obj.inCatalog() : obj.isGreaterThan95,
|
||||
], disabled: (this.type !== 'procedure') ? obj.inCatalog() : obj.isLessThan95ORNonSPL,
|
||||
controlProps: {allowClear: false},
|
||||
},{
|
||||
id: 'proretset', label: gettext('Returns a set?'), type: 'switch',
|
||||
@ -336,8 +324,22 @@ export default class FunctionSchema extends BaseUISchema {
|
||||
visible: obj.isVisible, readonly: obj.isReadonly,
|
||||
},{
|
||||
id: 'proisstrict', label: gettext('Strict?'), type: 'switch',
|
||||
group: gettext('Options'), disabled: obj.inCatalog(),
|
||||
group: gettext('Options'),
|
||||
disabled: obj.inCatalog() ? true : obj.isLessThan95ORNonSPL,
|
||||
deps: ['lanname'],
|
||||
depChange: (state, source) => (
|
||||
(source[source.length - 1] !== 'lanname') ? undefined : (
|
||||
obj.isLessThan95ORNonSPL(state)
|
||||
) ? {
|
||||
provolatile: null,
|
||||
proisstrict: false,
|
||||
procost: null,
|
||||
proleakproof: false,
|
||||
proparallel: null,
|
||||
} : (
|
||||
obj.isLessThan95ORNonSPL(state) ? { proparallel: null } : undefined
|
||||
)
|
||||
),
|
||||
},{
|
||||
id: 'prosecdef', label: gettext('Security of definer?'),
|
||||
group: gettext('Options'), type: 'switch',
|
||||
@ -357,13 +359,13 @@ export default class FunctionSchema extends BaseUISchema {
|
||||
{'label': 'RESTRICTED', 'value': 'r'},
|
||||
{'label': 'SAFE', 'value': 's'},
|
||||
],
|
||||
disabled: (this.type !== 'procedure') ? obj.inCatalog(): obj.isGreaterThan96,
|
||||
disabled: (this.type !== 'procedure') ? obj.inCatalog(): obj.isLessThan96ORNonSPL,
|
||||
min_version: 90600,
|
||||
controlProps: {allowClear: false},
|
||||
},{
|
||||
id: 'procost', label: gettext('Estimated cost'), group: gettext('Options'),
|
||||
cell:'string', type: 'text', deps: ['lanname'],
|
||||
disabled: (this.type !== 'procedure') ? obj.isDisabled: obj.isGreaterThan95,
|
||||
disabled: (this.type !== 'procedure') ? obj.isDisabled : obj.isLessThan95ORNonSPL,
|
||||
},{
|
||||
id: 'prorows', label: gettext('Estimated rows'), type: 'text',
|
||||
deps: ['proretset'], visible: obj.isVisible,
|
||||
@ -378,7 +380,7 @@ export default class FunctionSchema extends BaseUISchema {
|
||||
},{
|
||||
id: 'proleakproof', label: gettext('Leak proof?'),
|
||||
group: gettext('Options'), cell:'boolean', type: 'switch', min_version: 90200,
|
||||
disabled: (this.type !== 'procedure') ? obj.inCatalog(): obj.isGreaterThan95,
|
||||
disabled: (this.type !== 'procedure') ? obj.inCatalog() : obj.isLessThan95ORNonSPL,
|
||||
deps: ['lanname'],
|
||||
},{
|
||||
id: 'prosupportfunc', label: gettext('Support function'),
|
||||
|
@ -161,10 +161,7 @@ export default class TriggerFunctionSchema extends BaseUISchema {
|
||||
type: 'sql', isFullTab: true,
|
||||
mode: ['properties', 'create', 'edit'],
|
||||
group: gettext('Code'), deps: ['lanname'],
|
||||
visible: (state) => {
|
||||
return state.lanname !== 'c';
|
||||
},
|
||||
disabled: obj.isDisabled, readonly: obj.isReadonly,
|
||||
readonly: (state) => (obj.isDisabled() || state.lanname === 'c'),
|
||||
},{
|
||||
id: 'probin', label: gettext('Object file'), cell: 'string',
|
||||
type: 'text', group: gettext('Definition'), deps: ['lanname'],
|
||||
@ -260,8 +257,8 @@ export default class TriggerFunctionSchema extends BaseUISchema {
|
||||
|
||||
if (isEmptyString(state.service)) {
|
||||
|
||||
/* code validation*/
|
||||
if (isEmptyString(state.prosrc)) {
|
||||
/* code validation */
|
||||
if (isEmptyString(state.prosrc) && state.lanname !== 'c') {
|
||||
errmsg = gettext('Code cannot be empty.');
|
||||
setError('prosrc', errmsg);
|
||||
return true;
|
||||
|
@ -77,7 +77,7 @@ export function DataGridFormHeader({tableEleRef}) {
|
||||
viewHelperProps,
|
||||
} = useContext(DataGridContext);
|
||||
const {
|
||||
addOnTop, canAddRow, canEdit, expandEditOnAdd, headerFormVisible
|
||||
canAdd, addOnTop, canAddRow, canEdit, expandEditOnAdd, headerFormVisible
|
||||
} = options;
|
||||
const rows = table.getRowModel().rows;
|
||||
|
||||
@ -85,8 +85,12 @@ export function DataGridFormHeader({tableEleRef}) {
|
||||
const newRowIndex = useRef(-1);
|
||||
const schemaState = useContext(SchemaStateContext);
|
||||
const headerFormData = useRef({});
|
||||
const [addDisabled, setAddDisabled] = useState(canAddRow);
|
||||
const [addDisabled, setAddDisabled] = useState(!canAdd || !canAddRow);
|
||||
const {headerSchema} = field;
|
||||
const disableAddButton = (flag) => {
|
||||
if (!canAdd || !canAddRow) return;
|
||||
setAddDisabled(flag);
|
||||
};
|
||||
|
||||
const onAddClick = useCallback(() => {
|
||||
|
||||
@ -154,8 +158,8 @@ export function DataGridFormHeader({tableEleRef}) {
|
||||
showFooter={false}
|
||||
onDataChange={(isDataChanged, dataChanged)=>{
|
||||
headerFormData.current = dataChanged;
|
||||
setAddDisabled(
|
||||
canAddRow && headerSchema.addDisabled(headerFormData.current)
|
||||
disableAddButton(
|
||||
headerSchema.addDisabled(headerFormData.current)
|
||||
);
|
||||
}}
|
||||
hasSQL={false}
|
||||
|
@ -114,7 +114,7 @@ export const sessDataReducer = (state, action) => {
|
||||
return data;
|
||||
|
||||
case SCHEMA_STATE_ACTIONS.DEFERRED_DEPCHANGE:
|
||||
data = getDepChange(action.path, data, state, action);
|
||||
data = getDepChange(action.path, _.cloneDeep(data), state, action);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -9,11 +9,14 @@
|
||||
|
||||
import { useEffect } from 'react';
|
||||
|
||||
const convertKeysToString = (arr) => (arr||[]).map((key) => String(key));
|
||||
const isPathEqual = (path1, path2) => (
|
||||
JSON.stringify(path1) === JSON.stringify(path2)
|
||||
Array.isArray(path1) &&
|
||||
Array.isArray(path2) &&
|
||||
JSON.stringify(convertKeysToString(path1)) ===
|
||||
JSON.stringify(convertKeysToString(path2))
|
||||
);
|
||||
|
||||
|
||||
export const useFieldError = (path, schemaState, subscriberManager) => {
|
||||
|
||||
useEffect(() => {
|
||||
@ -38,7 +41,9 @@ export const useFieldError = (path, schemaState, subscriberManager) => {
|
||||
});
|
||||
|
||||
const errors = schemaState?.errors || {};
|
||||
const error = isPathEqual(errors.name, path) ? errors.message : null;
|
||||
const error = (
|
||||
Array.isArray(errors.name) && isPathEqual(errors.name, path)
|
||||
) ? errors.message : null;
|
||||
|
||||
return {hasError: !_.isNull(error), error};
|
||||
};
|
||||
|
@ -67,13 +67,23 @@ export default function withStandardTabInfo(Component, tabId) {
|
||||
};
|
||||
}, []);
|
||||
|
||||
////////
|
||||
// Special case:
|
||||
//
|
||||
// When the tree is being recreated during reloading on changes of some
|
||||
// preferences, it is possible that the tree returns 'selected' node, but -
|
||||
// it does not have the 'treeNodeInfo' as it was actually part of the
|
||||
// previous instance of the tree.
|
||||
//
|
||||
// In that case - we consider that there is no node selected in the tree.
|
||||
///////
|
||||
return (
|
||||
<ErrorBoundary>
|
||||
<Component
|
||||
{...props}
|
||||
nodeItem={nodeItem}
|
||||
nodeData={nodeData}
|
||||
node={node}
|
||||
nodeItem={treeNodeInfo ? nodeItem : undefined}
|
||||
nodeData={treeNodeInfo ? nodeData : undefined}
|
||||
node={treeNodeInfo ? node : undefined}
|
||||
treeNodeInfo={treeNodeInfo}
|
||||
isActive={isActive}
|
||||
isStale={isStale}
|
||||
|
@ -52,7 +52,7 @@ class NewConnectionSchema extends BaseUISchema {
|
||||
if(this.groupedServers?.length != 0) {
|
||||
return Promise.resolve(this.groupedServers);
|
||||
}
|
||||
return new Promise((resolve, reject)=>{
|
||||
return new Promise((resolve, reject) => {
|
||||
this.api.get(url_for('sqleditor.get_new_connection_servers'))
|
||||
.then(({data: respData})=>{
|
||||
let groupedOptions = [];
|
||||
@ -61,7 +61,7 @@ class NewConnectionSchema extends BaseUISchema {
|
||||
return;
|
||||
}
|
||||
/* initial selection */
|
||||
let foundServer = _.find(v, (o)=>o.value==obj.params.sid);
|
||||
let foundServer = _.find(v, (o) => o.value == obj.params.sid);
|
||||
foundServer && (foundServer.selected = true);
|
||||
groupedOptions.push({
|
||||
label: k,
|
||||
@ -69,7 +69,7 @@ class NewConnectionSchema extends BaseUISchema {
|
||||
});
|
||||
});
|
||||
/* Will be re-used for changing icon when connected */
|
||||
this.groupedServers = groupedOptions.map((group)=>{
|
||||
this.groupedServers = groupedOptions.map((group) => {
|
||||
return {
|
||||
label: group.label,
|
||||
options: group.options.map((o)=>({...o, selected: false})),
|
||||
@ -118,18 +118,19 @@ class NewConnectionSchema extends BaseUISchema {
|
||||
optionsLoaded: (res) => self.flatServers = flattenSelectOptions(res),
|
||||
optionsReloadBasis: self.flatServers.map((s) => s.connected).join(''),
|
||||
}),
|
||||
depChange: (state)=>{
|
||||
depChange: (state) => {
|
||||
/* Once the option is selected get the name */
|
||||
/* Force sid to null, and set only if connected */
|
||||
let selectedServer = _.find(
|
||||
self.flatServers, (s) => s.value == state.sid
|
||||
);
|
||||
|
||||
return {
|
||||
server_name: selectedServer?.label,
|
||||
did: null,
|
||||
user: null,
|
||||
role: null,
|
||||
sid: null,
|
||||
sid: state.sid,
|
||||
fgcolor: selectedServer?.fgcolor,
|
||||
bgcolor: selectedServer?.bgcolor,
|
||||
connected: selectedServer?.connected,
|
||||
@ -138,6 +139,7 @@ class NewConnectionSchema extends BaseUISchema {
|
||||
deferredDepChange: (state, source, topState, actionObj) => {
|
||||
return new Promise((resolve) => {
|
||||
let sid = actionObj.value;
|
||||
|
||||
if(!_.find(self.flatServers, (s) => s.value == sid)?.connected) {
|
||||
this.connectServer(sid, state.user, null, (data) => {
|
||||
self.setServerConnected(sid, data.icon);
|
||||
|
Loading…
Reference in New Issue
Block a user