From cdfc46ef45511110451a646eec308ed9edff2690 Mon Sep 17 00:00:00 2001 From: Serge Rider Date: Fri, 29 Jul 2022 12:27:05 +0300 Subject: [PATCH] Revert "dbeaver/dbeaver-ee#1734 RM resource properties (#985)" This reverts commit 08b24cace6237cbf0232f918d4fd07ffe4de3eb8. --- .../rm/local/LocalResourceController.java | 66 +++---------- .../cloudbeaver/model/session/WebSession.java | 4 +- .../utils/WebConnectionFolderUtils.java | 11 +-- .../cloudbeaver/utils/WebDataSourceUtils.java | 6 +- .../schema/service.core.graphqls | 18 ++-- .../schema/service.navigator.graphqls | 13 ++- .../schema/service.sql.graphqls | 23 +++-- .../service/WebServiceBindingBase.java | 7 +- .../service/core/DBWServiceCore.java | 25 +++-- .../service/core/WebServiceBindingCore.java | 40 ++++++-- .../service/core/impl/WebServiceCore.java | 77 +++++++++------ .../navigator/DBWServiceNavigator.java | 7 -- .../navigator/WebServiceBindingNavigator.java | 7 -- .../navigator/impl/WebServiceNavigator.java | 21 +---- .../service/sql/DBWServiceSQL.java | 6 +- .../service/sql/WebServiceBindingSQL.java | 1 + .../service/sql/impl/WebServiceSQL.java | 9 +- .../auth/local/LocalSessionHandler.java | 6 +- .../CBEmbeddedSecurityController.java | 2 +- .../src/LocalizationService.ts | 2 +- webapp/packages/core-projects/package.json | 2 + .../core-projects/src/ProjectsResource.ts | 35 +++---- .../core-projects/src/ProjectsService.ts | 52 ++++++++++ webapp/packages/core-projects/src/index.ts | 1 + webapp/packages/core-projects/src/manifest.ts | 2 + .../core-sdk/src/CustomGraphQLClient.ts | 23 +++-- .../src/queries/project/getProjectList.gql | 7 ++ ...ectList.gql => getResourceProjectList.gql} | 3 +- webapp/packages/core-sdk/src/sdk.ts | 94 +++++++++++++++---- .../src/ThemeSettingsService.test.ts | 4 +- .../core-theming/src/ThemeSettingsService.ts | 2 +- .../src/NavNodes/ResourceFoldersBootstrap.ts | 16 ++-- .../src/ResourceProjectsResource.ts | 43 +++++++++ .../src/Tree/ResourceManagerTree.tsx | 4 +- .../plugin-resource-manager/src/index.ts | 1 + .../plugin-resource-manager/src/manifest.ts | 2 + .../src/PluginBootstrap.ts | 9 +- 37 files changed, 420 insertions(+), 231 deletions(-) create mode 100644 webapp/packages/core-projects/src/ProjectsService.ts create mode 100644 webapp/packages/core-sdk/src/queries/project/getProjectList.gql rename webapp/packages/core-sdk/src/queries/resource-manager/{getProjectList.gql => getResourceProjectList.gql} (57%) create mode 100644 webapp/packages/plugin-resource-manager/src/ResourceProjectsResource.ts 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 9fd6289e1f..f45b5f2fdd 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 @@ -21,7 +21,6 @@ import io.cloudbeaver.VirtualProjectImpl; import io.cloudbeaver.model.rm.RMUtils; import io.cloudbeaver.service.sql.WebSQLConstants; import io.cloudbeaver.utils.WebAppUtils; -import org.eclipse.core.runtime.IPath; import org.jkiss.code.NotNull; import org.jkiss.code.Nullable; import org.jkiss.dbeaver.DBException; @@ -43,10 +42,7 @@ import org.jkiss.utils.CommonUtils; import java.io.IOException; import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.InvalidPathException; -import java.nio.file.NoSuchFileException; -import java.nio.file.Path; +import java.nio.file.*; import java.time.OffsetDateTime; import java.time.ZoneId; import java.util.*; @@ -237,11 +233,11 @@ public class LocalResourceController implements RMController { } @Override - public RMProject getProject(@NotNull String projectId, boolean readResources, boolean readProperties) throws DBException { + public RMProject getProject(@NotNull String projectId, boolean readResources) throws DBException { RMProject project = makeProjectFromId(projectId, true); if (readResources) { project.setChildren( - listResources(projectId, null, null, readProperties, false, true) + listResources(projectId, null, null, true, false, true) ); } return project; @@ -316,7 +312,7 @@ public class LocalResourceController implements RMController { if (!folderPath.startsWith(projectPath)) { throw new DBException("Invalid folder path"); } - return readChildResources(projectId, folderPath, readProperties, readHistory, recursive); + return readChildResources(folderPath, readProperties, readHistory, recursive); } catch (NoSuchFileException e) { throw new DBException("Invalid resource folder " + folder); } catch (IOException e) { @@ -325,16 +321,10 @@ public class LocalResourceController implements RMController { } @NotNull - private RMResource[] readChildResources( - @NotNull String projectId, - @NotNull Path folderPath, - boolean readProperties, - boolean readHistory, - boolean recursive - ) throws IOException { + private RMResource[] readChildResources(Path folderPath, boolean readProperties, boolean readHistory, boolean recursive) throws IOException { try (Stream files = Files.list(folderPath)) { return files.filter(path -> !path.getFileName().toString().startsWith(".")) // skip hidden files - .map((Path path) -> makeResourceFromPath(projectId, path, readProperties, readHistory, recursive)) + .map((Path path) -> makeResourceFromPath(path, readProperties, readHistory, recursive)) .filter(Objects::nonNull) .toArray(RMResource[]::new); } @@ -459,19 +449,6 @@ public class LocalResourceController implements RMController { return DEFAULT_CHANGE_ID; } - @NotNull - @Override - public String setResourceProperty( - @NotNull String projectId, - @NotNull String resourcePath, - @NotNull String propertyName, - @Nullable Object propertyValue - ) throws DBException { - validateResourcePath(resourcePath); - getProjectMetadata(projectId).setResourceProperty(resourcePath, propertyName, propertyValue); - return DEFAULT_CHANGE_ID; - } - private void validateResourcePath(String resourcePath) throws DBException { if (resourcePath.startsWith(".")) { throw new DBException("Resource path '" + resourcePath + "' can't start with dot"); @@ -582,20 +559,14 @@ public class LocalResourceController implements RMController { for (var resourceName : relativeResourcePath) { resourcePath = resourcePath.resolve(resourceName); - result.add(makeResourceFromPath(projectId, resourcePath, false, false, recursive)); + result.add(makeResourceFromPath(resourcePath, false, false, recursive)); } return result; } - private RMResource makeResourceFromPath( - @NotNull String projectId, - @NotNull Path path, - boolean readProperties, - boolean readHistory, - boolean recursive - ) { - if (Files.notExists(path)) { + private RMResource makeResourceFromPath(Path path, boolean readProperties, boolean readHistory, boolean recursive) { + if (path == null || !Files.exists(path)) { return null; } RMResource resource = new RMResource(); @@ -623,22 +594,14 @@ public class LocalResourceController implements RMController { )) ); } - if (readProperties) { - final BaseProjectImpl project = (BaseProjectImpl) getProjectMetadata(projectId); - final String resourcePath = getProjectRelativePath(projectId, path); - final Map properties = project.getResourceProperties(resourcePath); - - if (properties != null && !properties.isEmpty()) { - resource.setProperties(new LinkedHashMap<>(properties)); - } - } - } catch (Exception e) { + } catch (IOException e) { log.error(e); } if (recursive && resource.isFolder()) { try { - resource.setChildren(readChildResources(projectId, path, readProperties, readHistory, true)); + resource.setChildren( + readChildResources(path, readProperties, readHistory, recursive)); } catch (IOException e) { log.error(e); } @@ -647,11 +610,6 @@ public class LocalResourceController implements RMController { return resource; } - @NotNull - private String getProjectRelativePath(@NotNull String projectId, @NotNull Path path) throws DBException { - return getProjectPath(projectId).toAbsolutePath().relativize(path).toString().replace('\\', IPath.SEPARATOR); - } - public static Builder builder(SMCredentialsProvider credentialsProvider, SMController smController) { return new Builder(credentialsProvider, smController); } diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/session/WebSession.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/session/WebSession.java index 4baf895085..d18a6b3a22 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/session/WebSession.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/session/WebSession.java @@ -528,13 +528,13 @@ public class WebSession extends AbstractSessionPersistent implements SMSession, } @NotNull - public WebConnectionInfo getWebConnectionInfo(String connectionID) throws DBWebException { + public WebConnectionInfo getWebConnectionInfo(@Nullable String projectId, String connectionID) throws DBWebException { WebConnectionInfo connectionInfo; synchronized (connections) { connectionInfo = connections.get(connectionID); } if (connectionInfo == null) { - DBPDataSourceContainer dataSource = WebDataSourceUtils.getLocalOrGlobalDataSource(application, this, connectionID); + DBPDataSourceContainer dataSource = getProjectById(projectId).getDataSourceRegistry().getDataSource(connectionID); if (dataSource != null) { connectionInfo = new WebConnectionInfo(this, dataSource); synchronized (connections) { 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 f35193b67e..c95ebd05d3 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 @@ -16,22 +16,21 @@ */ package io.cloudbeaver.utils; -import io.cloudbeaver.DBWConstants; import io.cloudbeaver.DBWebException; 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.utils.CommonUtils; public class WebConnectionFolderUtils { - public static WebConnectionFolderInfo getFolderInfo(WebSession session, String folderPath) throws DBWebException { + public static WebConnectionFolderInfo getFolderInfo( + WebSession session, @Nullable String projectId, String folderPath + ) throws DBWebException { DBPDataSourceFolder folder = null; if (!CommonUtils.isEmpty(folderPath)) { - folder = session.getSingletonProject().getDataSourceRegistry().getFolder(folderPath); - if (folder == null && (session.hasPermission(DBWConstants.PERMISSION_ADMIN) || session.getApplication().isConfigurationMode())) { - folder = WebDataSourceUtils.getGlobalDataSourceRegistry().getFolder(folderPath); - } + folder = session.getProjectById(projectId).getDataSourceRegistry().getFolder(folderPath); } if (folder != null) { return new WebConnectionFolderInfo(session, folder); diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/utils/WebDataSourceUtils.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/utils/WebDataSourceUtils.java index 9774394574..08e47eaf89 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/utils/WebDataSourceUtils.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/utils/WebDataSourceUtils.java @@ -60,10 +60,12 @@ public class WebDataSourceUtils { } @Nullable - public static DBPDataSourceContainer getLocalOrGlobalDataSource(WebApplication application, WebSession webSession, String connectionId) throws DBWebException { + public static DBPDataSourceContainer getLocalOrGlobalDataSource( + WebApplication application, WebSession webSession, @Nullable String projectId, String connectionId + ) throws DBWebException { DBPDataSourceContainer dataSource = null; if (!CommonUtils.isEmpty(connectionId)) { - dataSource = webSession.getSingletonProject().getDataSourceRegistry().getDataSource(connectionId); + dataSource = webSession.getProjectById(projectId).getDataSourceRegistry().getDataSource(connectionId); if (dataSource == null && (webSession.hasPermission(DBWConstants.PERMISSION_ADMIN) || application.isConfigurationMode())) { // If called for new connection in admin mode then this connection may absent in session registry yet dataSource = getGlobalDataSourceRegistry().getDataSource(connectionId); diff --git a/server/bundles/io.cloudbeaver.server/schema/service.core.graphqls b/server/bundles/io.cloudbeaver.server/schema/service.core.graphqls index 1de26b17c1..7ced602cc4 100644 --- a/server/bundles/io.cloudbeaver.server/schema/service.core.graphqls +++ b/server/bundles/io.cloudbeaver.server/schema/service.core.graphqls @@ -470,21 +470,21 @@ extend type Query { networkHandlers: [NetworkHandlerDescriptor!]! # List of user connections. - userConnections( id: ID ): [ ConnectionInfo! ]! + userConnections( projectId: String, id: ID ): [ ConnectionInfo! ]! # List of template connections. templateConnections: [ ConnectionInfo! ]! # List of connection folders - connectionFolders( path: ID ): [ ConnectionFolderInfo! ]! + connectionFolders( projectId: String, path: ID ): [ ConnectionFolderInfo! ]! # Return connection state - connectionState( id: ID! ): ConnectionInfo! @deprecated + connectionState( projectId: String, id: ID! ): ConnectionInfo! @deprecated # Return connection info - connectionInfo( id: ID! ): ConnectionInfo! + connectionInfo( projectId: String, id: ID! ): ConnectionInfo! # Return list of accessible user projects - listProjects: [ ProjectInfo ] + listProjects: [ ProjectInfo! ]! readSessionLog(maxEntries: Int, clearEntries: Boolean): [ LogEntry! ]! } @@ -523,19 +523,19 @@ extend type Mutation { copyConnectionFromNode( nodePath: String!, config: ConnectionConfig, projectId: String ): ConnectionInfo! # Test connection configuration. Returns remote server version - testConnection( config: ConnectionConfig! ): ConnectionInfo! + testConnection( config: ConnectionConfig!, projectId: String ): ConnectionInfo! # Test connection configuration. Returns remote server version testNetworkHandler( config: NetworkHandlerConfigInput! ): NetworkEndpointInfo! # Initiate existing connection - initConnection( id: ID!, credentials: Object, networkCredentials: [NetworkHandlerConfigInput!], saveCredentials: Boolean ): ConnectionInfo! + initConnection( id: ID!, projectId: String, credentials: Object, networkCredentials: [NetworkHandlerConfigInput!], saveCredentials: Boolean ): ConnectionInfo! # Disconnect from database - closeConnection( id: ID! ): ConnectionInfo! + closeConnection( id: ID!, projectId: String ): ConnectionInfo! # Changes navigator settings for connection - setConnectionNavigatorSettings( id: ID!, settings: NavigatorSettingsInput!): ConnectionInfo! + setConnectionNavigatorSettings( id: ID!, projectId: String, settings: NavigatorSettingsInput!): ConnectionInfo! #### Generic async functions diff --git a/server/bundles/io.cloudbeaver.server/schema/service.navigator.graphqls b/server/bundles/io.cloudbeaver.server/schema/service.navigator.graphqls index ce0d24b4d0..5750ad0fdf 100644 --- a/server/bundles/io.cloudbeaver.server/schema/service.navigator.graphqls +++ b/server/bundles/io.cloudbeaver.server/schema/service.navigator.graphqls @@ -107,18 +107,17 @@ extend type Query { # Get child nodes navNodeChildren( - projectId: ID, parentPath: ID!, offset: Int, limit: Int, onlyFolders: Boolean): [ NavigatorNodeInfo! ]! # Get child nodes - navNodeParents( projectId: ID, nodePath: ID! ): [ NavigatorNodeInfo! ]! + navNodeParents( nodePath: ID! ): [ NavigatorNodeInfo! ]! - navNodeInfo( projectId: ID, nodePath: ID! ): NavigatorNodeInfo! + navNodeInfo( nodePath: ID! ): NavigatorNodeInfo! - navRefreshNode( projectId: ID, nodePath: ID! ): Boolean + navRefreshNode( nodePath: ID! ): Boolean # contextId currently not using navGetStructContainers( projectId: ID, connectionId: ID!, contextId: ID, catalog: ID ): DatabaseStructContainers! @@ -128,12 +127,12 @@ extend type Query { extend type Mutation { # Rename node and returns new node name - navRenameNode( projectId: ID, nodePath: ID!, newName: String! ): String + navRenameNode( nodePath: ID!, newName: String! ): String # Deletes nodes with specified IDs and returns number of deleted nodes - navDeleteNodes( projectId: ID, nodePaths: [ID!]! ): Int + navDeleteNodes( nodePaths: [ID!]! ): Int # Moves nodes with specified IDs to the connection folder - navMoveNodesToFolder(projectId: ID, nodePaths: [ID!]!, folderPath: ID!): Boolean + navMoveNodesToFolder( nodePaths: [ID!]!, folderPath: ID!): Boolean } \ No newline at end of file diff --git a/server/bundles/io.cloudbeaver.server/schema/service.sql.graphqls b/server/bundles/io.cloudbeaver.server/schema/service.sql.graphqls index 3c3d66543a..c36e1a54de 100644 --- a/server/bundles/io.cloudbeaver.server/schema/service.sql.graphqls +++ b/server/bundles/io.cloudbeaver.server/schema/service.sql.graphqls @@ -197,12 +197,13 @@ type SQLScriptQuery { extend type Query { - sqlDialectInfo( connectionId: ID! ): SQLDialectInfo + sqlDialectInfo( projectId: ID, connectionId: ID! ): SQLDialectInfo # Lists SQL contexts for a connection (optional) or returns the particular context info - sqlListContexts( connectionId: ID, contextId: ID ): [ SQLContextInfo ]! + sqlListContexts( projectId: ID, connectionId: ID, contextId: ID ): [ SQLContextInfo ]! sqlCompletionProposals( + projectId: ID, connectionId: ID!, contextId: ID!, query: String!, @@ -212,12 +213,14 @@ extend type Query { ): [ SQLCompletionProposal ] sqlFormatQuery( + projectId: ID, connectionId: ID!, contextId: ID!, query: String! ): String! sqlSupportedOperations( + projectId: ID, connectionId: ID!, contextId: ID!, resultsId: ID!, @@ -243,11 +246,13 @@ extend type Query { ): String! sqlParseScript( + projectId: ID, connectionId: ID!, script: String! ): SQLScriptInfo! sqlParseQuery( + projectId: ID, connectionId: ID!, script: String!, position: Int! @@ -255,14 +260,15 @@ extend type Query { } extend type Mutation { - sqlContextCreate( connectionId: ID!, defaultCatalog: String, defaultSchema: String ): SQLContextInfo! + sqlContextCreate( projectId: ID, connectionId: ID!, defaultCatalog: String, defaultSchema: String ): SQLContextInfo! - sqlContextSetDefaults( connectionId: ID!, contextId: ID!, defaultCatalog: ID, defaultSchema: ID ): Boolean! + sqlContextSetDefaults( projectId: ID, connectionId: ID!, contextId: ID!, defaultCatalog: ID, defaultSchema: ID ): Boolean! - sqlContextDestroy( connectionId: ID!, contextId: ID! ): Boolean! + sqlContextDestroy( projectId: ID, connectionId: ID!, contextId: ID! ): Boolean! # Execute SQL and return results asyncSqlExecuteQuery( + projectId: ID, connectionId: ID!, contextId: ID!, sql: String!, @@ -273,6 +279,7 @@ extend type Mutation { # Read data from table asyncReadDataFromContainer( + projectId: ID, connectionId: ID!, contextId: ID!, containerNodePath: ID!, @@ -282,10 +289,11 @@ extend type Mutation { ): AsyncTaskInfo! # Close results (free resources) - sqlResultClose(connectionId: ID!, contextId: ID!, resultId: ID!): Boolean! + sqlResultClose(projectId: ID, connectionId: ID!, contextId: ID!, resultId: ID!): Boolean! # Update multiple cell values updateResultsDataBatch( + projectId: ID, connectionId: ID!, contextId: ID!, resultsId: ID!, @@ -297,6 +305,7 @@ extend type Mutation { # Return SQL script for cell values update updateResultsDataBatchScript( + projectId: ID, connectionId: ID!, contextId: ID!, resultsId: ID!, @@ -308,6 +317,7 @@ extend type Mutation { #Return BLOB name readLobValue( + projectId: ID, connectionId: ID!, contextId: ID!, resultsId: ID!, @@ -320,6 +330,7 @@ extend type Mutation { # Read data from table asyncSqlExplainExecutionPlan( + projectId: ID, connectionId: ID!, contextId: ID!, query: String!, diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/WebServiceBindingBase.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/WebServiceBindingBase.java index 33ad78014b..ff27dd0077 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/WebServiceBindingBase.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/WebServiceBindingBase.java @@ -27,6 +27,7 @@ import io.cloudbeaver.server.CBApplication; import io.cloudbeaver.server.CBPlatform; import io.cloudbeaver.server.graphql.GraphQLEndpoint; import org.jkiss.code.NotNull; +import org.jkiss.code.Nullable; import org.jkiss.dbeaver.DBException; import org.jkiss.dbeaver.Log; import org.jkiss.dbeaver.model.rm.RMProject; @@ -117,7 +118,7 @@ public abstract class WebServiceBindingBase impleme @NotNull protected static WebConnectionInfo getWebConnection(DataFetchingEnvironment env) throws DBWebException { - return getWebConnection(getWebSession(env), env.getArgument("connectionId")); + return getWebConnection(getWebSession(env), getProjectReference(env), env.getArgument("connectionId")); } public static WebSession findWebSession(DataFetchingEnvironment env) { @@ -126,8 +127,8 @@ public abstract class WebServiceBindingBase impleme } @NotNull - public static WebConnectionInfo getWebConnection(WebSession session, String connectionId) throws DBWebException { - return session.getWebConnectionInfo(connectionId); + public static WebConnectionInfo getWebConnection(WebSession session, String projectId, String connectionId) throws DBWebException { + return session.getWebConnectionInfo(projectId, connectionId); } private class ServiceInvocationHandler implements InvocationHandler { diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/DBWServiceCore.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/DBWServiceCore.java index 1994a80d37..f2ff5d83f9 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/DBWServiceCore.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/DBWServiceCore.java @@ -51,10 +51,12 @@ public interface DBWServiceCore extends DBWService { List getNetworkHandlers(@NotNull WebSession webSession); @WebAction(requirePermissions = {}) - List getUserConnections(@NotNull WebSession webSession, @Nullable String id) throws DBWebException; + List getUserConnections( + @NotNull WebSession webSession, @Nullable String projectId, @Nullable String id) throws DBWebException; @WebAction(requirePermissions = {}) - List getConnectionFolders(@NotNull WebSession webSession, @Nullable String id) throws DBWebException; + List getConnectionFolders( + @NotNull WebSession webSession, @Nullable String projectId, @Nullable String id) throws DBWebException; @Deprecated @WebAction @@ -98,11 +100,12 @@ public interface DBWServiceCore extends DBWService { // Connections @WebAction - WebConnectionInfo getConnectionState(WebSession webSession, String connectionId) throws DBWebException; + WebConnectionInfo getConnectionState(WebSession webSession, @Nullable String projectId, String connectionId) throws DBWebException; @WebAction WebConnectionInfo initConnection( @NotNull WebSession webSession, + @Nullable String projectId, @NotNull String connectionId, @NotNull Map authProperties, @Nullable List networkCredentials, @@ -141,7 +144,8 @@ public interface DBWServiceCore extends DBWService { @NotNull WebConnectionConfig config) throws DBWebException; @WebAction - WebConnectionInfo testConnection(@NotNull WebSession webSession, @NotNull WebConnectionConfig connectionConfig) throws DBWebException; + WebConnectionInfo testConnection( + @NotNull WebSession webSession, @Nullable String projectId, @NotNull WebConnectionConfig connectionConfig) throws DBWebException; @WebAction WebNetworkEndpointInfo testNetworkHandler(@NotNull WebSession webSession, @NotNull WebNetworkHandlerConfigInput nhConfig) throws DBWebException; @@ -159,19 +163,24 @@ public interface DBWServiceCore extends DBWService { // Folders @WebAction - WebConnectionFolderInfo createConnectionFolder(@NotNull WebSession session, @Nullable String folderPath, @NotNull String newName) throws DBWebException; + WebConnectionFolderInfo createConnectionFolder( + @NotNull WebSession session, @Nullable String projectId, @Nullable String folderPath, @NotNull String newName + ) throws DBWebException; @WebAction - WebConnectionFolderInfo renameConnectionFolder(@NotNull WebSession session, @NotNull String folderPath, @NotNull String newName) throws DBWebException; + WebConnectionFolderInfo renameConnectionFolder( + @NotNull WebSession session, @Nullable String projectId, @NotNull String folderPath, @NotNull String newName + ) throws DBWebException; @WebAction - boolean deleteConnectionFolder(@NotNull WebSession session, @NotNull String nodePath) throws DBWebException; + boolean deleteConnectionFolder(@NotNull WebSession session, @Nullable String projectId, @NotNull String nodePath) throws DBWebException; /////////////////////////////////////////// // Navigator settings @WebAction - WebConnectionInfo setConnectionNavigatorSettings(WebSession webSession, String id, DBNBrowseSettings settings) throws DBWebException; + WebConnectionInfo setConnectionNavigatorSettings( + WebSession webSession, @Nullable String projectId, String id, DBNBrowseSettings settings) throws DBWebException; /////////////////////////////////////////// // Async tasks diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/WebServiceBindingCore.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/WebServiceBindingCore.java index 531925bf0a..b747b6c4f1 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/WebServiceBindingCore.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/WebServiceBindingCore.java @@ -58,16 +58,20 @@ public class WebServiceBindingCore extends WebServiceBindingBase .dataFetcher("authModels", env -> getService(env).getAuthModels(getWebSession(env))) .dataFetcher("networkHandlers", env -> getService(env).getNetworkHandlers(getWebSession(env))) .dataFetcher("templateDataSources", env -> getService(env).getTemplateDataSources()) - .dataFetcher("userConnections", env -> getService(env).getUserConnections(getWebSession(env), env.getArgument("id"))) + .dataFetcher("userConnections", env -> getService(env).getUserConnections( + getWebSession(env), getProjectReference(env), env.getArgument("id"))) .dataFetcher("templateConnections", env -> getService(env).getTemplateConnections(getWebSession(env))) - .dataFetcher("connectionFolders", env -> getService(env).getConnectionFolders(getWebSession(env), env.getArgument("path"))) + .dataFetcher("connectionFolders", env -> getService(env).getConnectionFolders( + getWebSession(env), getProjectReference(env), env.getArgument("path"))) .dataFetcher("sessionPermissions", env -> getService(env).getSessionPermissions(getWebSession(env))) .dataFetcher("sessionState", env -> getService(env).getSessionState(getWebSession(env))) - .dataFetcher("connectionState", env -> getService(env).getConnectionState(getWebSession(env), env.getArgument("id"))) - .dataFetcher("connectionInfo", env -> getService(env).getConnectionState(getWebSession(env), env.getArgument("id"))) + .dataFetcher("connectionState", env -> getService(env).getConnectionState( + getWebSession(env), getProjectReference(env), env.getArgument("id"))) + .dataFetcher("connectionInfo", env -> getService(env).getConnectionState( + getWebSession(env), getProjectReference(env), env.getArgument("id"))) .dataFetcher("listProjects", env -> getService(env).getProjects(getWebSession(env))) @@ -101,9 +105,12 @@ public class WebServiceBindingCore extends WebServiceBindingBase GraphQLEndpoint.getServletRequest(env), GraphQLEndpoint.getServletResponse(env))) .dataFetcher("changeSessionLanguage", env -> getService(env).changeSessionLanguage(getWebSession(env), env.getArgument("locale"))) - .dataFetcher("createConnection", env -> getService(env).createConnection(getWebSession(env), getProjectReference(env), getConnectionConfig(env))) - .dataFetcher("updateConnection", env -> getService(env).updateConnection(getWebSession(env), getProjectReference(env), getConnectionConfig(env))) - .dataFetcher("deleteConnection", env -> getService(env).deleteConnection(getWebSession(env), getProjectReference(env), env.getArgument("id"))) + .dataFetcher("createConnection", env -> getService(env).createConnection( + getWebSession(env), getProjectReference(env), getConnectionConfig(env))) + .dataFetcher("updateConnection", env -> getService(env).updateConnection( + getWebSession(env), getProjectReference(env), getConnectionConfig(env))) + .dataFetcher("deleteConnection", env -> getService(env).deleteConnection( + getWebSession(env), getProjectReference(env), env.getArgument("id"))) .dataFetcher("createConnectionFromTemplate", env -> getService(env).createConnectionFromTemplate( getWebSession(env), getProjectReference(env), @@ -122,17 +129,27 @@ public class WebServiceBindingCore extends WebServiceBindingBase } return getService(env).initConnection( getWebSession(env), + getProjectReference(env), env.getArgument("id"), env.getArgument("credentials"), nhc, env.getArgument("saveCredentials")); } ) - .dataFetcher("testConnection", env -> getService(env).testConnection(getWebSession(env), getConnectionConfig(env))) - .dataFetcher("testNetworkHandler", env -> getService(env).testNetworkHandler(getWebSession(env), new WebNetworkHandlerConfigInput(env.getArgument("config")))) + .dataFetcher("testConnection", env -> getService(env).testConnection( + getWebSession(env), getProjectReference(env), getConnectionConfig(env) + )) + .dataFetcher("testNetworkHandler", env -> getService(env).testNetworkHandler( + getWebSession(env), new WebNetworkHandlerConfigInput(env.getArgument("config")) + )) .dataFetcher("closeConnection", env -> getService(env).closeConnection(getWebSession(env), getProjectReference(env), env.getArgument("id"))) - .dataFetcher("setConnectionNavigatorSettings", env -> getService(env).setConnectionNavigatorSettings(getWebSession(env), env.getArgument("id"), WebServiceUtils.parseNavigatorSettings(env.getArgument("settings")))) + .dataFetcher("setConnectionNavigatorSettings", env -> getService(env).setConnectionNavigatorSettings( + getWebSession(env), + getProjectReference(env), + env.getArgument("id"), + WebServiceUtils.parseNavigatorSettings(env.getArgument("settings")) + )) .dataFetcher("asyncTaskInfo", env -> getService(env).getAsyncTaskInfo( getWebSession(env), @@ -142,16 +159,19 @@ public class WebServiceBindingCore extends WebServiceBindingBase .dataFetcher("createConnectionFolder", env -> getService(env).createConnectionFolder( getWebSession(env), + getProjectReference(env), env.getArgument("parentFolderPath"), env.getArgument("folderName") )) .dataFetcher("renameConnectionFolder", env -> getService(env).renameConnectionFolder( getWebSession(env), + getProjectReference(env), env.getArgument("folderPath"), env.getArgument("newName") )) .dataFetcher("deleteConnectionFolder", env -> getService(env).deleteConnectionFolder( getWebSession(env), + getProjectReference(env), env.getArgument("folderPath") )) ; 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 5c656ecb90..cafa5987af 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 @@ -100,9 +100,11 @@ public class WebServiceCore implements DBWServiceCore { } @Override - public List getUserConnections(@NotNull WebSession webSession, @Nullable String id) throws DBWebException { + public List getUserConnections( + @NotNull WebSession webSession, @Nullable String projectId, @Nullable String id + ) throws DBWebException { if (id != null) { - WebConnectionInfo connectionInfo = getConnectionState(webSession, id); + WebConnectionInfo connectionInfo = getConnectionState(webSession, projectId, id); if (connectionInfo != null) { return Collections.singletonList(connectionInfo); } @@ -149,12 +151,14 @@ public class WebServiceCore implements DBWServiceCore { } @Override - public List getConnectionFolders(@NotNull WebSession webSession, @Nullable String id) throws DBWebException { + public List getConnectionFolders( + @NotNull WebSession webSession, @Nullable String projectId, @Nullable String id + ) throws DBWebException { if (id != null) { - WebConnectionFolderInfo folderInfo = WebConnectionFolderUtils.getFolderInfo(webSession, id); + WebConnectionFolderInfo folderInfo = WebConnectionFolderUtils.getFolderInfo(webSession, projectId, id); return Collections.singletonList(folderInfo); } - return webSession.getSingletonProject().getDataSourceRegistry().getAllFolders().stream() + return webSession.getProjectById(projectId).getDataSourceRegistry().getAllFolders().stream() .map(f -> new WebConnectionFolderInfo(webSession, f)).collect(Collectors.toList()); } @@ -240,20 +244,23 @@ public class WebServiceCore implements DBWServiceCore { } @Override - public WebConnectionInfo getConnectionState(WebSession webSession, String connectionId) throws DBWebException { - return webSession.getWebConnectionInfo(connectionId); + public WebConnectionInfo getConnectionState( + WebSession webSession, @Nullable String projectId, String connectionId + ) throws DBWebException { + return webSession.getWebConnectionInfo(projectId, connectionId); } @Override public WebConnectionInfo initConnection( @NotNull WebSession webSession, + @Nullable String projectId, @NotNull String connectionId, @NotNull Map authProperties, @Nullable List networkCredentials, @Nullable Boolean saveCredentials) throws DBWebException { - WebConnectionInfo connectionInfo = webSession.getWebConnectionInfo(connectionId); + WebConnectionInfo connectionInfo = webSession.getWebConnectionInfo(projectId, connectionId); connectionInfo.setSavedCredentials(authProperties, networkCredentials); DBPDataSourceContainer dataSourceContainer = connectionInfo.getDataSourceContainer(); @@ -342,7 +349,7 @@ public class WebServiceCore implements DBWServiceCore { // } DBPDataSourceRegistry sessionRegistry = webSession.getProjectById(projectId).getDataSourceRegistry(); - WebConnectionInfo connectionInfo = webSession.getWebConnectionInfo(config.getConnectionId()); + WebConnectionInfo connectionInfo = webSession.getWebConnectionInfo(projectId, config.getConnectionId()); DBPDataSourceContainer dataSource = connectionInfo.getDataSourceContainer(); webSession.addInfoMessage("Update connection - " + WebServiceUtils.getConnectionContainerInfo(dataSource)); @@ -371,7 +378,7 @@ public class WebServiceCore implements DBWServiceCore { @Override public boolean deleteConnection( @NotNull WebSession webSession, @Nullable String projectId, @NotNull String connectionId) throws DBWebException { - WebConnectionInfo connectionInfo = webSession.getWebConnectionInfo(connectionId); + WebConnectionInfo connectionInfo = webSession.getWebConnectionInfo(projectId, connectionId); if (connectionInfo.getDataSourceContainer().getProject() != webSession.getProjectById(projectId)) { throw new DBWebException("Global connection '" + connectionInfo.getName() + "' configuration cannot be deleted"); } @@ -397,7 +404,9 @@ public class WebServiceCore implements DBWServiceCore { DBPDataSourceRegistry projectRegistry = webSession.getProjectById(projectId).getDataSourceRegistry(); DBPDataSourceContainer newDataSource = projectRegistry.createDataSource(dataSourceTemplate); - ((DataSourceDescriptor) newDataSource).setNavigatorSettings(CBApplication.getInstance().getAppConfiguration().getDefaultNavigatorSettings()); + ((DataSourceDescriptor) newDataSource).setNavigatorSettings( + CBApplication.getInstance().getAppConfiguration().getDefaultNavigatorSettings()); + if (!CommonUtils.isEmpty(connectionName)) { newDataSource.setName(connectionName); } @@ -435,7 +444,8 @@ public class WebServiceCore implements DBWServiceCore { DBPDataSourceContainer newDataSource = dataSourceRegistry.createDataSource(dataSourceTemplate); - ((DataSourceDescriptor) newDataSource).setNavigatorSettings(CBApplication.getInstance().getAppConfiguration().getDefaultNavigatorSettings()); + ((DataSourceDescriptor) newDataSource).setNavigatorSettings( + CBApplication.getInstance().getAppConfiguration().getDefaultNavigatorSettings()); // Copy props from config if (!CommonUtils.isEmpty(config.getName())) { @@ -457,14 +467,17 @@ public class WebServiceCore implements DBWServiceCore { } @Override - public WebConnectionInfo testConnection(@NotNull WebSession webSession, @NotNull WebConnectionConfig connectionConfig) throws DBWebException { + public WebConnectionInfo testConnection( + @NotNull WebSession webSession, @Nullable String projectId, @NotNull WebConnectionConfig connectionConfig + ) throws DBWebException { String connectionId = connectionConfig.getConnectionId(); connectionConfig.setSaveCredentials(true); // It is used in createConnectionFromConfig - DBPDataSourceContainer dataSource = WebDataSourceUtils.getLocalOrGlobalDataSource(CBApplication.getInstance(), webSession, connectionId); + DBPDataSourceContainer dataSource = WebDataSourceUtils.getLocalOrGlobalDataSource( + CBApplication.getInstance(), webSession, projectId, connectionId); - DBPDataSourceRegistry sessionRegistry = webSession.getSingletonProject().getDataSourceRegistry(); + DBPDataSourceRegistry sessionRegistry = webSession.getProjectById(projectId).getDataSourceRegistry(); DBPDataSourceContainer testDataSource; if (dataSource != null) { testDataSource = dataSource.createCopy(dataSource.getRegistry()); @@ -548,13 +561,17 @@ public class WebServiceCore implements DBWServiceCore { } @Override - public WebConnectionInfo closeConnection(@NotNull WebSession webSession, @Nullable String projectId, @NotNull String connectionId) throws DBWebException { + public WebConnectionInfo closeConnection( + @NotNull WebSession webSession, @Nullable String projectId, @NotNull String connectionId + ) throws DBWebException { return closeAndDeleteConnection(webSession, projectId, connectionId, false); } @NotNull - private WebConnectionInfo closeAndDeleteConnection(WebSession webSession, String projectId, String connectionId, boolean forceDelete) throws DBWebException { - WebConnectionInfo connectionInfo = webSession.getWebConnectionInfo(connectionId); + private WebConnectionInfo closeAndDeleteConnection( + WebSession webSession, String projectId, String connectionId, boolean forceDelete + ) throws DBWebException { + WebConnectionInfo connectionInfo = webSession.getWebConnectionInfo(projectId, connectionId); boolean disconnected = false; DBPDataSourceContainer dataSourceContainer = connectionInfo.getDataSourceContainer(); @@ -596,6 +613,7 @@ public class WebServiceCore implements DBWServiceCore { @Override public WebConnectionFolderInfo createConnectionFolder( @NotNull WebSession session, + @Nullable String projectId, @Nullable String parentPath, @NotNull String folderName ) throws DBWebException { @@ -605,9 +623,9 @@ public class WebServiceCore implements DBWServiceCore { WebConnectionFolderInfo parentNode = null; try { if (parentPath != null) { - parentNode = WebConnectionFolderUtils.getFolderInfo(session, parentPath); + parentNode = WebConnectionFolderUtils.getFolderInfo(session, projectId, parentPath); } - DBPDataSourceRegistry sessionRegistry = session.getSingletonProject().getDataSourceRegistry(); + DBPDataSourceRegistry sessionRegistry = session.getProjectById(projectId).getDataSourceRegistry(); DBPDataSourceFolder newFolder = WebServiceUtils.createFolder(parentNode, folderName, sessionRegistry); WebConnectionFolderInfo folderInfo = new WebConnectionFolderInfo(session, newFolder); WebServiceUtils.updateConfigAndRefreshDatabases(session); @@ -621,26 +639,29 @@ public class WebServiceCore implements DBWServiceCore { @Override public WebConnectionFolderInfo renameConnectionFolder( @NotNull WebSession session, + @Nullable String projectId, @NotNull String folderPath, @NotNull String newName ) throws DBWebException { WebConnectionFolderUtils.validateConnectionFolder(newName); - WebConnectionFolderInfo folderInfo = WebConnectionFolderUtils.getFolderInfo(session, folderPath); + WebConnectionFolderInfo folderInfo = WebConnectionFolderUtils.getFolderInfo(session, projectId, folderPath); folderInfo.getDataSourceFolder().setName(newName); WebServiceUtils.updateConfigAndRefreshDatabases(session); return folderInfo; } @Override - public boolean deleteConnectionFolder(@NotNull WebSession session, @NotNull String folderPath) throws DBWebException { + public boolean deleteConnectionFolder( + @NotNull WebSession session, @Nullable String projectId, @NotNull String folderPath + ) throws DBWebException { try { - WebConnectionFolderInfo folderInfo = WebConnectionFolderUtils.getFolderInfo(session, folderPath); + WebConnectionFolderInfo folderInfo = WebConnectionFolderUtils.getFolderInfo(session, projectId, folderPath); DBPDataSourceFolder folder = folderInfo.getDataSourceFolder(); - if (folder.getDataSourceRegistry().getProject() != session.getSingletonProject()) { + if (folder.getDataSourceRegistry().getProject() != session.getProjectById(projectId)) { throw new DBWebException("Global folder '" + folderInfo.getId() + "' cannot be deleted"); } session.addInfoMessage("Delete folder"); - DBPDataSourceRegistry sessionRegistry = session.getSingletonProject().getDataSourceRegistry(); + DBPDataSourceRegistry sessionRegistry = session.getProjectById(projectId).getDataSourceRegistry(); sessionRegistry.removeFolder(folderInfo.getDataSourceFolder(), false); WebServiceUtils.updateConfigAndRefreshDatabases(session); } catch (DBException e) { @@ -650,8 +671,10 @@ public class WebServiceCore implements DBWServiceCore { } @Override - public WebConnectionInfo setConnectionNavigatorSettings(WebSession webSession, String id, DBNBrowseSettings settings) throws DBWebException { - WebConnectionInfo connectionInfo = webSession.getWebConnectionInfo(id); + public WebConnectionInfo setConnectionNavigatorSettings( + WebSession webSession, @Nullable String projectId, String id, DBNBrowseSettings settings + ) throws DBWebException { + WebConnectionInfo connectionInfo = webSession.getWebConnectionInfo(projectId, id); DataSourceDescriptor dataSourceDescriptor = ((DataSourceDescriptor)connectionInfo.getDataSourceContainer()); dataSourceDescriptor.setNavigatorSettings(settings); dataSourceDescriptor.persistConfiguration(); diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/navigator/DBWServiceNavigator.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/navigator/DBWServiceNavigator.java index df6c570d6c..2f58304ff3 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/navigator/DBWServiceNavigator.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/navigator/DBWServiceNavigator.java @@ -34,7 +34,6 @@ public interface DBWServiceNavigator extends DBWService { @WebAction List getNavigatorNodeChildren( @NotNull WebSession session, - @Nullable String projectId, String parentPath, Integer offset, Integer limit, @@ -43,19 +42,16 @@ public interface DBWServiceNavigator extends DBWService { @WebAction List getNavigatorNodeParents( @NotNull WebSession session, - @Nullable String projectId, String nodePath) throws DBWebException; @WebAction WebNavigatorNodeInfo getNavigatorNodeInfo( @NotNull WebSession session, - @Nullable String projectId, @NotNull String nodePath) throws DBWebException; @WebAction boolean refreshNavigatorNode( @NotNull WebSession session, - @Nullable String projectId, @NotNull String nodePath) throws DBWebException; @WebAction @@ -68,20 +64,17 @@ public interface DBWServiceNavigator extends DBWService { @WebAction String renameNode( @NotNull WebSession session, - @Nullable String projectId, @NotNull String nodePath, @NotNull String newName) throws DBWebException; @WebAction int deleteNodes( @NotNull WebSession session, - @Nullable String projectId, @NotNull List nodePaths) throws DBWebException; @WebAction boolean moveNodesToFolder( @NotNull WebSession session, - @Nullable String projectId, @NotNull List nodePaths, @NotNull String folderPath) throws DBWebException; } diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/navigator/WebServiceBindingNavigator.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/navigator/WebServiceBindingNavigator.java index 97ea9d4a44..78117d0783 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/navigator/WebServiceBindingNavigator.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/navigator/WebServiceBindingNavigator.java @@ -38,7 +38,6 @@ public class WebServiceBindingNavigator extends WebServiceBindingBase getService(env).getNavigatorNodeChildren( getWebSession(env), - getProjectReference(env), env.getArgument("parentPath"), env.getArgument("offset"), env.getArgument("limit"), @@ -46,17 +45,14 @@ public class WebServiceBindingNavigator extends WebServiceBindingBase getService(env).getNavigatorNodeParents( getWebSession(env), - getProjectReference(env), env.getArgument("nodePath") )) .dataFetcher("navNodeInfo", env -> getService(env).getNavigatorNodeInfo( getWebSession(env), - getProjectReference(env), env.getArgument("nodePath") )) .dataFetcher("navRefreshNode", env -> getService(env).refreshNavigatorNode( getWebSession(env), - getProjectReference(env), env.getArgument("nodePath") )) .dataFetcher("navGetStructContainers", env -> getService(env).getStructContainers( @@ -69,18 +65,15 @@ public class WebServiceBindingNavigator extends WebServiceBindingBase getService(env).renameNode( getWebSession(env), - getProjectReference(env), env.getArgument("nodePath"), env.getArgument("newName") )) .dataFetcher("navDeleteNodes", env -> getService(env).deleteNodes( getWebSession(env), - getProjectReference(env), env.getArgument("nodePaths") )) .dataFetcher("navMoveNodesToFolder", env -> getService(env).moveNodesToFolder( getWebSession(env), - getProjectReference(env), env.getArgument("nodePaths"), env.getArgument("folderPath") )); 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 9d763e406b..650819ebdb 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 @@ -68,7 +68,6 @@ public class WebServiceNavigator implements DBWServiceNavigator { @Override public List getNavigatorNodeChildren( @NotNull WebSession session, - @Nullable String projectId, @NotNull String parentPath, Integer offset, Integer limit, @@ -82,11 +81,11 @@ public class WebServiceNavigator implements DBWServiceNavigator { DBNModel navigatorModel = session.getNavigatorModel(); List applicableDrivers = CBPlatform.getInstance().getApplicableDrivers(); if (isRootPath) { - DBNProject projectNode = navigatorModel.getRoot().getProjectNode(session.getProjectById(projectId)); - nodeChildren = DBNUtils.getNodeChildrenFiltered(monitor, projectNode.getDatabases(), true); + DBNRoot rootNode = navigatorModel.getRoot(); + nodeChildren = DBNUtils.getNodeChildrenFiltered(monitor, rootNode, true); if (SHOW_EXTRA_NODES) { // Inject extra nodes. Disabled because we use different root path for extra nodes - List extraNodes = projectNode.getExtraNodes(); + List extraNodes = rootNode.getExtraNodes(); if (!extraNodes.isEmpty()) { nodeChildren = ArrayUtils.concatArrays(extraNodes.toArray(new DBNNode[0]), nodeChildren); } @@ -147,7 +146,6 @@ public class WebServiceNavigator implements DBWServiceNavigator { @Override public List getNavigatorNodeParents( @NotNull WebSession session, - @Nullable String projectId, String nodePath ) throws DBWebException { try { @@ -174,7 +172,6 @@ public class WebServiceNavigator implements DBWServiceNavigator { @NotNull public WebNavigatorNodeInfo getNavigatorNodeInfo( @NotNull WebSession session, - @Nullable String projectId, @NotNull String nodePath ) throws DBWebException { try { @@ -193,7 +190,6 @@ public class WebServiceNavigator implements DBWServiceNavigator { @Override public boolean refreshNavigatorNode( @NotNull WebSession session, - @Nullable String projectId, @NotNull String nodePath ) throws DBWebException { try { @@ -335,7 +331,6 @@ public class WebServiceNavigator implements DBWServiceNavigator { @Override public String renameNode( @NotNull WebSession session, - @Nullable String projectId, @NotNull String nodePath, @NotNull String newName ) throws DBWebException { @@ -376,7 +371,6 @@ public class WebServiceNavigator implements DBWServiceNavigator { @Override public int deleteNodes( @NotNull WebSession session, - @Nullable String projectId, @NotNull List nodePaths ) throws DBWebException { try { @@ -424,9 +418,7 @@ public class WebServiceNavigator implements DBWServiceNavigator { sessionRegistry.removeFolder(((DBNLocalFolder) ne.getKey()).getFolder(), false); } else if (ne.getKey() instanceof DBNResourceManagerResource) { DBNResourceManagerResource rmResource = ((DBNResourceManagerResource) ne.getKey()); - if (projectId == null) { - projectId = rmResource.getResourceProject().getId(); - } + String projectId = rmResource.getResourceProject().getId(); String resourcePath = rmResource.getResourceFolder(); session.getRmController().deleteResource(projectId, resourcePath, true); } @@ -444,7 +436,6 @@ public class WebServiceNavigator implements DBWServiceNavigator { @Override public boolean moveNodesToFolder( @NotNull WebSession session, - @Nullable String projectId, @NotNull List nodePaths, @NotNull String folderNodePath ) throws DBWebException { @@ -476,9 +467,7 @@ public class WebServiceNavigator implements DBWServiceNavigator { throw new DBWebException("Navigator node '" + folderNodePath + "' is not a resource manager node"); } // Get project id from node - if (projectId == null) { - projectId = rmOldNode.getResourceProject().getId(); - } + String projectId = rmOldNode.getResourceProject().getId(); // Get paths from nodes String newPath = rmOldNode.getResource().getName(); if (folderNode instanceof DBNResourceManagerResource) { diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/DBWServiceSQL.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/DBWServiceSQL.java index e17438f65c..34e935371c 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/DBWServiceSQL.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/DBWServiceSQL.java @@ -37,7 +37,11 @@ import java.util.Map; public interface DBWServiceSQL extends DBWService { @WebAction - WebSQLContextInfo[] listContexts(@NotNull WebSession session, @Nullable String connectionId, @Nullable String contextId) throws DBWebException; + WebSQLContextInfo[] listContexts( + @NotNull WebSession session, + @Nullable String projectId, + @Nullable String connectionId, + @Nullable String contextId) throws DBWebException; @WebAction WebSQLDialectInfo getDialectInfo(@NotNull WebConnectionInfo processor) throws DBWebException; diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebServiceBindingSQL.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebServiceBindingSQL.java index 0e37ee0be7..b237809063 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebServiceBindingSQL.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebServiceBindingSQL.java @@ -53,6 +53,7 @@ public class WebServiceBindingSQL extends WebServiceBindingBase i ) .dataFetcher("sqlListContexts", env -> getService(env).listContexts(getWebSession(env), + getProjectReference(env), env.getArgument("connectionId"), env.getArgument("contextId")) ) diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/impl/WebServiceSQL.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/impl/WebServiceSQL.java index 7813cd0e13..542bc9b295 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/impl/WebServiceSQL.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/impl/WebServiceSQL.java @@ -71,10 +71,15 @@ public class WebServiceSQL implements DBWServiceSQL { private static final Log log = Log.getLog(WebServiceSQL.class); @Override - public WebSQLContextInfo[] listContexts(@NotNull WebSession session, @Nullable String connectionId, @Nullable String contextId) throws DBWebException { + public WebSQLContextInfo[] listContexts( + @NotNull WebSession session, + @Nullable String projectId, + @Nullable String connectionId, + @Nullable String contextId + ) throws DBWebException { List conToRead = new ArrayList<>(); if (connectionId != null) { - WebConnectionInfo webConnection = WebServiceBindingBase.getWebConnection(session, connectionId); + WebConnectionInfo webConnection = WebServiceBindingBase.getWebConnection(session, projectId, connectionId); conToRead.add(webConnection); } else { conToRead.addAll(session.getConnections()); diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/local/LocalSessionHandler.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/local/LocalSessionHandler.java index bcfc001e5c..2421214560 100644 --- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/local/LocalSessionHandler.java +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/local/LocalSessionHandler.java @@ -57,16 +57,16 @@ public class LocalSessionHandler extends AbstractActionSessionHandler { List connectionInfoList = webSession.getConnections(); WebConnectionInfo connectionInfo = null; if (connectionId != null) { - connectionInfo = webSession.getWebConnectionInfo(connectionId); + connectionInfo = webSession.getWebConnectionInfo(null, connectionId); } else if (connectionName != null) { List filteredConnections = connectionInfoList.stream().filter(t -> t.getName().equals(connectionName)).collect(Collectors.toList()); if (filteredConnections.size() == 1) { - connectionInfo = webSession.getWebConnectionInfo(filteredConnections.get(0).getId()); + connectionInfo = webSession.getWebConnectionInfo(null, filteredConnections.get(0).getId()); } } else if (connectionURL != null) { List filteredConnections = connectionInfoList.stream().filter(t -> t.getUrl().equals(connectionURL)).collect(Collectors.toList()); if (filteredConnections.size() == 1) { - connectionInfo = webSession.getWebConnectionInfo(filteredConnections.get(0).getId()); + connectionInfo = webSession.getWebConnectionInfo(null, filteredConnections.get(0).getId()); } } if (connectionInfo == null) { diff --git a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/internal/CBEmbeddedSecurityController.java b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/internal/CBEmbeddedSecurityController.java index 2603d661e6..c8bfadb81b 100644 --- a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/internal/CBEmbeddedSecurityController.java +++ b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/internal/CBEmbeddedSecurityController.java @@ -922,7 +922,7 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen .filter((cred) -> { AuthPropertyDescriptor property = credProfile.getCredentialParameter(cred.getKey()); - return property != null && property.getEncryption() != AuthPropertyEncryption.none; + return property != null && property.getEncryption() == AuthPropertyEncryption.none; }) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); } diff --git a/webapp/packages/core-localization/src/LocalizationService.ts b/webapp/packages/core-localization/src/LocalizationService.ts index 4fe038cb59..9641210e5f 100644 --- a/webapp/packages/core-localization/src/LocalizationService.ts +++ b/webapp/packages/core-localization/src/LocalizationService.ts @@ -43,7 +43,7 @@ export class LocalizationService extends Bootstrap { } get defaultLanguage(): string { - if (this.pluginSettings.getValue('defaultLanguage')) { + if (this.pluginSettings.isValueDefault('defaultLanguage')) { return this.deprecatedPluginSettings.getValue('defaultLanguage'); } diff --git a/webapp/packages/core-projects/package.json b/webapp/packages/core-projects/package.json index 082a555f0a..586837dcb6 100644 --- a/webapp/packages/core-projects/package.json +++ b/webapp/packages/core-projects/package.json @@ -16,6 +16,8 @@ }, "peerDependencies": { "@cloudbeaver/core-di": "~0.1.0", + "@cloudbeaver/core-authentication": "~0.1.0", + "@cloudbeaver/core-sdk": "~0.1.0", "@cloudbeaver/core-root": "~0.1.0", "@cloudbeaver/core-settings": "~0.1.0", "@cloudbeaver/core-utils": "~0.1.0" diff --git a/webapp/packages/core-projects/src/ProjectsResource.ts b/webapp/packages/core-projects/src/ProjectsResource.ts index c57defb311..767ca16554 100644 --- a/webapp/packages/core-projects/src/ProjectsResource.ts +++ b/webapp/packages/core-projects/src/ProjectsResource.ts @@ -6,38 +6,41 @@ * you may not use this file except in compliance with the License. */ -import { computed, makeObservable } from 'mobx'; +import { runInAction } from 'mobx'; import { UserInfoResource } from '@cloudbeaver/core-authentication'; import { injectable } from '@cloudbeaver/core-di'; -import { GraphQLService, CachedDataResource, RmProject } from '@cloudbeaver/core-sdk'; +import { GraphQLService, ProjectInfo, CachedMapResource, CachedMapAllKey, ResourceKey, ResourceKeyUtils, resourceKeyList } from '@cloudbeaver/core-sdk'; -export type Project = Omit; +export type Project = ProjectInfo; @injectable() -export class ProjectsResource extends CachedDataResource { - get userProject(): Project | undefined { - return this.data.filter(project => !project.shared)[0]; - } - +export class ProjectsResource extends CachedMapResource { constructor( private readonly graphQLService: GraphQLService, private readonly userInfoResource: UserInfoResource, ) { super([]); + this.sync(this.userInfoResource); this.userInfoResource.onUserChange.addPostHandler(() => { - this.loaded = false; - this.markOutdated(); - }); - - makeObservable(this, { - userProject: computed, + this.clear(); }); } - protected async loader(): Promise { + protected async loader(key: ResourceKey): Promise> { + const all = ResourceKeyUtils.includes(key, CachedMapAllKey); + const { projects } = await this.graphQLService.sdk.getProjectList(); - return projects; + + runInAction(() => { + if (all) { + this.data.clear(); + } + + this.set(resourceKeyList(projects.map(project => project.id)), projects); + }); + + return this.data; } } \ No newline at end of file diff --git a/webapp/packages/core-projects/src/ProjectsService.ts b/webapp/packages/core-projects/src/ProjectsService.ts new file mode 100644 index 0000000000..c7ca924550 --- /dev/null +++ b/webapp/packages/core-projects/src/ProjectsService.ts @@ -0,0 +1,52 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2022 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 { computed, makeObservable } from 'mobx'; + +import { UserInfoResource } from '@cloudbeaver/core-authentication'; +import { injectable } from '@cloudbeaver/core-di'; + +import { Project, ProjectsResource } from './ProjectsResource'; + +@injectable() +export class ProjectsService { + get activeProject(): Project | undefined { + let project: Project | undefined; + + if (this.activeProjectId) { + project = this.projectsResource.get(this.activeProjectId); + } + + if (!project && this.userInfoResource.data) { + project = this.projectsResource.get(this.userInfoResource.data.userId); + } + + if (!project && this.projectsResource.values.length > 0) { + project = this.projectsResource.values[0]; + } + + return project; + } + + private activeProjectId: string | null; + + constructor( + private readonly projectsResource: ProjectsResource, + private readonly userInfoResource: UserInfoResource + ) { + this.activeProjectId = null; + + makeObservable(this, { + activeProject: computed, + }); + } + + setActiveProject(project: Project): void { + this.activeProjectId = project.id; + } +} \ No newline at end of file diff --git a/webapp/packages/core-projects/src/index.ts b/webapp/packages/core-projects/src/index.ts index 9b0d44ce39..e733cd6d45 100644 --- a/webapp/packages/core-projects/src/index.ts +++ b/webapp/packages/core-projects/src/index.ts @@ -1,2 +1,3 @@ export * from './ProjectsResource'; +export * from './ProjectsService'; export { manifest as coreProjectsManifest } from './manifest'; diff --git a/webapp/packages/core-projects/src/manifest.ts b/webapp/packages/core-projects/src/manifest.ts index fc3c58f809..ee5574c003 100644 --- a/webapp/packages/core-projects/src/manifest.ts +++ b/webapp/packages/core-projects/src/manifest.ts @@ -9,6 +9,7 @@ import type { PluginManifest } from '@cloudbeaver/core-di'; import { ProjectsResource } from './ProjectsResource'; +import { ProjectsService } from './ProjectsService'; export const manifest: PluginManifest = { info: { @@ -17,5 +18,6 @@ export const manifest: PluginManifest = { providers: [ ProjectsResource, + ProjectsService, ], }; diff --git a/webapp/packages/core-sdk/src/CustomGraphQLClient.ts b/webapp/packages/core-sdk/src/CustomGraphQLClient.ts index 1e1bc88d73..be8b4193f7 100644 --- a/webapp/packages/core-sdk/src/CustomGraphQLClient.ts +++ b/webapp/packages/core-sdk/src/CustomGraphQLClient.ts @@ -6,8 +6,9 @@ * you may not use this file except in compliance with the License. */ -import { GraphQLClient, ClientError } from 'graphql-request'; -import type { RequestDocument, Variables } from 'graphql-request/dist/types'; +import { GraphQLClient, ClientError, resolveRequestDocument } from 'graphql-request'; +import { parseRequestArgs } from 'graphql-request/dist/parseArgs'; +import type { RequestDocument, RequestOptions, Variables } from 'graphql-request/dist/types'; import type * as Dom from 'graphql-request/dist/types.dom'; import { GQLError } from './GQLError'; @@ -27,15 +28,16 @@ export class CustomGraphQLClient extends GraphQLClient { this.interceptors.push(interceptor); } - // @ts-expect-error should be fixed somehow + request(document: RequestDocument, variables?: V, requestHeaders?: Dom.RequestInit['headers']): Promise; + request(options: RequestOptions): Promise; request( - document: RequestDocument, + document: RequestDocument | RequestOptions, variables?: V, requestHeaders?: Dom.RequestInit['headers'] ): Promise { return this.interceptors.reduce( (accumulator, interceptor) => interceptor(accumulator), - this.overrideRequest(document as string, variables, requestHeaders) + this.overrideRequest(document, variables, requestHeaders) ); } @@ -59,13 +61,20 @@ export class CustomGraphQLClient extends GraphQLClient { } } - private async overrideRequest(query: string, variables?: Variables, requestHeaders?: Dom.RequestInit['headers']): Promise { + private async overrideRequest( + documentOrOptions: RequestDocument | RequestOptions, + variables?: V, + requestHeaders?: Dom.RequestInit['headers'] + ): Promise { this.blockRequestsReasonHandler(); try { + const requestOptions = parseRequestArgs(documentOrOptions, variables, requestHeaders); + const { query, operationName } = resolveRequestDocument(requestOptions.document); + const response = await this.rawRequest(query, variables, requestHeaders); // TODO: seems here can be undefined - return response.data as T; + return response.data; } catch (error: any) { if (isClientError(error)) { if (isObjectError(error)) { diff --git a/webapp/packages/core-sdk/src/queries/project/getProjectList.gql b/webapp/packages/core-sdk/src/queries/project/getProjectList.gql new file mode 100644 index 0000000000..11229bfb34 --- /dev/null +++ b/webapp/packages/core-sdk/src/queries/project/getProjectList.gql @@ -0,0 +1,7 @@ +query getProjectList { + projects: listProjects { + id, + name, + description, + } +} \ No newline at end of file diff --git a/webapp/packages/core-sdk/src/queries/resource-manager/getProjectList.gql b/webapp/packages/core-sdk/src/queries/resource-manager/getResourceProjectList.gql similarity index 57% rename from webapp/packages/core-sdk/src/queries/resource-manager/getProjectList.gql rename to webapp/packages/core-sdk/src/queries/resource-manager/getResourceProjectList.gql index de56777f92..a45ecc9e84 100644 --- a/webapp/packages/core-sdk/src/queries/resource-manager/getProjectList.gql +++ b/webapp/packages/core-sdk/src/queries/resource-manager/getResourceProjectList.gql @@ -1,7 +1,8 @@ -query getProjectList { +query getResourceProjectList { projects: rmListProjects { id, name, shared, + description, } } \ No newline at end of file diff --git a/webapp/packages/core-sdk/src/sdk.ts b/webapp/packages/core-sdk/src/sdk.ts index 340a3be106..702b861b24 100644 --- a/webapp/packages/core-sdk/src/sdk.ts +++ b/webapp/packages/core-sdk/src/sdk.ts @@ -358,7 +358,9 @@ export interface Mutation { openSession: SessionInfo; readLobValue: Scalars['String']; refreshSessionConnections?: Maybe; + rmCreateProject?: Maybe; rmCreateResource: Scalars['String']; + rmDeleteProject: Scalars['Boolean']; rmDeleteResource?: Maybe; rmMoveResource: Scalars['String']; rmWriteResourceStringContent: Scalars['String']; @@ -444,33 +446,39 @@ export interface MutationCloseConnectionArgs { export interface MutationCopyConnectionFromNodeArgs { config?: InputMaybe; nodePath: Scalars['String']; + projectId?: InputMaybe; } export interface MutationCreateConnectionArgs { config: ConnectionConfig; + projectId?: InputMaybe; } export interface MutationCreateConnectionFolderArgs { folderName: Scalars['String']; parentFolderPath?: InputMaybe; + projectId?: InputMaybe; } export interface MutationCreateConnectionFromTemplateArgs { connectionName?: InputMaybe; + projectId?: InputMaybe; templateId: Scalars['ID']; } export interface MutationDeleteConnectionArgs { id: Scalars['ID']; + projectId?: InputMaybe; } export interface MutationDeleteConnectionFolderArgs { folderPath: Scalars['ID']; + projectId?: InputMaybe; } @@ -484,18 +492,21 @@ export interface MutationInitConnectionArgs { export interface MutationNavDeleteNodesArgs { nodePaths: Array; + projectId?: InputMaybe; } export interface MutationNavMoveNodesToFolderArgs { folderPath: Scalars['ID']; nodePaths: Array; + projectId?: InputMaybe; } export interface MutationNavRenameNodeArgs { newName: Scalars['String']; nodePath: Scalars['ID']; + projectId?: InputMaybe; } @@ -518,6 +529,12 @@ export interface MutationReadLobValueArgs { } +export interface MutationRmCreateProjectArgs { + description?: InputMaybe; + projectName: Scalars['String']; +} + + export interface MutationRmCreateResourceArgs { isFolder: Scalars['Boolean']; projectId: Scalars['String']; @@ -525,6 +542,11 @@ export interface MutationRmCreateResourceArgs { } +export interface MutationRmDeleteProjectArgs { + projectId: Scalars['ID']; +} + + export interface MutationRmDeleteResourceArgs { projectId: Scalars['String']; recursive: Scalars['Boolean']; @@ -598,6 +620,7 @@ export interface MutationTestNetworkHandlerArgs { export interface MutationUpdateConnectionArgs { config: ConnectionConfig; + projectId?: InputMaybe; } @@ -634,6 +657,7 @@ export interface NavigatorNodeInfo { nodeDetails?: Maybe>; nodeType?: Maybe; object?: Maybe; + projectId?: Maybe; } export interface NavigatorSettings { @@ -771,6 +795,12 @@ export interface ProductInfo { version: Scalars['String']; } +export interface ProjectInfo { + description?: Maybe; + id: Scalars['String']; + name: Scalars['String']; +} + export interface Query { activeUser?: Maybe; allConnections: Array; @@ -807,6 +837,7 @@ export interface Query { listAuthProviderConfigurations: Array; listFeatureSets: Array; listPermissions: Array; + listProjects: Array; listRoles: Array; listUserProfileProperties: Array; listUsers: Array; @@ -1026,6 +1057,7 @@ export interface QueryNavGetStructContainersArgs { catalog?: InputMaybe; connectionId: Scalars['ID']; contextId?: InputMaybe; + projectId?: InputMaybe; } @@ -1034,21 +1066,25 @@ export interface QueryNavNodeChildrenArgs { offset?: InputMaybe; onlyFolders?: InputMaybe; parentPath: Scalars['ID']; + projectId?: InputMaybe; } export interface QueryNavNodeInfoArgs { nodePath: Scalars['ID']; + projectId?: InputMaybe; } export interface QueryNavNodeParentsArgs { nodePath: Scalars['ID']; + projectId?: InputMaybe; } export interface QueryNavRefreshNodeArgs { nodePath: Scalars['ID']; + projectId?: InputMaybe; } @@ -1218,6 +1254,8 @@ export interface QueryUserConnectionsArgs { } export interface RmProject { + canEditConnection: Scalars['Boolean']; + canEditResource: Scalars['Boolean']; createTime: Scalars['DateTime']; creator: Scalars['String']; description?: Maybe; @@ -1226,6 +1264,10 @@ export interface RmProject { shared: Scalars['Boolean']; } +export enum RmProjectPermission { + ResourceEdit = 'resource_edit' +} + export interface RmResource { folder: Scalars['Boolean']; length: Scalars['Int']; @@ -2297,6 +2339,11 @@ export type NavRenameNodeMutationVariables = Exact<{ export type NavRenameNodeMutation = { navRenameNode?: string }; +export type GetProjectListQueryVariables = Exact<{ [key: string]: never }>; + + +export type GetProjectListQuery = { projects: Array<{ id: string; name: string; description?: string }> }; + export type CreateResourceMutationVariables = Exact<{ projectId: Scalars['String']; resourcePath: Scalars['String']; @@ -2315,11 +2362,6 @@ export type DeleteResourceMutationVariables = Exact<{ export type DeleteResourceMutation = { rmDeleteResource?: boolean }; -export type GetProjectListQueryVariables = Exact<{ [key: string]: never }>; - - -export type GetProjectListQuery = { projects: Array<{ id: string; name: string; shared: boolean }> }; - export type GetResourceListQueryVariables = Exact<{ projectId: Scalars['String']; folder?: InputMaybe; @@ -2331,6 +2373,11 @@ export type GetResourceListQueryVariables = Exact<{ export type GetResourceListQuery = { resources: Array<{ name: string; folder: boolean; length: number }> }; +export type GetResourceProjectListQueryVariables = Exact<{ [key: string]: never }>; + + +export type GetResourceProjectListQuery = { projects: Array<{ id: string; name: string; shared: boolean; description?: string }> }; + export type MoveResourceMutationVariables = Exact<{ projectId: Scalars['String']; oldPath: Scalars['String']; @@ -3684,6 +3731,15 @@ export const NavRenameNodeDocument = ` navRenameNode(nodePath: $nodePath, newName: $newName) } `; +export const GetProjectListDocument = ` + query getProjectList { + projects: listProjects { + id + name + description + } +} + `; export const CreateResourceDocument = ` mutation createResource($projectId: String!, $resourcePath: String!, $isFolder: Boolean!) { rmCreateResource( @@ -3702,15 +3758,6 @@ export const DeleteResourceDocument = ` ) } `; -export const GetProjectListDocument = ` - query getProjectList { - projects: rmListProjects { - id - name - shared - } -} - `; export const GetResourceListDocument = ` query getResourceList($projectId: String!, $folder: String, $nameMask: String, $readProperties: Boolean, $readHistory: Boolean) { resources: rmListResources( @@ -3726,6 +3773,16 @@ export const GetResourceListDocument = ` } } `; +export const GetResourceProjectListDocument = ` + query getResourceProjectList { + projects: rmListProjects { + id + name + shared + description + } +} + `; export const MoveResourceDocument = ` mutation moveResource($projectId: String!, $oldPath: String!, $newPath: String!) { rmMoveResource( @@ -4229,18 +4286,21 @@ export function getSdk(client: GraphQLClient, withWrapper: SdkFunctionWrapper = navRenameNode(variables: NavRenameNodeMutationVariables, requestHeaders?: Dom.RequestInit['headers']): Promise { return withWrapper(wrappedRequestHeaders => client.request(NavRenameNodeDocument, variables, { ...requestHeaders, ...wrappedRequestHeaders }), 'navRenameNode', 'mutation'); }, + getProjectList(variables?: GetProjectListQueryVariables, requestHeaders?: Dom.RequestInit['headers']): Promise { + return withWrapper(wrappedRequestHeaders => client.request(GetProjectListDocument, variables, { ...requestHeaders, ...wrappedRequestHeaders }), 'getProjectList', 'query'); + }, createResource(variables: CreateResourceMutationVariables, requestHeaders?: Dom.RequestInit['headers']): Promise { return withWrapper(wrappedRequestHeaders => client.request(CreateResourceDocument, variables, { ...requestHeaders, ...wrappedRequestHeaders }), 'createResource', 'mutation'); }, deleteResource(variables: DeleteResourceMutationVariables, requestHeaders?: Dom.RequestInit['headers']): Promise { return withWrapper(wrappedRequestHeaders => client.request(DeleteResourceDocument, variables, { ...requestHeaders, ...wrappedRequestHeaders }), 'deleteResource', 'mutation'); }, - getProjectList(variables?: GetProjectListQueryVariables, requestHeaders?: Dom.RequestInit['headers']): Promise { - return withWrapper(wrappedRequestHeaders => client.request(GetProjectListDocument, variables, { ...requestHeaders, ...wrappedRequestHeaders }), 'getProjectList', 'query'); - }, getResourceList(variables: GetResourceListQueryVariables, requestHeaders?: Dom.RequestInit['headers']): Promise { return withWrapper(wrappedRequestHeaders => client.request(GetResourceListDocument, variables, { ...requestHeaders, ...wrappedRequestHeaders }), 'getResourceList', 'query'); }, + getResourceProjectList(variables?: GetResourceProjectListQueryVariables, requestHeaders?: Dom.RequestInit['headers']): Promise { + return withWrapper(wrappedRequestHeaders => client.request(GetResourceProjectListDocument, variables, { ...requestHeaders, ...wrappedRequestHeaders }), 'getResourceProjectList', 'query'); + }, moveResource(variables: MoveResourceMutationVariables, requestHeaders?: Dom.RequestInit['headers']): Promise { return withWrapper(wrappedRequestHeaders => client.request(MoveResourceDocument, variables, { ...requestHeaders, ...wrappedRequestHeaders }), 'moveResource', 'mutation'); }, diff --git a/webapp/packages/core-theming/src/ThemeSettingsService.test.ts b/webapp/packages/core-theming/src/ThemeSettingsService.test.ts index 3cc90236f4..459a85403f 100644 --- a/webapp/packages/core-theming/src/ThemeSettingsService.test.ts +++ b/webapp/packages/core-theming/src/ThemeSettingsService.test.ts @@ -35,7 +35,7 @@ const equalConfigA = { 'core.user': { defaultTheme: testValueA, } as IThemeSettings, - plugin: { + core: { theming: { defaultTheme: testValueA, } as IThemeSettings, @@ -46,7 +46,7 @@ const equalConfigB = { 'core.user': { defaultTheme: testValueB, } as IThemeSettings, - plugin: { + core: { theming: { defaultTheme: testValueB, } as IThemeSettings, diff --git a/webapp/packages/core-theming/src/ThemeSettingsService.ts b/webapp/packages/core-theming/src/ThemeSettingsService.ts index 451dc4887e..aaa3502808 100644 --- a/webapp/packages/core-theming/src/ThemeSettingsService.ts +++ b/webapp/packages/core-theming/src/ThemeSettingsService.ts @@ -26,7 +26,7 @@ export class ThemeSettingsService { readonly deprecatedSettings: PluginSettings; constructor(private readonly pluginManagerService: PluginManagerService) { - this.settings = this.pluginManagerService.getPluginSettings('theming', defaultThemeSettings); + this.settings = this.pluginManagerService.getCoreSettings('theming', defaultThemeSettings); this.deprecatedSettings = this.pluginManagerService.getCoreSettings('user', defaultThemeSettings); } } diff --git a/webapp/packages/plugin-resource-manager/src/NavNodes/ResourceFoldersBootstrap.ts b/webapp/packages/plugin-resource-manager/src/NavNodes/ResourceFoldersBootstrap.ts index 644a9aa5ac..11746be06f 100644 --- a/webapp/packages/plugin-resource-manager/src/NavNodes/ResourceFoldersBootstrap.ts +++ b/webapp/packages/plugin-resource-manager/src/NavNodes/ResourceFoldersBootstrap.ts @@ -16,7 +16,6 @@ import { NotificationService } from '@cloudbeaver/core-events'; import type { IExecutionContextProvider } from '@cloudbeaver/core-executor'; import { LocalizationService } from '@cloudbeaver/core-localization'; import { NavTreeResource, NavNodeManagerService, NavNodeInfoResource, type INodeMoveData, navNodeMoveContext, getNodesFromContext, ENodeMoveType, type NavNode } from '@cloudbeaver/core-navigation-tree'; -import { ProjectsResource } from '@cloudbeaver/core-projects'; import { ResourceKey, resourceKeyList, ResourceKeyUtils } from '@cloudbeaver/core-sdk'; import { createPath } from '@cloudbeaver/core-utils'; import { ActionService, MenuService, ACTION_NEW_FOLDER, DATA_CONTEXT_MENU, IAction, IDataContextProvider } from '@cloudbeaver/core-view'; @@ -25,6 +24,7 @@ import { DATA_CONTEXT_ELEMENTS_TREE, MENU_ELEMENTS_TREE_TOOLS, type IElementsTre import { NAV_NODE_TYPE_RM_PROJECT } from '../NAV_NODE_TYPE_RM_PROJECT'; import { NavResourceNodeService } from '../NavResourceNodeService'; import { ResourceManagerResource } from '../ResourceManagerResource'; +import { ResourceProjectsResource } from '../ResourceProjectsResource'; import { RESOURCES_NODE_PATH } from '../RESOURCES_NODE_PATH'; import { NAV_NODE_TYPE_RM_RESOURCE } from './NAV_NODE_TYPE_RM_RESOURCE'; @@ -39,7 +39,7 @@ export class ResourceFoldersBootstrap extends Bootstrap { private readonly navNodeManagerService: NavNodeManagerService, private readonly navResourceNodeService: NavResourceNodeService, private readonly resourceManagerResource: ResourceManagerResource, - private readonly projectsResource: ProjectsResource, + private readonly resourceProjectsResource: ResourceProjectsResource, private readonly commonDialogService: CommonDialogService, private readonly actionService: ActionService, private readonly menuService: MenuService, @@ -70,7 +70,7 @@ export class ResourceFoldersBootstrap extends Bootstrap { return true; } - untracked(async () => await this.projectsResource.load()); + untracked(async () => await this.resourceProjectsResource.load()); return this.getTargetNode(tree) === undefined; }, handler: this.elementsTreeActionHandler.bind(this), @@ -111,10 +111,10 @@ export class ResourceFoldersBootstrap extends Bootstrap { return; } - await this.projectsResource.load(); - const projectPath = createPath(RESOURCES_NODE_PATH, this.projectsResource.userProject?.id); + await this.resourceProjectsResource.load(); + const projectPath = createPath(RESOURCES_NODE_PATH, this.resourceProjectsResource.userProject?.id); - if (!(this.projectsResource.userProject?.id === data.key.projectId)) { + if (!(this.resourceProjectsResource.userProject?.id === data.key.projectId)) { return; } @@ -154,7 +154,7 @@ export class ResourceFoldersBootstrap extends Bootstrap { if (tree === undefined) { return; } - await this.projectsResource.load(); + await this.resourceProjectsResource.load(); switch (action) { case ACTION_NEW_FOLDER: { @@ -245,7 +245,7 @@ export class ResourceFoldersBootstrap extends Bootstrap { targetNode = this.navNodeInfoResource.get(targetFolder); } - const projectPath = createPath(RESOURCES_NODE_PATH, this.projectsResource.userProject?.id); + const projectPath = createPath(RESOURCES_NODE_PATH, this.resourceProjectsResource.userProject?.id); if (!targetNode?.id.startsWith(projectPath)) { targetNode = undefined; } diff --git a/webapp/packages/plugin-resource-manager/src/ResourceProjectsResource.ts b/webapp/packages/plugin-resource-manager/src/ResourceProjectsResource.ts new file mode 100644 index 0000000000..58f8381c0d --- /dev/null +++ b/webapp/packages/plugin-resource-manager/src/ResourceProjectsResource.ts @@ -0,0 +1,43 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2022 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 { computed, makeObservable } from 'mobx'; + +import { UserInfoResource } from '@cloudbeaver/core-authentication'; +import { injectable } from '@cloudbeaver/core-di'; +import { GraphQLService, CachedDataResource, RmProject } from '@cloudbeaver/core-sdk'; + +export type Project = Omit; + +@injectable() +export class ResourceProjectsResource extends CachedDataResource { + get userProject(): Project | undefined { + return this.data.filter(project => !project.shared)[0]; + } + + constructor( + private readonly graphQLService: GraphQLService, + private readonly userInfoResource: UserInfoResource, + ) { + super([]); + + this.userInfoResource.onUserChange.addPostHandler(() => { + this.loaded = false; + this.markOutdated(); + }); + + makeObservable(this, { + userProject: computed, + }); + } + + protected async loader(): Promise { + const { projects } = await this.graphQLService.sdk.getResourceProjectList(); + return projects; + } +} \ No newline at end of file diff --git a/webapp/packages/plugin-resource-manager/src/Tree/ResourceManagerTree.tsx b/webapp/packages/plugin-resource-manager/src/Tree/ResourceManagerTree.tsx index 2ad2447688..a244bfe1c0 100644 --- a/webapp/packages/plugin-resource-manager/src/Tree/ResourceManagerTree.tsx +++ b/webapp/packages/plugin-resource-manager/src/Tree/ResourceManagerTree.tsx @@ -12,11 +12,11 @@ import styled, { css } from 'reshadow'; import { Loader, useDataResource } from '@cloudbeaver/core-blocks'; import { useService } from '@cloudbeaver/core-di'; import { useTranslate } from '@cloudbeaver/core-localization'; -import { ProjectsResource } from '@cloudbeaver/core-projects'; import { createPath } from '@cloudbeaver/core-utils'; import { CaptureView } from '@cloudbeaver/core-view'; import { NavigationTreeService, ElementsTree, NavigationNodeControl } from '@cloudbeaver/plugin-navigation-tree'; +import { ResourceProjectsResource } from '../ResourceProjectsResource'; import { RESOURCES_NODE_PATH } from '../RESOURCES_NODE_PATH'; const styles = css` @@ -56,7 +56,7 @@ export const ResourceManagerTree = observer(function ResourceManagerTree() { const translate = useTranslate(); const navTreeService = useService(NavigationTreeService); - const { resource } = useDataResource(ResourceManagerTree, ProjectsResource, undefined); + const { resource } = useDataResource(ResourceManagerTree, ResourceProjectsResource, undefined); return styled(styles)( diff --git a/webapp/packages/plugin-resource-manager/src/index.ts b/webapp/packages/plugin-resource-manager/src/index.ts index ae7b19be16..a7c53a91ba 100644 --- a/webapp/packages/plugin-resource-manager/src/index.ts +++ b/webapp/packages/plugin-resource-manager/src/index.ts @@ -7,4 +7,5 @@ export * from './NavResourceNodeService'; export * from './ResourceManagerResource'; export * from './SaveScriptDialog'; export * from './ResourceManagerService'; +export * from './ResourceProjectsResource'; export * from './RESOURCES_NODE_PATH'; \ No newline at end of file diff --git a/webapp/packages/plugin-resource-manager/src/manifest.ts b/webapp/packages/plugin-resource-manager/src/manifest.ts index f61166b5ee..d5ea903ed0 100644 --- a/webapp/packages/plugin-resource-manager/src/manifest.ts +++ b/webapp/packages/plugin-resource-manager/src/manifest.ts @@ -15,6 +15,7 @@ import { PluginBootstrap } from './PluginBootstrap'; import { ResourceManagerResource } from './ResourceManagerResource'; import { ResourceManagerService } from './ResourceManagerService'; import { ResourceManagerSettingsService } from './ResourceManagerSettingsService'; +import { ResourceProjectsResource } from './ResourceProjectsResource'; export const resourceManagerPlugin: PluginManifest = { info: { name: 'Resource manager plugin' }, @@ -26,5 +27,6 @@ export const resourceManagerPlugin: PluginManifest = { ResourceManagerResource, NavResourceNodeService, ResourceFoldersBootstrap, + ResourceProjectsResource, ], }; \ No newline at end of file diff --git a/webapp/packages/plugin-sql-editor-navigation-tab-resource/src/PluginBootstrap.ts b/webapp/packages/plugin-sql-editor-navigation-tab-resource/src/PluginBootstrap.ts index ac2ebef6c9..ad452d3422 100644 --- a/webapp/packages/plugin-sql-editor-navigation-tab-resource/src/PluginBootstrap.ts +++ b/webapp/packages/plugin-sql-editor-navigation-tab-resource/src/PluginBootstrap.ts @@ -11,12 +11,11 @@ import { CommonDialogService, DialogueStateResult } from '@cloudbeaver/core-dial import { NotificationService } from '@cloudbeaver/core-events'; import type { IExecutionContextProvider } from '@cloudbeaver/core-executor'; import { NavNodeManagerService, NavTreeResource, NavNodeInfoResource, NodeManagerUtils, type INodeNavigationData, NavigationType } from '@cloudbeaver/core-navigation-tree'; -import { ProjectsResource } from '@cloudbeaver/core-projects'; import { DATA_CONTEXT_TAB_ID } from '@cloudbeaver/core-ui'; import { createPath } from '@cloudbeaver/core-utils'; import { ActionService, ACTION_SAVE, DATA_CONTEXT_MENU, MenuService } from '@cloudbeaver/core-view'; import { NavigationTabsService } from '@cloudbeaver/plugin-navigation-tabs'; -import { NavResourceNodeService, RESOURCE_NODE_TYPE, SaveScriptDialog, ResourceManagerService, RESOURCES_NODE_PATH } from '@cloudbeaver/plugin-resource-manager'; +import { NavResourceNodeService, RESOURCE_NODE_TYPE, SaveScriptDialog, ResourceManagerService, RESOURCES_NODE_PATH, ResourceProjectsResource } from '@cloudbeaver/plugin-resource-manager'; import { DATA_CONTEXT_SQL_EDITOR_STATE, getSqlEditorName, SqlDataSourceService, SqlEditorService, SqlEditorSettingsService, SQL_EDITOR_ACTIONS_MENU } from '@cloudbeaver/plugin-sql-editor'; import { isSQLEditorTab, SqlEditorNavigatorService } from '@cloudbeaver/plugin-sql-editor-navigation-tab'; @@ -37,7 +36,7 @@ export class PluginBootstrap extends Bootstrap { private readonly notificationService: NotificationService, private readonly sqlEditorNavigatorService: SqlEditorNavigatorService, private readonly resourceManagerService: ResourceManagerService, - private readonly projectsResource: ProjectsResource, + private readonly resourceProjectsResource: ResourceProjectsResource, private readonly sqlEditorTabResourceService: SqlEditorTabResourceService, private readonly commonDialogService: CommonDialogService, private readonly actionService: ActionService, @@ -86,9 +85,9 @@ export class PluginBootstrap extends Bootstrap { if (result !== DialogueStateResult.Rejected && result !== DialogueStateResult.Resolved) { try { - await this.projectsResource.load(); + await this.resourceProjectsResource.load(); const scriptName = `${result.trim()}.${SCRIPT_EXTENSION}`; - const folder = createPath(RESOURCES_NODE_PATH, this.projectsResource.userProject?.id); + const folder = createPath(RESOURCES_NODE_PATH, this.resourceProjectsResource.userProject?.id); const resourceData = this.navResourceNodeService.getResourceData(folder); if (!resourceData) {