feat(接口测试): 场景导出
This commit is contained in:
parent
3b437fe180
commit
710d51a0bc
|
@ -55,7 +55,6 @@ public class LocalRepositoryDir {
|
||||||
public static String getSystemTempDir() {
|
public static String getSystemTempDir() {
|
||||||
return SYSTEM_TEMP_DIR;
|
return SYSTEM_TEMP_DIR;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getSystemCacheDir() {
|
public static String getSystemCacheDir() {
|
||||||
return SYSTEM_CACHE_DIR;
|
return SYSTEM_CACHE_DIR;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,10 @@ import io.metersphere.sdk.dto.FileMetadataRepositoryDTO;
|
||||||
import io.metersphere.sdk.dto.FileModuleRepositoryDTO;
|
import io.metersphere.sdk.dto.FileModuleRepositoryDTO;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
public class FileRequest {
|
public class FileRequest {
|
||||||
|
|
||||||
private String folder;
|
private String folder;
|
||||||
|
@ -22,6 +24,12 @@ public class FileRequest {
|
||||||
public void setGitFileRequest(FileModuleRepositoryDTO repository, FileMetadataRepositoryDTO file) {
|
public void setGitFileRequest(FileModuleRepositoryDTO repository, FileMetadataRepositoryDTO file) {
|
||||||
gitFileRequest = new GitFileRequest(repository.getUrl(), repository.getToken(), repository.getUserName(), file.getBranch(), file.getCommitId());
|
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
|
@Data
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package io.metersphere.sdk.util;
|
package io.metersphere.sdk.util;
|
||||||
|
|
||||||
|
import io.metersphere.sdk.exception.MSException;
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
@ -138,4 +139,21 @@ public class TempFileUtils {
|
||||||
}
|
}
|
||||||
return previewByte;
|
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.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 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;
|
||||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||||
|
@ -330,4 +331,20 @@ public class ApiScenarioController {
|
||||||
request.setOperator(SessionUtils.getUserId());
|
request.setOperator(SessionUtils.getUserId());
|
||||||
apiScenarioDataTransferService.importScenario(file, request);
|
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
|
* @author wx
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
public class ApiExportResponse implements Serializable {
|
public class ApiDefinitionExportResponse implements Serializable {
|
||||||
|
|
||||||
@Serial
|
@Serial
|
||||||
private static final long serialVersionUID = 1L;
|
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
|
* @author wx
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
public class MetersphereApiExportResponse extends ApiExportResponse {
|
public class MetersphereApiDefinitionExportResponse extends ApiDefinitionExportResponse {
|
||||||
|
|
||||||
private List<ApiDefinitionExportDetail> apiDefinitions = new ArrayList<>();
|
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
|
* @author wx
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
public class SwaggerApiExportResponse extends ApiExportResponse{
|
public class SwaggerApiDefinitionExportResponse extends ApiDefinitionExportResponse {
|
||||||
|
|
||||||
private String openapi;
|
private String openapi;
|
||||||
private SwaggerInfo info;
|
private SwaggerInfo info;
|
|
@ -14,7 +14,7 @@ public class ApiScenarioImportRequest {
|
||||||
@NotBlank
|
@NotBlank
|
||||||
private String projectId;
|
private String projectId;
|
||||||
|
|
||||||
@Schema(description = "导入的类型 暂定 METERSPHERE JMETER")
|
@Schema(description = "导入的类型 METERSPHERE/JMETER")
|
||||||
@NotBlank
|
@NotBlank
|
||||||
private String type;
|
private String type;
|
||||||
|
|
||||||
|
|
|
@ -764,5 +764,4 @@
|
||||||
WHERE module_id = #{0}
|
WHERE module_id = #{0}
|
||||||
AND deleted = false
|
AND deleted = false
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|
|
@ -2,8 +2,8 @@ package io.metersphere.api.parser.api;
|
||||||
|
|
||||||
import io.metersphere.api.dto.converter.ApiDefinitionExportDetail;
|
import io.metersphere.api.dto.converter.ApiDefinitionExportDetail;
|
||||||
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.ApiDefinitionExportResponse;
|
||||||
import io.metersphere.api.dto.export.MetersphereApiExportResponse;
|
import io.metersphere.api.dto.export.MetersphereApiDefinitionExportResponse;
|
||||||
import io.metersphere.api.dto.mockserver.MockMatchRule;
|
import io.metersphere.api.dto.mockserver.MockMatchRule;
|
||||||
import io.metersphere.api.dto.mockserver.MockResponse;
|
import io.metersphere.api.dto.mockserver.MockResponse;
|
||||||
import io.metersphere.api.utils.ApiDataUtils;
|
import io.metersphere.api.utils.ApiDataUtils;
|
||||||
|
@ -18,12 +18,12 @@ import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class MetersphereExportParser {
|
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<ApiTestCaseWithBlob>> apiTestCaseMap = apiTestCaseList.stream().collect(Collectors.groupingBy(ApiTestCaseWithBlob::getApiDefinitionId));
|
||||||
Map<String, List<ApiMockWithBlob>> apiMockMap = apiMockList.stream().collect(Collectors.groupingBy(ApiMockWithBlob::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) {
|
for (ApiDefinitionWithBlob blob : apiDefinitionList) {
|
||||||
ApiDefinitionExportDetail detail = new ApiDefinitionExportDetail();
|
ApiDefinitionExportDetail detail = new ApiDefinitionExportDetail();
|
||||||
if (blob.getRequest() != null) {
|
if (blob.getRequest() != null) {
|
||||||
|
|
|
@ -24,12 +24,12 @@ import org.springframework.http.MediaType;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
public class Swagger3ExportParser implements ExportParser<ApiExportResponse> {
|
public class Swagger3ExportParser implements ExportParser<ApiDefinitionExportResponse> {
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ApiExportResponse parse(List<ApiDefinitionWithBlob> list, Project project, Map<String, String> moduleMap) throws Exception {
|
public ApiDefinitionExportResponse parse(List<ApiDefinitionWithBlob> list, Project project, Map<String, String> moduleMap) throws Exception {
|
||||||
SwaggerApiExportResponse response = new SwaggerApiExportResponse();
|
SwaggerApiDefinitionExportResponse response = new SwaggerApiDefinitionExportResponse();
|
||||||
//openapi
|
//openapi
|
||||||
response.setOpenapi("3.0.2");
|
response.setOpenapi("3.0.2");
|
||||||
//info
|
//info
|
||||||
|
|
|
@ -7,7 +7,7 @@ import io.metersphere.api.dto.converter.ApiDefinitionImportDataAnalysisResult;
|
||||||
import io.metersphere.api.dto.converter.ApiDefinitionImportFileParseResult;
|
import io.metersphere.api.dto.converter.ApiDefinitionImportFileParseResult;
|
||||||
import io.metersphere.api.dto.definition.ApiDefinitionMockDTO;
|
import io.metersphere.api.dto.definition.ApiDefinitionMockDTO;
|
||||||
import io.metersphere.api.dto.definition.ApiTestCaseDTO;
|
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.dto.request.ImportRequest;
|
||||||
import io.metersphere.api.parser.ApiDefinitionImportParser;
|
import io.metersphere.api.parser.ApiDefinitionImportParser;
|
||||||
import io.metersphere.api.utils.ApiDataUtils;
|
import io.metersphere.api.utils.ApiDataUtils;
|
||||||
|
@ -29,9 +29,9 @@ public class MetersphereParserApiDefinition implements ApiDefinitionImportParser
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ApiDefinitionImportFileParseResult parse(InputStream source, ImportRequest request) throws Exception {
|
public ApiDefinitionImportFileParseResult parse(InputStream source, ImportRequest request) throws Exception {
|
||||||
MetersphereApiExportResponse metersphereApiExportResponse = null;
|
MetersphereApiDefinitionExportResponse metersphereApiExportResponse = null;
|
||||||
try {
|
try {
|
||||||
metersphereApiExportResponse = ApiDataUtils.parseObject(source, MetersphereApiExportResponse.class);
|
metersphereApiExportResponse = ApiDataUtils.parseObject(source, MetersphereApiDefinitionExportResponse.class);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LogUtils.error(e.getMessage(), e);
|
LogUtils.error(e.getMessage(), e);
|
||||||
throw new MSException(e.getMessage());
|
throw new MSException(e.getMessage());
|
||||||
|
|
|
@ -1,23 +1,35 @@
|
||||||
package io.metersphere.api.service;
|
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.domain.*;
|
||||||
import io.metersphere.api.dto.ApiFile;
|
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.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.dto.scenario.*;
|
||||||
import io.metersphere.api.mapper.*;
|
import io.metersphere.api.mapper.*;
|
||||||
import io.metersphere.api.parser.ApiScenarioImportParser;
|
import io.metersphere.api.parser.ApiScenarioImportParser;
|
||||||
import io.metersphere.api.parser.ImportParserFactory;
|
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.ApiScenarioModuleService;
|
||||||
import io.metersphere.api.service.scenario.ApiScenarioService;
|
import io.metersphere.api.service.scenario.ApiScenarioService;
|
||||||
|
import io.metersphere.api.utils.ApiDataUtils;
|
||||||
import io.metersphere.api.utils.ApiDefinitionImportUtils;
|
import io.metersphere.api.utils.ApiDefinitionImportUtils;
|
||||||
import io.metersphere.api.utils.ApiScenarioImportUtils;
|
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.domain.Project;
|
||||||
import io.metersphere.project.mapper.ExtBaseProjectVersionMapper;
|
import io.metersphere.project.mapper.ExtBaseProjectVersionMapper;
|
||||||
import io.metersphere.project.mapper.ProjectMapper;
|
import io.metersphere.project.mapper.ProjectMapper;
|
||||||
import io.metersphere.sdk.constants.DefaultRepositoryDir;
|
import io.metersphere.project.utils.FileDownloadUtils;
|
||||||
import io.metersphere.sdk.constants.ModuleConstants;
|
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.*;
|
import io.metersphere.sdk.util.*;
|
||||||
import io.metersphere.system.constants.ExportConstants;
|
import io.metersphere.system.constants.ExportConstants;
|
||||||
import io.metersphere.system.domain.User;
|
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.mapper.UserMapper;
|
||||||
import io.metersphere.system.notice.constants.NoticeConstants;
|
import io.metersphere.system.notice.constants.NoticeConstants;
|
||||||
import io.metersphere.system.service.CommonNoticeSendService;
|
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.uid.IDGenerator;
|
||||||
import io.metersphere.system.utils.TreeNodeParseUtils;
|
import io.metersphere.system.utils.TreeNodeParseUtils;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import org.apache.commons.collections.MapUtils;
|
import org.apache.commons.collections.MapUtils;
|
||||||
import org.apache.commons.collections4.CollectionUtils;
|
import org.apache.commons.collections4.CollectionUtils;
|
||||||
import org.apache.commons.collections4.ListUtils;
|
import org.apache.commons.collections4.ListUtils;
|
||||||
|
@ -46,6 +62,8 @@ import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
@ -56,6 +74,12 @@ import static io.metersphere.project.utils.NodeSortUtils.DEFAULT_NODE_INTERVAL_P
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public class ApiScenarioDataTransferService {
|
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
|
@Resource
|
||||||
private ProjectMapper projectMapper;
|
private ProjectMapper projectMapper;
|
||||||
@Resource
|
@Resource
|
||||||
|
@ -66,7 +90,13 @@ public class ApiScenarioDataTransferService {
|
||||||
private ExportTaskManager exportTaskManager;
|
private ExportTaskManager exportTaskManager;
|
||||||
@Resource
|
@Resource
|
||||||
private ExtApiScenarioMapper extApiScenarioMapper;
|
private ExtApiScenarioMapper extApiScenarioMapper;
|
||||||
|
@Resource
|
||||||
|
private ExtApiDefinitionMapper extApiDefinitionMapper;
|
||||||
|
@Resource
|
||||||
|
private ExtApiTestCaseMapper extApiTestCaseMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private FileService fileService;
|
||||||
@Resource
|
@Resource
|
||||||
private CommonNoticeSendService commonNoticeSendService;
|
private CommonNoticeSendService commonNoticeSendService;
|
||||||
@Resource
|
@Resource
|
||||||
|
@ -79,17 +109,17 @@ public class ApiScenarioDataTransferService {
|
||||||
private SqlSessionFactory sqlSessionFactory;
|
private SqlSessionFactory sqlSessionFactory;
|
||||||
@Resource
|
@Resource
|
||||||
private OperationLogService operationLogService;
|
private OperationLogService operationLogService;
|
||||||
|
@Resource
|
||||||
private final ThreadLocal<Long> currentApiScenarioOrder = new ThreadLocal<>();
|
private NoticeSendService noticeSendService;
|
||||||
|
@Resource
|
||||||
private final ThreadLocal<Long> currentModuleOrder = new ThreadLocal<>();
|
private ApiScenarioLogService apiScenarioLogService;
|
||||||
|
@Resource
|
||||||
|
private ApiDefinitionExportService apiDefinitionExportService;
|
||||||
|
|
||||||
public String exportScenario(ApiScenarioBatchExportRequest request, String type, String userId) {
|
public String exportScenario(ApiScenarioBatchExportRequest request, String type, String userId) {
|
||||||
String returnId;
|
String returnId;
|
||||||
try {
|
try {
|
||||||
exportTaskManager.exportCheck(request.getProjectId(), ExportConstants.ExportType.API_SCENARIO.toString(), userId);
|
exportTaskManager.exportCheck(request.getProjectId(), ExportConstants.ExportType.API_SCENARIO.toString(), userId);
|
||||||
|
|
||||||
returnId = exportTaskManager.exportAsyncTask(
|
returnId = exportTaskManager.exportAsyncTask(
|
||||||
request.getProjectId(),
|
request.getProjectId(),
|
||||||
request.getFileId(), userId,
|
request.getFileId(), userId,
|
||||||
|
@ -323,7 +353,7 @@ public class ApiScenarioDataTransferService {
|
||||||
module.setParentId(t.getParentId());
|
module.setParentId(t.getParentId());
|
||||||
module.setProjectId(projectId);
|
module.setProjectId(projectId);
|
||||||
module.setCreateUser(operator);
|
module.setCreateUser(operator);
|
||||||
module.setPos(getImportNextModuleOrder(apiScenarioModuleService::getNextOrder, projectId));
|
module.setPos(getImportNextOrder(apiScenarioModuleService::getNextOrder, currentModuleOrder, projectId));
|
||||||
module.setCreateTime(System.currentTimeMillis());
|
module.setCreateTime(System.currentTimeMillis());
|
||||||
module.setUpdateUser(operator);
|
module.setUpdateUser(operator);
|
||||||
module.setUpdateTime(module.getCreateTime());
|
module.setUpdateTime(module.getCreateTime());
|
||||||
|
@ -352,7 +382,7 @@ public class ApiScenarioDataTransferService {
|
||||||
ApiScenario scenario = new ApiScenario();
|
ApiScenario scenario = new ApiScenario();
|
||||||
BeanUtils.copyBean(scenario, t);
|
BeanUtils.copyBean(scenario, t);
|
||||||
scenario.setNum(apiScenarioService.getNextNum(projectId));
|
scenario.setNum(apiScenarioService.getNextNum(projectId));
|
||||||
scenario.setPos(getImportNextModuleOrder(apiScenarioService::getNextOrder, projectId));
|
scenario.setPos(getImportNextOrder(apiScenarioService::getNextOrder, currentApiScenarioOrder, projectId));
|
||||||
scenario.setLatest(true);
|
scenario.setLatest(true);
|
||||||
scenario.setCreateUser(operator);
|
scenario.setCreateUser(operator);
|
||||||
scenario.setUpdateUser(operator);
|
scenario.setUpdateUser(operator);
|
||||||
|
@ -479,13 +509,13 @@ public class ApiScenarioDataTransferService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Long getImportNextModuleOrder(Function<String, Long> subFunc, String projectId) {
|
private Long getImportNextOrder(Function<String, Long> subFunc, ThreadLocal<Long> nextOrder, String projectId) {
|
||||||
Long order = currentModuleOrder.get();
|
Long order = nextOrder.get();
|
||||||
if (order == null) {
|
if (order == null) {
|
||||||
order = subFunc.apply(projectId);
|
order = subFunc.apply(projectId);
|
||||||
}
|
}
|
||||||
order = order + DEFAULT_NODE_INTERVAL_POS;
|
order = order + DEFAULT_NODE_INTERVAL_POS;
|
||||||
currentModuleOrder.set(order);
|
nextOrder.set(order);
|
||||||
return order;
|
return order;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -542,8 +572,167 @@ public class ApiScenarioDataTransferService {
|
||||||
return analysisResult;
|
return analysisResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String exportApiScenarioZip(ApiScenarioBatchExportRequest request, String type, String userId) {
|
private String exportApiScenarioZip(ApiScenarioBatchExportRequest request, String exportType, String userId) throws Exception {
|
||||||
// todo 场景导出
|
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;
|
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;
|
package io.metersphere.api.service.definition;
|
||||||
|
|
||||||
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.ApiDefinitionExportResponse;
|
||||||
import io.metersphere.api.mapper.ApiDefinitionModuleMapper;
|
|
||||||
import io.metersphere.api.mapper.ExtApiDefinitionMapper;
|
import io.metersphere.api.mapper.ExtApiDefinitionMapper;
|
||||||
import io.metersphere.api.mapper.ExtApiDefinitionMockMapper;
|
import io.metersphere.api.mapper.ExtApiDefinitionMockMapper;
|
||||||
import io.metersphere.api.mapper.ExtApiTestCaseMapper;
|
import io.metersphere.api.mapper.ExtApiTestCaseMapper;
|
||||||
|
@ -31,13 +30,10 @@ import io.metersphere.system.uid.IDGenerator;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
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.File;
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -52,12 +48,9 @@ import java.util.stream.Collectors;
|
||||||
@Service
|
@Service
|
||||||
public class ApiDefinitionExportService {
|
public class ApiDefinitionExportService {
|
||||||
|
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private ExtApiDefinitionMapper extApiDefinitionMapper;
|
private ExtApiDefinitionMapper extApiDefinitionMapper;
|
||||||
@Resource
|
@Resource
|
||||||
private ApiDefinitionModuleMapper apiDefinitionModuleMapper;
|
|
||||||
@Resource
|
|
||||||
private ExtApiTestCaseMapper extApiTestCaseMapper;
|
private ExtApiTestCaseMapper extApiTestCaseMapper;
|
||||||
@Resource
|
@Resource
|
||||||
private ExtApiDefinitionMockMapper extApiDefinitionMockMapper;
|
private ExtApiDefinitionMockMapper extApiDefinitionMockMapper;
|
||||||
|
@ -75,10 +68,12 @@ public class ApiDefinitionExportService {
|
||||||
private OperationLogService operationLogService;
|
private OperationLogService operationLogService;
|
||||||
@Resource
|
@Resource
|
||||||
private ApiDefinitionImportService apiDefinitionImportService;
|
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);
|
List<BaseTreeNode> apiModules = apiDefinitionImportService.buildTreeData(projectId, null);
|
||||||
Map<String, String> modulePathMap = new HashMap<>();
|
Map<String, String> modulePathMap = new HashMap<>();
|
||||||
apiModules.forEach(item -> {
|
apiModules.forEach(item -> {
|
||||||
|
@ -87,12 +82,12 @@ public class ApiDefinitionExportService {
|
||||||
return modulePathMap;
|
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());
|
List<ApiDefinitionWithBlob> list = this.selectAndSortByIds(request.getSelectIds());
|
||||||
return switch (type.toLowerCase()) {
|
return switch (type.toLowerCase()) {
|
||||||
case "swagger" -> exportSwagger(request, list, moduleMap);
|
case "swagger" -> exportSwagger(request, list, moduleMap);
|
||||||
case "metersphere" -> exportMetersphere(request, list, moduleMap);
|
case "metersphere" -> exportMetersphere(request, list, moduleMap);
|
||||||
default -> new ApiExportResponse();
|
default -> new ApiDefinitionExportResponse();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,31 +113,17 @@ public class ApiDefinitionExportService {
|
||||||
return returnId;
|
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 {
|
public String exportApiDefinitionZip(ApiDefinitionBatchExportRequest request, String exportType, String userId) throws Exception {
|
||||||
// 为避免客户端未及时开启ws,此处延迟1s
|
// 为避免客户端未及时开启ws,此处延迟1s
|
||||||
Thread.sleep(1000);
|
Thread.sleep(1000);
|
||||||
File tmpDir = null;
|
File tmpDir = new File(LocalRepositoryDir.getSystemTempDir() + File.separatorChar + EXPORT_CASE_TMP_DIR + "_" + IDGenerator.nextStr());
|
||||||
String fileType = "";
|
if (!tmpDir.mkdirs()) {
|
||||||
|
throw new MSException(Translator.get("upload_fail"));
|
||||||
|
}
|
||||||
|
String fileType = "zip";
|
||||||
try {
|
try {
|
||||||
User user = userMapper.selectByPrimaryKey(userId);
|
User user = userMapper.selectByPrimaryKey(userId);
|
||||||
noticeSendService.setLanguage(user.getLanguage());
|
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集合
|
//获取导出的ids集合
|
||||||
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)) {
|
||||||
|
@ -155,44 +136,23 @@ public class ApiDefinitionExportService {
|
||||||
int fileIndex = 1;
|
int fileIndex = 1;
|
||||||
SubListUtils.dealForSubList(ids, 1000, subList -> {
|
SubListUtils.dealForSubList(ids, 1000, subList -> {
|
||||||
request.setSelectIds(subList);
|
request.setSelectIds(subList);
|
||||||
ApiExportResponse exportResponse = this.genApiExportResponse(request, moduleMap, exportType, userId);
|
ApiDefinitionExportResponse exportResponse = this.genApiExportResponse(request, moduleMap, exportType, userId);
|
||||||
File createFile = new File(fileFolder + File.separatorChar + fileIndex + ".json");
|
TempFileUtils.writeExportFile(fileFolder + File.separatorChar + fileIndex + ".json", exportResponse);
|
||||||
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);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
File zipFile = MsFileUtils.zipFile(tmpDir.getPath(), request.getFileId());
|
File zipFile = MsFileUtils.zipFile(tmpDir.getPath(), request.getFileId());
|
||||||
if (zipFile == null) {
|
if (zipFile == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
fileService.upload(zipFile, new FileRequest(DefaultRepositoryDir.getExportApiTempDir(), StorageType.MINIO.name(), request.getFileId()));
|
||||||
uploadFileToMinioExportFolder(zipFile, request.getFileId());
|
|
||||||
|
|
||||||
// 生成日志
|
// 生成日志
|
||||||
LogDTO logDTO = apiDefinitionLogService.exportExcelLog(request, exportType, userId, projectMapper.selectByPrimaryKey(request.getProjectId()).getOrganizationId());
|
LogDTO logDTO = apiDefinitionLogService.exportExcelLog(request, exportType, userId, projectMapper.selectByPrimaryKey(request.getProjectId()).getOrganizationId());
|
||||||
operationLogService.add(logDTO);
|
operationLogService.add(logDTO);
|
||||||
|
|
||||||
List<ExportTask> exportTasks = exportTaskManager.getExportTasks(request.getProjectId(), ExportConstants.ExportType.API_DEFINITION.name(), ExportConstants.ExportState.PREPARED.toString(), userId, null);
|
String taskId = exportTaskManager.getExportTaskId(request.getProjectId(), ExportConstants.ExportType.API_DEFINITION.name(), ExportConstants.ExportState.PREPARED.toString(), userId, null, fileType);
|
||||||
String taskId;
|
ExportWebSocketHandler.sendMessageSingle(
|
||||||
if (CollectionUtils.isNotEmpty(exportTasks)) {
|
new ExportMsgDTO(request.getFileId(), taskId, ids.size(), true, MsgType.EXEC_RESULT.name())
|
||||||
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) {
|
} catch (Exception e) {
|
||||||
LogUtils.error(e);
|
LogUtils.error(e);
|
||||||
List<ExportTask> exportTasks = exportTaskManager.getExportTasks(request.getProjectId(), ExportConstants.ExportType.API_DEFINITION.name(), ExportConstants.ExportState.PREPARED.toString(), userId, null);
|
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());
|
Project project = projectMapper.selectByPrimaryKey(request.getProjectId());
|
||||||
Swagger3ExportParser swagger3Parser = new Swagger3ExportParser();
|
Swagger3ExportParser swagger3Parser = new Swagger3ExportParser();
|
||||||
try {
|
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 {
|
try {
|
||||||
List<String> apiIds = list.stream().map(ApiDefinitionWithBlob::getId).toList();
|
List<String> apiIds = list.stream().map(ApiDefinitionWithBlob::getId).toList();
|
||||||
List<ApiTestCaseWithBlob> apiTestCaseWithBlobs = new ArrayList<>();
|
List<ApiTestCaseWithBlob> apiTestCaseWithBlobs = new ArrayList<>();
|
||||||
|
@ -273,7 +233,7 @@ public class ApiDefinitionExportService {
|
||||||
fileRequest.setFileName(tasksFirst.getFileId());
|
fileRequest.setFileName(tasksFirst.getFileId());
|
||||||
fileRequest.setFolder(DefaultRepositoryDir.getExportApiTempDir());
|
fileRequest.setFolder(DefaultRepositoryDir.getExportApiTempDir());
|
||||||
fileRequest.setStorage(StorageType.MINIO.name());
|
fileRequest.setStorage(StorageType.MINIO.name());
|
||||||
String fileName = "Metersphere_case_" + project.getName() + "." + tasksFirst.getFileType();
|
String fileName = "Metersphere_api_" + project.getName() + "." + tasksFirst.getFileType();
|
||||||
try {
|
try {
|
||||||
InputStream fileInputStream = fileService.getFileAsStream(fileRequest);
|
InputStream fileInputStream = fileService.getFileAsStream(fileRequest);
|
||||||
FileDownloadUtils.zipFilesWithResponse(fileName, fileInputStream, httpServletResponse);
|
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.dto.LogDTO;
|
||||||
import io.metersphere.system.log.service.OperationLogService;
|
import io.metersphere.system.log.service.OperationLogService;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@ -233,4 +234,19 @@ public class ApiScenarioLogService {
|
||||||
);
|
);
|
||||||
operationLogService.batchAdd(logs);
|
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.debug.ApiFileResourceUpdateRequest;
|
||||||
import io.metersphere.api.dto.definition.ExecutePageRequest;
|
import io.metersphere.api.dto.definition.ExecutePageRequest;
|
||||||
import io.metersphere.api.dto.definition.ExecuteReportDTO;
|
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.ApiTransferRequest;
|
||||||
import io.metersphere.api.dto.request.controller.MsScriptElement;
|
import io.metersphere.api.dto.request.controller.MsScriptElement;
|
||||||
import io.metersphere.api.dto.request.http.MsHTTPElement;
|
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.service.definition.ApiTestCaseService;
|
||||||
import io.metersphere.api.utils.ApiDataUtils;
|
import io.metersphere.api.utils.ApiDataUtils;
|
||||||
import io.metersphere.api.utils.ApiScenarioBatchOperationUtils;
|
import io.metersphere.api.utils.ApiScenarioBatchOperationUtils;
|
||||||
|
import io.metersphere.api.utils.ApiScenarioUtils;
|
||||||
import io.metersphere.functional.domain.FunctionalCaseTestExample;
|
import io.metersphere.functional.domain.FunctionalCaseTestExample;
|
||||||
import io.metersphere.functional.mapper.FunctionalCaseTestMapper;
|
import io.metersphere.functional.mapper.FunctionalCaseTestMapper;
|
||||||
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
|
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.ScheduleUtils;
|
||||||
import io.metersphere.system.utils.ServiceUtils;
|
import io.metersphere.system.utils.ServiceUtils;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
import jakarta.validation.constraints.NotBlank;
|
import jakarta.validation.constraints.NotBlank;
|
||||||
import jakarta.validation.constraints.NotEmpty;
|
import jakarta.validation.constraints.NotEmpty;
|
||||||
import org.apache.commons.collections.MapUtils;
|
import org.apache.commons.collections.MapUtils;
|
||||||
|
@ -2463,4 +2466,50 @@ public class ApiScenarioService extends MoveNodeService {
|
||||||
setParamFunc.accept(apiStepResourceInfo, resource);
|
setParamFunc.accept(apiStepResourceInfo, resource);
|
||||||
return apiStepResourceInfo;
|
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.ApiFile;
|
||||||
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.MetersphereApiExportResponse;
|
import io.metersphere.api.dto.export.MetersphereApiDefinitionExportResponse;
|
||||||
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;
|
||||||
|
@ -2117,7 +2117,7 @@ public class ApiDefinitionControllerTests extends BaseTest {
|
||||||
Assertions.assertEquals(files.length, 1);
|
Assertions.assertEquals(files.length, 1);
|
||||||
String fileContent = FileUtils.readFileToString(files[0], StandardCharsets.UTF_8);
|
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);
|
apiDefinitionImportTestService.compareApiExport(exportResponse, exportApiBlobs);
|
||||||
|
|
||||||
|
|
|
@ -1,21 +1,33 @@
|
||||||
package io.metersphere.api.controller;
|
package io.metersphere.api.controller;
|
||||||
|
|
||||||
import io.metersphere.api.dto.definition.ApiDefinitionBatchExportRequest;
|
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.dto.scenario.ApiScenarioImportRequest;
|
||||||
|
import io.metersphere.api.utils.ApiDataUtils;
|
||||||
|
import io.metersphere.functional.domain.ExportTask;
|
||||||
import io.metersphere.project.domain.Project;
|
import io.metersphere.project.domain.Project;
|
||||||
|
import io.metersphere.sdk.constants.SessionConstants;
|
||||||
import io.metersphere.sdk.util.JSON;
|
import io.metersphere.sdk.util.JSON;
|
||||||
|
import io.metersphere.sdk.util.MsFileUtils;
|
||||||
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.dto.AddProjectRequest;
|
import io.metersphere.system.dto.AddProjectRequest;
|
||||||
import io.metersphere.system.log.constants.OperationLogModule;
|
import io.metersphere.system.log.constants.OperationLogModule;
|
||||||
|
import io.metersphere.system.manager.ExportTaskManager;
|
||||||
import io.metersphere.system.service.CommonProjectService;
|
import io.metersphere.system.service.CommonProjectService;
|
||||||
import io.metersphere.system.uid.IDGenerator;
|
import io.metersphere.system.uid.IDGenerator;
|
||||||
import jakarta.annotation.Resource;
|
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.junit.jupiter.api.*;
|
||||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.mock.web.MockMultipartFile;
|
import org.springframework.mock.web.MockMultipartFile;
|
||||||
import org.springframework.test.web.servlet.MvcResult;
|
import org.springframework.test.web.servlet.MvcResult;
|
||||||
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||||
import org.springframework.util.LinkedMultiValueMap;
|
import org.springframework.util.LinkedMultiValueMap;
|
||||||
import org.springframework.util.MultiValueMap;
|
import org.springframework.util.MultiValueMap;
|
||||||
|
|
||||||
|
@ -81,9 +93,14 @@ public class ApiScenarioControllerImportAndExportTests extends BaseTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ExportTaskManager exportTaskManager;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Order(1)
|
@Order(2)
|
||||||
public void testExport() throws Exception {
|
public void testExport() throws Exception {
|
||||||
|
MsFileUtils.deleteDir("/tmp/api-scenario-export/");
|
||||||
|
|
||||||
ApiDefinitionBatchExportRequest exportRequest = new ApiDefinitionBatchExportRequest();
|
ApiDefinitionBatchExportRequest exportRequest = new ApiDefinitionBatchExportRequest();
|
||||||
String fileId = IDGenerator.nextStr();
|
String fileId = IDGenerator.nextStr();
|
||||||
exportRequest.setProjectId(project.getId());
|
exportRequest.setProjectId(project.getId());
|
||||||
|
@ -93,5 +110,43 @@ public class ApiScenarioControllerImportAndExportTests extends BaseTest {
|
||||||
exportRequest.setExportApiMock(true);
|
exportRequest.setExportApiMock(true);
|
||||||
MvcResult mvcResult = this.requestPostWithOkAndReturn(URL_POST_EXPORT + "metersphere", exportRequest);
|
MvcResult mvcResult = this.requestPostWithOkAndReturn(URL_POST_EXPORT + "metersphere", exportRequest);
|
||||||
String returnData = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
|
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.domain.*;
|
||||||
import io.metersphere.api.dto.converter.ApiDefinitionExportDetail;
|
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.dto.request.http.MsHTTPElement;
|
||||||
import io.metersphere.api.mapper.ApiDefinitionBlobMapper;
|
import io.metersphere.api.mapper.ApiDefinitionBlobMapper;
|
||||||
import io.metersphere.api.mapper.ApiDefinitionMapper;
|
import io.metersphere.api.mapper.ApiDefinitionMapper;
|
||||||
|
@ -99,7 +99,7 @@ public class ApiDefinitionImportTestService extends ApiDefinitionImportService {
|
||||||
Assertions.assertEquals(moduleChangeCount, diffApiCount);
|
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());
|
Assertions.assertEquals(exportResponse.getApiDefinitions().size(), exportApiBlobs.size());
|
||||||
List<ApiDefinitionExportDetail> compareList = new ArrayList<>();
|
List<ApiDefinitionExportDetail> compareList = new ArrayList<>();
|
||||||
for (ApiDefinitionBlob blob : exportApiBlobs) {
|
for (ApiDefinitionBlob blob : exportApiBlobs) {
|
||||||
|
|
|
@ -4,6 +4,7 @@ import io.metersphere.functional.domain.ExportTask;
|
||||||
import io.metersphere.functional.domain.ExportTaskExample;
|
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.constants.MsgType;
|
||||||
import io.metersphere.sdk.exception.MSException;
|
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;
|
||||||
|
@ -11,6 +12,7 @@ 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;
|
||||||
|
import org.apache.commons.collections4.CollectionUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.kafka.clients.consumer.ConsumerRecord;
|
import org.apache.kafka.clients.consumer.ConsumerRecord;
|
||||||
import org.springframework.kafka.annotation.KafkaListener;
|
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) {
|
public List<ExportTask> getExportTasks(String projectId, String exportType, String exportState, String userId, String fileId) {
|
||||||
ExportTaskExample exportTaskExample = new ExportTaskExample();
|
ExportTaskExample exportTaskExample = new ExportTaskExample();
|
||||||
ExportTaskExample.Criteria criteria = exportTaskExample.createCriteria().andCreateUserEqualTo(userId).andProjectIdEqualTo(projectId);
|
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.stereotype.Service;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
|
@ -13,6 +15,12 @@ public class FileService {
|
||||||
return FileCenter.getRepository(request.getStorage()).saveFile(file, request);
|
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 {
|
public String upload(InputStream inputStream, FileRequest request) throws Exception {
|
||||||
return FileCenter.getRepository(request.getStorage()).saveFile(inputStream, request);
|
return FileCenter.getRepository(request.getStorage()).saveFile(inputStream, request);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue