feat(接口测试): 添加执行文件下载接口
This commit is contained in:
parent
bc85e9e40d
commit
49942eb646
|
@ -1,14 +1,13 @@
|
||||||
package io.metersphere.api.controller;
|
package io.metersphere.api.controller;
|
||||||
|
|
||||||
import io.metersphere.api.service.ApiExecuteService;
|
import io.metersphere.api.service.ApiExecuteService;
|
||||||
|
import io.metersphere.sdk.file.FileRequest;
|
||||||
import io.metersphere.sdk.util.LogUtils;
|
import io.metersphere.sdk.util.LogUtils;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@ -27,6 +26,7 @@ public class ApiExecuteResourceController {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取执行脚本
|
* 获取执行脚本
|
||||||
|
*
|
||||||
* @param reportId
|
* @param reportId
|
||||||
* @param testId
|
* @param testId
|
||||||
* @return
|
* @return
|
||||||
|
@ -39,4 +39,18 @@ public class ApiExecuteResourceController {
|
||||||
stringRedisTemplate.delete(key);
|
stringRedisTemplate.delete(key);
|
||||||
return Optional.ofNullable(script).orElse(StringUtils.EMPTY);
|
return Optional.ofNullable(script).orElse(StringUtils.EMPTY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载执行所需的文件
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping("/file")
|
||||||
|
public void downloadFile(@RequestParam("reportId") String reportId,
|
||||||
|
@RequestParam("testId") String testId,
|
||||||
|
@RequestBody FileRequest fileRequest,
|
||||||
|
HttpServletResponse response) throws Exception {
|
||||||
|
apiExecuteService.downloadFile(reportId, testId, fileRequest, response);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,9 @@ import io.metersphere.sdk.dto.api.task.ApiExecuteFileInfo;
|
||||||
import io.metersphere.sdk.dto.api.task.ApiRunModeConfigDTO;
|
import io.metersphere.sdk.dto.api.task.ApiRunModeConfigDTO;
|
||||||
import io.metersphere.sdk.dto.api.task.TaskRequestDTO;
|
import io.metersphere.sdk.dto.api.task.TaskRequestDTO;
|
||||||
import io.metersphere.sdk.exception.MSException;
|
import io.metersphere.sdk.exception.MSException;
|
||||||
|
import io.metersphere.sdk.file.FileCenter;
|
||||||
|
import io.metersphere.sdk.file.FileRepository;
|
||||||
|
import io.metersphere.sdk.file.FileRequest;
|
||||||
import io.metersphere.sdk.util.*;
|
import io.metersphere.sdk.util.*;
|
||||||
import io.metersphere.system.config.MinioProperties;
|
import io.metersphere.system.config.MinioProperties;
|
||||||
import io.metersphere.system.domain.TestResourcePool;
|
import io.metersphere.system.domain.TestResourcePool;
|
||||||
|
@ -36,16 +39,22 @@ import io.metersphere.system.service.TestResourcePoolService;
|
||||||
import io.metersphere.system.utils.TaskRunnerClient;
|
import io.metersphere.system.utils.TaskRunnerClient;
|
||||||
import jakarta.annotation.PostConstruct;
|
import jakarta.annotation.PostConstruct;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import org.apache.commons.collections.CollectionUtils;
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
import org.apache.commons.collections.MapUtils;
|
import org.apache.commons.collections.MapUtils;
|
||||||
|
import org.apache.commons.lang3.BooleanUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.jmeter.util.JMeterUtils;
|
import org.apache.jmeter.util.JMeterUtils;
|
||||||
import org.springframework.context.i18n.LocaleContextHolder;
|
import org.springframework.context.i18n.LocaleContextHolder;
|
||||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@ -185,6 +194,9 @@ public class ApiExecuteService {
|
||||||
String endpoint = TaskRunnerClient.getEndpoint(testResourceNodeDTO.getIp(), testResourceNodeDTO.getPort());
|
String endpoint = TaskRunnerClient.getEndpoint(testResourceNodeDTO.getIp(), testResourceNodeDTO.getPort());
|
||||||
LogUtils.info(String.format("开始发送请求【 %s 】到 %s 节点执行", testId, endpoint), reportId);
|
LogUtils.info(String.format("开始发送请求【 %s 】到 %s 节点执行", testId, endpoint), reportId);
|
||||||
TaskRunnerClient.debugApi(endpoint, taskRequest);
|
TaskRunnerClient.debugApi(endpoint, taskRequest);
|
||||||
|
// 清空mino和kafka配置信息,避免前端获取
|
||||||
|
taskRequest.setMinioConfig(null);
|
||||||
|
taskRequest.setKafkaConfig(null);
|
||||||
return taskRequest;
|
return taskRequest;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LogUtils.error(e);
|
LogUtils.error(e);
|
||||||
|
@ -273,7 +285,7 @@ public class ApiExecuteService {
|
||||||
List<ApiExecuteFileInfo> refFiles = fileAssociationService.getFiles(request.getId()).
|
List<ApiExecuteFileInfo> refFiles = fileAssociationService.getFiles(request.getId()).
|
||||||
stream()
|
stream()
|
||||||
.map(file -> {
|
.map(file -> {
|
||||||
ApiExecuteFileInfo refFileInfo = getApiExecuteFileInfo(file.getFileId(), file.getFileName(),
|
ApiExecuteFileInfo refFileInfo = getApiExecuteFileInfo(file.getFileId(), file.getOriginalName(),
|
||||||
file.getProjectId(), file.getStorage());
|
file.getProjectId(), file.getStorage());
|
||||||
if (StorageType.isGit(file.getStorage())) {
|
if (StorageType.isGit(file.getStorage())) {
|
||||||
// 设置Git信息
|
// 设置Git信息
|
||||||
|
@ -286,7 +298,6 @@ public class ApiExecuteService {
|
||||||
// 没有保存的本地临时文件
|
// 没有保存的本地临时文件
|
||||||
List<String> uploadFileIds = request.getUploadFileIds();
|
List<String> uploadFileIds = request.getUploadFileIds();
|
||||||
if (CollectionUtils.isNotEmpty(uploadFileIds)) {
|
if (CollectionUtils.isNotEmpty(uploadFileIds)) {
|
||||||
// 去掉文件管理的文件,即通过本地上传的临时文件
|
|
||||||
List<ApiExecuteFileInfo> localTempFiles = uploadFileIds.stream()
|
List<ApiExecuteFileInfo> localTempFiles = uploadFileIds.stream()
|
||||||
.map(tempFileId -> {
|
.map(tempFileId -> {
|
||||||
String fileName = apiFileResourceService.getTempFileNameByFileId(tempFileId);
|
String fileName = apiFileResourceService.getTempFileNameByFileId(tempFileId);
|
||||||
|
@ -299,40 +310,15 @@ public class ApiExecuteService {
|
||||||
List<String> linkFileIds = request.getLinkFileIds();
|
List<String> linkFileIds = request.getLinkFileIds();
|
||||||
// 没有保存的文件管理临时文件
|
// 没有保存的文件管理临时文件
|
||||||
if (CollectionUtils.isNotEmpty(linkFileIds)) {
|
if (CollectionUtils.isNotEmpty(linkFileIds)) {
|
||||||
List<ApiExecuteFileInfo> refTempFiles = fileMetadataService.getByFileIds(linkFileIds)
|
List<FileMetadata> fileMetadataList = fileMetadataService.getByFileIds(linkFileIds);
|
||||||
.stream()
|
|
||||||
.map(file -> {
|
|
||||||
String fileName = file.getName();
|
|
||||||
if (StringUtils.isNotBlank(file.getType())) {
|
|
||||||
fileName += "." + file.getType();
|
|
||||||
}
|
|
||||||
ApiExecuteFileInfo tempFileInfo = getApiExecuteFileInfo(file.getId(), fileName,
|
|
||||||
file.getProjectId(), file.getStorage());
|
|
||||||
if (StorageType.isGit(file.getStorage())) {
|
|
||||||
// 设置Git信息
|
|
||||||
tempFileInfo.setFileMetadataRepositoryDTO(fileManagementService.getFileMetadataRepositoryDTO(file.getId()));
|
|
||||||
tempFileInfo.setFileModuleRepositoryDTO(fileManagementService.getFileModuleRepositoryDTO(file.getModuleId()));
|
|
||||||
}
|
|
||||||
return tempFileInfo;
|
|
||||||
}).toList();
|
|
||||||
// 添加临时的文件管理的文件
|
// 添加临时的文件管理的文件
|
||||||
refFiles.addAll(refTempFiles);
|
refFiles.addAll(getApiExecuteFileInfo(fileMetadataList));
|
||||||
}
|
}
|
||||||
|
|
||||||
taskRequest.setRefFiles(refFiles);
|
taskRequest.setRefFiles(refFiles);
|
||||||
// 获取函数jar包
|
// 获取函数jar包
|
||||||
List<FileMetadata> fileMetadataList = fileManagementService.findJarByProjectId(List.of(taskRequest.getProjectId()));
|
List<FileMetadata> fileMetadataList = fileManagementService.findJarByProjectId(List.of(taskRequest.getProjectId()));
|
||||||
taskRequest.setFuncJars(fileMetadataList.stream()
|
taskRequest.setFuncJars(getApiExecuteFileInfo(fileMetadataList));
|
||||||
.map(file -> {
|
|
||||||
String fileName = file.getOriginalName();
|
|
||||||
ApiExecuteFileInfo tempFileInfo = getApiExecuteFileInfo(file.getId(), fileName, file.getProjectId(), file.getStorage());
|
|
||||||
if (StorageType.isGit(file.getStorage())) {
|
|
||||||
// 设置Git信息
|
|
||||||
tempFileInfo.setFileMetadataRepositoryDTO(fileManagementService.getFileMetadataRepositoryDTO(file.getId()));
|
|
||||||
tempFileInfo.setFileModuleRepositoryDTO(fileManagementService.getFileModuleRepositoryDTO(file.getModuleId()));
|
|
||||||
}
|
|
||||||
return tempFileInfo;
|
|
||||||
}).toList());
|
|
||||||
|
|
||||||
// TODO 当前项目没有包分两种情况,1 之前存在被删除,2 一直不存在
|
// TODO 当前项目没有包分两种情况,1 之前存在被删除,2 一直不存在
|
||||||
// 为了兼容1 这种情况需要初始化一条空的数据,由执行机去做卸载
|
// 为了兼容1 这种情况需要初始化一条空的数据,由执行机去做卸载
|
||||||
|
@ -343,11 +329,25 @@ public class ApiExecuteService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ApiExecuteFileInfo getApiExecuteFileInfo(String fileId, String fileName, String projectId) {
|
private List<ApiExecuteFileInfo> getApiExecuteFileInfo(List<FileMetadata> fileMetadataList) {
|
||||||
|
return fileMetadataList.stream()
|
||||||
|
.map(file -> {
|
||||||
|
ApiExecuteFileInfo tempFileInfo = getApiExecuteFileInfo(file.getId(), file.getOriginalName(),
|
||||||
|
file.getProjectId(), file.getStorage());
|
||||||
|
if (StorageType.isGit(file.getStorage())) {
|
||||||
|
// 设置Git信息
|
||||||
|
tempFileInfo.setFileMetadataRepositoryDTO(fileManagementService.getFileMetadataRepositoryDTO(file.getId()));
|
||||||
|
tempFileInfo.setFileModuleRepositoryDTO(fileManagementService.getFileModuleRepositoryDTO(file.getModuleId()));
|
||||||
|
}
|
||||||
|
return tempFileInfo;
|
||||||
|
}).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ApiExecuteFileInfo getApiExecuteFileInfo(String fileId, String fileName, String projectId) {
|
||||||
return getApiExecuteFileInfo(fileId, fileName, projectId, StorageType.MINIO.name());
|
return getApiExecuteFileInfo(fileId, fileName, projectId, StorageType.MINIO.name());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ApiExecuteFileInfo getApiExecuteFileInfo(String fileId, String fileName, String projectId, String storage) {
|
private ApiExecuteFileInfo getApiExecuteFileInfo(String fileId, String fileName, String projectId, String storage) {
|
||||||
ApiExecuteFileInfo apiExecuteFileInfo = new ApiExecuteFileInfo();
|
ApiExecuteFileInfo apiExecuteFileInfo = new ApiExecuteFileInfo();
|
||||||
apiExecuteFileInfo.setStorage(storage);
|
apiExecuteFileInfo.setStorage(storage);
|
||||||
apiExecuteFileInfo.setFileName(fileName);
|
apiExecuteFileInfo.setFileName(fileName);
|
||||||
|
@ -412,4 +412,33 @@ public class ApiExecuteService {
|
||||||
}
|
}
|
||||||
return (String) configMap.get(ProjectApplicationType.API.API_RESOURCE_POOL_ID.name());
|
return (String) configMap.get(ProjectApplicationType.API.API_RESOURCE_POOL_ID.name());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void downloadFile(String reportId, String testId, FileRequest fileRequest, HttpServletResponse response) throws Exception {
|
||||||
|
String key = getScriptRedisKey(reportId, testId);
|
||||||
|
if (BooleanUtils.isTrue(stringRedisTemplate.hasKey(key))) {
|
||||||
|
FileRepository repository = StringUtils.isBlank(fileRequest.getStorage()) ? FileCenter.getDefaultRepository()
|
||||||
|
: FileCenter.getRepository(fileRequest.getStorage());
|
||||||
|
write2Response(repository.getFileAsStream(fileRequest), response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void write2Response(InputStream in, HttpServletResponse response) {
|
||||||
|
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
|
||||||
|
try (OutputStream out = response.getOutputStream()) {
|
||||||
|
int len;
|
||||||
|
byte[] bytes = new byte[1024 * 2];
|
||||||
|
while ((len = in.read(bytes)) != -1) {
|
||||||
|
out.write(bytes, 0, len);
|
||||||
|
}
|
||||||
|
out.flush();
|
||||||
|
} catch (Exception e) {
|
||||||
|
LogUtils.error(e);
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
in.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
LogUtils.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,11 +1,27 @@
|
||||||
package io.metersphere.api.controller;
|
package io.metersphere.api.controller;
|
||||||
|
|
||||||
|
import io.metersphere.api.service.ApiExecuteService;
|
||||||
|
import io.metersphere.api.service.debug.ApiDebugService;
|
||||||
|
import io.metersphere.sdk.constants.DefaultRepositoryDir;
|
||||||
|
import io.metersphere.sdk.constants.StorageType;
|
||||||
|
import io.metersphere.sdk.file.FileCopyRequest;
|
||||||
|
import io.metersphere.sdk.file.FileRequest;
|
||||||
import io.metersphere.system.base.BaseTest;
|
import io.metersphere.system.base.BaseTest;
|
||||||
|
import io.metersphere.system.uid.IDGenerator;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
import org.junit.jupiter.api.MethodOrderer;
|
import org.junit.jupiter.api.MethodOrderer;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.TestMethodOrder;
|
import org.junit.jupiter.api.TestMethodOrder;
|
||||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.mock.web.MockMultipartFile;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author: jianxing
|
* @Author: jianxing
|
||||||
|
@ -18,6 +34,13 @@ public class ApiExecuteResourceControllerTest extends BaseTest {
|
||||||
|
|
||||||
private static final String BASE_PATH = "/api/execute/resource/";
|
private static final String BASE_PATH = "/api/execute/resource/";
|
||||||
private static final String SCRIPT = "script?reportId={0}&testId={1}";
|
private static final String SCRIPT = "script?reportId={0}&testId={1}";
|
||||||
|
private static final String FILE = "file?reportId={0}&testId={1}";
|
||||||
|
@Resource
|
||||||
|
private StringRedisTemplate stringRedisTemplate;
|
||||||
|
@Resource
|
||||||
|
private ApiExecuteService apiExecuteService;
|
||||||
|
@Resource
|
||||||
|
private ApiDebugService apiDebugService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getBasePath() {
|
public String getBasePath() {
|
||||||
|
@ -28,4 +51,28 @@ public class ApiExecuteResourceControllerTest extends BaseTest {
|
||||||
public void getScript() throws Exception {
|
public void getScript() throws Exception {
|
||||||
this.requestGetWithOk(SCRIPT, "reportId", "testId");
|
this.requestGetWithOk(SCRIPT, "reportId", "testId");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void downloadFile() throws Exception {
|
||||||
|
String fileName = IDGenerator.nextStr() + "_file_upload.JPG";
|
||||||
|
MockMultipartFile file = new MockMultipartFile("file", fileName, MediaType.APPLICATION_OCTET_STREAM_VALUE, "aa".getBytes());
|
||||||
|
String fileId = apiDebugService.uploadTempFile(file);
|
||||||
|
FileRequest fileRequest = new FileCopyRequest();
|
||||||
|
fileRequest.setFileName(fileName);
|
||||||
|
fileRequest.setFolder(DefaultRepositoryDir.getSystemTempDir() + "/" + fileId);
|
||||||
|
mockMvc.perform(getPostRequestBuilder(FILE, fileRequest, "reportId", "testId"))
|
||||||
|
.andExpect(status().isOk());
|
||||||
|
|
||||||
|
String reportId = UUID.randomUUID().toString();
|
||||||
|
String testId = UUID.randomUUID().toString();
|
||||||
|
String scriptRedisKey = apiExecuteService.getScriptRedisKey(reportId, testId);
|
||||||
|
stringRedisTemplate.opsForValue().set(scriptRedisKey, "aaa");
|
||||||
|
mockMvc.perform(getPostRequestBuilder(FILE, fileRequest, reportId, testId))
|
||||||
|
.andExpect(status().isOk());
|
||||||
|
|
||||||
|
fileRequest.setStorage(StorageType.MINIO.name());
|
||||||
|
mockMvc.perform(getPostRequestBuilder(FILE, fileRequest, reportId, testId))
|
||||||
|
.andExpect(status().isOk());
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,9 @@ public class FileInfo implements Serializable {
|
||||||
@Schema(description = "文件名称")
|
@Schema(description = "文件名称")
|
||||||
private String fileName;
|
private String fileName;
|
||||||
|
|
||||||
|
@Schema(description = "原始文件名")
|
||||||
|
private String originalName;
|
||||||
|
|
||||||
@Schema(description = "文件大小")
|
@Schema(description = "文件大小")
|
||||||
private Long size;
|
private Long size;
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
file_association.id AS id,
|
file_association.id AS id,
|
||||||
file_association.file_id AS fileId,
|
file_association.file_id AS fileId,
|
||||||
CONCAT( file_metadata.`name`, IF(LENGTH(file_metadata.type) = 0, '', '.'), file_metadata.type ) AS fileName,
|
CONCAT( file_metadata.`name`, IF(LENGTH(file_metadata.type) = 0, '', '.'), file_metadata.type ) AS fileName,
|
||||||
|
file_metadata.original_name,
|
||||||
file_metadata.size AS size,
|
file_metadata.size AS size,
|
||||||
file_metadata.storage,
|
file_metadata.storage,
|
||||||
file_metadata.project_id,
|
file_metadata.project_id,
|
||||||
|
|
Loading…
Reference in New Issue