mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-10-24 01:23:21 +08:00
[CB] SQL Query generation: front-end part (#4132)
* dbeaver/pro#8048 pass commit message validation * fixes json + translations * fixes gis generation * uses format action to get text value for sql generators
This commit is contained in:
@@ -32,6 +32,7 @@
|
||||
"@cloudbeaver/core-navigation-tree": "workspace:*",
|
||||
"@cloudbeaver/core-resource": "workspace:*",
|
||||
"@cloudbeaver/core-sdk": "workspace:*",
|
||||
"@cloudbeaver/core-utils": "workspace:*",
|
||||
"@cloudbeaver/core-view": "workspace:*",
|
||||
"@cloudbeaver/plugin-codemirror6": "workspace:*",
|
||||
"@cloudbeaver/plugin-data-viewer": "workspace:*",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2025 DBeaver Corp and others
|
||||
* Copyright (C) 2020-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -42,6 +42,7 @@ export class GeneratorMenuBootstrap extends Bootstrap {
|
||||
const resultIndex = context.get(DATA_CONTEXT_DV_DDM_RESULT_INDEX)!;
|
||||
const presentation = context.get(DATA_CONTEXT_DV_PRESENTATION);
|
||||
return (
|
||||
// TODO add more proper way to define to what features it should be added https://github.com/dbeaver/pro/issues/8299
|
||||
!model.isReadonly(resultIndex) &&
|
||||
model.source.getResult(resultIndex)?.dataFormat === ResultDataFormat.Resultset &&
|
||||
!presentation?.readonly &&
|
||||
|
||||
@@ -5,9 +5,7 @@
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
import { observable } from 'mobx';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import {
|
||||
Button,
|
||||
@@ -15,12 +13,8 @@ import {
|
||||
CommonDialogFooter,
|
||||
CommonDialogHeader,
|
||||
CommonDialogWrapper,
|
||||
ErrorMessage,
|
||||
Loader,
|
||||
s,
|
||||
useClipboard,
|
||||
useErrorDetails,
|
||||
useObservableRef,
|
||||
useResource,
|
||||
useS,
|
||||
useTranslate,
|
||||
@@ -28,16 +22,14 @@ import {
|
||||
import { ConnectionDialectResource, ConnectionInfoResource, createConnectionParam } from '@cloudbeaver/core-connections';
|
||||
import { useService } from '@cloudbeaver/core-di';
|
||||
import type { DialogComponentProps } from '@cloudbeaver/core-dialogs';
|
||||
import { GQLErrorCatcher } from '@cloudbeaver/core-sdk';
|
||||
import { useCodemirrorExtensions } from '@cloudbeaver/plugin-codemirror6';
|
||||
import { SQLCodeEditor, useSqlDialectExtension } from '@cloudbeaver/plugin-sql-editor-codemirror';
|
||||
|
||||
import style from './GeneratedSqlDialog.module.css';
|
||||
import { SqlGeneratorsResource } from './SqlGeneratorsResource.js';
|
||||
|
||||
interface Payload {
|
||||
generatorId: string;
|
||||
pathId: string;
|
||||
nodeId: string;
|
||||
query: string;
|
||||
}
|
||||
|
||||
export const GeneratedSqlDialog = observer<DialogComponentProps<Payload>>(function GeneratedSqlDialog({ rejectDialog, payload }) {
|
||||
@@ -45,34 +37,9 @@ export const GeneratedSqlDialog = observer<DialogComponentProps<Payload>>(functi
|
||||
const copy = useClipboard();
|
||||
const styles = useS(style);
|
||||
|
||||
const sqlGeneratorsResource = useService(SqlGeneratorsResource);
|
||||
const connectionInfoResource = useService(ConnectionInfoResource);
|
||||
const connection = connectionInfoResource.getConnectionForNode(payload.pathId);
|
||||
const connection = connectionInfoResource.getConnectionForNode(payload.nodeId);
|
||||
|
||||
const state = useObservableRef(
|
||||
() => ({
|
||||
query: '',
|
||||
loading: true,
|
||||
error: new GQLErrorCatcher(),
|
||||
async load() {
|
||||
this.error.clear();
|
||||
|
||||
try {
|
||||
this.query = await sqlGeneratorsResource.generateEntityQuery(payload.generatorId, payload.pathId);
|
||||
} catch (exception: any) {
|
||||
this.error.catch(exception);
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
}),
|
||||
{
|
||||
query: observable.ref,
|
||||
loading: observable.ref,
|
||||
connection: observable.ref,
|
||||
},
|
||||
{ connection },
|
||||
);
|
||||
const connectionDialectResource = useResource(GeneratedSqlDialog, ConnectionDialectResource, connection ? createConnectionParam(connection) : null);
|
||||
const sqlDialect = useSqlDialectExtension(connectionDialectResource.data);
|
||||
|
||||
@@ -80,34 +47,19 @@ export const GeneratedSqlDialog = observer<DialogComponentProps<Payload>>(functi
|
||||
if (sqlDialect) {
|
||||
extensions.set(...sqlDialect);
|
||||
}
|
||||
const error = useErrorDetails(state.error.exception);
|
||||
|
||||
useEffect(() => {
|
||||
state.load();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<CommonDialogWrapper size="large">
|
||||
<CommonDialogHeader title="app_shared_sql_generators_dialog_title" icon="sql-script" onReject={rejectDialog} />
|
||||
<CommonDialogBody noOverflow noBodyPadding>
|
||||
<div className={s(styles, { wrapper: true })}>
|
||||
<Loader loading={state.loading}>
|
||||
{() => <SQLCodeEditor className={s(styles, { sqlCodeEditorLoader: true })} value={state.query} extensions={extensions} readonly />}
|
||||
</Loader>
|
||||
<SQLCodeEditor className={s(styles, { sqlCodeEditorLoader: true })} value={payload.query} extensions={extensions} readonly />
|
||||
</div>
|
||||
</CommonDialogBody>
|
||||
<CommonDialogFooter>
|
||||
<div className={s(styles, { footerContainer: true })}>
|
||||
{state.error.responseMessage && (
|
||||
<ErrorMessage
|
||||
className={s(styles, { errorMessage: true })}
|
||||
text={state.error.responseMessage}
|
||||
hasDetails={error.hasDetails}
|
||||
onShowDetails={error.open}
|
||||
/>
|
||||
)}
|
||||
<div className={s(styles, { buttons: true })}>
|
||||
<Button variant="secondary" onClick={() => copy(state.query, true)}>
|
||||
<Button variant="secondary" disabled={!payload.query} onClick={() => copy(payload.query, true)}>
|
||||
{translate('ui_copy_to_clipboard')}
|
||||
</Button>
|
||||
<Button onClick={() => rejectDialog()}>{translate('ui_close')}</Button>
|
||||
|
||||
@@ -14,15 +14,17 @@ import { MenuBaseItem, MenuService } from '@cloudbeaver/core-view';
|
||||
|
||||
import { MENU_SQL_GENERATORS } from './MENU_SQL_GENERATORS.js';
|
||||
import { SqlGeneratorsResource } from './SqlGeneratorsResource.js';
|
||||
import { NotificationService } from '@cloudbeaver/core-events';
|
||||
|
||||
const GeneratedSqlDialog = importLazyComponent(() => import('./GeneratedSqlDialog.js').then(m => m.GeneratedSqlDialog));
|
||||
|
||||
@injectable(() => [SqlGeneratorsResource, CommonDialogService, MenuService])
|
||||
@injectable(() => [SqlGeneratorsResource, CommonDialogService, MenuService, NotificationService])
|
||||
export class SqlGeneratorsBootstrap extends Bootstrap {
|
||||
constructor(
|
||||
private readonly sqlGeneratorsResource: SqlGeneratorsResource,
|
||||
private readonly commonDialogService: CommonDialogService,
|
||||
private readonly menuService: MenuService,
|
||||
private readonly notificationService: NotificationService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
@@ -77,11 +79,16 @@ export class SqlGeneratorsBootstrap extends Bootstrap {
|
||||
tooltip: action.description,
|
||||
},
|
||||
{
|
||||
onSelect: () => {
|
||||
this.commonDialogService.open(GeneratedSqlDialog, {
|
||||
generatorId: action.id,
|
||||
pathId: node.id,
|
||||
});
|
||||
onSelect: async () => {
|
||||
try {
|
||||
const query = await this.sqlGeneratorsResource.generateEntityQuery(action.id, node.id);
|
||||
await this.commonDialogService.open(GeneratedSqlDialog, {
|
||||
query,
|
||||
nodeId: node.id,
|
||||
});
|
||||
} catch (e: any) {
|
||||
this.notificationService.logException(e, 'sql_generator_error_title');
|
||||
}
|
||||
},
|
||||
},
|
||||
),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2024 DBeaver Corp and others
|
||||
* Copyright (C) 2020-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -8,7 +8,7 @@
|
||||
import { injectable } from '@cloudbeaver/core-di';
|
||||
import { NavNodeInfoResource } from '@cloudbeaver/core-navigation-tree';
|
||||
import { CachedMapResource, isResourceAlias, type ResourceKey, resourceKeyList, ResourceKeyUtils } from '@cloudbeaver/core-resource';
|
||||
import { GraphQLService, type SqlQueryGenerator } from '@cloudbeaver/core-sdk';
|
||||
import { GraphQLService, type SqlGenerateResultSetQueryQueryVariables, type SqlQueryGenerator } from '@cloudbeaver/core-sdk';
|
||||
|
||||
export const MAX_GENERATORS_LENGTH = 15;
|
||||
|
||||
@@ -34,6 +34,12 @@ export class SqlGeneratorsResource extends CachedMapResource<string, SqlQueryGen
|
||||
return result.sqlGenerateEntityQuery;
|
||||
}
|
||||
|
||||
async generateResultSetSql(params: SqlGenerateResultSetQueryQueryVariables): Promise<string | null> {
|
||||
const response = await this.graphQLService.sdk.sqlGenerateResultSetQuery(params);
|
||||
|
||||
return response.sqlGenerateResultSetQuery;
|
||||
}
|
||||
|
||||
protected async loader(key: ResourceKey<string>): Promise<Map<string, SqlQueryGenerator[]>> {
|
||||
if (isResourceAlias(key)) {
|
||||
throw new Error('Aliases not supported by this resource.');
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2025 DBeaver Corp and others
|
||||
* Copyright (C) 2020-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -11,3 +11,4 @@ import './module.js';
|
||||
export * from './SqlGenerators/SqlGeneratorsResource.js';
|
||||
export * from './SqlGenerators/SqlGeneratorsBootstrap.js';
|
||||
export * from './actions/ACTION_SQL_GENERATE.js';
|
||||
export * from './SqlGenerators/GeneratedSqlDialog.js';
|
||||
|
||||
@@ -1 +1 @@
|
||||
export default [];
|
||||
export default [['sql_generator_error_title', 'SQL Generation Error']];
|
||||
|
||||
@@ -1 +1 @@
|
||||
export default [];
|
||||
export default [['sql_generator_error_title', 'Erreur de génération SQL']];
|
||||
|
||||
@@ -1 +1 @@
|
||||
export default [];
|
||||
export default [['sql_generator_error_title', 'Errore di generazione SQL']];
|
||||
|
||||
@@ -1 +1 @@
|
||||
export default [];
|
||||
export default [['sql_generator_error_title', 'Ошибка генерации SQL']];
|
||||
|
||||
@@ -1 +1 @@
|
||||
export default [];
|
||||
export default [['sql_generator_error_title', 'Lỗi tạo SQL']];
|
||||
|
||||
@@ -1 +1 @@
|
||||
export default [];
|
||||
export default [['sql_generator_error_title', 'SQL生成错误']];
|
||||
|
||||
@@ -40,6 +40,9 @@
|
||||
{
|
||||
"path": "../core-sdk"
|
||||
},
|
||||
{
|
||||
"path": "../core-utils"
|
||||
},
|
||||
{
|
||||
"path": "../core-view"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user