diff --git a/deploy/build.bat b/deploy/build.bat index 4ba5e94ba0..1e84e13607 100644 --- a/deploy/build.bat +++ b/deploy/build.bat @@ -20,7 +20,6 @@ mkdir cloudbeaver\web echo Pull cloudbeaver platform cd ..\.. -rem IF NOT EXIST react-data-grid git clone https://github.com/dbeaver/react-data-grid.git echo Pull dbeaver platform @@ -54,16 +53,9 @@ echo Build static content cd ..\ -rem cd ..\react-data-grid -rem call npm i -rem call npm run build -rem call npm run build:types -rem call yarn link - cd ..\cloudbeaver\webapp call yarn -rem call yarn link "react-data-grid" call lerna bootstrap call lerna run build --no-bail --stream --scope=@cloudbeaver/product-default &::-- -- --env source-map diff --git a/deploy/build.sh b/deploy/build.sh index 9e14523585..cabe4319f5 100755 --- a/deploy/build.sh +++ b/deploy/build.sh @@ -18,7 +18,6 @@ mkdir ./cloudbeaver/web echo "Pull cloudbeaver platform" cd ../.. -#[ ! -d react-data-grid ] && git clone https://github.com/dbeaver/react-data-grid.git echo "Pull dbeaver platform" [ ! -d dbeaver ] && git clone https://github.com/dbeaver/dbeaver.git @@ -54,16 +53,9 @@ echo "Build static content" cd ../ -#cd ../react-data-grid -#npm i -#npm run build -#npm run build:types -#yarn link - cd ../cloudbeaver/webapp yarn -#yarn link "react-data-grid" lerna run bootstrap lerna run build --no-bail --stream --scope=@cloudbeaver/product-default #-- -- --env source-map if [[ "$?" -ne 0 ]] ; then diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/events/CBEventConstants.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/events/CBEventConstants.java index 35aabe6f19..cd57965ad9 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/events/CBEventConstants.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/events/CBEventConstants.java @@ -21,4 +21,6 @@ package io.cloudbeaver.events; */ public interface CBEventConstants { String CLOUDBEAVER_CONFIG_CHANGED = "cb_config_changed"; + String CLOUDBEAVER_DATASOURCE_UPDATED = "cb_datasource_updated"; + String CLOUDBEAVER_RM_RESOURCE_UPDATED = "cb_rm_resource_updated"; } diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/app/BaseWebApplication.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/app/BaseWebApplication.java index 4fca09f7d0..c3f4e64e59 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/app/BaseWebApplication.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/app/BaseWebApplication.java @@ -18,6 +18,7 @@ package io.cloudbeaver.model.app; import io.cloudbeaver.DataSourceFilter; import io.cloudbeaver.VirtualProjectImpl; +import io.cloudbeaver.events.CBEventController; import io.cloudbeaver.model.log.SLF4JLogHandler; import io.cloudbeaver.model.rm.local.LocalResourceController; import io.cloudbeaver.server.WebWorkspace; @@ -198,4 +199,9 @@ public abstract class BaseWebApplication extends BaseApplicationImpl implements throw new DBException("Error during generation instance id generation", e); } } + + @Override + public CBEventController getEventController() { + return null; + } } diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/app/WebApplication.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/app/WebApplication.java index 98fd6b91b0..a4db2db804 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/app/WebApplication.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/app/WebApplication.java @@ -18,6 +18,7 @@ package io.cloudbeaver.model.app; import io.cloudbeaver.DataSourceFilter; import io.cloudbeaver.VirtualProjectImpl; +import io.cloudbeaver.events.CBEventController; import org.jkiss.code.NotNull; import org.jkiss.dbeaver.DBException; import org.jkiss.dbeaver.model.app.DBPApplication; @@ -76,6 +77,8 @@ public interface WebApplication extends DBPApplication { String getApplicationInstanceId() throws DBException; + CBEventController getEventController(); + /** * Port this server listens on */ diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/DBNAbstractResourceManagerNode.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/DBNAbstractResourceManagerNode.java index bc1fccd4e2..7792ca1aad 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/DBNAbstractResourceManagerNode.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/DBNAbstractResourceManagerNode.java @@ -1,9 +1,11 @@ package io.cloudbeaver.model.rm; import org.jkiss.code.NotNull; +import org.jkiss.dbeaver.DBException; import org.jkiss.dbeaver.model.DBPObjectWithDetails; import org.jkiss.dbeaver.model.navigator.DBNNode; import org.jkiss.dbeaver.model.rm.RMResource; +import org.jkiss.dbeaver.model.runtime.VoidProgressMonitor; import org.jkiss.utils.ArrayUtils; import java.util.Queue; @@ -31,4 +33,29 @@ public abstract class DBNAbstractResourceManagerNode extends DBNNode implements node.removeChildResourceNode(resourcePath); } } + + public void addChildResourceNode(@NotNull Queue resourcePath) { + if (children == null) { + // initialize children + try { + getChildren(new VoidProgressMonitor()); + } catch (DBException e) { + return; + } + } + if (resourcePath.isEmpty()) { + return; + } + var expectedResource = resourcePath.poll(); + var node = RMNavigatorUtils.findResourceNode(children, expectedResource); + if (node == null) { // we are in expected parent node + DBNResourceManagerResource newResourceNode = new DBNResourceManagerResource(this, expectedResource); + children = ArrayUtils.add(DBNResourceManagerResource.class, children, newResourceNode); + return; + } + if (resourcePath.size() > 0) { + node.addChildResourceNode(resourcePath); + } + + } } diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/DBNResourceManagerRoot.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/DBNResourceManagerRoot.java index de59ccc520..b8a38cef62 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/DBNResourceManagerRoot.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/DBNResourceManagerRoot.java @@ -18,6 +18,7 @@ package io.cloudbeaver.model.rm; import io.cloudbeaver.model.session.WebSession; +import org.jkiss.code.NotNull; import org.jkiss.dbeaver.DBException; import org.jkiss.dbeaver.model.DBPHiddenObject; import org.jkiss.dbeaver.model.DBPImage; @@ -28,10 +29,7 @@ import org.jkiss.dbeaver.model.navigator.DBNProject; import org.jkiss.dbeaver.model.rm.*; import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor; -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; +import java.util.*; public class DBNResourceManagerRoot extends DBNNode implements DBPHiddenObject, RMEventListener { @@ -126,13 +124,26 @@ public class DBNResourceManagerRoot extends DBNNode implements DBPHiddenObject, case RESOURCE_DELETE: deleteResourceNode(event.getProject(), event.getResourceTree()); break; + case RESOURCE_ADD: + addResourceNode(event.getProject(), event.getResourceTree()); + break; } } private void deleteResourceNode(RMProject project, List resourcePath) { - var projectNode = Arrays.stream(projects) - .filter(rmProjectNode -> rmProjectNode.getProject().getId().equals(project.getId())) - .findFirst(); + var projectNode = getProjectNode(project); projectNode.ifPresent(dbnResourceManagerProject -> dbnResourceManagerProject.removeChildResourceNode(new ArrayDeque<>(resourcePath))); } + + @NotNull + private Optional getProjectNode(RMProject project) { + return Arrays.stream(projects) + .filter(rmProjectNode -> rmProjectNode.getProject().getId().equals(project.getId())) + .findFirst(); + } + + private void addResourceNode(RMProject project, List resourcePath) { + var projectNode = getProjectNode(project); + projectNode.ifPresent(dbnResourceManagerProject -> dbnResourceManagerProject.addChildResourceNode(new ArrayDeque<>(resourcePath))); + } } diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/LocalResourceController.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/LocalResourceController.java index d71d16502d..7e9a8f9fa0 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/LocalResourceController.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/LocalResourceController.java @@ -293,7 +293,11 @@ public class LocalResourceController implements RMController { } @Override - public void saveProjectDataSources(@NotNull String projectId, @NotNull String configuration) throws DBException { + public void saveProjectDataSources( + @NotNull String projectId, + @NotNull String configuration, + @Nullable List dataSourceIds + ) throws DBException { final DBPProject project = getProjectMetadata(projectId); final DBPDataSourceRegistry registry = project.getDataSourceRegistry(); final DBPDataSourceConfigurationStorage storage = new DataSourceMemoryStorage(configuration.getBytes(StandardCharsets.UTF_8)); @@ -393,6 +397,7 @@ public class LocalResourceController implements RMController { } catch (IOException e) { throw new DBException("Error creating resource '" + resourcePath + "'", e); } + fireRmResourceAddEvent(projectId, resourcePath); return DEFAULT_CHANGE_ID; } @@ -403,6 +408,7 @@ public class LocalResourceController implements RMController { @NotNull String newResourcePath ) throws DBException { Path oldTargetPath = getTargetPath(projectId, oldResourcePath); + List rmOldResourcePath = makeResourcePath(projectId, oldTargetPath, false); if (!Files.exists(oldTargetPath)) { throw new DBException("Resource '" + oldTargetPath + "' doesn't exists"); } @@ -413,6 +419,8 @@ public class LocalResourceController implements RMController { } catch (IOException e) { throw new DBException("Error moving resource '" + oldResourcePath + "'", e); } + fireRmResourceDeleteEvent(projectId, rmOldResourcePath); + fireRmResourceAddEvent(projectId, newResourcePath); return DEFAULT_CHANGE_ID; } @@ -437,12 +445,7 @@ public class LocalResourceController implements RMController { VirtualProjectImpl project = getProjectMetadata(projectId); project.resetResourceProperties(resourcePath); - RMEventManager.fireEvent( - new RMEvent(RMEvent.Action.RESOURCE_DELETE, - makeProjectFromId(projectId, false), - rmResourcePath - ) - ); + fireRmResourceDeleteEvent(projectId, rmResourcePath); } @Override @@ -496,7 +499,9 @@ public class LocalResourceController implements RMController { } catch (IOException e) { throw new DBException("Error reading resource '" + resourcePath + "'", e); } - + if (!forceOverwrite) { + fireRmResourceAddEvent(projectId, resourcePath); + } return DEFAULT_CHANGE_ID; } @@ -703,10 +708,26 @@ public class LocalResourceController implements RMController { return getProjectPath(projectId).toAbsolutePath().relativize(path).toString().replace('\\', IPath.SEPARATOR); } + private void fireRmResourceAddEvent(@NotNull String projectId, @NotNull String resourcePath) throws DBException { + RMEventManager.fireEvent( + new RMEvent(RMEvent.Action.RESOURCE_ADD, + getProject(projectId, false, false), + Arrays.asList(getResourcePath(projectId, resourcePath))) + ); + } + + private void fireRmResourceDeleteEvent(@NotNull String projectId, @NotNull List resourcePath) throws DBException { + RMEventManager.fireEvent( + new RMEvent(RMEvent.Action.RESOURCE_DELETE, + makeProjectFromId(projectId, false), + resourcePath + ) + ); + } + public static Builder builder(SMCredentialsProvider credentialsProvider, SMController smController) { return new Builder(credentialsProvider, smController); } - public static final class Builder { private final SMCredentialsProvider credentialsProvider; private final SMController smController; diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/utils/WebAppUtils.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/utils/WebAppUtils.java index b9af9a72cc..9d3fb72cb2 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/utils/WebAppUtils.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/utils/WebAppUtils.java @@ -17,6 +17,8 @@ package io.cloudbeaver.utils; import io.cloudbeaver.auth.NoAuthCredentialsProvider; +import io.cloudbeaver.events.CBEvent; +import io.cloudbeaver.events.CBEventConstants; import io.cloudbeaver.model.app.WebApplication; import io.cloudbeaver.model.app.WebAuthApplication; import org.jkiss.code.NotNull; @@ -34,10 +36,7 @@ import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.nio.file.Path; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; +import java.util.*; public class WebAppUtils { private static final Log log = Log.getLog(WebAppUtils.class); @@ -205,4 +204,27 @@ public class WebAppUtils { String globalConfigurationName = getWebApplication().getDefaultProjectName(); return RMProjectType.GLOBAL.getPrefix() + "_" + globalConfigurationName; } + + public static void addDataSourceUpdatedEvent(DBPProject project, String datasourceId) { + if (project == null) { + return; + } + getWebApplication().getEventController().addEvent( + new CBEvent( + CBEventConstants.CLOUDBEAVER_DATASOURCE_UPDATED, + Map.of("projectId", project.getId(), "dataSourceIds", new String[] {datasourceId}) + ) + ); + } + + public static void addRmResourceUpdatedEvent(String eventId, String projectId, String resourcePath) { + getWebApplication().getEventController().addEvent( + new CBEvent(eventId, + Map.of( + "projectId", projectId, + "resourcePath", resourcePath + ) + ) + ); + } } diff --git a/server/bundles/io.cloudbeaver.server/plugin.xml b/server/bundles/io.cloudbeaver.server/plugin.xml index 8ad68efdc3..ec3704d572 100644 --- a/server/bundles/io.cloudbeaver.server/plugin.xml +++ b/server/bundles/io.cloudbeaver.server/plugin.xml @@ -48,6 +48,8 @@ + \ + diff --git a/server/bundles/io.cloudbeaver.server/schema/service.core.graphqls b/server/bundles/io.cloudbeaver.server/schema/service.core.graphqls index 54af8ea12c..46c72229fa 100644 --- a/server/bundles/io.cloudbeaver.server/schema/service.core.graphqls +++ b/server/bundles/io.cloudbeaver.server/schema/service.core.graphqls @@ -388,7 +388,9 @@ type LogEntry { } enum CBEventType { - cb_config_changed + cb_config_changed, + cb_datasource_updated, + cb_rm_resource_updated } type CBEvent { diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBApplication.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBApplication.java index 1f879248e4..6444fa45d1 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBApplication.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBApplication.java @@ -1056,4 +1056,9 @@ public class CBApplication extends BaseWebApplication implements WebAuthApplicat newConfig.put(key, newConfigValue); } } + + @Override + public CBEventController getEventController() { + return eventController; + } } diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/events/CBDataSourceUpdatedEventHandlerImpl.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/events/CBDataSourceUpdatedEventHandlerImpl.java new file mode 100644 index 0000000000..7c3b770698 --- /dev/null +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/events/CBDataSourceUpdatedEventHandlerImpl.java @@ -0,0 +1,47 @@ +/* + * DBeaver - Universal Database Manager + * Copyright (C) 2010-2022 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.cloudbeaver.server.events; + +import io.cloudbeaver.VirtualProjectImpl; +import io.cloudbeaver.events.CBEvent; +import io.cloudbeaver.events.CBEventConstants; +import io.cloudbeaver.model.session.WebSession; +import org.jkiss.code.NotNull; +import org.jkiss.dbeaver.model.data.json.JSONUtils; + +/** + * Notify all active user session that datasource has been updated + */ +public class CBDataSourceUpdatedEventHandlerImpl extends CBProjectUpdatedEventHandler { + @NotNull + @Override + public String getSupportedEventType() { + return CBEventConstants.CLOUDBEAVER_DATASOURCE_UPDATED; + } + + @Override + protected void updateSessionData(WebSession activeUserSession, CBEvent event) { + String projectId = JSONUtils.getString(event.getEventData(), "projectId"); + VirtualProjectImpl project = activeUserSession.getProjectById(projectId); + if (project == null || !project.getRmProject().isShared()) { + return; + } + activeUserSession.refreshUserData(); + activeUserSession.addSessionEvent(event); + } +} + diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/events/CBProjectUpdatedEventHandler.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/events/CBProjectUpdatedEventHandler.java new file mode 100644 index 0000000000..9e86d159e2 --- /dev/null +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/events/CBProjectUpdatedEventHandler.java @@ -0,0 +1,48 @@ +/* + * DBeaver - Universal Database Manager + * Copyright (C) 2010-2022 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.cloudbeaver.server.events; + +import io.cloudbeaver.events.CBEvent; +import io.cloudbeaver.events.CBEventHandler; +import io.cloudbeaver.model.session.WebSession; +import io.cloudbeaver.server.CBPlatform; +import org.jkiss.code.NotNull; +import org.jkiss.dbeaver.Log; + +import java.util.Collection; + +/** + * Notify all active user session that project has been updated + */ +public abstract class CBProjectUpdatedEventHandler implements CBEventHandler { + private static final Log log = Log.getLog(CBProjectUpdatedEventHandler.class); + + @NotNull + @Override + public abstract String getSupportedEventType(); + + @Override + public void handleEvent(@NotNull CBEvent event) { + log.debug(getSupportedEventType() + " event handled"); + Collection allSessions = CBPlatform.getInstance().getSessionManager().getAllActiveSessions(); + for (WebSession activeUserSession : allSessions) { + updateSessionData(activeUserSession, event); + } + } + + protected abstract void updateSessionData(WebSession activeUserSession, CBEvent event); +} diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/events/CBRmResourceUpdatedEventHandlerImpl.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/events/CBRmResourceUpdatedEventHandlerImpl.java new file mode 100644 index 0000000000..9800c97709 --- /dev/null +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/events/CBRmResourceUpdatedEventHandlerImpl.java @@ -0,0 +1,77 @@ +/* + * DBeaver - Universal Database Manager + * Copyright (C) 2010-2022 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.cloudbeaver.server.events; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import io.cloudbeaver.VirtualProjectImpl; +import io.cloudbeaver.events.CBEvent; +import io.cloudbeaver.events.CBEventConstants; +import io.cloudbeaver.model.session.WebSession; +import org.jkiss.code.NotNull; +import org.jkiss.dbeaver.DBException; +import org.jkiss.dbeaver.model.app.DBPProject; +import org.jkiss.dbeaver.model.data.json.JSONUtils; +import org.jkiss.dbeaver.model.rm.*; + +import java.util.Arrays; +import java.util.List; + +/** + * Notify all active user session that rm resource has been updated + */ +public class CBRmResourceUpdatedEventHandlerImpl extends CBProjectUpdatedEventHandler { + + private static final Gson gson = new GsonBuilder().create(); + + @NotNull + @Override + public String getSupportedEventType() { + return CBEventConstants.CLOUDBEAVER_RM_RESOURCE_UPDATED; + } + + @Override + protected void updateSessionData(WebSession activeUserSession, CBEvent event) { + String projectId = JSONUtils.getString(event.getEventData(), "projectId"); + String eventType = JSONUtils.getString(event.getEventData(), "eventType"); + String resourcePath = JSONUtils.getString(event.getEventData(), "resourcePath"); + RMResource[] resourceParsedPath = gson.fromJson( + gson.toJson(JSONUtils.getObjectList(event.getEventData(), "resourceParsedPath")), RMResource[].class); + if (projectId == null || eventType == null || resourcePath == null) { + return; + } + VirtualProjectImpl project = activeUserSession.getProjectById(projectId); + if (project == null) { + return; + } + List rmResourcePath = Arrays.asList(resourceParsedPath); + if (eventType.equals("TYPE_CREATE")) { + RMEventManager.fireEvent( + new RMEvent(RMEvent.Action.RESOURCE_ADD, + project.getRmProject(), + rmResourcePath) + ); + } else if (eventType.equals("TYPE_DELETE")) { + RMEventManager.fireEvent( + new RMEvent(RMEvent.Action.RESOURCE_DELETE, + project.getRmProject(), + rmResourcePath) + ); + } + activeUserSession.addSessionEvent(event); + } +} diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java index 7889510c17..5b46964097 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java @@ -29,6 +29,7 @@ import io.cloudbeaver.server.CBApplication; import io.cloudbeaver.server.CBPlatform; import io.cloudbeaver.service.core.DBWServiceCore; import io.cloudbeaver.service.security.SMUtils; +import io.cloudbeaver.utils.WebAppUtils; import io.cloudbeaver.utils.WebConnectionFolderUtils; import io.cloudbeaver.utils.WebDataSourceUtils; import org.jkiss.code.NotNull; @@ -52,7 +53,6 @@ import org.jkiss.dbeaver.model.net.DBWTunnel; import org.jkiss.dbeaver.model.net.ssh.SSHImplementation; import org.jkiss.dbeaver.model.rm.RMProjectType; import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor; -import org.jkiss.dbeaver.registry.BaseProjectImpl; import org.jkiss.dbeaver.registry.DataSourceDescriptor; import org.jkiss.dbeaver.registry.DataSourceProviderRegistry; import org.jkiss.dbeaver.registry.network.NetworkHandlerDescriptor; @@ -402,6 +402,7 @@ public class WebServiceCore implements DBWServiceCore { WebConnectionInfo connectionInfo = new WebConnectionInfo(webSession, newDataSource); webSession.addConnection(connectionInfo); webSession.addInfoMessage("New connection was created - " + WebServiceUtils.getConnectionContainerInfo(newDataSource)); + WebAppUtils.addDataSourceUpdatedEvent(webSession.getProjectById(projectId), connectionInfo.getId()); return connectionInfo; } @@ -439,7 +440,7 @@ public class WebServiceCore implements DBWServiceCore { } catch (DBException e) { throw new DBWebException("Failed to update connection", e); } - + WebAppUtils.addDataSourceUpdatedEvent(webSession.getProjectById(projectId), connectionInfo.getId()); return connectionInfo; } @@ -453,6 +454,7 @@ public class WebServiceCore implements DBWServiceCore { webSession.addInfoMessage("Delete connection - " + WebServiceUtils.getConnectionContainerInfo(connectionInfo.getDataSourceContainer())); closeAndDeleteConnection(webSession, projectId, connectionId, true); + WebAppUtils.addDataSourceUpdatedEvent(webSession.getProjectById(projectId), connectionId); return true; } @@ -489,6 +491,8 @@ public class WebServiceCore implements DBWServiceCore { WebConnectionInfo connectionInfo = new WebConnectionInfo(webSession, newDataSource); webSession.addConnection(connectionInfo); + WebAppUtils.addDataSourceUpdatedEvent(webSession.getProjectById(projectId), connectionInfo.getId()); + return connectionInfo; } @@ -529,6 +533,7 @@ public class WebServiceCore implements DBWServiceCore { WebConnectionInfo connectionInfo = new WebConnectionInfo(webSession, newDataSource); dataSourceRegistry.checkForErrors(); webSession.addConnection(connectionInfo); + WebAppUtils.addDataSourceUpdatedEvent(webSession.getProjectById(projectId), connectionInfo.getId()); return connectionInfo; } catch (DBException e) { throw new DBWebException("Error copying connection", e); @@ -707,7 +712,7 @@ public class WebServiceCore implements DBWServiceCore { DBPDataSourceFolder newFolder = WebServiceUtils.createFolder(parentNode, folderName, sessionRegistry); WebConnectionFolderInfo folderInfo = new WebConnectionFolderInfo(session, newFolder); WebServiceUtils.updateConfigAndRefreshDatabases(session, projectId); - + WebAppUtils.addDataSourceUpdatedEvent(session.getProjectById(projectId), folderInfo.getId()); return folderInfo; } catch (DBException e) { throw new DBWebException(e.getMessage(), e); @@ -725,6 +730,7 @@ public class WebServiceCore implements DBWServiceCore { WebConnectionFolderInfo folderInfo = WebConnectionFolderUtils.getFolderInfo(session, projectId, folderPath); folderInfo.getDataSourceFolder().setName(newName); WebServiceUtils.updateConfigAndRefreshDatabases(session, projectId); + WebAppUtils.addDataSourceUpdatedEvent(session.getProjectById(projectId), folderInfo.getId()); return folderInfo; } @@ -742,6 +748,7 @@ public class WebServiceCore implements DBWServiceCore { DBPDataSourceRegistry sessionRegistry = session.getProjectById(projectId).getDataSourceRegistry(); sessionRegistry.removeFolder(folderInfo.getDataSourceFolder(), false); WebServiceUtils.updateConfigAndRefreshDatabases(session, projectId); + WebAppUtils.addDataSourceUpdatedEvent(session.getProjectById(projectId), folderInfo.getId()); } catch (DBException e) { throw new DBWebException(e.getMessage(), e); } @@ -756,6 +763,7 @@ public class WebServiceCore implements DBWServiceCore { DataSourceDescriptor dataSourceDescriptor = ((DataSourceDescriptor)connectionInfo.getDataSourceContainer()); dataSourceDescriptor.setNavigatorSettings(settings); dataSourceDescriptor.persistConfiguration(); + WebAppUtils.addDataSourceUpdatedEvent(webSession.getProjectById(projectId), id); return connectionInfo; } 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 93d6198b9c..8ec9ccb4cb 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 @@ -19,6 +19,7 @@ package io.cloudbeaver.service.navigator.impl; import io.cloudbeaver.DBWebException; import io.cloudbeaver.WebServiceUtils; +import io.cloudbeaver.events.CBEventConstants; import io.cloudbeaver.model.WebCommandContext; import io.cloudbeaver.model.WebConnectionInfo; import io.cloudbeaver.model.rm.DBNAbstractResourceManagerNode; @@ -30,6 +31,7 @@ import io.cloudbeaver.service.navigator.WebCatalog; import io.cloudbeaver.service.navigator.WebNavigatorNodeInfo; import io.cloudbeaver.service.navigator.WebStructContainers; import io.cloudbeaver.service.security.SMUtils; +import io.cloudbeaver.utils.WebAppUtils; import io.cloudbeaver.utils.WebConnectionFolderUtils; import org.jkiss.code.NotNull; import org.jkiss.code.Nullable; @@ -469,6 +471,7 @@ public class WebServiceNavigator implements DBWServiceNavigator { ((DBNDataSource) node).moveToFolder(folderNode.getOwnerProject(), folder); node.getOwnerProject().getDataSourceRegistry().updateDataSource( ((DBNDataSource) node).getDataSourceContainer()); + WebAppUtils.addDataSourceUpdatedEvent(node.getOwnerProject(), ((DBNDataSource) node).getDataSourceContainer().getId()); } else if (node instanceof DBNLocalFolder) { DBPDataSourceFolder folder = WebConnectionFolderUtils.getParentFolder(folderNode); if (folder != null) { @@ -481,6 +484,7 @@ public class WebServiceNavigator implements DBWServiceNavigator { } ((DBNLocalFolder) node).getFolder().setParent(folder); WebServiceUtils.updateConfigAndRefreshDatabases(session, node.getOwnerProject().getId()); + WebAppUtils.addDataSourceUpdatedEvent(node.getOwnerProject(), ((DBNLocalFolder) node).getFolder().getFolderPath()); } else if (node instanceof DBNResourceManagerResource) { boolean rmNewNode = folderNode instanceof DBNAbstractResourceManagerNode; DBNResourceManagerResource rmOldNode = (DBNResourceManagerResource) node; @@ -496,6 +500,7 @@ public class WebServiceNavigator implements DBWServiceNavigator { } String resourcePath = rmOldNode.getResourceFolder(); session.getRmController().moveResource(projectId, resourcePath, newPath); + WebAppUtils.addRmResourceUpdatedEvent(CBEventConstants.CLOUDBEAVER_RM_RESOURCE_UPDATED, projectId, resourcePath); } else { throw new DBWebException("Navigator node '" + path + "' is not a data source node"); } diff --git a/server/bundles/io.cloudbeaver.service.rm/src/io/cloudbeaver/service/rm/impl/WebServiceRM.java b/server/bundles/io.cloudbeaver.service.rm/src/io/cloudbeaver/service/rm/impl/WebServiceRM.java index 5b8864e406..3152131d7f 100644 --- a/server/bundles/io.cloudbeaver.service.rm/src/io/cloudbeaver/service/rm/impl/WebServiceRM.java +++ b/server/bundles/io.cloudbeaver.service.rm/src/io/cloudbeaver/service/rm/impl/WebServiceRM.java @@ -17,12 +17,14 @@ package io.cloudbeaver.service.rm.impl; import io.cloudbeaver.DBWebException; +import io.cloudbeaver.events.CBEventConstants; import io.cloudbeaver.model.session.WebSession; import io.cloudbeaver.service.admin.AdminPermissionInfo; import io.cloudbeaver.service.rm.DBWServiceRM; import io.cloudbeaver.service.rm.model.RMProjectPermissions; import io.cloudbeaver.service.rm.model.RMSubjectProjectPermissions; import io.cloudbeaver.service.security.SMUtils; +import io.cloudbeaver.utils.WebAppUtils; import org.jkiss.code.NotNull; import org.jkiss.code.Nullable; import org.jkiss.dbeaver.DBException; @@ -137,7 +139,9 @@ public class WebServiceRM implements DBWServiceRM { ) throws DBException { checkIsRmEnabled(webSession); try { - return getResourceController(webSession).createResource(projectId, resourcePath, isFolder); + String result = getResourceController(webSession).createResource(projectId, resourcePath, isFolder); + WebAppUtils.addRmResourceUpdatedEvent(CBEventConstants.CLOUDBEAVER_RM_RESOURCE_UPDATED, projectId, resourcePath); + return result; } catch (Exception e) { throw new DBWebException("Error creating resource " + resourcePath, e); } @@ -151,6 +155,7 @@ public class WebServiceRM implements DBWServiceRM { checkIsRmEnabled(webSession); try { getResourceController(webSession).deleteResource(projectId, resourcePath, false); + WebAppUtils.addRmResourceUpdatedEvent(CBEventConstants.CLOUDBEAVER_RM_RESOURCE_UPDATED, projectId, resourcePath); return true; } catch (Exception e) { throw new DBWebException("Error deleting resource " + resourcePath, e); diff --git a/server/drivers/postgresql/pom.xml b/server/drivers/postgresql/pom.xml index fda1b17e9c..79f7c1c225 100644 --- a/server/drivers/postgresql/pom.xml +++ b/server/drivers/postgresql/pom.xml @@ -18,7 +18,7 @@ org.postgresql postgresql - 42.3.3 + 42.5.0 net.postgis diff --git a/webapp/packages/core-sdk/src/queries/session/serverConfig.gql b/webapp/packages/core-sdk/src/queries/session/serverConfig.gql index 938c97d35e..b440e3dfbc 100644 --- a/webapp/packages/core-sdk/src/queries/session/serverConfig.gql +++ b/webapp/packages/core-sdk/src/queries/session/serverConfig.gql @@ -25,6 +25,7 @@ query serverConfig { configurationMode developmentMode redirectOnFederatedAuth + distributed enabledFeatures enabledAuthProviders diff --git a/webapp/packages/core-sdk/src/sdk.ts b/webapp/packages/core-sdk/src/sdk.ts index 7777d0419b..a1a8a8c36f 100644 --- a/webapp/packages/core-sdk/src/sdk.ts +++ b/webapp/packages/core-sdk/src/sdk.ts @@ -1551,6 +1551,7 @@ export interface ServerConfig { defaultNavigatorSettings: NavigatorSettings; developmentMode?: Maybe; disabledDrivers: Array; + distributed: Scalars['Boolean']; enabledAuthProviders: Array; enabledFeatures: Array; hostName: Scalars['String']; @@ -2723,7 +2724,7 @@ export type ReadSessionLogQuery = { log: Array<{ time?: any, type: string, messa export type ServerConfigQueryVariables = Exact<{ [key: string]: never; }>; -export type ServerConfigQuery = { serverConfig: { name: string, version: string, workspaceId: string, serverURL: string, rootURI: string, hostName: string, productConfiguration: any, supportsCustomConnections?: boolean, supportsConnectionBrowser?: boolean, supportsWorkspaces?: boolean, sessionExpireTime?: number, anonymousAccessEnabled?: boolean, adminCredentialsSaveEnabled?: boolean, publicCredentialsSaveEnabled?: boolean, resourceManagerEnabled?: boolean, licenseRequired: boolean, licenseValid: boolean, configurationMode?: boolean, developmentMode?: boolean, redirectOnFederatedAuth?: boolean, enabledFeatures: Array, enabledAuthProviders: Array, resourceQuotas: any, disabledDrivers: Array, supportedLanguages: Array<{ isoCode: string, displayName?: string, nativeName?: string }>, defaultNavigatorSettings: { showSystemObjects: boolean, showUtilityObjects: boolean, showOnlyEntities: boolean, mergeEntities: boolean, hideFolders: boolean, hideSchemas: boolean, hideVirtualModel: boolean }, productInfo: { id: string, version: string, latestVersionInfo?: string, name: string, description?: string, buildTime: string, releaseTime: string, licenseInfo?: string } } }; +export type ServerConfigQuery = { serverConfig: { name: string, version: string, workspaceId: string, serverURL: string, rootURI: string, hostName: string, productConfiguration: any, supportsCustomConnections?: boolean, supportsConnectionBrowser?: boolean, supportsWorkspaces?: boolean, sessionExpireTime?: number, anonymousAccessEnabled?: boolean, adminCredentialsSaveEnabled?: boolean, publicCredentialsSaveEnabled?: boolean, resourceManagerEnabled?: boolean, licenseRequired: boolean, licenseValid: boolean, configurationMode?: boolean, developmentMode?: boolean, redirectOnFederatedAuth?: boolean, distributed: boolean, enabledFeatures: Array, enabledAuthProviders: Array, resourceQuotas: any, disabledDrivers: Array, supportedLanguages: Array<{ isoCode: string, displayName?: string, nativeName?: string }>, defaultNavigatorSettings: { showSystemObjects: boolean, showUtilityObjects: boolean, showOnlyEntities: boolean, mergeEntities: boolean, hideFolders: boolean, hideSchemas: boolean, hideVirtualModel: boolean }, productInfo: { id: string, version: string, latestVersionInfo?: string, name: string, description?: string, buildTime: string, releaseTime: string, licenseInfo?: string } } }; export type SessionPermissionsQueryVariables = Exact<{ [key: string]: never; }>; @@ -4352,6 +4353,7 @@ export const ServerConfigDocument = ` configurationMode developmentMode redirectOnFederatedAuth + distributed enabledFeatures enabledAuthProviders supportedLanguages { diff --git a/webapp/packages/plugin-connections/src/ConnectionForm/Options/ConnectionOptionsTabService.ts b/webapp/packages/plugin-connections/src/ConnectionForm/Options/ConnectionOptionsTabService.ts index 7b304fcc06..d09e98bcf4 100644 --- a/webapp/packages/plugin-connections/src/ConnectionForm/Options/ConnectionOptionsTabService.ts +++ b/webapp/packages/plugin-connections/src/ConnectionForm/Options/ConnectionOptionsTabService.ts @@ -15,7 +15,7 @@ import { Bootstrap, injectable } from '@cloudbeaver/core-di'; import type { IExecutionContextProvider } from '@cloudbeaver/core-executor'; import { LocalizationService } from '@cloudbeaver/core-localization'; import { ProjectInfoResource } from '@cloudbeaver/core-projects'; -import { PermissionsService } from '@cloudbeaver/core-root'; +import { PermissionsService, ServerConfigResource } from '@cloudbeaver/core-root'; import { CachedMapAllKey, DriverConfigurationType, isObjectPropertyInfoStateEqual, ObjectPropertyInfo } from '@cloudbeaver/core-sdk'; import { getUniqueName, isValuesEqual } from '@cloudbeaver/core-utils'; @@ -30,6 +30,7 @@ import { Options } from './Options'; @injectable() export class ConnectionOptionsTabService extends Bootstrap { constructor( + private readonly serverConfigResource: ServerConfigResource, private readonly projectInfoResource: ProjectInfoResource, private readonly connectionFormService: ConnectionFormService, private readonly dbDriverResource: DBDriverResource, @@ -95,7 +96,7 @@ export class ConnectionOptionsTabService extends Bootstrap { const adminPermission = this.permissionsService.has(EAdminPermission.admin); const originLocal = !state.info || isLocalConnection(state.info); - return adminPermission && originLocal && isProjectShared; + return adminPermission && originLocal && isProjectShared && !this.serverConfigResource.data?.distributed; } private async save(