CB-3930 fs gql api (#2026)

* CB-3930 fs gql model

* CB-3930 fs manager

* CB-3930 add fs modules to server feature

* CB-3930 fs per project

* CB-3930 fix rm file store

* CB-3930 fixes
This commit is contained in:
Alexander Skoblikov
2023-10-03 16:52:14 +03:00
committed by GitHub
parent c874b4b440
commit 1f8dd642bc
34 changed files with 858 additions and 126 deletions
+9
View File
@@ -0,0 +1,9 @@
projects:
schema:
include: ./**/*.graphqls
schema: "gql.ce"
extensions:
endpoints:
Default GraphQL Endpoint:
url: http://localhost:8080/api/gql
introspect: false
@@ -1,50 +0,0 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2023 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.model.rm.fs.nio;
import org.jkiss.dbeaver.model.nio.NIOFileStore;
import org.jkiss.dbeaver.model.rm.RMResource;
import java.io.IOException;
public class RMNIOResourceFileStore extends NIOFileStore {
private final RMResource rmResource;
public RMNIOResourceFileStore(RMResource rmResource) {
this.rmResource = rmResource;
}
@Override
public String name() {
return rmResource.getName();
}
@Override
public String type() {
return "rmfs";
}
@Override
public boolean isReadOnly() {
return false;
}
@Override
public long getTotalSpace() throws IOException {
return rmResource.getLength();
}
}
@@ -51,6 +51,7 @@ import org.jkiss.dbeaver.model.app.DBPProject;
import org.jkiss.dbeaver.model.auth.*;
import org.jkiss.dbeaver.model.connection.DBPConnectionConfiguration;
import org.jkiss.dbeaver.model.exec.DBCException;
import org.jkiss.dbeaver.model.fs.DBFFileSystemManager;
import org.jkiss.dbeaver.model.meta.Association;
import org.jkiss.dbeaver.model.meta.Property;
import org.jkiss.dbeaver.model.navigator.DBNModel;
@@ -76,15 +77,15 @@ import org.jkiss.dbeaver.runtime.DBWorkbench;
import org.jkiss.dbeaver.runtime.jobs.DisconnectJob;
import org.jkiss.utils.CommonUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.lang.reflect.InvocationTargetException;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/**
* Web session.
@@ -617,7 +618,6 @@ public class WebSession extends BaseWebSession
log.error("Error closing web session tokens");
}
this.userContext.setUser(null);
super.close();
}
@@ -901,7 +901,11 @@ public class WebSession extends BaseWebSession
// Auth credentials provider
// Adds auth properties passed from web (by user)
@Override
public boolean provideAuthParameters(@NotNull DBRProgressMonitor monitor, @NotNull DBPDataSourceContainer dataSourceContainer, @NotNull DBPConnectionConfiguration configuration) {
public boolean provideAuthParameters(
@NotNull DBRProgressMonitor monitor,
@NotNull DBPDataSourceContainer dataSourceContainer,
@NotNull DBPConnectionConfiguration configuration
) {
try {
// Properties from nested auth sessions
for (DBACredentialsProvider contextCredentialsProvider : getContextCredentialsProviders()) {
@@ -1019,6 +1023,9 @@ public class WebSession extends BaseWebSession
}
public void deleteSessionProject(@Nullable WebProjectImpl project) {
if (project != null) {
project.dispose();
}
getWorkspace().removeProject(project);
if (navigatorModel != null) {
navigatorModel.getRoot().removeProject(project);
@@ -1046,6 +1053,15 @@ public class WebSession extends BaseWebSession
}
}
@NotNull
public DBFFileSystemManager getFileSystemManager(String projectId) throws DBException {
var project = getProjectById(projectId);
if (project == null) {
throw new DBException("Project not found: " + projectId);
}
return project.getFileSystemManager();
}
private class SessionProgressMonitor extends BaseProgressMonitor {
@Override
public void beginTask(String name, int totalWork) {
@@ -115,11 +115,7 @@ public class WebUserContext implements SMCredentialsProvider {
);
setRefreshToken(smRefreshToken);
setUserPermissions(smAuthPermissions.getPermissions());
this.securityController = application.createSecurityController(this);
this.adminSecurityController = application.getAdminSecurityController(this);
this.secretController = application.getSecretController(this);
this.rmController = application.createResourceController(this, workspace);
this.fileController = application.createFileController(this);
if (isSessionChanged) {
this.smSessionId = smAuthPermissions.getSessionId();
setUser(smAuthPermissions.getUserId() == null ? null : new WebUser(securityController.getCurrentUser()));
@@ -8,6 +8,8 @@ input PageInput {
offset: Int
}
directive @since(version: String!) on OBJECT|SCALAR|QUERY|MUTATION|FIELD|VARIABLE_DEFINITION|OBJECT|FIELD_DEFINITION|ARGUMENT_DEFINITION|INTERFACE|ENUM|ENUM_VALUE|INPUT_OBJECT|INPUT_FIELD_DEFINITION
type Query
type Mutation
@@ -0,0 +1,13 @@
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Web Service - File System
Bundle-SymbolicName: io.cloudbeaver.service.fs;singleton:=true
Bundle-Version: 1.0.0.qualifier
Bundle-Release-Date: 20230904
Bundle-RequiredExecutionEnvironment: JavaSE-11
Bundle-ActivationPolicy: lazy
Bundle-ClassPath: .
Require-Bundle: io.cloudbeaver.server,
io.cloudbeaver.service.admin
Automatic-Module-Name: io.cloudbeaver.service.fs
@@ -0,0 +1,6 @@
source.. = src/
output.. = target/classes/
bin.includes = .,\
META-INF/,\
schema/,\
plugin.xml
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.2"?>
<plugin>
<extension point="io.cloudbeaver.service">
<service id="file.system" label="File system" description="File system services"
class="io.cloudbeaver.service.fs.WebServiceBindingFS">
</service>
</extension>
</plugin>
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>io.cloudbeaver</groupId>
<artifactId>bundles</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.service.fs</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -0,0 +1,34 @@
type FSFile @since(version: "23.2.2") {
name: String!
length: Int!
folder: Boolean!
metaData: Object!
}
extend type Query @since(version: "23.2.2") {
fsListFileSystems(projectId: ID!): [String!]!
fsFile(projectId: ID!, fileURI: String!): FSFile!
fsListFiles(projectId: ID!, folderURI: String!): [FSFile!]!
# Reads file contents as string in UTF-8
fsReadFileContentAsString(projectId: ID!, fileURI: String!): String!
}
extend type Mutation @since(version: "23.2.2") {
fsCreateFile(projectId: ID!, fileURI:String!): FSFile!
fsCreateFolder(projectId: ID!, folderURI:String!): FSFile!
fsDeleteFile(projectId: ID!, fileURI:String!): Boolean!
fsMoveFile(projectId: ID!, fromURI: String!, toURI: String!): FSFile!
fsWriteFileStringContent(
projectId: ID!,
fileURI:String!,
data: String!,
forceOverwrite: Boolean!
): Boolean!
}
@@ -0,0 +1,90 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2023 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.service.fs;
import io.cloudbeaver.DBWebException;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.service.DBWService;
import io.cloudbeaver.service.fs.model.FSFile;
import org.jkiss.code.NotNull;
import java.net.URI;
/**
* Web service API
*/
public interface DBWServiceFS extends DBWService {
@NotNull
String[] getAvailableFileSystems(@NotNull WebSession webSession, @NotNull String projectId) throws DBWebException;
@NotNull
FSFile getFile(
@NotNull WebSession webSession,
@NotNull String projectId,
@NotNull URI fileURI
) throws DBWebException;
@NotNull
FSFile[] getFiles(
@NotNull WebSession webSession,
@NotNull String projectId,
@NotNull URI folderURI
) throws DBWebException;
@NotNull
String readFileContent(
@NotNull WebSession webSession,
@NotNull String projectId,
@NotNull URI fileURI
) throws DBWebException;
boolean writeFileContent(
@NotNull WebSession webSession,
@NotNull String projectId,
@NotNull URI fileURI,
@NotNull String data,
boolean forceOverwrite
) throws DBWebException;
@NotNull
FSFile createFile(
@NotNull WebSession webSession,
@NotNull String projectId,
@NotNull URI fileURI
) throws DBWebException;
FSFile moveFile(
@NotNull WebSession webSession,
@NotNull String projectId,
@NotNull URI fromURI,
@NotNull URI toURI
) throws DBWebException;
@NotNull
FSFile createFolder(
@NotNull WebSession webSession,
@NotNull String projectId,
@NotNull URI folderURI
) throws DBWebException;
boolean deleteFile(
@NotNull WebSession webSession,
@NotNull String projectId,
@NotNull URI fileURI
) throws DBWebException;
}
@@ -0,0 +1,91 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2023 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.service.fs;
import io.cloudbeaver.DBWebException;
import io.cloudbeaver.service.DBWBindingContext;
import io.cloudbeaver.service.WebServiceBindingBase;
import io.cloudbeaver.service.fs.impl.WebServiceFS;
import org.jkiss.utils.CommonUtils;
/**
* Web service implementation
*/
public class WebServiceBindingFS extends WebServiceBindingBase<DBWServiceFS> {
private static final String SCHEMA_FILE_NAME = "schema/service.fs.graphqls";
public WebServiceBindingFS() {
super(DBWServiceFS.class, new WebServiceFS(), SCHEMA_FILE_NAME);
}
@Override
public void bindWiring(DBWBindingContext model) throws DBWebException {
model.getQueryType()
.dataFetcher("fsListFileSystems",
env -> getService(env).getAvailableFileSystems(getWebSession(env), env.getArgument("projectId")))
.dataFetcher("fsFile",
env -> getService(env).getFile(getWebSession(env),
env.getArgument("projectId"),
env.getArgument("fileURI"))
)
.dataFetcher("fsListFiles",
env -> getService(env).getFiles(getWebSession(env),
env.getArgument("projectId"),
env.getArgument("folderURI"))
)
.dataFetcher("fsReadFileContentAsString",
env -> getService(env).readFileContent(getWebSession(env),
env.getArgument("projectId"),
env.getArgument("fileURI"))
)
;
model.getMutationType()
.dataFetcher("fsCreateFile",
env -> getService(env).createFile(getWebSession(env),
env.getArgument("projectId"),
env.getArgument("fileURI"))
)
.dataFetcher("fsCreateFolder",
env -> getService(env).createFolder(getWebSession(env),
env.getArgument("projectId"),
env.getArgument("folderURI"))
)
.dataFetcher("fsDeleteFile",
env -> getService(env).deleteFile(getWebSession(env),
env.getArgument("projectId"),
env.getArgument("fileURI"))
)
.dataFetcher("fsMoveFile",
env -> getService(env).moveFile(
getWebSession(env),
env.getArgument("projectId"),
env.getArgument("fromURI"),
env.getArgument("toURI"))
)
.dataFetcher("fsWriteFileStringContent",
env -> getService(env).writeFileContent(
getWebSession(env),
env.getArgument("projectId"),
env.getArgument("fileURI"),
env.getArgument("data"),
CommonUtils.toBoolean(env.getArgument("forceOverwrite"))
)
)
;
}
}
@@ -0,0 +1,174 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2023 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.service.fs.impl;
import io.cloudbeaver.DBWebException;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.service.fs.DBWServiceFS;
import io.cloudbeaver.service.fs.model.FSFile;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.model.fs.DBFVirtualFileSystem;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
/**
* Web file system implementation
*/
public class WebServiceFS implements DBWServiceFS {
@NotNull
@Override
public String[] getAvailableFileSystems(@NotNull WebSession webSession, @NotNull String projectId)
throws DBWebException {
try {
return webSession.getFileSystemManager(projectId)
.getVirtualFileSystems()
.stream()
.map(DBFVirtualFileSystem::getType)
.toArray(String[]::new);
} catch (Exception e) {
throw new DBWebException("Failed to load file systems: " + e.getMessage(), e);
}
}
@NotNull
@Override
public FSFile getFile(@NotNull WebSession webSession, @NotNull String projectId, @NotNull URI fileUri)
throws DBWebException {
try {
Path filePath = webSession.getFileSystemManager(projectId).of(webSession.getProgressMonitor(), fileUri);
return new FSFile(filePath);
} catch (Exception e) {
throw new DBWebException("Failed to found file: " + e.getMessage(), e);
}
}
@NotNull
@Override
public FSFile[] getFiles(@NotNull WebSession webSession, @NotNull String projectId, @NotNull URI folderURI)
throws DBWebException {
try {
Path folderPath = webSession.getFileSystemManager(projectId).of(webSession.getProgressMonitor(), folderURI);
try (var filesStream = Files.list(folderPath)) {
return filesStream.map(FSFile::new)
.toArray(FSFile[]::new);
}
} catch (Exception e) {
throw new DBWebException("Failed to list files: " + e.getMessage(), e);
}
}
@NotNull
@Override
public String readFileContent(@NotNull WebSession webSession, @NotNull String projectId, @NotNull URI fileUri)
throws DBWebException {
try {
Path filePath = webSession.getFileSystemManager(projectId).of(webSession.getProgressMonitor(), fileUri);
return Files.readString(filePath);
} catch (Exception e) {
throw new DBWebException("Failed to read file content: " + e.getMessage(), e);
}
}
@Override
public boolean writeFileContent(
@NotNull WebSession webSession,
@NotNull String projectId,
@NotNull URI fileURI,
@NotNull String data,
boolean forceOverwrite
)
throws DBWebException {
try {
Path filePath = webSession.getFileSystemManager(projectId).of(webSession.getProgressMonitor(), fileURI);
if (!forceOverwrite && Files.exists(filePath)) {
throw new DBException("Cannot overwrite exist file");
}
Files.writeString(filePath, data);
return true;
} catch (Exception e) {
throw new DBWebException("Failed to write file content: " + e.getMessage(), e);
}
}
@NotNull
@Override
public FSFile createFile(
@NotNull WebSession webSession,
@NotNull String projectId,
@NotNull URI fileUri
) throws DBWebException {
try {
Path filePath = webSession.getFileSystemManager(projectId).of(webSession.getProgressMonitor(), fileUri);
Files.createFile(filePath);
return new FSFile(filePath);
} catch (Exception e) {
throw new DBWebException("Failed to create file: " + e.getMessage(), e);
}
}
@Override
public FSFile moveFile(
@NotNull WebSession webSession,
@NotNull String projectId,
@NotNull URI fromURI,
@NotNull URI toURI
) throws DBWebException {
try {
Path from = webSession.getFileSystemManager(projectId).of(webSession.getProgressMonitor(), fromURI);
Path to = webSession.getFileSystemManager(projectId).of(webSession.getProgressMonitor(), toURI);
Files.move(from, to);
return new FSFile(to);
} catch (Exception e) {
throw new DBWebException("Failed to move file: " + e.getMessage(), e);
}
}
@NotNull
@Override
public FSFile createFolder(
@NotNull WebSession webSession,
@NotNull String projectId,
@NotNull URI folderURI
) throws DBWebException {
try {
Path folderPath = webSession.getFileSystemManager(projectId).of(webSession.getProgressMonitor(), folderURI);
Files.createDirectory(folderPath);
return new FSFile(folderPath);
} catch (Exception e) {
throw new DBWebException("Failed to create folder: " + e.getMessage(), e);
}
}
@Override
public boolean deleteFile(
@NotNull WebSession webSession,
@NotNull String projectId,
@NotNull URI fileUri
) throws DBWebException {
try {
Path filePath = webSession.getFileSystemManager(projectId).of(webSession.getProgressMonitor(), fileUri);
Files.delete(filePath);
return true;
} catch (Exception e) {
throw new DBWebException("Failed to create folder: " + e.getMessage(), e);
}
}
}
@@ -0,0 +1,59 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2023 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.service.fs.model;
import org.jkiss.dbeaver.model.meta.Property;
import org.jkiss.utils.ArrayUtils;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Map;
public class FSFile {
private final Path path;
public FSFile(Path path) {
this.path = path;
}
@Property
public String getName() {
String[] pathParts = path.getFileName().toString().split(path.getFileSystem().getSeparator());
if (ArrayUtils.isEmpty(pathParts)) {
return "";
}
return pathParts[pathParts.length - 1];
}
@Property
public long getLength() throws IOException {
return Files.size(path);
}
@Property
public boolean isFolder() {
return Files.isDirectory(path);
}
@Property
public Map<String, String> getMetaData() {
return Map.of();
}
}
@@ -0,0 +1,16 @@
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Resource manager NIO implementation
Bundle-SymbolicName: io.cloudbeaver.service.rm.nio;singleton:=true
Bundle-Version: 1.0.0.qualifier
Bundle-Release-Date: 20230927
Bundle-RequiredExecutionEnvironment: JavaSE-11
Bundle-ActivationPolicy: lazy
Bundle-ClassPath: .
Require-Bundle: io.cloudbeaver.model,
org.jkiss.dbeaver.model,
org.jkiss.dbeaver.model.nio
Export-Package: io.cloudbeaver.service.rm.nio,
io.cloudbeaver.service.rm.fs
Automatic-Module-Name: io.cloudbeaver.service.rm.nio
@@ -0,0 +1,5 @@
source.. = src/
output.. = target/classes/
bin.includes = .,\
META-INF/,\
plugin.xml
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.2"?>
<plugin>
<extension point="org.jkiss.dbeaver.fileSystem">
<fileSystemProvider id="rm-nio" label="RM NIO" class="io.cloudbeaver.service.rm.fs.RMVirtualFileSystemProvider">
</fileSystemProvider>
</extension>
</plugin>
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>io.cloudbeaver</groupId>
<artifactId>bundles</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.service.rm.nio</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -0,0 +1,81 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2023 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.service.rm.fs;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.service.rm.nio.RMNIOFileSystemProvider;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.model.DBPImage;
import org.jkiss.dbeaver.model.fs.DBFVirtualFileSystem;
import org.jkiss.dbeaver.model.fs.DBFVirtualFileSystemRoot;
import org.jkiss.dbeaver.model.rm.RMProject;
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
import java.net.URI;
import java.nio.file.Path;
public class RMVirtualFileSystem implements DBFVirtualFileSystem {
@NotNull
private final RMNIOFileSystemProvider rmNioFileSystemProvider;
@NotNull
private final RMProject rmProject;
public RMVirtualFileSystem(@NotNull WebSession webSession, @NotNull RMProject rmProject) {
this.rmNioFileSystemProvider = new RMNIOFileSystemProvider(webSession.getRmController());
this.rmProject = rmProject;
}
@NotNull
@Override
public String getFileSystemDisplayName() {
return "RM";
}
@NotNull
@Override
public String getType() {
return "rm";
}
@Override
public String getDescription() {
return "Resource Manager file system";
}
@Override
public DBPImage getIcon() {
return null;
}
@NotNull
@Override
public String getId() {
return rmProject.getId();
}
@Override
public Path getPathByURI(DBRProgressMonitor monitor, URI uri) {
return rmNioFileSystemProvider.getPath(uri);
}
@NotNull
@Override
public DBFVirtualFileSystemRoot[] getRootFolders(DBRProgressMonitor monitor) throws DBException {
return new RMVirtualFileSystemRoot[]{new RMVirtualFileSystemRoot(this, rmProject, rmNioFileSystemProvider)};
}
}
@@ -0,0 +1,48 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2023 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.service.rm.fs;
import io.cloudbeaver.WebProjectImpl;
import io.cloudbeaver.model.session.WebSession;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.app.DBPProject;
import org.jkiss.dbeaver.model.fs.DBFFileSystemProvider;
import org.jkiss.dbeaver.model.fs.DBFVirtualFileSystem;
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
public class RMVirtualFileSystemProvider implements DBFFileSystemProvider {
private static final Log log = Log.getLog(RMVirtualFileSystemProvider.class);
@Override
public DBFVirtualFileSystem[] getAvailableFileSystems(
@NotNull DBRProgressMonitor monitor,
@NotNull DBPProject project
) {
var session = project.getSessionContext().getPrimaryAuthSpace();
if (!(session instanceof WebSession)) {
return new DBFVirtualFileSystem[0];
}
WebSession webSession = (WebSession) session;
WebProjectImpl webProject = webSession.getProjectById(project.getId());
if (webProject == null) {
log.warn(String.format("Project %s not found in session %s", project.getId(), webSession.getSessionId()));
return new DBFVirtualFileSystem[0];
}
return new DBFVirtualFileSystem[]{new RMVirtualFileSystem(webSession, webProject.getRmProject())};
}
}
@@ -0,0 +1,77 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2023 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.service.rm.fs;
import io.cloudbeaver.service.rm.nio.RMNIOFileSystemProvider;
import io.cloudbeaver.service.rm.nio.RMNIOFileSystem;
import io.cloudbeaver.service.rm.nio.RMPath;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.model.DBPImage;
import org.jkiss.dbeaver.model.fs.DBFVirtualFileSystem;
import org.jkiss.dbeaver.model.fs.DBFVirtualFileSystemRoot;
import org.jkiss.dbeaver.model.rm.RMProject;
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
import java.nio.file.Path;
public class RMVirtualFileSystemRoot implements DBFVirtualFileSystemRoot {
@NotNull
private final RMVirtualFileSystem rmFileSystem;
@NotNull
private final RMProject rmProject;
@NotNull
private final RMNIOFileSystemProvider rmNioFileSystemProvider;
public RMVirtualFileSystemRoot(
@NotNull RMVirtualFileSystem rmFileSystem,
@NotNull RMProject rmProject,
@NotNull RMNIOFileSystemProvider rmNioFileSystemProvider
) {
this.rmFileSystem = rmFileSystem;
this.rmProject = rmProject;
this.rmNioFileSystemProvider = rmNioFileSystemProvider;
}
@NotNull
@Override
public String getName() {
return rmProject.getName();
}
@NotNull
@Override
public DBFVirtualFileSystem getFileSystem() {
return rmFileSystem;
}
@NotNull
@Override
public String getRootId() {
return rmProject.getId();
}
@Override
public DBPImage getRootIcon() {
return null;
}
@NotNull
@Override
public Path getRootPath(DBRProgressMonitor monitor) {
return new RMPath(new RMNIOFileSystem(rmProject.getId(), rmNioFileSystemProvider));
}
}
@@ -23,7 +23,7 @@
* questions.
*/
package io.cloudbeaver.model.rm.fs.nio;
package io.cloudbeaver.service.rm.nio;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.model.nio.ByteArrayChannel;
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.cloudbeaver.model.rm.fs.nio;
package io.cloudbeaver.service.rm.nio;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
@@ -41,19 +41,15 @@ public class RMNIOFileSystem extends NIOFileSystem {
// null for root 'rm://' path
private final String rmProjectId;
@NotNull
private final RMController rmController;
@NotNull
private final RMNIOFileSystemProvider rmNioFileSystemProvider;
@Nullable
private RMProject rmProject;
public RMNIOFileSystem(
@Nullable String rmProjectId,
@NotNull RMController rmController,
@NotNull RMNIOFileSystemProvider rmNioFileSystemProvider
) {
this.rmProjectId = rmProjectId;
this.rmController = rmController;
this.rmNioFileSystemProvider = rmNioFileSystemProvider;
}
@@ -119,7 +115,7 @@ public class RMNIOFileSystem extends NIOFileSystem {
@NotNull
public RMController getRmController() {
return rmController;
return rmNioFileSystemProvider.getRmController();
}
@Nullable
@@ -136,7 +132,7 @@ public class RMNIOFileSystem extends NIOFileSystem {
throw new IOException("Project id not specified");
}
try {
RMProject project = rmController.getProject(rmProjectId, false, false);
RMProject project = getRmController().getProject(rmProjectId, false, false);
if (project == null) {
throw new FileNotFoundException("Project not exist: " + rmProjectId);
}
@@ -146,4 +142,8 @@ public class RMNIOFileSystem extends NIOFileSystem {
throw new IOException("Failed to get project:" + e.getMessage(), e);
}
}
public RMNIOFileSystemProvider rmProvider() {
return rmNioFileSystemProvider;
}
}
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.cloudbeaver.model.rm.fs.nio;
package io.cloudbeaver.service.rm.nio;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.DBException;
@@ -53,8 +53,8 @@ public class RMNIOFileSystemProvider extends NIOFileSystemProvider {
}
@Override
public FileSystem newFileSystem(URI uri, Map<String, ?> env) throws IOException {
throw new FileAlreadyExistsException("RM file system already exist");
public FileSystem newFileSystem(URI uri, Map<String, ?> env) {
return getFileSystem(uri);
}
@Override
@@ -65,7 +65,7 @@ public class RMNIOFileSystemProvider extends NIOFileSystemProvider {
projectId = null;
}
try {
return new RMNIOFileSystem(projectId, rmController, this);
return new RMNIOFileSystem(projectId, this);
} catch (Exception e) {
throw new FileSystemNotFoundException("RM file system not found: " + e.getMessage());
}
@@ -78,7 +78,7 @@ public class RMNIOFileSystemProvider extends NIOFileSystemProvider {
if (CommonUtils.isEmpty(projectId)) {
projectId = null;
}
RMNIOFileSystem rmNioFileSystem = new RMNIOFileSystem(projectId, rmController, this);
RMNIOFileSystem rmNioFileSystem = new RMNIOFileSystem(projectId, this);
String resourcePath = uri.getPath();
if (CommonUtils.isNotEmpty(resourcePath) && projectId == null) {
throw new IllegalArgumentException("Project is not specified in URI");
@@ -91,10 +91,11 @@ public class RMNIOFileSystemProvider extends NIOFileSystemProvider {
}
@Override
public RMByteArrayChannel newByteChannel(Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs) throws IOException {
public RMByteArrayChannel newByteChannel(Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs)
throws IOException {
RMPath rmPath = (RMPath) path;
if (rmPath.isProjectPath()) {
throw new IllegalArgumentException("Cannot open channel for the project");
if (Files.isDirectory(rmPath)) {
throw new IllegalArgumentException("Cannot open channel for a folder");
}
try {
@@ -115,7 +116,8 @@ public class RMNIOFileSystemProvider extends NIOFileSystemProvider {
}
@Override
public DirectoryStream<Path> newDirectoryStream(Path dir, DirectoryStream.Filter<? super Path> filter) throws IOException {
public DirectoryStream<Path> newDirectoryStream(Path dir, DirectoryStream.Filter<? super Path> filter)
throws IOException {
RMPath rmDir = (RMPath) dir;
String rmDirPath = rmDir.getResourcePath();
String separator = rmDir.getFileSystem().getSeparator();
@@ -130,7 +132,6 @@ public class RMNIOFileSystemProvider extends NIOFileSystemProvider {
.map(rmProject -> (Path) new RMPath(
new RMNIOFileSystem(
rmProject.getId(),
rmController,
RMNIOFileSystemProvider.this
)
)
@@ -166,6 +167,9 @@ public class RMNIOFileSystemProvider extends NIOFileSystemProvider {
public void createDirectory(Path dir, FileAttribute<?>... attrs) throws IOException {
RMPath rmDir = (RMPath) dir;
try {
if (rmDir.isRmRootPath()) {
return;
}
if (rmDir.isProjectPath()) {
rmController.createProject(RMUtils.getProjectName(rmDir.getRmProjectId()), null);
} else {
@@ -180,7 +184,7 @@ public class RMNIOFileSystemProvider extends NIOFileSystemProvider {
public void delete(Path path) throws IOException {
RMPath rmPath = (RMPath) path;
if (rmPath.isRmRootPath()) {
throw new IOException("Can not delete root rm directory");
throw new IOException("Cannot delete root rm dir:" + path);
}
try {
String rmProjectId = rmPath.getRmProjectId();
@@ -205,7 +209,9 @@ public class RMNIOFileSystemProvider extends NIOFileSystemProvider {
RMPath rmSource = (RMPath) source;
RMPath rmTarget = (RMPath) target;
try {
rmController.moveResource(rmSource.getRmProjectId(), rmSource.getResourcePath(), rmTarget.getResourcePath());
rmController.moveResource(rmSource.getRmProjectId(),
rmSource.getResourcePath(),
rmTarget.getResourcePath());
} catch (DBException e) {
throw new IOException("Failed to move rm resource: " + e.getMessage(), e);
}
@@ -225,25 +231,19 @@ public class RMNIOFileSystemProvider extends NIOFileSystemProvider {
@Override
public FileStore getFileStore(Path path) throws IOException {
RMPath rmPath = (RMPath) path;
if (rmPath.isProjectPath()) {
return new RMNIOProjectFileStore(rmPath.getFileSystem().getRmProject());
} else {
try {
RMResource rmResource = rmController.getResource(rmPath.getRmProjectId(), rmPath.getResourcePath());
if (rmResource == null) {
throw new FileNotFoundException();
}
return new RMNIOResourceFileStore(rmResource);
} catch (DBException e) {
throw new IOException("Failed to ");
}
if (rmPath.isRmRootPath()) {
throw new IllegalArgumentException("Cannot create file store for path: " + path);
}
return new RMNIOProjectFileStore(rmPath.getFileSystem().getRmProject());
}
@Override
public void checkAccess(Path path, AccessMode... modes) throws IOException {
RMPath rmPath = (RMPath) path;
try {
if (rmPath.isRmRootPath()) {
return;
}
if (rmPath.isProjectPath()) {
if (rmController.getProject(rmPath.getRmProjectId(), false, false) == null) {
// we should throw error, otherwise Files.exists return true
@@ -272,8 +272,8 @@ public class RMNIOFileSystemProvider extends NIOFileSystemProvider {
return type.cast(new RMRootBasicAttribute());
}
if (rmPath.isProjectPath()) {
boolean projectNotExist = rmController.getProject(rmPath.getRmProjectId(), false, false) == null;
if (projectNotExist) {
boolean projectExist = rmController.getProject(rmPath.getRmProjectId(), false, false) != null;
if (!projectExist) {
throw new FileNotFoundException();
}
return type.cast(new RMRootBasicAttribute());
@@ -290,4 +290,9 @@ public class RMNIOFileSystemProvider extends NIOFileSystemProvider {
}
return null;
}
@NotNull
public RMController getRmController() {
return rmController;
}
}
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.cloudbeaver.model.rm.fs.nio;
package io.cloudbeaver.service.rm.nio;
import org.jkiss.dbeaver.model.nio.NIOFileStore;
import org.jkiss.dbeaver.model.rm.RMProject;
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.cloudbeaver.model.rm.fs.nio;
package io.cloudbeaver.service.rm.nio;
import org.jkiss.dbeaver.DBException;
@@ -14,12 +14,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.cloudbeaver.model.rm.fs.nio;
package io.cloudbeaver.service.rm.nio;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.model.nio.NIOPath;
import org.jkiss.utils.ArrayUtils;
import org.jkiss.utils.CommonUtils;
import java.io.IOException;
@@ -58,7 +59,7 @@ public class RMPath extends NIOPath {
@Override
public Path getRoot() {
if (isProjectPath()) {
if (isProjectPath() || isRmRootPath()) {
return null;
}
return new RMPath(rmNioFileSystem);
@@ -66,13 +67,16 @@ public class RMPath extends NIOPath {
@Override
public Path getFileName() {
return this;
var parts = pathParts();
if (ArrayUtils.isEmpty(parts)) {
return this;
}
return new RMPath(rmNioFileSystem, parts[parts.length - 1]);
}
@Override
public Path getParent() {
// project is root and have no parent
if (isProjectPath()) {
if (isRmRootPath() || isProjectPath()) {
return null;
}
@@ -90,14 +94,6 @@ public class RMPath extends NIOPath {
return pathParts().length;
}
@Override
public boolean startsWith(@NotNull Path other) {
if (!(other instanceof RMPath)) {
return false;
}
return toString().startsWith(other.toString());
}
@Override
public Path normalize() {
return this;
@@ -127,8 +123,11 @@ public class RMPath extends NIOPath {
public URI toUri() {
var fileSystem = getFileSystem();
var uriBuilder = new StringBuilder(fileSystem.provider().getScheme())
.append("://")
.append(rmProjectId);
.append("://");
if (rmProjectId != null) {
uriBuilder.append(rmProjectId);
}
String rmResourcePath = getResourcePath();
if (rmResourcePath != null) {
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.cloudbeaver.model.rm.fs.nio;
package io.cloudbeaver.service.rm.nio;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.model.nio.NIOFileBasicAttribute;
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.cloudbeaver.model.rm.fs.nio;
package io.cloudbeaver.service.rm.nio;
import org.jkiss.dbeaver.model.nio.NIOFileBasicAttribute;
@@ -34,6 +34,7 @@ input RMSubjectProjectPermissions {
permissions: [String!]!
}
input RMProjectPermissions {
projectId: String!
permissions: [String!]!
@@ -59,42 +60,41 @@ extend type Query {
folder: String,
nameMask: String,
readProperties: Boolean,
readHistory: Boolean): [RMResource!]!
readHistory: Boolean): [RMResource!]! @deprecated
# Reads resource contents as string in UTF-8
rmReadResourceAsString(
projectId: String!,
resourcePath: String!): String!
resourcePath: String!): String! @deprecated
}
extend type Mutation {
rmCreateResource(
projectId: String!,
resourcePath: String!,
isFolder: Boolean!): String!
isFolder: Boolean!): String! @deprecated
rmMoveResource(
projectId: String!,
oldResourcePath: String!,
newResourcePath: String): String!
newResourcePath: String): String! @deprecated
rmDeleteResource(
projectId: String!,
resourcePath: String!,
recursive: Boolean!): Boolean
recursive: Boolean!): Boolean @deprecated
rmWriteResourceStringContent(
projectId: String!,
resourcePath: String!,
data: String!,
forceOverwrite: Boolean!): String!
forceOverwrite: Boolean!): String! @deprecated
rmCreateProject(
projectId: ID,
projectName: String!,
description: String): RMProject!
description: String): RMProject! @deprecated
rmDeleteProject(projectId: ID!): Boolean!
+2
View File
@@ -19,7 +19,9 @@
<module>io.cloudbeaver.service.admin</module>
<module>io.cloudbeaver.service.auth</module>
<module>io.cloudbeaver.service.metadata</module>
<module>io.cloudbeaver.service.fs</module>
<module>io.cloudbeaver.service.rm</module>
<module>io.cloudbeaver.service.rm.nio</module>
<module>io.cloudbeaver.service.data.transfer</module>
<module>io.cloudbeaver.service.security</module>
@@ -36,7 +36,9 @@
<plugin id="io.cloudbeaver.service.auth" download-size="0" install-size="0" version="0.0.0" unpack="false"/>
<plugin id="io.cloudbeaver.service.data.transfer" download-size="0" install-size="0" version="0.0.0" unpack="false"/>
<plugin id="io.cloudbeaver.service.metadata" download-size="0" install-size="0" version="0.0.0" unpack="false"/>
<plugin id="io.cloudbeaver.service.fs" download-size="0" install-size="0" version="0.0.0" unpack="false"/>
<plugin id="io.cloudbeaver.service.rm" download-size="0" install-size="0" version="0.0.0" unpack="false"/>
<plugin id="io.cloudbeaver.service.rm.nio" download-size="0" install-size="0" version="0.0.0" unpack="false"/>
<plugin id="io.cloudbeaver.service.security" download-size="0" install-size="0" version="0.0.0" unpack="false"/>
<!-- Extra plugins -->
@@ -24,5 +24,6 @@ Require-Bundle: org.eclipse.core.runtime,
io.cloudbeaver.resources.drivers.base,
io.cloudbeaver.product.ce,
io.cloudbeaver.service.auth,
io.cloudbeaver.service.rm
io.cloudbeaver.service.rm,
io.cloudbeaver.service.rm.nio
Export-Package: io.cloudbeaver.test.platform
@@ -16,11 +16,11 @@
*/
package io.cloudbeaver.model.rm;
import io.cloudbeaver.model.rm.fs.nio.RMNIOFileSystem;
import io.cloudbeaver.model.rm.fs.nio.RMNIOFileSystemProvider;
import io.cloudbeaver.model.rm.fs.nio.RMPath;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.server.CBConstants;
import io.cloudbeaver.service.rm.nio.RMNIOFileSystem;
import io.cloudbeaver.service.rm.nio.RMNIOFileSystemProvider;
import io.cloudbeaver.service.rm.nio.RMPath;
import io.cloudbeaver.test.platform.CEServerTestSuite;
import io.cloudbeaver.utils.WebTestUtils;
import org.jkiss.dbeaver.DBException;
@@ -96,7 +96,7 @@ public class RMNIOTest {
@Test
public void testNotExistProject() {
RMPath notExistPath = new RMPath(new RMNIOFileSystem("s_not_exist", webSession.getRmController(), rmFsProvider));
RMPath notExistPath = new RMPath(new RMNIOFileSystem("s_not_exist", rmFsProvider));
Assert.assertFalse(Files.exists(notExistPath));
}
@@ -105,7 +105,7 @@ public class RMNIOTest {
var randomName = SecurityUtils.generateUniqueId();
String randomProject = "s_random_project_" + randomName;
RMPath newProjectPath = new RMPath(new RMNIOFileSystem(randomProject, webSession.getRmController(), rmFsProvider));
RMPath newProjectPath = new RMPath(new RMNIOFileSystem(randomProject, rmFsProvider));
Assert.assertFalse(Files.exists(newProjectPath));
//create project via nio