From a18b1bef51bd4222405b9487ad7cedeb737071e9 Mon Sep 17 00:00:00 2001 From: sergeyteleshev Date: Wed, 29 Jul 2026 11:27:13 +0200 Subject: [PATCH 1/7] dbeaver/pro#4551 adds full DDL for GeneratedSql dialog --- .../queries/asyncSqlGenerateEntityQuery.gql | 5 +++++ .../src/queries/sqlGenerateEntityQuery.gql | 3 --- .../DataGridContextMenuGenerateSqlService.ts | 2 ++ .../plugin-sql-generator/package.json | 1 + .../src/SqlGenerators/GeneratedSqlDialog.tsx | 18 ++++++++++++++- .../SqlGenerators/SqlGeneratorsBootstrap.ts | 1 + .../SqlGenerators/SqlGeneratorsResource.ts | 22 ++++++++++++++----- .../plugin-sql-generator/src/locales/en.ts | 1 + .../plugin-sql-generator/src/locales/fr.ts | 1 + .../plugin-sql-generator/src/locales/it.ts | 1 + .../plugin-sql-generator/src/locales/ru.ts | 1 + .../plugin-sql-generator/src/locales/vi.ts | 1 + .../plugin-sql-generator/src/locales/zh.ts | 1 + .../plugin-sql-generator/tsconfig.json | 3 +++ webapp/yarn.lock | 1 + 15 files changed, 52 insertions(+), 10 deletions(-) create mode 100644 webapp/packages/core-sdk/src/queries/asyncSqlGenerateEntityQuery.gql delete mode 100644 webapp/packages/core-sdk/src/queries/sqlGenerateEntityQuery.gql diff --git a/webapp/packages/core-sdk/src/queries/asyncSqlGenerateEntityQuery.gql b/webapp/packages/core-sdk/src/queries/asyncSqlGenerateEntityQuery.gql new file mode 100644 index 00000000000..3cc9c98b026 --- /dev/null +++ b/webapp/packages/core-sdk/src/queries/asyncSqlGenerateEntityQuery.gql @@ -0,0 +1,5 @@ +mutation asyncSqlGenerateEntityQuery($generatorId: String!, $nodePathList: [String!]!, $generatorOptions: SQLQueryGeneratorOptions) { + taskInfo: asyncSqlGenerateEntityQuery(generatorId: $generatorId, nodePathList: $nodePathList, generatorOptions: $generatorOptions) { + ...AsyncTaskInfo + } +} diff --git a/webapp/packages/core-sdk/src/queries/sqlGenerateEntityQuery.gql b/webapp/packages/core-sdk/src/queries/sqlGenerateEntityQuery.gql deleted file mode 100644 index aa437c90138..00000000000 --- a/webapp/packages/core-sdk/src/queries/sqlGenerateEntityQuery.gql +++ /dev/null @@ -1,3 +0,0 @@ -query sqlGenerateEntityQuery($generatorId: String!, $nodePathList: [String!]!, $generatorOptions: SQLQueryGeneratorOptions) { - sqlGenerateEntityQuery(generatorId: $generatorId, nodePathList: $nodePathList, generatorOptions: $generatorOptions) -} diff --git a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridContextMenu/DataGridContextMenuGenerateSqlService.ts b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridContextMenu/DataGridContextMenuGenerateSqlService.ts index 7e80a0cc1e5..be59e15d0f6 100644 --- a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridContextMenu/DataGridContextMenuGenerateSqlService.ts +++ b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridContextMenu/DataGridContextMenuGenerateSqlService.ts @@ -179,6 +179,7 @@ export class DataGridContextMenuGenerateSqlService { query, nodeId: nodePath ?? connectionId, nodeName: this.getEntityNameFromNodePath(containerNodePath, model.name), + generatorId, generatorName: this.localizationService.translate(generatorLabel), options: getDefaultQueryGeneratorOptions(), regenerateQuery: options => @@ -234,6 +235,7 @@ export class DataGridContextMenuGenerateSqlService { query, nodeId: nodePathList, nodeName: this.getEntityNameFromNodePath(nodePathList, model.name), + generatorId: createGenerator.id, generatorName: createGenerator.label, options: getDefaultQueryGeneratorOptions(), regenerateQuery: genOptions => this.sqlGenerationResource.generateEntityQuery(createGenerator.id, nodePathList, genOptions), diff --git a/webapp/packages/plugin-sql-generator/package.json b/webapp/packages/plugin-sql-generator/package.json index 92bd0f40de3..9ac2e3a58d8 100644 --- a/webapp/packages/plugin-sql-generator/package.json +++ b/webapp/packages/plugin-sql-generator/package.json @@ -30,6 +30,7 @@ "@cloudbeaver/core-localization": "workspace:*", "@cloudbeaver/core-navigation-tree": "workspace:*", "@cloudbeaver/core-resource": "workspace:*", + "@cloudbeaver/core-root": "workspace:*", "@cloudbeaver/core-sdk": "workspace:*", "@cloudbeaver/core-utils": "workspace:*", "@cloudbeaver/core-view": "workspace:*", diff --git a/webapp/packages/plugin-sql-generator/src/SqlGenerators/GeneratedSqlDialog.tsx b/webapp/packages/plugin-sql-generator/src/SqlGenerators/GeneratedSqlDialog.tsx index e448a1061f8..396444e2a53 100644 --- a/webapp/packages/plugin-sql-generator/src/SqlGenerators/GeneratedSqlDialog.tsx +++ b/webapp/packages/plugin-sql-generator/src/SqlGenerators/GeneratedSqlDialog.tsx @@ -33,9 +33,12 @@ import { action, observable } from 'mobx'; import { NotificationService } from '@cloudbeaver/core-events'; import type { SqlQueryGeneratorOptions } from '@cloudbeaver/core-sdk'; +import { DDL_GENERATOR_ID } from './SqlGeneratorsResource.js'; + interface Payload { nodeId: string; nodeName?: string; + generatorId: string; generatorName?: string; query: string; options?: SqlQueryGeneratorOptions; @@ -51,6 +54,7 @@ export const GeneratedSqlDialog = observer>(functi () => ({ useFullyQualifiedNames: payload.options?.useFullyQualifiedNames ?? true, compactSql: payload.options?.compactSql ?? false, + showFullDdl: payload.options?.showFullDdl, query: payload.query, loading: false, handleOptionChange: async function handleOptionChange(key: T, value: (typeof state)[T]) { @@ -61,6 +65,7 @@ export const GeneratedSqlDialog = observer>(functi this.query = await this.payload.regenerateQuery({ compactSql: this.compactSql, useFullyQualifiedNames: this.useFullyQualifiedNames, + showFullDdl: this.showFullDdl, }); } catch (error: any) { this.notificationService.logException(error, 'app_shared_sql_generators_error_title'); @@ -73,6 +78,7 @@ export const GeneratedSqlDialog = observer>(functi handleOptionChange: action.bound, useFullyQualifiedNames: observable.ref, compactSql: observable.ref, + showFullDdl: observable.ref, query: observable.ref, loading: observable.ref, }, @@ -96,6 +102,7 @@ export const GeneratedSqlDialog = observer>(functi } const visibleLoading = useStateDelay(state.loading, 300); + const isDdlGenerator = payload.generatorId.toLowerCase().includes(DDL_GENERATOR_ID.toLowerCase()); async function handleOpenInEditor() { try { @@ -140,7 +147,6 @@ export const GeneratedSqlDialog = observer>(functi label={translate('app_shared_sql_generators_use_fully_qualified_names')} onChange={value => state.handleOptionChange('useFullyQualifiedNames', value)} /> - >(functi label={translate('app_shared_sql_generators_compact_sql')} onChange={value => state.handleOptionChange('compactSql', value)} /> + {isDdlGenerator && ( + state.handleOptionChange('showFullDdl', value)} + /> + )}
diff --git a/webapp/packages/plugin-sql-generator/src/SqlGenerators/SqlGeneratorsBootstrap.ts b/webapp/packages/plugin-sql-generator/src/SqlGenerators/SqlGeneratorsBootstrap.ts index ee4d5385de7..d839cd3015e 100644 --- a/webapp/packages/plugin-sql-generator/src/SqlGenerators/SqlGeneratorsBootstrap.ts +++ b/webapp/packages/plugin-sql-generator/src/SqlGenerators/SqlGeneratorsBootstrap.ts @@ -86,6 +86,7 @@ export class SqlGeneratorsBootstrap extends Bootstrap { query, nodeId: node.uri, nodeName: node.name, + generatorId: action.id, generatorName: action.label, options: getDefaultQueryGeneratorOptions(), regenerateQuery: options => this.sqlGeneratorsResource.generateEntityQuery(action.id, node.uri, options), diff --git a/webapp/packages/plugin-sql-generator/src/SqlGenerators/SqlGeneratorsResource.ts b/webapp/packages/plugin-sql-generator/src/SqlGenerators/SqlGeneratorsResource.ts index 95eaf63a122..235057f4c12 100644 --- a/webapp/packages/plugin-sql-generator/src/SqlGenerators/SqlGeneratorsResource.ts +++ b/webapp/packages/plugin-sql-generator/src/SqlGenerators/SqlGeneratorsResource.ts @@ -8,6 +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 { AsyncTaskInfoService } from '@cloudbeaver/core-root'; import { GraphQLService, type SqlGenerateResultSetQueryQueryVariables, @@ -19,14 +20,16 @@ export const MAX_GENERATORS_LENGTH = 15; export const getDefaultQueryGeneratorOptions = (): SqlQueryGeneratorOptions => ({ useFullyQualifiedNames: true, compactSql: false, + showFullDdl: false, }); export const DDL_GENERATOR_ID = 'tableDDL'; -@injectable(() => [GraphQLService, NavNodeInfoResource]) +@injectable(() => [GraphQLService, NavNodeInfoResource, AsyncTaskInfoService]) export class SqlGeneratorsResource extends CachedMapResource { constructor( private readonly graphQLService: GraphQLService, private readonly navNodeInfoResource: NavNodeInfoResource, + private readonly asyncTaskInfoService: AsyncTaskInfoService, ) { super(); @@ -35,13 +38,20 @@ export class SqlGeneratorsResource extends CachedMapResource { - const result = await this.graphQLService.sdk.sqlGenerateEntityQuery({ - generatorId, - nodePathList, - generatorOptions: options, + const task = this.asyncTaskInfoService.create(async () => { + const { taskInfo } = await this.graphQLService.sdk.asyncSqlGenerateEntityQuery({ + generatorId, + nodePathList, + generatorOptions: options, + }); + + return taskInfo; }); - return result.sqlGenerateEntityQuery; + const info = await this.asyncTaskInfoService.run(task); + await this.asyncTaskInfoService.remove(task.id); + + return info.taskResult; } async generateResultSetSql(params: SqlGenerateResultSetQueryQueryVariables): Promise { diff --git a/webapp/packages/plugin-sql-generator/src/locales/en.ts b/webapp/packages/plugin-sql-generator/src/locales/en.ts index ee34546328a..48c468ac3ee 100644 --- a/webapp/packages/plugin-sql-generator/src/locales/en.ts +++ b/webapp/packages/plugin-sql-generator/src/locales/en.ts @@ -2,6 +2,7 @@ export default [ ['app_shared_sql_generators_error_title', 'SQL Generation Error'], ['app_shared_sql_generators_use_fully_qualified_names', 'Use fully qualified names'], ['app_shared_sql_generators_compact_sql', 'Compact SQL'], + ['app_shared_sql_generators_full_ddl', 'Full DDL'], ['app_shared_sql_generators_open_in_editor', 'Open in SQL Editor'], ['app_shared_sql_generators_error_open_editor', 'Error opening SQL Editor'], ]; diff --git a/webapp/packages/plugin-sql-generator/src/locales/fr.ts b/webapp/packages/plugin-sql-generator/src/locales/fr.ts index b1877ca2c77..326376d22d6 100644 --- a/webapp/packages/plugin-sql-generator/src/locales/fr.ts +++ b/webapp/packages/plugin-sql-generator/src/locales/fr.ts @@ -2,6 +2,7 @@ export default [ ['app_shared_sql_generators_error_title', 'Erreur de génération SQL'], ['app_shared_sql_generators_use_fully_qualified_names', 'Utiliser les noms complets'], ['app_shared_sql_generators_compact_sql', 'SQL compact'], + ['app_shared_sql_generators_full_ddl', 'Full DDL'], ['app_shared_sql_generators_open_in_editor', "Ouvrir dans l'éditeur SQL"], ['app_shared_sql_generators_error_open_editor', "Erreur lors de l'ouverture de l'éditeur SQL"], ]; diff --git a/webapp/packages/plugin-sql-generator/src/locales/it.ts b/webapp/packages/plugin-sql-generator/src/locales/it.ts index b980aadd5a5..50176cbdfcd 100644 --- a/webapp/packages/plugin-sql-generator/src/locales/it.ts +++ b/webapp/packages/plugin-sql-generator/src/locales/it.ts @@ -2,6 +2,7 @@ export default [ ['app_shared_sql_generators_error_title', 'Errore di generazione SQL'], ['app_shared_sql_generators_use_fully_qualified_names', 'Usa nomi completi'], ['app_shared_sql_generators_compact_sql', 'SQL compatto'], + ['app_shared_sql_generators_full_ddl', 'Full DDL'], ['app_shared_sql_generators_open_in_editor', "Apri nell'editor SQL"], ['app_shared_sql_generators_error_open_editor', "Errore durante l'apertura dell'editor SQL"], ]; diff --git a/webapp/packages/plugin-sql-generator/src/locales/ru.ts b/webapp/packages/plugin-sql-generator/src/locales/ru.ts index 8a44f1cefbf..643f6c438e7 100644 --- a/webapp/packages/plugin-sql-generator/src/locales/ru.ts +++ b/webapp/packages/plugin-sql-generator/src/locales/ru.ts @@ -2,6 +2,7 @@ export default [ ['app_shared_sql_generators_error_title', 'Ошибка генерации SQL'], ['app_shared_sql_generators_use_fully_qualified_names', 'Использовать полные имена'], ['app_shared_sql_generators_compact_sql', 'Компактный SQL'], + ['app_shared_sql_generators_full_ddl', 'Полный DDL'], ['app_shared_sql_generators_open_in_editor', 'Открыть в SQL редакторе'], ['app_shared_sql_generators_error_open_editor', 'Ошибка при открытии SQL редактора'], ]; diff --git a/webapp/packages/plugin-sql-generator/src/locales/vi.ts b/webapp/packages/plugin-sql-generator/src/locales/vi.ts index 41cbd4a09ce..454f3896784 100644 --- a/webapp/packages/plugin-sql-generator/src/locales/vi.ts +++ b/webapp/packages/plugin-sql-generator/src/locales/vi.ts @@ -2,6 +2,7 @@ export default [ ['app_shared_sql_generators_error_title', 'Lỗi tạo SQL'], ['app_shared_sql_generators_use_fully_qualified_names', 'Sử dụng tên đầy đủ'], ['app_shared_sql_generators_compact_sql', 'SQL gọn'], + ['app_shared_sql_generators_full_ddl', 'Full DDL'], ['app_shared_sql_generators_open_in_editor', 'Mở trong trình soạn thảo SQL'], ['app_shared_sql_generators_error_open_editor', 'Lỗi khi mở trình soạn thảo SQL'], ]; diff --git a/webapp/packages/plugin-sql-generator/src/locales/zh.ts b/webapp/packages/plugin-sql-generator/src/locales/zh.ts index e40f345c8c9..0eee5fc34ec 100644 --- a/webapp/packages/plugin-sql-generator/src/locales/zh.ts +++ b/webapp/packages/plugin-sql-generator/src/locales/zh.ts @@ -2,6 +2,7 @@ export default [ ['app_shared_sql_generators_error_title', 'SQL生成错误'], ['app_shared_sql_generators_use_fully_qualified_names', '使用完全限定名'], ['app_shared_sql_generators_compact_sql', '紧凑SQL'], + ['app_shared_sql_generators_full_ddl', 'Full DDL'], ['app_shared_sql_generators_open_in_editor', '在SQL编辑器中打开'], ['app_shared_sql_generators_error_open_editor', '打开SQL编辑器时出错'], ]; diff --git a/webapp/packages/plugin-sql-generator/tsconfig.json b/webapp/packages/plugin-sql-generator/tsconfig.json index b13d67548af..38285497ef6 100644 --- a/webapp/packages/plugin-sql-generator/tsconfig.json +++ b/webapp/packages/plugin-sql-generator/tsconfig.json @@ -37,6 +37,9 @@ { "path": "../core-resource" }, + { + "path": "../core-root" + }, { "path": "../core-sdk" }, diff --git a/webapp/yarn.lock b/webapp/yarn.lock index 1512937a60f..c5f81d08122 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -4693,6 +4693,7 @@ __metadata: "@cloudbeaver/core-localization": "workspace:*" "@cloudbeaver/core-navigation-tree": "workspace:*" "@cloudbeaver/core-resource": "workspace:*" + "@cloudbeaver/core-root": "workspace:*" "@cloudbeaver/core-sdk": "workspace:*" "@cloudbeaver/core-utils": "workspace:*" "@cloudbeaver/core-view": "workspace:*" From 75ecf384aa096e61649e8dfdabd5c576a445e709 Mon Sep 17 00:00:00 2001 From: sergeyteleshev Date: Wed, 29 Jul 2026 12:46:02 +0200 Subject: [PATCH 2/7] dbeaver/pro#4551 introduces core-sql-generator plugin + adds "full DDL" action into data editor footer for DDL properties tab --- .../src/queries/metadataGetNodeDDL.gql | 3 -- .../packages/core-sql-generator/package.json | 38 +++++++++++++ .../src}/SqlGeneratorsResource.ts | 0 .../packages/core-sql-generator/src/index.ts | 11 ++++ .../packages/core-sql-generator/src/module.ts | 17 ++++++ .../packages/core-sql-generator/tsconfig.json | 39 ++++++++++++++ .../packages/plugin-ddl-viewer/package.json | 3 ++ .../DdlViewer/ACTION_DDL_VIEWER_FULL_DDL.ts | 14 +++++ .../DATA_CONTEXT_DDL_VIEWER_FULL_DDL.ts | 16 ++++++ .../src/DdlViewer/DDLViewerFooterService.ts | 31 ++++++++++- .../src/DdlViewer/DDLViewerTabPanel.tsx | 53 +++++++++++++++++-- .../src/DdlViewer/DdlResource.ts | 47 ---------------- .../plugin-ddl-viewer/src/LocaleService.ts | 29 ++++++++++ .../plugin-ddl-viewer/src/locales/en.ts | 4 ++ .../plugin-ddl-viewer/src/locales/ru.ts | 4 ++ .../packages/plugin-ddl-viewer/src/module.ts | 7 ++- .../packages/plugin-ddl-viewer/tsconfig.json | 9 ++++ .../packages/plugin-set-common/package.json | 1 + .../packages/plugin-set-common/src/index.ts | 2 + .../packages/plugin-set-common/tsconfig.json | 3 ++ .../plugin-sql-generator/package.json | 2 +- .../src/SqlGenerators/GeneratedSqlDialog.tsx | 2 +- .../SqlGenerators/SqlGeneratorsBootstrap.ts | 2 +- .../plugin-sql-generator/src/index.ts | 2 +- .../plugin-sql-generator/src/module.ts | 5 +- .../plugin-sql-generator/tsconfig.json | 4 +- webapp/yarn.lock | 23 +++++++- 27 files changed, 300 insertions(+), 71 deletions(-) delete mode 100644 webapp/packages/core-sdk/src/queries/metadataGetNodeDDL.gql create mode 100644 webapp/packages/core-sql-generator/package.json rename webapp/packages/{plugin-sql-generator/src/SqlGenerators => core-sql-generator/src}/SqlGeneratorsResource.ts (100%) create mode 100644 webapp/packages/core-sql-generator/src/index.ts create mode 100644 webapp/packages/core-sql-generator/src/module.ts create mode 100644 webapp/packages/core-sql-generator/tsconfig.json create mode 100644 webapp/packages/plugin-ddl-viewer/src/DdlViewer/ACTION_DDL_VIEWER_FULL_DDL.ts create mode 100644 webapp/packages/plugin-ddl-viewer/src/DdlViewer/DATA_CONTEXT_DDL_VIEWER_FULL_DDL.ts delete mode 100644 webapp/packages/plugin-ddl-viewer/src/DdlViewer/DdlResource.ts create mode 100644 webapp/packages/plugin-ddl-viewer/src/LocaleService.ts create mode 100644 webapp/packages/plugin-ddl-viewer/src/locales/en.ts create mode 100644 webapp/packages/plugin-ddl-viewer/src/locales/ru.ts diff --git a/webapp/packages/core-sdk/src/queries/metadataGetNodeDDL.gql b/webapp/packages/core-sdk/src/queries/metadataGetNodeDDL.gql deleted file mode 100644 index eb2944a809b..00000000000 --- a/webapp/packages/core-sdk/src/queries/metadataGetNodeDDL.gql +++ /dev/null @@ -1,3 +0,0 @@ -query metadataGetNodeDDL( $nodeId: ID! ) { - metadataGetNodeDDL( nodeId: $nodeId ) -} diff --git a/webapp/packages/core-sql-generator/package.json b/webapp/packages/core-sql-generator/package.json new file mode 100644 index 00000000000..213f19f0ffc --- /dev/null +++ b/webapp/packages/core-sql-generator/package.json @@ -0,0 +1,38 @@ +{ + "name": "@cloudbeaver/core-sql-generator", + "type": "module", + "sideEffects": [ + "./lib/module.js", + "./lib/index.js", + "src/**/*.css", + "public/**/*" + ], + "version": "0.1.0", + "description": "", + "license": "Apache-2.0", + "exports": { + ".": "./lib/index.js", + "./module": "./lib/module.js" + }, + "scripts": { + "build": "tsc -b", + "clean": "rimraf --glob lib", + "lint": "eslint ./src/ --ext .ts,.tsx", + "test": "dbeaver-test", + "validate-dependencies": "core-cli-validate-dependencies" + }, + "dependencies": { + "@cloudbeaver/core-di": "workspace:*", + "@cloudbeaver/core-navigation-tree": "workspace:*", + "@cloudbeaver/core-resource": "workspace:*", + "@cloudbeaver/core-root": "workspace:*", + "@cloudbeaver/core-sdk": "workspace:*", + "tslib": "^2" + }, + "devDependencies": { + "@cloudbeaver/core-cli": "workspace:*", + "@cloudbeaver/tsconfig": "workspace:*", + "rimraf": "^6", + "typescript": "^5" + } +} diff --git a/webapp/packages/plugin-sql-generator/src/SqlGenerators/SqlGeneratorsResource.ts b/webapp/packages/core-sql-generator/src/SqlGeneratorsResource.ts similarity index 100% rename from webapp/packages/plugin-sql-generator/src/SqlGenerators/SqlGeneratorsResource.ts rename to webapp/packages/core-sql-generator/src/SqlGeneratorsResource.ts diff --git a/webapp/packages/core-sql-generator/src/index.ts b/webapp/packages/core-sql-generator/src/index.ts new file mode 100644 index 00000000000..9067c769245 --- /dev/null +++ b/webapp/packages/core-sql-generator/src/index.ts @@ -0,0 +1,11 @@ +/* + * CloudBeaver - Cloud Database Manager + * 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. + */ + +import './module.js'; + +export * from './SqlGeneratorsResource.js'; diff --git a/webapp/packages/core-sql-generator/src/module.ts b/webapp/packages/core-sql-generator/src/module.ts new file mode 100644 index 00000000000..04828847006 --- /dev/null +++ b/webapp/packages/core-sql-generator/src/module.ts @@ -0,0 +1,17 @@ +/* + * CloudBeaver - Cloud Database Manager + * 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. + */ +import { Dependency, ModuleRegistry, proxy } from '@cloudbeaver/core-di'; +import { SqlGeneratorsResource } from './SqlGeneratorsResource.js'; + +export default ModuleRegistry.add({ + name: '@cloudbeaver/core-sql-generator', + + configure: serviceCollection => { + serviceCollection.addSingleton(Dependency, proxy(SqlGeneratorsResource)).addSingleton(SqlGeneratorsResource); + }, +}); diff --git a/webapp/packages/core-sql-generator/tsconfig.json b/webapp/packages/core-sql-generator/tsconfig.json new file mode 100644 index 00000000000..e12d3d6ed61 --- /dev/null +++ b/webapp/packages/core-sql-generator/tsconfig.json @@ -0,0 +1,39 @@ +{ + "extends": "@cloudbeaver/tsconfig/tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "lib", + "tsBuildInfoFile": "lib/tsconfig.tsbuildinfo", + "composite": true + }, + "references": [ + { + "path": "../core-cli" + }, + { + "path": "../core-di" + }, + { + "path": "../core-navigation-tree" + }, + { + "path": "../core-resource" + }, + { + "path": "../core-root" + }, + { + "path": "../core-sdk" + } + ], + "include": [ + "__custom_mocks__/**/*", + "src/**/*", + "src/**/*.json", + "src/**/*.css" + ], + "exclude": [ + "**/node_modules", + "lib/**/*" + ] +} diff --git a/webapp/packages/plugin-ddl-viewer/package.json b/webapp/packages/plugin-ddl-viewer/package.json index 8f882961a45..0da1e5cf610 100644 --- a/webapp/packages/plugin-ddl-viewer/package.json +++ b/webapp/packages/plugin-ddl-viewer/package.json @@ -25,9 +25,12 @@ "@cloudbeaver/core-connections": "workspace:*", "@cloudbeaver/core-data-context": "workspace:*", "@cloudbeaver/core-di": "workspace:*", + "@cloudbeaver/core-events": "workspace:*", + "@cloudbeaver/core-localization": "workspace:*", "@cloudbeaver/core-navigation-tree": "workspace:*", "@cloudbeaver/core-resource": "workspace:*", "@cloudbeaver/core-sdk": "workspace:*", + "@cloudbeaver/core-sql-generator": "workspace:*", "@cloudbeaver/core-ui": "workspace:*", "@cloudbeaver/core-utils": "workspace:*", "@cloudbeaver/core-view": "workspace:*", diff --git a/webapp/packages/plugin-ddl-viewer/src/DdlViewer/ACTION_DDL_VIEWER_FULL_DDL.ts b/webapp/packages/plugin-ddl-viewer/src/DdlViewer/ACTION_DDL_VIEWER_FULL_DDL.ts new file mode 100644 index 00000000000..40fb969bcf4 --- /dev/null +++ b/webapp/packages/plugin-ddl-viewer/src/DdlViewer/ACTION_DDL_VIEWER_FULL_DDL.ts @@ -0,0 +1,14 @@ +/* + * CloudBeaver - Cloud Database Manager + * 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. + */ +import { createAction } from '@cloudbeaver/core-view'; + +export const ACTION_DDL_VIEWER_FULL_DDL = createAction('ddl-viewer-full-ddl', { + label: 'plugin_ddl_viewer_full_ddl', + tooltip: 'plugin_ddl_viewer_full_ddl', + type: 'checkbox', +}); diff --git a/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DATA_CONTEXT_DDL_VIEWER_FULL_DDL.ts b/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DATA_CONTEXT_DDL_VIEWER_FULL_DDL.ts new file mode 100644 index 00000000000..eeada23523c --- /dev/null +++ b/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DATA_CONTEXT_DDL_VIEWER_FULL_DDL.ts @@ -0,0 +1,16 @@ +/* + * CloudBeaver - Cloud Database Manager + * 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. + */ +import { createDataContext } from '@cloudbeaver/core-data-context'; + +export interface IDdlViewerFullDdlState { + value: boolean; + loading: boolean; + onChange: (value: boolean) => void; +} + +export const DATA_CONTEXT_DDL_VIEWER_FULL_DDL = createDataContext('ddl-full-ddl'); diff --git a/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DDLViewerFooterService.ts b/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DDLViewerFooterService.ts index 8e4ead0170b..6fdbdd171ba 100644 --- a/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DDLViewerFooterService.ts +++ b/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DDLViewerFooterService.ts @@ -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. @@ -13,6 +13,8 @@ import { ACTION_SAVE, ActionService, MenuService } from '@cloudbeaver/core-view' import { LocalStorageSqlDataSource } from '@cloudbeaver/plugin-sql-editor'; import { ACTION_SQL_EDITOR_OPEN, SqlEditorNavigatorService } from '@cloudbeaver/plugin-sql-editor-navigation-tab'; +import { ACTION_DDL_VIEWER_FULL_DDL } from './ACTION_DDL_VIEWER_FULL_DDL.js'; +import { DATA_CONTEXT_DDL_VIEWER_FULL_DDL } from './DATA_CONTEXT_DDL_VIEWER_FULL_DDL.js'; import { DATA_CONTEXT_DDL_VIEWER_NODE } from './DATA_CONTEXT_DDL_VIEWER_NODE.js'; import { DATA_CONTEXT_DDL_VIEWER_VALUE } from './DATA_CONTEXT_DDL_VIEWER_VALUE.js'; import { MENU_DDL_VIEWER_FOOTER } from './MENU_DDL_VIEWER_FOOTER.js'; @@ -92,5 +94,32 @@ export class DDLViewerFooterService { menus: [MENU_DDL_VIEWER_FOOTER], getItems: (context, items) => [...items, ACTION_SAVE, ACTION_SQL_EDITOR_OPEN], }); + + this.menuService.addCreator({ + menus: [MENU_DDL_VIEWER_FOOTER], + getItems: (context, items) => { + if (!context.get(DATA_CONTEXT_DDL_VIEWER_FULL_DDL)) { + return items; + } + + return [...items, ACTION_DDL_VIEWER_FULL_DDL]; + }, + }); + + this.actionsService.addHandler({ + id: 'ddl-viewer-footer-full-ddl-handler', + menus: [MENU_DDL_VIEWER_FOOTER], + actions: [ACTION_DDL_VIEWER_FULL_DDL], + contexts: [DATA_CONTEXT_DDL_VIEWER_FULL_DDL], + isChecked: context => context.get(DATA_CONTEXT_DDL_VIEWER_FULL_DDL)!.value, + isDisabled: context => context.get(DATA_CONTEXT_DDL_VIEWER_FULL_DDL)!.loading, + isLoading: context => context.get(DATA_CONTEXT_DDL_VIEWER_FULL_DDL)!.loading, + handler: (context, action) => { + if (action === ACTION_DDL_VIEWER_FULL_DDL) { + const fullDdlState = context.get(DATA_CONTEXT_DDL_VIEWER_FULL_DDL)!; + fullDdlState.onChange(!fullDdlState.value); + } + }, + }); } } diff --git a/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DDLViewerTabPanel.tsx b/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DDLViewerTabPanel.tsx index 716f93861c6..aa1e6733ebf 100644 --- a/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DDLViewerTabPanel.tsx +++ b/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DDLViewerTabPanel.tsx @@ -6,6 +6,7 @@ * you may not use this file except in compliance with the License. */ import { observer } from 'mobx-react-lite'; +import { useEffect, useState } from 'react'; import { s, useResource, useS } from '@cloudbeaver/core-blocks'; import { @@ -15,23 +16,32 @@ import { createConnectionParam, } from '@cloudbeaver/core-connections'; import { useDataContextLink } from '@cloudbeaver/core-data-context'; +import { useService } from '@cloudbeaver/core-di'; +import { NotificationService } from '@cloudbeaver/core-events'; import { MenuBar, MenuBarGroupStyles, MenuBarItemStyles, MenuBarStyles } from '@cloudbeaver/core-ui'; import { useMenu } from '@cloudbeaver/core-view'; import { useCodemirrorExtensions } from '@cloudbeaver/plugin-codemirror6'; import type { NavNodeTransformViewComponent } from '@cloudbeaver/plugin-navigation-tree'; import { SQLCodeEditor, useSqlDialectExtension } from '@cloudbeaver/plugin-sql-editor-codemirror'; +import { DDL_GENERATOR_ID, getDefaultQueryGeneratorOptions, SqlGeneratorsResource } from '@cloudbeaver/core-sql-generator'; +import { DATA_CONTEXT_DDL_VIEWER_FULL_DDL } from './DATA_CONTEXT_DDL_VIEWER_FULL_DDL.js'; import { DATA_CONTEXT_DDL_VIEWER_NODE } from './DATA_CONTEXT_DDL_VIEWER_NODE.js'; import { DATA_CONTEXT_DDL_VIEWER_VALUE } from './DATA_CONTEXT_DDL_VIEWER_VALUE.js'; -import { DdlResource } from './DdlResource.js'; import style from './DDLViewerTabPanel.module.css'; import { MENU_DDL_VIEWER_FOOTER } from './MENU_DDL_VIEWER_FOOTER.js'; export const DDLViewerTabPanel: NavNodeTransformViewComponent = observer(function DDLViewerTabPanel({ nodeId, folderId }) { const styles = useS(style, MenuBarStyles, MenuBarItemStyles, MenuBarGroupStyles); const menu = useMenu({ menu: MENU_DDL_VIEWER_FOOTER }); + const notificationService = useService(NotificationService); - const ddlResource = useResource(DDLViewerTabPanel, DdlResource, nodeId); + const [isFullDdl, setIsFullDdl] = useState(false); + const [query, setQuery] = useState(''); + const [ddlLoading, setDdlLoading] = useState(false); + + const sqlGeneratorsResource = useResource(DDLViewerTabPanel, SqlGeneratorsResource, nodeId); + const ddlGenerator = sqlGeneratorsResource.data?.find(generator => generator.id.toLowerCase().includes(DDL_GENERATOR_ID.toLowerCase())); const connectionInfoResource = useResource(DDLViewerTabPanel, ConnectionInfoResource, ConnectionInfoActiveProjectKey); const connection = connectionInfoResource.resource.getConnectionForNode(nodeId); @@ -42,16 +52,49 @@ export const DDLViewerTabPanel: NavNodeTransformViewComponent = observer(functio if (sqlDialect) { extensions.set(...sqlDialect); } - const ddlData = ddlResource.data; + + async function loadDdl(showFullDdl: boolean) { + if (!ddlGenerator) { + return; + } + + setDdlLoading(true); + try { + const newQuery = await sqlGeneratorsResource.resource.generateEntityQuery(ddlGenerator.id, nodeId, { + ...getDefaultQueryGeneratorOptions(), + showFullDdl, + }); + setQuery(newQuery); + } catch (error: any) { + notificationService.logException(error, 'plugin_ddl_viewer_full_ddl_error_title'); + } finally { + setDdlLoading(false); + } + } + + useEffect(() => { + if (ddlGenerator) { + loadDdl(isFullDdl); + } + }, [nodeId, !!ddlGenerator]); + + async function handleFullDdlChange(value: boolean) { + setIsFullDdl(value); + await loadDdl(value); + } useDataContextLink(menu.context, (context, id) => { context.set(DATA_CONTEXT_DDL_VIEWER_NODE, nodeId, id); - context.set(DATA_CONTEXT_DDL_VIEWER_VALUE, ddlData, id); + context.set(DATA_CONTEXT_DDL_VIEWER_VALUE, query, id); + + if (ddlGenerator) { + context.set(DATA_CONTEXT_DDL_VIEWER_FULL_DDL, { value: isFullDdl, loading: ddlLoading, onChange: handleFullDdlChange }, id); + } }); return (
- +
); diff --git a/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DdlResource.ts b/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DdlResource.ts deleted file mode 100644 index c6a8ecb43ae..00000000000 --- a/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DdlResource.ts +++ /dev/null @@ -1,47 +0,0 @@ -/* - * CloudBeaver - Cloud Database Manager - * Copyright (C) 2020-2024 DBeaver Corp and others - * - * Licensed under the Apache License, Version 2.0. - * you may not use this file except in compliance with the License. - */ -import { injectable } from '@cloudbeaver/core-di'; -import { NavNodeInfoResource } from '@cloudbeaver/core-navigation-tree'; -import { CachedMapResource, isResourceAlias, type ResourceKey, ResourceKeyUtils } from '@cloudbeaver/core-resource'; -import { GraphQLService } from '@cloudbeaver/core-sdk'; - -@injectable(() => [GraphQLService, NavNodeInfoResource]) -export class DdlResource extends CachedMapResource { - constructor( - private readonly graphQLService: GraphQLService, - private readonly navNodeInfoResource: NavNodeInfoResource, - ) { - super(); - - this.navNodeInfoResource.outdateResource(this); - this.navNodeInfoResource.deleteInResource(this); - } - - protected async loader(key: ResourceKey): Promise> { - if (isResourceAlias(key)) { - throw new Error('Aliases not supported by this resource.'); - } - - const values: string[] = []; - - await ResourceKeyUtils.forEachAsync(key, async nodeId => { - const { metadataGetNodeDDL } = await this.graphQLService.sdk.metadataGetNodeDDL({ nodeId }); - if (metadataGetNodeDDL) { - values.push(metadataGetNodeDDL); - } - }); - - this.set(ResourceKeyUtils.toList(key), values); - - return this.data; - } - - protected validateKey(key: string): boolean { - return typeof key === 'string'; - } -} diff --git a/webapp/packages/plugin-ddl-viewer/src/LocaleService.ts b/webapp/packages/plugin-ddl-viewer/src/LocaleService.ts new file mode 100644 index 00000000000..fae7357b770 --- /dev/null +++ b/webapp/packages/plugin-ddl-viewer/src/LocaleService.ts @@ -0,0 +1,29 @@ +/* + * CloudBeaver - Cloud Database Manager + * 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. + */ +import { Bootstrap, injectable } from '@cloudbeaver/core-di'; +import { LocalizationService } from '@cloudbeaver/core-localization'; + +@injectable(() => [LocalizationService]) +export class LocaleService extends Bootstrap { + constructor(private readonly localizationService: LocalizationService) { + super(); + } + + override register(): void { + this.localizationService.addProvider(this.provider.bind(this)); + } + + private async provider(locale: string) { + switch (locale) { + case 'ru': + return (await import('./locales/ru.js')).default; + default: + return (await import('./locales/en.js')).default; + } + } +} diff --git a/webapp/packages/plugin-ddl-viewer/src/locales/en.ts b/webapp/packages/plugin-ddl-viewer/src/locales/en.ts new file mode 100644 index 00000000000..a0d8e3ed2e0 --- /dev/null +++ b/webapp/packages/plugin-ddl-viewer/src/locales/en.ts @@ -0,0 +1,4 @@ +export default [ + ['plugin_ddl_viewer_full_ddl', 'Full DDL'], + ['plugin_ddl_viewer_full_ddl_error_title', 'Error generating full DDL'], +]; diff --git a/webapp/packages/plugin-ddl-viewer/src/locales/ru.ts b/webapp/packages/plugin-ddl-viewer/src/locales/ru.ts new file mode 100644 index 00000000000..3277e1a5d34 --- /dev/null +++ b/webapp/packages/plugin-ddl-viewer/src/locales/ru.ts @@ -0,0 +1,4 @@ +export default [ + ['plugin_ddl_viewer_full_ddl', 'Полный DDL'], + ['plugin_ddl_viewer_full_ddl_error_title', 'Ошибка генерации полного DDL'], +]; diff --git a/webapp/packages/plugin-ddl-viewer/src/module.ts b/webapp/packages/plugin-ddl-viewer/src/module.ts index 31511d19d02..dcf10dd4f82 100644 --- a/webapp/packages/plugin-ddl-viewer/src/module.ts +++ b/webapp/packages/plugin-ddl-viewer/src/module.ts @@ -9,8 +9,8 @@ import { Bootstrap, Dependency, ModuleRegistry, proxy } from '@cloudbeaver/core-di'; import { ExtendedDDLResource } from './ExtendedDDLViewer/ExtendedDDLResource.js'; import { DdlViewerBootstrap } from './DdlViewerBootstrap.js'; -import { DdlResource } from './DdlViewer/DdlResource.js'; import { DDLViewerFooterService } from './DdlViewer/DDLViewerFooterService.js'; +import { LocaleService } from './LocaleService.js'; export default ModuleRegistry.add({ name: '@cloudbeaver/plugin-ddl-viewer', @@ -18,10 +18,9 @@ export default ModuleRegistry.add({ configure: serviceCollection => { serviceCollection .addSingleton(Bootstrap, DdlViewerBootstrap) + .addSingleton(Bootstrap, LocaleService) .addSingleton(Dependency, proxy(ExtendedDDLResource)) - .addSingleton(Dependency, proxy(DdlResource)) .addSingleton(DDLViewerFooterService) - .addSingleton(ExtendedDDLResource) - .addSingleton(DdlResource); + .addSingleton(ExtendedDDLResource); }, }); diff --git a/webapp/packages/plugin-ddl-viewer/tsconfig.json b/webapp/packages/plugin-ddl-viewer/tsconfig.json index 000492456e9..fc2a3057f96 100644 --- a/webapp/packages/plugin-ddl-viewer/tsconfig.json +++ b/webapp/packages/plugin-ddl-viewer/tsconfig.json @@ -22,6 +22,12 @@ { "path": "../core-di" }, + { + "path": "../core-events" + }, + { + "path": "../core-localization" + }, { "path": "../core-navigation-tree" }, @@ -31,6 +37,9 @@ { "path": "../core-sdk" }, + { + "path": "../core-sql-generator" + }, { "path": "../core-ui" }, diff --git a/webapp/packages/plugin-set-common/package.json b/webapp/packages/plugin-set-common/package.json index 0405dd7ec47..ad927e97a0a 100644 --- a/webapp/packages/plugin-set-common/package.json +++ b/webapp/packages/plugin-set-common/package.json @@ -47,6 +47,7 @@ "@cloudbeaver/core-settings": "workspace:*", "@cloudbeaver/core-settings-localization": "workspace:*", "@cloudbeaver/core-settings-user": "workspace:*", + "@cloudbeaver/core-sql-generator": "workspace:*", "@cloudbeaver/core-storage": "workspace:*", "@cloudbeaver/core-support": "workspace:*", "@cloudbeaver/core-task-manager": "workspace:*", diff --git a/webapp/packages/plugin-set-common/src/index.ts b/webapp/packages/plugin-set-common/src/index.ts index e5a52d24122..d610fa916d8 100644 --- a/webapp/packages/plugin-set-common/src/index.ts +++ b/webapp/packages/plugin-set-common/src/index.ts @@ -25,6 +25,7 @@ import coreResourceManager from '@cloudbeaver/core-resource-manager/module'; import coreRoot from '@cloudbeaver/core-root/module'; import coreRouting from '@cloudbeaver/core-routing/module'; import coreSdk from '@cloudbeaver/core-sdk/module'; +import coreSqlGenerator from '@cloudbeaver/core-sql-generator/module'; import coreServerLocalization from '@cloudbeaver/core-server-localization/module'; import coreSessionLocalization from '@cloudbeaver/core-session-localization/module'; import coreSettings from '@cloudbeaver/core-settings/module'; @@ -150,6 +151,7 @@ const core = [ coreAdministration, coreDialogs, coreResourceManager, + coreSqlGenerator, coreApp, coreClientActivity, coreNavigationTree, diff --git a/webapp/packages/plugin-set-common/tsconfig.json b/webapp/packages/plugin-set-common/tsconfig.json index 68595ba915d..54add273999 100644 --- a/webapp/packages/plugin-set-common/tsconfig.json +++ b/webapp/packages/plugin-set-common/tsconfig.json @@ -88,6 +88,9 @@ { "path": "../core-settings-user" }, + { + "path": "../core-sql-generator" + }, { "path": "../core-storage" }, diff --git a/webapp/packages/plugin-sql-generator/package.json b/webapp/packages/plugin-sql-generator/package.json index 9ac2e3a58d8..c7426df3f9d 100644 --- a/webapp/packages/plugin-sql-generator/package.json +++ b/webapp/packages/plugin-sql-generator/package.json @@ -30,8 +30,8 @@ "@cloudbeaver/core-localization": "workspace:*", "@cloudbeaver/core-navigation-tree": "workspace:*", "@cloudbeaver/core-resource": "workspace:*", - "@cloudbeaver/core-root": "workspace:*", "@cloudbeaver/core-sdk": "workspace:*", + "@cloudbeaver/core-sql-generator": "workspace:*", "@cloudbeaver/core-utils": "workspace:*", "@cloudbeaver/core-view": "workspace:*", "@cloudbeaver/plugin-codemirror6": "workspace:*", diff --git a/webapp/packages/plugin-sql-generator/src/SqlGenerators/GeneratedSqlDialog.tsx b/webapp/packages/plugin-sql-generator/src/SqlGenerators/GeneratedSqlDialog.tsx index 396444e2a53..cd9bfaad085 100644 --- a/webapp/packages/plugin-sql-generator/src/SqlGenerators/GeneratedSqlDialog.tsx +++ b/webapp/packages/plugin-sql-generator/src/SqlGenerators/GeneratedSqlDialog.tsx @@ -33,7 +33,7 @@ import { action, observable } from 'mobx'; import { NotificationService } from '@cloudbeaver/core-events'; import type { SqlQueryGeneratorOptions } from '@cloudbeaver/core-sdk'; -import { DDL_GENERATOR_ID } from './SqlGeneratorsResource.js'; +import { DDL_GENERATOR_ID } from '@cloudbeaver/core-sql-generator'; interface Payload { nodeId: string; diff --git a/webapp/packages/plugin-sql-generator/src/SqlGenerators/SqlGeneratorsBootstrap.ts b/webapp/packages/plugin-sql-generator/src/SqlGenerators/SqlGeneratorsBootstrap.ts index d839cd3015e..f8dfc7e171d 100644 --- a/webapp/packages/plugin-sql-generator/src/SqlGenerators/SqlGeneratorsBootstrap.ts +++ b/webapp/packages/plugin-sql-generator/src/SqlGenerators/SqlGeneratorsBootstrap.ts @@ -9,11 +9,11 @@ import { importLazyComponent } from '@cloudbeaver/core-blocks'; import { Bootstrap, injectable } from '@cloudbeaver/core-di'; import { CommonDialogService } from '@cloudbeaver/core-dialogs'; import { DATA_CONTEXT_NAV_NODE, EObjectFeature } from '@cloudbeaver/core-navigation-tree'; +import { getDefaultQueryGeneratorOptions, SqlGeneratorsResource } from '@cloudbeaver/core-sql-generator'; import { getCachedMapResourceLoaderState } from '@cloudbeaver/core-resource'; import { MenuBaseItem, MenuService } from '@cloudbeaver/core-view'; import { MENU_SQL_GENERATORS } from './MENU_SQL_GENERATORS.js'; -import { getDefaultQueryGeneratorOptions, SqlGeneratorsResource } from './SqlGeneratorsResource.js'; import { NotificationService } from '@cloudbeaver/core-events'; const GeneratedSqlDialog = importLazyComponent(() => import('./GeneratedSqlDialog.js').then(m => m.GeneratedSqlDialog)); diff --git a/webapp/packages/plugin-sql-generator/src/index.ts b/webapp/packages/plugin-sql-generator/src/index.ts index aa18008aaf1..e8e551adf0c 100644 --- a/webapp/packages/plugin-sql-generator/src/index.ts +++ b/webapp/packages/plugin-sql-generator/src/index.ts @@ -8,7 +8,7 @@ import './module.js'; -export * from './SqlGenerators/SqlGeneratorsResource.js'; +export * from '@cloudbeaver/core-sql-generator'; export * from './SqlGenerators/SqlGeneratorsBootstrap.js'; export * from './actions/ACTION_SQL_GENERATE.js'; export * from './SqlGenerators/GeneratedSqlDialog.js'; diff --git a/webapp/packages/plugin-sql-generator/src/module.ts b/webapp/packages/plugin-sql-generator/src/module.ts index b9e35e0b1e9..3fb06c12cf8 100644 --- a/webapp/packages/plugin-sql-generator/src/module.ts +++ b/webapp/packages/plugin-sql-generator/src/module.ts @@ -6,9 +6,8 @@ * you may not use this file except in compliance with the License. */ -import { Bootstrap, Dependency, ModuleRegistry, proxy } from '@cloudbeaver/core-di'; +import { Bootstrap, ModuleRegistry } from '@cloudbeaver/core-di'; import { SqlGeneratorsBootstrap } from './SqlGenerators/SqlGeneratorsBootstrap.js'; -import { SqlGeneratorsResource } from './SqlGenerators/SqlGeneratorsResource.js'; import { ScriptPreviewService } from './ScriptPreview/ScriptPreviewService.js'; import { LocaleService } from './LocaleService.js'; import { GeneratorMenuBootstrap } from './GeneratorMenuBootstrap.js'; @@ -18,11 +17,9 @@ export default ModuleRegistry.add({ configure: serviceCollection => { serviceCollection - .addSingleton(Dependency, proxy(SqlGeneratorsResource)) .addSingleton(Bootstrap, GeneratorMenuBootstrap) .addSingleton(Bootstrap, SqlGeneratorsBootstrap) .addSingleton(Bootstrap, LocaleService) - .addSingleton(SqlGeneratorsResource) .addSingleton(ScriptPreviewService); }, }); diff --git a/webapp/packages/plugin-sql-generator/tsconfig.json b/webapp/packages/plugin-sql-generator/tsconfig.json index 38285497ef6..b50426aadc5 100644 --- a/webapp/packages/plugin-sql-generator/tsconfig.json +++ b/webapp/packages/plugin-sql-generator/tsconfig.json @@ -38,10 +38,10 @@ "path": "../core-resource" }, { - "path": "../core-root" + "path": "../core-sdk" }, { - "path": "../core-sdk" + "path": "../core-sql-generator" }, { "path": "../core-utils" diff --git a/webapp/yarn.lock b/webapp/yarn.lock index cbe63ab38f1..34b66ea615e 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -2184,6 +2184,23 @@ __metadata: languageName: unknown linkType: soft +"@cloudbeaver/core-sql-generator@workspace:*, @cloudbeaver/core-sql-generator@workspace:packages/core-sql-generator": + version: 0.0.0-use.local + resolution: "@cloudbeaver/core-sql-generator@workspace:packages/core-sql-generator" + dependencies: + "@cloudbeaver/core-cli": "workspace:*" + "@cloudbeaver/core-di": "workspace:*" + "@cloudbeaver/core-navigation-tree": "workspace:*" + "@cloudbeaver/core-resource": "workspace:*" + "@cloudbeaver/core-root": "workspace:*" + "@cloudbeaver/core-sdk": "workspace:*" + "@cloudbeaver/tsconfig": "workspace:*" + rimraf: "npm:^6" + tslib: "npm:^2" + typescript: "npm:^5" + languageName: unknown + linkType: soft + "@cloudbeaver/core-storage@workspace:*, @cloudbeaver/core-storage@workspace:packages/core-storage": version: 0.0.0-use.local resolution: "@cloudbeaver/core-storage@workspace:packages/core-storage" @@ -3503,9 +3520,12 @@ __metadata: "@cloudbeaver/core-connections": "workspace:*" "@cloudbeaver/core-data-context": "workspace:*" "@cloudbeaver/core-di": "workspace:*" + "@cloudbeaver/core-events": "workspace:*" + "@cloudbeaver/core-localization": "workspace:*" "@cloudbeaver/core-navigation-tree": "workspace:*" "@cloudbeaver/core-resource": "workspace:*" "@cloudbeaver/core-sdk": "workspace:*" + "@cloudbeaver/core-sql-generator": "workspace:*" "@cloudbeaver/core-ui": "workspace:*" "@cloudbeaver/core-utils": "workspace:*" "@cloudbeaver/core-view": "workspace:*" @@ -4239,6 +4259,7 @@ __metadata: "@cloudbeaver/core-settings": "workspace:*" "@cloudbeaver/core-settings-localization": "workspace:*" "@cloudbeaver/core-settings-user": "workspace:*" + "@cloudbeaver/core-sql-generator": "workspace:*" "@cloudbeaver/core-storage": "workspace:*" "@cloudbeaver/core-support": "workspace:*" "@cloudbeaver/core-task-manager": "workspace:*" @@ -4717,8 +4738,8 @@ __metadata: "@cloudbeaver/core-localization": "workspace:*" "@cloudbeaver/core-navigation-tree": "workspace:*" "@cloudbeaver/core-resource": "workspace:*" - "@cloudbeaver/core-root": "workspace:*" "@cloudbeaver/core-sdk": "workspace:*" + "@cloudbeaver/core-sql-generator": "workspace:*" "@cloudbeaver/core-utils": "workspace:*" "@cloudbeaver/core-view": "workspace:*" "@cloudbeaver/plugin-codemirror6": "workspace:*" From 7aacbc8f234292e633b4b7e4f4bc9f67ea1b266e Mon Sep 17 00:00:00 2001 From: sergeyteleshev Date: Wed, 29 Jul 2026 12:53:33 +0200 Subject: [PATCH 3/7] dbeaver/pro#4551 adds supportsFullDdl flag to hide full ddl feature if connection doesn't support it --- .../src/NodesManager/EObjectFeature.ts | 3 ++- .../plugin-ddl-viewer/src/DdlViewer/DDLViewerTabPanel.tsx | 7 ++++++- .../src/SqlGenerators/GeneratedSqlDialog.tsx | 7 +++++-- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/webapp/packages/core-navigation-tree/src/NodesManager/EObjectFeature.ts b/webapp/packages/core-navigation-tree/src/NodesManager/EObjectFeature.ts index 5208f2b4a0c..7538c59897d 100644 --- a/webapp/packages/core-navigation-tree/src/NodesManager/EObjectFeature.ts +++ b/webapp/packages/core-navigation-tree/src/NodesManager/EObjectFeature.ts @@ -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. @@ -20,4 +20,5 @@ export enum EObjectFeature { 'dataSourceConnected' = 'dataSourceConnected', 'entityContainer' = 'entityContainer', 'supportsDataFilter' = 'supportsDataFilter', + 'supportsFullDdl' = 'supportsFullDdl', // some connections may not have the full DDL feature } diff --git a/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DDLViewerTabPanel.tsx b/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DDLViewerTabPanel.tsx index aa1e6733ebf..b15d97efe80 100644 --- a/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DDLViewerTabPanel.tsx +++ b/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DDLViewerTabPanel.tsx @@ -18,6 +18,7 @@ import { import { useDataContextLink } from '@cloudbeaver/core-data-context'; import { useService } from '@cloudbeaver/core-di'; import { NotificationService } from '@cloudbeaver/core-events'; +import { EObjectFeature, NavNodeInfoResource } from '@cloudbeaver/core-navigation-tree'; import { MenuBar, MenuBarGroupStyles, MenuBarItemStyles, MenuBarStyles } from '@cloudbeaver/core-ui'; import { useMenu } from '@cloudbeaver/core-view'; import { useCodemirrorExtensions } from '@cloudbeaver/plugin-codemirror6'; @@ -43,6 +44,10 @@ export const DDLViewerTabPanel: NavNodeTransformViewComponent = observer(functio const sqlGeneratorsResource = useResource(DDLViewerTabPanel, SqlGeneratorsResource, nodeId); const ddlGenerator = sqlGeneratorsResource.data?.find(generator => generator.id.toLowerCase().includes(DDL_GENERATOR_ID.toLowerCase())); + const navNodeInfoResource = useResource(DDLViewerTabPanel, NavNodeInfoResource, nodeId); + const supportsFullDdl = navNodeInfoResource.data?.objectFeatures.includes(EObjectFeature.supportsFullDdl) ?? false; + const hasFullDdl = !!ddlGenerator && supportsFullDdl; + const connectionInfoResource = useResource(DDLViewerTabPanel, ConnectionInfoResource, ConnectionInfoActiveProjectKey); const connection = connectionInfoResource.resource.getConnectionForNode(nodeId); const connectionParam = connection ? createConnectionParam(connection) : null; @@ -87,7 +92,7 @@ export const DDLViewerTabPanel: NavNodeTransformViewComponent = observer(functio context.set(DATA_CONTEXT_DDL_VIEWER_NODE, nodeId, id); context.set(DATA_CONTEXT_DDL_VIEWER_VALUE, query, id); - if (ddlGenerator) { + if (hasFullDdl) { context.set(DATA_CONTEXT_DDL_VIEWER_FULL_DDL, { value: isFullDdl, loading: ddlLoading, onChange: handleFullDdlChange }, id); } }); diff --git a/webapp/packages/plugin-sql-generator/src/SqlGenerators/GeneratedSqlDialog.tsx b/webapp/packages/plugin-sql-generator/src/SqlGenerators/GeneratedSqlDialog.tsx index cd9bfaad085..cb59bf66cdf 100644 --- a/webapp/packages/plugin-sql-generator/src/SqlGenerators/GeneratedSqlDialog.tsx +++ b/webapp/packages/plugin-sql-generator/src/SqlGenerators/GeneratedSqlDialog.tsx @@ -24,7 +24,7 @@ import { ConnectionDialectResource, ConnectionInfoResource, createConnectionPara import { useService } from '@cloudbeaver/core-di'; import type { DialogComponentProps } from '@cloudbeaver/core-dialogs'; import { download, withTimestamp } from '@cloudbeaver/core-utils'; -import { NavNodeManagerService } from '@cloudbeaver/core-navigation-tree'; +import { EObjectFeature, NavNodeManagerService } from '@cloudbeaver/core-navigation-tree'; import { useCodemirrorExtensions } from '@cloudbeaver/plugin-codemirror6'; import { SqlEditorNavigatorService } from '@cloudbeaver/plugin-sql-editor-navigation-tab'; import { SQLCodeEditor, useSqlDialectExtension } from '@cloudbeaver/plugin-sql-editor-codemirror'; @@ -103,6 +103,9 @@ export const GeneratedSqlDialog = observer>(functi const visibleLoading = useStateDelay(state.loading, 300); const isDdlGenerator = payload.generatorId.toLowerCase().includes(DDL_GENERATOR_ID.toLowerCase()); + const node = navNodeManagerService.getNode(payload.nodeId); + const supportsFullDdl = node?.objectFeatures.includes(EObjectFeature.supportsFullDdl) ?? false; + const hasFullDdl = isDdlGenerator && supportsFullDdl; async function handleOpenInEditor() { try { @@ -155,7 +158,7 @@ export const GeneratedSqlDialog = observer>(functi label={translate('app_shared_sql_generators_compact_sql')} onChange={value => state.handleOptionChange('compactSql', value)} /> - {isDdlGenerator && ( + {hasFullDdl && ( Date: Mon, 3 Aug 2026 15:02:36 +0200 Subject: [PATCH 4/7] dbeaver/pro#4551 adds SqlEntityQueryResource to separate sqlGenerator logic --- .../src/SqlEntityQueryResource.ts | 92 +++++++++++++++++++ .../src/SqlGeneratorsResource.ts | 21 +---- .../packages/core-sql-generator/src/index.ts | 1 + .../packages/core-sql-generator/src/module.ts | 7 +- .../DataGridContextMenuGenerateSqlService.ts | 19 +++- .../src/DdlViewer/DDLViewerTabPanel.tsx | 52 ++++------- .../packages/plugin-ddl-viewer/src/module.ts | 2 +- .../SqlGenerators/SqlGeneratorsBootstrap.ts | 13 ++- 8 files changed, 145 insertions(+), 62 deletions(-) create mode 100644 webapp/packages/core-sql-generator/src/SqlEntityQueryResource.ts diff --git a/webapp/packages/core-sql-generator/src/SqlEntityQueryResource.ts b/webapp/packages/core-sql-generator/src/SqlEntityQueryResource.ts new file mode 100644 index 00000000000..f15d25aa7b2 --- /dev/null +++ b/webapp/packages/core-sql-generator/src/SqlEntityQueryResource.ts @@ -0,0 +1,92 @@ +/* + * CloudBeaver - Cloud Database Manager + * 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. + */ +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 { AsyncTaskInfoService } from '@cloudbeaver/core-root'; +import { GraphQLService, type SqlQueryGeneratorOptions } from '@cloudbeaver/core-sdk'; + +export interface ISqlEntityQueryKey { + readonly nodeId: string; + readonly generatorId: string; + readonly options: SqlQueryGeneratorOptions; +} + +@injectable(() => [GraphQLService, AsyncTaskInfoService, NavNodeInfoResource]) +export class SqlEntityQueryResource extends CachedMapResource { + constructor( + private readonly graphQLService: GraphQLService, + private readonly asyncTaskInfoService: AsyncTaskInfoService, + private readonly navNodeInfoResource: NavNodeInfoResource, + ) { + super(); + + this.navNodeInfoResource.outdateResource(this, nodeId => + resourceKeyList(this.keys.filter(key => ResourceKeyUtils.isIntersect(nodeId, key.nodeId))), + ); + this.navNodeInfoResource.deleteInResource(this, nodeId => + resourceKeyList(this.keys.filter(key => ResourceKeyUtils.isIntersect(nodeId, key.nodeId))), + ); + } + + protected async loader(key: ResourceKey): Promise> { + if (isResourceAlias(key)) { + throw new Error('Aliases not supported by this resource.'); + } + + const keys: ISqlEntityQueryKey[] = []; + const values: string[] = []; + + await ResourceKeyUtils.forEachAsync(key, async entityQueryKey => { + const query = await this.generateEntityQuery(entityQueryKey.generatorId, entityQueryKey.nodeId, entityQueryKey.options); + keys.push(entityQueryKey); + values.push(query); + }); + + this.set(resourceKeyList(keys), values); + + return this.data; + } + + private async generateEntityQuery(generatorId: string, nodeId: string, options?: SqlQueryGeneratorOptions): Promise { + const task = this.asyncTaskInfoService.create(async () => { + const { taskInfo } = await this.graphQLService.sdk.asyncSqlGenerateEntityQuery({ + generatorId, + nodePathList: nodeId, + generatorOptions: options, + }); + + return taskInfo; + }); + + const info = await this.asyncTaskInfoService.run(task); + await this.asyncTaskInfoService.remove(task.id); + + return info.taskResult; + } + + override isKeyEqual(param: ISqlEntityQueryKey, second: ISqlEntityQueryKey): boolean { + return ( + param.nodeId === second.nodeId && + param.generatorId === second.generatorId && + param.options.useFullyQualifiedNames === second.options.useFullyQualifiedNames && + param.options.compactSql === second.options.compactSql && + param.options.showFullDdl === second.options.showFullDdl + ); + } + + protected validateKey(key: ISqlEntityQueryKey): boolean { + return ( + typeof key === 'object' && + key !== null && + typeof key.nodeId === 'string' && + typeof key.generatorId === 'string' && + typeof key.options === 'object' + ); + } +} diff --git a/webapp/packages/core-sql-generator/src/SqlGeneratorsResource.ts b/webapp/packages/core-sql-generator/src/SqlGeneratorsResource.ts index 235057f4c12..d31c5dcca99 100644 --- a/webapp/packages/core-sql-generator/src/SqlGeneratorsResource.ts +++ b/webapp/packages/core-sql-generator/src/SqlGeneratorsResource.ts @@ -8,7 +8,6 @@ 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 { AsyncTaskInfoService } from '@cloudbeaver/core-root'; import { GraphQLService, type SqlGenerateResultSetQueryQueryVariables, @@ -24,12 +23,11 @@ export const getDefaultQueryGeneratorOptions = (): SqlQueryGeneratorOptions => ( }); export const DDL_GENERATOR_ID = 'tableDDL'; -@injectable(() => [GraphQLService, NavNodeInfoResource, AsyncTaskInfoService]) +@injectable(() => [GraphQLService, NavNodeInfoResource]) export class SqlGeneratorsResource extends CachedMapResource { constructor( private readonly graphQLService: GraphQLService, private readonly navNodeInfoResource: NavNodeInfoResource, - private readonly asyncTaskInfoService: AsyncTaskInfoService, ) { super(); @@ -37,23 +35,6 @@ export class SqlGeneratorsResource extends CachedMapResource { - const task = this.asyncTaskInfoService.create(async () => { - const { taskInfo } = await this.graphQLService.sdk.asyncSqlGenerateEntityQuery({ - generatorId, - nodePathList, - generatorOptions: options, - }); - - return taskInfo; - }); - - const info = await this.asyncTaskInfoService.run(task); - await this.asyncTaskInfoService.remove(task.id); - - return info.taskResult; - } - async generateResultSetSql(params: SqlGenerateResultSetQueryQueryVariables): Promise { const response = await this.graphQLService.sdk.sqlGenerateResultSetQuery(params); diff --git a/webapp/packages/core-sql-generator/src/index.ts b/webapp/packages/core-sql-generator/src/index.ts index 9067c769245..a7e19d8f739 100644 --- a/webapp/packages/core-sql-generator/src/index.ts +++ b/webapp/packages/core-sql-generator/src/index.ts @@ -9,3 +9,4 @@ import './module.js'; export * from './SqlGeneratorsResource.js'; +export * from './SqlEntityQueryResource.js'; diff --git a/webapp/packages/core-sql-generator/src/module.ts b/webapp/packages/core-sql-generator/src/module.ts index 04828847006..ac422fc999e 100644 --- a/webapp/packages/core-sql-generator/src/module.ts +++ b/webapp/packages/core-sql-generator/src/module.ts @@ -6,12 +6,17 @@ * you may not use this file except in compliance with the License. */ import { Dependency, ModuleRegistry, proxy } from '@cloudbeaver/core-di'; +import { SqlEntityQueryResource } from './SqlEntityQueryResource.js'; import { SqlGeneratorsResource } from './SqlGeneratorsResource.js'; export default ModuleRegistry.add({ name: '@cloudbeaver/core-sql-generator', configure: serviceCollection => { - serviceCollection.addSingleton(Dependency, proxy(SqlGeneratorsResource)).addSingleton(SqlGeneratorsResource); + serviceCollection + .addSingleton(Dependency, proxy(SqlGeneratorsResource)) + .addSingleton(SqlGeneratorsResource) + .addSingleton(Dependency, proxy(SqlEntityQueryResource)) + .addSingleton(SqlEntityQueryResource); }, }); diff --git a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridContextMenu/DataGridContextMenuGenerateSqlService.ts b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridContextMenu/DataGridContextMenuGenerateSqlService.ts index be59e15d0f6..2b26f49fae1 100644 --- a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridContextMenu/DataGridContextMenuGenerateSqlService.ts +++ b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridContextMenu/DataGridContextMenuGenerateSqlService.ts @@ -41,7 +41,13 @@ import { ACTION_DATA_GRID_GENERATE_SQL_SELECT_MANY } from '../Actions/GenerateSQ import { ACTION_DATA_GRID_GENERATE_SQL_UPDATE } from '../Actions/GenerateSQL/ACTION_DATA_GRID_GENERATE_SQL_UPDATE.js'; import { MENU_DATA_GRID_GENERATE_SQL } from './GenerateSQL/MENU_DATA_GRID_GENERATE_SQL.js'; import type { IDataContextProvider } from '@cloudbeaver/core-data-context'; -import { getDefaultQueryGeneratorOptions, GeneratedSqlDialog, SqlGeneratorsResource, DDL_GENERATOR_ID } from '@cloudbeaver/plugin-sql-generator'; +import { + getDefaultQueryGeneratorOptions, + GeneratedSqlDialog, + SqlEntityQueryResource, + SqlGeneratorsResource, + DDL_GENERATOR_ID, +} from '@cloudbeaver/plugin-sql-generator'; import { isNotNullDefined } from '@dbeaver/js-helpers'; @injectable(() => [ @@ -50,6 +56,7 @@ import { isNotNullDefined } from '@dbeaver/js-helpers'; CommonDialogService, NotificationService, SqlGeneratorsResource, + SqlEntityQueryResource, LocalizationService, NavNodeInfoResource, ]) @@ -60,6 +67,7 @@ export class DataGridContextMenuGenerateSqlService { private readonly commonDialogService: CommonDialogService, private readonly notificationService: NotificationService, private readonly sqlGenerationResource: SqlGeneratorsResource, + private readonly sqlEntityQueryResource: SqlEntityQueryResource, private readonly localizationService: LocalizationService, private readonly navNodeInfoResource: NavNodeInfoResource, ) {} @@ -229,7 +237,11 @@ export class DataGridContextMenuGenerateSqlService { return; } - const query = await this.sqlGenerationResource.generateEntityQuery(createGenerator.id, nodePathList, getDefaultQueryGeneratorOptions()); + const query = await this.sqlEntityQueryResource.load({ + nodeId: nodePathList, + generatorId: createGenerator.id, + options: getDefaultQueryGeneratorOptions(), + }); await this.commonDialogService.open(GeneratedSqlDialog, { query, @@ -238,7 +250,8 @@ export class DataGridContextMenuGenerateSqlService { generatorId: createGenerator.id, generatorName: createGenerator.label, options: getDefaultQueryGeneratorOptions(), - regenerateQuery: genOptions => this.sqlGenerationResource.generateEntityQuery(createGenerator.id, nodePathList, genOptions), + regenerateQuery: genOptions => + this.sqlEntityQueryResource.load({ nodeId: nodePathList, generatorId: createGenerator.id, options: genOptions }), }); } catch (e: any) { this.notificationService.logException(e, 'data_grid_table_generate_sql_error_title'); diff --git a/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DDLViewerTabPanel.tsx b/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DDLViewerTabPanel.tsx index b15d97efe80..113d12d9716 100644 --- a/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DDLViewerTabPanel.tsx +++ b/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DDLViewerTabPanel.tsx @@ -6,7 +6,7 @@ * you may not use this file except in compliance with the License. */ import { observer } from 'mobx-react-lite'; -import { useEffect, useState } from 'react'; +import { useState } from 'react'; import { s, useResource, useS } from '@cloudbeaver/core-blocks'; import { @@ -16,15 +16,13 @@ import { createConnectionParam, } from '@cloudbeaver/core-connections'; import { useDataContextLink } from '@cloudbeaver/core-data-context'; -import { useService } from '@cloudbeaver/core-di'; -import { NotificationService } from '@cloudbeaver/core-events'; import { EObjectFeature, NavNodeInfoResource } from '@cloudbeaver/core-navigation-tree'; import { MenuBar, MenuBarGroupStyles, MenuBarItemStyles, MenuBarStyles } from '@cloudbeaver/core-ui'; import { useMenu } from '@cloudbeaver/core-view'; import { useCodemirrorExtensions } from '@cloudbeaver/plugin-codemirror6'; import type { NavNodeTransformViewComponent } from '@cloudbeaver/plugin-navigation-tree'; import { SQLCodeEditor, useSqlDialectExtension } from '@cloudbeaver/plugin-sql-editor-codemirror'; -import { DDL_GENERATOR_ID, getDefaultQueryGeneratorOptions, SqlGeneratorsResource } from '@cloudbeaver/core-sql-generator'; +import { DDL_GENERATOR_ID, getDefaultQueryGeneratorOptions, SqlEntityQueryResource, SqlGeneratorsResource } from '@cloudbeaver/core-sql-generator'; import { DATA_CONTEXT_DDL_VIEWER_FULL_DDL } from './DATA_CONTEXT_DDL_VIEWER_FULL_DDL.js'; import { DATA_CONTEXT_DDL_VIEWER_NODE } from './DATA_CONTEXT_DDL_VIEWER_NODE.js'; @@ -35,11 +33,8 @@ import { MENU_DDL_VIEWER_FOOTER } from './MENU_DDL_VIEWER_FOOTER.js'; export const DDLViewerTabPanel: NavNodeTransformViewComponent = observer(function DDLViewerTabPanel({ nodeId, folderId }) { const styles = useS(style, MenuBarStyles, MenuBarItemStyles, MenuBarGroupStyles); const menu = useMenu({ menu: MENU_DDL_VIEWER_FOOTER }); - const notificationService = useService(NotificationService); const [isFullDdl, setIsFullDdl] = useState(false); - const [query, setQuery] = useState(''); - const [ddlLoading, setDdlLoading] = useState(false); const sqlGeneratorsResource = useResource(DDLViewerTabPanel, SqlGeneratorsResource, nodeId); const ddlGenerator = sqlGeneratorsResource.data?.find(generator => generator.id.toLowerCase().includes(DDL_GENERATOR_ID.toLowerCase())); @@ -58,34 +53,25 @@ export const DDLViewerTabPanel: NavNodeTransformViewComponent = observer(functio extensions.set(...sqlDialect); } - async function loadDdl(showFullDdl: boolean) { - if (!ddlGenerator) { - return; - } - - setDdlLoading(true); - try { - const newQuery = await sqlGeneratorsResource.resource.generateEntityQuery(ddlGenerator.id, nodeId, { - ...getDefaultQueryGeneratorOptions(), - showFullDdl, - }); - setQuery(newQuery); - } catch (error: any) { - notificationService.logException(error, 'plugin_ddl_viewer_full_ddl_error_title'); - } finally { - setDdlLoading(false); - } - } - - useEffect(() => { - if (ddlGenerator) { - loadDdl(isFullDdl); - } - }, [nodeId, !!ddlGenerator]); + const ddlEntityQueryResource = useResource( + DDLViewerTabPanel, + SqlEntityQueryResource, + ddlGenerator + ? { + nodeId, + generatorId: ddlGenerator.id, + options: { + ...getDefaultQueryGeneratorOptions(), + showFullDdl: isFullDdl, + }, + } + : null, + ); + const query = ddlEntityQueryResource.data ?? ''; + const ddlLoading = ddlEntityQueryResource.isLoading(); - async function handleFullDdlChange(value: boolean) { + function handleFullDdlChange(value: boolean) { setIsFullDdl(value); - await loadDdl(value); } useDataContextLink(menu.context, (context, id) => { diff --git a/webapp/packages/plugin-ddl-viewer/src/module.ts b/webapp/packages/plugin-ddl-viewer/src/module.ts index dcf10dd4f82..c25c2b4e27c 100644 --- a/webapp/packages/plugin-ddl-viewer/src/module.ts +++ b/webapp/packages/plugin-ddl-viewer/src/module.ts @@ -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. diff --git a/webapp/packages/plugin-sql-generator/src/SqlGenerators/SqlGeneratorsBootstrap.ts b/webapp/packages/plugin-sql-generator/src/SqlGenerators/SqlGeneratorsBootstrap.ts index f8dfc7e171d..d7f1b06ff8c 100644 --- a/webapp/packages/plugin-sql-generator/src/SqlGenerators/SqlGeneratorsBootstrap.ts +++ b/webapp/packages/plugin-sql-generator/src/SqlGenerators/SqlGeneratorsBootstrap.ts @@ -9,7 +9,7 @@ import { importLazyComponent } from '@cloudbeaver/core-blocks'; import { Bootstrap, injectable } from '@cloudbeaver/core-di'; import { CommonDialogService } from '@cloudbeaver/core-dialogs'; import { DATA_CONTEXT_NAV_NODE, EObjectFeature } from '@cloudbeaver/core-navigation-tree'; -import { getDefaultQueryGeneratorOptions, SqlGeneratorsResource } from '@cloudbeaver/core-sql-generator'; +import { getDefaultQueryGeneratorOptions, SqlEntityQueryResource, SqlGeneratorsResource } from '@cloudbeaver/core-sql-generator'; import { getCachedMapResourceLoaderState } from '@cloudbeaver/core-resource'; import { MenuBaseItem, MenuService } from '@cloudbeaver/core-view'; @@ -18,10 +18,11 @@ import { NotificationService } from '@cloudbeaver/core-events'; const GeneratedSqlDialog = importLazyComponent(() => import('./GeneratedSqlDialog.js').then(m => m.GeneratedSqlDialog)); -@injectable(() => [SqlGeneratorsResource, CommonDialogService, MenuService, NotificationService]) +@injectable(() => [SqlGeneratorsResource, SqlEntityQueryResource, CommonDialogService, MenuService, NotificationService]) export class SqlGeneratorsBootstrap extends Bootstrap { constructor( private readonly sqlGeneratorsResource: SqlGeneratorsResource, + private readonly sqlEntityQueryResource: SqlEntityQueryResource, private readonly commonDialogService: CommonDialogService, private readonly menuService: MenuService, private readonly notificationService: NotificationService, @@ -81,7 +82,11 @@ export class SqlGeneratorsBootstrap extends Bootstrap { { onSelect: async () => { try { - const query = await this.sqlGeneratorsResource.generateEntityQuery(action.id, node.uri, getDefaultQueryGeneratorOptions()); + const query = await this.sqlEntityQueryResource.load({ + nodeId: node.uri, + generatorId: action.id, + options: getDefaultQueryGeneratorOptions(), + }); await this.commonDialogService.open(GeneratedSqlDialog, { query, nodeId: node.uri, @@ -89,7 +94,7 @@ export class SqlGeneratorsBootstrap extends Bootstrap { generatorId: action.id, generatorName: action.label, options: getDefaultQueryGeneratorOptions(), - regenerateQuery: options => this.sqlGeneratorsResource.generateEntityQuery(action.id, node.uri, options), + regenerateQuery: options => this.sqlEntityQueryResource.load({ nodeId: node.uri, generatorId: action.id, options }), }); } catch (e: any) { this.notificationService.logException(e, 'app_shared_sql_generators_error_title'); From 83712d46c071bc138fda3b7743fe0b90be1b4a7a Mon Sep 17 00:00:00 2001 From: sergeyteleshev Date: Thu, 6 Aug 2026 09:28:44 +0200 Subject: [PATCH 5/7] dbeaver/pro#4551 refactor: remove core-sql-generator package and related files; migrate SQL generation logic to plugin-sql-generator --- .../packages/core-sql-generator/package.json | 38 -------- .../src/SqlEntityQueryResource.ts | 92 ------------------ .../packages/core-sql-generator/src/index.ts | 12 --- .../packages/core-sql-generator/src/module.ts | 22 ----- .../packages/core-sql-generator/tsconfig.json | 39 -------- .../DataGridContextMenuGenerateSqlService.ts | 19 +--- .../packages/plugin-ddl-viewer/package.json | 2 +- .../src/DdlViewer/DDLViewerTabPanel.tsx | 31 +++--- .../src/DdlViewer/useDdlEntityQuery.ts | 96 +++++++++++++++++++ .../packages/plugin-ddl-viewer/tsconfig.json | 6 +- .../packages/plugin-set-common/package.json | 1 - .../packages/plugin-set-common/src/index.ts | 2 - .../packages/plugin-set-common/tsconfig.json | 3 - .../plugin-sql-generator/package.json | 2 +- .../src/SqlGenerators/GeneratedSqlDialog.tsx | 2 +- .../SqlGenerators/SqlGeneratorsBootstrap.ts | 13 +-- .../SqlGenerators}/SqlGeneratorsResource.ts | 21 +++- .../plugin-sql-generator/src/index.ts | 2 +- .../plugin-sql-generator/src/module.ts | 5 +- .../plugin-sql-generator/tsconfig.json | 4 +- webapp/yarn.lock | 22 +---- 21 files changed, 151 insertions(+), 283 deletions(-) delete mode 100644 webapp/packages/core-sql-generator/package.json delete mode 100644 webapp/packages/core-sql-generator/src/SqlEntityQueryResource.ts delete mode 100644 webapp/packages/core-sql-generator/src/index.ts delete mode 100644 webapp/packages/core-sql-generator/src/module.ts delete mode 100644 webapp/packages/core-sql-generator/tsconfig.json create mode 100644 webapp/packages/plugin-ddl-viewer/src/DdlViewer/useDdlEntityQuery.ts rename webapp/packages/{core-sql-generator/src => plugin-sql-generator/src/SqlGenerators}/SqlGeneratorsResource.ts (73%) diff --git a/webapp/packages/core-sql-generator/package.json b/webapp/packages/core-sql-generator/package.json deleted file mode 100644 index 213f19f0ffc..00000000000 --- a/webapp/packages/core-sql-generator/package.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "name": "@cloudbeaver/core-sql-generator", - "type": "module", - "sideEffects": [ - "./lib/module.js", - "./lib/index.js", - "src/**/*.css", - "public/**/*" - ], - "version": "0.1.0", - "description": "", - "license": "Apache-2.0", - "exports": { - ".": "./lib/index.js", - "./module": "./lib/module.js" - }, - "scripts": { - "build": "tsc -b", - "clean": "rimraf --glob lib", - "lint": "eslint ./src/ --ext .ts,.tsx", - "test": "dbeaver-test", - "validate-dependencies": "core-cli-validate-dependencies" - }, - "dependencies": { - "@cloudbeaver/core-di": "workspace:*", - "@cloudbeaver/core-navigation-tree": "workspace:*", - "@cloudbeaver/core-resource": "workspace:*", - "@cloudbeaver/core-root": "workspace:*", - "@cloudbeaver/core-sdk": "workspace:*", - "tslib": "^2" - }, - "devDependencies": { - "@cloudbeaver/core-cli": "workspace:*", - "@cloudbeaver/tsconfig": "workspace:*", - "rimraf": "^6", - "typescript": "^5" - } -} diff --git a/webapp/packages/core-sql-generator/src/SqlEntityQueryResource.ts b/webapp/packages/core-sql-generator/src/SqlEntityQueryResource.ts deleted file mode 100644 index f15d25aa7b2..00000000000 --- a/webapp/packages/core-sql-generator/src/SqlEntityQueryResource.ts +++ /dev/null @@ -1,92 +0,0 @@ -/* - * CloudBeaver - Cloud Database Manager - * 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. - */ -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 { AsyncTaskInfoService } from '@cloudbeaver/core-root'; -import { GraphQLService, type SqlQueryGeneratorOptions } from '@cloudbeaver/core-sdk'; - -export interface ISqlEntityQueryKey { - readonly nodeId: string; - readonly generatorId: string; - readonly options: SqlQueryGeneratorOptions; -} - -@injectable(() => [GraphQLService, AsyncTaskInfoService, NavNodeInfoResource]) -export class SqlEntityQueryResource extends CachedMapResource { - constructor( - private readonly graphQLService: GraphQLService, - private readonly asyncTaskInfoService: AsyncTaskInfoService, - private readonly navNodeInfoResource: NavNodeInfoResource, - ) { - super(); - - this.navNodeInfoResource.outdateResource(this, nodeId => - resourceKeyList(this.keys.filter(key => ResourceKeyUtils.isIntersect(nodeId, key.nodeId))), - ); - this.navNodeInfoResource.deleteInResource(this, nodeId => - resourceKeyList(this.keys.filter(key => ResourceKeyUtils.isIntersect(nodeId, key.nodeId))), - ); - } - - protected async loader(key: ResourceKey): Promise> { - if (isResourceAlias(key)) { - throw new Error('Aliases not supported by this resource.'); - } - - const keys: ISqlEntityQueryKey[] = []; - const values: string[] = []; - - await ResourceKeyUtils.forEachAsync(key, async entityQueryKey => { - const query = await this.generateEntityQuery(entityQueryKey.generatorId, entityQueryKey.nodeId, entityQueryKey.options); - keys.push(entityQueryKey); - values.push(query); - }); - - this.set(resourceKeyList(keys), values); - - return this.data; - } - - private async generateEntityQuery(generatorId: string, nodeId: string, options?: SqlQueryGeneratorOptions): Promise { - const task = this.asyncTaskInfoService.create(async () => { - const { taskInfo } = await this.graphQLService.sdk.asyncSqlGenerateEntityQuery({ - generatorId, - nodePathList: nodeId, - generatorOptions: options, - }); - - return taskInfo; - }); - - const info = await this.asyncTaskInfoService.run(task); - await this.asyncTaskInfoService.remove(task.id); - - return info.taskResult; - } - - override isKeyEqual(param: ISqlEntityQueryKey, second: ISqlEntityQueryKey): boolean { - return ( - param.nodeId === second.nodeId && - param.generatorId === second.generatorId && - param.options.useFullyQualifiedNames === second.options.useFullyQualifiedNames && - param.options.compactSql === second.options.compactSql && - param.options.showFullDdl === second.options.showFullDdl - ); - } - - protected validateKey(key: ISqlEntityQueryKey): boolean { - return ( - typeof key === 'object' && - key !== null && - typeof key.nodeId === 'string' && - typeof key.generatorId === 'string' && - typeof key.options === 'object' - ); - } -} diff --git a/webapp/packages/core-sql-generator/src/index.ts b/webapp/packages/core-sql-generator/src/index.ts deleted file mode 100644 index a7e19d8f739..00000000000 --- a/webapp/packages/core-sql-generator/src/index.ts +++ /dev/null @@ -1,12 +0,0 @@ -/* - * CloudBeaver - Cloud Database Manager - * 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. - */ - -import './module.js'; - -export * from './SqlGeneratorsResource.js'; -export * from './SqlEntityQueryResource.js'; diff --git a/webapp/packages/core-sql-generator/src/module.ts b/webapp/packages/core-sql-generator/src/module.ts deleted file mode 100644 index ac422fc999e..00000000000 --- a/webapp/packages/core-sql-generator/src/module.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - * CloudBeaver - Cloud Database Manager - * 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. - */ -import { Dependency, ModuleRegistry, proxy } from '@cloudbeaver/core-di'; -import { SqlEntityQueryResource } from './SqlEntityQueryResource.js'; -import { SqlGeneratorsResource } from './SqlGeneratorsResource.js'; - -export default ModuleRegistry.add({ - name: '@cloudbeaver/core-sql-generator', - - configure: serviceCollection => { - serviceCollection - .addSingleton(Dependency, proxy(SqlGeneratorsResource)) - .addSingleton(SqlGeneratorsResource) - .addSingleton(Dependency, proxy(SqlEntityQueryResource)) - .addSingleton(SqlEntityQueryResource); - }, -}); diff --git a/webapp/packages/core-sql-generator/tsconfig.json b/webapp/packages/core-sql-generator/tsconfig.json deleted file mode 100644 index e12d3d6ed61..00000000000 --- a/webapp/packages/core-sql-generator/tsconfig.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "extends": "@cloudbeaver/tsconfig/tsconfig.json", - "compilerOptions": { - "rootDir": "src", - "outDir": "lib", - "tsBuildInfoFile": "lib/tsconfig.tsbuildinfo", - "composite": true - }, - "references": [ - { - "path": "../core-cli" - }, - { - "path": "../core-di" - }, - { - "path": "../core-navigation-tree" - }, - { - "path": "../core-resource" - }, - { - "path": "../core-root" - }, - { - "path": "../core-sdk" - } - ], - "include": [ - "__custom_mocks__/**/*", - "src/**/*", - "src/**/*.json", - "src/**/*.css" - ], - "exclude": [ - "**/node_modules", - "lib/**/*" - ] -} diff --git a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridContextMenu/DataGridContextMenuGenerateSqlService.ts b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridContextMenu/DataGridContextMenuGenerateSqlService.ts index 2b26f49fae1..be59e15d0f6 100644 --- a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridContextMenu/DataGridContextMenuGenerateSqlService.ts +++ b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridContextMenu/DataGridContextMenuGenerateSqlService.ts @@ -41,13 +41,7 @@ import { ACTION_DATA_GRID_GENERATE_SQL_SELECT_MANY } from '../Actions/GenerateSQ import { ACTION_DATA_GRID_GENERATE_SQL_UPDATE } from '../Actions/GenerateSQL/ACTION_DATA_GRID_GENERATE_SQL_UPDATE.js'; import { MENU_DATA_GRID_GENERATE_SQL } from './GenerateSQL/MENU_DATA_GRID_GENERATE_SQL.js'; import type { IDataContextProvider } from '@cloudbeaver/core-data-context'; -import { - getDefaultQueryGeneratorOptions, - GeneratedSqlDialog, - SqlEntityQueryResource, - SqlGeneratorsResource, - DDL_GENERATOR_ID, -} from '@cloudbeaver/plugin-sql-generator'; +import { getDefaultQueryGeneratorOptions, GeneratedSqlDialog, SqlGeneratorsResource, DDL_GENERATOR_ID } from '@cloudbeaver/plugin-sql-generator'; import { isNotNullDefined } from '@dbeaver/js-helpers'; @injectable(() => [ @@ -56,7 +50,6 @@ import { isNotNullDefined } from '@dbeaver/js-helpers'; CommonDialogService, NotificationService, SqlGeneratorsResource, - SqlEntityQueryResource, LocalizationService, NavNodeInfoResource, ]) @@ -67,7 +60,6 @@ export class DataGridContextMenuGenerateSqlService { private readonly commonDialogService: CommonDialogService, private readonly notificationService: NotificationService, private readonly sqlGenerationResource: SqlGeneratorsResource, - private readonly sqlEntityQueryResource: SqlEntityQueryResource, private readonly localizationService: LocalizationService, private readonly navNodeInfoResource: NavNodeInfoResource, ) {} @@ -237,11 +229,7 @@ export class DataGridContextMenuGenerateSqlService { return; } - const query = await this.sqlEntityQueryResource.load({ - nodeId: nodePathList, - generatorId: createGenerator.id, - options: getDefaultQueryGeneratorOptions(), - }); + const query = await this.sqlGenerationResource.generateEntityQuery(createGenerator.id, nodePathList, getDefaultQueryGeneratorOptions()); await this.commonDialogService.open(GeneratedSqlDialog, { query, @@ -250,8 +238,7 @@ export class DataGridContextMenuGenerateSqlService { generatorId: createGenerator.id, generatorName: createGenerator.label, options: getDefaultQueryGeneratorOptions(), - regenerateQuery: genOptions => - this.sqlEntityQueryResource.load({ nodeId: nodePathList, generatorId: createGenerator.id, options: genOptions }), + regenerateQuery: genOptions => this.sqlGenerationResource.generateEntityQuery(createGenerator.id, nodePathList, genOptions), }); } catch (e: any) { this.notificationService.logException(e, 'data_grid_table_generate_sql_error_title'); diff --git a/webapp/packages/plugin-ddl-viewer/package.json b/webapp/packages/plugin-ddl-viewer/package.json index 0da1e5cf610..623a2456fee 100644 --- a/webapp/packages/plugin-ddl-viewer/package.json +++ b/webapp/packages/plugin-ddl-viewer/package.json @@ -30,7 +30,6 @@ "@cloudbeaver/core-navigation-tree": "workspace:*", "@cloudbeaver/core-resource": "workspace:*", "@cloudbeaver/core-sdk": "workspace:*", - "@cloudbeaver/core-sql-generator": "workspace:*", "@cloudbeaver/core-ui": "workspace:*", "@cloudbeaver/core-utils": "workspace:*", "@cloudbeaver/core-view": "workspace:*", @@ -39,6 +38,7 @@ "@cloudbeaver/plugin-sql-editor": "workspace:*", "@cloudbeaver/plugin-sql-editor-codemirror": "workspace:*", "@cloudbeaver/plugin-sql-editor-navigation-tab": "workspace:*", + "@cloudbeaver/plugin-sql-generator": "workspace:*", "mobx": "^6", "mobx-react-lite": "^4", "react": "^19", diff --git a/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DDLViewerTabPanel.tsx b/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DDLViewerTabPanel.tsx index 113d12d9716..e9d84c36f65 100644 --- a/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DDLViewerTabPanel.tsx +++ b/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DDLViewerTabPanel.tsx @@ -8,7 +8,7 @@ import { observer } from 'mobx-react-lite'; import { useState } from 'react'; -import { s, useResource, useS } from '@cloudbeaver/core-blocks'; +import { s, useAutoLoad, useResource, useS } from '@cloudbeaver/core-blocks'; import { ConnectionDialectResource, ConnectionInfoActiveProjectKey, @@ -22,13 +22,14 @@ import { useMenu } from '@cloudbeaver/core-view'; import { useCodemirrorExtensions } from '@cloudbeaver/plugin-codemirror6'; import type { NavNodeTransformViewComponent } from '@cloudbeaver/plugin-navigation-tree'; import { SQLCodeEditor, useSqlDialectExtension } from '@cloudbeaver/plugin-sql-editor-codemirror'; -import { DDL_GENERATOR_ID, getDefaultQueryGeneratorOptions, SqlEntityQueryResource, SqlGeneratorsResource } from '@cloudbeaver/core-sql-generator'; +import { DDL_GENERATOR_ID, SqlGeneratorsResource } from '@cloudbeaver/plugin-sql-generator'; import { DATA_CONTEXT_DDL_VIEWER_FULL_DDL } from './DATA_CONTEXT_DDL_VIEWER_FULL_DDL.js'; import { DATA_CONTEXT_DDL_VIEWER_NODE } from './DATA_CONTEXT_DDL_VIEWER_NODE.js'; import { DATA_CONTEXT_DDL_VIEWER_VALUE } from './DATA_CONTEXT_DDL_VIEWER_VALUE.js'; import style from './DDLViewerTabPanel.module.css'; import { MENU_DDL_VIEWER_FOOTER } from './MENU_DDL_VIEWER_FOOTER.js'; +import { useDdlEntityQuery } from './useDdlEntityQuery.js'; export const DDLViewerTabPanel: NavNodeTransformViewComponent = observer(function DDLViewerTabPanel({ nodeId, folderId }) { const styles = useS(style, MenuBarStyles, MenuBarItemStyles, MenuBarGroupStyles); @@ -53,22 +54,16 @@ export const DDLViewerTabPanel: NavNodeTransformViewComponent = observer(functio extensions.set(...sqlDialect); } - const ddlEntityQueryResource = useResource( - DDLViewerTabPanel, - SqlEntityQueryResource, - ddlGenerator - ? { - nodeId, - generatorId: ddlGenerator.id, - options: { - ...getDefaultQueryGeneratorOptions(), - showFullDdl: isFullDdl, - }, - } - : null, - ); - const query = ddlEntityQueryResource.data ?? ''; - const ddlLoading = ddlEntityQueryResource.isLoading(); + const ddlQueryState = useDdlEntityQuery({ + nodeId, + generatorId: ddlGenerator?.id ?? null, + showFullDdl: isFullDdl, + }); + + useAutoLoad(DDLViewerTabPanel, ddlQueryState); + + const query = ddlQueryState.query ?? ''; + const ddlLoading = ddlQueryState.isLoading(); function handleFullDdlChange(value: boolean) { setIsFullDdl(value); diff --git a/webapp/packages/plugin-ddl-viewer/src/DdlViewer/useDdlEntityQuery.ts b/webapp/packages/plugin-ddl-viewer/src/DdlViewer/useDdlEntityQuery.ts new file mode 100644 index 00000000000..d428bf4698b --- /dev/null +++ b/webapp/packages/plugin-ddl-viewer/src/DdlViewer/useDdlEntityQuery.ts @@ -0,0 +1,96 @@ +/* + * CloudBeaver - Cloud Database Manager + * 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. + */ +import { observable } from 'mobx'; + +import { useObservableRef } from '@cloudbeaver/core-blocks'; +import type { ILoadableState } from '@cloudbeaver/core-utils'; +import { getDefaultQueryGeneratorOptions, SqlGeneratorsResource } from '@cloudbeaver/plugin-sql-generator'; +import { useService } from '@cloudbeaver/core-di'; + +interface IPayload { + nodeId: string; + generatorId: string | null; + showFullDdl: boolean; +} + +interface IState extends ILoadableState { + query: string | null; + exception: Error | null; + promise: Promise | null; + key: string | null; + payload: IPayload; + sqlGeneratorsResource: SqlGeneratorsResource; + getCurrentKey: () => string | null; +} + +export function useDdlEntityQuery(payload: IPayload): IState { + const sqlGeneratorsResource = useService(SqlGeneratorsResource); + const state = useObservableRef( + () => ({ + query: null, + exception: null, + promise: null, + key: null, + isLoadable() { + return this.payload.generatorId !== null; + }, + isLoaded() { + return this.query !== null; + }, + isError() { + return this.exception !== null; + }, + isLoading() { + return this.promise !== null; + }, + isOutdated() { + return this.getCurrentKey() !== this.key; + }, + getCurrentKey() { + const { generatorId, nodeId, showFullDdl } = this.payload; + + if (generatorId === null) { + return null; + } + + return `${nodeId}::${generatorId}::${showFullDdl}`; + }, + async load() { + if (this.payload.generatorId === null) { + return; + } + + const key = this.getCurrentKey(); + + try { + this.exception = null; + this.promise = this.sqlGeneratorsResource.generateEntityQuery(this.payload.generatorId, this.payload.nodeId, { + ...getDefaultQueryGeneratorOptions(), + showFullDdl: this.payload.showFullDdl, + }); + this.query = await this.promise; + this.key = key; + } catch (exception: any) { + this.exception = exception; + } finally { + this.promise = null; + } + }, + }), + { + query: observable.ref, + exception: observable.ref, + promise: observable.ref, + key: observable.ref, + payload: observable.ref, + }, + { payload, sqlGeneratorsResource }, + ); + + return state; +} diff --git a/webapp/packages/plugin-ddl-viewer/tsconfig.json b/webapp/packages/plugin-ddl-viewer/tsconfig.json index fc2a3057f96..219d5bf5f79 100644 --- a/webapp/packages/plugin-ddl-viewer/tsconfig.json +++ b/webapp/packages/plugin-ddl-viewer/tsconfig.json @@ -37,9 +37,6 @@ { "path": "../core-sdk" }, - { - "path": "../core-sql-generator" - }, { "path": "../core-ui" }, @@ -63,6 +60,9 @@ }, { "path": "../plugin-sql-editor-navigation-tab" + }, + { + "path": "../plugin-sql-generator" } ], "include": [ diff --git a/webapp/packages/plugin-set-common/package.json b/webapp/packages/plugin-set-common/package.json index ad927e97a0a..0405dd7ec47 100644 --- a/webapp/packages/plugin-set-common/package.json +++ b/webapp/packages/plugin-set-common/package.json @@ -47,7 +47,6 @@ "@cloudbeaver/core-settings": "workspace:*", "@cloudbeaver/core-settings-localization": "workspace:*", "@cloudbeaver/core-settings-user": "workspace:*", - "@cloudbeaver/core-sql-generator": "workspace:*", "@cloudbeaver/core-storage": "workspace:*", "@cloudbeaver/core-support": "workspace:*", "@cloudbeaver/core-task-manager": "workspace:*", diff --git a/webapp/packages/plugin-set-common/src/index.ts b/webapp/packages/plugin-set-common/src/index.ts index d610fa916d8..e5a52d24122 100644 --- a/webapp/packages/plugin-set-common/src/index.ts +++ b/webapp/packages/plugin-set-common/src/index.ts @@ -25,7 +25,6 @@ import coreResourceManager from '@cloudbeaver/core-resource-manager/module'; import coreRoot from '@cloudbeaver/core-root/module'; import coreRouting from '@cloudbeaver/core-routing/module'; import coreSdk from '@cloudbeaver/core-sdk/module'; -import coreSqlGenerator from '@cloudbeaver/core-sql-generator/module'; import coreServerLocalization from '@cloudbeaver/core-server-localization/module'; import coreSessionLocalization from '@cloudbeaver/core-session-localization/module'; import coreSettings from '@cloudbeaver/core-settings/module'; @@ -151,7 +150,6 @@ const core = [ coreAdministration, coreDialogs, coreResourceManager, - coreSqlGenerator, coreApp, coreClientActivity, coreNavigationTree, diff --git a/webapp/packages/plugin-set-common/tsconfig.json b/webapp/packages/plugin-set-common/tsconfig.json index 54add273999..68595ba915d 100644 --- a/webapp/packages/plugin-set-common/tsconfig.json +++ b/webapp/packages/plugin-set-common/tsconfig.json @@ -88,9 +88,6 @@ { "path": "../core-settings-user" }, - { - "path": "../core-sql-generator" - }, { "path": "../core-storage" }, diff --git a/webapp/packages/plugin-sql-generator/package.json b/webapp/packages/plugin-sql-generator/package.json index c7426df3f9d..9ac2e3a58d8 100644 --- a/webapp/packages/plugin-sql-generator/package.json +++ b/webapp/packages/plugin-sql-generator/package.json @@ -30,8 +30,8 @@ "@cloudbeaver/core-localization": "workspace:*", "@cloudbeaver/core-navigation-tree": "workspace:*", "@cloudbeaver/core-resource": "workspace:*", + "@cloudbeaver/core-root": "workspace:*", "@cloudbeaver/core-sdk": "workspace:*", - "@cloudbeaver/core-sql-generator": "workspace:*", "@cloudbeaver/core-utils": "workspace:*", "@cloudbeaver/core-view": "workspace:*", "@cloudbeaver/plugin-codemirror6": "workspace:*", diff --git a/webapp/packages/plugin-sql-generator/src/SqlGenerators/GeneratedSqlDialog.tsx b/webapp/packages/plugin-sql-generator/src/SqlGenerators/GeneratedSqlDialog.tsx index cb59bf66cdf..c6889c2f70b 100644 --- a/webapp/packages/plugin-sql-generator/src/SqlGenerators/GeneratedSqlDialog.tsx +++ b/webapp/packages/plugin-sql-generator/src/SqlGenerators/GeneratedSqlDialog.tsx @@ -33,7 +33,7 @@ import { action, observable } from 'mobx'; import { NotificationService } from '@cloudbeaver/core-events'; import type { SqlQueryGeneratorOptions } from '@cloudbeaver/core-sdk'; -import { DDL_GENERATOR_ID } from '@cloudbeaver/core-sql-generator'; +import { DDL_GENERATOR_ID } from './SqlGeneratorsResource.js'; interface Payload { nodeId: string; diff --git a/webapp/packages/plugin-sql-generator/src/SqlGenerators/SqlGeneratorsBootstrap.ts b/webapp/packages/plugin-sql-generator/src/SqlGenerators/SqlGeneratorsBootstrap.ts index d7f1b06ff8c..c24631afcb5 100644 --- a/webapp/packages/plugin-sql-generator/src/SqlGenerators/SqlGeneratorsBootstrap.ts +++ b/webapp/packages/plugin-sql-generator/src/SqlGenerators/SqlGeneratorsBootstrap.ts @@ -9,20 +9,19 @@ import { importLazyComponent } from '@cloudbeaver/core-blocks'; import { Bootstrap, injectable } from '@cloudbeaver/core-di'; import { CommonDialogService } from '@cloudbeaver/core-dialogs'; import { DATA_CONTEXT_NAV_NODE, EObjectFeature } from '@cloudbeaver/core-navigation-tree'; -import { getDefaultQueryGeneratorOptions, SqlEntityQueryResource, SqlGeneratorsResource } from '@cloudbeaver/core-sql-generator'; import { getCachedMapResourceLoaderState } from '@cloudbeaver/core-resource'; import { MenuBaseItem, MenuService } from '@cloudbeaver/core-view'; +import { getDefaultQueryGeneratorOptions, SqlGeneratorsResource } from './SqlGeneratorsResource.js'; import { MENU_SQL_GENERATORS } from './MENU_SQL_GENERATORS.js'; import { NotificationService } from '@cloudbeaver/core-events'; const GeneratedSqlDialog = importLazyComponent(() => import('./GeneratedSqlDialog.js').then(m => m.GeneratedSqlDialog)); -@injectable(() => [SqlGeneratorsResource, SqlEntityQueryResource, CommonDialogService, MenuService, NotificationService]) +@injectable(() => [SqlGeneratorsResource, CommonDialogService, MenuService, NotificationService]) export class SqlGeneratorsBootstrap extends Bootstrap { constructor( private readonly sqlGeneratorsResource: SqlGeneratorsResource, - private readonly sqlEntityQueryResource: SqlEntityQueryResource, private readonly commonDialogService: CommonDialogService, private readonly menuService: MenuService, private readonly notificationService: NotificationService, @@ -82,11 +81,7 @@ export class SqlGeneratorsBootstrap extends Bootstrap { { onSelect: async () => { try { - const query = await this.sqlEntityQueryResource.load({ - nodeId: node.uri, - generatorId: action.id, - options: getDefaultQueryGeneratorOptions(), - }); + const query = await this.sqlGeneratorsResource.generateEntityQuery(action.id, node.uri, getDefaultQueryGeneratorOptions()); await this.commonDialogService.open(GeneratedSqlDialog, { query, nodeId: node.uri, @@ -94,7 +89,7 @@ export class SqlGeneratorsBootstrap extends Bootstrap { generatorId: action.id, generatorName: action.label, options: getDefaultQueryGeneratorOptions(), - regenerateQuery: options => this.sqlEntityQueryResource.load({ nodeId: node.uri, generatorId: action.id, options }), + regenerateQuery: options => this.sqlGeneratorsResource.generateEntityQuery(action.id, node.uri, options), }); } catch (e: any) { this.notificationService.logException(e, 'app_shared_sql_generators_error_title'); diff --git a/webapp/packages/core-sql-generator/src/SqlGeneratorsResource.ts b/webapp/packages/plugin-sql-generator/src/SqlGenerators/SqlGeneratorsResource.ts similarity index 73% rename from webapp/packages/core-sql-generator/src/SqlGeneratorsResource.ts rename to webapp/packages/plugin-sql-generator/src/SqlGenerators/SqlGeneratorsResource.ts index d31c5dcca99..a712535ef0d 100644 --- a/webapp/packages/core-sql-generator/src/SqlGeneratorsResource.ts +++ b/webapp/packages/plugin-sql-generator/src/SqlGenerators/SqlGeneratorsResource.ts @@ -8,6 +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 { AsyncTaskInfoService } from '@cloudbeaver/core-root'; import { GraphQLService, type SqlGenerateResultSetQueryQueryVariables, @@ -23,10 +24,11 @@ export const getDefaultQueryGeneratorOptions = (): SqlQueryGeneratorOptions => ( }); export const DDL_GENERATOR_ID = 'tableDDL'; -@injectable(() => [GraphQLService, NavNodeInfoResource]) +@injectable(() => [GraphQLService, AsyncTaskInfoService, NavNodeInfoResource]) export class SqlGeneratorsResource extends CachedMapResource { constructor( private readonly graphQLService: GraphQLService, + private readonly asyncTaskInfoService: AsyncTaskInfoService, private readonly navNodeInfoResource: NavNodeInfoResource, ) { super(); @@ -35,6 +37,23 @@ export class SqlGeneratorsResource extends CachedMapResource { + const task = this.asyncTaskInfoService.create(async () => { + const { taskInfo } = await this.graphQLService.sdk.asyncSqlGenerateEntityQuery({ + generatorId, + nodePathList, + generatorOptions: options, + }); + + return taskInfo; + }); + + const info = await this.asyncTaskInfoService.run(task); + await this.asyncTaskInfoService.remove(task.id); + + return info.taskResult; + } + async generateResultSetSql(params: SqlGenerateResultSetQueryQueryVariables): Promise { const response = await this.graphQLService.sdk.sqlGenerateResultSetQuery(params); diff --git a/webapp/packages/plugin-sql-generator/src/index.ts b/webapp/packages/plugin-sql-generator/src/index.ts index e8e551adf0c..aa18008aaf1 100644 --- a/webapp/packages/plugin-sql-generator/src/index.ts +++ b/webapp/packages/plugin-sql-generator/src/index.ts @@ -8,7 +8,7 @@ import './module.js'; -export * from '@cloudbeaver/core-sql-generator'; +export * from './SqlGenerators/SqlGeneratorsResource.js'; export * from './SqlGenerators/SqlGeneratorsBootstrap.js'; export * from './actions/ACTION_SQL_GENERATE.js'; export * from './SqlGenerators/GeneratedSqlDialog.js'; diff --git a/webapp/packages/plugin-sql-generator/src/module.ts b/webapp/packages/plugin-sql-generator/src/module.ts index 3fb06c12cf8..b9e35e0b1e9 100644 --- a/webapp/packages/plugin-sql-generator/src/module.ts +++ b/webapp/packages/plugin-sql-generator/src/module.ts @@ -6,8 +6,9 @@ * you may not use this file except in compliance with the License. */ -import { Bootstrap, ModuleRegistry } from '@cloudbeaver/core-di'; +import { Bootstrap, Dependency, ModuleRegistry, proxy } from '@cloudbeaver/core-di'; import { SqlGeneratorsBootstrap } from './SqlGenerators/SqlGeneratorsBootstrap.js'; +import { SqlGeneratorsResource } from './SqlGenerators/SqlGeneratorsResource.js'; import { ScriptPreviewService } from './ScriptPreview/ScriptPreviewService.js'; import { LocaleService } from './LocaleService.js'; import { GeneratorMenuBootstrap } from './GeneratorMenuBootstrap.js'; @@ -17,9 +18,11 @@ export default ModuleRegistry.add({ configure: serviceCollection => { serviceCollection + .addSingleton(Dependency, proxy(SqlGeneratorsResource)) .addSingleton(Bootstrap, GeneratorMenuBootstrap) .addSingleton(Bootstrap, SqlGeneratorsBootstrap) .addSingleton(Bootstrap, LocaleService) + .addSingleton(SqlGeneratorsResource) .addSingleton(ScriptPreviewService); }, }); diff --git a/webapp/packages/plugin-sql-generator/tsconfig.json b/webapp/packages/plugin-sql-generator/tsconfig.json index b50426aadc5..38285497ef6 100644 --- a/webapp/packages/plugin-sql-generator/tsconfig.json +++ b/webapp/packages/plugin-sql-generator/tsconfig.json @@ -38,10 +38,10 @@ "path": "../core-resource" }, { - "path": "../core-sdk" + "path": "../core-root" }, { - "path": "../core-sql-generator" + "path": "../core-sdk" }, { "path": "../core-utils" diff --git a/webapp/yarn.lock b/webapp/yarn.lock index 34b66ea615e..6c2b0fc1786 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -2184,23 +2184,6 @@ __metadata: languageName: unknown linkType: soft -"@cloudbeaver/core-sql-generator@workspace:*, @cloudbeaver/core-sql-generator@workspace:packages/core-sql-generator": - version: 0.0.0-use.local - resolution: "@cloudbeaver/core-sql-generator@workspace:packages/core-sql-generator" - dependencies: - "@cloudbeaver/core-cli": "workspace:*" - "@cloudbeaver/core-di": "workspace:*" - "@cloudbeaver/core-navigation-tree": "workspace:*" - "@cloudbeaver/core-resource": "workspace:*" - "@cloudbeaver/core-root": "workspace:*" - "@cloudbeaver/core-sdk": "workspace:*" - "@cloudbeaver/tsconfig": "workspace:*" - rimraf: "npm:^6" - tslib: "npm:^2" - typescript: "npm:^5" - languageName: unknown - linkType: soft - "@cloudbeaver/core-storage@workspace:*, @cloudbeaver/core-storage@workspace:packages/core-storage": version: 0.0.0-use.local resolution: "@cloudbeaver/core-storage@workspace:packages/core-storage" @@ -3525,7 +3508,6 @@ __metadata: "@cloudbeaver/core-navigation-tree": "workspace:*" "@cloudbeaver/core-resource": "workspace:*" "@cloudbeaver/core-sdk": "workspace:*" - "@cloudbeaver/core-sql-generator": "workspace:*" "@cloudbeaver/core-ui": "workspace:*" "@cloudbeaver/core-utils": "workspace:*" "@cloudbeaver/core-view": "workspace:*" @@ -3534,6 +3516,7 @@ __metadata: "@cloudbeaver/plugin-sql-editor": "workspace:*" "@cloudbeaver/plugin-sql-editor-codemirror": "workspace:*" "@cloudbeaver/plugin-sql-editor-navigation-tab": "workspace:*" + "@cloudbeaver/plugin-sql-generator": "workspace:*" "@cloudbeaver/tsconfig": "workspace:*" "@types/react": "npm:^19" mobx: "npm:^6" @@ -4259,7 +4242,6 @@ __metadata: "@cloudbeaver/core-settings": "workspace:*" "@cloudbeaver/core-settings-localization": "workspace:*" "@cloudbeaver/core-settings-user": "workspace:*" - "@cloudbeaver/core-sql-generator": "workspace:*" "@cloudbeaver/core-storage": "workspace:*" "@cloudbeaver/core-support": "workspace:*" "@cloudbeaver/core-task-manager": "workspace:*" @@ -4738,8 +4720,8 @@ __metadata: "@cloudbeaver/core-localization": "workspace:*" "@cloudbeaver/core-navigation-tree": "workspace:*" "@cloudbeaver/core-resource": "workspace:*" + "@cloudbeaver/core-root": "workspace:*" "@cloudbeaver/core-sdk": "workspace:*" - "@cloudbeaver/core-sql-generator": "workspace:*" "@cloudbeaver/core-utils": "workspace:*" "@cloudbeaver/core-view": "workspace:*" "@cloudbeaver/plugin-codemirror6": "workspace:*" From 1021985fc6eae12a3919aee4ffa26799c9ec5786 Mon Sep 17 00:00:00 2001 From: sergeyteleshev Date: Thu, 6 Aug 2026 10:13:48 +0200 Subject: [PATCH 6/7] dbeaver/pro#4551 implements DDL query state management and refactors DDL viewer context --- .../DATA_CONTEXT_DDL_VIEWER_FULL_DDL.ts | 16 ------ ...UE.ts => DATA_CONTEXT_DDL_VIEWER_QUERY.ts} | 4 +- .../src/DdlViewer/DDLQueryStateService.ts | 26 +++++++++ .../src/DdlViewer/DDLViewerFooterService.ts | 55 ++++++++++++------- .../src/DdlViewer/DDLViewerTabPanel.tsx | 32 +---------- .../src/DdlViewer/useDdlEntityQuery.ts | 45 ++++++++------- .../ExtendedDDLViewerTabPanel.tsx | 4 +- .../packages/plugin-ddl-viewer/src/module.ts | 2 + 8 files changed, 94 insertions(+), 90 deletions(-) delete mode 100644 webapp/packages/plugin-ddl-viewer/src/DdlViewer/DATA_CONTEXT_DDL_VIEWER_FULL_DDL.ts rename webapp/packages/plugin-ddl-viewer/src/DdlViewer/{DATA_CONTEXT_DDL_VIEWER_VALUE.ts => DATA_CONTEXT_DDL_VIEWER_QUERY.ts} (63%) create mode 100644 webapp/packages/plugin-ddl-viewer/src/DdlViewer/DDLQueryStateService.ts diff --git a/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DATA_CONTEXT_DDL_VIEWER_FULL_DDL.ts b/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DATA_CONTEXT_DDL_VIEWER_FULL_DDL.ts deleted file mode 100644 index eeada23523c..00000000000 --- a/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DATA_CONTEXT_DDL_VIEWER_FULL_DDL.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * CloudBeaver - Cloud Database Manager - * 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. - */ -import { createDataContext } from '@cloudbeaver/core-data-context'; - -export interface IDdlViewerFullDdlState { - value: boolean; - loading: boolean; - onChange: (value: boolean) => void; -} - -export const DATA_CONTEXT_DDL_VIEWER_FULL_DDL = createDataContext('ddl-full-ddl'); diff --git a/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DATA_CONTEXT_DDL_VIEWER_VALUE.ts b/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DATA_CONTEXT_DDL_VIEWER_QUERY.ts similarity index 63% rename from webapp/packages/plugin-ddl-viewer/src/DdlViewer/DATA_CONTEXT_DDL_VIEWER_VALUE.ts rename to webapp/packages/plugin-ddl-viewer/src/DdlViewer/DATA_CONTEXT_DDL_VIEWER_QUERY.ts index ac2e51d7669..caa900ab7a8 100644 --- a/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DATA_CONTEXT_DDL_VIEWER_VALUE.ts +++ b/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DATA_CONTEXT_DDL_VIEWER_QUERY.ts @@ -1,10 +1,10 @@ /* * 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. */ import { createDataContext } from '@cloudbeaver/core-data-context'; -export const DATA_CONTEXT_DDL_VIEWER_VALUE = createDataContext('ddl-value'); +export const DATA_CONTEXT_DDL_VIEWER_QUERY = createDataContext('ddl-query'); diff --git a/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DDLQueryStateService.ts b/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DDLQueryStateService.ts new file mode 100644 index 00000000000..6278039aa49 --- /dev/null +++ b/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DDLQueryStateService.ts @@ -0,0 +1,26 @@ +/* + * CloudBeaver - Cloud Database Manager + * 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. + */ +import { injectable } from '@cloudbeaver/core-di'; +import { MetadataMap } from '@cloudbeaver/core-utils'; + +@injectable() +export class DDLQueryStateService { + private readonly fullDdlState = new MetadataMap(() => false); + + isFullDdlEnabled(nodeId: string): boolean { + return this.fullDdlState.get(nodeId); + } + + setFullDdlEnabled(nodeId: string, value: boolean): void { + this.fullDdlState.set(nodeId, value); + } + + toggleFullDdl(nodeId: string): void { + this.setFullDdlEnabled(nodeId, !this.isFullDdlEnabled(nodeId)); + } +} diff --git a/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DDLViewerFooterService.ts b/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DDLViewerFooterService.ts index 6fdbdd171ba..822be7522b3 100644 --- a/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DDLViewerFooterService.ts +++ b/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DDLViewerFooterService.ts @@ -7,19 +7,29 @@ */ import { ConnectionInfoResource, createConnectionParam } from '@cloudbeaver/core-connections'; import { injectable } from '@cloudbeaver/core-di'; -import { NavNodeManagerService } from '@cloudbeaver/core-navigation-tree'; +import { EObjectFeature, NavNodeInfoResource, NavNodeManagerService } from '@cloudbeaver/core-navigation-tree'; import { download, withTimestamp } from '@cloudbeaver/core-utils'; import { ACTION_SAVE, ActionService, MenuService } from '@cloudbeaver/core-view'; +import { DDL_GENERATOR_ID, SqlGeneratorsResource } from '@cloudbeaver/plugin-sql-generator'; import { LocalStorageSqlDataSource } from '@cloudbeaver/plugin-sql-editor'; import { ACTION_SQL_EDITOR_OPEN, SqlEditorNavigatorService } from '@cloudbeaver/plugin-sql-editor-navigation-tab'; import { ACTION_DDL_VIEWER_FULL_DDL } from './ACTION_DDL_VIEWER_FULL_DDL.js'; -import { DATA_CONTEXT_DDL_VIEWER_FULL_DDL } from './DATA_CONTEXT_DDL_VIEWER_FULL_DDL.js'; import { DATA_CONTEXT_DDL_VIEWER_NODE } from './DATA_CONTEXT_DDL_VIEWER_NODE.js'; -import { DATA_CONTEXT_DDL_VIEWER_VALUE } from './DATA_CONTEXT_DDL_VIEWER_VALUE.js'; +import { DATA_CONTEXT_DDL_VIEWER_QUERY } from './DATA_CONTEXT_DDL_VIEWER_QUERY.js'; +import { DDLQueryStateService } from './DDLQueryStateService.js'; import { MENU_DDL_VIEWER_FOOTER } from './MENU_DDL_VIEWER_FOOTER.js'; -@injectable(() => [NavNodeManagerService, ActionService, MenuService, SqlEditorNavigatorService, ConnectionInfoResource]) +@injectable(() => [ + NavNodeManagerService, + ActionService, + MenuService, + SqlEditorNavigatorService, + ConnectionInfoResource, + DDLQueryStateService, + NavNodeInfoResource, + SqlGeneratorsResource, +]) export class DDLViewerFooterService { constructor( private readonly navNodeManagerService: NavNodeManagerService, @@ -27,18 +37,21 @@ export class DDLViewerFooterService { private readonly menuService: MenuService, private readonly sqlEditorNavigatorService: SqlEditorNavigatorService, private readonly connectionInfoResource: ConnectionInfoResource, + private readonly ddlQueryStateService: DDLQueryStateService, + private readonly navNodeInfoResource: NavNodeInfoResource, + private readonly sqlGeneratorsResource: SqlGeneratorsResource, ) {} register(): void { this.actionsService.addHandler({ id: 'ddl-viewer-footer-base-handler', menus: [MENU_DDL_VIEWER_FOOTER], - contexts: [DATA_CONTEXT_DDL_VIEWER_NODE, DATA_CONTEXT_DDL_VIEWER_VALUE], + contexts: [DATA_CONTEXT_DDL_VIEWER_NODE, DATA_CONTEXT_DDL_VIEWER_QUERY], actions: [ACTION_SAVE, ACTION_SQL_EDITOR_OPEN], handler: async (context, action) => { switch (action) { case ACTION_SAVE: { - const ddl = context.get(DATA_CONTEXT_DDL_VIEWER_VALUE)!; + const ddl = context.get(DATA_CONTEXT_DDL_VIEWER_QUERY)!; const nodeId = context.get(DATA_CONTEXT_DDL_VIEWER_NODE); const blob = new Blob([ddl], { @@ -52,7 +65,7 @@ export class DDLViewerFooterService { break; } case ACTION_SQL_EDITOR_OPEN: { - const ddl = context.get(DATA_CONTEXT_DDL_VIEWER_VALUE)!; + const ddl = context.get(DATA_CONTEXT_DDL_VIEWER_QUERY)!; const nodeId = context.get(DATA_CONTEXT_DDL_VIEWER_NODE)!; const connection = this.connectionInfoResource.getConnectionForNode(nodeId); @@ -97,29 +110,31 @@ export class DDLViewerFooterService { this.menuService.addCreator({ menus: [MENU_DDL_VIEWER_FOOTER], - getItems: (context, items) => { - if (!context.get(DATA_CONTEXT_DDL_VIEWER_FULL_DDL)) { - return items; - } - - return [...items, ACTION_DDL_VIEWER_FULL_DDL]; - }, + contexts: [DATA_CONTEXT_DDL_VIEWER_NODE], + isApplicable: context => this.hasFullDdl(context.get(DATA_CONTEXT_DDL_VIEWER_NODE)!), + getItems: (context, items) => [...items, ACTION_DDL_VIEWER_FULL_DDL], }); this.actionsService.addHandler({ id: 'ddl-viewer-footer-full-ddl-handler', menus: [MENU_DDL_VIEWER_FOOTER], actions: [ACTION_DDL_VIEWER_FULL_DDL], - contexts: [DATA_CONTEXT_DDL_VIEWER_FULL_DDL], - isChecked: context => context.get(DATA_CONTEXT_DDL_VIEWER_FULL_DDL)!.value, - isDisabled: context => context.get(DATA_CONTEXT_DDL_VIEWER_FULL_DDL)!.loading, - isLoading: context => context.get(DATA_CONTEXT_DDL_VIEWER_FULL_DDL)!.loading, + contexts: [DATA_CONTEXT_DDL_VIEWER_NODE], + isChecked: context => this.ddlQueryStateService.isFullDdlEnabled(context.get(DATA_CONTEXT_DDL_VIEWER_NODE)!), handler: (context, action) => { if (action === ACTION_DDL_VIEWER_FULL_DDL) { - const fullDdlState = context.get(DATA_CONTEXT_DDL_VIEWER_FULL_DDL)!; - fullDdlState.onChange(!fullDdlState.value); + this.ddlQueryStateService.toggleFullDdl(context.get(DATA_CONTEXT_DDL_VIEWER_NODE)!); } }, }); } + + private hasFullDdl(nodeId: string): boolean { + const supportsFullDdl = this.navNodeInfoResource.get(nodeId)?.objectFeatures.includes(EObjectFeature.supportsFullDdl) ?? false; + const ddlGenerator = this.sqlGeneratorsResource + .get(nodeId) + ?.find(generator => generator.id.toLowerCase().includes(DDL_GENERATOR_ID.toLowerCase())); + + return supportsFullDdl && !!ddlGenerator; + } } diff --git a/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DDLViewerTabPanel.tsx b/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DDLViewerTabPanel.tsx index e9d84c36f65..7eedf48bdf5 100644 --- a/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DDLViewerTabPanel.tsx +++ b/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DDLViewerTabPanel.tsx @@ -6,7 +6,6 @@ * you may not use this file except in compliance with the License. */ import { observer } from 'mobx-react-lite'; -import { useState } from 'react'; import { s, useAutoLoad, useResource, useS } from '@cloudbeaver/core-blocks'; import { @@ -16,17 +15,14 @@ import { createConnectionParam, } from '@cloudbeaver/core-connections'; import { useDataContextLink } from '@cloudbeaver/core-data-context'; -import { EObjectFeature, NavNodeInfoResource } from '@cloudbeaver/core-navigation-tree'; import { MenuBar, MenuBarGroupStyles, MenuBarItemStyles, MenuBarStyles } from '@cloudbeaver/core-ui'; import { useMenu } from '@cloudbeaver/core-view'; import { useCodemirrorExtensions } from '@cloudbeaver/plugin-codemirror6'; import type { NavNodeTransformViewComponent } from '@cloudbeaver/plugin-navigation-tree'; import { SQLCodeEditor, useSqlDialectExtension } from '@cloudbeaver/plugin-sql-editor-codemirror'; -import { DDL_GENERATOR_ID, SqlGeneratorsResource } from '@cloudbeaver/plugin-sql-generator'; -import { DATA_CONTEXT_DDL_VIEWER_FULL_DDL } from './DATA_CONTEXT_DDL_VIEWER_FULL_DDL.js'; import { DATA_CONTEXT_DDL_VIEWER_NODE } from './DATA_CONTEXT_DDL_VIEWER_NODE.js'; -import { DATA_CONTEXT_DDL_VIEWER_VALUE } from './DATA_CONTEXT_DDL_VIEWER_VALUE.js'; +import { DATA_CONTEXT_DDL_VIEWER_QUERY } from './DATA_CONTEXT_DDL_VIEWER_QUERY.js'; import style from './DDLViewerTabPanel.module.css'; import { MENU_DDL_VIEWER_FOOTER } from './MENU_DDL_VIEWER_FOOTER.js'; import { useDdlEntityQuery } from './useDdlEntityQuery.js'; @@ -35,15 +31,6 @@ export const DDLViewerTabPanel: NavNodeTransformViewComponent = observer(functio const styles = useS(style, MenuBarStyles, MenuBarItemStyles, MenuBarGroupStyles); const menu = useMenu({ menu: MENU_DDL_VIEWER_FOOTER }); - const [isFullDdl, setIsFullDdl] = useState(false); - - const sqlGeneratorsResource = useResource(DDLViewerTabPanel, SqlGeneratorsResource, nodeId); - const ddlGenerator = sqlGeneratorsResource.data?.find(generator => generator.id.toLowerCase().includes(DDL_GENERATOR_ID.toLowerCase())); - - const navNodeInfoResource = useResource(DDLViewerTabPanel, NavNodeInfoResource, nodeId); - const supportsFullDdl = navNodeInfoResource.data?.objectFeatures.includes(EObjectFeature.supportsFullDdl) ?? false; - const hasFullDdl = !!ddlGenerator && supportsFullDdl; - const connectionInfoResource = useResource(DDLViewerTabPanel, ConnectionInfoResource, ConnectionInfoActiveProjectKey); const connection = connectionInfoResource.resource.getConnectionForNode(nodeId); const connectionParam = connection ? createConnectionParam(connection) : null; @@ -54,28 +41,15 @@ export const DDLViewerTabPanel: NavNodeTransformViewComponent = observer(functio extensions.set(...sqlDialect); } - const ddlQueryState = useDdlEntityQuery({ - nodeId, - generatorId: ddlGenerator?.id ?? null, - showFullDdl: isFullDdl, - }); + const ddlQueryState = useDdlEntityQuery(nodeId); useAutoLoad(DDLViewerTabPanel, ddlQueryState); const query = ddlQueryState.query ?? ''; - const ddlLoading = ddlQueryState.isLoading(); - - function handleFullDdlChange(value: boolean) { - setIsFullDdl(value); - } useDataContextLink(menu.context, (context, id) => { context.set(DATA_CONTEXT_DDL_VIEWER_NODE, nodeId, id); - context.set(DATA_CONTEXT_DDL_VIEWER_VALUE, query, id); - - if (hasFullDdl) { - context.set(DATA_CONTEXT_DDL_VIEWER_FULL_DDL, { value: isFullDdl, loading: ddlLoading, onChange: handleFullDdlChange }, id); - } + context.set(DATA_CONTEXT_DDL_VIEWER_QUERY, query, id); }); return ( diff --git a/webapp/packages/plugin-ddl-viewer/src/DdlViewer/useDdlEntityQuery.ts b/webapp/packages/plugin-ddl-viewer/src/DdlViewer/useDdlEntityQuery.ts index d428bf4698b..45d04cd24d9 100644 --- a/webapp/packages/plugin-ddl-viewer/src/DdlViewer/useDdlEntityQuery.ts +++ b/webapp/packages/plugin-ddl-viewer/src/DdlViewer/useDdlEntityQuery.ts @@ -7,29 +7,30 @@ */ import { observable } from 'mobx'; -import { useObservableRef } from '@cloudbeaver/core-blocks'; +import { useObservableRef, useResource } from '@cloudbeaver/core-blocks'; import type { ILoadableState } from '@cloudbeaver/core-utils'; -import { getDefaultQueryGeneratorOptions, SqlGeneratorsResource } from '@cloudbeaver/plugin-sql-generator'; +import { DDL_GENERATOR_ID, getDefaultQueryGeneratorOptions, SqlGeneratorsResource } from '@cloudbeaver/plugin-sql-generator'; import { useService } from '@cloudbeaver/core-di'; - -interface IPayload { - nodeId: string; - generatorId: string | null; - showFullDdl: boolean; -} +import { DDLQueryStateService } from './DDLQueryStateService.js'; interface IState extends ILoadableState { query: string | null; exception: Error | null; promise: Promise | null; key: string | null; - payload: IPayload; + nodeId: string; sqlGeneratorsResource: SqlGeneratorsResource; + ddlGeneratorId: string | null; + ddlViewerService: DDLQueryStateService; getCurrentKey: () => string | null; } -export function useDdlEntityQuery(payload: IPayload): IState { - const sqlGeneratorsResource = useService(SqlGeneratorsResource); +export function useDdlEntityQuery(nodeId: string): IState { + const ddlViewerService = useService(DDLQueryStateService); + const sqlGeneratorsResource = useResource(useDdlEntityQuery, SqlGeneratorsResource, nodeId); + const ddlGenerator = sqlGeneratorsResource.data?.find(generator => generator.id.toLowerCase().includes(DDL_GENERATOR_ID.toLowerCase())); + const ddlGeneratorId = ddlGenerator?.id ?? null; + const state = useObservableRef( () => ({ query: null, @@ -37,7 +38,7 @@ export function useDdlEntityQuery(payload: IPayload): IState { promise: null, key: null, isLoadable() { - return this.payload.generatorId !== null; + return this.ddlGeneratorId !== null; }, isLoaded() { return this.query !== null; @@ -52,26 +53,27 @@ export function useDdlEntityQuery(payload: IPayload): IState { return this.getCurrentKey() !== this.key; }, getCurrentKey() { - const { generatorId, nodeId, showFullDdl } = this.payload; - - if (generatorId === null) { + if (this.ddlGeneratorId === null) { return null; } - return `${nodeId}::${generatorId}::${showFullDdl}`; + const isFullDdl = this.ddlViewerService.isFullDdlEnabled(this.nodeId); + + return `${this.nodeId}::${this.ddlGeneratorId}::${isFullDdl}`; }, async load() { - if (this.payload.generatorId === null) { + if (this.ddlGeneratorId === null) { return; } const key = this.getCurrentKey(); + const isFullDdl = this.ddlViewerService.isFullDdlEnabled(this.nodeId); try { this.exception = null; - this.promise = this.sqlGeneratorsResource.generateEntityQuery(this.payload.generatorId, this.payload.nodeId, { + this.promise = this.sqlGeneratorsResource.generateEntityQuery(this.ddlGeneratorId, this.nodeId, { ...getDefaultQueryGeneratorOptions(), - showFullDdl: this.payload.showFullDdl, + showFullDdl: isFullDdl, }); this.query = await this.promise; this.key = key; @@ -87,9 +89,10 @@ export function useDdlEntityQuery(payload: IPayload): IState { exception: observable.ref, promise: observable.ref, key: observable.ref, - payload: observable.ref, + nodeId: observable.ref, + ddlGeneratorId: observable.ref, }, - { payload, sqlGeneratorsResource }, + { nodeId, ddlGeneratorId, sqlGeneratorsResource: sqlGeneratorsResource.resource, ddlViewerService }, ); return state; diff --git a/webapp/packages/plugin-ddl-viewer/src/ExtendedDDLViewer/ExtendedDDLViewerTabPanel.tsx b/webapp/packages/plugin-ddl-viewer/src/ExtendedDDLViewer/ExtendedDDLViewerTabPanel.tsx index 466ece51a33..08fdde94288 100644 --- a/webapp/packages/plugin-ddl-viewer/src/ExtendedDDLViewer/ExtendedDDLViewerTabPanel.tsx +++ b/webapp/packages/plugin-ddl-viewer/src/ExtendedDDLViewer/ExtendedDDLViewerTabPanel.tsx @@ -22,7 +22,7 @@ import type { NavNodeTransformViewComponent } from '@cloudbeaver/plugin-navigati import { SQLCodeEditor, useSqlDialectExtension } from '@cloudbeaver/plugin-sql-editor-codemirror'; import { DATA_CONTEXT_DDL_VIEWER_NODE } from '../DdlViewer/DATA_CONTEXT_DDL_VIEWER_NODE.js'; -import { DATA_CONTEXT_DDL_VIEWER_VALUE } from '../DdlViewer/DATA_CONTEXT_DDL_VIEWER_VALUE.js'; +import { DATA_CONTEXT_DDL_VIEWER_QUERY } from '../DdlViewer/DATA_CONTEXT_DDL_VIEWER_QUERY.js'; import style from '../DdlViewer/DDLViewerTabPanel.module.css'; import { MENU_DDL_VIEWER_FOOTER } from '../DdlViewer/MENU_DDL_VIEWER_FOOTER.js'; import { ExtendedDDLResource } from './ExtendedDDLResource.js'; @@ -46,7 +46,7 @@ export const ExtendedDDLViewerTabPanel: NavNodeTransformViewComponent = observer useDataContextLink(menu.context, (context, id) => { context.set(DATA_CONTEXT_DDL_VIEWER_NODE, nodeId, id); - context.set(DATA_CONTEXT_DDL_VIEWER_VALUE, extendedDDlData, id); + context.set(DATA_CONTEXT_DDL_VIEWER_QUERY, extendedDDlData, id); }); return ( diff --git a/webapp/packages/plugin-ddl-viewer/src/module.ts b/webapp/packages/plugin-ddl-viewer/src/module.ts index c25c2b4e27c..522e601368f 100644 --- a/webapp/packages/plugin-ddl-viewer/src/module.ts +++ b/webapp/packages/plugin-ddl-viewer/src/module.ts @@ -10,6 +10,7 @@ import { Bootstrap, Dependency, ModuleRegistry, proxy } from '@cloudbeaver/core- import { ExtendedDDLResource } from './ExtendedDDLViewer/ExtendedDDLResource.js'; import { DdlViewerBootstrap } from './DdlViewerBootstrap.js'; import { DDLViewerFooterService } from './DdlViewer/DDLViewerFooterService.js'; +import { DDLQueryStateService } from './DdlViewer/DDLQueryStateService.js'; import { LocaleService } from './LocaleService.js'; export default ModuleRegistry.add({ @@ -21,6 +22,7 @@ export default ModuleRegistry.add({ .addSingleton(Bootstrap, LocaleService) .addSingleton(Dependency, proxy(ExtendedDDLResource)) .addSingleton(DDLViewerFooterService) + .addSingleton(DDLQueryStateService) .addSingleton(ExtendedDDLResource); }, }); From 7e121ed9972e48ced12723e4a768d098652ebd6e Mon Sep 17 00:00:00 2001 From: sergeyteleshev Date: Thu, 6 Aug 2026 13:36:24 +0200 Subject: [PATCH 7/7] dbeaver/pro#4551 removes state & name in favor of onChange for GeneratedSqlDIalog --- .../src/DdlViewer/DDLQueryStateService.ts | 26 ------------------ .../src/DdlViewer/DDLViewerFooterService.ts | 27 +++++++++++++------ .../src/DdlViewer/useDdlEntityQuery.ts | 12 ++++----- .../packages/plugin-ddl-viewer/src/module.ts | 2 -- .../src/SqlGenerators/GeneratedSqlDialog.tsx | 6 ----- 5 files changed, 25 insertions(+), 48 deletions(-) delete mode 100644 webapp/packages/plugin-ddl-viewer/src/DdlViewer/DDLQueryStateService.ts diff --git a/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DDLQueryStateService.ts b/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DDLQueryStateService.ts deleted file mode 100644 index 6278039aa49..00000000000 --- a/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DDLQueryStateService.ts +++ /dev/null @@ -1,26 +0,0 @@ -/* - * CloudBeaver - Cloud Database Manager - * 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. - */ -import { injectable } from '@cloudbeaver/core-di'; -import { MetadataMap } from '@cloudbeaver/core-utils'; - -@injectable() -export class DDLQueryStateService { - private readonly fullDdlState = new MetadataMap(() => false); - - isFullDdlEnabled(nodeId: string): boolean { - return this.fullDdlState.get(nodeId); - } - - setFullDdlEnabled(nodeId: string, value: boolean): void { - this.fullDdlState.set(nodeId, value); - } - - toggleFullDdl(nodeId: string): void { - this.setFullDdlEnabled(nodeId, !this.isFullDdlEnabled(nodeId)); - } -} diff --git a/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DDLViewerFooterService.ts b/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DDLViewerFooterService.ts index 822be7522b3..ad2d2074241 100644 --- a/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DDLViewerFooterService.ts +++ b/webapp/packages/plugin-ddl-viewer/src/DdlViewer/DDLViewerFooterService.ts @@ -8,7 +8,7 @@ import { ConnectionInfoResource, createConnectionParam } from '@cloudbeaver/core-connections'; import { injectable } from '@cloudbeaver/core-di'; import { EObjectFeature, NavNodeInfoResource, NavNodeManagerService } from '@cloudbeaver/core-navigation-tree'; -import { download, withTimestamp } from '@cloudbeaver/core-utils'; +import { download, MetadataMap, withTimestamp } from '@cloudbeaver/core-utils'; import { ACTION_SAVE, ActionService, MenuService } from '@cloudbeaver/core-view'; import { DDL_GENERATOR_ID, SqlGeneratorsResource } from '@cloudbeaver/plugin-sql-generator'; import { LocalStorageSqlDataSource } from '@cloudbeaver/plugin-sql-editor'; @@ -17,7 +17,6 @@ import { ACTION_SQL_EDITOR_OPEN, SqlEditorNavigatorService } from '@cloudbeaver/ import { ACTION_DDL_VIEWER_FULL_DDL } from './ACTION_DDL_VIEWER_FULL_DDL.js'; import { DATA_CONTEXT_DDL_VIEWER_NODE } from './DATA_CONTEXT_DDL_VIEWER_NODE.js'; import { DATA_CONTEXT_DDL_VIEWER_QUERY } from './DATA_CONTEXT_DDL_VIEWER_QUERY.js'; -import { DDLQueryStateService } from './DDLQueryStateService.js'; import { MENU_DDL_VIEWER_FOOTER } from './MENU_DDL_VIEWER_FOOTER.js'; @injectable(() => [ @@ -26,22 +25,34 @@ import { MENU_DDL_VIEWER_FOOTER } from './MENU_DDL_VIEWER_FOOTER.js'; MenuService, SqlEditorNavigatorService, ConnectionInfoResource, - DDLQueryStateService, NavNodeInfoResource, SqlGeneratorsResource, ]) export class DDLViewerFooterService { + private readonly fullDdlState = new MetadataMap(() => false); + constructor( private readonly navNodeManagerService: NavNodeManagerService, private readonly actionsService: ActionService, private readonly menuService: MenuService, private readonly sqlEditorNavigatorService: SqlEditorNavigatorService, private readonly connectionInfoResource: ConnectionInfoResource, - private readonly ddlQueryStateService: DDLQueryStateService, private readonly navNodeInfoResource: NavNodeInfoResource, private readonly sqlGeneratorsResource: SqlGeneratorsResource, ) {} + isFullDdlEnabled(nodeId: string): boolean { + return this.fullDdlState.get(nodeId); + } + + setFullDdlEnabled(nodeId: string, value: boolean): void { + this.fullDdlState.set(nodeId, value); + } + + toggleFullDdl(nodeId: string): void { + this.setFullDdlEnabled(nodeId, !this.isFullDdlEnabled(nodeId)); + } + register(): void { this.actionsService.addHandler({ id: 'ddl-viewer-footer-base-handler', @@ -111,7 +122,7 @@ export class DDLViewerFooterService { this.menuService.addCreator({ menus: [MENU_DDL_VIEWER_FOOTER], contexts: [DATA_CONTEXT_DDL_VIEWER_NODE], - isApplicable: context => this.hasFullDdl(context.get(DATA_CONTEXT_DDL_VIEWER_NODE)!), + isApplicable: context => this.isFullDDLSupported(context.get(DATA_CONTEXT_DDL_VIEWER_NODE)!), getItems: (context, items) => [...items, ACTION_DDL_VIEWER_FULL_DDL], }); @@ -120,16 +131,16 @@ export class DDLViewerFooterService { menus: [MENU_DDL_VIEWER_FOOTER], actions: [ACTION_DDL_VIEWER_FULL_DDL], contexts: [DATA_CONTEXT_DDL_VIEWER_NODE], - isChecked: context => this.ddlQueryStateService.isFullDdlEnabled(context.get(DATA_CONTEXT_DDL_VIEWER_NODE)!), + isChecked: context => this.isFullDdlEnabled(context.get(DATA_CONTEXT_DDL_VIEWER_NODE)!), handler: (context, action) => { if (action === ACTION_DDL_VIEWER_FULL_DDL) { - this.ddlQueryStateService.toggleFullDdl(context.get(DATA_CONTEXT_DDL_VIEWER_NODE)!); + this.toggleFullDdl(context.get(DATA_CONTEXT_DDL_VIEWER_NODE)!); } }, }); } - private hasFullDdl(nodeId: string): boolean { + private isFullDDLSupported(nodeId: string): boolean { const supportsFullDdl = this.navNodeInfoResource.get(nodeId)?.objectFeatures.includes(EObjectFeature.supportsFullDdl) ?? false; const ddlGenerator = this.sqlGeneratorsResource .get(nodeId) diff --git a/webapp/packages/plugin-ddl-viewer/src/DdlViewer/useDdlEntityQuery.ts b/webapp/packages/plugin-ddl-viewer/src/DdlViewer/useDdlEntityQuery.ts index 45d04cd24d9..9c154a63109 100644 --- a/webapp/packages/plugin-ddl-viewer/src/DdlViewer/useDdlEntityQuery.ts +++ b/webapp/packages/plugin-ddl-viewer/src/DdlViewer/useDdlEntityQuery.ts @@ -11,7 +11,7 @@ import { useObservableRef, useResource } from '@cloudbeaver/core-blocks'; import type { ILoadableState } from '@cloudbeaver/core-utils'; import { DDL_GENERATOR_ID, getDefaultQueryGeneratorOptions, SqlGeneratorsResource } from '@cloudbeaver/plugin-sql-generator'; import { useService } from '@cloudbeaver/core-di'; -import { DDLQueryStateService } from './DDLQueryStateService.js'; +import { DDLViewerFooterService } from './DDLViewerFooterService.js'; interface IState extends ILoadableState { query: string | null; @@ -21,12 +21,12 @@ interface IState extends ILoadableState { nodeId: string; sqlGeneratorsResource: SqlGeneratorsResource; ddlGeneratorId: string | null; - ddlViewerService: DDLQueryStateService; + ddlViewerFooterService: DDLViewerFooterService; getCurrentKey: () => string | null; } export function useDdlEntityQuery(nodeId: string): IState { - const ddlViewerService = useService(DDLQueryStateService); + const ddlViewerFooterService = useService(DDLViewerFooterService); const sqlGeneratorsResource = useResource(useDdlEntityQuery, SqlGeneratorsResource, nodeId); const ddlGenerator = sqlGeneratorsResource.data?.find(generator => generator.id.toLowerCase().includes(DDL_GENERATOR_ID.toLowerCase())); const ddlGeneratorId = ddlGenerator?.id ?? null; @@ -57,7 +57,7 @@ export function useDdlEntityQuery(nodeId: string): IState { return null; } - const isFullDdl = this.ddlViewerService.isFullDdlEnabled(this.nodeId); + const isFullDdl = this.ddlViewerFooterService.isFullDdlEnabled(this.nodeId); return `${this.nodeId}::${this.ddlGeneratorId}::${isFullDdl}`; }, @@ -67,7 +67,7 @@ export function useDdlEntityQuery(nodeId: string): IState { } const key = this.getCurrentKey(); - const isFullDdl = this.ddlViewerService.isFullDdlEnabled(this.nodeId); + const isFullDdl = this.ddlViewerFooterService.isFullDdlEnabled(this.nodeId); try { this.exception = null; @@ -92,7 +92,7 @@ export function useDdlEntityQuery(nodeId: string): IState { nodeId: observable.ref, ddlGeneratorId: observable.ref, }, - { nodeId, ddlGeneratorId, sqlGeneratorsResource: sqlGeneratorsResource.resource, ddlViewerService }, + { nodeId, ddlGeneratorId, sqlGeneratorsResource: sqlGeneratorsResource.resource, ddlViewerFooterService }, ); return state; diff --git a/webapp/packages/plugin-ddl-viewer/src/module.ts b/webapp/packages/plugin-ddl-viewer/src/module.ts index 522e601368f..c25c2b4e27c 100644 --- a/webapp/packages/plugin-ddl-viewer/src/module.ts +++ b/webapp/packages/plugin-ddl-viewer/src/module.ts @@ -10,7 +10,6 @@ import { Bootstrap, Dependency, ModuleRegistry, proxy } from '@cloudbeaver/core- import { ExtendedDDLResource } from './ExtendedDDLViewer/ExtendedDDLResource.js'; import { DdlViewerBootstrap } from './DdlViewerBootstrap.js'; import { DDLViewerFooterService } from './DdlViewer/DDLViewerFooterService.js'; -import { DDLQueryStateService } from './DdlViewer/DDLQueryStateService.js'; import { LocaleService } from './LocaleService.js'; export default ModuleRegistry.add({ @@ -22,7 +21,6 @@ export default ModuleRegistry.add({ .addSingleton(Bootstrap, LocaleService) .addSingleton(Dependency, proxy(ExtendedDDLResource)) .addSingleton(DDLViewerFooterService) - .addSingleton(DDLQueryStateService) .addSingleton(ExtendedDDLResource); }, }); diff --git a/webapp/packages/plugin-sql-generator/src/SqlGenerators/GeneratedSqlDialog.tsx b/webapp/packages/plugin-sql-generator/src/SqlGenerators/GeneratedSqlDialog.tsx index c6889c2f70b..a5d2ca222ac 100644 --- a/webapp/packages/plugin-sql-generator/src/SqlGenerators/GeneratedSqlDialog.tsx +++ b/webapp/packages/plugin-sql-generator/src/SqlGenerators/GeneratedSqlDialog.tsx @@ -144,16 +144,12 @@ export const GeneratedSqlDialog = observer>(functi
state.handleOptionChange('useFullyQualifiedNames', value)} /> state.handleOptionChange('compactSql', value)} @@ -161,8 +157,6 @@ export const GeneratedSqlDialog = observer>(functi {hasFullDdl && ( state.handleOptionChange('showFullDdl', value)}