feat(接口测试): 场景导出
This commit is contained in:
parent
3b437fe180
commit
710d51a0bc
|
@ -55,7 +55,6 @@ public class LocalRepositoryDir {
|
|||
public static String getSystemTempDir() {
|
||||
return SYSTEM_TEMP_DIR;
|
||||
}
|
||||
|
||||
public static String getSystemCacheDir() {
|
||||
return SYSTEM_CACHE_DIR;
|
||||
}
|
||||
|
|
|
@ -4,8 +4,10 @@ import io.metersphere.sdk.dto.FileMetadataRepositoryDTO;
|
|||
import io.metersphere.sdk.dto.FileModuleRepositoryDTO;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class FileRequest {
|
||||
|
||||
private String folder;
|
||||
|
@ -22,6 +24,12 @@ public class FileRequest {
|
|||
public void setGitFileRequest(FileModuleRepositoryDTO repository, FileMetadataRepositoryDTO file) {
|
||||
gitFileRequest = new GitFileRequest(repository.getUrl(), repository.getToken(), repository.getUserName(), file.getBranch(), file.getCommitId());
|
||||
}
|
||||
|
||||
public FileRequest(String folder, String storage, String fileName) {
|
||||
this.folder = folder;
|
||||
this.storage = storage;
|
||||
this.fileName = fileName;
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package io.metersphere.sdk.util;
|
||||
|
||||
import io.metersphere.sdk.exception.MSException;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
|
@ -138,4 +139,21 @@ public class TempFileUtils {
|
|||
}
|
||||
return previewByte;
|
||||
}
|
||||
|
||||
public static void writeExportFile(String fileAbsoluteName, Object exportResponse) {
|
||||
File createFile = new File(fileAbsoluteName);
|
||||
if (!createFile.exists()) {
|
||||
try {
|
||||
createFile.getParentFile().mkdirs();
|
||||
createFile.createNewFile();
|
||||
} catch (IOException e) {
|
||||
throw new MSException(e);
|
||||
}
|
||||
}
|
||||
try {
|
||||
FileUtils.writeByteArrayToFile(createFile, JSON.toJSONString(exportResponse).getBytes());
|
||||
} catch (Exception e) {
|
||||
LogUtils.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
package io.metersphere.api.constants;
|
||||
|
||||
public enum ApiScenarioExportType {
|
||||
METERSPHERE_SIMPLE,
|
||||
METERSPHERE_ALL_DATA
|
||||
}
|
|
@ -37,6 +37,7 @@ import io.metersphere.system.utils.SessionUtils;
|
|||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.shiro.authz.annotation.Logical;
|
||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||
|
@ -330,4 +331,20 @@ public class ApiScenarioController {
|
|||
request.setOperator(SessionUtils.getUserId());
|
||||
apiScenarioDataTransferService.importScenario(file, request);
|
||||
}
|
||||
|
||||
@GetMapping("/stop/{taskId}")
|
||||
@Operation(summary = "接口测试-接口场景管理-导出-停止导出")
|
||||
@RequiresPermissions(PermissionConstants.PROJECT_API_SCENARIO_EXPORT)
|
||||
@CheckOwner(resourceId = "#projectId", resourceType = "project")
|
||||
public void caseStopExport(@PathVariable String taskId) {
|
||||
apiScenarioDataTransferService.stopExport(taskId, SessionUtils.getUserId());
|
||||
}
|
||||
|
||||
@GetMapping(value = "/download/file/{projectId}/{fileId}")
|
||||
@Operation(summary = "接口测试-接口场景管理-下载文件")
|
||||
@RequiresPermissions(PermissionConstants.PROJECT_API_SCENARIO_EXPORT)
|
||||
@CheckOwner(resourceId = "#projectId", resourceType = "project")
|
||||
public void downloadImgById(@PathVariable String projectId, @PathVariable String fileId, HttpServletResponse httpServletResponse) {
|
||||
apiScenarioDataTransferService.downloadFile(projectId, fileId, SessionUtils.getUserId(), httpServletResponse);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import java.io.Serializable;
|
|||
* @author wx
|
||||
*/
|
||||
@Data
|
||||
public class ApiExportResponse implements Serializable {
|
||||
public class ApiDefinitionExportResponse implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
|
@ -0,0 +1,21 @@
|
|||
package io.metersphere.api.dto.export;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author wx
|
||||
*/
|
||||
@Data
|
||||
public class ApiScenarioExportResponse implements Serializable {
|
||||
|
||||
private String organizationId;
|
||||
|
||||
private String projectId;
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package io.metersphere.api.dto.export;
|
||||
|
||||
import io.metersphere.api.domain.ApiScenarioStep;
|
||||
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ApiScenarioStepExportDTO extends ApiScenarioStep {
|
||||
private AbstractMsTestElement stepComponent;
|
||||
}
|
|
@ -10,7 +10,7 @@ import java.util.List;
|
|||
* @author wx
|
||||
*/
|
||||
@Data
|
||||
public class MetersphereApiExportResponse extends ApiExportResponse {
|
||||
public class MetersphereApiDefinitionExportResponse extends ApiDefinitionExportResponse {
|
||||
|
||||
private List<ApiDefinitionExportDetail> apiDefinitions = new ArrayList<>();
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
package io.metersphere.api.dto.export;
|
||||
|
||||
import io.metersphere.api.constants.ApiScenarioStepType;
|
||||
import io.metersphere.api.domain.ApiScenarioCsv;
|
||||
import io.metersphere.api.dto.converter.ApiDefinitionDetail;
|
||||
import io.metersphere.api.dto.definition.ApiTestCaseDTO;
|
||||
import io.metersphere.api.dto.scenario.ApiScenarioDetail;
|
||||
import io.metersphere.api.dto.scenario.ApiScenarioStepDTO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author wx
|
||||
*/
|
||||
@Data
|
||||
public class MetersphereApiScenarioExportResponse extends ApiScenarioExportResponse {
|
||||
|
||||
@Schema(description = "导出的场景")
|
||||
private List<ApiScenarioDetail> exportScenarioList = new ArrayList<>();
|
||||
|
||||
@Schema(description = "场景CSV相关的数据")
|
||||
private List<ApiScenarioCsv> apiScenarioCsvList = new ArrayList<>();
|
||||
|
||||
@Schema(description = "所有场景步骤")
|
||||
private List<ApiScenarioStepDTO> scenarioStepList = new ArrayList<>();
|
||||
|
||||
@Schema(description = "所有场景步骤内容")
|
||||
private Map<String, String> scenarioStepBlobMap = new HashMap<>();
|
||||
|
||||
|
||||
@Schema(description = "有关联的接口定义")
|
||||
private List<ApiDefinitionDetail> relatedApiDefinitions = new ArrayList<>();
|
||||
|
||||
@Schema(description = "有关联的接口用例")
|
||||
private List<ApiTestCaseDTO> relatedApiTestCaseList = new ArrayList<>();
|
||||
|
||||
@Schema(description = "有关联的场景")
|
||||
private List<ApiScenarioDetail> relatedScenarioList = new ArrayList<>();
|
||||
|
||||
public void addExportApi(ApiDefinitionDetail detail) {
|
||||
relatedApiDefinitions.add(detail);
|
||||
}
|
||||
|
||||
public void addExportApiCase(ApiTestCaseDTO apiCase) {
|
||||
relatedApiTestCaseList.add(apiCase);
|
||||
}
|
||||
|
||||
public void addExportScenario(ApiScenarioDetail apiScenarioDetail) {
|
||||
exportScenarioList.add(apiScenarioDetail);
|
||||
}
|
||||
|
||||
public void setStepTypeToCustomRequest() {
|
||||
scenarioStepList.forEach(step -> {
|
||||
if (StringUtils.equalsAnyIgnoreCase(step.getStepType(), ApiScenarioStepType.API.name(), ApiScenarioStepType.API_CASE.name())) {
|
||||
step.setStepType(ApiScenarioStepType.CUSTOM_REQUEST.name());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -9,7 +9,7 @@ import java.util.List;
|
|||
* @author wx
|
||||
*/
|
||||
@Data
|
||||
public class SwaggerApiExportResponse extends ApiExportResponse{
|
||||
public class SwaggerApiDefinitionExportResponse extends ApiDefinitionExportResponse {
|
||||
|
||||
private String openapi;
|
||||
private SwaggerInfo info;
|
|
@ -14,7 +14,7 @@ public class ApiScenarioImportRequest {
|
|||
@NotBlank
|
||||
private String projectId;
|
||||
|
||||
@Schema(description = "导入的类型 暂定 METERSPHERE JMETER")
|
||||
@Schema(description = "导入的类型 METERSPHERE/JMETER")
|
||||
@NotBlank
|
||||
private String type;
|
||||
|
||||
|
|
|
@ -764,5 +764,4 @@
|
|||
WHERE module_id = #{0}
|
||||
AND deleted = false
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
|
|
|
@ -2,8 +2,8 @@ package io.metersphere.api.parser.api;
|
|||
|
||||
import io.metersphere.api.dto.converter.ApiDefinitionExportDetail;
|
||||
import io.metersphere.api.dto.definition.*;
|
||||
import io.metersphere.api.dto.export.ApiExportResponse;
|
||||
import io.metersphere.api.dto.export.MetersphereApiExportResponse;
|
||||
import io.metersphere.api.dto.export.ApiDefinitionExportResponse;
|
||||
import io.metersphere.api.dto.export.MetersphereApiDefinitionExportResponse;
|
||||
import io.metersphere.api.dto.mockserver.MockMatchRule;
|
||||
import io.metersphere.api.dto.mockserver.MockResponse;
|
||||
import io.metersphere.api.utils.ApiDataUtils;
|
||||
|
@ -18,12 +18,12 @@ import java.util.stream.Collectors;
|
|||
|
||||
public class MetersphereExportParser {
|
||||
|
||||
public ApiExportResponse parse(List<ApiDefinitionWithBlob> apiDefinitionList, List<ApiTestCaseWithBlob> apiTestCaseList, List<ApiMockWithBlob> apiMockList, Map<String, String> moduleMap) {
|
||||
public ApiDefinitionExportResponse parse(List<ApiDefinitionWithBlob> apiDefinitionList, List<ApiTestCaseWithBlob> apiTestCaseList, List<ApiMockWithBlob> apiMockList, Map<String, String> moduleMap) {
|
||||
|
||||
Map<String, List<ApiTestCaseWithBlob>> apiTestCaseMap = apiTestCaseList.stream().collect(Collectors.groupingBy(ApiTestCaseWithBlob::getApiDefinitionId));
|
||||
Map<String, List<ApiMockWithBlob>> apiMockMap = apiMockList.stream().collect(Collectors.groupingBy(ApiMockWithBlob::getApiDefinitionId));
|
||||
|
||||
MetersphereApiExportResponse response = new MetersphereApiExportResponse();
|
||||
MetersphereApiDefinitionExportResponse response = new MetersphereApiDefinitionExportResponse();
|
||||
for (ApiDefinitionWithBlob blob : apiDefinitionList) {
|
||||
ApiDefinitionExportDetail detail = new ApiDefinitionExportDetail();
|
||||
if (blob.getRequest() != null) {
|
||||
|
|
|
@ -24,12 +24,12 @@ import org.springframework.http.MediaType;
|
|||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
|
||||
public class Swagger3ExportParser implements ExportParser<ApiExportResponse> {
|
||||
public class Swagger3ExportParser implements ExportParser<ApiDefinitionExportResponse> {
|
||||
|
||||
|
||||
@Override
|
||||
public ApiExportResponse parse(List<ApiDefinitionWithBlob> list, Project project, Map<String, String> moduleMap) throws Exception {
|
||||
SwaggerApiExportResponse response = new SwaggerApiExportResponse();
|
||||
public ApiDefinitionExportResponse parse(List<ApiDefinitionWithBlob> list, Project project, Map<String, String> moduleMap) throws Exception {
|
||||
SwaggerApiDefinitionExportResponse response = new SwaggerApiDefinitionExportResponse();
|
||||
//openapi
|
||||
response.setOpenapi("3.0.2");
|
||||
//info
|
||||
|
|
|
@ -7,7 +7,7 @@ import io.metersphere.api.dto.converter.ApiDefinitionImportDataAnalysisResult;
|
|||
import io.metersphere.api.dto.converter.ApiDefinitionImportFileParseResult;
|
||||
import io.metersphere.api.dto.definition.ApiDefinitionMockDTO;
|
||||
import io.metersphere.api.dto.definition.ApiTestCaseDTO;
|
||||
import io.metersphere.api.dto.export.MetersphereApiExportResponse;
|
||||
import io.metersphere.api.dto.export.MetersphereApiDefinitionExportResponse;
|
||||
import io.metersphere.api.dto.request.ImportRequest;
|
||||
import io.metersphere.api.parser.ApiDefinitionImportParser;
|
||||
import io.metersphere.api.utils.ApiDataUtils;
|
||||
|
@ -29,9 +29,9 @@ public class MetersphereParserApiDefinition implements ApiDefinitionImportParser
|
|||
|
||||
@Override
|
||||
public ApiDefinitionImportFileParseResult parse(InputStream source, ImportRequest request) throws Exception {
|
||||
MetersphereApiExportResponse metersphereApiExportResponse = null;
|
||||
MetersphereApiDefinitionExportResponse metersphereApiExportResponse = null;
|
||||
try {
|
||||
metersphereApiExportResponse = ApiDataUtils.parseObject(source, MetersphereApiExportResponse.class);
|
||||
metersphereApiExportResponse = ApiDataUtils.parseObject(source, MetersphereApiDefinitionExportResponse.class);
|
||||
} catch (Exception e) {
|
||||
LogUtils.error(e.getMessage(), e);
|
||||
throw new MSException(e.getMessage());
|
||||
|
|
|
@ -1,23 +1,35 @@
|
|||
package io.metersphere.api.service;
|
||||
|
||||
import io.metersphere.api.constants.ApiScenarioExportType;
|
||||
import io.metersphere.api.constants.ApiScenarioStepType;
|
||||
import io.metersphere.api.domain.*;
|
||||
import io.metersphere.api.dto.ApiFile;
|
||||
import io.metersphere.api.dto.converter.ApiDefinitionExportDetail;
|
||||
import io.metersphere.api.dto.converter.ApiScenarioPreImportAnalysisResult;
|
||||
import io.metersphere.api.dto.definition.ApiScenarioBatchExportRequest;
|
||||
import io.metersphere.api.dto.definition.*;
|
||||
import io.metersphere.api.dto.export.ApiScenarioExportResponse;
|
||||
import io.metersphere.api.dto.export.MetersphereApiScenarioExportResponse;
|
||||
import io.metersphere.api.dto.scenario.*;
|
||||
import io.metersphere.api.mapper.*;
|
||||
import io.metersphere.api.parser.ApiScenarioImportParser;
|
||||
import io.metersphere.api.parser.ImportParserFactory;
|
||||
import io.metersphere.api.service.definition.ApiDefinitionExportService;
|
||||
import io.metersphere.api.service.scenario.ApiScenarioLogService;
|
||||
import io.metersphere.api.service.scenario.ApiScenarioModuleService;
|
||||
import io.metersphere.api.service.scenario.ApiScenarioService;
|
||||
import io.metersphere.api.utils.ApiDataUtils;
|
||||
import io.metersphere.api.utils.ApiDefinitionImportUtils;
|
||||
import io.metersphere.api.utils.ApiScenarioImportUtils;
|
||||
import io.metersphere.functional.domain.ExportTask;
|
||||
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
|
||||
import io.metersphere.project.domain.Project;
|
||||
import io.metersphere.project.mapper.ExtBaseProjectVersionMapper;
|
||||
import io.metersphere.project.mapper.ProjectMapper;
|
||||
import io.metersphere.sdk.constants.DefaultRepositoryDir;
|
||||
import io.metersphere.sdk.constants.ModuleConstants;
|
||||
import io.metersphere.project.utils.FileDownloadUtils;
|
||||
import io.metersphere.sdk.constants.*;
|
||||
import io.metersphere.sdk.dto.ExportMsgDTO;
|
||||
import io.metersphere.sdk.exception.MSException;
|
||||
import io.metersphere.sdk.file.FileRequest;
|
||||
import io.metersphere.sdk.util.*;
|
||||
import io.metersphere.system.constants.ExportConstants;
|
||||
import io.metersphere.system.domain.User;
|
||||
|
@ -30,9 +42,13 @@ import io.metersphere.system.manager.ExportTaskManager;
|
|||
import io.metersphere.system.mapper.UserMapper;
|
||||
import io.metersphere.system.notice.constants.NoticeConstants;
|
||||
import io.metersphere.system.service.CommonNoticeSendService;
|
||||
import io.metersphere.system.service.FileService;
|
||||
import io.metersphere.system.service.NoticeSendService;
|
||||
import io.metersphere.system.socket.ExportWebSocketHandler;
|
||||
import io.metersphere.system.uid.IDGenerator;
|
||||
import io.metersphere.system.utils.TreeNodeParseUtils;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.collections4.ListUtils;
|
||||
|
@ -46,6 +62,8 @@ import org.springframework.stereotype.Service;
|
|||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
@ -56,6 +74,12 @@ import static io.metersphere.project.utils.NodeSortUtils.DEFAULT_NODE_INTERVAL_P
|
|||
@Transactional(rollbackFor = Exception.class)
|
||||
public class ApiScenarioDataTransferService {
|
||||
|
||||
private final ThreadLocal<Long> currentApiScenarioOrder = new ThreadLocal<>();
|
||||
|
||||
private final ThreadLocal<Long> currentModuleOrder = new ThreadLocal<>();
|
||||
|
||||
private static final String EXPORT_CASE_TMP_DIR = "apiScenario";
|
||||
|
||||
@Resource
|
||||
private ProjectMapper projectMapper;
|
||||
@Resource
|
||||
|
@ -66,7 +90,13 @@ public class ApiScenarioDataTransferService {
|
|||
private ExportTaskManager exportTaskManager;
|
||||
@Resource
|
||||
private ExtApiScenarioMapper extApiScenarioMapper;
|
||||
@Resource
|
||||
private ExtApiDefinitionMapper extApiDefinitionMapper;
|
||||
@Resource
|
||||
private ExtApiTestCaseMapper extApiTestCaseMapper;
|
||||
|
||||
@Resource
|
||||
private FileService fileService;
|
||||
@Resource
|
||||
private CommonNoticeSendService commonNoticeSendService;
|
||||
@Resource
|
||||
|
@ -79,17 +109,17 @@ public class ApiScenarioDataTransferService {
|
|||
private SqlSessionFactory sqlSessionFactory;
|
||||
@Resource
|
||||
private OperationLogService operationLogService;
|
||||
|
||||
private final ThreadLocal<Long> currentApiScenarioOrder = new ThreadLocal<>();
|
||||
|
||||
private final ThreadLocal<Long> currentModuleOrder = new ThreadLocal<>();
|
||||
|
||||
@Resource
|
||||
private NoticeSendService noticeSendService;
|
||||
@Resource
|
||||
private ApiScenarioLogService apiScenarioLogService;
|
||||
@Resource
|
||||
private ApiDefinitionExportService apiDefinitionExportService;
|
||||
|
||||
public String exportScenario(ApiScenarioBatchExportRequest request, String type, String userId) {
|
||||
String returnId;
|
||||
try {
|
||||
exportTaskManager.exportCheck(request.getProjectId(), ExportConstants.ExportType.API_SCENARIO.toString(), userId);
|
||||
|
||||
returnId = exportTaskManager.exportAsyncTask(
|
||||
request.getProjectId(),
|
||||
request.getFileId(), userId,
|
||||
|
@ -323,7 +353,7 @@ public class ApiScenarioDataTransferService {
|
|||
module.setParentId(t.getParentId());
|
||||
module.setProjectId(projectId);
|
||||
module.setCreateUser(operator);
|
||||
module.setPos(getImportNextModuleOrder(apiScenarioModuleService::getNextOrder, projectId));
|
||||
module.setPos(getImportNextOrder(apiScenarioModuleService::getNextOrder, currentModuleOrder, projectId));
|
||||
module.setCreateTime(System.currentTimeMillis());
|
||||
module.setUpdateUser(operator);
|
||||
module.setUpdateTime(module.getCreateTime());
|
||||
|
@ -352,7 +382,7 @@ public class ApiScenarioDataTransferService {
|
|||
ApiScenario scenario = new ApiScenario();
|
||||
BeanUtils.copyBean(scenario, t);
|
||||
scenario.setNum(apiScenarioService.getNextNum(projectId));
|
||||
scenario.setPos(getImportNextModuleOrder(apiScenarioService::getNextOrder, projectId));
|
||||
scenario.setPos(getImportNextOrder(apiScenarioService::getNextOrder, currentApiScenarioOrder, projectId));
|
||||
scenario.setLatest(true);
|
||||
scenario.setCreateUser(operator);
|
||||
scenario.setUpdateUser(operator);
|
||||
|
@ -479,13 +509,13 @@ public class ApiScenarioDataTransferService {
|
|||
}
|
||||
}
|
||||
|
||||
private Long getImportNextModuleOrder(Function<String, Long> subFunc, String projectId) {
|
||||
Long order = currentModuleOrder.get();
|
||||
private Long getImportNextOrder(Function<String, Long> subFunc, ThreadLocal<Long> nextOrder, String projectId) {
|
||||
Long order = nextOrder.get();
|
||||
if (order == null) {
|
||||
order = subFunc.apply(projectId);
|
||||
}
|
||||
order = order + DEFAULT_NODE_INTERVAL_POS;
|
||||
currentModuleOrder.set(order);
|
||||
nextOrder.set(order);
|
||||
return order;
|
||||
}
|
||||
|
||||
|
@ -542,8 +572,167 @@ public class ApiScenarioDataTransferService {
|
|||
return analysisResult;
|
||||
}
|
||||
|
||||
private String exportApiScenarioZip(ApiScenarioBatchExportRequest request, String type, String userId) {
|
||||
// todo 场景导出
|
||||
private String exportApiScenarioZip(ApiScenarioBatchExportRequest request, String exportType, String userId) throws Exception {
|
||||
File tmpDir = new File(LocalRepositoryDir.getSystemTempDir() + File.separatorChar + EXPORT_CASE_TMP_DIR + "_" + IDGenerator.nextStr());
|
||||
if (!tmpDir.mkdirs()) {
|
||||
throw new MSException(Translator.get("upload_fail"));
|
||||
}
|
||||
String fileType = "zip";
|
||||
try {
|
||||
User user = userMapper.selectByPrimaryKey(userId);
|
||||
noticeSendService.setLanguage(user.getLanguage());
|
||||
//获取导出的ids集合
|
||||
List<String> ids = apiScenarioService.doSelectIds(request, false);
|
||||
if (CollectionUtils.isEmpty(ids)) {
|
||||
return null;
|
||||
}
|
||||
Map<String, String> moduleMap = this.apiScenarioModuleService.getTree(request.getProjectId()).stream().collect(Collectors.toMap(BaseTreeNode::getId, BaseTreeNode::getPath));
|
||||
|
||||
String fileFolder = tmpDir.getPath() + File.separatorChar + request.getFileId();
|
||||
int fileIndex = 1;
|
||||
SubListUtils.dealForSubList(ids, 500, subList -> {
|
||||
request.setSelectIds(subList);
|
||||
ApiScenarioExportResponse exportResponse = this.genMetersphereExportResponse(request, moduleMap, exportType, userId);
|
||||
TempFileUtils.writeExportFile(fileFolder + File.separatorChar + "scenario_export_" + fileIndex + ".ms", exportResponse);
|
||||
});
|
||||
File zipFile = MsFileUtils.zipFile(tmpDir.getPath(), request.getFileId());
|
||||
if (zipFile == null) {
|
||||
return null;
|
||||
}
|
||||
fileService.upload(zipFile, new FileRequest(DefaultRepositoryDir.getExportApiTempDir(), StorageType.MINIO.name(), request.getFileId()));
|
||||
|
||||
// 生成日志
|
||||
LogDTO logDTO = apiScenarioLogService.exportExcelLog(request.getFileId(), exportType, userId, projectMapper.selectByPrimaryKey(request.getProjectId()));
|
||||
operationLogService.add(logDTO);
|
||||
|
||||
String taskId = exportTaskManager.getExportTaskId(request.getProjectId(), ExportConstants.ExportType.API_SCENARIO.name(), ExportConstants.ExportState.PREPARED.toString(), userId, null, fileType);
|
||||
ExportWebSocketHandler.sendMessageSingle(
|
||||
new ExportMsgDTO(request.getFileId(), taskId, ids.size(), true, MsgType.EXEC_RESULT.name())
|
||||
);
|
||||
} catch (Exception e) {
|
||||
LogUtils.error(e);
|
||||
List<ExportTask> exportTasks = exportTaskManager.getExportTasks(request.getProjectId(), ExportConstants.ExportType.API_SCENARIO.name(), ExportConstants.ExportState.PREPARED.toString(), userId, null);
|
||||
if (CollectionUtils.isNotEmpty(exportTasks)) {
|
||||
exportTaskManager.updateExportTask(ExportConstants.ExportState.ERROR.name(), exportTasks.getFirst().getId(), fileType);
|
||||
}
|
||||
ExportMsgDTO exportMsgDTO = new ExportMsgDTO(request.getFileId(), "", 0, false, MsgType.EXEC_RESULT.name());
|
||||
ExportWebSocketHandler.sendMessageSingle(exportMsgDTO);
|
||||
throw new MSException(e);
|
||||
} finally {
|
||||
MsFileUtils.deleteDir(tmpDir.getPath());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private ApiScenarioExportResponse genMetersphereExportResponse(ApiScenarioBatchExportRequest request, Map<String, String> moduleMap, String exportType, String userId) {
|
||||
Project project = projectMapper.selectByPrimaryKey(request.getProjectId());
|
||||
MetersphereApiScenarioExportResponse response = apiScenarioService.selectAndSortScenarioDetailWithIds(request.getSelectIds());
|
||||
response.setProjectId(project.getId());
|
||||
response.setOrganizationId(project.getOrganizationId());
|
||||
|
||||
if (StringUtils.equalsIgnoreCase(ApiScenarioExportType.METERSPHERE_ALL_DATA.name(), exportType)) {
|
||||
// 全量导出,导出引用的api、apiCase
|
||||
List<String> apiDefinitionIdList = new ArrayList<>();
|
||||
List<String> apiCaseIdList = new ArrayList<>();
|
||||
response.getScenarioStepList().forEach(step -> {
|
||||
if (StringUtils.isNotBlank(step.getResourceId())) {
|
||||
if (StringUtils.equalsIgnoreCase(step.getStepType(), ApiScenarioStepType.API.name())) {
|
||||
if (!apiDefinitionIdList.contains(step.getResourceId())) {
|
||||
apiDefinitionIdList.add(step.getResourceId());
|
||||
}
|
||||
} else if (StringUtils.equalsIgnoreCase(step.getStepType(), ApiScenarioStepType.API_CASE.name())) {
|
||||
if (!apiCaseIdList.contains(step.getResourceId())) {
|
||||
apiCaseIdList.add(step.getResourceId());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
List<ApiTestCaseWithBlob> apiTestCaseWithBlobs = extApiTestCaseMapper.selectAllDetailByApiIds(apiCaseIdList);
|
||||
if (CollectionUtils.isNotEmpty(apiCaseIdList)) {
|
||||
apiTestCaseWithBlobs.forEach(item -> {
|
||||
if (!apiDefinitionIdList.contains(item.getApiDefinitionId())) {
|
||||
apiDefinitionIdList.add(item.getApiDefinitionId());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Map<String, Map<String, String>> projectApiModuleIdMap = new HashMap<>();
|
||||
if (CollectionUtils.isNotEmpty(apiDefinitionIdList)) {
|
||||
List<ApiDefinitionWithBlob> apiList = extApiDefinitionMapper.selectApiDefinitionWithBlob(apiDefinitionIdList);
|
||||
List<String> projectIdList = apiList.stream().map(ApiDefinitionWithBlob::getProjectId).distinct().toList();
|
||||
projectIdList.forEach(projectId -> projectApiModuleIdMap.put(projectId, apiDefinitionExportService.buildModuleIdPathMap(request.getProjectId())));
|
||||
|
||||
for (ApiDefinitionWithBlob blob : apiList) {
|
||||
ApiDefinitionExportDetail detail = new ApiDefinitionExportDetail();
|
||||
if (blob.getRequest() != null) {
|
||||
detail.setRequest(ApiDataUtils.parseObject(new String(blob.getRequest()), AbstractMsTestElement.class));
|
||||
}
|
||||
if (blob.getResponse() != null) {
|
||||
detail.setResponse(ApiDataUtils.parseArray(new String(blob.getResponse()), HttpResponse.class));
|
||||
}
|
||||
detail.setName(blob.getName());
|
||||
detail.setProtocol(blob.getProtocol());
|
||||
detail.setMethod(blob.getMethod());
|
||||
detail.setPath(blob.getPath());
|
||||
detail.setStatus(blob.getStatus());
|
||||
detail.setTags(blob.getTags());
|
||||
detail.setPos(blob.getPos());
|
||||
detail.setDescription(blob.getDescription());
|
||||
|
||||
String moduleName;
|
||||
if (StringUtils.equals(blob.getModuleId(), ModuleConstants.DEFAULT_NODE_ID)) {
|
||||
moduleName = Translator.get("api_unplanned_request");
|
||||
} else if (projectApiModuleIdMap.containsKey(detail.getProjectId()) && projectApiModuleIdMap.get(detail.getProjectId()).containsKey(blob.getModuleId())) {
|
||||
moduleName = projectApiModuleIdMap.get(detail.getProjectId()).get(blob.getModuleId());
|
||||
} else {
|
||||
detail.setModuleId(ModuleConstants.DEFAULT_NODE_ID);
|
||||
moduleName = Translator.get("api_unplanned_request");
|
||||
}
|
||||
detail.setModulePath(moduleName);
|
||||
response.addExportApi(detail);
|
||||
|
||||
}
|
||||
}
|
||||
if (CollectionUtils.isNotEmpty(apiTestCaseWithBlobs)) {
|
||||
for (ApiTestCaseWithBlob apiTestCaseWithBlob : apiTestCaseWithBlobs) {
|
||||
ApiTestCaseDTO dto = new ApiTestCaseDTO();
|
||||
dto.setName(apiTestCaseWithBlob.getName());
|
||||
dto.setPriority(apiTestCaseWithBlob.getPriority());
|
||||
dto.setStatus(apiTestCaseWithBlob.getStatus());
|
||||
dto.setTags(apiTestCaseWithBlob.getTags());
|
||||
dto.setRequest(ApiDataUtils.parseObject(new String(apiTestCaseWithBlob.getRequest()), AbstractMsTestElement.class));
|
||||
response.addExportApiCase(dto);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// 普通导出,所有的引用都改为复制,并且Api、ApiCase改为CUSTOM_REQUEST
|
||||
response.setStepTypeToCustomRequest();
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
public void stopExport(String taskId, String userId) {
|
||||
exportTaskManager.sendStopMessage(taskId, userId);
|
||||
}
|
||||
|
||||
public void downloadFile(String projectId, String fileId, String userId, HttpServletResponse httpServletResponse) {
|
||||
List<ExportTask> exportTasks = exportTaskManager.getExportTasks(projectId, ExportConstants.ExportType.API_SCENARIO.name(), ExportConstants.ExportState.SUCCESS.toString(), userId, fileId);
|
||||
if (CollectionUtils.isEmpty(exportTasks)) {
|
||||
return;
|
||||
}
|
||||
ExportTask tasksFirst = exportTasks.getFirst();
|
||||
Project project = projectMapper.selectByPrimaryKey(projectId);
|
||||
FileRequest fileRequest = new FileRequest();
|
||||
fileRequest.setFileName(tasksFirst.getFileId());
|
||||
fileRequest.setFolder(DefaultRepositoryDir.getExportApiTempDir());
|
||||
fileRequest.setStorage(StorageType.MINIO.name());
|
||||
String fileName = "Metersphere_scenario_" + project.getName() + "." + tasksFirst.getFileType();
|
||||
try {
|
||||
InputStream fileInputStream = fileService.getFileAsStream(fileRequest);
|
||||
FileDownloadUtils.zipFilesWithResponse(fileName, fileInputStream, httpServletResponse);
|
||||
} catch (Exception e) {
|
||||
throw new MSException("get file error");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
package io.metersphere.api.service.definition;
|
||||
|
||||
import io.metersphere.api.dto.definition.*;
|
||||
import io.metersphere.api.dto.export.ApiExportResponse;
|
||||
import io.metersphere.api.mapper.ApiDefinitionModuleMapper;
|
||||
import io.metersphere.api.dto.export.ApiDefinitionExportResponse;
|
||||
import io.metersphere.api.mapper.ExtApiDefinitionMapper;
|
||||
import io.metersphere.api.mapper.ExtApiDefinitionMockMapper;
|
||||
import io.metersphere.api.mapper.ExtApiTestCaseMapper;
|
||||
|
@ -31,13 +30,10 @@ import io.metersphere.system.uid.IDGenerator;
|
|||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
@ -52,12 +48,9 @@ import java.util.stream.Collectors;
|
|||
@Service
|
||||
public class ApiDefinitionExportService {
|
||||
|
||||
|
||||
@Resource
|
||||
private ExtApiDefinitionMapper extApiDefinitionMapper;
|
||||
@Resource
|
||||
private ApiDefinitionModuleMapper apiDefinitionModuleMapper;
|
||||
@Resource
|
||||
private ExtApiTestCaseMapper extApiTestCaseMapper;
|
||||
@Resource
|
||||
private ExtApiDefinitionMockMapper extApiDefinitionMockMapper;
|
||||
|
@ -75,10 +68,12 @@ public class ApiDefinitionExportService {
|
|||
private OperationLogService operationLogService;
|
||||
@Resource
|
||||
private ApiDefinitionImportService apiDefinitionImportService;
|
||||
@Resource
|
||||
private FileService fileService;
|
||||
|
||||
private static final String EXPORT_CASE_TMP_DIR = "tmp";
|
||||
private static final String EXPORT_CASE_TMP_DIR = "apiDefinition";
|
||||
|
||||
private Map<String, String> buildModuleIdPathMap(String projectId) {
|
||||
public Map<String, String> buildModuleIdPathMap(String projectId) {
|
||||
List<BaseTreeNode> apiModules = apiDefinitionImportService.buildTreeData(projectId, null);
|
||||
Map<String, String> modulePathMap = new HashMap<>();
|
||||
apiModules.forEach(item -> {
|
||||
|
@ -87,12 +82,12 @@ public class ApiDefinitionExportService {
|
|||
return modulePathMap;
|
||||
}
|
||||
|
||||
public ApiExportResponse genApiExportResponse(ApiDefinitionBatchExportRequest request, Map<String, String> moduleMap, String type, String userId) {
|
||||
public ApiDefinitionExportResponse genApiExportResponse(ApiDefinitionBatchExportRequest request, Map<String, String> moduleMap, String type, String userId) {
|
||||
List<ApiDefinitionWithBlob> list = this.selectAndSortByIds(request.getSelectIds());
|
||||
return switch (type.toLowerCase()) {
|
||||
case "swagger" -> exportSwagger(request, list, moduleMap);
|
||||
case "metersphere" -> exportMetersphere(request, list, moduleMap);
|
||||
default -> new ApiExportResponse();
|
||||
default -> new ApiDefinitionExportResponse();
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -118,31 +113,17 @@ public class ApiDefinitionExportService {
|
|||
return returnId;
|
||||
}
|
||||
|
||||
@Resource
|
||||
private FileService fileService;
|
||||
|
||||
public void uploadFileToMinioExportFolder(File file, String fileName) throws Exception {
|
||||
FileRequest fileRequest = new FileRequest();
|
||||
fileRequest.setFileName(fileName);
|
||||
fileRequest.setFolder(DefaultRepositoryDir.getExportApiTempDir());
|
||||
fileRequest.setStorage(StorageType.MINIO.name());
|
||||
try (FileInputStream inputStream = new FileInputStream(file)) {
|
||||
fileService.upload(inputStream, fileRequest);
|
||||
}
|
||||
}
|
||||
|
||||
public String exportApiDefinitionZip(ApiDefinitionBatchExportRequest request, String exportType, String userId) throws Exception {
|
||||
// 为避免客户端未及时开启ws,此处延迟1s
|
||||
Thread.sleep(1000);
|
||||
File tmpDir = null;
|
||||
String fileType = "";
|
||||
File tmpDir = new File(LocalRepositoryDir.getSystemTempDir() + File.separatorChar + EXPORT_CASE_TMP_DIR + "_" + IDGenerator.nextStr());
|
||||
if (!tmpDir.mkdirs()) {
|
||||
throw new MSException(Translator.get("upload_fail"));
|
||||
}
|
||||
String fileType = "zip";
|
||||
try {
|
||||
User user = userMapper.selectByPrimaryKey(userId);
|
||||
noticeSendService.setLanguage(user.getLanguage());
|
||||
tmpDir = new File(LocalRepositoryDir.getSystemTempDir() + File.separatorChar + EXPORT_CASE_TMP_DIR + "_" + IDGenerator.nextStr());
|
||||
if (!tmpDir.exists() && !tmpDir.mkdirs()) {
|
||||
throw new MSException(Translator.get("upload_fail"));
|
||||
}
|
||||
//获取导出的ids集合
|
||||
List<String> ids = this.getBatchExportApiIds(request, request.getProjectId(), userId);
|
||||
if (CollectionUtils.isEmpty(ids)) {
|
||||
|
@ -155,44 +136,23 @@ public class ApiDefinitionExportService {
|
|||
int fileIndex = 1;
|
||||
SubListUtils.dealForSubList(ids, 1000, subList -> {
|
||||
request.setSelectIds(subList);
|
||||
ApiExportResponse exportResponse = this.genApiExportResponse(request, moduleMap, exportType, userId);
|
||||
File createFile = new File(fileFolder + File.separatorChar + fileIndex + ".json");
|
||||
if (!createFile.exists()) {
|
||||
try {
|
||||
createFile.getParentFile().mkdirs();
|
||||
createFile.createNewFile();
|
||||
} catch (IOException e) {
|
||||
throw new MSException(e);
|
||||
}
|
||||
}
|
||||
try {
|
||||
FileUtils.writeByteArrayToFile(createFile, JSON.toJSONString(exportResponse).getBytes());
|
||||
} catch (Exception e) {
|
||||
LogUtils.error(e);
|
||||
}
|
||||
ApiDefinitionExportResponse exportResponse = this.genApiExportResponse(request, moduleMap, exportType, userId);
|
||||
TempFileUtils.writeExportFile(fileFolder + File.separatorChar + fileIndex + ".json", exportResponse);
|
||||
});
|
||||
File zipFile = MsFileUtils.zipFile(tmpDir.getPath(), request.getFileId());
|
||||
if (zipFile == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
uploadFileToMinioExportFolder(zipFile, request.getFileId());
|
||||
fileService.upload(zipFile, new FileRequest(DefaultRepositoryDir.getExportApiTempDir(), StorageType.MINIO.name(), request.getFileId()));
|
||||
|
||||
// 生成日志
|
||||
LogDTO logDTO = apiDefinitionLogService.exportExcelLog(request, exportType, userId, projectMapper.selectByPrimaryKey(request.getProjectId()).getOrganizationId());
|
||||
operationLogService.add(logDTO);
|
||||
|
||||
List<ExportTask> exportTasks = exportTaskManager.getExportTasks(request.getProjectId(), ExportConstants.ExportType.API_DEFINITION.name(), ExportConstants.ExportState.PREPARED.toString(), userId, null);
|
||||
String taskId;
|
||||
if (CollectionUtils.isNotEmpty(exportTasks)) {
|
||||
taskId = exportTasks.getFirst().getId();
|
||||
exportTaskManager.updateExportTask(ExportConstants.ExportState.SUCCESS.name(), taskId, fileType);
|
||||
} else {
|
||||
taskId = MsgType.CONNECT.name();
|
||||
}
|
||||
|
||||
ExportMsgDTO exportMsgDTO = new ExportMsgDTO(request.getFileId(), taskId, ids.size(), true, MsgType.EXEC_RESULT.name());
|
||||
ExportWebSocketHandler.sendMessageSingle(exportMsgDTO);
|
||||
String taskId = exportTaskManager.getExportTaskId(request.getProjectId(), ExportConstants.ExportType.API_DEFINITION.name(), ExportConstants.ExportState.PREPARED.toString(), userId, null, fileType);
|
||||
ExportWebSocketHandler.sendMessageSingle(
|
||||
new ExportMsgDTO(request.getFileId(), taskId, ids.size(), true, MsgType.EXEC_RESULT.name())
|
||||
);
|
||||
} catch (Exception e) {
|
||||
LogUtils.error(e);
|
||||
List<ExportTask> exportTasks = exportTaskManager.getExportTasks(request.getProjectId(), ExportConstants.ExportType.API_DEFINITION.name(), ExportConstants.ExportState.PREPARED.toString(), userId, null);
|
||||
|
@ -231,7 +191,7 @@ public class ApiDefinitionExportService {
|
|||
}
|
||||
}
|
||||
|
||||
private ApiExportResponse exportSwagger(ApiDefinitionBatchRequest request, List<ApiDefinitionWithBlob> list, Map<String, String> moduleMap) {
|
||||
private ApiDefinitionExportResponse exportSwagger(ApiDefinitionBatchRequest request, List<ApiDefinitionWithBlob> list, Map<String, String> moduleMap) {
|
||||
Project project = projectMapper.selectByPrimaryKey(request.getProjectId());
|
||||
Swagger3ExportParser swagger3Parser = new Swagger3ExportParser();
|
||||
try {
|
||||
|
@ -241,7 +201,7 @@ public class ApiDefinitionExportService {
|
|||
}
|
||||
}
|
||||
|
||||
private ApiExportResponse exportMetersphere(ApiDefinitionBatchExportRequest request, List<ApiDefinitionWithBlob> list, Map<String, String> moduleMap) {
|
||||
private ApiDefinitionExportResponse exportMetersphere(ApiDefinitionBatchExportRequest request, List<ApiDefinitionWithBlob> list, Map<String, String> moduleMap) {
|
||||
try {
|
||||
List<String> apiIds = list.stream().map(ApiDefinitionWithBlob::getId).toList();
|
||||
List<ApiTestCaseWithBlob> apiTestCaseWithBlobs = new ArrayList<>();
|
||||
|
@ -273,7 +233,7 @@ public class ApiDefinitionExportService {
|
|||
fileRequest.setFileName(tasksFirst.getFileId());
|
||||
fileRequest.setFolder(DefaultRepositoryDir.getExportApiTempDir());
|
||||
fileRequest.setStorage(StorageType.MINIO.name());
|
||||
String fileName = "Metersphere_case_" + project.getName() + "." + tasksFirst.getFileType();
|
||||
String fileName = "Metersphere_api_" + project.getName() + "." + tasksFirst.getFileType();
|
||||
try {
|
||||
InputStream fileInputStream = fileService.getFileAsStream(fileRequest);
|
||||
FileDownloadUtils.zipFilesWithResponse(fileName, fileInputStream, httpServletResponse);
|
||||
|
|
|
@ -18,6 +18,7 @@ import io.metersphere.system.log.constants.OperationLogType;
|
|||
import io.metersphere.system.log.dto.LogDTO;
|
||||
import io.metersphere.system.log.service.OperationLogService;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
|
@ -233,4 +234,19 @@ public class ApiScenarioLogService {
|
|||
);
|
||||
operationLogService.batchAdd(logs);
|
||||
}
|
||||
|
||||
public LogDTO exportExcelLog(String sourceId, String exportType, String userId, @NotNull Project project) {
|
||||
LogDTO dto = new LogDTO(
|
||||
project.getId(),
|
||||
project.getOrganizationId(),
|
||||
sourceId,
|
||||
userId,
|
||||
OperationLogType.EXPORT.name(),
|
||||
OperationLogModule.API_SCENARIO_MANAGEMENT_SCENARIO,
|
||||
"");
|
||||
dto.setHistory(true);
|
||||
dto.setPath("/api/scenario/export/" + exportType);
|
||||
dto.setMethod(HttpMethodConstants.POST.name());
|
||||
return dto;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import io.metersphere.api.dto.*;
|
|||
import io.metersphere.api.dto.debug.ApiFileResourceUpdateRequest;
|
||||
import io.metersphere.api.dto.definition.ExecutePageRequest;
|
||||
import io.metersphere.api.dto.definition.ExecuteReportDTO;
|
||||
import io.metersphere.api.dto.export.MetersphereApiScenarioExportResponse;
|
||||
import io.metersphere.api.dto.request.ApiTransferRequest;
|
||||
import io.metersphere.api.dto.request.controller.MsScriptElement;
|
||||
import io.metersphere.api.dto.request.http.MsHTTPElement;
|
||||
|
@ -23,6 +24,7 @@ import io.metersphere.api.service.definition.ApiDefinitionService;
|
|||
import io.metersphere.api.service.definition.ApiTestCaseService;
|
||||
import io.metersphere.api.utils.ApiDataUtils;
|
||||
import io.metersphere.api.utils.ApiScenarioBatchOperationUtils;
|
||||
import io.metersphere.api.utils.ApiScenarioUtils;
|
||||
import io.metersphere.functional.domain.FunctionalCaseTestExample;
|
||||
import io.metersphere.functional.mapper.FunctionalCaseTestMapper;
|
||||
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
|
||||
|
@ -64,6 +66,7 @@ import io.metersphere.system.uid.NumGenerator;
|
|||
import io.metersphere.system.utils.ScheduleUtils;
|
||||
import io.metersphere.system.utils.ServiceUtils;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
|
@ -2463,4 +2466,50 @@ public class ApiScenarioService extends MoveNodeService {
|
|||
setParamFunc.accept(apiStepResourceInfo, resource);
|
||||
return apiStepResourceInfo;
|
||||
}
|
||||
|
||||
public MetersphereApiScenarioExportResponse selectAndSortScenarioDetailWithIds(@Valid List<@NotBlank(message = "{id must not be blank}") String> scenarioIds) {
|
||||
MetersphereApiScenarioExportResponse response = new MetersphereApiScenarioExportResponse();
|
||||
|
||||
// 数据准备
|
||||
{
|
||||
ApiScenarioExample scenarioExample = new ApiScenarioExample();
|
||||
scenarioExample.createCriteria().andIdIn(scenarioIds);
|
||||
List<ApiScenario> exportApiScenarios = apiScenarioMapper.selectByExample(scenarioExample);
|
||||
|
||||
// 获取所有步骤(包含引用的场景)
|
||||
List<ApiScenarioStepDTO> allSteps = getAllStepsByScenarioIds(scenarioIds)
|
||||
.stream()
|
||||
.distinct() // 这里可能存在多次引用相同场景,步骤可能会重复,去重
|
||||
.collect(Collectors.toList());
|
||||
|
||||
//查询引用的场景ID
|
||||
List<String> stepScenarioIds = allSteps.stream().filter(step -> isScenarioStep(step.getStepType())).map(ApiScenarioStepDTO::getResourceId).toList();
|
||||
|
||||
// 查询所有场景的blob
|
||||
List<String> allScenarioIds = new ArrayList<>(scenarioIds);
|
||||
allScenarioIds.addAll(stepScenarioIds);
|
||||
ApiScenarioBlobExample scenarioBlobExample = new ApiScenarioBlobExample();
|
||||
scenarioBlobExample.createCriteria().andIdIn(allScenarioIds);
|
||||
Map<String, ApiScenarioBlob> scenarioBlobMap = apiScenarioBlobMapper.selectByExampleWithBLOBs(scenarioBlobExample).stream().collect(Collectors.toMap(ApiScenarioBlob::getId, Function.identity()));
|
||||
// 场景CSV相关的信息
|
||||
ApiScenarioCsvExample apiScenarioCsvExample = new ApiScenarioCsvExample();
|
||||
apiScenarioCsvExample.createCriteria().andScenarioIdIn(allScenarioIds);
|
||||
response.setApiScenarioCsvList(apiScenarioCsvMapper.selectByExample(apiScenarioCsvExample));
|
||||
|
||||
// 导出的场景
|
||||
response.setExportScenarioList(ApiScenarioUtils.parseApiScenarioDetail(exportApiScenarios, scenarioBlobMap));
|
||||
|
||||
//步骤引用的场景
|
||||
if (CollectionUtils.isNotEmpty(stepScenarioIds)) {
|
||||
scenarioExample.clear();
|
||||
scenarioExample.createCriteria().andIdIn(stepScenarioIds);
|
||||
List<ApiScenario> otherScenarios = apiScenarioMapper.selectByExample(scenarioExample);
|
||||
response.setExportScenarioList(ApiScenarioUtils.parseApiScenarioDetail(otherScenarios, scenarioBlobMap));
|
||||
}
|
||||
|
||||
response.setScenarioStepBlobMap(getPartialRefStepDetailMap(allSteps));
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
package io.metersphere.api.utils;
|
||||
|
||||
import io.metersphere.api.domain.ApiScenario;
|
||||
import io.metersphere.api.domain.ApiScenarioBlob;
|
||||
import io.metersphere.api.dto.scenario.ApiScenarioDetail;
|
||||
import io.metersphere.api.dto.scenario.ScenarioConfig;
|
||||
import io.metersphere.sdk.util.BeanUtils;
|
||||
import io.metersphere.sdk.util.JSON;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class ApiScenarioUtils {
|
||||
public static List<ApiScenarioDetail> parseApiScenarioDetail(List<ApiScenario> apiScenarios, Map<String, ApiScenarioBlob> scenarioBlobMap) {
|
||||
List<ApiScenarioDetail> returnList = new ArrayList<>();
|
||||
for (ApiScenario apiScenario : apiScenarios) {
|
||||
ApiScenarioDetail apiScenarioDetail = BeanUtils.copyBean(new ApiScenarioDetail(), apiScenario);
|
||||
apiScenarioDetail.setSteps(List.of());
|
||||
ApiScenarioBlob apiScenarioBlob = scenarioBlobMap.get(apiScenario.getId());
|
||||
if (apiScenarioBlob != null) {
|
||||
apiScenarioDetail.setScenarioConfig(JSON.parseObject(new String(apiScenarioBlob.getConfig()), ScenarioConfig.class));
|
||||
}
|
||||
returnList.add(apiScenarioDetail);
|
||||
}
|
||||
return returnList;
|
||||
}
|
||||
}
|
|
@ -9,7 +9,7 @@ import io.metersphere.api.domain.*;
|
|||
import io.metersphere.api.dto.ApiFile;
|
||||
import io.metersphere.api.dto.ReferenceRequest;
|
||||
import io.metersphere.api.dto.definition.*;
|
||||
import io.metersphere.api.dto.export.MetersphereApiExportResponse;
|
||||
import io.metersphere.api.dto.export.MetersphereApiDefinitionExportResponse;
|
||||
import io.metersphere.api.dto.request.ApiEditPosRequest;
|
||||
import io.metersphere.api.dto.request.ApiTransferRequest;
|
||||
import io.metersphere.api.dto.request.ImportRequest;
|
||||
|
@ -2117,7 +2117,7 @@ public class ApiDefinitionControllerTests extends BaseTest {
|
|||
Assertions.assertEquals(files.length, 1);
|
||||
String fileContent = FileUtils.readFileToString(files[0], StandardCharsets.UTF_8);
|
||||
|
||||
MetersphereApiExportResponse exportResponse = ApiDataUtils.parseObject(fileContent, MetersphereApiExportResponse.class);
|
||||
MetersphereApiDefinitionExportResponse exportResponse = ApiDataUtils.parseObject(fileContent, MetersphereApiDefinitionExportResponse.class);
|
||||
|
||||
apiDefinitionImportTestService.compareApiExport(exportResponse, exportApiBlobs);
|
||||
|
||||
|
|
|
@ -1,21 +1,33 @@
|
|||
package io.metersphere.api.controller;
|
||||
|
||||
import io.metersphere.api.dto.definition.ApiDefinitionBatchExportRequest;
|
||||
import io.metersphere.api.dto.export.MetersphereApiScenarioExportResponse;
|
||||
import io.metersphere.api.dto.scenario.ApiScenarioImportRequest;
|
||||
import io.metersphere.api.utils.ApiDataUtils;
|
||||
import io.metersphere.functional.domain.ExportTask;
|
||||
import io.metersphere.project.domain.Project;
|
||||
import io.metersphere.sdk.constants.SessionConstants;
|
||||
import io.metersphere.sdk.util.JSON;
|
||||
import io.metersphere.sdk.util.MsFileUtils;
|
||||
import io.metersphere.system.base.BaseTest;
|
||||
import io.metersphere.system.constants.ExportConstants;
|
||||
import io.metersphere.system.controller.handler.ResultHolder;
|
||||
import io.metersphere.system.dto.AddProjectRequest;
|
||||
import io.metersphere.system.log.constants.OperationLogModule;
|
||||
import io.metersphere.system.manager.ExportTaskManager;
|
||||
import io.metersphere.system.service.CommonProjectService;
|
||||
import io.metersphere.system.uid.IDGenerator;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.junit.jupiter.api.*;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.mock.web.MockMultipartFile;
|
||||
import org.springframework.test.web.servlet.MvcResult;
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
|
@ -81,9 +93,14 @@ public class ApiScenarioControllerImportAndExportTests extends BaseTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Resource
|
||||
private ExportTaskManager exportTaskManager;
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
@Order(2)
|
||||
public void testExport() throws Exception {
|
||||
MsFileUtils.deleteDir("/tmp/api-scenario-export/");
|
||||
|
||||
ApiDefinitionBatchExportRequest exportRequest = new ApiDefinitionBatchExportRequest();
|
||||
String fileId = IDGenerator.nextStr();
|
||||
exportRequest.setProjectId(project.getId());
|
||||
|
@ -93,5 +110,43 @@ public class ApiScenarioControllerImportAndExportTests extends BaseTest {
|
|||
exportRequest.setExportApiMock(true);
|
||||
MvcResult mvcResult = this.requestPostWithOkAndReturn(URL_POST_EXPORT + "metersphere", exportRequest);
|
||||
String returnData = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
|
||||
|
||||
JSON.parseObject(returnData, ResultHolder.class).getData().toString();
|
||||
Assertions.assertTrue(StringUtils.isNotBlank(fileId));
|
||||
List<ExportTask> taskList = exportTaskManager.getExportTasks(exportRequest.getProjectId(), null, null, "admin", fileId);
|
||||
while (CollectionUtils.isEmpty(taskList)) {
|
||||
Thread.sleep(1000);
|
||||
taskList = exportTaskManager.getExportTasks(exportRequest.getProjectId(), null, null, "admin", fileId);
|
||||
}
|
||||
|
||||
ExportTask task = taskList.getFirst();
|
||||
while (!StringUtils.equalsIgnoreCase(task.getState(), ExportConstants.ExportState.SUCCESS.name())) {
|
||||
Thread.sleep(1000);
|
||||
task = exportTaskManager.getExportTasks(exportRequest.getProjectId(), null, null, "admin", fileId).getFirst();
|
||||
}
|
||||
|
||||
mvcResult = this.download(exportRequest.getProjectId(), fileId);
|
||||
|
||||
byte[] fileBytes = mvcResult.getResponse().getContentAsByteArray();
|
||||
|
||||
File zipFile = new File("/tmp/api-scenario-export/downloadFiles.zip");
|
||||
FileUtils.writeByteArrayToFile(zipFile, fileBytes);
|
||||
|
||||
File[] files = MsFileUtils.unZipFile(zipFile, "/tmp/api-scenario-export/unzip/");
|
||||
assert files != null;
|
||||
Assertions.assertEquals(files.length, 1);
|
||||
String fileContent = FileUtils.readFileToString(files[0], StandardCharsets.UTF_8);
|
||||
|
||||
MetersphereApiScenarioExportResponse exportResponse = ApiDataUtils.parseObject(fileContent, MetersphereApiScenarioExportResponse.class);
|
||||
|
||||
Assertions.assertEquals(exportResponse.getExportScenarioList().size(), 3);
|
||||
|
||||
MsFileUtils.deleteDir("/tmp/api-scenario-export/");
|
||||
}
|
||||
|
||||
private MvcResult download(String projectId, String fileId) throws Exception {
|
||||
return mockMvc.perform(MockMvcRequestBuilders.get("/api/scenario/download/file/" + projectId + "/" + fileId)
|
||||
.header(SessionConstants.HEADER_TOKEN, sessionId)
|
||||
.header(SessionConstants.CSRF_TOKEN, csrfToken)).andReturn();
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@ package io.metersphere.api.service;
|
|||
|
||||
import io.metersphere.api.domain.*;
|
||||
import io.metersphere.api.dto.converter.ApiDefinitionExportDetail;
|
||||
import io.metersphere.api.dto.export.MetersphereApiExportResponse;
|
||||
import io.metersphere.api.dto.export.MetersphereApiDefinitionExportResponse;
|
||||
import io.metersphere.api.dto.request.http.MsHTTPElement;
|
||||
import io.metersphere.api.mapper.ApiDefinitionBlobMapper;
|
||||
import io.metersphere.api.mapper.ApiDefinitionMapper;
|
||||
|
@ -99,7 +99,7 @@ public class ApiDefinitionImportTestService extends ApiDefinitionImportService {
|
|||
Assertions.assertEquals(moduleChangeCount, diffApiCount);
|
||||
}
|
||||
|
||||
public void compareApiExport(MetersphereApiExportResponse exportResponse, List<ApiDefinitionBlob> exportApiBlobs) {
|
||||
public void compareApiExport(MetersphereApiDefinitionExportResponse exportResponse, List<ApiDefinitionBlob> exportApiBlobs) {
|
||||
Assertions.assertEquals(exportResponse.getApiDefinitions().size(), exportApiBlobs.size());
|
||||
List<ApiDefinitionExportDetail> compareList = new ArrayList<>();
|
||||
for (ApiDefinitionBlob blob : exportApiBlobs) {
|
||||
|
|
|
@ -4,6 +4,7 @@ import io.metersphere.functional.domain.ExportTask;
|
|||
import io.metersphere.functional.domain.ExportTaskExample;
|
||||
import io.metersphere.functional.mapper.ExportTaskMapper;
|
||||
import io.metersphere.sdk.constants.KafkaTopicConstants;
|
||||
import io.metersphere.sdk.constants.MsgType;
|
||||
import io.metersphere.sdk.exception.MSException;
|
||||
import io.metersphere.sdk.util.JSON;
|
||||
import io.metersphere.sdk.util.LogUtils;
|
||||
|
@ -11,6 +12,7 @@ import io.metersphere.sdk.util.Translator;
|
|||
import io.metersphere.system.constants.ExportConstants;
|
||||
import io.metersphere.system.uid.IDGenerator;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.kafka.clients.consumer.ConsumerRecord;
|
||||
import org.springframework.kafka.annotation.KafkaListener;
|
||||
|
@ -111,6 +113,17 @@ public class ExportTaskManager {
|
|||
}
|
||||
}
|
||||
|
||||
public String getExportTaskId(String projectId, String exportType, String exportState, String userId, String fileId, String fileType) {
|
||||
List<ExportTask> exportTasks = this.getExportTasks(projectId, exportType, exportState, userId, fileId);
|
||||
String taskId;
|
||||
if (CollectionUtils.isNotEmpty(exportTasks)) {
|
||||
taskId = exportTasks.getFirst().getId();
|
||||
this.updateExportTask(ExportConstants.ExportState.SUCCESS.name(), taskId, fileType);
|
||||
} else {
|
||||
taskId = MsgType.CONNECT.name();
|
||||
}
|
||||
return taskId;
|
||||
}
|
||||
public List<ExportTask> getExportTasks(String projectId, String exportType, String exportState, String userId, String fileId) {
|
||||
ExportTaskExample exportTaskExample = new ExportTaskExample();
|
||||
ExportTaskExample.Criteria criteria = exportTaskExample.createCriteria().andCreateUserEqualTo(userId).andProjectIdEqualTo(projectId);
|
||||
|
|
|
@ -5,6 +5,8 @@ import io.metersphere.sdk.file.FileRequest;
|
|||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
|
||||
@Service
|
||||
|
@ -13,6 +15,12 @@ public class FileService {
|
|||
return FileCenter.getRepository(request.getStorage()).saveFile(file, request);
|
||||
}
|
||||
|
||||
public void upload(File file, FileRequest request) throws Exception {
|
||||
try (FileInputStream inputStream = new FileInputStream(file)) {
|
||||
this.upload(inputStream, request);
|
||||
}
|
||||
}
|
||||
|
||||
public String upload(InputStream inputStream, FileRequest request) throws Exception {
|
||||
return FileCenter.getRepository(request.getStorage()).saveFile(inputStream, request);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue