mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-09-24 16:04:36 +08:00
CB-3451 git integration (#1782)
* CB-3570 add manual save * CB-3451 git settings api * CB-3451 migrate to the new config model * CB-3451 improve locks, update gql * CB-3451 fix RM configuration * CB-3570 resolve conflicts * CB-3451 checkout remote branch * CB-3451 review fix * CB-3451 improve logs/fix private project access * CB-3570 review fixes * CB-3570 remove hasChanges flag * CB-3570 debounce saveProperties --------- Co-authored-by: Aleksandr Skoblikov <aleksandr.skoblikov@dbeaver.com>
This commit is contained in:
co-authored by
Aleksandr Skoblikov
parent
ba48b282fd
commit
3642bbfa41
+4
-2
@@ -70,8 +70,10 @@ public abstract class BaseWebApplication extends BaseApplicationImpl implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public RMController createResourceController(@NotNull SMCredentialsProvider credentialsProvider,
|
||||
@NotNull DBPWorkspace workspace) throws DBException {
|
||||
public RMController createResourceController(
|
||||
@NotNull SMCredentialsProvider credentialsProvider,
|
||||
@NotNull DBPWorkspace workspace
|
||||
) throws DBException {
|
||||
throw new IllegalStateException("Resource controller is not supported by " + getClass().getSimpleName());
|
||||
}
|
||||
|
||||
|
||||
+6
-3
@@ -53,7 +53,8 @@ public interface WebApplication extends DBPApplication {
|
||||
WebProjectImpl createProjectImpl(
|
||||
@NotNull WebSession webSession,
|
||||
@NotNull RMProject project,
|
||||
@NotNull DataSourceFilter dataSourceFilter);
|
||||
@NotNull DataSourceFilter dataSourceFilter
|
||||
);
|
||||
|
||||
SMController createSecurityController(@NotNull SMCredentialsProvider credentialsProvider) throws DBException;
|
||||
|
||||
@@ -61,8 +62,10 @@ public interface WebApplication extends DBPApplication {
|
||||
|
||||
DBSSecretController getSecretController(@NotNull SMCredentialsProvider credentialsProvider) throws DBException;
|
||||
|
||||
RMController createResourceController(@NotNull SMCredentialsProvider credentialsProvider,
|
||||
@NotNull DBPWorkspace workspace) throws DBException;
|
||||
RMController createResourceController(
|
||||
@NotNull SMCredentialsProvider credentialsProvider,
|
||||
@NotNull DBPWorkspace workspace
|
||||
) throws DBException;
|
||||
|
||||
DBFileController createFileController(@NotNull SMCredentialsProvider credentialsProvider);
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ public class RMUtils {
|
||||
* @return path to the project based on the name of projects and prefixes that it contains
|
||||
*/
|
||||
@NotNull
|
||||
public static Path getProjectPathByName(@NotNull String projectId) throws DBException {
|
||||
public static Path getProjectPathById(@NotNull String projectId) throws DBException {
|
||||
int divPos = projectId.indexOf("_");
|
||||
if (divPos <= 0) {
|
||||
throw new DBException("Bad project ID");
|
||||
|
||||
+18
-15
@@ -71,7 +71,6 @@ public class LocalResourceController implements RMController {
|
||||
|
||||
private static final String FILE_REGEX = "(?U)[\\w.$()@/\\\\ -]+";
|
||||
private static final String PROJECT_REGEX = "(?U)[\\w.$()@ -]+"; // slash not allowed in project name
|
||||
|
||||
public static final String DEFAULT_CHANGE_ID = "0";
|
||||
|
||||
private final DBPWorkspace workspace;
|
||||
@@ -82,8 +81,8 @@ public class LocalResourceController implements RMController {
|
||||
private final Path sharedProjectsPath;
|
||||
private final String globalProjectName;
|
||||
private Supplier<SMController> smControllerSupplier;
|
||||
private final RMFileLockController lockController;
|
||||
private final List<RMFileOperationHandler> fileHandlers;
|
||||
protected final RMFileLockController lockController;
|
||||
protected final List<RMFileOperationHandler> fileHandlers;
|
||||
|
||||
private final Map<String, BaseWebProjectImpl> projectRegistries = new LinkedHashMap<>();
|
||||
|
||||
@@ -121,7 +120,7 @@ public class LocalResourceController implements RMController {
|
||||
return userId == null ? null : this.userProjectsPath.resolve(userId);
|
||||
}
|
||||
|
||||
private BaseWebProjectImpl getWebProject(String projectId, boolean refresh) throws DBException {
|
||||
protected BaseWebProjectImpl getWebProject(String projectId, boolean refresh) throws DBException {
|
||||
synchronized (projectRegistries) {
|
||||
BaseWebProjectImpl project = projectRegistries.get(projectId);
|
||||
if (project == null || refresh) {
|
||||
@@ -765,7 +764,7 @@ public class LocalResourceController implements RMController {
|
||||
try {
|
||||
Files.write(targetPath, data);
|
||||
} catch (IOException e) {
|
||||
throw new DBException("Error reading resource '" + resourcePath + "'", e);
|
||||
throw new DBException("Error writing resource '" + resourcePath + "'", e);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
@@ -777,7 +776,7 @@ public class LocalResourceController implements RMController {
|
||||
return DEFAULT_CHANGE_ID;
|
||||
}
|
||||
|
||||
private void createFolder(Path targetPath) throws DBException {
|
||||
protected void createFolder(Path targetPath) throws DBException {
|
||||
if (!Files.exists(targetPath)) {
|
||||
try {
|
||||
Files.createDirectories(targetPath);
|
||||
@@ -924,34 +923,31 @@ public class LocalResourceController implements RMController {
|
||||
}
|
||||
|
||||
private <T> T doProjectOperation(String projectId, RMFileOperation<T> operation) throws DBException {
|
||||
var projectPath = getProjectPath(projectId);
|
||||
for (RMFileOperationHandler fileHandler : fileHandlers) {
|
||||
fileHandler.projectOpened(projectPath);
|
||||
fileHandler.projectOpened(projectId);
|
||||
}
|
||||
return operation.doOperation();
|
||||
}
|
||||
|
||||
private <T> T doFileReadOperation(String projectId, Path file, RMFileOperation<T> operation) throws DBException {
|
||||
var projectPath = getProjectPath(projectId);
|
||||
for (RMFileOperationHandler fileHandler : fileHandlers) {
|
||||
fileHandler.beforeFileRead(projectPath, file);
|
||||
fileHandler.beforeFileRead(projectId, file);
|
||||
}
|
||||
return operation.doOperation();
|
||||
}
|
||||
|
||||
private <T> T doFileWriteOperation(String projectId, Path file, RMFileOperation<T> operation) throws DBException {
|
||||
var projectPath = getProjectPath(projectId);
|
||||
for (RMFileOperationHandler fileHandler : fileHandlers) {
|
||||
fileHandler.beforeFileChange(projectPath, file);
|
||||
fileHandler.beforeFileChange(projectId, file);
|
||||
}
|
||||
var result = operation.doOperation();
|
||||
for (RMFileOperationHandler fileHandler : fileHandlers) {
|
||||
fileHandler.afterFileChange(projectPath, file, credentialsProvider.getActiveUserCredentials());
|
||||
fileHandler.afterFileChange(projectId, file, credentialsProvider.getActiveUserCredentials());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Path getProjectPath(String projectId) throws DBException {
|
||||
protected Path getProjectPath(String projectId) throws DBException {
|
||||
RMProjectName project = parseProjectName(projectId);
|
||||
RMProjectType type = project.getType();
|
||||
String projectName = project.getName();
|
||||
@@ -1179,9 +1175,16 @@ public class LocalResourceController implements RMController {
|
||||
return RMProjectType.GLOBAL.getPrefix().equals(rmProjectName.getPrefix());
|
||||
}
|
||||
|
||||
public static boolean isPrivateProject(String projectId, String userId) {
|
||||
public static boolean isPrivateProject(String projectId) {
|
||||
RMProjectName rmProjectName = parseProjectNameUnsafe(projectId);
|
||||
return RMProjectType.USER.getPrefix().equals(rmProjectName.getPrefix());
|
||||
}
|
||||
|
||||
public static boolean isProjectOwner(String projectId, String userId) {
|
||||
RMProjectName rmProjectName = parseProjectNameUnsafe(projectId);
|
||||
return RMProjectType.USER.getPrefix().equals(rmProjectName.getPrefix()) &&
|
||||
rmProjectName.name.equals(userId);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
+4
-4
@@ -25,23 +25,23 @@ import java.nio.file.Path;
|
||||
|
||||
public interface RMFileOperationHandler {
|
||||
|
||||
void projectOpened(@NotNull Path projectPath) throws DBException;
|
||||
void projectOpened(@NotNull String projectId) throws DBException;
|
||||
|
||||
/**
|
||||
* Executed before reading file
|
||||
*/
|
||||
void beforeFileRead(@NotNull Path projectPath, @NotNull Path filePath) throws DBException;
|
||||
void beforeFileRead(@NotNull String projectId, @NotNull Path filePath) throws DBException;
|
||||
|
||||
/**
|
||||
* Executed before file modification
|
||||
*/
|
||||
void beforeFileChange(@NotNull Path projectPath, @NotNull Path filePath) throws DBException;
|
||||
void beforeFileChange(@NotNull String projectId, @NotNull Path filePath) throws DBException;
|
||||
|
||||
/**
|
||||
* Executed after file modification
|
||||
*/
|
||||
void afterFileChange(
|
||||
@NotNull Path projectPath,
|
||||
@NotNull String projectId,
|
||||
@NotNull Path filePath,
|
||||
@Nullable SMCredentials smCredentials
|
||||
) throws DBException;
|
||||
|
||||
+21
-1
@@ -18,6 +18,7 @@ package io.cloudbeaver.model.rm.lock;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import io.cloudbeaver.model.app.WebApplication;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
@@ -71,7 +72,8 @@ public class RMFileLockController {
|
||||
* @param operationName - executed operation name
|
||||
* @return - lock
|
||||
*/
|
||||
public RMLock lockProject(String projectId, String operationName) throws DBException {
|
||||
@NotNull
|
||||
public RMLock lockProject(@NotNull String projectId,@NotNull String operationName) throws DBException {
|
||||
synchronized (RMFileLockController.class) {
|
||||
try {
|
||||
createLockFolderIfNeeded();
|
||||
@@ -91,6 +93,24 @@ public class RMFileLockController {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* if the project is already locked, the operation will be executed as a child of the first lock,
|
||||
* otherwise it creates its own lock.
|
||||
*
|
||||
* @param projectId - project to be locked
|
||||
* @param operationName - executed operation name
|
||||
* @return - lock
|
||||
*/
|
||||
@Nullable
|
||||
public RMLock lockIfNotLocked(@NotNull String projectId, @NotNull String operationName) throws DBException {
|
||||
synchronized (RMFileLockController.class) {
|
||||
if (isProjectLocked(projectId)) {
|
||||
return null;
|
||||
}
|
||||
return lockProject(projectId, operationName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that project locked
|
||||
*/
|
||||
|
||||
Regular → Executable
@@ -17,6 +17,8 @@ import { DataSynchronizationQueue } from './DataSynchronization/DataSynchronizat
|
||||
import { DataSynchronizationService } from './DataSynchronization/DataSynchronizationService';
|
||||
import { ServerConfigEventHandler } from './ServerConfigEventHandler';
|
||||
|
||||
export const FEATURE_GIT_ID = 'git';
|
||||
|
||||
export type ServerConfig = Omit<SDKServerConfig, 'hostName'>;
|
||||
|
||||
@injectable()
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2023 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 { createKeyBinding } from '../createKeyBinding';
|
||||
|
||||
export const KEY_BINDING_SAVE = createKeyBinding({
|
||||
id: 'save',
|
||||
keysWin: ['ctrl+s'],
|
||||
keysMac: ['cmd+s'],
|
||||
preventDefault: true,
|
||||
});
|
||||
@@ -18,6 +18,7 @@ export * from './Action/Actions/ACTION_ZOOM_OUT';
|
||||
export * from './Action/KeyBinding/Bindings/KEY_BINDING_OPEN_IN_TAB';
|
||||
export * from './Action/KeyBinding/Bindings/KEY_BINDING_REDO';
|
||||
export * from './Action/KeyBinding/Bindings/KEY_BINDING_UNDO';
|
||||
export * from './Action/KeyBinding/Bindings/KEY_BINDING_SAVE';
|
||||
export * from './Action/KeyBinding/KeyBindingService';
|
||||
export * from './Action/KeyBinding/createKeyBinding';
|
||||
export * from './Action/ActionService';
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2023 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_SAVE_AS_SCRIPT = createAction('save-as-script', {
|
||||
label: 'plugin_sql_editor_navigation_tab_resource_save_script_title',
|
||||
tooltip: 'plugin_sql_editor_navigation_tab_resource_save_script_title',
|
||||
icon: '/icons/sql_script_sm.svg',
|
||||
});
|
||||
@@ -14,7 +14,7 @@ import { isResourceOfType, ProjectInfoResource, ProjectsService } from '@cloudbe
|
||||
import { NAV_NODE_TYPE_RM_RESOURCE, ResourceManagerResource, RESOURCES_NODE_PATH } from '@cloudbeaver/core-resource-manager';
|
||||
import { CachedMapAllKey } from '@cloudbeaver/core-sdk';
|
||||
import { createPath, getPathName } from '@cloudbeaver/core-utils';
|
||||
import { ACTION_SAVE, ActionService, DATA_CONTEXT_MENU, MenuService } from '@cloudbeaver/core-view';
|
||||
import { ACTION_SAVE, ActionService, DATA_CONTEXT_MENU, KEY_BINDING_SAVE, KeyBindingService, MenuService } from '@cloudbeaver/core-view';
|
||||
import { NavigationTabsService } from '@cloudbeaver/plugin-navigation-tabs';
|
||||
import { getResourceKeyFromNodeId, NavResourceNodeService } from '@cloudbeaver/plugin-navigation-tree-rm';
|
||||
import { ResourceManagerService } from '@cloudbeaver/plugin-resource-manager';
|
||||
@@ -28,12 +28,14 @@ import {
|
||||
MemorySqlDataSource,
|
||||
SQL_EDITOR_TOOLS_MENU,
|
||||
SqlDataSourceService,
|
||||
SqlEditorService,
|
||||
SqlEditorSettingsService,
|
||||
} from '@cloudbeaver/plugin-sql-editor';
|
||||
import { isSQLEditorTab, SqlEditorNavigatorService } from '@cloudbeaver/plugin-sql-editor-navigation-tab';
|
||||
|
||||
import { ResourceSqlDataSource } from './ResourceSqlDataSource';
|
||||
import { SqlEditorTabResourceService } from './SqlEditorTabResourceService';
|
||||
import { ACTION_SAVE_AS_SCRIPT } from './ACTION_SAVE_AS_SCRIPT';
|
||||
|
||||
@injectable()
|
||||
export class PluginBootstrap extends Bootstrap {
|
||||
@@ -53,8 +55,10 @@ export class PluginBootstrap extends Bootstrap {
|
||||
private readonly menuService: MenuService,
|
||||
private readonly sqlDataSourceService: SqlDataSourceService,
|
||||
private readonly sqlEditorSettingsService: SqlEditorSettingsService,
|
||||
private readonly sqlEditorService: SqlEditorService,
|
||||
private readonly resourceManagerResource: ResourceManagerResource,
|
||||
private readonly resourceManagerScriptsService: ResourceManagerScriptsService,
|
||||
private readonly keyBindingService: KeyBindingService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
@@ -66,7 +70,7 @@ export class PluginBootstrap extends Bootstrap {
|
||||
this.actionService.addHandler({
|
||||
id: 'scripts-base-handler',
|
||||
isActionApplicable: (context, action): boolean => {
|
||||
if (action === ACTION_SAVE) {
|
||||
if (action === ACTION_SAVE_AS_SCRIPT || action === ACTION_SAVE) {
|
||||
const state = context.tryGet(DATA_CONTEXT_SQL_EDITOR_STATE);
|
||||
|
||||
if (!state) {
|
||||
@@ -79,7 +83,20 @@ export class PluginBootstrap extends Bootstrap {
|
||||
|
||||
const dataSource = this.sqlDataSourceService.get(state.editorId);
|
||||
|
||||
return dataSource instanceof MemorySqlDataSource || dataSource instanceof LocalStorageSqlDataSource;
|
||||
if (action === ACTION_SAVE_AS_SCRIPT) {
|
||||
return dataSource instanceof MemorySqlDataSource || dataSource instanceof LocalStorageSqlDataSource;
|
||||
}
|
||||
|
||||
if (action === ACTION_SAVE) {
|
||||
return dataSource instanceof ResourceSqlDataSource;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
isHidden: (context, action) => {
|
||||
if (action === ACTION_SAVE) {
|
||||
return this.sqlEditorService.autoSave;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -93,7 +110,7 @@ export class PluginBootstrap extends Bootstrap {
|
||||
return;
|
||||
}
|
||||
|
||||
if (action === ACTION_SAVE) {
|
||||
if (action === ACTION_SAVE_AS_SCRIPT) {
|
||||
let projectId = dataSource.executionContext?.projectId ?? null;
|
||||
await this.projectInfoResource.load(CachedMapAllKey);
|
||||
const name = getSqlEditorName(state, dataSource);
|
||||
@@ -157,14 +174,40 @@ export class PluginBootstrap extends Bootstrap {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (action === ACTION_SAVE) {
|
||||
const state = context.get(DATA_CONTEXT_SQL_EDITOR_STATE);
|
||||
const source = this.sqlDataSourceService.get(state.editorId) as ResourceSqlDataSource | undefined;
|
||||
|
||||
if (!source) {
|
||||
return;
|
||||
}
|
||||
|
||||
await source.save();
|
||||
}
|
||||
},
|
||||
isDisabled: (context, action) => {
|
||||
if (action === ACTION_SAVE) {
|
||||
const state = context.get(DATA_CONTEXT_SQL_EDITOR_STATE);
|
||||
const source = this.sqlDataSourceService.get(state.editorId) as ResourceSqlDataSource | undefined;
|
||||
|
||||
if (!source) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return source.isLoading() || source.isSaved();
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
getActionInfo: (context, action) => {
|
||||
if (action === ACTION_SAVE) {
|
||||
if (action === ACTION_SAVE_AS_SCRIPT || action === ACTION_SAVE) {
|
||||
return {
|
||||
...action.info,
|
||||
label: '',
|
||||
};
|
||||
}
|
||||
|
||||
return action.info;
|
||||
},
|
||||
});
|
||||
@@ -176,6 +219,7 @@ export class PluginBootstrap extends Bootstrap {
|
||||
if (!state) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const dataSource = this.sqlDataSourceService.get(state.editorId);
|
||||
|
||||
return (
|
||||
@@ -184,11 +228,27 @@ export class PluginBootstrap extends Bootstrap {
|
||||
!!dataSource?.hasFeature(ESqlDataSourceFeatures.script)
|
||||
);
|
||||
},
|
||||
getItems: (context, items) => [...items, ACTION_SAVE],
|
||||
getItems: (context, items) => [...items, ACTION_SAVE_AS_SCRIPT, ACTION_SAVE],
|
||||
});
|
||||
|
||||
this.keyBindingService.addKeyBindingHandler({
|
||||
id: 'script-save',
|
||||
binding: KEY_BINDING_SAVE,
|
||||
isBindingApplicable: (context, action) => action === ACTION_SAVE,
|
||||
handler: async context => {
|
||||
const state = context.get(DATA_CONTEXT_SQL_EDITOR_STATE);
|
||||
const source = this.sqlDataSourceService.get(state.editorId) as ResourceSqlDataSource | undefined;
|
||||
|
||||
if (!source) {
|
||||
return;
|
||||
}
|
||||
|
||||
await source.save();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
load(): void | Promise<void> {}
|
||||
load(): void | Promise<void> { }
|
||||
|
||||
private canOpenHandler(data: INodeNavigationData, contexts: IExecutionContextProvider<INodeNavigationData>): void {
|
||||
const nodeInfo = contexts.getContext(this.navNodeManagerService.navigationNavNodeContext);
|
||||
|
||||
+12
-3
@@ -19,7 +19,7 @@ import { getRmResourceKey, ResourceManagerResource } from '@cloudbeaver/core-res
|
||||
import { isResourceAlias, ResourceKey, ResourceKeyUtils } from '@cloudbeaver/core-sdk';
|
||||
import { createPath, debounce, getPathName, getPathParent, isArraysEqual, isObjectsEqual, isValuesEqual } from '@cloudbeaver/core-utils';
|
||||
import { SCRIPTS_TYPE_ID } from '@cloudbeaver/plugin-resource-manager-scripts';
|
||||
import { BaseSqlDataSource, ESqlDataSourceFeatures } from '@cloudbeaver/plugin-sql-editor';
|
||||
import { BaseSqlDataSource, ESqlDataSourceFeatures, SqlEditorService } from '@cloudbeaver/plugin-sql-editor';
|
||||
|
||||
import type { IResourceSqlDataSourceState } from './IResourceSqlDataSourceState';
|
||||
|
||||
@@ -106,6 +106,7 @@ export class ResourceSqlDataSource extends BaseSqlDataSource {
|
||||
private readonly projectInfoResource: ProjectInfoResource,
|
||||
private readonly connectionInfoResource: ConnectionInfoResource,
|
||||
private readonly resourceManagerResource: ResourceManagerResource,
|
||||
private readonly sqlEditorService: SqlEditorService,
|
||||
state: IResourceSqlDataSourceState,
|
||||
) {
|
||||
super();
|
||||
@@ -116,6 +117,7 @@ export class ResourceSqlDataSource extends BaseSqlDataSource {
|
||||
this.resourceUseKeyId = null;
|
||||
this.scheduler = new TaskScheduler(() => true);
|
||||
this.debouncedWrite = debounce(this.debouncedWrite.bind(this), VALUE_SYNC_DELAY);
|
||||
this.debouncedSaveProperties = debounce(this.debouncedSaveProperties.bind(this), VALUE_SYNC_DELAY);
|
||||
this.syncResource = this.syncResource.bind(this);
|
||||
|
||||
resourceManagerResource.onDataOutdated.addHandler(this.syncResource);
|
||||
@@ -200,7 +202,7 @@ export class ResourceSqlDataSource extends BaseSqlDataSource {
|
||||
this._script = script;
|
||||
super.setScript(script);
|
||||
|
||||
if (previous !== script) {
|
||||
if (this.sqlEditorService.autoSave && previous !== script) {
|
||||
this.debouncedWrite();
|
||||
}
|
||||
}
|
||||
@@ -264,7 +266,9 @@ export class ResourceSqlDataSource extends BaseSqlDataSource {
|
||||
this.state.executionContext = toJS(executionContext);
|
||||
super.setExecutionContext(executionContext);
|
||||
|
||||
this.saveProperties();
|
||||
if (this.sqlEditorService.autoSave) {
|
||||
this.debouncedSaveProperties();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -351,6 +355,7 @@ export class ResourceSqlDataSource extends BaseSqlDataSource {
|
||||
|
||||
async save(): Promise<void> {
|
||||
await this.write();
|
||||
await this.saveProperties();
|
||||
}
|
||||
|
||||
private async saveProperties() {
|
||||
@@ -385,4 +390,8 @@ export class ResourceSqlDataSource extends BaseSqlDataSource {
|
||||
private debouncedWrite() {
|
||||
this.write();
|
||||
}
|
||||
|
||||
private debouncedSaveProperties() {
|
||||
this.saveProperties();
|
||||
}
|
||||
}
|
||||
|
||||
+4
-3
@@ -21,13 +21,12 @@ import { NavigationTabsService } from '@cloudbeaver/plugin-navigation-tabs';
|
||||
import { NavResourceNodeService } from '@cloudbeaver/plugin-navigation-tree-rm';
|
||||
import { ResourceManagerService } from '@cloudbeaver/plugin-resource-manager';
|
||||
import { ResourceManagerScriptsService } from '@cloudbeaver/plugin-resource-manager-scripts';
|
||||
import { getSqlEditorName, SqlDataSourceService } from '@cloudbeaver/plugin-sql-editor';
|
||||
import { getSqlEditorName, SqlDataSourceService, SqlEditorService } from '@cloudbeaver/plugin-sql-editor';
|
||||
import { SqlEditorTabService } from '@cloudbeaver/plugin-sql-editor-navigation-tab';
|
||||
|
||||
import type { IResourceSqlDataSourceState } from './IResourceSqlDataSourceState';
|
||||
import { ResourceSqlDataSource } from './ResourceSqlDataSource';
|
||||
import { SqlEditorTabResourceService } from './SqlEditorTabResourceService';
|
||||
|
||||
const RESOURCE_TAB_STATE = 'sql_editor_resource_tab_state';
|
||||
const SYNC_DELAY = 5 * 60 * 1000;
|
||||
|
||||
@@ -50,6 +49,7 @@ export class ResourceSqlDataSourceBootstrap extends Bootstrap {
|
||||
private readonly projectInfoResource: ProjectInfoResource,
|
||||
private readonly windowEventsService: WindowEventsService,
|
||||
private readonly sqlEditorTabResourceService: SqlEditorTabResourceService,
|
||||
private readonly sqlEditorService: SqlEditorService,
|
||||
localStorageSaveService: LocalStorageSaveService,
|
||||
) {
|
||||
super();
|
||||
@@ -95,6 +95,7 @@ export class ResourceSqlDataSourceBootstrap extends Bootstrap {
|
||||
this.projectInfoResource,
|
||||
this.connectionInfoResource,
|
||||
this.resourceManagerResource,
|
||||
this.sqlEditorService,
|
||||
this.createState(editorId),
|
||||
);
|
||||
|
||||
@@ -169,7 +170,7 @@ export class ResourceSqlDataSourceBootstrap extends Bootstrap {
|
||||
});
|
||||
}
|
||||
|
||||
load(): void | Promise<void> {}
|
||||
load(): void | Promise<void> { }
|
||||
|
||||
private createState(editorId: string, resourceKey?: string): IResourceSqlDataSourceState {
|
||||
let state = this.dataSourceStateState.get(editorId);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export default [
|
||||
['plugin_sql_editor_navigation_tab_resource_save_script_title', 'Save as script'],
|
||||
['plugin_sql_editor_navigation_tab_script_state_renaming', 'Renaming script...'],
|
||||
['plugin_sql_editor_navigation_tab_script_state_reading', 'Reading script...'],
|
||||
['plugin_sql_editor_navigation_tab_script_state_saving', 'Saving script...'],
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export default [
|
||||
['plugin_sql_editor_navigation_tab_resource_save_script_title', 'Save as script'],
|
||||
['plugin_sql_editor_navigation_tab_script_state_renaming', 'Renaming script...'],
|
||||
['plugin_sql_editor_navigation_tab_script_state_reading', 'Reading script...'],
|
||||
['plugin_sql_editor_navigation_tab_script_state_saving', 'Saving script...'],
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export default [
|
||||
['plugin_sql_editor_navigation_tab_resource_save_script_title', 'Сохранить как скрипт'],
|
||||
['plugin_sql_editor_navigation_tab_script_state_renaming', 'Переименовывание скрипта...'],
|
||||
['plugin_sql_editor_navigation_tab_script_state_reading', 'Чтение скрипта...'],
|
||||
['plugin_sql_editor_navigation_tab_script_state_saving', 'Сохранение скрипта...'],
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export default [
|
||||
['plugin_sql_editor_navigation_tab_resource_save_script_title', 'Save as script'],
|
||||
['plugin_sql_editor_navigation_tab_script_state_renaming', 'Renaming script...'],
|
||||
['plugin_sql_editor_navigation_tab_script_state_reading', 'Reading script...'],
|
||||
['plugin_sql_editor_navigation_tab_script_state_saving', 'Saving script...'],
|
||||
|
||||
@@ -76,12 +76,13 @@ export abstract class BaseSqlDataSource implements ISqlDataSource {
|
||||
|
||||
this.history.onNavigate.addHandler(value => this.setScript(value, SOURCE_HISTORY));
|
||||
|
||||
makeObservable<this, 'outdated' | 'editing'>(this, {
|
||||
makeObservable<this, 'outdated' | 'editing' | 'saved'>(this, {
|
||||
databaseModels: observable.ref,
|
||||
exception: observable.ref,
|
||||
outdated: observable.ref,
|
||||
message: observable.ref,
|
||||
editing: observable.ref,
|
||||
saved: observable.ref,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -97,6 +98,7 @@ export abstract class BaseSqlDataSource implements ISqlDataSource {
|
||||
}
|
||||
|
||||
setExecutionContext(executionContext?: IConnectionExecutionContextInfo | undefined): void {
|
||||
this.saved = false;
|
||||
this.onUpdate.execute();
|
||||
}
|
||||
|
||||
|
||||
@@ -23,11 +23,13 @@ import { injectable } from '@cloudbeaver/core-di';
|
||||
import { NotificationService } from '@cloudbeaver/core-events';
|
||||
import { ISyncExecutor, SyncExecutor } from '@cloudbeaver/core-executor';
|
||||
import { CachedMapAllKey, GraphQLService, SqlCompletionProposal, SqlScriptInfoFragment } from '@cloudbeaver/core-sdk';
|
||||
import { FEATURE_GIT_ID, ServerConfigResource } from '@cloudbeaver/core-root';
|
||||
|
||||
import { getSqlEditorName } from './getSqlEditorName';
|
||||
import type { ISqlEditorTabState } from './ISqlEditorTabState';
|
||||
import { ESqlDataSourceFeatures } from './SqlDataSource/ESqlDataSourceFeatures';
|
||||
import { SqlDataSourceService } from './SqlDataSource/SqlDataSourceService';
|
||||
import { SqlEditorSettingsService } from './SqlEditorSettingsService';
|
||||
|
||||
export type SQLProposal = SqlCompletionProposal;
|
||||
|
||||
@@ -39,6 +41,10 @@ export interface IQueryChangeData {
|
||||
|
||||
@injectable()
|
||||
export class SqlEditorService {
|
||||
get autoSave() {
|
||||
return this.sqlEditorSettingsService.settings.getValue('autoSave') && !this.serverConfigResource.isFeatureEnabled(FEATURE_GIT_ID, true);
|
||||
}
|
||||
|
||||
readonly onQueryChange: ISyncExecutor<IQueryChangeData>;
|
||||
|
||||
constructor(
|
||||
@@ -49,6 +55,8 @@ export class SqlEditorService {
|
||||
private readonly connectionExecutionContextResource: ConnectionExecutionContextResource,
|
||||
private readonly connectionInfoResource: ConnectionInfoResource,
|
||||
private readonly sqlDataSourceService: SqlDataSourceService,
|
||||
private readonly sqlEditorSettingsService: SqlEditorSettingsService,
|
||||
private readonly serverConfigResource: ServerConfigResource,
|
||||
) {
|
||||
this.onQueryChange = new SyncExecutor();
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import { PluginManagerService, PluginSettings } from '@cloudbeaver/core-plugin';
|
||||
const defaultSettings = {
|
||||
maxFileSize: 10 * 1024, // kilobyte
|
||||
disabled: false,
|
||||
autoSave: true,
|
||||
};
|
||||
|
||||
export type SqlEditorSettings = typeof defaultSettings;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
import { injectable } from '@cloudbeaver/core-di';
|
||||
import { ACTION_REDO, ACTION_UNDO, IActiveView, View } from '@cloudbeaver/core-view';
|
||||
import { ACTION_REDO, ACTION_SAVE, ACTION_UNDO, IActiveView, View } from '@cloudbeaver/core-view';
|
||||
import { ITab, NavigationTabsService } from '@cloudbeaver/plugin-navigation-tabs';
|
||||
|
||||
import { ACTION_SQL_EDITOR_EXECUTE } from './actions/ACTION_SQL_EDITOR_EXECUTE';
|
||||
@@ -27,6 +27,7 @@ export class SqlEditorView extends View<ITab> {
|
||||
ACTION_UNDO,
|
||||
ACTION_REDO,
|
||||
ACTION_SQL_EDITOR_SHOW_EXECUTION_PLAN,
|
||||
ACTION_SAVE,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user