[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.setName(resource.getFileName());
tempResourceComponent.setFullName(resource.getFullName()); tempResourceComponent.setFullName(resource.getFullName());
tempResourceComponent.setType(resource.getType()); tempResourceComponent.setType(resource.getType());
tempResourceComponent.setCurrentDir(resource.getRelativePath());
return tempResourceComponent; 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 FORMAT_S_S_COLON = "%s:%s";
public static final String FOLDER_SEPARATOR = "/"; public static final String FOLDER_SEPARATOR = "/";
public static final String RESOURCE_TYPE_FILE = "resources";
public static final String EMPTY_STRING = ""; public static final String EMPTY_STRING = "";
/** /**

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -15,12 +15,15 @@
* limitations under the License. * 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 { useI18n } from 'vue-i18n'
import { queryResourceList } from '@/service/modules/resources' import { queryResourceList } from '@/service/modules/resources'
import { useTaskNodeStore } from '@/store/project/task-node' import { useTaskNodeStore } from '@/store/project/task-node'
import utils from '@/utils' import utils from '@/utils'
import type { IJsonItem, IResource } from '../types' 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( export function useResources(
span: number | Ref<number> = 24, span: number | Ref<number> = 24,
@ -42,6 +45,9 @@ export function useResources(
const taskStore = useTaskNodeStore() const taskStore = useTaskNodeStore()
const source = ref('Hello')
const { copy, isSupported } = useClipboard({ source })
const getResources = async () => { const getResources = async () => {
if (taskStore.resources.length) { if (taskStore.resources.length) {
resourcesOptions.value = taskStore.resources 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(() => { onMounted(() => {
getResources() getResources()
}) })
@ -139,7 +155,44 @@ export function useResources(
keyField: 'fullName', keyField: 'fullName',
labelField: 'name', labelField: 'name',
disabledField: 'disable', 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: { validate: {
trigger: ['blur'], trigger: ['blur'],