From 3ae5f02dec05db4c0111f6a78de3ba70136dbc23 Mon Sep 17 00:00:00 2001 From: Wroud Date: Tue, 23 Aug 2022 09:58:18 +0300 Subject: [PATCH 1/5] feat(plugin-connections): CB-2302 dnd connection folders --- .../src/NavNodes/ConnectionFoldersBootstrap.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/webapp/packages/plugin-connections/src/NavNodes/ConnectionFoldersBootstrap.ts b/webapp/packages/plugin-connections/src/NavNodes/ConnectionFoldersBootstrap.ts index 95680fcaea..f2b19ee695 100644 --- a/webapp/packages/plugin-connections/src/NavNodes/ConnectionFoldersBootstrap.ts +++ b/webapp/packages/plugin-connections/src/NavNodes/ConnectionFoldersBootstrap.ts @@ -155,6 +155,10 @@ export class ConnectionFoldersBootstrap extends Bootstrap { }: INodeMoveData, contexts: IExecutionContextProvider ) { + if (![NAV_NODE_TYPE_PROJECT, NAV_NODE_TYPE_FOLDER].includes(targetNode.nodeType!)) { + return; + } + await this.projectsResource.load(CachedMapAllKey); const move = contexts.getContext(navNodeMoveContext); @@ -163,14 +167,12 @@ export class ConnectionFoldersBootstrap extends Bootstrap { const children = this.navTreeResource.get(targetNode.id); const targetProject = this.projectsNavNodeService.getProject(targetNode.id); - const supported = ( - [NAV_NODE_TYPE_PROJECT, NAV_NODE_TYPE_FOLDER].includes(targetNode.nodeType!) - && nodes.every(node => ( - node.nodeType === NAV_NODE_TYPE_CONNECTION - && targetProject === this.projectsNavNodeService.getProject(node.id) - && !children?.includes(node.id) - )) - ); + const supported = nodes.every(node => ( + [NAV_NODE_TYPE_CONNECTION, NAV_NODE_TYPE_FOLDER, NAV_NODE_TYPE_PROJECT].includes(node.nodeType!) + && targetProject === this.projectsNavNodeService.getProject(node.id) + && !children?.includes(node.id) + && targetNode.id !== node.id + )); if (!supported) { return; From 780fcbc509c23d67757cfe144ce11ae229a7e008 Mon Sep 17 00:00:00 2001 From: yagudin10 Date: Tue, 23 Aug 2022 10:45:42 +0300 Subject: [PATCH 2/5] CB-2302 move connection folders --- .../utils/WebConnectionFolderUtils.java | 14 ++++++++++++++ .../navigator/impl/WebServiceNavigator.java | 13 +++++-------- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/utils/WebConnectionFolderUtils.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/utils/WebConnectionFolderUtils.java index c95ebd05d3..0ddb8dda88 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/utils/WebConnectionFolderUtils.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/utils/WebConnectionFolderUtils.java @@ -21,6 +21,10 @@ import io.cloudbeaver.model.WebConnectionFolderInfo; import io.cloudbeaver.model.session.WebSession; import org.jkiss.code.Nullable; import org.jkiss.dbeaver.model.DBPDataSourceFolder; +import org.jkiss.dbeaver.model.navigator.DBNLocalFolder; +import org.jkiss.dbeaver.model.navigator.DBNNode; +import org.jkiss.dbeaver.model.navigator.DBNProject; +import org.jkiss.dbeaver.model.navigator.DBNRoot; import org.jkiss.utils.CommonUtils; public class WebConnectionFolderUtils { @@ -44,4 +48,14 @@ public class WebConnectionFolderUtils { throw new DBWebException("Folder name '" + folderName + "' contains illegal characters: /"); } } + + public static DBPDataSourceFolder getParentFolder(DBNNode folderNode) throws DBWebException { + if (folderNode instanceof DBNRoot || folderNode instanceof DBNProject) { + return null; + } else if (folderNode instanceof DBNLocalFolder) { + return ((DBNLocalFolder) folderNode).getFolder(); + } else { + throw new DBWebException("Navigator node '" + folderNode.getNodeItemPath() + "' is not a folder node"); + } + } } diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/navigator/impl/WebServiceNavigator.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/navigator/impl/WebServiceNavigator.java index 38d127ed38..2617fcc3ba 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/navigator/impl/WebServiceNavigator.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/navigator/impl/WebServiceNavigator.java @@ -466,17 +466,14 @@ public class WebServiceNavigator implements DBWServiceNavigator { } checkProjectEditAccess(node, session); if (node instanceof DBNDataSource) { - DBPDataSourceFolder folder; - if (folderNode instanceof DBNRoot || folderNode instanceof DBNProject) { - folder = null; - } else if (folderNode instanceof DBNLocalFolder) { - folder = ((DBNLocalFolder) folderNode).getFolder(); - } else { - throw new DBWebException("Navigator node '" + folderNodePath + "' is not a folder node"); - } + DBPDataSourceFolder folder = WebConnectionFolderUtils.getParentFolder(folderNode); ((DBNDataSource) node).moveToFolder(folderNode.getOwnerProject(), folder); node.getOwnerProject().getDataSourceRegistry().updateDataSource( ((DBNDataSource) node).getDataSourceContainer()); + } else if (node instanceof DBNLocalFolder) { + DBPDataSourceFolder folder = WebConnectionFolderUtils.getParentFolder(folderNode); + ((DBNLocalFolder) node).getFolder().setParent(folder); + WebServiceUtils.updateConfigAndRefreshDatabases(session, node.getOwnerProject().getId()); } else if (node instanceof DBNResourceManagerResource) { boolean rmNewNode = folderNode instanceof DBNAbstractResourceManagerNode; DBNResourceManagerResource rmOldNode = (DBNResourceManagerResource) node; From 5580c374f502e5cb5c12a6f31955ec33b6b8061b Mon Sep 17 00:00:00 2001 From: Ainur Iagudin Date: Wed, 24 Aug 2022 17:40:41 +0300 Subject: [PATCH 3/5] CB-2302 move folder validate siblings --- .../service/navigator/impl/WebServiceNavigator.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/navigator/impl/WebServiceNavigator.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/navigator/impl/WebServiceNavigator.java index 2617fcc3ba..7205a3d409 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/navigator/impl/WebServiceNavigator.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/navigator/impl/WebServiceNavigator.java @@ -472,6 +472,14 @@ public class WebServiceNavigator implements DBWServiceNavigator { ((DBNDataSource) node).getDataSourceContainer()); } else if (node instanceof DBNLocalFolder) { DBPDataSourceFolder folder = WebConnectionFolderUtils.getParentFolder(folderNode); + if (folder != null) { + List siblings = Arrays.stream(folder.getChildren()) + .map(DBPDataSourceFolder::getName) + .collect(Collectors.toList()); + if (siblings.contains(node.getName())) { + throw new DBWebException("Node " + folderNodePath + " contains folder with name '" + node.getName() +"'"); + } + } ((DBNLocalFolder) node).getFolder().setParent(folder); WebServiceUtils.updateConfigAndRefreshDatabases(session, node.getOwnerProject().getId()); } else if (node instanceof DBNResourceManagerResource) { From f9fa1d01482283af89f7824dfc44732a7a340e58 Mon Sep 17 00:00:00 2001 From: Wroud Date: Thu, 25 Aug 2022 01:02:44 +0300 Subject: [PATCH 4/5] fix(plugin-connections): CB-2302 folder's name duplication --- .../NavNodes/ConnectionFoldersBootstrap.ts | 47 +++++++++++++++---- .../plugin-connections/src/locales/en.ts | 1 + .../plugin-connections/src/locales/it.ts | 1 + .../plugin-connections/src/locales/ru.ts | 1 + .../plugin-connections/src/locales/zh.ts | 1 + 5 files changed, 43 insertions(+), 8 deletions(-) diff --git a/webapp/packages/plugin-connections/src/NavNodes/ConnectionFoldersBootstrap.ts b/webapp/packages/plugin-connections/src/NavNodes/ConnectionFoldersBootstrap.ts index f2b19ee695..5f1b4513b3 100644 --- a/webapp/packages/plugin-connections/src/NavNodes/ConnectionFoldersBootstrap.ts +++ b/webapp/packages/plugin-connections/src/NavNodes/ConnectionFoldersBootstrap.ts @@ -16,7 +16,7 @@ import { CommonDialogService, ConfirmationDialogDelete, DialogueStateResult, Ren import { NotificationService } from '@cloudbeaver/core-events'; import { ExecutorInterrupter, IExecutionContextProvider } from '@cloudbeaver/core-executor'; import { LocalizationService } from '@cloudbeaver/core-localization'; -import { ENodeMoveType, getNodesFromContext, INodeMoveData, NavNode, NavNodeInfoResource, NavNodeManagerService, navNodeMoveContext, NavTreeResource, NAV_NODE_TYPE_FOLDER, nodeDeleteContext, ROOT_NODE_PATH } from '@cloudbeaver/core-navigation-tree'; +import { ENodeMoveType, getNodesFromContext, INodeMoveData, NavNode, NavNodeInfoResource, NavNodeManagerService, navNodeMoveContext, NavTreeResource, NAV_NODE_TYPE_FOLDER, nodeDeleteContext } from '@cloudbeaver/core-navigation-tree'; import { NAV_NODE_TYPE_PROJECT, ProjectsNavNodeService, ProjectsResource } from '@cloudbeaver/core-projects'; import { CachedMapAllKey, ResourceKey, resourceKeyList, ResourceKeyUtils } from '@cloudbeaver/core-sdk'; import { createPath } from '@cloudbeaver/core-utils'; @@ -164,15 +164,21 @@ export class ConnectionFoldersBootstrap extends Bootstrap { const move = contexts.getContext(navNodeMoveContext); const nodes = getNodesFromContext(moveContexts); const nodeIdList = nodes.map(node => node.id); - const children = this.navTreeResource.get(targetNode.id); + const children = this.navTreeResource.get(targetNode.id) ?? []; const targetProject = this.projectsNavNodeService.getProject(targetNode.id); - const supported = nodes.every(node => ( - [NAV_NODE_TYPE_CONNECTION, NAV_NODE_TYPE_FOLDER, NAV_NODE_TYPE_PROJECT].includes(node.nodeType!) - && targetProject === this.projectsNavNodeService.getProject(node.id) - && !children?.includes(node.id) - && targetNode.id !== node.id - )); + const supported = nodes.every(node => { + if ( + ![NAV_NODE_TYPE_CONNECTION, NAV_NODE_TYPE_FOLDER, NAV_NODE_TYPE_PROJECT].includes(node.nodeType!) + || targetProject !== this.projectsNavNodeService.getProject(node.id) + || children.includes(node.id) + || targetNode.id === node.id + ) { + return false; + } + + return true; + }); if (!supported) { return; @@ -183,6 +189,31 @@ export class ConnectionFoldersBootstrap extends Bootstrap { move.setCanMove(true); } } else { + const childrenNode = this.navNodeInfoResource.get(resourceKeyList(children)); + const folderDuplicates = nodes.filter(node => ( + node.nodeType === NAV_NODE_TYPE_FOLDER + && ( + childrenNode.some(child => child?.nodeType === NAV_NODE_TYPE_FOLDER && child.name === node.name) + || nodes.some(child => ( + child.nodeType === NAV_NODE_TYPE_FOLDER + && child.name === node.name + && child.id !== node.id + )) + ) + )); + + if (folderDuplicates.length > 0) { + this.notificationService.logError({ + title: 'connections_public_connection_folder_move_failed', + message: this.localizationService.translate( + 'connections_public_connection_folder_move_duplication', + undefined, + { name: folderDuplicates.map(node => `"${node.name}"`).join(', ') } + ), + }); + return; + } + try { await this.navTreeResource.moveTo(resourceKeyList(nodeIdList), targetNode.id); const connections = nodeIdList diff --git a/webapp/packages/plugin-connections/src/locales/en.ts b/webapp/packages/plugin-connections/src/locales/en.ts index 7586edd8c0..35e4d191d4 100644 --- a/webapp/packages/plugin-connections/src/locales/en.ts +++ b/webapp/packages/plugin-connections/src/locales/en.ts @@ -6,5 +6,6 @@ export default [ ['connections_public_connection_edit_reconnect_message', 'Connection has been updated. Do you want to reconnect?'], ['connections_public_connection_edit_reconnect_failed', 'Failed to reconnect'], ['connections_public_connection_folder_move_failed', 'Failed to move to folder'], + ['connections_public_connection_folder_move_duplication', 'Target folder or selected folders contains folder with the same name ({args.name})'], ['connections_public_connection_folder_delete_confirmation', 'You\'re going to delete "{args.name}". Connections won\'t be deleted. Are you sure?'], ]; diff --git a/webapp/packages/plugin-connections/src/locales/it.ts b/webapp/packages/plugin-connections/src/locales/it.ts index fc4b70d326..a250f28a29 100644 --- a/webapp/packages/plugin-connections/src/locales/it.ts +++ b/webapp/packages/plugin-connections/src/locales/it.ts @@ -6,5 +6,6 @@ export default [ ['connections_public_connection_edit_reconnect_message', 'Connection has been updated. Do you want to reconnect?'], ['connections_public_connection_edit_reconnect_failed', 'Failed to reconnect'], ['connections_public_connection_folder_move_failed', 'Failed to move to folder'], + ['connections_public_connection_folder_move_duplication', 'Target folder or selected folders contains folder with the same name ({args.name})'], ['connections_public_connection_folder_delete_confirmation', 'You\'re going to delete "{args.name}". Connections won\'t be deleted. Are you sure?'], ]; diff --git a/webapp/packages/plugin-connections/src/locales/ru.ts b/webapp/packages/plugin-connections/src/locales/ru.ts index 1976281521..7595609214 100644 --- a/webapp/packages/plugin-connections/src/locales/ru.ts +++ b/webapp/packages/plugin-connections/src/locales/ru.ts @@ -6,5 +6,6 @@ export default [ ['connections_public_connection_edit_reconnect_message', 'Подключение было обновлено. Вы хотите переподключиться?'], ['connections_public_connection_edit_reconnect_failed', 'Не удалось переподключиться'], ['connections_public_connection_folder_move_failed', 'Ошибка перемещения в папку'], + ['connections_public_connection_folder_move_duplication', 'Выбранные папки или папка назначения содержит папки с таким же названием ({args.name})'], ['connections_public_connection_folder_delete_confirmation', 'Вы удаляете "{args.name}". Подключения не будут удалены. Вы уверены?'], ]; diff --git a/webapp/packages/plugin-connections/src/locales/zh.ts b/webapp/packages/plugin-connections/src/locales/zh.ts index 5576999d6b..50053ac465 100644 --- a/webapp/packages/plugin-connections/src/locales/zh.ts +++ b/webapp/packages/plugin-connections/src/locales/zh.ts @@ -6,5 +6,6 @@ export default [ ['connections_public_connection_edit_reconnect_message', '连接已更新。您想重新连接吗?'], ['connections_public_connection_edit_reconnect_failed', '重新连接失败'], ['connections_public_connection_folder_move_failed', 'Failed to move to folder'], + ['connections_public_connection_folder_move_duplication', 'Target folder or selected folders contains folder with the same name ({args.name})'], ['connections_public_connection_folder_delete_confirmation', 'You\'re going to delete "{args.name}". Connections won\'t be deleted. Are you sure?'], ]; From 347030ec664943196b22e8c7b45ce7663555ff07 Mon Sep 17 00:00:00 2001 From: yagudin10 Date: Fri, 26 Aug 2022 12:03:51 +0300 Subject: [PATCH 5/5] CB-2302 code style fix --- .../cloudbeaver/service/navigator/impl/WebServiceNavigator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/navigator/impl/WebServiceNavigator.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/navigator/impl/WebServiceNavigator.java index 7205a3d409..5b01f9b60d 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/navigator/impl/WebServiceNavigator.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/navigator/impl/WebServiceNavigator.java @@ -477,7 +477,7 @@ public class WebServiceNavigator implements DBWServiceNavigator { .map(DBPDataSourceFolder::getName) .collect(Collectors.toList()); if (siblings.contains(node.getName())) { - throw new DBWebException("Node " + folderNodePath + " contains folder with name '" + node.getName() +"'"); + throw new DBWebException("Node " + folderNodePath + " contains folder with name '" + node.getName() + "'"); } } ((DBNLocalFolder) node).getFolder().setParent(folder);