Ensure that restored query tool tabs display the correct title. #3319

pull/8870/head
Yogesh Mahajan 2025-06-19 12:12:54 +05:30 committed by GitHub
parent 5df2c7686c
commit f07eabedbe
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 117 additions and 50 deletions

View File

@ -16,7 +16,6 @@ import getApiInstance, {parseApiError} from '../../../static/js/api_instance';
import usePreferences, { setupPreferenceBroadcast } from '../../../preferences/static/js/store';
import checkNodeVisibility from '../../../static/js/check_node_visibility';
import * as showQueryTool from '../../../tools/sqleditor/static/js/show_query_tool';
import {getRandomInt} from 'sources/utils';
define('pgadmin.browser', [
'sources/gettext', 'sources/url_for', 'sources/pgadmin',
@ -304,30 +303,35 @@ define('pgadmin.browser', [
url_for('settings.get_application_state')
).then((res)=> {
if(res.data.success && res.data.data.result.length > 0){
let deleteToolDataIds = [];
_.each(res.data.data.result, function(toolState){
let toolNme = toolState.tool_name;
let toolDataId = `${toolNme}-${getRandomInt(1, 9999999)}`;
let toolDataId = `${toolNme}-${toolState.id}`;
let connectionInfo = toolState.connection_info;
localStorage.setItem(toolDataId, toolState.tool_data);
if (toolNme == 'sqleditor'){
showQueryTool.relaunchSqlTool(connectionInfo, toolDataId);
}else if(toolNme == 'psql'){
pgAdmin.Tools.Psql.openPsqlTool(null, null, connectionInfo);
}else if(toolNme == 'ERD'){
pgAdmin.Tools.ERD.showErdTool(null, null, false, connectionInfo, toolDataId);
}else if(toolNme == 'schema_diff'){
pgAdmin.Tools.SchemaDiff.launchSchemaDiff(toolDataId);
}else{
localStorage.setItem(toolDataId, toolState.tool_data);
deleteToolDataIds.push(toolState.id);
if(toolNme == 'psql'){
pgAdmin.Tools.Psql.openPsqlTool(null, null, connectionInfo);
}else if(toolNme == 'ERD'){
pgAdmin.Tools.ERD.showErdTool(null, null, false, connectionInfo, toolDataId);
}else if(toolNme == 'schema_diff'){
pgAdmin.Tools.SchemaDiff.launchSchemaDiff(toolDataId);
}
}
});
// call clear application state data.
try {
getApiInstance().delete(url_for('settings.delete_application_state'), {});
} catch (error) {
console.error(error);
pgAdmin.Browser.notifier.error(gettext('Failed to remove query data.') + parseApiError(error));
}
if(deleteToolDataIds.length > 0){
try {
getApiInstance().delete(url_for('settings.delete_application_state'), {data:{'trans_ids': deleteToolDataIds}});
} catch (error) {
console.error(error);
pgAdmin.Browser.notifier.error(gettext('Failed to remove query data.') + parseApiError(error));
}}
}
}).catch(function(error) {
pgAdmin.Browser.notifier.pgRespErrorNotify(error);

View File

@ -57,7 +57,8 @@ class SettingsModule(PgAdminModule):
'settings.get_file_format_setting',
'settings.save_application_state',
'settings.get_application_state',
'settings.delete_application_state'
'settings.delete_application_state',
'settings.get_tool_data'
]
@ -326,8 +327,7 @@ def get_last_saved_file_hash(file_path, trans_id):
@blueprint.route(
'/get_application_state',
methods=["GET"], endpoint='get_application_state'
)
methods=["GET"], endpoint='get_application_state')
@pga_login_required
def get_application_state():
"""
@ -370,16 +370,44 @@ def get_application_state():
)
@blueprint.route(
'/get_tool_data/<int:trans_id>',
methods=["GET"], endpoint='get_tool_data')
@pga_login_required
def get_tool_data(trans_id):
fernet = Fernet(current_app.config['SECRET_KEY'].encode())
result = db.session \
.query(ApplicationState) \
.filter(ApplicationState.uid == current_user.id,
ApplicationState.id == trans_id) \
.first()
return make_json_response(
data={
'status': True,
'msg': '',
'result': {
'tool_data': fernet.decrypt(result.tool_data).decode(),
}
}
)
@blueprint.route(
'/delete_application_state/',
methods=["DELETE"], endpoint='delete_application_state')
@pga_login_required
def delete_application_state():
trans_id = None
status = False
msg = gettext('Unable to delete application state data.')
if request.data:
data = json.loads(request.data)
trans_id = int(data['panelId'].split('_')[-1])
status, msg = delete_tool_data(trans_id)
if 'trans_ids' in data:
for trans_id in data['trans_ids']:
status, msg = delete_tool_data(trans_id)
else:
trans_id = int(data['panelId'].split('_')[-1])
status, msg = delete_tool_data(trans_id)
return make_json_response(
data={
'status': status,

View File

@ -37,7 +37,7 @@ export function deleteToolData(panelId, closePanelId){
}
};
export function ApplicationStateProvider({children}){
export function ApplicationStateProvider({children, toolDataId}){
const preferencesStore = usePreferences();
const saveAppState = preferencesStore?.getPreferencesForModule('misc')?.save_app_state;
const openNewTab = preferencesStore?.getPreferencesForModule('browser')?.new_browser_tab_open;
@ -63,9 +63,36 @@ export function ApplicationStateProvider({children}){
return saveAppState;
};
async function getQueryToolContent() {
try {
let transId = toolDataId.split('-')[1];
const res = await getApiInstance({'Content-Encoding': 'gzip'}).get(
url_for('settings.get_tool_data', {
'trans_id': transId,
})
);
return res.data.success? JSON.parse(res.data.data.result.tool_data): null;
} catch (error) {
pgAdmin.Browser.notifier.pgRespErrorNotify(error);
return null;
}
}
const deleteToolData = ()=>{
let transId = toolDataId.split('-')[1];
getApiInstance().delete(
url_for('settings.delete_application_state'), {data:{'panelId': transId}}
).then(()=> { /* Sonar Qube */}).catch(function(error) {
pgAdmin.Browser.notifier.pgRespErrorNotify(error);
});
};
const value = useMemo(()=>({
saveToolData,
isSaveToolDataEnabled,
getQueryToolContent,
deleteToolData
}), []);
return <ApplicationStateContext.Provider value={value}>
@ -75,5 +102,6 @@ export function ApplicationStateProvider({children}){
}
ApplicationStateProvider.propTypes = {
children: PropTypes.object
children: PropTypes.object,
toolDataId: PropTypes.string
};

View File

@ -45,7 +45,7 @@ function TabTitle({id, closable, defaultInternal}) {
}, []);
useEffect(()=>{
const deregister = layoutDocker.eventBus.registerListener(LAYOUT_EVENTS.REFRESH_TITLE, _.debounce((panelId)=>{
const deregister = layoutDocker.eventBus.registerListener(LAYOUT_EVENTS.REFRESH_TITLE, (panelId)=>{
if(panelId == id) {
const internal = layoutDocker?.find(id)?.internal??{};
setAttrs({
@ -54,7 +54,7 @@ function TabTitle({id, closable, defaultInternal}) {
tooltip: internal.tooltip ?? internal.title,
});
}
}, 100));
});
return ()=>deregister?.();
}, []);

View File

@ -360,6 +360,8 @@ export default class ERDTool extends React.Component {
this.diagram.deserialize(sqlValue);
this.diagram.clearSelection();
this.registerModelEvents();
this.setState({dirty: true});
this.eventBus.fireEvent(ERD_EVENTS.DIRTY, true, this.serializeFile());
}
}
else if(this.props.params.gen) {

View File

@ -220,9 +220,7 @@ export default class SQLEditor {
let browser_preferences = usePreferences.getState().getPreferencesForModule('browser');
let open_new_tab = browser_preferences.new_browser_tab_open;
const [icon, tooltip] = panelTitleFunc.getQueryToolIcon(panel_title, is_query_tool);
let selectedNodeInfo = pgAdmin.Browser.tree?.getTreeNodeHierarchy(
pgAdmin.Browser.tree.selected()
);
let selectedNodeInfo = pgAdmin.Browser.tree?.selected() ? pgAdmin.Browser.tree?.getTreeNodeHierarchy(pgAdmin.Browser.tree.selected()) : null;
pgAdmin.Browser.Events.trigger(
'pgadmin:tool:show',
@ -248,7 +246,7 @@ export default class SQLEditor {
root.render(
<Theme>
<PgAdminProvider value={pgAdmin}>
<ApplicationStateProvider>
<ApplicationStateProvider toolDataId={params.toolDataId}>
<ModalProvider>
<NotifierProvider pgAdmin={pgAdmin} pgWindow={pgWindow} />
{ params.error ?

View File

@ -166,7 +166,7 @@ export default function QueryToolComponent({params, pgWindow, pgAdmin, selectedN
const docker = useRef(null);
const api = useMemo(()=>getApiInstance(), []);
const modal = useModal();
const {isSaveToolDataEnabled} = useApplicationState();
const {isSaveToolDataEnabled, deleteToolData} = useApplicationState();
/* Connection status poller */
let pollTime = qtState.preferences.sqleditor.connection_status_fetch_time > 0
@ -283,7 +283,7 @@ export default function QueryToolComponent({params, pgWindow, pgAdmin, selectedN
eventBus.current.fireEvent(QUERY_TOOL_EVENTS.HANDLE_API_ERROR, err);
setQtStatePartial({ editor_disabled: true });
});
} else if (qtState.params.sql_id) {
} else if (qtState.params.toolDataId) {
populateEditorData();
} else {
setQtStatePartial({ editor_disabled: false });
@ -291,23 +291,24 @@ export default function QueryToolComponent({params, pgWindow, pgAdmin, selectedN
};
const populateEditorData = () =>{
let sqlId = qtState.params.sql_id,
loadSqlFromLocalStorage = true;
let populateQueryContent = true;
if(qtState.params.open_file_name){
if(qtState.params.file_deleted == 'false' && qtState.params.is_editor_dirty == 'false'){
// call load file from disk as no fil changes
// call load file from disk as no file changes
eventBus.current.fireEvent(QUERY_TOOL_EVENTS.LOAD_FILE, qtState.params.open_file_name, qtState.params?.storage);
populateQueryContent = false;
deleteToolData();
}else if(qtState.params.file_deleted != 'true'){
if(qtState.params.external_file_changes == 'true'){
loadSqlFromLocalStorage = false;
eventBus.current.fireEvent(QUERY_TOOL_EVENTS.WARN_RELOAD_FILE, qtState.params.open_file_name, sqlId);
populateQueryContent = false;
eventBus.current.fireEvent(QUERY_TOOL_EVENTS.WARN_RELOAD_FILE, qtState.params.open_file_name);
}else{
eventBus.current.fireEvent(QUERY_TOOL_EVENTS.LOAD_FILE_DONE, qtState.params.open_file_name, true);
}
}
}
if(loadSqlFromLocalStorage) eventBus.current.fireEvent(QUERY_TOOL_EVENTS.LOAD_SQL_FROM_LOCAL_STORAGE, sqlId);
if(populateQueryContent) eventBus.current.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_GET_QUERY_CONTENT);
setQtStatePartial({ editor_disabled: false });
};
@ -545,7 +546,7 @@ export default function QueryToolComponent({params, pgWindow, pgAdmin, selectedN
});
isDirtyRef.current = false;
setPanelTitle(qtPanelDocker, qtPanelId, fileName, {...qtState, current_file: fileName}, isDirtyRef.current);
if(isSaveToolDataEnabled('sqleditor'))eventBus.current.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_SAVE_QUERY_TOOL_DATA);
}
eventBus.current.fireEvent(QUERY_TOOL_EVENTS.EDITOR_LAST_FOCUS);
@ -584,6 +585,8 @@ export default function QueryToolComponent({params, pgWindow, pgAdmin, selectedN
[QUERY_TOOL_EVENTS.LOAD_FILE_DONE, fileDone],
[QUERY_TOOL_EVENTS.SAVE_FILE_DONE, fileDone],
[QUERY_TOOL_EVENTS.QUERY_CHANGED, (isDirty)=>{
if(isDirtyRef.current === isDirty) return;
isDirtyRef.current = isDirty;
if(qtState.params.is_query_tool) {
setPanelTitle(qtPanelDocker, qtPanelId, null, qtState, isDirty);

View File

@ -29,6 +29,7 @@ export const QUERY_TOOL_EVENTS = {
TRIGGER_GRAPH_VISUALISER: 'TRIGGER_GRAPH_VISUALISER',
TRIGGER_SELECT_ALL: 'TRIGGER_SELECT_ALL',
TRIGGER_SAVE_QUERY_TOOL_DATA: 'TRIGGER_SAVE_QUERY_TOOL_DATA',
TRIGGER_GET_QUERY_CONTENT: 'TRIGGER_GET_QUERY_CONTENT',
COPY_DATA: 'COPY_DATA',
SET_LIMIT_VALUE: 'SET_LIMIT_VALUE',

View File

@ -23,10 +23,9 @@ import ConfirmExecuteQueryContent from '../dialogs/ConfirmExecuteQueryContent';
import usePreferences from '../../../../../../preferences/static/js/store';
import { getTitle } from '../../sqleditor_title';
import PropTypes from 'prop-types';
import { useApplicationState, getToolData } from '../../../../../../settings/static/ApplicationStateProvider';
import { useApplicationState } from '../../../../../../settings/static/ApplicationStateProvider';
import { useDelayDebounce } from '../../../../../../static/js/custom_hooks';
async function registerAutocomplete(editor, api, transId) {
editor.registerAutocomplete((context, onAvailable)=>{
return new Promise((resolve, reject)=>{
@ -64,7 +63,7 @@ export default function Query({onTextSelect, setQtStatePartial}) {
const layoutDocker = useContext(LayoutDockerContext);
const lastCursorPos = React.useRef();
const pgAdmin = usePgAdmin();
const {saveToolData, isSaveToolDataEnabled} = useApplicationState();
const {saveToolData, isSaveToolDataEnabled, getQueryToolContent, deleteToolData} = useApplicationState();
const preferencesStore = usePreferences();
const modalId = MODAL_DIALOGS.QT_CONFIRMATIONS;
@ -160,15 +159,16 @@ export default function Query({onTextSelect, setQtStatePartial}) {
}
};
const warnReloadFile = (fileName, sqlId, storage=null)=>{
const warnReloadFile = (fileName, storage=null)=>{
queryToolCtx.modal.confirm(
gettext('Reload file?'),
gettext('The file has been modified by another program. Do you want to reload it and loose changes made in pgadmin?'),
function() {
eventBus.fireEvent(QUERY_TOOL_EVENTS.LOAD_FILE, fileName);
deleteToolData();
},
function() {
eventBus.fireEvent(QUERY_TOOL_EVENTS.LOAD_SQL_FROM_LOCAL_STORAGE, sqlId);
eventBus.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_GET_QUERY_CONTENT);
eventBus.fireEvent(QUERY_TOOL_EVENTS.LOAD_FILE_DONE, fileName, true, storage);
}
);
@ -244,6 +244,7 @@ export default function Query({onTextSelect, setQtStatePartial}) {
focus && editor.current?.focus();
editor.current?.setValue(value, !queryToolCtx.params.is_query_tool);
});
eventBus.registerListener(QUERY_TOOL_EVENTS.TRIGGER_QUERY_CHANGE, ()=>{
change();
});
@ -283,10 +284,12 @@ export default function Query({onTextSelect, setQtStatePartial}) {
setSaveQtData(true);
});
eventBus.registerListener(QUERY_TOOL_EVENTS.LOAD_SQL_FROM_LOCAL_STORAGE, (sqlId)=>{
let sqlValue = getToolData(sqlId);
if (sqlValue) {
eventBus.registerListener(QUERY_TOOL_EVENTS.TRIGGER_GET_QUERY_CONTENT, async ()=>{
let sqlValue = await getQueryToolContent();
if(sqlValue){
eventBus.fireEvent(QUERY_TOOL_EVENTS.EDITOR_SET_SQL, sqlValue);
// call delete appplication state api
deleteToolData();
}
});
}, []);

View File

@ -114,7 +114,7 @@ export function showERDSqlTool(parentData, erdSqlId, queryToolTitle, queryToolMo
launchQueryTool(queryToolMod, transId, gridUrl, queryToolTitle, {});
}
export function relaunchSqlTool(connectionInfo, sqlId){
export function relaunchSqlTool(connectionInfo, toolDataId){
let browserPref = usePreferences.getState().getPreferencesForModule('browser');
let parentData = {
server_group: {
@ -135,7 +135,7 @@ export function relaunchSqlTool(connectionInfo, sqlId){
const qtUrl = generateUrl(transId, parentData, null);
const title = getTitle(pgAdmin, browserPref, parentData, false, connectionInfo.server_name, connectionInfo.database_name, connectionInfo.role || connectionInfo.user);
launchQueryTool(pgWindow.pgAdmin.Tools.SQLEditor, transId, qtUrl, title, {
sql_id: sqlId,
toolDataId: toolDataId,
...connectionInfo,
});