[Feature-16801][UI] Add copy path of resource file in task definition (#16803)

This commit is contained in:
xiangzihao 2024-11-18 17:12:18 +08:00 committed by GitHub
parent 15adf21e45
commit f3b5d1f57d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 70 additions and 14 deletions

View File

@ -126,6 +126,7 @@ public class ResourceTreeVisitor implements Visitor {
tempResourceComponent.setName(resource.getFileName());
tempResourceComponent.setFullName(resource.getFullName());
tempResourceComponent.setType(resource.getType());
tempResourceComponent.setCurrentDir(resource.getRelativePath());
return tempResourceComponent;
}

View File

@ -43,8 +43,6 @@ public final class Constants {
public static final String FORMAT_S_S_COLON = "%s:%s";
public static final String FOLDER_SEPARATOR = "/";
public static final String RESOURCE_TYPE_FILE = "resources";
public static final String EMPTY_STRING = "";
/**

View File

@ -85,7 +85,7 @@ public abstract class AbstractStorageOperator implements StorageOperator {
String resourceBaseDirectory;
switch (resourceType) {
case FILE:
resourceBaseDirectory = FileUtils.concatFilePath(tenantBaseDirectory, FILE_FOLDER_NAME);
resourceBaseDirectory = FileUtils.concatFilePath(tenantBaseDirectory, StorageOperator.FILE_FOLDER_NAME);
break;
case ALL:
resourceBaseDirectory = tenantBaseDirectory;

View File

@ -46,4 +46,5 @@ public class StorageEntity {
private long size;
private Date createTime;
private Date updateTime;
private String relativePath;
}

View File

@ -25,7 +25,6 @@ import java.util.List;
public interface StorageOperator {
String FILE_FOLDER_NAME = "resources";
String UDF_FOLDER_NAME = "udfs";
ResourceMetadata getResourceMetaData(String resourceAbsolutePath);

View File

@ -306,6 +306,7 @@ public class CosStorageOperator extends AbstractStorageOperator implements Close
.type(resourceMetaData.getResourceType())
.isDirectory(StringUtils.isEmpty(fileExtension))
.size(metadata.getContentLength())
.relativePath(resourceMetaData.getResourceRelativePath())
.createTime(metadata.getLastModified())
.updateTime(metadata.getLastModified())
.build();

View File

@ -273,6 +273,7 @@ public class GcsStorageOperator extends AbstractStorageOperator implements Close
entity.setDirectory(resourceMetaData.isDirectory());
entity.setType(resourceMetaData.getResourceType());
entity.setSize(blob.getSize());
entity.setRelativePath(resourceMetaData.getResourceRelativePath());
entity.setCreateTime(Date.from(blob.getCreateTimeOffsetDateTime().toInstant()));
entity.setUpdateTime(Date.from(blob.getUpdateTimeOffsetDateTime().toInstant()));
return entity;

View File

@ -268,7 +268,6 @@ public class HdfsStorageOperator extends AbstractStorageOperator implements Clos
Path fileStatusPath = fileStatus.getPath();
String fileAbsolutePath = fileStatusPath.toString();
ResourceMetadata resourceMetaData = getResourceMetaData(fileAbsolutePath);
return StorageEntity.builder()
.fileName(fileStatusPath.getName())
.fullName(fileAbsolutePath)
@ -276,6 +275,7 @@ public class HdfsStorageOperator extends AbstractStorageOperator implements Clos
.type(resourceMetaData.getResourceType())
.isDirectory(fileStatus.isDirectory())
.size(fileStatus.getLen())
.relativePath(resourceMetaData.getResourceRelativePath())
.createTime(new Date(fileStatus.getModificationTime()))
.updateTime(new Date(fileStatus.getModificationTime()))
.build();

View File

@ -20,7 +20,6 @@ package org.apache.dolphinscheduler.plugin.storage.hdfs;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.apache.dolphinscheduler.common.constants.Constants;
import org.apache.dolphinscheduler.common.utils.FileUtils;
import org.apache.dolphinscheduler.plugin.storage.api.ResourceMetadata;
import org.apache.dolphinscheduler.plugin.storage.api.StorageEntity;
@ -47,7 +46,7 @@ class LocalStorageOperatorTest {
Paths.get(LocalStorageOperatorTest.class.getResource("/").getFile(), "localStorage").toString();
private static final String tenantCode = "default";
private static final String baseDir =
Paths.get(resourceBaseDir, tenantCode, StorageConstants.RESOURCE_TYPE_FILE).toString();
Paths.get(resourceBaseDir, tenantCode, StorageOperator.FILE_FOLDER_NAME).toString();
@SneakyThrows
@BeforeEach
@ -116,7 +115,7 @@ class LocalStorageOperatorTest {
public void testGetStorageBaseDirectory_withTenant_withResourceTypeFile() {
String storageBaseDirectory = storageOperator.getStorageBaseDirectory("default", ResourceType.FILE);
assertThat(storageBaseDirectory)
.isEqualTo("file:" + Paths.get(resourceBaseDir, tenantCode, Constants.RESOURCE_TYPE_FILE));
.isEqualTo("file:" + Paths.get(resourceBaseDir, tenantCode, StorageOperator.FILE_FOLDER_NAME));
}
@Test
@ -143,21 +142,21 @@ class LocalStorageOperatorTest {
public void testGetStorageFileAbsolutePath() {
String fileAbsolutePath = storageOperator.getStorageFileAbsolutePath("default", "test.sh");
assertThat(fileAbsolutePath).isEqualTo(
"file:" + Paths.get(resourceBaseDir, tenantCode, Constants.RESOURCE_TYPE_FILE, "test.sh"));
"file:" + Paths.get(resourceBaseDir, tenantCode, StorageOperator.FILE_FOLDER_NAME, "test.sh"));
}
@SneakyThrows
@Test
public void testCreateStorageDir_notExists() {
String testDirFileAbsolutePath =
"file:" + Paths.get(resourceBaseDir, "root", Constants.RESOURCE_TYPE_FILE, "testDir");
"file:" + Paths.get(resourceBaseDir, "root", StorageOperator.FILE_FOLDER_NAME, "testDir");
try {
storageOperator.createStorageDir(testDirFileAbsolutePath);
StorageEntity storageEntity = storageOperator.getStorageEntity(testDirFileAbsolutePath);
assertThat(storageEntity.getFullName()).isEqualTo(testDirFileAbsolutePath);
assertThat(storageEntity.getFileName()).isEqualTo("testDir");
assertThat(storageEntity.getPfullName())
.isEqualTo("file:" + Paths.get(resourceBaseDir, "root", Constants.RESOURCE_TYPE_FILE));
.isEqualTo("file:" + Paths.get(resourceBaseDir, "root", StorageOperator.FILE_FOLDER_NAME));
assertThat(storageEntity.isDirectory()).isTrue();
assertThat(storageEntity.getType()).isEqualTo(ResourceType.FILE);
} finally {
@ -169,7 +168,7 @@ class LocalStorageOperatorTest {
@Test
public void testCreateStorageDir_exists() {
String testDirFileAbsolutePath =
"file:" + Paths.get(resourceBaseDir, "default", Constants.RESOURCE_TYPE_FILE, "sqlDirectory");
"file:" + Paths.get(resourceBaseDir, "default", StorageOperator.FILE_FOLDER_NAME, "sqlDirectory");
assertThrows(FileAlreadyExistsException.class, () -> storageOperator.createStorageDir(testDirFileAbsolutePath));
}

View File

@ -260,6 +260,7 @@ public class ObsStorageOperator extends AbstractStorageOperator implements Close
.type(resourceMetaData.getResourceType())
.isDirectory(StringUtils.isEmpty(fileExtension))
.size(metadata.getContentLength())
.relativePath(resourceMetaData.getResourceRelativePath())
.createTime(metadata.getLastModified())
.updateTime(metadata.getLastModified())
.build();

View File

@ -331,6 +331,7 @@ public class OssStorageOperator extends AbstractStorageOperator implements Close
storageEntity.setType(resourceMetaData.getResourceType());
storageEntity.setDirectory(resourceMetaData.isDirectory());
storageEntity.setSize(ossObject.getObjectMetadata().getContentLength());
storageEntity.setRelativePath(resourceMetaData.getResourceRelativePath());
storageEntity.setCreateTime(ossObject.getObjectMetadata().getLastModified());
storageEntity.setUpdateTime(ossObject.getObjectMetadata().getLastModified());
return storageEntity;

View File

@ -294,6 +294,7 @@ public class S3StorageOperator extends AbstractStorageOperator implements Closea
entity.setDirectory(resourceMetaData.isDirectory());
entity.setType(resourceMetaData.getResourceType());
entity.setSize(object.getObjectMetadata().getContentLength());
entity.setRelativePath(resourceMetaData.getResourceRelativePath());
entity.setCreateTime(object.getObjectMetadata().getLastModified());
entity.setUpdateTime(object.getObjectMetadata().getLastModified());
return entity;

View File

@ -15,12 +15,15 @@
* limitations under the License.
*/
import { ref, onMounted, Ref, isRef } from 'vue'
import { ref, onMounted, Ref, isRef, h } from 'vue'
import { useI18n } from 'vue-i18n'
import { queryResourceList } from '@/service/modules/resources'
import { useTaskNodeStore } from '@/store/project/task-node'
import utils from '@/utils'
import type { IJsonItem, IResource } from '../types'
import { NButton, NIcon, NTag } from 'naive-ui'
import { CopyOutlined } from '@vicons/antd'
import { useClipboard } from '@vueuse/core'
export function useResources(
span: number | Ref<number> = 24,
@ -42,6 +45,9 @@ export function useResources(
const taskStore = useTaskNodeStore()
const source = ref('Hello')
const { copy, isSupported } = useClipboard({ source })
const getResources = async () => {
if (taskStore.resources.length) {
resourcesOptions.value = taskStore.resources
@ -116,6 +122,16 @@ export function useResources(
}
}
const copyResourceName = async (name: string) => {
if (isSupported.value) {
event?.stopPropagation()
await copy(name)
window.$message.success(t('project.node.copy_success'))
} else {
window.$message.error(t('project.node.copy_failed'))
}
}
onMounted(() => {
getResources()
})
@ -139,7 +155,44 @@ export function useResources(
keyField: 'fullName',
labelField: 'name',
disabledField: 'disable',
loading: resourcesLoading
loading: resourcesLoading,
'render-tag': ({
option,
handleClose
}: {
option: any
handleClose: any
}) => {
return h(
NTag,
{
type: 'success',
closable: true,
onClose: () => {
handleClose()
}
},
{
default: () => option.currentDir,
avatar: () =>
h(
NButton,
{
tag: 'div',
type: 'info',
size: 'tiny',
onClick: () => copyResourceName(option.currentDir)
},
{
icon: () =>
h(NIcon, null, {
default: () => h(CopyOutlined)
})
}
)
}
)
}
},
validate: {
trigger: ['blur'],