diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/DBWConstants.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/DBWConstants.java index 7f854034b3..bc512b6f83 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/DBWConstants.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/DBWConstants.java @@ -29,6 +29,8 @@ public interface DBWConstants { String PERMISSION_CONFIGURATION_MANAGER = RMConstants.PERMISSION_CONFIGURATION_MANAGER; String PERMISSION_PRIVATE_PROJECT_ACCESS = "private-project-access"; String PERMISSION_SECRET_MANAGER = "secret-manager"; + String PERMISSION_SQL_RESULT_UPDATE = "sql-result-update"; + String PERMISSION_SQL_EXECUTE_QUERY = "sql-execute-query"; String GLOBAL_PERMISSION_SCRIPT_EXECUTE = "permission.sql.script.execution"; diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/WebServerConfig.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/WebServerConfig.java index 3a0e570312..72aceef401 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/WebServerConfig.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/WebServerConfig.java @@ -136,7 +136,7 @@ public class WebServerConfig { @Property public WebServerLanguage[] getSupportedLanguages() { - List langs = PlatformLanguageRegistry.getInstance().getLanguages(); + List langs = new ArrayList<>(PlatformLanguageRegistry.getInstance().getLanguages()); // FIXME: remove hardcoded ordering langs.sort(Comparator.comparingInt(x -> x.getCode().equals("vi") ? 1 : -1)); WebServerLanguage[] webLangs = new WebServerLanguage[langs.size()]; diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/DBWServiceSQL.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/DBWServiceSQL.java index 9b298c2f26..02dd7215c1 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/DBWServiceSQL.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/DBWServiceSQL.java @@ -19,6 +19,7 @@ package io.cloudbeaver.service.sql; import io.cloudbeaver.DBWConstants; import io.cloudbeaver.DBWebException; import io.cloudbeaver.WebAction; +import io.cloudbeaver.WebObjectId; import io.cloudbeaver.model.WebAsyncTaskInfo; import io.cloudbeaver.model.WebConnectionInfo; import io.cloudbeaver.model.WebTransactionLogInfo; @@ -93,13 +94,15 @@ public interface DBWServiceSQL extends DBWService { @WebAction(requireGlobalPermissions = DBWConstants.GLOBAL_PERMISSION_SCRIPT_EXECUTE) WebAsyncTaskInfo asyncExecuteQuery( + @NotNull WebSession webSession, + @WebObjectId @NotNull String projectId, @NotNull WebSQLContextInfo contextInfo, @NotNull String sql, @Nullable String resultId, @Nullable WebSQLDataFilter filter, @Nullable WebDataFormat dataFormat, - boolean readLogs, - @NotNull WebSession webSession) throws DBException; + boolean readLogs + ) throws DBException; @WebAction WebAsyncTaskInfo asyncReadDataFromContainer( diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLUtils.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLUtils.java index a2d48d4559..c53143bf25 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLUtils.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLUtils.java @@ -16,12 +16,15 @@ */ package io.cloudbeaver.service.sql; +import io.cloudbeaver.model.WebAsyncTaskInfo; import io.cloudbeaver.model.app.ServletAppConfiguration; +import io.cloudbeaver.model.session.WebAsyncTaskProcessor; import io.cloudbeaver.model.session.WebSession; import io.cloudbeaver.registry.WebServiceRegistry; import io.cloudbeaver.utils.CBModelConstants; import io.cloudbeaver.utils.ServletAppUtils; import org.jkiss.code.NotNull; +import org.jkiss.code.Nullable; import org.jkiss.dbeaver.Log; import org.jkiss.dbeaver.model.DBPEvaluationContext; import org.jkiss.dbeaver.model.data.*; @@ -30,6 +33,7 @@ import org.jkiss.dbeaver.model.exec.DBCSession; import org.jkiss.dbeaver.model.gis.DBGeometry; import org.jkiss.dbeaver.model.gis.GisConstants; import org.jkiss.dbeaver.model.gis.GisTransformUtils; +import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor; import org.jkiss.dbeaver.model.struct.DBSAttributeBase; import org.jkiss.dbeaver.model.struct.DBSTypedObject; import org.jkiss.dbeaver.utils.ContentUtils; @@ -38,6 +42,7 @@ import org.jkiss.utils.Base64; import org.jkiss.utils.CommonUtils; import java.io.StringWriter; +import java.lang.reflect.InvocationTargetException; import java.math.BigDecimal; import java.util.*; @@ -263,4 +268,34 @@ public class WebSQLUtils { public static String getColumnName(@NotNull DBDAttributeBinding binding) { return binding.getFullyQualifiedName(DBPEvaluationContext.UI); } + + @NotNull + public static WebAsyncTaskInfo createAsyncTaskExecuteSqlQuery( + @NotNull WebSession webSession, + @NotNull WebSQLContextInfo contextInfo, + @NotNull String sql, + @Nullable String resultId, + @Nullable WebSQLDataFilter filter, + @Nullable WebDataFormat dataFormat, + boolean readLogs + ) { + WebAsyncTaskProcessor runnable = new WebAsyncTaskProcessor<>() { + @Override + public void run(DBRProgressMonitor monitor) throws InvocationTargetException { + try { + monitor.beginTask("Execute query", 1); + monitor.subTask("Process query " + sql); + WebSQLExecuteInfo executeResults = contextInfo.getProcessor().processQuery( + monitor, contextInfo, sql, resultId, filter, dataFormat, webSession, readLogs); + this.result = executeResults.getStatusMessage(); + this.extendedResults = executeResults; + } catch (Throwable e) { + throw new InvocationTargetException(e); + } finally { + monitor.done(); + } + } + }; + return webSession.createAndRunAsyncTask("SQL execute", runnable); + } } diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebServiceBindingSQL.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebServiceBindingSQL.java index 0000684942..ff79e1cb0f 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebServiceBindingSQL.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebServiceBindingSQL.java @@ -184,13 +184,16 @@ public class WebServiceBindingSQL extends WebServiceBindingBase .dataFetcher("asyncSqlExecuteQuery", env -> getService(env).asyncExecuteQuery( + getWebSession(env), + getProjectReference(env), getSQLContext(env), env.getArgument("sql"), env.getArgument("resultId"), getDataFilter(env), getDataFormat(env), - CommonUtils.toBoolean(env.getArgument("readLogs")), - getWebSession(env))) + CommonUtils.toBoolean(env.getArgument("readLogs")) + ) + ) .dataFetcher("asyncReadDataFromContainer", env -> getService(env).asyncReadDataFromContainer( getSQLContext(env), @@ -248,7 +251,7 @@ public class WebServiceBindingSQL extends WebServiceBindingBase } @NotNull - private WebDataFormat getDataFormat(DataFetchingEnvironment env) { + public static WebDataFormat getDataFormat(DataFetchingEnvironment env) { String dataFormat = env.getArgument("dataFormat"); return CommonUtils.valueOf(WebDataFormat.class, dataFormat, WebDataFormat.resultset); } @@ -370,8 +373,7 @@ public class WebServiceBindingSQL extends WebServiceBindingBase /////////////////////////////////////// // Helpers - - private static WebSQLDataFilter getDataFilter(DataFetchingEnvironment env) { + public static WebSQLDataFilter getDataFilter(DataFetchingEnvironment env) { Map filterProps = env.getArgument("filter"); return filterProps == null ? null : new WebSQLDataFilter(filterProps); } diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/impl/WebServiceSQL.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/impl/WebServiceSQL.java index 1bd77903d2..20c183203d 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/impl/WebServiceSQL.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/impl/WebServiceSQL.java @@ -17,6 +17,7 @@ package io.cloudbeaver.service.sql.impl; +import io.cloudbeaver.DBWConstants; import io.cloudbeaver.DBWebException; import io.cloudbeaver.model.WebAsyncTaskInfo; import io.cloudbeaver.model.WebConnectionInfo; @@ -58,6 +59,7 @@ import org.jkiss.dbeaver.model.sql.semantics.completion.SQLQueryCompletionAnalyz import org.jkiss.dbeaver.model.struct.DBSDataContainer; import org.jkiss.dbeaver.model.struct.DBSObject; import org.jkiss.dbeaver.model.struct.DBSWrapper; +import org.jkiss.dbeaver.runtime.DBWorkbench; import org.jkiss.dbeaver.utils.RuntimeUtils; import org.jkiss.utils.CommonUtils; @@ -321,6 +323,9 @@ public class WebServiceSQL implements DBWServiceSQL { @Nullable List addedRows, @Nullable WebDataFormat dataFormat ) throws DBWebException { + if (DBWorkbench.isDistributed() && !webSession.hasPermission(DBWConstants.PERMISSION_SQL_RESULT_UPDATE)) { + throw new DBWebException("Permission denied"); + } WebAsyncTaskProcessor runnable = new WebAsyncTaskProcessor<>() { @Override public void run(DBRProgressMonitor monitor) throws InvocationTargetException { @@ -357,6 +362,9 @@ public class WebServiceSQL implements DBWServiceSQL { @Nullable List addedRows, @Nullable WebDataFormat dataFormat ) throws DBWebException { + if (DBWorkbench.isDistributed() && !contextInfo.getWebSession().hasPermission(DBWConstants.PERMISSION_SQL_RESULT_UPDATE)) { + throw new DBWebException("Permission denied"); + } try { return updateResultsDataBatch( contextInfo.getWebSession().getProgressMonitor(), @@ -469,33 +477,29 @@ public class WebServiceSQL implements DBWServiceSQL { } @NotNull + @Override public WebAsyncTaskInfo asyncExecuteQuery( + @NotNull WebSession webSession, + @NotNull String projectId, @NotNull WebSQLContextInfo contextInfo, @NotNull String sql, @Nullable String resultId, @Nullable WebSQLDataFilter filter, @Nullable WebDataFormat dataFormat, - boolean readLogs, - @NotNull WebSession webSession) - { - WebAsyncTaskProcessor runnable = new WebAsyncTaskProcessor<>() { - @Override - public void run(DBRProgressMonitor monitor) throws InvocationTargetException { - try { - monitor.beginTask("Execute query", 1); - monitor.subTask("Process query " + sql); - WebSQLExecuteInfo executeResults = contextInfo.getProcessor().processQuery( - monitor, contextInfo, sql, resultId, filter, dataFormat, webSession, readLogs); - this.result = executeResults.getStatusMessage(); - this.extendedResults = executeResults; - } catch (Throwable e) { - throw new InvocationTargetException(e); - } finally { - monitor.done(); - } - } - }; - return contextInfo.getProcessor().getWebSession().createAndRunAsyncTask("SQL execute", runnable); + boolean readLogs + ) throws DBException { + if (DBWorkbench.isDistributed() && !webSession.hasPermission(DBWConstants.PERMISSION_SQL_EXECUTE_QUERY)) { + throw new DBWebException("Permission denied"); + } + return WebSQLUtils.createAsyncTaskExecuteSqlQuery( + webSession, + contextInfo, + sql, + resultId, + filter, + dataFormat, + readLogs + ); } @Override diff --git a/webapp/packages/plugin-sql-editor-screen/package.json b/webapp/packages/plugin-sql-editor-screen/package.json index 4146af7b77..588394c6f1 100644 --- a/webapp/packages/plugin-sql-editor-screen/package.json +++ b/webapp/packages/plugin-sql-editor-screen/package.json @@ -26,7 +26,9 @@ "@cloudbeaver/core-events": "workspace:*", "@cloudbeaver/core-localization": "workspace:*", "@cloudbeaver/core-resource": "workspace:*", + "@cloudbeaver/core-root": "workspace:*", "@cloudbeaver/core-routing": "workspace:*", + "@cloudbeaver/core-settings": "workspace:*", "@cloudbeaver/core-ui": "workspace:*", "@cloudbeaver/core-utils": "workspace:*", "@cloudbeaver/core-view": "workspace:*", diff --git a/webapp/packages/plugin-sql-editor-screen/src/PluginBootstrap.ts b/webapp/packages/plugin-sql-editor-screen/src/PluginBootstrap.ts index 0a4ff6c443..0eb6a02697 100644 --- a/webapp/packages/plugin-sql-editor-screen/src/PluginBootstrap.ts +++ b/webapp/packages/plugin-sql-editor-screen/src/PluginBootstrap.ts @@ -1,6 +1,6 @@ /* * CloudBeaver - Cloud Database Manager - * Copyright (C) 2020-2024 DBeaver Corp and others + * Copyright (C) 2020-2025 DBeaver Corp and others * * Licensed under the Apache License, Version 2.0. * you may not use this file except in compliance with the License. @@ -10,10 +10,11 @@ import { Bootstrap, injectable } from '@cloudbeaver/core-di'; import { NotificationService } from '@cloudbeaver/core-events'; import { MENU_TAB } from '@cloudbeaver/core-ui'; import { ACTION_OPEN_IN_TAB, ActionService, type IAction, KEY_BINDING_OPEN_IN_TAB, KeyBindingService, MenuService } from '@cloudbeaver/core-view'; -import { DATA_CONTEXT_SQL_EDITOR_STATE, SqlDataSourceService } from '@cloudbeaver/plugin-sql-editor'; +import { DATA_CONTEXT_SQL_EDITOR_STATE, ESqlDataSourceFeatures, SqlDataSourceService } from '@cloudbeaver/plugin-sql-editor'; import { DATA_CONTEXT_SQL_EDITOR_TAB } from '@cloudbeaver/plugin-sql-editor-navigation-tab'; import { SqlEditorScreenService } from './Screen/SqlEditorScreenService.js'; +import { SqlEditorScreenSettingsService } from './SqlEditorScreenSettingsService.js'; @injectable() export class PluginBootstrap extends Bootstrap { @@ -24,6 +25,7 @@ export class PluginBootstrap extends Bootstrap { private readonly notificationService: NotificationService, private readonly menuService: MenuService, private readonly sqlDataSourceService: SqlDataSourceService, + private readonly sqlEditorScreenSettingsService: SqlEditorScreenSettingsService, ) { super(); } @@ -33,6 +35,13 @@ export class PluginBootstrap extends Bootstrap { id: 'sql-editor-screen', actions: [ACTION_OPEN_IN_TAB], contexts: [DATA_CONTEXT_SQL_EDITOR_STATE], + isActionApplicable: context => { + const state = context.get(DATA_CONTEXT_SQL_EDITOR_STATE)!; + + const dataSource = this.sqlDataSourceService.get(state.editorId); + + return !!dataSource?.hasFeature(ESqlDataSourceFeatures.script) && this.sqlEditorScreenSettingsService.enabled; + }, isDisabled: context => { const state = context.get(DATA_CONTEXT_SQL_EDITOR_STATE)!; @@ -47,6 +56,13 @@ export class PluginBootstrap extends Bootstrap { binding: KEY_BINDING_OPEN_IN_TAB, actions: [ACTION_OPEN_IN_TAB], contexts: [DATA_CONTEXT_SQL_EDITOR_STATE], + isBindingApplicable: context => { + const state = context.get(DATA_CONTEXT_SQL_EDITOR_STATE)!; + + const dataSource = this.sqlDataSourceService.get(state.editorId); + + return !!dataSource?.hasFeature(ESqlDataSourceFeatures.script) && this.sqlEditorScreenSettingsService.enabled; + }, handler: this.openTab.bind(this), }); diff --git a/webapp/packages/plugin-sql-editor-screen/src/Screen/SqlEditorScreenService.ts b/webapp/packages/plugin-sql-editor-screen/src/Screen/SqlEditorScreenService.ts index 3663a555bb..4084c972a1 100644 --- a/webapp/packages/plugin-sql-editor-screen/src/Screen/SqlEditorScreenService.ts +++ b/webapp/packages/plugin-sql-editor-screen/src/Screen/SqlEditorScreenService.ts @@ -1,6 +1,6 @@ /* * CloudBeaver - Cloud Database Manager - * Copyright (C) 2020-2024 DBeaver Corp and others + * Copyright (C) 2020-2025 DBeaver Corp and others * * Licensed under the Apache License, Version 2.0. * you may not use this file except in compliance with the License. @@ -10,15 +10,22 @@ import { type IScreen, ScreenService } from '@cloudbeaver/core-routing'; import type { ISqlEditorScreenParams } from './ISqlEditorScreenParams.js'; import { SqlEditorScreen } from './SqlEditorScreen.js'; +import { SqlEditorScreenSettingsService } from '../SqlEditorScreenSettingsService.js'; @injectable() export class SqlEditorScreenService { readonly screen: IScreen; - constructor(private readonly screenService: ScreenService) { + constructor( + private readonly screenService: ScreenService, + sqlEditorScreenSettingsService: SqlEditorScreenSettingsService, + ) { this.screen = { name: 'sql-editor', routes: [{ name: 'sql-editor', path: '/sql-editor/:contextId' }], component: SqlEditorScreen, + canDeActivate() { + return sqlEditorScreenSettingsService.enabled; + }, }; } diff --git a/webapp/packages/plugin-sql-editor-screen/src/SqlEditorScreenSettingsService.ts b/webapp/packages/plugin-sql-editor-screen/src/SqlEditorScreenSettingsService.ts new file mode 100644 index 0000000000..7eda871157 --- /dev/null +++ b/webapp/packages/plugin-sql-editor-screen/src/SqlEditorScreenSettingsService.ts @@ -0,0 +1,44 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2025 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, injectable } from '@cloudbeaver/core-di'; +import { HIGHEST_SETTINGS_LAYER } from '@cloudbeaver/core-root'; +import { createSettingsOverrideResolver, SettingsProvider, SettingsProviderService, SettingsResolverService } from '@cloudbeaver/core-settings'; +import { schema, schemaExtra } from '@cloudbeaver/core-utils'; + +const defaultSettings = schema.object({ + 'plugin.sql-editor-screen.enabled': schemaExtra.stringedBoolean().default(true), +}); + +type SqlEditorScreenSettingsSchema = typeof defaultSettings; +export type SqlEditorSettings = schema.infer; + +@injectable() +export class SqlEditorScreenSettingsService extends Dependency { + get enabled(): boolean { + return this.settings.getValue('plugin.sql-editor-screen.enabled'); + } + + readonly settings: SettingsProvider; + + constructor( + private readonly settingsProviderService: SettingsProviderService, + private readonly settingsResolverService: SettingsResolverService, + ) { + super(); + this.settings = this.settingsProviderService.createSettings(defaultSettings); + this.settingsResolverService.addResolver( + HIGHEST_SETTINGS_LAYER, + createSettingsOverrideResolver(this.settingsResolverService, { + 'plugin.sql-editor-screen.enabled': { + key: 'permission.sql.script.execution', + filter: value => !value, + }, + }), + ); + } +} diff --git a/webapp/packages/plugin-sql-editor-screen/src/manifest.ts b/webapp/packages/plugin-sql-editor-screen/src/manifest.ts index c5bb2e82d3..bcb8d60825 100644 --- a/webapp/packages/plugin-sql-editor-screen/src/manifest.ts +++ b/webapp/packages/plugin-sql-editor-screen/src/manifest.ts @@ -1,6 +1,6 @@ /* * CloudBeaver - Cloud Database Manager - * Copyright (C) 2020-2024 DBeaver Corp and others + * Copyright (C) 2020-2025 DBeaver Corp and others * * Licensed under the Apache License, Version 2.0. * you may not use this file except in compliance with the License. @@ -17,5 +17,6 @@ export const sqlEditorPagePluginManifest: PluginManifest = { () => import('./LocaleService.js').then(m => m.LocaleService), () => import('./Screen/SqlEditorScreenBootstrap.js').then(m => m.SqlEditorScreenBootstrap), () => import('./Screen/SqlEditorScreenService.js').then(m => m.SqlEditorScreenService), + () => import('./SqlEditorScreenSettingsService.js').then(m => m.SqlEditorScreenSettingsService), ], }; diff --git a/webapp/packages/plugin-sql-editor-screen/tsconfig.json b/webapp/packages/plugin-sql-editor-screen/tsconfig.json index 2e41a54885..5a13c9795f 100644 --- a/webapp/packages/plugin-sql-editor-screen/tsconfig.json +++ b/webapp/packages/plugin-sql-editor-screen/tsconfig.json @@ -31,9 +31,15 @@ { "path": "../core-resource" }, + { + "path": "../core-root" + }, { "path": "../core-routing" }, + { + "path": "../core-settings" + }, { "path": "../core-ui" }, diff --git a/webapp/packages/plugin-sql-editor/src/QueryDataSource.ts b/webapp/packages/plugin-sql-editor/src/QueryDataSource.ts index 9e0d49a0e8..4cda3fa0b0 100644 --- a/webapp/packages/plugin-sql-editor/src/QueryDataSource.ts +++ b/webapp/packages/plugin-sql-editor/src/QueryDataSource.ts @@ -17,6 +17,7 @@ import { type SqlExecuteInfo, type SqlQueryResults, type AsyncUpdateResultsDataBatchMutationVariables, + type AsyncTaskInfo, } from '@cloudbeaver/core-sdk'; import { uuid } from '@cloudbeaver/core-utils'; import { @@ -190,25 +191,7 @@ export class QueryDataSource { - const { taskInfo } = await this.graphQLService.sdk.asyncSqlExecuteQuery({ - projectId: executionContextInfo.projectId, - connectionId: executionContextInfo.connectionId, - contextId: executionContextInfo.id, - query: options.query, - resultId: firstResultId, - filter: { - offset: this.offset, - limit, - constraints: options.constraints, - where: options.whereFilter || undefined, - }, - dataFormat: this.dataFormat, - readLogs: options.readLogs, - }); - - return taskInfo; - }); + const task = this.asyncTaskInfoService.create(() => this.executeQuery(executionContextInfo, options, firstResultId, limit)); this.currentTask = executionContext.run( async () => { @@ -238,6 +221,31 @@ export class QueryDataSource { + const { taskInfo } = await this.graphQLService.sdk.asyncSqlExecuteQuery({ + projectId: executionContextInfo.projectId, + connectionId: executionContextInfo.connectionId, + contextId: executionContextInfo.id, + query: options.query, + resultId: firstResultId, + filter: { + offset: this.offset, + limit, + constraints: options.constraints, + where: options.whereFilter || undefined, + }, + dataFormat: this.dataFormat, + readLogs: options.readLogs, + }); + + return taskInfo; + } + private innerGetResults( executionContextInfo: IConnectionExecutionContextInfo, response: SqlExecuteInfo, diff --git a/webapp/packages/plugin-sql-editor/src/SqlDataSource/BaseSqlDataSource.ts b/webapp/packages/plugin-sql-editor/src/SqlDataSource/BaseSqlDataSource.ts index 0cbb1025f1..28447d6931 100644 --- a/webapp/packages/plugin-sql-editor/src/SqlDataSource/BaseSqlDataSource.ts +++ b/webapp/packages/plugin-sql-editor/src/SqlDataSource/BaseSqlDataSource.ts @@ -21,7 +21,7 @@ import { SqlDataSourceHistory } from './SqlDataSourceHistory/SqlDataSourceHistor const SOURCE_HISTORY = 'history'; @staticImplements() -export abstract class BaseSqlDataSource implements ISqlDataSource { +export abstract class BaseSqlDataSource implements ISqlDataSource { static key = 'base'; abstract get name(): string | null; @@ -32,7 +32,7 @@ export abstract class BaseSqlDataSource implements ISqlDataSource { abstract get baseExecutionContext(): IConnectionExecutionContextInfo | undefined; abstract get executionContext(): IConnectionExecutionContextInfo | undefined; - databaseModels: IDatabaseDataModel[]; + databaseModels: IDatabaseDataModel[]; incomingScript: string | undefined; incomingExecutionContext: IConnectionExecutionContextInfo | undefined | null; exception?: Error | Error[] | null | undefined; @@ -77,7 +77,7 @@ export abstract class BaseSqlDataSource implements ISqlDataSource { readonly history: ISqlDataSourceHistory; readonly onUpdate: ISyncExecutor; readonly onSetScript: ISyncExecutor; - readonly onDatabaseModelUpdate: ISyncExecutor[]>; + readonly onDatabaseModelUpdate: ISyncExecutor[]>; protected get features(): ESqlDataSourceFeatures[] { return [ESqlDataSourceFeatures.script, ESqlDataSourceFeatures.query, ESqlDataSourceFeatures.executable]; diff --git a/webapp/packages/plugin-sql-editor/src/SqlDataSource/ISqlDataSource.ts b/webapp/packages/plugin-sql-editor/src/SqlDataSource/ISqlDataSource.ts index a61c03b216..36fd6f0987 100644 --- a/webapp/packages/plugin-sql-editor/src/SqlDataSource/ISqlDataSource.ts +++ b/webapp/packages/plugin-sql-editor/src/SqlDataSource/ISqlDataSource.ts @@ -29,7 +29,7 @@ export interface ISqlEditorCursor { readonly head: number; } -export interface ISqlDataSource extends ILoadableState { +export interface ISqlDataSource extends ILoadableState { readonly name: string | null; readonly icon?: string; readonly emptyPlaceholder?: string; @@ -43,7 +43,7 @@ export interface ISqlDataSource extends ILoadableState { readonly incomingScript?: string; readonly history: ISqlDataSourceHistory; - readonly databaseModels: IDatabaseDataModel[]; + readonly databaseModels: IDatabaseDataModel[]; readonly executionContext?: IConnectionExecutionContextInfo; readonly isAutoSaveEnabled: boolean; @@ -54,7 +54,7 @@ export interface ISqlDataSource extends ILoadableState { readonly onUpdate: ISyncExecutor; readonly onSetScript: ISyncExecutor; - readonly onDatabaseModelUpdate: ISyncExecutor[]>; + readonly onDatabaseModelUpdate: ISyncExecutor[]>; isOpened(): boolean; isReadonly(): boolean; diff --git a/webapp/packages/plugin-sql-editor/src/SqlDataSource/SqlDataSourceService.ts b/webapp/packages/plugin-sql-editor/src/SqlDataSource/SqlDataSourceService.ts index 11ec5bc30e..0d772ce0f4 100644 --- a/webapp/packages/plugin-sql-editor/src/SqlDataSource/SqlDataSourceService.ts +++ b/webapp/packages/plugin-sql-editor/src/SqlDataSource/SqlDataSourceService.ts @@ -14,6 +14,7 @@ import { type ISyncExecutor, SyncExecutor } from '@cloudbeaver/core-executor'; import type { ISqlEditorTabState } from '../ISqlEditorTabState.js'; import type { ISqlDataSource } from './ISqlDataSource.js'; import { MemorySqlDataSource } from './MemorySqlDataSource.js'; +import type { QueryDataSource } from '../QueryDataSource.js'; export interface ISqlDataSourceOptions { name?: string; @@ -22,18 +23,18 @@ export interface ISqlDataSourceOptions { dataSourceState?: Record; } -type ISqlDataSourceFactory = (editorId: string, options?: ISqlDataSourceOptions) => ISqlDataSource; +type ISqlDataSourceFactory = (editorId: string, options?: ISqlDataSourceOptions) => ISqlDataSource; -interface ISqlDataSourceFabric { +interface ISqlDataSourceFabric { key: string; - getDataSource: ISqlDataSourceFactory; - onDestroy?: (dataSource: ISqlDataSource, editorId: string) => Promise | void; - onUnload?: (dataSource: ISqlDataSource, editorId: string) => Promise | void; - canDestroy?: (dataSource: ISqlDataSource, editorId: string) => Promise | boolean; + getDataSource: ISqlDataSourceFactory; + onDestroy?: (dataSource: ISqlDataSource, editorId: string) => Promise | void; + onUnload?: (dataSource: ISqlDataSource, editorId: string) => Promise | void; + canDestroy?: (dataSource: ISqlDataSource, editorId: string) => Promise | boolean; } interface ISqlDataSourceProvider { - provider: ISqlDataSourceFabric; + provider: ISqlDataSourceFabric; dataSource: ISqlDataSource; isActionActive: boolean; } @@ -51,7 +52,7 @@ export class SqlDataSourceService { readonly onCreate: ISyncExecutor<[string, string]>; readonly onUpdate: ISyncExecutor; - private readonly dataSourceProviders: Map; + private readonly dataSourceProviders: Map>; private readonly providers: Map; constructor() { @@ -77,11 +78,11 @@ export class SqlDataSourceService { }); } - get(editorId: string): ISqlDataSource | undefined { - return this.providers.get(editorId)?.dataSource; + get(editorId: string): ISqlDataSource | undefined { + return this.providers.get(editorId)?.dataSource as ISqlDataSource | undefined; } - create(state: ISqlEditorTabState, key: string, options?: ISqlDataSourceOptions): ISqlDataSource { + create(state: ISqlEditorTabState, key: string, options?: ISqlDataSourceOptions): ISqlDataSource { const editorId = state.editorId; const provider = this.dataSourceProviders.get(key); @@ -111,7 +112,7 @@ export class SqlDataSourceService { this.onCreate.execute([editorId, key]); } - return activeProvider.dataSource; + return activeProvider.dataSource as unknown as ISqlDataSource; } async executeAction(editorId: string, action: (dataSource: ISqlDataSource) => Promise | T, notFound: () => void): Promise { @@ -166,7 +167,7 @@ export class SqlDataSourceService { this.providers.delete(editorId); } - register(dataSourceOptions: ISqlDataSourceFabric) { + register(dataSourceOptions: ISqlDataSourceFabric) { if (this.dataSourceProviders.has(dataSourceOptions.key)) { throw new Error(`SQL Data Source with key (${dataSourceOptions.key}) already registered`); } diff --git a/webapp/packages/plugin-sql-editor/src/getSqlEditorName.ts b/webapp/packages/plugin-sql-editor/src/getSqlEditorName.ts index cb6227661f..e014680b7c 100644 --- a/webapp/packages/plugin-sql-editor/src/getSqlEditorName.ts +++ b/webapp/packages/plugin-sql-editor/src/getSqlEditorName.ts @@ -1,6 +1,6 @@ /* * CloudBeaver - Cloud Database Manager - * Copyright (C) 2020-2024 DBeaver Corp and others + * Copyright (C) 2020-2025 DBeaver Corp and others * * Licensed under the Apache License, Version 2.0. * you may not use this file except in compliance with the License. @@ -10,7 +10,7 @@ import type { Connection } from '@cloudbeaver/core-connections'; import type { ISqlEditorTabState } from './ISqlEditorTabState.js'; import type { ISqlDataSource } from './SqlDataSource/ISqlDataSource.js'; -export function getSqlEditorName(state: ISqlEditorTabState, dataSource?: ISqlDataSource, connection?: Connection): string { +export function getSqlEditorName(state: ISqlEditorTabState, dataSource?: ISqlDataSource, connection?: Connection): string { if (dataSource?.name) { return dataSource.name; } diff --git a/webapp/yarn.lock b/webapp/yarn.lock index e43fabede6..c6ed8ab689 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -3791,7 +3791,9 @@ __metadata: "@cloudbeaver/core-events": "workspace:*" "@cloudbeaver/core-localization": "workspace:*" "@cloudbeaver/core-resource": "workspace:*" + "@cloudbeaver/core-root": "workspace:*" "@cloudbeaver/core-routing": "workspace:*" + "@cloudbeaver/core-settings": "workspace:*" "@cloudbeaver/core-ui": "workspace:*" "@cloudbeaver/core-utils": "workspace:*" "@cloudbeaver/core-view": "workspace:*"