1) Fixed an issue where loadingText message is not shown in SchemaView.

2) Fixed SonarQube Bugs and Code Smells.
pull/8395/head
Akshay Joshi 2025-01-24 13:58:21 +05:30
parent 684818def1
commit 22b7ae6cdc
29 changed files with 67 additions and 82 deletions

View File

@ -777,9 +777,6 @@ class ServerNode(PGChildNodeView):
request.data
)
old_server_name = ''
if 'name' in data:
old_server_name = server.name
if 'db_res' in data:
data['db_res'] = ','.join(data['db_res'])

View File

@ -425,7 +425,7 @@ def migrate_saved_passwords(master_key, master_password):
servers_with_pwd_in_os_secret)
# Update driver manager with new passwords
try:
update_session_manager(saved_password_servers)
update_session_manager(current_user.id, saved_password_servers)
except Exception:
current_app.logger.warning(
'Error while updating session manger')
@ -497,7 +497,7 @@ def delete_saved_passwords_from_os_secret_storage(servers):
if len(servers) > 0:
for ser in servers:
server, is_password_saved, is_tunnel_password_saved = ser
server, _, _ = ser
server_name = KEY_RING_USERNAME_FORMAT.format(server.name,
server.id)
server_password = keyring.get_password(
@ -550,7 +550,7 @@ def update_session_manager(user_id=None, servers=None):
return True
except Exception:
db.session.rollback()
raise
raise
def get_replication_type(conn, sversion):

View File

@ -187,7 +187,7 @@ export default class MainMenuFactory {
const mi = getNewMenuItem(i);
if(!mi) return;
if(i.category??'common' != 'common') {
if((i.category??'common') != 'common') {
const cmi = getMenuCategory(i.category);
if(cmi) {
cmi.addMenuItems([...applySeparators(mi)]);

View File

@ -349,7 +349,7 @@ define('pgadmin.browser', [
const getFullPath = (currPath, currMenu)=>{
if(currMenu.node) {
return currPath.concat([currMenu.node]);
} else if(currMenu.category??'common' != 'common') {
} else if((currMenu.category??'common') != 'common') {
const currCat = self.menu_categories[currMenu.category];
if(currCat?.category) {
return getFullPath(currPath.concat([currMenu.category]), currCat);

View File

@ -551,7 +551,7 @@ def logs(log_format=None, disp_format=None, sid=None, page=0):
status, res = g.conn.execute_scalar(sql)
if not status:
return internal_server_error(errormsg=res)
if not res or len(res) < 0:
if not res or len(res) <= 0:
return ajax_response(
response={'logs_disabled': True},
status=200
@ -594,7 +594,7 @@ def logs(log_format=None, disp_format=None, sid=None, page=0):
{"error_severity": _tmp_log['error_severity'],
"timestamp": _tmp_log['timestamp'],
"message": _tmp_log['message']})
except Exception as e:
except Exception:
pass
# CSV format
@ -605,12 +605,10 @@ def logs(log_format=None, disp_format=None, sid=None, page=0):
final_cols.append({"error_severity": _tmp_log[11],
"timestamp": _tmp_log[0],
"message": _tmp_log[13]})
except Exception as e:
except Exception:
pass
else:
col1 = []
col2 = []
_pattern = re.compile(LOG_STATEMENTS)
for f in final_res:
tmp = re.search(LOG_STATEMENTS, f)

View File

@ -272,8 +272,6 @@ function ActiveOnlyHeader({activeOnly, setActiveOnly}) {
ActiveOnlyHeader.propTypes = {
activeOnly: PropTypes.bool,
setActiveOnly: PropTypes.func,
refresh: PropTypes.bool,
setRefresh: PropTypes.func,
};
function Dashboard({
@ -533,7 +531,7 @@ function Dashboard({
maxSize: 35,
minSize: 35,
id: 'btn-terminate',
cell: getTerminateCell(pgAdmin, sid, did, canTakeAction, setRefresh, ()=>setRefresh(!refresh)),
cell: getTerminateCell(pgAdmin, sid, did, canTakeAction, ()=>setRefresh(!refresh)),
},
{
header: () => null,
@ -544,7 +542,7 @@ function Dashboard({
maxSize: 35,
minSize: 35,
id: 'btn-cancel',
cell: getCancelCell(pgAdmin, sid, did, canTakeAction, setRefresh, ()=>setRefresh(!refresh)),
cell: getCancelCell(pgAdmin, sid, did, canTakeAction, ()=>setRefresh(!refresh)),
},
{
header: () => null,
@ -886,7 +884,7 @@ function Dashboard({
type: 'GET',
})
.then((res) => {
if (res.data && res.data['logs_disabled']) {
if (res?.data?.['logs_disabled']) {
setSsMsg(gettext('Please enable the logging to view the server logs or check the log file is in place or not.'));
} else {
setDashData(parseData(res.data));
@ -938,7 +936,7 @@ function Dashboard({
// we want to show 'idle in transaction', 'active', 'active in transaction', and future non-blank, non-"idle" status values
return dashData[0]['activity']?.filter((r)=>(r.state && r.state != '' && r.state != 'idle'));
}
return dashData && dashData[0] && dashData[0]['activity'] || [];
return dashData?.[0]?.['activity'] || [];
}, [dashData, activeOnly, mainTabVal]);
const showDefaultContents = () => {
@ -1089,7 +1087,7 @@ function Dashboard({
tableNoBorder={false}
customHeader={<ActiveOnlyHeader activeOnly={activeOnly} setActiveOnly={setActiveOnly} refresh={refresh} setRefresh={setRefresh}/>}
columns={activityColumns}
data={(dashData !== undefined && dashData[0] && filteredDashData) || []}
data={(dashData?.[0] && filteredDashData) || []}
schema={activeQSchemaObj}
></PgTable>
</SectionContainer>
@ -1098,7 +1096,7 @@ function Dashboard({
caveTable={false}
tableNoBorder={false}
columns={databaseLocksColumns}
data={(dashData !== undefined && dashData[0] && dashData[0]['locks']) || []}
data={(dashData?.[0]?.['locks']) || []}
></PgTable>
</SectionContainer>
<SectionContainer title={gettext('Prepared Transactions')} style={{height: 'auto', minHeight: '200px', maxHeight:'400px', paddingBottom: '20px'}}>
@ -1106,7 +1104,7 @@ function Dashboard({
caveTable={false}
tableNoBorder={false}
columns={databasePreparedColumns}
data={(dashData !== undefined && dashData[0] && dashData[0]['prepared']) || []}
data={(dashData?.[0]?.['prepared']) || []}
></PgTable>
</SectionContainer>
</Fragment>

View File

@ -122,7 +122,6 @@ class AzureCredSchema extends BaseUISchema {
auth_code: ''
});
}).catch((err) => {
// TODO:: Show error message.
console.error(
err instanceof Error ?
err : Error(gettext('Something went wrong'))

View File

@ -68,7 +68,6 @@ class GoogleCredSchema extends BaseUISchema{
obj.fieldOptions.verification_ack();
}
}).catch((err) => {
// FIXME:: Show error message.
console.error(
err instanceof Error ?
err : Error(gettext('Something went wrong'))
@ -505,7 +504,7 @@ class GoogleClusterSchema extends BaseUISchema {
}
validate(data, setErr) {
if ( !isEmptyString(data.name) && (!/^(?=[a-z])[a-z0-9\-]*$/.test(data.name) || data.name.length > 97)) {
if ( !isEmptyString(data.name) && (!/^(?=[a-z])[a-z0-9-]*$/.test(data.name) || data.name.length > 97)) {
setErr('name',gettext('Name must only contain lowercase letters, numbers, and hyphens.Should start with a letter and must be 97 characters or less'));
return true;
}

View File

@ -111,11 +111,11 @@ export default class FileManagerModule {
}
}
show(params, onOK, onCancel, modalObj) {
async show(params, onOK, onCancel, modalObj) {
let {name: browser} = getBrowser();
if(browser == 'Electron') {
try {
this.showNative(params, onOK, onCancel);
await this.showNative(params, onOK, onCancel);
} catch {
// Fall back to internal
this.showInternal(params, onOK, onCancel, modalObj);

View File

@ -18,6 +18,7 @@ import { usePgAdmin } from '../../static/js/PgAdminProvider';
import { LAYOUT_EVENTS, LayoutDockerContext } from '../../static/js/helpers/Layout';
import usePreferences from '../../preferences/static/js/store';
import PropTypes from 'prop-types';
import _ from 'lodash';
export default function ObjectNodeProperties({panelId, node, treeNodeInfo, nodeData, actionType, formType, onEdit, onSave, onClose,
isActive, setIsStale, isStale}) {
@ -44,7 +45,7 @@ export default function ObjectNodeProperties({panelId, node, treeNodeInfo, nodeD
let updatedData = ['table', 'partition'].includes(nodeType) && !_.isEmpty(nodeData.rows_cnt) ? {rows_cnt: nodeData.rows_cnt} : undefined;
const objToString = (obj) => (
(obj && typeof obj === 'object') ? Object.keys(obj).sort().reduce(
(obj && typeof obj === 'object') ? Object.keys(obj).sort((a, b)=>a.localeCompare(b)).reduce(
(acc, key) => (acc + `${key}=` + objToString(obj[key])), ''
) : String(obj)
);

View File

@ -7,7 +7,7 @@
//
//////////////////////////////////////////////////////////////
import React, { useEffect, useMemo, useState } from 'react';
import React, { useEffect, useMemo } from 'react';
import gettext from 'sources/gettext';
import url_for from 'sources/url_for';
import _ from 'lodash';
@ -329,7 +329,6 @@ class AdHocConnectionSchema extends BaseUISchema {
}
export default function AdHocConnection({mode}) {
const [connecting, setConnecting] = useState(false);
const api = getApiInstance();
const modal = useModal();
const pgAdmin = usePgAdmin();
@ -337,7 +336,6 @@ export default function AdHocConnection({mode}) {
const {currentWorkspace} = useWorkspace();
const connectExistingServer = async (sid, user, formData, connectCallback) => {
setConnecting(true);
try {
let {data: respData} = await api({
method: 'POST',
@ -350,7 +348,6 @@ export default function AdHocConnection({mode}) {
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
data: formData
});
setConnecting(false);
connectCallback?.(respData.data);
} catch (error) {
if(!error.response) {
@ -360,7 +357,6 @@ export default function AdHocConnection({mode}) {
return (
<ConnectServerContent
closeModal={()=>{
setConnecting(false);
closeModal();
}}
data={error.response?.data?.result}
@ -451,21 +447,18 @@ export default function AdHocConnection({mode}) {
};
const onSaveClick = async (isNew, formData) => {
setConnecting(true);
try {
let {data: respData} = await api({
method: 'POST',
url: url_for('workspace.adhoc_connect_server'),
data: JSON.stringify(formData)
});
setConnecting(false);
if (mode == WORKSPACES.QUERY_TOOL) {
openQueryTool(respData, formData);
} else if (mode == WORKSPACES.PSQL_TOOL) {
openPSQLTool(respData, formData);
}
} catch (error) {
setConnecting(false);
if(!error.response) {
pgAdmin.Browser.notifier.pgNotifier('error', error, 'Connection error', gettext('Connect to server.'));
} else {
@ -508,7 +501,7 @@ export default function AdHocConnection({mode}) {
viewHelperProps={{
mode: 'create',
}}
loadingText={connecting ? 'Connecting...' : ''}
loadingText={'Connecting...'}
onSave={onSaveClick}
customSaveBtnName= {saveBtnName}
customCloseBtnName={''}

View File

@ -7,7 +7,7 @@
//
//////////////////////////////////////////////////////////////
import React, {Fragment, useEffect, useMemo, useState } from 'react';
import React, {useEffect, useMemo, useState } from 'react';
import AppMenuBar from './AppMenuBar';
import ObjectBreadcrumbs from './components/ObjectBreadcrumbs';
import Layout, { LAYOUT_EVENTS, LayoutDocker, getDefaultGroup } from './helpers/Layout';

View File

@ -31,12 +31,9 @@ export default class Feature {
this.schemaState = schemaState;
}
generateColumns(/* { pgAdmin, columns, columnVisibility, options } */) {}
onTable(/* { table, options, classList } */) {}
onRow(/* {
index, row, rowRef, classList, attributes,
expandedRowContents, rowOptions, tableOptions
} */) {}
generateColumns() {/*This is intentional (SonarQube)*/}
onTable() {/*This is intentional (SonarQube)*/}
onRow() {/*This is intentional (SonarQube)*/}
}
function isValidFeatureClass(cls) {

View File

@ -55,7 +55,7 @@ export default class Reorder extends Feature {
<div
className='reorder-cell'
data-handler-id={handlerId}
ref={dragHandleRef ? dragHandleRef : null}>
ref={dragHandleRef || null}>
<DragIndicatorRoundedIcon fontSize="small" />
</div>
);

View File

@ -17,10 +17,9 @@ export const FieldControl = ({schemaId, item}) => {
return useMemo(() =>
<Control {...props}>
{
children &&
children.map(
(child, idx) => <FieldControl key={idx} item={child}/>
)
children?.map(
(child, idx) => <FieldControl key={idx} item={child}/>
)
}
</Control>, [schemaId, Control, props, children]
);

View File

@ -120,12 +120,12 @@ export default function SchemaDialogView({
const onSaveClick = () => {
// Do nothing when there is no change or there is an error
if (
!schemaState._changes || Object.keys(schemaState._changes) === 0 ||
!schemaState._changes || Object.keys(schemaState._changes).length === 0 ||
schemaState.errors.name
) return;
setSaving(true);
setLoaderText('Saving...');
setLoaderText(schemaState.customLoadingText || gettext('Saving...'));
if (!schema.warningText) {
save(schemaState.changes(true));

View File

@ -288,14 +288,14 @@ export class SchemaState extends DepListener {
this.dataStore.setState(_data);
}
accessPath(path=[], key) {
accessPath(path, key) {
return this.__pathGenerator.cached(
_.isUndefined(key) ? path : path.concat(key)
);
}
value(path) {
if (!path || !path.length) return this.data;
if (!path?.length) return this.data;
return _.get(this.data, path);
}

View File

@ -99,16 +99,16 @@ export const sessDataReducer = (state, action) => {
break;
case SCHEMA_STATE_ACTIONS.MOVE_ROW:
case SCHEMA_STATE_ACTIONS.MOVE_ROW: {
rows = _.get(data, action.path)||[];
var row = rows[action.oldIndex];
let row = rows[action.oldIndex];
rows.splice(action.oldIndex, 1);
rows.splice(action.newIndex, 0, row);
_.set(data, action.path, rows);
break;
}
case SCHEMA_STATE_ACTIONS.CLEAR_DEFERRED_QUEUE:
data.__deferred__ = [];
return data;

View File

@ -96,7 +96,7 @@ export function prepareData(val, createMode=false) {
return val;
}
export const flatternObject = (obj, base=[]) => Object.keys(obj).sort().reduce(
export const flatternObject = (obj, base=[]) => Object.keys(obj).sort((a, b)=>a.localeCompare(b)).reduce(
(r, k) => {
r = r.concat(k);
const value = obj[k];

View File

@ -101,8 +101,6 @@ export function schemaOptionsEvalulator({
_.set(options, fieldOptionsPath, fieldOptions);
const rowIndexes = [FIELD_OPTIONS];
rows?.forEach((row, idx) => {
const schemaPath = [...fieldPath, idx];
const schemaOptions = _.get(options, schemaPath, {});
@ -123,8 +121,6 @@ export function schemaOptionsEvalulator({
option: 'row', schema: field.schema, value: row, viewHelperProps,
field, options: rowOptions, parentOptions: fieldOptions
});
rowIndexes.push(idx);
});
}

View File

@ -60,7 +60,7 @@ export const createFieldControls = ({
if(field.type === 'group') {
if (!field.id || (field.id in groups)) {
if (!field.id || (groups.includes(field.id))) {
throw new Error('Group-id must be unique within a schema.');
}

View File

@ -213,7 +213,6 @@ function UtilityViewContent({schema, treeNodeInfo, actionType, formType, onClose
}
UtilityViewContent.propTypes = {
panelId: PropTypes.string,
schema: PropTypes.object,
treeNodeInfo: PropTypes.object,
actionType: PropTypes.string,

View File

@ -155,7 +155,7 @@ export function Table({
// Render the UI for your table
const tableRef = useRef();
let flatData = [];
let fetchMoreOnBottomReached = undefined;
let fetchMoreOnBottomReached;
let totalFetched = 0;
let totalDBRowCount = 0;

View File

@ -1,3 +1,12 @@
/////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2025, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
import { useSingleAndDoubleClick } from '../../../custom_hooks';
import * as React from 'react';
import PropTypes from 'prop-types';
@ -15,4 +24,4 @@ DoubleClickHandler.propTypes = {
onSingleClick: PropTypes.func,
onDoubleClick: PropTypes.func,
children: CustomPropTypes.children
};
};

View File

@ -83,7 +83,7 @@ function alert(title, text, onOkClick, okLabel = gettext('OK')) {
});
}
function confirm(title, text, onOkClick, onCancelClick, okLabel = gettext('Yes'), cancelLabel = gettext('No'), okIcon) {
function confirm(title, text, onOkClick, onCancelClick, okLabel = gettext('Yes'), cancelLabel = gettext('No'), okIcon = 'default') {
// bind the modal provider before calling
this.showModal(title, (closeModal) => {
const onCancelClickClose = () => {

View File

@ -175,7 +175,7 @@ class Notifier {
this.modal.alert(title, text, onOkClick, okLabel);
}
confirm(title, text, onOkClick, onCancelClick, okLabel=gettext('Yes'), cancelLabel=gettext('No'), okIcon) {
confirm(title, text, onOkClick, onCancelClick, okLabel=gettext('Yes'), cancelLabel=gettext('No'), okIcon='default') {
/* Use this if you want to use pgAdmin global notifier.
Or else, if you want to use modal inside iframe only then use ModalProvider eg- query tool */
this.modal.confirm(title, text, onOkClick, onCancelClick, okLabel, cancelLabel, okIcon);

View File

@ -379,7 +379,7 @@ export function evalFunc(obj, func, ...param) {
export function getBrowser() {
if(navigator.userAgent.indexOf('Electron') >= 0) {
return {name: 'Electron', version: navigator.userAgent.match(/Electron\/([\d\.]+\d+)/)[1]};
return {name: 'Electron', version: /Electron\/([\d.]+\d+)/.exec(navigator.userAgent)[1]};
}
let ua=navigator.userAgent,tem,M=(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i).exec(ua) || [];
@ -388,7 +388,7 @@ export function getBrowser() {
return {name:'IE', version:(tem[1]||'')};
}
if(ua.indexOf('Electron') >= 0) {
return {name: 'Electron', version: ua.match(/Electron\/([\d\.]+\d+)/)[1]};
return {name: 'Electron', version: /Electron\/([\d.]+\d+)/.exec(ua)[1]};
}
if(M[1]==='Chrome') {

View File

@ -10,7 +10,7 @@ import { Box } from '@mui/material';
import { styled } from '@mui/material/styles';
import _ from 'lodash';
import React, {useState, useEffect, useContext, useRef, useLayoutEffect, useMemo, useCallback} from 'react';
import {Row, useRowSelection} from 'react-data-grid';
import {Row, useRowSelection, useHeaderRowSelection} from 'react-data-grid';
import LockIcon from '@mui/icons-material/Lock';
import EditIcon from '@mui/icons-material/Edit';
import { QUERY_TOOL_EVENTS } from '../QueryToolConstants';
@ -24,7 +24,6 @@ import gettext from 'sources/gettext';
import PgReactDataGrid from '../../../../../../static/js/components/PgReactDataGrid';
import { isMac } from '../../../../../../static/js/keyboard_shortcuts';
import { measureText } from '../../../../../../static/js/utils';
import { useHeaderRowSelection } from 'react-data-grid';
export const ROWNUM_KEY = '$_pgadmin_rownum_key_$';
export const GRID_ROW_SELECT_KEY = '$_pgadmin_gridrowselect_key_$';
@ -158,10 +157,9 @@ function SelectAllHeaderRenderer({isCellSelected}) {
}, [isRowSelected]);
return <div ref={cellRef} style={{width: '100%', height: '100%'}} onClick={onClick}
tabIndex="0" onKeyDown={(e)=>dataGridExtras.handleShortcuts(e, true)}></div>;
tabIndex="-1" onKeyDown={(e)=>dataGridExtras.handleShortcuts(e, true)}></div>;
}
SelectAllHeaderRenderer.propTypes = {
onAllRowsSelectionChange: PropTypes.func,
isCellSelected: PropTypes.bool,
};

View File

@ -310,17 +310,19 @@ function TheMap({data}) {
});
zoomControlWithHome.current._zoomHome = function () {
if (this.options.maxLength > 0) {
this._map.fitBounds(this.options.homeCoordinates);
let obj = this;
if (obj.options.maxLength > 0) {
obj._map.fitBounds(obj.options.homeCoordinates);
} else {
this.options.homeCoordinates && this._map.setView(this.options.homeCoordinates.getCenter(), this.options.homeZoom);
obj.options.homeCoordinates && obj._map.setView(obj.options.homeCoordinates.getCenter(), obj.options.homeZoom);
}
};
zoomControlWithHome.current.onAdd = function (map) {
let obj = this;
let controlName = 'leaflet-control-zoom',
container = Leaflet.DomUtil.create('div', controlName + ' leaflet-bar'),
options = this.options;
options = obj.options;
if (options.homeCoordinates === null) {
options.homeCoordinates = homeCoordinates.current?.bounds;
@ -334,12 +336,12 @@ function TheMap({data}) {
let zoomHomeText = `<i class="fa fa-${options.zoomHomeIcon}"></i>`;
this._zoomInButton = this._createButton(options.zoomInText, options.zoomInTitle, controlName + '-in', container, this._zoomIn.bind(this));
this._createButton(zoomHomeText, options.zoomHomeTitle, controlName + '-home', container, this._zoomHome.bind(this));
this._zoomOutButton = this._createButton(options.zoomOutText, options.zoomOutTitle, controlName + '-out', container, this._zoomOut.bind(this));
obj._zoomInButton = obj._createButton(options.zoomInText, options.zoomInTitle, controlName + '-in', container, obj._zoomIn.bind(this));
obj._createButton(zoomHomeText, options.zoomHomeTitle, controlName + '-home', container, obj._zoomHome.bind(this));
obj._zoomOutButton = obj._createButton(options.zoomOutText, options.zoomOutTitle, controlName + '-out', container, obj._zoomOut.bind(this));
this._updateDisabled();
map.on('zoomend zoomlevelschange', this._updateDisabled, this);
obj._updateDisabled();
map.on('zoomend zoomlevelschange', obj._updateDisabled, this);
return container;
};