refactor(接口测试): 重构接口导出功能
This commit is contained in:
parent
3781f72523
commit
b6b93775b8
|
@ -1,12 +1,16 @@
|
||||||
package io.metersphere.listener;
|
package io.metersphere.listener;
|
||||||
|
|
||||||
import io.metersphere.api.event.ApiEventSource;
|
import io.metersphere.api.event.ApiEventSource;
|
||||||
|
import io.metersphere.functional.domain.ExportTask;
|
||||||
|
import io.metersphere.functional.domain.ExportTaskExample;
|
||||||
|
import io.metersphere.functional.mapper.ExportTaskMapper;
|
||||||
import io.metersphere.plan.listener.ExecEventListener;
|
import io.metersphere.plan.listener.ExecEventListener;
|
||||||
import io.metersphere.sdk.constants.StorageType;
|
import io.metersphere.sdk.constants.StorageType;
|
||||||
import io.metersphere.sdk.file.FileCenter;
|
import io.metersphere.sdk.file.FileCenter;
|
||||||
import io.metersphere.sdk.file.MinioRepository;
|
import io.metersphere.sdk.file.MinioRepository;
|
||||||
import io.metersphere.sdk.util.CommonBeanFactory;
|
import io.metersphere.sdk.util.CommonBeanFactory;
|
||||||
import io.metersphere.sdk.util.LogUtils;
|
import io.metersphere.sdk.util.LogUtils;
|
||||||
|
import io.metersphere.system.constants.ExportConstants;
|
||||||
import io.metersphere.system.service.BaseScheduleService;
|
import io.metersphere.system.service.BaseScheduleService;
|
||||||
import io.metersphere.system.service.PluginLoadService;
|
import io.metersphere.system.service.PluginLoadService;
|
||||||
import io.metersphere.system.uid.impl.DefaultUidGenerator;
|
import io.metersphere.system.uid.impl.DefaultUidGenerator;
|
||||||
|
@ -30,6 +34,9 @@ public class AppStartListener implements ApplicationRunner {
|
||||||
@Resource
|
@Resource
|
||||||
private DefaultUidGenerator defaultUidGenerator;
|
private DefaultUidGenerator defaultUidGenerator;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ExportTaskMapper exportTaskMapper;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run(ApplicationArguments args) throws Exception {
|
public void run(ApplicationArguments args) throws Exception {
|
||||||
LogUtils.info("================= 应用启动 =================");
|
LogUtils.info("================= 应用启动 =================");
|
||||||
|
@ -40,6 +47,13 @@ public class AppStartListener implements ApplicationRunner {
|
||||||
LogUtils.info("初始化定时任务");
|
LogUtils.info("初始化定时任务");
|
||||||
baseScheduleService.startEnableSchedules();
|
baseScheduleService.startEnableSchedules();
|
||||||
|
|
||||||
|
LogUtils.info("初始化导出未完成任务的状态");
|
||||||
|
ExportTaskExample exportTaskExample = new ExportTaskExample();
|
||||||
|
exportTaskExample.createCriteria().andStateEqualTo(ExportConstants.ExportState.PREPARED.name());
|
||||||
|
ExportTask exportTask = new ExportTask();
|
||||||
|
exportTask.setState(ExportConstants.ExportState.STOP.name());
|
||||||
|
exportTaskMapper.updateByExampleSelective(exportTask, exportTaskExample);
|
||||||
|
|
||||||
// 注册所有监听源
|
// 注册所有监听源
|
||||||
LogUtils.info("初始化接口事件源");
|
LogUtils.info("初始化接口事件源");
|
||||||
ApiEventSource apiEventSource = CommonBeanFactory.getBean(ApiEventSource.class);
|
ApiEventSource apiEventSource = CommonBeanFactory.getBean(ApiEventSource.class);
|
||||||
|
|
|
@ -6,7 +6,6 @@ import io.metersphere.api.domain.ApiDefinition;
|
||||||
import io.metersphere.api.dto.ReferenceDTO;
|
import io.metersphere.api.dto.ReferenceDTO;
|
||||||
import io.metersphere.api.dto.ReferenceRequest;
|
import io.metersphere.api.dto.ReferenceRequest;
|
||||||
import io.metersphere.api.dto.definition.*;
|
import io.metersphere.api.dto.definition.*;
|
||||||
import io.metersphere.api.dto.export.ApiExportResponse;
|
|
||||||
import io.metersphere.api.dto.request.ApiEditPosRequest;
|
import io.metersphere.api.dto.request.ApiEditPosRequest;
|
||||||
import io.metersphere.api.dto.request.ApiTransferRequest;
|
import io.metersphere.api.dto.request.ApiTransferRequest;
|
||||||
import io.metersphere.api.dto.request.ImportRequest;
|
import io.metersphere.api.dto.request.ImportRequest;
|
||||||
|
@ -33,6 +32,7 @@ import io.metersphere.system.utils.SessionUtils;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import jakarta.validation.constraints.NotBlank;
|
import jakarta.validation.constraints.NotBlank;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.shiro.authz.annotation.Logical;
|
import org.apache.shiro.authz.annotation.Logical;
|
||||||
|
@ -314,7 +314,24 @@ public class ApiDefinitionController {
|
||||||
@PostMapping(value = "/export/{type}")
|
@PostMapping(value = "/export/{type}")
|
||||||
@Operation(summary = "接口测试-接口管理-导出接口定义")
|
@Operation(summary = "接口测试-接口管理-导出接口定义")
|
||||||
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_EXPORT)
|
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_EXPORT)
|
||||||
public ApiExportResponse export(@RequestBody ApiDefinitionBatchExportRequest request, @PathVariable String type) {
|
public String newExport(@RequestBody ApiDefinitionBatchExportRequest request, @PathVariable String type) {
|
||||||
return apiDefinitionExportService.export(request, type, SessionUtils.getUserId());
|
return apiDefinitionExportService.exportApiDefinition(request, type, SessionUtils.getUserId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/stop/{taskId}")
|
||||||
|
@Operation(summary = "接口测试-接口管理-导出-停止导出")
|
||||||
|
@RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ_EXPORT)
|
||||||
|
@CheckOwner(resourceId = "#projectId", resourceType = "project")
|
||||||
|
public void caseStopExport(@PathVariable String taskId) {
|
||||||
|
apiDefinitionExportService.stopExport(taskId, SessionUtils.getUserId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping(value = "/download/file/{projectId}/{fileId}")
|
||||||
|
@Operation(summary = "接口测试-接口管理-下载文件")
|
||||||
|
@RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ_EXPORT)
|
||||||
|
@CheckOwner(resourceId = "#projectId", resourceType = "project")
|
||||||
|
public void downloadImgById(@PathVariable String projectId, @PathVariable String fileId, HttpServletResponse httpServletResponse) {
|
||||||
|
apiDefinitionExportService.downloadFile(projectId, fileId, SessionUtils.getUserId(), httpServletResponse);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,9 @@ public class ApiDefinitionBatchExportRequest extends ApiDefinitionBatchRequest i
|
||||||
@Serial
|
@Serial
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Schema(description = "文件id")
|
||||||
|
@NotBlank
|
||||||
|
private String fileId;
|
||||||
|
|
||||||
@Schema(description = "是否同步导出接口用例")
|
@Schema(description = "是否同步导出接口用例")
|
||||||
private boolean exportApiCase;
|
private boolean exportApiCase;
|
||||||
|
|
|
@ -55,6 +55,9 @@ public class Swagger3ExportParser implements ExportParser<ApiExportResponse> {
|
||||||
JSONObject components = new JSONObject();
|
JSONObject components = new JSONObject();
|
||||||
List<JSONObject> schemas = new LinkedList<>();
|
List<JSONObject> schemas = new LinkedList<>();
|
||||||
for (ApiDefinitionWithBlob apiDefinition : list) {
|
for (ApiDefinitionWithBlob apiDefinition : list) {
|
||||||
|
if (apiDefinition.getPath() == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
SwaggerApiInfo swaggerApiInfo = new SwaggerApiInfo();
|
SwaggerApiInfo swaggerApiInfo = new SwaggerApiInfo();
|
||||||
swaggerApiInfo.setSummary(apiDefinition.getName());
|
swaggerApiInfo.setSummary(apiDefinition.getName());
|
||||||
String moduleName = "";
|
String moduleName = "";
|
||||||
|
|
|
@ -4,18 +4,45 @@ import io.metersphere.api.domain.ApiDefinitionModule;
|
||||||
import io.metersphere.api.domain.ApiDefinitionModuleExample;
|
import io.metersphere.api.domain.ApiDefinitionModuleExample;
|
||||||
import io.metersphere.api.dto.definition.*;
|
import io.metersphere.api.dto.definition.*;
|
||||||
import io.metersphere.api.dto.export.ApiExportResponse;
|
import io.metersphere.api.dto.export.ApiExportResponse;
|
||||||
import io.metersphere.api.mapper.*;
|
import io.metersphere.api.mapper.ApiDefinitionModuleMapper;
|
||||||
|
import io.metersphere.api.mapper.ExtApiDefinitionMapper;
|
||||||
|
import io.metersphere.api.mapper.ExtApiDefinitionMockMapper;
|
||||||
|
import io.metersphere.api.mapper.ExtApiTestCaseMapper;
|
||||||
import io.metersphere.api.parser.api.MetersphereExportParser;
|
import io.metersphere.api.parser.api.MetersphereExportParser;
|
||||||
import io.metersphere.api.parser.api.Swagger3ExportParser;
|
import io.metersphere.api.parser.api.Swagger3ExportParser;
|
||||||
|
import io.metersphere.functional.domain.ExportTask;
|
||||||
import io.metersphere.project.domain.Project;
|
import io.metersphere.project.domain.Project;
|
||||||
import io.metersphere.project.mapper.ProjectMapper;
|
import io.metersphere.project.mapper.ProjectMapper;
|
||||||
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.exception.MSException;
|
||||||
|
import io.metersphere.sdk.file.FileRequest;
|
||||||
|
import io.metersphere.sdk.util.JSON;
|
||||||
|
import io.metersphere.sdk.util.LogUtils;
|
||||||
|
import io.metersphere.sdk.util.MsFileUtils;
|
||||||
|
import io.metersphere.sdk.util.Translator;
|
||||||
|
import io.metersphere.system.constants.ExportConstants;
|
||||||
|
import io.metersphere.system.domain.User;
|
||||||
|
import io.metersphere.system.log.dto.LogDTO;
|
||||||
|
import io.metersphere.system.log.service.OperationLogService;
|
||||||
|
import io.metersphere.system.manager.ExportTaskManager;
|
||||||
|
import io.metersphere.system.mapper.UserMapper;
|
||||||
|
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 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.io.FileUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.stereotype.Service;
|
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.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -40,10 +67,18 @@ public class ApiDefinitionExportService {
|
||||||
@Resource
|
@Resource
|
||||||
private ProjectMapper projectMapper;
|
private ProjectMapper projectMapper;
|
||||||
@Resource
|
@Resource
|
||||||
private ApiDefinitionMapper apiDefinitionMapper;
|
private UserMapper userMapper;
|
||||||
|
@Resource
|
||||||
|
private ExportTaskManager exportTaskManager;
|
||||||
|
@Resource
|
||||||
|
private NoticeSendService noticeSendService;
|
||||||
|
@Resource
|
||||||
|
private ApiDefinitionLogService apiDefinitionLogService;
|
||||||
|
@Resource
|
||||||
|
private OperationLogService operationLogService;
|
||||||
|
private static final String EXPORT_CASE_TMP_DIR = "tmp";
|
||||||
|
|
||||||
|
public ApiExportResponse genApiExportResponse(ApiDefinitionBatchExportRequest request, String type, String userId) {
|
||||||
public ApiExportResponse export(ApiDefinitionBatchExportRequest request, String type, String userId) {
|
|
||||||
List<String> ids = this.getBatchExportApiIds(request, request.getProjectId(), userId);
|
List<String> ids = this.getBatchExportApiIds(request, request.getProjectId(), userId);
|
||||||
if (CollectionUtils.isEmpty(ids)) {
|
if (CollectionUtils.isEmpty(ids)) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -61,6 +96,99 @@ public class ApiDefinitionExportService {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String exportApiDefinition(ApiDefinitionBatchExportRequest request, String type, String userId) {
|
||||||
|
String returnId;
|
||||||
|
try {
|
||||||
|
exportTaskManager.exportCheck(request.getProjectId(), ExportConstants.ExportType.API_DEFINITION.toString(), userId);
|
||||||
|
ExportTask exportTask = exportTaskManager.exportAsyncTask(
|
||||||
|
request.getProjectId(),
|
||||||
|
request.getFileId(), userId,
|
||||||
|
ExportConstants.ExportType.API_DEFINITION.name(), request, t -> {
|
||||||
|
try {
|
||||||
|
return exportApiDefinitionZip(request, type, userId);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new MSException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
returnId = exportTask.getId();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
LogUtils.error("导出失败:" + e);
|
||||||
|
throw new MSException(e);
|
||||||
|
}
|
||||||
|
return returnId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private FileService fileService;
|
||||||
|
|
||||||
|
public void uploadFileToMinio(String fileType, File file, String fileId) throws Exception {
|
||||||
|
FileRequest fileRequest = new FileRequest();
|
||||||
|
fileRequest.setFileName(fileId.concat(".").concat(fileType));
|
||||||
|
fileRequest.setFolder(DefaultRepositoryDir.getExportExcelTempDir());
|
||||||
|
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 {
|
||||||
|
File tmpDir = null;
|
||||||
|
String fileType = "";
|
||||||
|
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)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
ApiExportResponse exportResponse = this.genApiExportResponse(request, exportType, userId);
|
||||||
|
File createFile = new File(tmpDir.getPath() + File.separatorChar + request.getFileId() + ".json");
|
||||||
|
if (!createFile.exists()) {
|
||||||
|
try {
|
||||||
|
createFile.createNewFile();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new MSException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FileUtils.writeByteArrayToFile(createFile, JSON.toJSONString(exportResponse).getBytes());
|
||||||
|
fileType = "json";
|
||||||
|
uploadFileToMinio(fileType, createFile, 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);
|
||||||
|
} catch (Exception e) {
|
||||||
|
List<ExportTask> exportTasks = exportTaskManager.getExportTasks(request.getProjectId(), ExportConstants.ExportType.API_DEFINITION.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);
|
||||||
|
LogUtils.error(e);
|
||||||
|
throw new MSException(e);
|
||||||
|
} finally {
|
||||||
|
MsFileUtils.deleteDir(tmpDir.getPath());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private List<ApiDefinitionWithBlob> selectAndSortByIds(List<String> ids) {
|
private List<ApiDefinitionWithBlob> selectAndSortByIds(List<String> ids) {
|
||||||
Map<String, ApiDefinitionWithBlob> apiMap = extApiDefinitionMapper.selectApiDefinitionWithBlob(ids).stream().collect(Collectors.toMap(ApiDefinitionWithBlob::getId, v -> v));
|
Map<String, ApiDefinitionWithBlob> apiMap = extApiDefinitionMapper.selectApiDefinitionWithBlob(ids).stream().collect(Collectors.toMap(ApiDefinitionWithBlob::getId, v -> v));
|
||||||
return ids.stream().map(apiMap::get).toList();
|
return ids.stream().map(apiMap::get).toList();
|
||||||
|
@ -109,4 +237,28 @@ public class ApiDefinitionExportService {
|
||||||
throw new MSException(e);
|
throw new MSException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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_DEFINITION.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().concat(".").concat("json"));
|
||||||
|
fileRequest.setFolder(DefaultRepositoryDir.getExportExcelTempDir());
|
||||||
|
fileRequest.setStorage(StorageType.MINIO.name());
|
||||||
|
String fileName = "Metersphere_case_" + project.getName() + "." + tasksFirst.getFileType();
|
||||||
|
try {
|
||||||
|
InputStream fileInputStream = fileService.getFileAsStream(fileRequest);
|
||||||
|
FileDownloadUtils.zipFilesWithResponse(fileName, fileInputStream, httpServletResponse);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new MSException("get file error");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -370,4 +370,18 @@ public class ApiDefinitionLogService {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public LogDTO exportExcelLog(ApiDefinitionBatchExportRequest request, String exportType, String userId, String orgId) {
|
||||||
|
LogDTO dto = new LogDTO(
|
||||||
|
request.getProjectId(),
|
||||||
|
orgId,
|
||||||
|
request.getFileId(),
|
||||||
|
userId,
|
||||||
|
OperationLogType.EXPORT.name(),
|
||||||
|
OperationLogModule.API_TEST_MANAGEMENT_DEFINITION,
|
||||||
|
"");
|
||||||
|
dto.setHistory(true);
|
||||||
|
dto.setPath("/api/definition/export/" + exportType);
|
||||||
|
dto.setMethod(HttpMethodConstants.POST.name());
|
||||||
|
return dto;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ import io.metersphere.api.service.BaseFileManagementTestService;
|
||||||
import io.metersphere.api.service.definition.ApiDefinitionService;
|
import io.metersphere.api.service.definition.ApiDefinitionService;
|
||||||
import io.metersphere.api.service.definition.ApiTestCaseService;
|
import io.metersphere.api.service.definition.ApiTestCaseService;
|
||||||
import io.metersphere.api.utils.ApiDataUtils;
|
import io.metersphere.api.utils.ApiDataUtils;
|
||||||
|
import io.metersphere.functional.domain.ExportTask;
|
||||||
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
|
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
|
||||||
import io.metersphere.project.domain.Project;
|
import io.metersphere.project.domain.Project;
|
||||||
import io.metersphere.project.dto.filemanagement.FileInfo;
|
import io.metersphere.project.dto.filemanagement.FileInfo;
|
||||||
|
@ -40,6 +41,7 @@ import io.metersphere.sdk.file.FileCenter;
|
||||||
import io.metersphere.sdk.file.FileRequest;
|
import io.metersphere.sdk.file.FileRequest;
|
||||||
import io.metersphere.sdk.util.*;
|
import io.metersphere.sdk.util.*;
|
||||||
import io.metersphere.system.base.BaseTest;
|
import io.metersphere.system.base.BaseTest;
|
||||||
|
import io.metersphere.system.constants.ExportConstants;
|
||||||
import io.metersphere.system.controller.handler.ResultHolder;
|
import io.metersphere.system.controller.handler.ResultHolder;
|
||||||
import io.metersphere.system.controller.handler.result.MsHttpResultCode;
|
import io.metersphere.system.controller.handler.result.MsHttpResultCode;
|
||||||
import io.metersphere.system.domain.OperationHistory;
|
import io.metersphere.system.domain.OperationHistory;
|
||||||
|
@ -50,6 +52,7 @@ import io.metersphere.system.dto.request.OperationHistoryVersionRequest;
|
||||||
import io.metersphere.sdk.dto.BaseCondition;
|
import io.metersphere.sdk.dto.BaseCondition;
|
||||||
import io.metersphere.system.log.constants.OperationLogModule;
|
import io.metersphere.system.log.constants.OperationLogModule;
|
||||||
import io.metersphere.system.log.constants.OperationLogType;
|
import io.metersphere.system.log.constants.OperationLogType;
|
||||||
|
import io.metersphere.system.manager.ExportTaskManager;
|
||||||
import io.metersphere.system.mapper.OperationHistoryMapper;
|
import io.metersphere.system.mapper.OperationHistoryMapper;
|
||||||
import io.metersphere.system.service.CommonProjectService;
|
import io.metersphere.system.service.CommonProjectService;
|
||||||
import io.metersphere.system.service.UserLoginService;
|
import io.metersphere.system.service.UserLoginService;
|
||||||
|
@ -58,6 +61,7 @@ import io.metersphere.system.uid.NumGenerator;
|
||||||
import io.metersphere.system.utils.Pager;
|
import io.metersphere.system.utils.Pager;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import org.apache.commons.collections4.CollectionUtils;
|
import org.apache.commons.collections4.CollectionUtils;
|
||||||
|
import org.apache.commons.io.FileUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.junit.jupiter.api.*;
|
import org.junit.jupiter.api.*;
|
||||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||||
|
@ -2043,17 +2047,54 @@ public class ApiDefinitionControllerTests extends BaseTest {
|
||||||
Assertions.assertEquals(2, apiDefinitionIdList.size());
|
Assertions.assertEquals(2, apiDefinitionIdList.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ExportTaskManager exportTaskManager;
|
||||||
|
|
||||||
private void testExportAndImport(String exportProjectId, List<ApiDefinitionBlob> exportApiBlobs) throws Exception {
|
private void testExportAndImport(String exportProjectId, List<ApiDefinitionBlob> exportApiBlobs) throws Exception {
|
||||||
ApiDefinitionBatchExportRequest exportRequest = new ApiDefinitionBatchExportRequest();
|
ApiDefinitionBatchExportRequest exportRequest = new ApiDefinitionBatchExportRequest();
|
||||||
|
String fileId = IDGenerator.nextStr();
|
||||||
exportRequest.setProjectId(exportProjectId);
|
exportRequest.setProjectId(exportProjectId);
|
||||||
|
exportRequest.setFileId(fileId);
|
||||||
exportRequest.setSelectAll(true);
|
exportRequest.setSelectAll(true);
|
||||||
exportRequest.setExportApiCase(true);
|
exportRequest.setExportApiCase(true);
|
||||||
exportRequest.setExportApiMock(true);
|
exportRequest.setExportApiMock(true);
|
||||||
MvcResult mvcResult = this.requestPostWithOkAndReturn(EXPORT + "metersphere", exportRequest);
|
MvcResult mvcResult = this.requestPostWithOkAndReturn(EXPORT + "metersphere", exportRequest);
|
||||||
String returnData = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
|
String returnData = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
|
||||||
ResultHolder resultHolder = JSON.parseObject(returnData, ResultHolder.class);
|
String taskId = JSON.parseObject(returnData, ResultHolder.class).getData().toString();
|
||||||
MetersphereApiExportResponse exportResponse = ApiDataUtils.parseObject(JSON.toJSONString(resultHolder.getData()), MetersphereApiExportResponse.class);
|
Assertions.assertTrue(StringUtils.isNotBlank(fileId));
|
||||||
|
List<ExportTask> taskList = exportTaskManager.getExportTasks(exportProjectId, null, null, "admin", fileId);
|
||||||
|
while (CollectionUtils.isEmpty(taskList)) {
|
||||||
|
Thread.sleep(1000);
|
||||||
|
taskList = exportTaskManager.getExportTasks(exportProjectId, null, null, "admin", fileId);
|
||||||
|
}
|
||||||
|
ExportTask task = taskList.getFirst();
|
||||||
|
while (!StringUtils.equalsIgnoreCase(task.getState(), ExportConstants.ExportState.SUCCESS.name())) {
|
||||||
|
Thread.sleep(1000);
|
||||||
|
task = exportTaskManager.getExportTasks(exportProjectId, null, null, "admin", fileId).getFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
mvcResult = this.download(exportProjectId, fileId);
|
||||||
|
|
||||||
|
byte[] fileBytes = mvcResult.getResponse().getContentAsByteArray();
|
||||||
|
|
||||||
|
File file = new File("/tmp/test.json");
|
||||||
|
FileUtils.writeByteArrayToFile(file, fileBytes);
|
||||||
|
|
||||||
|
String fileContent = FileUtils.readFileToString(file, StandardCharsets.UTF_8);
|
||||||
|
|
||||||
|
MetersphereApiExportResponse exportResponse = ApiDataUtils.parseObject(fileContent, MetersphereApiExportResponse.class);
|
||||||
|
|
||||||
apiDefinitionImportTestService.compareApiExport(exportResponse, exportApiBlobs);
|
apiDefinitionImportTestService.compareApiExport(exportResponse, exportApiBlobs);
|
||||||
|
|
||||||
|
|
||||||
|
//测试stop
|
||||||
|
this.requestGetWithOk("/stop/" + taskId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private MvcResult download(String projectId, String fileId) throws Exception {
|
||||||
|
return mockMvc.perform(MockMvcRequestBuilders.get("/api/definition/download/file/" + projectId + "/" + fileId)
|
||||||
|
.header(SessionConstants.HEADER_TOKEN, sessionId)
|
||||||
|
.header(SessionConstants.CSRF_TOKEN, csrfToken)).andReturn();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected MvcResult requestMultipart(String url, MultiValueMap<String, Object> paramMap, ResultMatcher resultMatcher) throws Exception {
|
protected MvcResult requestMultipart(String url, MultiValueMap<String, Object> paramMap, ResultMatcher resultMatcher) throws Exception {
|
||||||
|
|
|
@ -27,7 +27,6 @@ import io.metersphere.functional.mapper.ExportTaskMapper;
|
||||||
import io.metersphere.functional.mapper.ExtFunctionalCaseCommentMapper;
|
import io.metersphere.functional.mapper.ExtFunctionalCaseCommentMapper;
|
||||||
import io.metersphere.functional.request.FunctionalCaseExportRequest;
|
import io.metersphere.functional.request.FunctionalCaseExportRequest;
|
||||||
import io.metersphere.functional.request.FunctionalCaseImportRequest;
|
import io.metersphere.functional.request.FunctionalCaseImportRequest;
|
||||||
import io.metersphere.functional.socket.ExportWebSocketHandler;
|
|
||||||
import io.metersphere.functional.xmind.parser.XMindCaseParser;
|
import io.metersphere.functional.xmind.parser.XMindCaseParser;
|
||||||
import io.metersphere.plan.domain.TestPlanCaseExecuteHistory;
|
import io.metersphere.plan.domain.TestPlanCaseExecuteHistory;
|
||||||
import io.metersphere.project.domain.Project;
|
import io.metersphere.project.domain.Project;
|
||||||
|
@ -56,6 +55,7 @@ import io.metersphere.system.mapper.SystemParameterMapper;
|
||||||
import io.metersphere.system.mapper.UserMapper;
|
import io.metersphere.system.mapper.UserMapper;
|
||||||
import io.metersphere.system.service.FileService;
|
import io.metersphere.system.service.FileService;
|
||||||
import io.metersphere.system.service.NoticeSendService;
|
import io.metersphere.system.service.NoticeSendService;
|
||||||
|
import io.metersphere.system.socket.ExportWebSocketHandler;
|
||||||
import io.metersphere.system.uid.IDGenerator;
|
import io.metersphere.system.uid.IDGenerator;
|
||||||
import io.metersphere.system.utils.ServiceUtils;
|
import io.metersphere.system.utils.ServiceUtils;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
|
@ -564,6 +564,7 @@ public class FunctionalCaseFileService {
|
||||||
throw new MSException(e);
|
throw new MSException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//生成临时EXCEL
|
//生成临时EXCEL
|
||||||
EasyExcel.write(createFile)
|
EasyExcel.write(createFile)
|
||||||
.head(Optional.ofNullable(headList).orElse(new ArrayList<>()))
|
.head(Optional.ofNullable(headList).orElse(new ArrayList<>()))
|
||||||
|
|
|
@ -5,7 +5,6 @@ import io.metersphere.functional.domain.FunctionalCase;
|
||||||
import io.metersphere.functional.domain.FunctionalCaseBlob;
|
import io.metersphere.functional.domain.FunctionalCaseBlob;
|
||||||
import io.metersphere.functional.domain.FunctionalCaseCustomField;
|
import io.metersphere.functional.domain.FunctionalCaseCustomField;
|
||||||
import io.metersphere.functional.request.FunctionalCaseExportRequest;
|
import io.metersphere.functional.request.FunctionalCaseExportRequest;
|
||||||
import io.metersphere.functional.socket.ExportWebSocketHandler;
|
|
||||||
import io.metersphere.functional.xmind.domain.FunctionalCaseXmindDTO;
|
import io.metersphere.functional.xmind.domain.FunctionalCaseXmindDTO;
|
||||||
import io.metersphere.functional.xmind.domain.FunctionalCaseXmindData;
|
import io.metersphere.functional.xmind.domain.FunctionalCaseXmindData;
|
||||||
import io.metersphere.functional.xmind.utils.XmindExportUtil;
|
import io.metersphere.functional.xmind.utils.XmindExportUtil;
|
||||||
|
@ -25,6 +24,7 @@ import io.metersphere.system.log.service.OperationLogService;
|
||||||
import io.metersphere.system.manager.ExportTaskManager;
|
import io.metersphere.system.manager.ExportTaskManager;
|
||||||
import io.metersphere.system.mapper.UserMapper;
|
import io.metersphere.system.mapper.UserMapper;
|
||||||
import io.metersphere.system.service.NoticeSendService;
|
import io.metersphere.system.service.NoticeSendService;
|
||||||
|
import io.metersphere.system.socket.ExportWebSocketHandler;
|
||||||
import io.metersphere.system.uid.IDGenerator;
|
import io.metersphere.system.uid.IDGenerator;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
|
|
@ -3,10 +3,7 @@ package io.metersphere.project.utils;
|
||||||
import io.metersphere.sdk.util.LogUtils;
|
import io.metersphere.sdk.util.LogUtils;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
import java.io.*;
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
|
@ -43,6 +40,23 @@ public class FileDownloadUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void zipFilesWithResponse(String fileName, InputStream fileInputStream, HttpServletResponse response) {
|
||||||
|
|
||||||
|
try (OutputStream outputStream = response.getOutputStream()) {
|
||||||
|
byte[] buffer = new byte[512];
|
||||||
|
int num;
|
||||||
|
while ((num = fileInputStream.read(buffer)) > 0) {
|
||||||
|
outputStream.write(buffer, 0, num);
|
||||||
|
}
|
||||||
|
outputStream.close();
|
||||||
|
response.setContentType("application/zip");
|
||||||
|
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
|
||||||
|
response.setHeader("Content-disposition", "attachment;filename=" + fileName);
|
||||||
|
} catch (Exception e) {
|
||||||
|
LogUtils.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static byte[] listFileBytesToZip(Map<String, byte[]> mapReport) {
|
public static byte[] listFileBytesToZip(Map<String, byte[]> mapReport) {
|
||||||
try {
|
try {
|
||||||
if (!mapReport.isEmpty()) {
|
if (!mapReport.isEmpty()) {
|
||||||
|
|
|
@ -3,7 +3,7 @@ package io.metersphere.system.constants;
|
||||||
public class ExportConstants {
|
public class ExportConstants {
|
||||||
|
|
||||||
public enum ExportType {
|
public enum ExportType {
|
||||||
API, CASE
|
API_DEFINITION, CASE
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ExportState {
|
public enum ExportState {
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
package io.metersphere.system.manager;
|
package io.metersphere.system.manager;
|
||||||
|
|
||||||
import io.metersphere.functional.domain.ExportTask;
|
import io.metersphere.functional.domain.ExportTask;
|
||||||
|
import io.metersphere.functional.domain.ExportTaskExample;
|
||||||
import io.metersphere.functional.mapper.ExportTaskMapper;
|
import io.metersphere.functional.mapper.ExportTaskMapper;
|
||||||
import io.metersphere.sdk.constants.KafkaTopicConstants;
|
import io.metersphere.sdk.constants.KafkaTopicConstants;
|
||||||
|
import io.metersphere.sdk.exception.MSException;
|
||||||
import io.metersphere.sdk.util.JSON;
|
import io.metersphere.sdk.util.JSON;
|
||||||
import io.metersphere.sdk.util.LogUtils;
|
import io.metersphere.sdk.util.LogUtils;
|
||||||
|
import io.metersphere.sdk.util.Translator;
|
||||||
import io.metersphere.system.constants.ExportConstants;
|
import io.metersphere.system.constants.ExportConstants;
|
||||||
import io.metersphere.system.uid.IDGenerator;
|
import io.metersphere.system.uid.IDGenerator;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
|
@ -15,6 +18,7 @@ import org.springframework.kafka.core.KafkaTemplate;
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
|
@ -96,4 +100,38 @@ public class ExportTaskManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void exportCheck(String projectId, String exportType, String userId) {
|
||||||
|
ExportTaskExample exportTaskExample = new ExportTaskExample();
|
||||||
|
exportTaskExample.createCriteria().andTypeEqualTo(exportType).andStateEqualTo(ExportConstants.ExportState.PREPARED.toString())
|
||||||
|
.andCreateUserEqualTo(userId).andProjectIdEqualTo(projectId);
|
||||||
|
exportTaskExample.setOrderByClause("create_time desc");
|
||||||
|
if (exportTaskMapper.countByExample(exportTaskExample) > 0) {
|
||||||
|
throw new MSException(Translator.get("export_case_task_existed"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
if (StringUtils.isNotBlank(exportType)) {
|
||||||
|
criteria.andTypeEqualTo(exportType);
|
||||||
|
}
|
||||||
|
if (StringUtils.isNotBlank(exportState)) {
|
||||||
|
criteria.andStateEqualTo(exportState);
|
||||||
|
}
|
||||||
|
if (StringUtils.isNotBlank(fileId)) {
|
||||||
|
criteria.andFileIdEqualTo(fileId);
|
||||||
|
}
|
||||||
|
exportTaskExample.setOrderByClause("create_time desc");
|
||||||
|
return exportTaskMapper.selectByExample(exportTaskExample);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateExportTask(String state, String taskId, String fileType) {
|
||||||
|
ExportTask exportTask = new ExportTask();
|
||||||
|
exportTask.setState(state);
|
||||||
|
exportTask.setFileType(fileType);
|
||||||
|
exportTask.setId(taskId);
|
||||||
|
exportTaskMapper.updateByPrimaryKeySelective(exportTask);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package io.metersphere.system.service;
|
package io.metersphere.system.service;
|
||||||
|
|
||||||
|
import io.metersphere.functional.domain.ExportTaskExample;
|
||||||
|
import io.metersphere.functional.mapper.ExportTaskMapper;
|
||||||
import io.metersphere.project.domain.Project;
|
import io.metersphere.project.domain.Project;
|
||||||
import io.metersphere.project.domain.ProjectExample;
|
import io.metersphere.project.domain.ProjectExample;
|
||||||
import io.metersphere.project.domain.ProjectTestResourcePool;
|
import io.metersphere.project.domain.ProjectTestResourcePool;
|
||||||
|
@ -50,6 +52,8 @@ public class CommonProjectService {
|
||||||
@Resource
|
@Resource
|
||||||
private ProjectMapper projectMapper;
|
private ProjectMapper projectMapper;
|
||||||
@Resource
|
@Resource
|
||||||
|
private ExportTaskMapper exportTaskMapper;
|
||||||
|
@Resource
|
||||||
private UserMapper userMapper;
|
private UserMapper userMapper;
|
||||||
@Resource
|
@Resource
|
||||||
private UserRoleRelationMapper userRoleRelationMapper;
|
private UserRoleRelationMapper userRoleRelationMapper;
|
||||||
|
@ -551,6 +555,10 @@ public class CommonProjectService {
|
||||||
ProjectTestResourcePoolExample projectTestResourcePoolExample = new ProjectTestResourcePoolExample();
|
ProjectTestResourcePoolExample projectTestResourcePoolExample = new ProjectTestResourcePoolExample();
|
||||||
projectTestResourcePoolExample.createCriteria().andProjectIdEqualTo(project.getId());
|
projectTestResourcePoolExample.createCriteria().andProjectIdEqualTo(project.getId());
|
||||||
projectTestResourcePoolMapper.deleteByExample(projectTestResourcePoolExample);
|
projectTestResourcePoolMapper.deleteByExample(projectTestResourcePoolExample);
|
||||||
|
//删除导出任务
|
||||||
|
ExportTaskExample exportTaskExample = new ExportTaskExample();
|
||||||
|
exportTaskExample.createCriteria().andProjectIdEqualTo(project.getId());
|
||||||
|
exportTaskMapper.deleteByExample(exportTaskExample);
|
||||||
// delete project
|
// delete project
|
||||||
projectMapper.deleteByPrimaryKey(project.getId());
|
projectMapper.deleteByPrimaryKey(project.getId());
|
||||||
LogDTO logDTO = new LogDTO(OperationLogConstants.SYSTEM, project.getOrganizationId(), project.getId(), Translator.get("scheduled_tasks"), OperationLogType.DELETE.name(), OperationLogModule.SETTING_ORGANIZATION_PROJECT, Translator.get("delete") + Translator.get("project") + ": " + project.getName());
|
LogDTO logDTO = new LogDTO(OperationLogConstants.SYSTEM, project.getOrganizationId(), project.getId(), Translator.get("scheduled_tasks"), OperationLogType.DELETE.name(), OperationLogModule.SETTING_ORGANIZATION_PROJECT, Translator.get("delete") + Translator.get("project") + ": " + project.getName());
|
||||||
|
|
|
@ -25,6 +25,10 @@ public class FileService {
|
||||||
return FileCenter.getRepository(request.getStorage()).getFile(request);
|
return FileCenter.getRepository(request.getStorage()).getFile(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public InputStream getFileAsStream(FileRequest request) throws Exception {
|
||||||
|
return FileCenter.getRepository(request.getStorage()).getFileAsStream(request);
|
||||||
|
}
|
||||||
|
|
||||||
public void deleteFile(FileRequest request) throws Exception {
|
public void deleteFile(FileRequest request) throws Exception {
|
||||||
FileCenter.getRepository(request.getStorage()).delete(request);
|
FileCenter.getRepository(request.getStorage()).delete(request);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package io.metersphere.functional.socket;
|
package io.metersphere.system.socket;
|
||||||
|
|
||||||
import io.metersphere.sdk.constants.MsgType;
|
import io.metersphere.sdk.constants.MsgType;
|
||||||
import io.metersphere.sdk.dto.ExportMsgDTO;
|
import io.metersphere.sdk.dto.ExportMsgDTO;
|
|
@ -40,6 +40,7 @@ import {
|
||||||
diffDataUrl,
|
diffDataUrl,
|
||||||
ExecuteCaseUrl,
|
ExecuteCaseUrl,
|
||||||
ExportDefinitionUrl,
|
ExportDefinitionUrl,
|
||||||
|
GetApiDownloadFileUrl,
|
||||||
GetCaseDetailUrl,
|
GetCaseDetailUrl,
|
||||||
GetCaseReportByIdUrl,
|
GetCaseReportByIdUrl,
|
||||||
GetCaseReportDetailUrl,
|
GetCaseReportDetailUrl,
|
||||||
|
@ -72,6 +73,7 @@ import {
|
||||||
SaveOperationHistoryUrl,
|
SaveOperationHistoryUrl,
|
||||||
SortCaseUrl,
|
SortCaseUrl,
|
||||||
SortDefinitionUrl,
|
SortDefinitionUrl,
|
||||||
|
StopApiExportUrl,
|
||||||
SwitchDefinitionScheduleUrl,
|
SwitchDefinitionScheduleUrl,
|
||||||
ToggleFollowCaseUrl,
|
ToggleFollowCaseUrl,
|
||||||
ToggleFollowDefinitionUrl,
|
ToggleFollowDefinitionUrl,
|
||||||
|
@ -93,6 +95,8 @@ import {
|
||||||
UploadTempFileUrl,
|
UploadTempFileUrl,
|
||||||
UploadTempMockFileUrl,
|
UploadTempMockFileUrl,
|
||||||
} from '@/api/requrls/api-test/management';
|
} from '@/api/requrls/api-test/management';
|
||||||
|
import { StopCaseExportUrl } from '@/api/requrls/case-management/featureCase';
|
||||||
|
import { BatchDownloadFileUrl } from '@/api/requrls/project-management/fileManagement';
|
||||||
|
|
||||||
import { ApiCaseReportDetail, ExecuteRequestParams } from '@/models/apiTest/common';
|
import { ApiCaseReportDetail, ExecuteRequestParams } from '@/models/apiTest/common';
|
||||||
import {
|
import {
|
||||||
|
@ -201,6 +205,25 @@ export function updateDefinition(data: ApiDefinitionUpdateParams) {
|
||||||
return MSR.post({ url: UpdateDefinitionUrl, data });
|
return MSR.post({ url: UpdateDefinitionUrl, data });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function stopApiExport(taskId: string) {
|
||||||
|
return MSR.get({ url: `${StopApiExportUrl}/${taskId}` });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取导出的文件
|
||||||
|
export function getApiDownloadFile(projectId: string, fileId: string) {
|
||||||
|
// return MSR.get(
|
||||||
|
// { url: `${GetApiDownloadFileUrl}/${projectId}/${fileId}`, responseType: 'blob' },
|
||||||
|
// { isTransformResponse: false, isReturnNativeResponse: true }
|
||||||
|
// );
|
||||||
|
|
||||||
|
return MSR.get(
|
||||||
|
{
|
||||||
|
url: `${GetApiDownloadFileUrl}/${projectId}/${fileId}`,
|
||||||
|
responseType: 'blob',
|
||||||
|
},
|
||||||
|
{ isTransformResponse: false }
|
||||||
|
);
|
||||||
|
}
|
||||||
// 获取接口定义详情
|
// 获取接口定义详情
|
||||||
export function getDefinitionDetail(id: string | number) {
|
export function getDefinitionDetail(id: string | number) {
|
||||||
return MSR.get<ApiDefinitionDetail>({ url: GetDefinitionDetailUrl, params: id });
|
return MSR.get<ApiDefinitionDetail>({ url: GetDefinitionDetailUrl, params: id });
|
||||||
|
|
|
@ -12,6 +12,8 @@ export const DeleteModuleUrl = '/api/definition/module/delete'; // 删除模块
|
||||||
export const DefinitionPageUrl = '/api/definition/page'; // 接口定义列表
|
export const DefinitionPageUrl = '/api/definition/page'; // 接口定义列表
|
||||||
export const AddDefinitionUrl = '/api/definition/add'; // 添加接口定义
|
export const AddDefinitionUrl = '/api/definition/add'; // 添加接口定义
|
||||||
export const UpdateDefinitionUrl = '/api/definition/update'; // 更新接口定义
|
export const UpdateDefinitionUrl = '/api/definition/update'; // 更新接口定义
|
||||||
|
export const StopApiExportUrl = '/api/definition/stop';
|
||||||
|
export const GetApiDownloadFileUrl = '/api/definition/download/file';
|
||||||
export const GetDefinitionDetailUrl = '/api/definition/get-detail'; // 获取接口定义详情
|
export const GetDefinitionDetailUrl = '/api/definition/get-detail'; // 获取接口定义详情
|
||||||
export const TransferFileUrl = '/api/definition/transfer'; // 文件转存
|
export const TransferFileUrl = '/api/definition/transfer'; // 文件转存
|
||||||
export const TransferFileModuleOptionUrl = '/api/definition/transfer/options'; // 文件转存目录
|
export const TransferFileModuleOptionUrl = '/api/definition/transfer/options'; // 文件转存目录
|
||||||
|
|
|
@ -85,6 +85,11 @@ export enum RequestImportFormat {
|
||||||
Jmeter = 'Jmeter',
|
Jmeter = 'Jmeter',
|
||||||
Har = 'Har',
|
Har = 'Har',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum RequestExportFormat {
|
||||||
|
SWAGGER = 'Swagger',
|
||||||
|
MeterSphere = 'MeterSphere',
|
||||||
|
}
|
||||||
// 接口导入方式
|
// 接口导入方式
|
||||||
export enum RequestImportType {
|
export enum RequestImportType {
|
||||||
API = 'API',
|
API = 'API',
|
||||||
|
|
|
@ -187,6 +187,7 @@ export interface ApiDefinitionBatchParams extends BatchApiParams {
|
||||||
export interface ApiDefinitionBatchExportParams extends ApiDefinitionBatchParams {
|
export interface ApiDefinitionBatchExportParams extends ApiDefinitionBatchParams {
|
||||||
exportApiCase: boolean;
|
exportApiCase: boolean;
|
||||||
exportApiMock: boolean;
|
exportApiMock: boolean;
|
||||||
|
fileId: string;
|
||||||
sort: Record<string, any>;
|
sort: Record<string, any>;
|
||||||
}
|
}
|
||||||
// 批量更新定义参数
|
// 批量更新定义参数
|
||||||
|
|
|
@ -308,24 +308,27 @@
|
||||||
batchUpdateDefinition,
|
batchUpdateDefinition,
|
||||||
deleteDefinition,
|
deleteDefinition,
|
||||||
exportApiDefinition,
|
exportApiDefinition,
|
||||||
|
getApiDownloadFile,
|
||||||
getDefinitionPage,
|
getDefinitionPage,
|
||||||
sortDefinition,
|
sortDefinition,
|
||||||
|
stopApiExport,
|
||||||
updateDefinition,
|
updateDefinition,
|
||||||
} from '@/api/modules/api-test/management';
|
} from '@/api/modules/api-test/management';
|
||||||
import { getProjectInfo } from '@/api/modules/project-management/basicInfo';
|
import { getProjectInfo } from '@/api/modules/project-management/basicInfo';
|
||||||
|
import { getSocket } from '@/api/modules/project-management/commonScript';
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
import useModal from '@/hooks/useModal';
|
import useModal from '@/hooks/useModal';
|
||||||
import useTableStore from '@/hooks/useTableStore';
|
import useTableStore from '@/hooks/useTableStore';
|
||||||
import useAppStore from '@/store/modules/app';
|
import useAppStore from '@/store/modules/app';
|
||||||
import useCacheStore from '@/store/modules/cache/cache';
|
import useCacheStore from '@/store/modules/cache/cache';
|
||||||
import { characterLimit, downloadByteFile, operationWidth } from '@/utils';
|
import { characterLimit, downloadByteFile, getGenerateId, operationWidth } from '@/utils';
|
||||||
import { hasAnyPermission } from '@/utils/permission';
|
import { hasAnyPermission } from '@/utils/permission';
|
||||||
|
|
||||||
import { ProtocolItem } from '@/models/apiTest/common';
|
import { ProtocolItem } from '@/models/apiTest/common';
|
||||||
import { ApiDefinitionDetail, ApiDefinitionGetModuleParams } from '@/models/apiTest/management';
|
import { ApiDefinitionDetail, ApiDefinitionGetModuleParams } from '@/models/apiTest/management';
|
||||||
import { DragSortParams, ModuleTreeNode } from '@/models/common';
|
import { DragSortParams, ModuleTreeNode } from '@/models/common';
|
||||||
import { FilterType, ViewTypeEnum } from '@/enums/advancedFilterEnum';
|
import { FilterType, ViewTypeEnum } from '@/enums/advancedFilterEnum';
|
||||||
import { RequestDefinitionStatus, RequestImportFormat, RequestMethods } from '@/enums/apiEnum';
|
import { RequestDefinitionStatus, RequestExportFormat, RequestImportFormat, RequestMethods } from '@/enums/apiEnum';
|
||||||
import { CacheTabTypeEnum } from '@/enums/cacheTabEnum';
|
import { CacheTabTypeEnum } from '@/enums/cacheTabEnum';
|
||||||
import { TableKeyEnum } from '@/enums/tableEnum';
|
import { TableKeyEnum } from '@/enums/tableEnum';
|
||||||
import { FilterRemoteMethodsEnum, FilterSlotNameEnum } from '@/enums/tableFilterEnum';
|
import { FilterRemoteMethodsEnum, FilterSlotNameEnum } from '@/enums/tableFilterEnum';
|
||||||
|
@ -1057,21 +1060,122 @@
|
||||||
const platformList = [
|
const platformList = [
|
||||||
{
|
{
|
||||||
name: 'Swagger',
|
name: 'Swagger',
|
||||||
value: RequestImportFormat.SWAGGER,
|
value: RequestExportFormat.SWAGGER,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'MeterSphere',
|
name: 'MeterSphere',
|
||||||
value: RequestImportFormat.MeterSphere,
|
value: RequestExportFormat.MeterSphere,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
const exportPlatform = ref(RequestImportFormat.SWAGGER);
|
const exportPlatform = ref(RequestExportFormat.SWAGGER);
|
||||||
const exportApiCase = ref(false);
|
const exportApiCase = ref(false);
|
||||||
const exportApiMock = ref(false);
|
const exportApiMock = ref(false);
|
||||||
const exportLoading = ref(false);
|
const exportLoading = ref(false);
|
||||||
|
|
||||||
function cancelExport() {
|
function cancelExport() {
|
||||||
showExportModal.value = false;
|
showExportModal.value = false;
|
||||||
exportPlatform.value = RequestImportFormat.SWAGGER;
|
exportPlatform.value = RequestExportFormat.SWAGGER;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下载文件
|
||||||
|
async function downloadFile(id: string) {
|
||||||
|
try {
|
||||||
|
const response = await getApiDownloadFile(appStore.currentProjectId, id);
|
||||||
|
downloadByteFile(response, 'metersphere-export.json');
|
||||||
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 提示:导出成功
|
||||||
|
function showExportSuccessfulMessage(id: string, count: number) {
|
||||||
|
Message.success({
|
||||||
|
content: () =>
|
||||||
|
h('div', { class: 'flex flex-col gap-[8px] items-start' }, [
|
||||||
|
h('div', { class: 'font-medium' }, t('common.exportSuccessful')),
|
||||||
|
h('div', { class: 'flex items-center gap-[12px]' }, [
|
||||||
|
h('div', t('caseManagement.featureCase.exportCaseCount', { number: count })),
|
||||||
|
h(
|
||||||
|
MsButton,
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
onClick() {
|
||||||
|
downloadFile(id);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ default: () => t('common.downloadFile') }
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
duration: 999999999, // 一直展示,除非手动关闭
|
||||||
|
closable: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const websocket = ref<WebSocket>();
|
||||||
|
const reportId = ref('');
|
||||||
|
const isShowExportingMessage = ref(false); // 正在导出提示显示中
|
||||||
|
const exportingMessage = ref();
|
||||||
|
|
||||||
|
// 开启websocket监听,接收结果
|
||||||
|
function startWebsocketGetExportResult() {
|
||||||
|
websocket.value = getSocket(reportId.value, '/ws/export');
|
||||||
|
websocket.value.addEventListener('message', (event) => {
|
||||||
|
const data = JSON.parse(event.data);
|
||||||
|
if (data.msgType === 'EXEC_RESULT') {
|
||||||
|
exportingMessage.value.close();
|
||||||
|
reportId.value = data.fileId;
|
||||||
|
// taskId.value = data.taskId;
|
||||||
|
if (data.isSuccessful) {
|
||||||
|
showExportSuccessfulMessage(reportId.value, data.count);
|
||||||
|
} else {
|
||||||
|
Message.error({
|
||||||
|
content: t('common.exportFailed'),
|
||||||
|
duration: 999999999, // 一直展示,除非手动关闭
|
||||||
|
closable: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
websocket.value?.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取消导出
|
||||||
|
async function stopExport(taskId: string) {
|
||||||
|
try {
|
||||||
|
await stopApiExport(taskId);
|
||||||
|
exportingMessage.value.close();
|
||||||
|
websocket.value?.close();
|
||||||
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 提示:正在导出
|
||||||
|
function showExportingMessage(taskId: string) {
|
||||||
|
if (isShowExportingMessage.value) return;
|
||||||
|
isShowExportingMessage.value = true;
|
||||||
|
exportingMessage.value = Message.loading({
|
||||||
|
content: () =>
|
||||||
|
h('div', { class: 'flex items-center gap-[12px]' }, [
|
||||||
|
h('div', t('common.exporting')),
|
||||||
|
h(
|
||||||
|
MsButton,
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
onClick() {
|
||||||
|
stopExport(taskId);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ default: () => t('common.cancel') }
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
duration: 999999999, // 一直展示,除非手动关闭
|
||||||
|
closable: true,
|
||||||
|
onClose() {
|
||||||
|
isShowExportingMessage.value = false;
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1080,8 +1184,10 @@
|
||||||
async function exportApi() {
|
async function exportApi() {
|
||||||
try {
|
try {
|
||||||
exportLoading.value = true;
|
exportLoading.value = true;
|
||||||
|
reportId.value = getGenerateId();
|
||||||
|
startWebsocketGetExportResult();
|
||||||
const batchConditionParams = await getBatchConditionParams();
|
const batchConditionParams = await getBatchConditionParams();
|
||||||
const result = await exportApiDefinition(
|
const res = await exportApiDefinition(
|
||||||
{
|
{
|
||||||
selectIds: tableSelected.value as string[],
|
selectIds: tableSelected.value as string[],
|
||||||
selectAll: !!batchParams.value?.selectAll,
|
selectAll: !!batchParams.value?.selectAll,
|
||||||
|
@ -1090,11 +1196,12 @@
|
||||||
exportApiCase: exportApiCase.value,
|
exportApiCase: exportApiCase.value,
|
||||||
exportApiMock: exportApiMock.value,
|
exportApiMock: exportApiMock.value,
|
||||||
sort: propsRes.value.sorter || {},
|
sort: propsRes.value.sorter || {},
|
||||||
|
fileId: reportId.value,
|
||||||
},
|
},
|
||||||
exportPlatform.value
|
exportPlatform.value
|
||||||
);
|
);
|
||||||
const res = await getProjectInfo(appStore.currentProjectId);
|
|
||||||
downloadByteFile(new Blob([JSON.stringify(result)]), `Swagger_Api_${res.name}.json`);
|
showExportingMessage(res);
|
||||||
showExportModal.value = false;
|
showExportModal.value = false;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
|
|
Loading…
Reference in New Issue