Skip to content

Commit a60fe00

Browse files
Merge pull request #12 from actiontech/fix/dazong-issue
Fix/dazong issue
2 parents 162f839 + d60e162 commit a60fe00

File tree

17 files changed

+418
-26
lines changed

17 files changed

+418
-26
lines changed

webapp/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,5 +40,6 @@
4040
"typescript-plugin-css-modules": "^5.0.1"
4141
},
4242
"dependencies": {},
43-
"prettier": "@cloudbeaver/prettier-config"
43+
"prettier": "@cloudbeaver/prettier-config",
44+
"packageManager": "[email protected]+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
4445
}

webapp/packages/core-blocks/src/useErrorDetails.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ interface IErrorDetailsHook {
2323
isOpen: boolean;
2424
open: () => void;
2525
refresh?: () => void;
26+
errorCode?: string;
2627
}
2728

2829
type HookType =
@@ -52,6 +53,7 @@ export function useErrorDetails(error: Error | null): HookType {
5253
const name = error?.name;
5354
const message = error?.message;
5455
const executionFailedMessage = (error as any)?.executionFailedMessage as string;
56+
const errorCode = (error as any)?.errorCode as string;
5557

5658
return {
5759
name,
@@ -63,5 +65,6 @@ export function useErrorDetails(error: Error | null): HookType {
6365
open,
6466
refresh: loadingError?.refresh,
6567
executionFailedMessage,
68+
errorCode,
6669
};
6770
}

webapp/packages/core-root/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,4 @@
2727
"mobx": "~6.x.x",
2828
"msw": "~1.x.x"
2929
}
30-
}
30+
}

webapp/packages/plugin-data-viewer/package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@
3737
"@cloudbeaver/core-theming": "~0.1.0",
3838
"@cloudbeaver/core-ui": "~0.1.0",
3939
"@cloudbeaver/core-utils": "~0.1.0",
40-
"@cloudbeaver/core-view": "~0.1.0"
40+
"@cloudbeaver/core-view": "~0.1.0",
41+
"@cloudbeaver/plugin-sql-editor": "~0.1.0",
42+
"@cloudbeaver/plugin-sql-editor-navigation-tab": "~0.1.0"
4143
},
4244
"peerDependencies": {
4345
"react": "~18.x.x",
@@ -46,4 +48,4 @@
4648
"reshadow": "~0.x.x",
4749
"@testing-library/jest-dom": "~6.x.x"
4850
}
49-
}
51+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
.container {
2+
display: flex;
3+
flex-direction: column;
4+
gap: 16px;
5+
}
6+
7+
.message {
8+
color: var(--theme-text-on-surface);
9+
line-height: 1.5;
10+
}
11+
12+
.queryInfo {
13+
display: flex;
14+
flex-direction: column;
15+
gap: 8px;
16+
padding: 12px;
17+
background-color: var(--theme-background-secondary);
18+
border-radius: 4px;
19+
border: 1px solid var(--theme-divider);
20+
}
21+
22+
.queryLabel {
23+
font-weight: 500;
24+
color: var(--theme-text-on-surface);
25+
font-size: 14px;
26+
}
27+
28+
.queryPreview {
29+
font-family: monospace;
30+
font-size: 13px;
31+
color: var(--theme-text-on-surface-secondary);
32+
word-break: break-all;
33+
white-space: pre-wrap;
34+
max-height: 120px;
35+
overflow-y: auto;
36+
}
37+
38+
.footer {
39+
display: flex;
40+
align-items: center;
41+
gap: 12px;
42+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { Button, Fill, s, Translate, useS } from '@cloudbeaver/core-blocks';
2+
import { CommonDialogBody, CommonDialogFooter, CommonDialogHeader, CommonDialogWrapper, type DialogComponent } from '@cloudbeaver/core-dialogs';
3+
4+
import style from './SqlEditorSessionClosedDialog.m.css';
5+
6+
export interface SqlEditorSessionClosedDialogPayload {
7+
query?: string;
8+
}
9+
10+
export const SqlEditorSessionClosedDialog: DialogComponent<SqlEditorSessionClosedDialogPayload, boolean> = function SqlSessionClosedDialog({
11+
payload,
12+
resolveDialog,
13+
rejectDialog,
14+
className,
15+
}) {
16+
const styles = useS(style);
17+
18+
return (
19+
<CommonDialogWrapper size="medium" className={className} fixedWidth>
20+
<CommonDialogHeader title="plugin_data_viewer_sql_session_closed_title" icon="/icons/info_icon.svg" onReject={rejectDialog} />
21+
<CommonDialogBody>
22+
<div className={s(styles, { container: true })}>
23+
<div className={s(styles, { message: true })}>
24+
<Translate token="plugin_data_viewer_sql_session_closed_message" />
25+
</div>
26+
{payload.query && (
27+
<div className={s(styles, { queryInfo: true })}>
28+
<div className={s(styles, { queryLabel: true })}>
29+
<Translate token="plugin_data_viewer_sql_session_closed_query_label" />
30+
</div>
31+
<div className={s(styles, { queryPreview: true })} title={payload.query}>
32+
{payload.query.length > 100 ? `${payload.query.substring(0, 100)}...` : payload.query}
33+
</div>
34+
</div>
35+
)}
36+
</div>
37+
</CommonDialogBody>
38+
<CommonDialogFooter className={s(styles, { footer: true })}>
39+
<Button type="button" mod={['outlined']} onClick={rejectDialog}>
40+
<Translate token="ui_processing_cancel" />
41+
</Button>
42+
<Fill />
43+
<Button type="button" mod={['raised']} onClick={() => resolveDialog(true)}>
44+
<Translate token="plugin_data_viewer_sql_session_closed_open_editor" />
45+
</Button>
46+
</CommonDialogFooter>
47+
</CommonDialogWrapper>
48+
);
49+
};

webapp/packages/plugin-data-viewer/src/TableViewer/TableError.tsx

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,22 @@
88
import { compressToEncodedURIComponent } from 'lz-string';
99
import { observable } from 'mobx';
1010
import { observer } from 'mobx-react-lite';
11+
import { useCallback, useEffect } from 'react';
1112
import styled, { css, use } from 'reshadow';
1213

1314
import { Button, IconOrImage, useErrorDetails, useObservableRef, useStateDelay, useTranslate } from '@cloudbeaver/core-blocks';
15+
import { ConnectionInfoResource, createConnectionParam } from '@cloudbeaver/core-connections';
1416
import { useService } from '@cloudbeaver/core-di';
17+
import { CommonDialogService, DialogueStateResult } from '@cloudbeaver/core-dialogs';
1518
import { ServerErrorType, ServerInternalError } from '@cloudbeaver/core-sdk';
1619
import { errorOf } from '@cloudbeaver/core-utils';
1720
import { ConnectionSchemaManagerService } from '@cloudbeaver/plugin-datasource-context-switch';
1821
import { NavigationTabsService } from '@cloudbeaver/plugin-navigation-tabs';
19-
import { SqlDataSourceService } from '@cloudbeaver/plugin-sql-editor';
22+
import { LocalStorageSqlDataSource, SqlDataSourceService } from '@cloudbeaver/plugin-sql-editor';
23+
import { isSQLEditorTab, SqlEditorNavigatorService } from '@cloudbeaver/plugin-sql-editor-navigation-tab';
2024

2125
import type { IDatabaseDataModel } from '../DatabaseDataModel/IDatabaseDataModel';
26+
import { SqlEditorSessionClosedDialog } from './SqlEditorSessionClosedDialog';
2227

2328
const style = css`
2429
error {
@@ -110,6 +115,9 @@ export const TableError = observer<Props>(function TableError({ model, loading,
110115
const connectionSchemaManagerService = useService(ConnectionSchemaManagerService);
111116
const sqlDataSourceService = useService(SqlDataSourceService);
112117
const navigationTabsService = useService(NavigationTabsService);
118+
const commonDialogService = useService(CommonDialogService);
119+
const sqlEditorNavigatorService = useService(SqlEditorNavigatorService);
120+
const connectionInfo = useService(ConnectionInfoResource);
113121

114122
const errorInfo = useObservableRef<ErrorInfo>(
115123
() => ({
@@ -128,11 +136,6 @@ export const TableError = observer<Props>(function TableError({ model, loading,
128136
false,
129137
);
130138

131-
if (errorInfo.error !== model.source.error) {
132-
errorInfo.error = model.source.error || null;
133-
errorInfo.display = !!model.source.error;
134-
}
135-
136139
const internalServerError = errorOf(model.source.error, ServerInternalError);
137140
const error = useErrorDetails(model.source.error);
138141
const animated = useStateDelay(!!errorInfo.error && !loading, 1);
@@ -175,6 +178,50 @@ export const TableError = observer<Props>(function TableError({ model, loading,
175178
};
176179
}
177180

181+
// 处理SQL会话关闭错误的重新打开编辑器逻辑
182+
const handleReopenEditor = useCallback(async () => {
183+
const contextId = navigationTabsService.getView()?.context.id ?? '';
184+
const dataSource = sqlDataSourceService.get(contextId);
185+
186+
if (dataSource) {
187+
const query = dataSource.script || '';
188+
const shouldReopen = await commonDialogService.open(SqlEditorSessionClosedDialog, { query });
189+
if (shouldReopen === true || shouldReopen === DialogueStateResult.Resolved) {
190+
const relatedTab = navigationTabsService.findTab(isSQLEditorTab(tab => tab.id === contextId));
191+
if (relatedTab) {
192+
await navigationTabsService.closeTab(relatedTab.id, true);
193+
194+
const executionContext = dataSource?.executionContext;
195+
196+
if (executionContext) {
197+
const connection = executionContext
198+
? connectionInfo.get(createConnectionParam(executionContext.projectId, executionContext.connectionId))
199+
: undefined;
200+
201+
await sqlEditorNavigatorService.openNewEditor({
202+
dataSourceKey: LocalStorageSqlDataSource.key,
203+
connectionKey: connection && createConnectionParam(connection),
204+
query: query,
205+
});
206+
}
207+
}
208+
}
209+
}
210+
}, [navigationTabsService, sqlDataSourceService, commonDialogService, connectionInfo, sqlEditorNavigatorService]);
211+
212+
useEffect(() => {
213+
const SQL_CONTEXT_ERROR_CODE = '508';
214+
if (errorInfo.error !== model.source.error) {
215+
errorInfo.error = model.source.error || null;
216+
errorInfo.display = !!model.source.error;
217+
}
218+
console.log(error, model.source.error);
219+
const isSqlContextError = error.errorCode === SQL_CONTEXT_ERROR_CODE || /SQL context .* not found/i.test(error.message || '');
220+
if (isSqlContextError) {
221+
handleReopenEditor();
222+
}
223+
}, [error.message, handleReopenEditor, model.source.error, errorInfo]);
224+
178225
return styled(style)(
179226
<error {...use({ animated, collapsed: !errorInfo.display, errorHidden })} className={className}>
180227
<error-body>

webapp/packages/plugin-data-viewer/src/locales/en.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,12 @@ export default [
4949
['settings_data_editor_fetch_max_description', 'Maximum number of rows to fetch'],
5050
['settings_data_editor_fetch_default_name', 'Default fetch size'],
5151
['settings_data_editor_fetch_default_description', 'Default number of rows to fetch'],
52+
53+
['plugin_data_viewer_sql_session_closed_title', 'SQL Execution Session Closed'],
54+
[
55+
'plugin_data_viewer_sql_session_closed_message',
56+
'The current SQL execution session has been closed, possibly due to connection timeout or manual disconnection. You can reopen the SQL editor and retain the current query statement.',
57+
],
58+
['plugin_data_viewer_sql_session_closed_query_label', 'Current Query:'],
59+
['plugin_data_viewer_sql_session_closed_open_editor', 'Reopen Editor'],
5260
];

webapp/packages/plugin-data-viewer/src/locales/zh.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,11 @@ export default [
4949
['settings_data_editor_fetch_max_description', 'Maximum number of rows to fetch'],
5050
['settings_data_editor_fetch_default_name', 'Default fetch size'],
5151
['settings_data_editor_fetch_default_description', 'Default number of rows to fetch'],
52+
['plugin_data_viewer_sql_session_closed_title', 'SQL执行会话已关闭'],
53+
[
54+
'plugin_data_viewer_sql_session_closed_message',
55+
'当前SQL执行会话已关闭,可能是由于连接超时或手动断开导致的。您可以重新打开SQL编辑器并保留当前的查询语句。',
56+
],
57+
['plugin_data_viewer_sql_session_closed_query_label', '当前查询语句:'],
58+
['plugin_data_viewer_sql_session_closed_open_editor', '重新打开编辑器'],
5259
];

webapp/packages/plugin-root/src/SessionExpireDialog/SessionExpiredDialog.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,22 @@
88
import { observer } from 'mobx-react-lite';
99

1010
import { Button, s, useS, useTranslate } from '@cloudbeaver/core-blocks';
11-
import { useService } from '@cloudbeaver/core-di';
1211
import { CommonDialogBody, CommonDialogFooter, CommonDialogHeader, CommonDialogWrapper, DialogComponent } from '@cloudbeaver/core-dialogs';
13-
import { RouterService } from '@cloudbeaver/core-routing';
1412

1513
import style from '../ServerNodeChangedDialog/ServerNodeChangedDialog.m.css';
1614

1715
export const SessionExpiredDialog: DialogComponent<null, null> = observer(function SessionExpiredDialog({ rejectDialog }) {
1816
const styles = useS(style);
19-
const routerService = useService(RouterService);
17+
// const routerService = useService(RouterService);
2018
const translate = useTranslate();
2119
function reload() {
22-
routerService.reload();
20+
// routerService.reload();
21+
const currentSearch = window.location.search;
22+
23+
document.cookie = 'cb-session-id=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
24+
localStorage.removeItem('TOKEN');
25+
const DMS_REDIRECT_KEY_PARAMS_NAME = 'target';
26+
window.location.href = `/login?${DMS_REDIRECT_KEY_PARAMS_NAME}=${encodeURIComponent('/project/700300/cloud-beaver' + currentSearch)}`;
2327
}
2428

2529
return (

0 commit comments

Comments
 (0)