/
vshmidt
/
cloudbeaver
Обзор
Документация
Войти
/
vshmidt
/
cloudbeaver
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
devel
webapp/packages/plugin-sql-editor-navigation-tab-script/src/PluginBootstrap.ts
341 строка
13 KB
alex
dbeaver/pro#7527 persist right panel tabs (#3951)
10 дек 2025, 12:13
Не верифицирован
10 дек 2025, 12:13
a39af0f
Код
Авторство
О чём код?
/* * 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 type { IDataContextProvider } from '@cloudbeaver/core-data-context'; import { Bootstrap, injectable } from '@cloudbeaver/core-di'; import { CommonDialogService, DialogueStateResult } from '@cloudbeaver/core-dialogs'; import { NotificationService } from '@cloudbeaver/core-events'; import type { IExecutionContextProvider } from '@cloudbeaver/core-executor'; import { type INodeNavigationData, NavNodeInfoResource, NavNodeManagerService } from '@cloudbeaver/core-navigation-tree'; import { isResourceOfType, ProjectInfoResource, ProjectsService } from '@cloudbeaver/core-projects'; import { CachedMapAllKey, CachedTreeChildrenKey } from '@cloudbeaver/core-resource'; import { getRmResourcePath, NAV_NODE_TYPE_RM_RESOURCE, ResourceManagerResource, RESOURCES_NODE_PATH } from '@cloudbeaver/core-resource-manager'; import { createPath, getPathName } from '@cloudbeaver/core-utils'; import { ActionService, KeyBindingService, MenuService } from '@cloudbeaver/core-view'; import { NavigationTabsService } from '@cloudbeaver/plugin-navigation-tabs'; import { getResourceKeyFromNodeId } from '@cloudbeaver/plugin-navigation-tree-rm'; import { RESOURCE_NAME_REGEX, ResourceManagerService } from '@cloudbeaver/plugin-resource-manager'; import { ResourceManagerScriptsService, SaveScriptDialog, SCRIPTS_TYPE_ID } from '@cloudbeaver/plugin-resource-manager-scripts'; import { DATA_CONTEXT_SQL_EDITOR_STATE, ESqlDataSourceFeatures, getSqlEditorName, type ISqlDataSource, LocalStorageSqlDataSource, MemorySqlDataSource, SQL_EDITOR_TOOLS_MENU, SqlDataSourceService, SqlEditorSettingsService, SqlEditorView, } from '@cloudbeaver/plugin-sql-editor'; import { isSQLEditorTab, SqlEditorNavigatorService } from '@cloudbeaver/plugin-sql-editor-navigation-tab'; import { ACTION_SAVE_AS_SCRIPT } from './ACTION_SAVE_AS_SCRIPT.js'; import { KEY_BINDING_SQL_EDITOR_SAVE_AS_SCRIPT } from './KEY_BINDING_SQL_EDITOR_SAVE_AS_SCRIPT.js'; import { ResourceSqlDataSource } from './ResourceSqlDataSource.js'; import { SqlEditorTabResourceService } from './SqlEditorTabResourceService.js'; @injectable(() => [ NavNodeManagerService, NavNodeInfoResource, NavigationTabsService, NotificationService, SqlEditorNavigatorService, ResourceManagerService, ProjectsService, ProjectInfoResource, SqlEditorTabResourceService, CommonDialogService, ActionService, MenuService, SqlDataSourceService, SqlEditorSettingsService, ResourceManagerResource, ResourceManagerScriptsService, KeyBindingService, SqlEditorView, ]) export class PluginBootstrap extends Bootstrap { constructor( private readonly navNodeManagerService: NavNodeManagerService, private readonly navNodeInfoResource: NavNodeInfoResource, private readonly navigationTabsService: NavigationTabsService, private readonly notificationService: NotificationService, private readonly sqlEditorNavigatorService: SqlEditorNavigatorService, private readonly resourceManagerService: ResourceManagerService, private readonly projectsService: ProjectsService, private readonly projectInfoResource: ProjectInfoResource, private readonly sqlEditorTabResourceService: SqlEditorTabResourceService, private readonly commonDialogService: CommonDialogService, private readonly actionService: ActionService, private readonly menuService: MenuService, private readonly sqlDataSourceService: SqlDataSourceService, private readonly sqlEditorSettingsService: SqlEditorSettingsService, private readonly resourceManagerResource: ResourceManagerResource, private readonly resourceManagerScriptsService: ResourceManagerScriptsService, private readonly keyBindingService: KeyBindingService, private readonly sqlEditorView: SqlEditorView, ) { super(); this.saveAsScriptHandler = this.saveAsScriptHandler.bind(this); } override register(): void { this.navNodeManagerService.onCanOpen.addHandler(this.canOpenHandler.bind(this)); this.navNodeManagerService.navigator.addHandler(this.navigationHandler.bind(this)); this.actionService.addHandler({ id: 'scripts-base-handler', actions: [ACTION_SAVE_AS_SCRIPT], contexts: [DATA_CONTEXT_SQL_EDITOR_STATE], isActionApplicable: (context): boolean => { const state = context.get(DATA_CONTEXT_SQL_EDITOR_STATE)!; if (!this.projectsService.activeProjects.some(project => project.canEditResources)) { return false; } const dataSource = this.sqlDataSourceService.get(state.editorId); return dataSource instanceof MemorySqlDataSource || dataSource instanceof LocalStorageSqlDataSource; }, handler: (context, action) => { if (action === ACTION_SAVE_AS_SCRIPT) { this.saveAsScriptHandler(context); } }, getActionInfo: (context, action) => { if (action === ACTION_SAVE_AS_SCRIPT) { return { ...action.info, label: '', }; } return action.info; }, }); this.sqlEditorView.registerAction(ACTION_SAVE_AS_SCRIPT); this.menuService.addCreator({ menus: [SQL_EDITOR_TOOLS_MENU], contexts: [DATA_CONTEXT_SQL_EDITOR_STATE], isApplicable: context => { const state = context.get(DATA_CONTEXT_SQL_EDITOR_STATE)!; const dataSource = this.sqlDataSourceService.get(state.editorId); return this.resourceManagerService.enabled && !!dataSource?.hasFeature(ESqlDataSourceFeatures.script); }, getItems: (context, items) => [ACTION_SAVE_AS_SCRIPT, ...items], }); this.keyBindingService.addKeyBindingHandler({ id: 'save-as-script', binding: KEY_BINDING_SQL_EDITOR_SAVE_AS_SCRIPT, actions: [ACTION_SAVE_AS_SCRIPT], contexts: [DATA_CONTEXT_SQL_EDITOR_STATE], isBindingApplicable: (_, action) => action === ACTION_SAVE_AS_SCRIPT, handler: this.saveAsScriptHandler.bind(this), }); } private async saveAsScriptHandler(context: IDataContextProvider) { const state = context.get(DATA_CONTEXT_SQL_EDITOR_STATE)!; let dataSource: ISqlDataSource | ResourceSqlDataSource | undefined = this.sqlDataSourceService.get(state.editorId); if (!dataSource) { return; } let projectId = dataSource.executionContext?.projectId ?? null; await this.projectInfoResource.load(CachedMapAllKey); const name = getSqlEditorName(state, dataSource); if (projectId) { const project = this.projectInfoResource.get(projectId); if (!project?.canEditResources) { projectId = null; } } const { status, result } = await this.commonDialogService.open(SaveScriptDialog, { defaultScriptName: name, projectId, validation: async ({ name, projectId }, setMessage) => { const trimmedName = name.trim(); if (!projectId || !trimmedName.length) { return false; } if (!RESOURCE_NAME_REGEX.test(trimmedName)) { setMessage('plugin_resource_manager_scripts_script_name_invalid_characters_message'); return false; } const project = this.projectInfoResource.get(projectId); const nameWithExtension = this.projectInfoResource.getNameWithExtension(projectId, SCRIPTS_TYPE_ID, trimmedName); const rootFolder = project ? this.resourceManagerScriptsService.getRootFolder(project) : undefined; const key = getRmResourcePath(projectId, rootFolder); try { await this.resourceManagerResource.load(CachedTreeChildrenKey(key)); return !this.resourceManagerResource.has(createPath(key, nameWithExtension)); } catch (exception: any) { return false; } }, }); if (status === DialogueStateResult.Resolved && result !== undefined) { try { projectId = result.projectId; if (!projectId) { throw new Error('Project not selected'); } const project = this.projectInfoResource.get(projectId); if (!project) { throw new Error('Project not found'); } const nameWithoutExtension = result.name.trim(); const scriptName = this.projectInfoResource.getNameWithExtension(projectId, SCRIPTS_TYPE_ID, nameWithoutExtension); const scriptsRootFolder = this.resourceManagerScriptsService.getRootFolder(project); const folderResourceKey = getResourceKeyFromNodeId(createPath(RESOURCES_NODE_PATH, projectId, scriptsRootFolder)); if (!folderResourceKey) { this.notificationService.logError({ title: 'ui_error', message: 'plugin_sql_editor_navigation_tab_resource_save_script_error' }); return; } const resourceKey = createPath(folderResourceKey, scriptName); await this.resourceManagerScriptsService.createScript(resourceKey, dataSource.executionContext, dataSource.script); dataSource = this.sqlDataSourceService.create(state, ResourceSqlDataSource.key, { script: dataSource.script, executionContext: dataSource.executionContext, }); (dataSource as ResourceSqlDataSource).setResourceKey(resourceKey); this.notificationService.logSuccess({ title: 'plugin_sql_editor_navigation_tab_resource_save_script_success', message: nameWithoutExtension, }); if (!this.resourceManagerScriptsService.active) { this.resourceManagerScriptsService.togglePanel(); } else { this.resourceManagerScriptsService.selectTab(); } } catch (exception) { this.notificationService.logException(exception as any, 'plugin_sql_editor_navigation_tab_resource_save_script_error'); } } } private canOpenHandler(data: INodeNavigationData, contexts: IExecutionContextProvider<INodeNavigationData>): void { const nodeInfo = contexts.getContext(this.navNodeManagerService.navigationNavNodeContext); if (this.canOpen(data, contexts)) { nodeInfo.markOpen(); } } private async navigationHandler(data: INodeNavigationData, contexts: IExecutionContextProvider<INodeNavigationData>) { if (!this.canOpen(data, contexts)) { return; } try { const resourceKey = getResourceKeyFromNodeId(data.nodeId); if (!resourceKey) { return; } const resource = await this.resourceManagerResource.load(resourceKey); const maxSize = this.sqlEditorSettingsService.maxFileSize; const size = Math.round(resource.length / 1000); // kilobyte if (size > maxSize) { this.notificationService.logInfo({ title: 'sql_editor_upload_script_max_size_title', message: `Max size: ${maxSize}KB\nFile size: ${size}KB`, autoClose: false, }); return; } const tab = this.sqlEditorTabResourceService.getResourceTab(resourceKey); if (tab) { this.navigationTabsService.selectTab(tab.id); } else { const contextProvider = await this.sqlEditorNavigatorService.openNewEditor({ name: getPathName(resourceKey), dataSourceKey: ResourceSqlDataSource.key, }); const context = contextProvider.getContext(this.navigationTabsService.navigationTabContext); if (context.tab && isSQLEditorTab(context.tab)) { const dataSource = this.sqlDataSourceService.create(context.tab.handlerState, ResourceSqlDataSource.key) as ResourceSqlDataSource; dataSource.setResourceKey(resourceKey); } } } catch (exception) { this.notificationService.logException(exception as any, 'plugin_sql_editor_navigation_tab_resource_open_script_error'); } } private canOpen(data: INodeNavigationData, contexts: IExecutionContextProvider<INodeNavigationData>): boolean { if (!this.resourceManagerService.enabled) { return false; } const nodeInfo = contexts.getContext(this.navNodeManagerService.navigationNavNodeContext); if (!nodeInfo.projectId) { return false; } const node = this.navNodeInfoResource.get(data.nodeId); const project = this.projectInfoResource.get(nodeInfo.projectId); if (!project) { return false; } const resourceType = this.projectInfoResource.getResourceType(project, SCRIPTS_TYPE_ID); if (!resourceType) { return false; } if (!node || node.nodeType !== NAV_NODE_TYPE_RM_RESOURCE || !isResourceOfType(resourceType, node.id)) { return false; } const resourceKey = getResourceKeyFromNodeId(node.id); if (!resourceKey) { return false; } return true; } }