feat(接口测试): 场景MS格式导入-全量数据导入 (#33723)

* feat(接口测试): 场景MS格式导入-全量数据导入

* feat(接口测试): 场景MS格式导入-全量数据导入

---------

Co-authored-by: Jianguo-Genius <herman.song.yang@gmail.com>
This commit is contained in:
MeterSphere Bot 2024-10-23 19:00:29 +08:00 committed by GitHub
parent 176801e843
commit fdbdb44364
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 704 additions and 94 deletions

View File

@ -22,7 +22,6 @@ public class ApiScenarioImportParseResult {
@Schema(description = "有关联关系的场景") @Schema(description = "有关联关系的场景")
List<ApiScenarioImportDetail> relatedScenarioList = new ArrayList<>(); List<ApiScenarioImportDetail> relatedScenarioList = new ArrayList<>();
@Schema(description = "场景CSV相关的数据") @Schema(description = "场景CSV相关的数据")
private List<ApiScenarioCsv> apiScenarioCsvList = new ArrayList<>(); private List<ApiScenarioCsv> apiScenarioCsvList = new ArrayList<>();

View File

@ -1,5 +1,6 @@
package io.metersphere.api.dto.converter; package io.metersphere.api.dto.converter;
import io.metersphere.api.dto.definition.ApiTestCaseDTO;
import io.metersphere.api.dto.scenario.ApiScenarioImportDetail; import io.metersphere.api.dto.scenario.ApiScenarioImportDetail;
import io.metersphere.system.dto.sdk.BaseTreeNode; import io.metersphere.system.dto.sdk.BaseTreeNode;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
@ -12,7 +13,7 @@ import java.util.List;
public class ApiScenarioPreImportAnalysisResult { public class ApiScenarioPreImportAnalysisResult {
@Schema(description = "需要创建的模块数据") @Schema(description = "需要创建的模块数据")
List<BaseTreeNode> insertModuleList = new ArrayList<>(); List<BaseTreeNode> insertScenarioModuleList = new ArrayList<>();
@Schema(description = "需要新增的场景") @Schema(description = "需要新增的场景")
List<ApiScenarioImportDetail> insertApiScenarioData = new ArrayList<>(); List<ApiScenarioImportDetail> insertApiScenarioData = new ArrayList<>();
@ -20,4 +21,24 @@ public class ApiScenarioPreImportAnalysisResult {
@Schema(description = "需要更新的场景") @Schema(description = "需要更新的场景")
List<ApiScenarioImportDetail> updateApiScenarioData = new ArrayList<>(); List<ApiScenarioImportDetail> updateApiScenarioData = new ArrayList<>();
@Schema(description = "需要新增的接口定义, ID已经生成好")
private List<ApiDefinitionDetail> insertApiDefinitions = new ArrayList<>();
@Schema(description = "需要新增的接口定义模块")
private List<BaseTreeNode> insertApiModuleList = new ArrayList<>();
@Schema(description = "需要新增的接口用例, ID已经生成好")
private List<ApiTestCaseDTO> insertApiTestCaseList = new ArrayList<>();
public void setApiDefinition(ApiDefinitionDetail insertApi) {
this.insertApiDefinitions.add(insertApi);
}
public void setApiTestCase(ApiTestCaseDTO insertCase) {
this.insertApiTestCaseList.add(insertCase);
}
public void setApiScenario(ApiScenarioImportDetail insertScenario) {
this.insertApiScenarioData.add(insertScenario);
}
} }

View File

@ -30,9 +30,30 @@ public class ApiTestCaseWithBlob extends ApiTestCaseBlob {
@Size(min = 1, max = 20, message = "{api_test_case.status.length_range}", groups = {Created.class, Updated.class}) @Size(min = 1, max = 20, message = "{api_test_case.status.length_range}", groups = {Created.class, Updated.class})
private String status; private String status;
@Schema(description = "api的协议")
private String protocol;
@Schema(description = "api的路径")
private String path;
@Schema(description = "api的方法")
private String method;
@Schema(description = "模块ID")
private String moduleId;
@Schema(description = "最新执行结果状态") @Schema(description = "最新执行结果状态")
private String lastReportStatus; private String lastReportStatus;
@Schema(description = "接口定义ID") @Schema(description = "接口定义ID")
private String apiDefinitionId; private String apiDefinitionId;
@Schema(description = "接口定义名称")
private String apiDefinitionName;
@Schema(description = "接口用例编号id")
private Long num;
@Schema(description = "项目fk", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{api_test_case.project_id.not_blank}", groups = {Created.class})
@Size(min = 1, max = 50, message = "{api_test_case.project_id.length_range}", groups = {Created.class, Updated.class})
private String projectId;
} }

View File

@ -103,4 +103,6 @@ public interface ExtApiDefinitionMapper {
List<ApiDefinition> getListBySelectIds(@Param("projectId") String projectId, @Param("ids") List<String> ids, @Param("protocols") List<String> protocols); List<ApiDefinition> getListBySelectIds(@Param("projectId") String projectId, @Param("ids") List<String> ids, @Param("protocols") List<String> protocols);
List<String> getIdsByShareParam(@Param("projectId") String projectId, @Param("condition") String condition); List<String> getIdsByShareParam(@Param("projectId") String projectId, @Param("condition") String condition);
long countByProjectAndId(@Param("projectId") String projectId, @Param("id") String id);
} }

View File

@ -159,7 +159,8 @@
</select> </select>
<select id="importList" resultType="io.metersphere.api.dto.converter.ApiDefinitionDetail"> <select id="importList" resultType="io.metersphere.api.dto.converter.ApiDefinitionDetail">
select select
api_definition.id, api_definition.`name`, api_definition.protocol, api_definition.`method`, api_definition.id, api_definition.`name`, api_definition.protocol,
api_definition.`method`,api_definition.project_id,
api_definition.`path`, api_definition.version_id, api_definition.`path`, api_definition.version_id,
api_definition.ref_id, api_definition.module_id api_definition.ref_id, api_definition.module_id
from api_definition from api_definition
@ -758,4 +759,11 @@
and ${condition} and ${condition}
</if> </if>
</select> </select>
<select id="countByProjectAndId" resultType="java.lang.Long">
SELECT count(id)
FROM api_definition
WHERE project_id = #{projectId}
AND id = #{id}
AND deleted = false
</select>
</mapper> </mapper>

View File

@ -129,4 +129,6 @@ public interface ExtApiTestCaseMapper {
List<ApiTestCaseWithBlob> selectAllDetailByApiIds(@Param("apiIds") List<String> apiIds); List<ApiTestCaseWithBlob> selectAllDetailByApiIds(@Param("apiIds") List<String> apiIds);
List<ApiTestCaseWithBlob> selectAllDetailByIds(@Param("ids") List<String> apiIds); List<ApiTestCaseWithBlob> selectAllDetailByIds(@Param("ids") List<String> apiIds);
List<ApiTestCaseDTO> selectBaseInfoByProjectIdAndApiId(@Param("projectId") String projectId, @Param("apiId") String apiId);
} }

View File

@ -978,6 +978,7 @@
and api_scenario.deleted = 0; and api_scenario.deleted = 0;
</select> </select>
<resultMap id="ApiTestCaseDetailMap" type="io.metersphere.api.dto.definition.ApiTestCaseWithBlob"> <resultMap id="ApiTestCaseDetailMap" type="io.metersphere.api.dto.definition.ApiTestCaseWithBlob">
<result column="module_id" jdbcType="VARCHAR" property="moduleId"/>
<result column="request" jdbcType="LONGVARBINARY" property="request"/> <result column="request" jdbcType="LONGVARBINARY" property="request"/>
<result column="tags" jdbcType="VARCHAR" property="tags" typeHandler="io.metersphere.handler.ListTypeHandler"/> <result column="tags" jdbcType="VARCHAR" property="tags" typeHandler="io.metersphere.handler.ListTypeHandler"/>
</resultMap> </resultMap>
@ -992,9 +993,11 @@
</select> </select>
<select id="selectAllDetailByIds" resultMap="ApiTestCaseDetailMap"> <select id="selectAllDetailByIds" resultMap="ApiTestCaseDetailMap">
SELECT apiTestCase.*,apiTestCaseBlob.request SELECT apiTestCase.*,ad.protocol,ad.path,ad.method,ad.module_id,ad.name AS apiDefinitionName,
apiTestCaseBlob.request
FROM api_test_case apiTestCase FROM api_test_case apiTestCase
INNER JOIN api_test_case_blob apiTestCaseBlob ON apiTestCase.id = apiTestCaseBlob.id INNER JOIN api_test_case_blob apiTestCaseBlob ON apiTestCase.id = apiTestCaseBlob.id
INNER JOIN api_definition ad ON apiTestCase.api_definition_id = ad.id
WHERE apiTestCase.id IN WHERE apiTestCase.id IN
<foreach collection="ids" item="id" separator="," open="(" close=")"> <foreach collection="ids" item="id" separator="," open="(" close=")">
#{id} #{id}
@ -1009,4 +1012,12 @@
#{id} #{id}
</foreach> </foreach>
</select> </select>
<select id="selectBaseInfoByProjectIdAndApiId"
resultType="io.metersphere.api.dto.definition.ApiTestCaseDTO">
SELECT id, num, name, project_id, api_definition_id
FROM api_test_case
WHERE project_id = #{projectId}
AND api_definition_id = #{apiId}
AND deleted = false
</select>
</mapper> </mapper>

View File

@ -12,6 +12,7 @@ import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.util.BeanUtils; import io.metersphere.sdk.util.BeanUtils;
import io.metersphere.sdk.util.LogUtils; import io.metersphere.sdk.util.LogUtils;
import io.metersphere.system.uid.IDGenerator; import io.metersphere.system.uid.IDGenerator;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import java.io.InputStream; import java.io.InputStream;
@ -85,9 +86,21 @@ public class MetersphereParserApiScenario implements ApiScenarioImportParser {
ApiScenarioStepParseResult apiScenarioStepParseResult = new ApiScenarioStepParseResult(); ApiScenarioStepParseResult apiScenarioStepParseResult = new ApiScenarioStepParseResult();
List<ApiScenarioStepDTO> stepList = apiScenarioStepMap.getOrDefault(scenarioId, new ArrayList<>()); List<ApiScenarioStepDTO> stepList = apiScenarioStepMap.getOrDefault(scenarioId, new ArrayList<>());
for (ApiScenarioStepDTO stepDTO : stepList) { if (stepList.isEmpty()) {
return apiScenarioStepParseResult;
}
List<ApiScenarioStepDTO> firstStepList = stepList.stream().filter(step -> StringUtils.isEmpty(step.getParentId())).toList();
if (CollectionUtils.isEmpty(firstStepList)) {
// 需要补充的场景数据中它的步骤很可能存在于要导入的场景数据里所以他们的parentId不一定为null这时候要去parentId
// 去parentId方案 将sort为1的步骤作为基点它的parentId不起效果所有以此为parentId的数据是第一层数据
List<ApiScenarioStepDTO> sort1ScenarioList = stepList.stream().filter(step -> step.getSort() == 1).toList();
if (CollectionUtils.isNotEmpty(sort1ScenarioList)) {
ApiScenarioStepDTO firstScenario = sort1ScenarioList.getFirst();
firstStepList = stepList.stream().filter(step -> StringUtils.equals(step.getParentId(), firstScenario.getParentId())).toList();
}
}
for (ApiScenarioStepDTO stepDTO : firstStepList) {
String oldStepId = stepDTO.getId(); String oldStepId = stepDTO.getId();
ApiScenarioStepRequest stepRequest = new ApiScenarioStepRequest(); ApiScenarioStepRequest stepRequest = new ApiScenarioStepRequest();
BeanUtils.copyBean(stepRequest, stepDTO); BeanUtils.copyBean(stepRequest, stepDTO);
// 赋值新ID防止和库内已有数据重复 // 赋值新ID防止和库内已有数据重复

View File

@ -1,9 +1,11 @@
package io.metersphere.api.service; package io.metersphere.api.service;
import io.metersphere.api.constants.ApiDefinitionStatus;
import io.metersphere.api.constants.ApiScenarioExportType; import io.metersphere.api.constants.ApiScenarioExportType;
import io.metersphere.api.constants.ApiScenarioStepType; 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.ApiDefinitionDetail;
import io.metersphere.api.dto.converter.ApiDefinitionExportDetail; import io.metersphere.api.dto.converter.ApiDefinitionExportDetail;
import io.metersphere.api.dto.converter.ApiScenarioImportParseResult; import io.metersphere.api.dto.converter.ApiScenarioImportParseResult;
import io.metersphere.api.dto.converter.ApiScenarioPreImportAnalysisResult; import io.metersphere.api.dto.converter.ApiScenarioPreImportAnalysisResult;
@ -15,6 +17,9 @@ 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.definition.ApiDefinitionExportService;
import io.metersphere.api.service.definition.ApiDefinitionImportService;
import io.metersphere.api.service.definition.ApiDefinitionModuleService;
import io.metersphere.api.service.definition.ApiTestCaseService;
import io.metersphere.api.service.scenario.ApiScenarioLogService; 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;
@ -26,6 +31,7 @@ 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.project.service.PermissionCheckService;
import io.metersphere.project.utils.FileDownloadUtils; import io.metersphere.project.utils.FileDownloadUtils;
import io.metersphere.sdk.constants.*; import io.metersphere.sdk.constants.*;
import io.metersphere.sdk.dto.ExportMsgDTO; import io.metersphere.sdk.dto.ExportMsgDTO;
@ -47,9 +53,12 @@ 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.socket.ExportWebSocketHandler;
import io.metersphere.system.uid.IDGenerator; import io.metersphere.system.uid.IDGenerator;
import io.metersphere.system.uid.NumGenerator;
import io.metersphere.system.utils.TreeNodeParseUtils; import io.metersphere.system.utils.TreeNodeParseUtils;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import lombok.Data;
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;
@ -65,6 +74,7 @@ import org.springframework.web.multipart.MultipartFile;
import java.io.File; import java.io.File;
import java.io.InputStream; import java.io.InputStream;
import java.nio.charset.StandardCharsets;
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;
@ -77,7 +87,13 @@ public class ApiScenarioDataTransferService {
private final ThreadLocal<Long> currentApiScenarioOrder = new ThreadLocal<>(); private final ThreadLocal<Long> currentApiScenarioOrder = new ThreadLocal<>();
private final ThreadLocal<Long> currentModuleOrder = new ThreadLocal<>(); private final ThreadLocal<Long> currentScenarioModuleOrder = new ThreadLocal<>();
private final ThreadLocal<Long> currentApiModuleOrder = new ThreadLocal<>();
private final ThreadLocal<Long> currentApiOrder = new ThreadLocal<>();
private final ThreadLocal<Long> currentApiTestCaseOrder = new ThreadLocal<>();
private static final String EXPORT_CASE_TMP_DIR = "apiScenario"; private static final String EXPORT_CASE_TMP_DIR = "apiScenario";
@ -116,6 +132,14 @@ public class ApiScenarioDataTransferService {
private ApiScenarioLogService apiScenarioLogService; private ApiScenarioLogService apiScenarioLogService;
@Resource @Resource
private ApiDefinitionExportService apiDefinitionExportService; private ApiDefinitionExportService apiDefinitionExportService;
@Resource
private PermissionCheckService permissionCheckService;
@Resource
private ApiDefinitionImportService apiDefinitionImportService;
@Resource
private ApiDefinitionModuleService apiDefinitionModuleService;
@Resource
private ApiTestCaseService apiTestCaseService;
public String exportScenario(ApiScenarioBatchExportRequest request, String type, String userId) { public String exportScenario(ApiScenarioBatchExportRequest request, String type, String userId) {
String returnId; String returnId;
@ -155,25 +179,114 @@ public class ApiScenarioDataTransferService {
} }
//解析 //解析
ApiScenarioPreImportAnalysisResult preImportAnalysisResult = this.importAnalysis( ApiScenarioPreImportAnalysisResult preImportAnalysisResult = this.importAnalysis(
parseResult, request.getProjectId(), request.getModuleId(), apiScenarioModuleService.getTree(request.getProjectId())); parseResult, request.getOperator(), request.getProjectId(), request.getModuleId(), apiScenarioModuleService.getTree(request.getProjectId()));
//存储 //存储
this.save(preImportAnalysisResult, request.getProjectId(), request.getOperator(), request.isCoverData()); this.save(preImportAnalysisResult, request.getProjectId(), request.getOperator(), request.isCoverData());
} }
private void save(ApiScenarioPreImportAnalysisResult preImportAnalysisResult, String projectId, String operator, boolean isCoverData) { private void save(ApiScenarioPreImportAnalysisResult preImportAnalysisResult, String projectId, String operator, boolean isCoverData) {
List<LogDTO> operationLogs = new ArrayList<>(); List<LogDTO> operationLogs = new ArrayList<>();
currentModuleOrder.remove();
currentApiScenarioOrder.remove();
// 更新修改数据 // 更新修改数据
{ {
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH); SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
this.insertModule(projectId, operator, preImportAnalysisResult.getInsertModuleList(), sqlSession); // 接口定义模块
if (CollectionUtils.isNotEmpty(preImportAnalysisResult.getInsertApiModuleList())) {
Map<String, List<BaseTreeNode>> projectMap = preImportAnalysisResult.getInsertApiModuleList().stream().collect(Collectors.groupingBy(BaseTreeNode::getProjectId));
ApiDefinitionModuleMapper batchApiModuleMapper = sqlSession.getMapper(ApiDefinitionModuleMapper.class);
projectMap.forEach((targetProjectId, nodeList) -> {
currentApiModuleOrder.remove();
nodeList.forEach(t -> {
ApiDefinitionModule module = new ApiDefinitionModule();
module.setId(t.getId());
module.setName(t.getName());
module.setParentId(t.getParentId());
module.setProjectId(t.getProjectId());
module.setCreateUser(operator);
module.setPos(getImportNextOrder(apiDefinitionModuleService::getNextOrder, currentApiModuleOrder, targetProjectId));
module.setCreateTime(System.currentTimeMillis());
module.setUpdateUser(operator);
module.setUpdateTime(System.currentTimeMillis());
batchApiModuleMapper.insertSelective(module);
});
sqlSession.flushStatements();
});
}
// 接口定义
if (CollectionUtils.isNotEmpty(preImportAnalysisResult.getInsertApiDefinitions())) {
Map<String, List<ApiDefinitionDetail>> projectMap = preImportAnalysisResult.getInsertApiDefinitions().stream().collect(Collectors.groupingBy(ApiDefinitionDetail::getProjectId));
ApiDefinitionMapper batchApiMapper = sqlSession.getMapper(ApiDefinitionMapper.class);
ApiDefinitionBlobMapper batchApiBlobMapper = sqlSession.getMapper(ApiDefinitionBlobMapper.class);
projectMap.forEach((targetProjectId, apiList) -> {
currentApiOrder.remove();
String versionId = extBaseProjectVersionMapper.getDefaultVersion(targetProjectId);
for (ApiDefinitionDetail t : apiList) {
ApiDefinition apiDefinition = new ApiDefinition();
BeanUtils.copyBean(apiDefinition, t);
apiDefinition.setPos(getImportNextOrder(apiDefinitionImportService::getNextOrder, currentApiOrder, targetProjectId));
apiDefinition.setLatest(true);
apiDefinition.setStatus(ApiDefinitionStatus.PROCESSING.name());
apiDefinition.setRefId(apiDefinition.getId());
apiDefinition.setVersionId(versionId);
apiDefinition.setCreateUser(operator);
apiDefinition.setCreateTime(System.currentTimeMillis());
apiDefinition.setUpdateUser(operator);
apiDefinition.setUpdateTime(System.currentTimeMillis());
batchApiMapper.insertSelective(apiDefinition);
//插入blob数据
ApiDefinitionBlob apiDefinitionBlob = new ApiDefinitionBlob();
apiDefinitionBlob.setId(apiDefinition.getId());
apiDefinitionBlob.setRequest(ApiDataUtils.toJSONString(t.getRequest()).getBytes(StandardCharsets.UTF_8));
apiDefinitionBlob.setResponse(ApiDataUtils.toJSONString(t.getResponse()).getBytes(StandardCharsets.UTF_8));
batchApiBlobMapper.insertSelective(apiDefinitionBlob);
}
sqlSession.flushStatements();
});
}
// 接口用例
if (CollectionUtils.isNotEmpty(preImportAnalysisResult.getInsertApiTestCaseList())) {
Map<String, List<ApiTestCaseDTO>> projectMap = preImportAnalysisResult.getInsertApiTestCaseList().stream().collect(Collectors.groupingBy(ApiTestCaseDTO::getProjectId));
ApiTestCaseMapper batchApiCaseMapper = sqlSession.getMapper(ApiTestCaseMapper.class);
ApiTestCaseBlobMapper batchApiBlobCaseMapper = sqlSession.getMapper(ApiTestCaseBlobMapper.class);
projectMap.forEach((targetProjectId, testCaseList) -> {
currentApiTestCaseOrder.remove();
String versionId = extBaseProjectVersionMapper.getDefaultVersion(targetProjectId);
testCaseList.forEach(t -> {
ApiTestCase apiTestCase = new ApiTestCase();
BeanUtils.copyBean(apiTestCase, t);
apiTestCase.setProjectId(targetProjectId);
apiTestCase.setPos(getImportNextOrder(apiTestCaseService::getNextOrder, currentApiTestCaseOrder, targetProjectId));
apiTestCase.setNum(NumGenerator.nextNum(targetProjectId, ApplicationNumScope.API_DEFINITION));
apiTestCase.setStatus(ApiDefinitionStatus.PROCESSING.name());
apiTestCase.setVersionId(versionId);
apiTestCase.setCreateUser(operator);
apiTestCase.setCreateTime(System.currentTimeMillis());
apiTestCase.setUpdateUser(operator);
apiTestCase.setUpdateTime(System.currentTimeMillis());
batchApiCaseMapper.insertSelective(apiTestCase);
//插入blob数据
ApiTestCaseBlob apiTestCaseBlob = new ApiTestCaseBlob();
apiTestCaseBlob.setId(apiTestCase.getId());
apiTestCaseBlob.setRequest(apiDefinitionImportService.getMsTestElementStr(t.getRequest()).getBytes());
batchApiBlobCaseMapper.insertSelective(apiTestCaseBlob);
});
sqlSession.flushStatements();
});
}
//场景模块
this.insertModule(operator, preImportAnalysisResult.getInsertScenarioModuleList(), sqlSession);
if (isCoverData) { if (isCoverData) {
this.updateScenarios(projectId, operator, preImportAnalysisResult.getUpdateApiScenarioData(), sqlSession); this.updateScenarios(projectId, operator, preImportAnalysisResult.getUpdateApiScenarioData(), sqlSession);
} }
String versionId = extBaseProjectVersionMapper.getDefaultVersion(projectId); String versionId = extBaseProjectVersionMapper.getDefaultVersion(projectId);
this.insertScenarios(projectId, operator, versionId, preImportAnalysisResult.getInsertApiScenarioData(), sqlSession); //场景
this.insertScenarios(operator, versionId, preImportAnalysisResult.getInsertApiScenarioData(), sqlSession);
sqlSession.flushStatements(); sqlSession.flushStatements();
SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory); SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory);
} }
@ -183,7 +296,7 @@ public class ApiScenarioDataTransferService {
List<ApiScenarioImportDetail> noticeCreateLists = new ArrayList<>(); List<ApiScenarioImportDetail> noticeCreateLists = new ArrayList<>();
List<ApiScenarioImportDetail> noticeUpdateLists = new ArrayList<>(); List<ApiScenarioImportDetail> noticeUpdateLists = new ArrayList<>();
preImportAnalysisResult.getInsertModuleList().forEach(t -> preImportAnalysisResult.getInsertScenarioModuleList().forEach(t ->
operationLogs.add(ApiDefinitionImportUtils.genImportLog(project, t.getId(), t.getName(), t, OperationLogModule.API_SCENARIO_MANAGEMENT_MODULE, operator, OperationLogType.ADD.name())) operationLogs.add(ApiDefinitionImportUtils.genImportLog(project, t.getId(), t.getName(), t, OperationLogModule.API_SCENARIO_MANAGEMENT_MODULE, operator, OperationLogType.ADD.name()))
); );
@ -339,20 +452,23 @@ public class ApiScenarioDataTransferService {
} }
} }
private void insertModule(String projectId, String operator, List<BaseTreeNode> insertModuleList, SqlSession sqlSession) { private void insertModule(String operator, List<BaseTreeNode> insertModuleList, SqlSession sqlSession) {
if (CollectionUtils.isEmpty(insertModuleList)) { if (CollectionUtils.isEmpty(insertModuleList)) {
return; return;
} }
ApiScenarioModuleMapper batchApiDefinitionMapper = sqlSession.getMapper(ApiScenarioModuleMapper.class); ApiScenarioModuleMapper batchApiDefinitionMapper = sqlSession.getMapper(ApiScenarioModuleMapper.class);
SubListUtils.dealForSubList(insertModuleList, 100, list -> {
Map<String, List<BaseTreeNode>> projectMap = insertModuleList.stream().collect(Collectors.groupingBy(BaseTreeNode::getProjectId));
projectMap.forEach((targetProjectId, list) -> {
list.forEach(t -> { list.forEach(t -> {
ApiScenarioModule module = new ApiScenarioModule(); ApiScenarioModule module = new ApiScenarioModule();
module.setId(t.getId()); module.setId(t.getId());
module.setName(t.getName()); module.setName(t.getName());
module.setParentId(t.getParentId()); module.setParentId(t.getParentId());
module.setProjectId(projectId); module.setProjectId(t.getProjectId());
module.setCreateUser(operator); module.setCreateUser(operator);
module.setPos(getImportNextOrder(apiScenarioModuleService::getNextOrder, currentModuleOrder, projectId)); module.setPos(getImportNextOrder(apiScenarioModuleService::getNextOrder, currentScenarioModuleOrder, targetProjectId));
module.setCreateTime(System.currentTimeMillis()); module.setCreateTime(System.currentTimeMillis());
module.setUpdateUser(operator); module.setUpdateUser(operator);
module.setUpdateTime(module.getCreateTime()); module.setUpdateTime(module.getCreateTime());
@ -362,7 +478,7 @@ public class ApiScenarioDataTransferService {
}); });
} }
private void insertScenarios(String projectId, String operator, String versionId, List<ApiScenarioImportDetail> insertScenarioList, SqlSession sqlSession) { private void insertScenarios(String operator, String versionId, List<ApiScenarioImportDetail> insertScenarioList, SqlSession sqlSession) {
// 创建场景 // 创建场景
if (CollectionUtils.isEmpty(insertScenarioList)) { if (CollectionUtils.isEmpty(insertScenarioList)) {
return; return;
@ -375,13 +491,16 @@ public class ApiScenarioDataTransferService {
ApiScenarioStepMapper stepBatchMapper = sqlSession.getMapper(ApiScenarioStepMapper.class); ApiScenarioStepMapper stepBatchMapper = sqlSession.getMapper(ApiScenarioStepMapper.class);
ApiScenarioStepBlobMapper stepBlobBatchMapper = sqlSession.getMapper(ApiScenarioStepBlobMapper.class); ApiScenarioStepBlobMapper stepBlobBatchMapper = sqlSession.getMapper(ApiScenarioStepBlobMapper.class);
SubListUtils.dealForSubList(insertScenarioList, 100, list -> { Map<String, List<ApiScenarioImportDetail>> projectMap = insertScenarioList.stream().collect(Collectors.groupingBy(ApiScenarioImportDetail::getProjectId));
projectMap.forEach((targetProjectId, targetList) -> {
currentApiScenarioOrder.remove();
SubListUtils.dealForSubList(targetList, 100, list -> {
list.forEach(t -> { list.forEach(t -> {
t.setId(IDGenerator.nextStr());
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(targetProjectId));
scenario.setPos(getImportNextOrder(apiScenarioService::getNextOrder, currentApiScenarioOrder, projectId)); scenario.setPos(getImportNextOrder(apiScenarioService::getNextOrder, currentApiScenarioOrder, targetProjectId));
scenario.setLatest(true); scenario.setLatest(true);
scenario.setCreateUser(operator); scenario.setCreateUser(operator);
scenario.setUpdateUser(operator); scenario.setUpdateUser(operator);
@ -411,6 +530,9 @@ public class ApiScenarioDataTransferService {
}); });
sqlSession.flushStatements(); sqlSession.flushStatements();
}); });
});
} }
private void handleStepAdd(ApiScenarioImportDetail t, ApiScenario scenario, private void handleStepAdd(ApiScenarioImportDetail t, ApiScenario scenario,
@ -518,14 +640,17 @@ public class ApiScenarioDataTransferService {
return order; return order;
} }
private ApiScenarioPreImportAnalysisResult importAnalysis(ApiScenarioImportParseResult parseResult, String projectId, String moduleId, List<BaseTreeNode> apiScenarioModules) { private ApiScenarioPreImportAnalysisResult importAnalysis(ApiScenarioImportParseResult parseResult, String operator, String projectId, String moduleId, List<BaseTreeNode> apiScenarioModules) {
ApiScenarioPreImportAnalysisResult analysisResult = new ApiScenarioPreImportAnalysisResult(); ApiScenarioPreImportAnalysisResult analysisResult = new ApiScenarioPreImportAnalysisResult();
Map<String, String> moduleIdPathMap = apiScenarioModules.stream().collect(Collectors.toMap(BaseTreeNode::getId, BaseTreeNode::getPath)); Map<String, String> moduleIdPathMap = apiScenarioModules.stream().collect(Collectors.toMap(BaseTreeNode::getId, BaseTreeNode::getPath));
Map<String, BaseTreeNode> modulePathMap = apiScenarioModules.stream().collect(Collectors.toMap(BaseTreeNode::getPath, k -> k, (k1, k2) -> k1)); Map<String, BaseTreeNode> modulePathMap = apiScenarioModules.stream().collect(Collectors.toMap(BaseTreeNode::getPath, k -> k, (k1, k2) -> k1));
ReplaceScenarioResource replaceScenarioResource = this.parseRelatedDataToAnalysisResult(operator, projectId, parseResult, analysisResult);
List<ApiScenarioImportDetail> importScenarios = parseResult.getImportScenarioList(); List<ApiScenarioImportDetail> importScenarios = parseResult.getImportScenarioList();
for (ApiScenarioImportDetail importScenario : importScenarios) { for (ApiScenarioImportDetail importScenario : importScenarios) {
//处理模块
if (StringUtils.isBlank(moduleId) || StringUtils.equalsIgnoreCase(moduleId, ModuleConstants.DEFAULT_NODE_ID) || !moduleIdPathMap.containsKey(moduleId)) { if (StringUtils.isBlank(moduleId) || StringUtils.equalsIgnoreCase(moduleId, ModuleConstants.DEFAULT_NODE_ID) || !moduleIdPathMap.containsKey(moduleId)) {
importScenario.setModuleId(ModuleConstants.DEFAULT_NODE_ID); importScenario.setModuleId(ModuleConstants.DEFAULT_NODE_ID);
importScenario.setModulePath(moduleIdPathMap.get(ModuleConstants.DEFAULT_NODE_ID)); importScenario.setModulePath(moduleIdPathMap.get(ModuleConstants.DEFAULT_NODE_ID));
@ -555,23 +680,329 @@ public class ApiScenarioDataTransferService {
scenario.setId(existenceNameIdMap.get(scenario.getName())); scenario.setId(existenceNameIdMap.get(scenario.getName()));
analysisResult.getUpdateApiScenarioData().add(scenario); analysisResult.getUpdateApiScenarioData().add(scenario);
} else { } else {
scenario.setId(IDGenerator.nextStr());
scenario.setModuleId(modulePathMap.get(finalModulePath).getId()); scenario.setModuleId(modulePathMap.get(finalModulePath).getId());
analysisResult.getInsertApiScenarioData().add(scenario); analysisResult.getInsertApiScenarioData().add(scenario);
} }
}); });
} else { } else {
//模块不存在的必定是新建 //模块不存在的必定是新建
analysisResult.getInsertModuleList().addAll(TreeNodeParseUtils.getInsertNodeByPath(modulePathMap, modulePath)); List<BaseTreeNode> insertModuleList = TreeNodeParseUtils.getInsertNodeByPath(modulePathMap, modulePath);
insertModuleList.forEach(item -> item.setProjectId(projectId));
analysisResult.getInsertScenarioModuleList().addAll(insertModuleList);
String finalModulePath = modulePath; String finalModulePath = modulePath;
scenarios.forEach(scenario -> scenarios.forEach(scenario -> {
scenario.setModuleId(modulePathMap.get(finalModulePath).getId()) scenario.setId(IDGenerator.nextStr());
); scenario.setModuleId(modulePathMap.get(finalModulePath).getId());
});
analysisResult.getInsertApiScenarioData().addAll(scenarios); analysisResult.getInsertApiScenarioData().addAll(scenarios);
} }
}); });
//将要补的场景数据导入进来
analysisResult.getInsertApiScenarioData().addAll(replaceScenarioResource.getInsertRelatedApiScenarioData());
for (ApiScenarioImportDetail importScenario : analysisResult.getInsertApiScenarioData()) {
// 处理步骤里的关联资源文件
importScenario.getSteps().forEach(item -> {
if (StringUtils.equalsIgnoreCase(item.getStepType(), ApiScenarioStepType.API.name())) {
ApiDefinitionDetail apiDetail = replaceScenarioResource.getApi(item.getResourceId());
if (apiDetail != null) {
item.setResourceId(apiDetail.getId());
item.setProjectId(importScenario.getProjectId());
item.setOriginProjectId(apiDetail.getProjectId());
}
} else if (StringUtils.equalsIgnoreCase(item.getStepType(), ApiScenarioStepType.API_CASE.name())) {
ApiTestCaseDTO newData = replaceScenarioResource.getApiCase(item.getResourceId());
if (newData != null) {
item.setResourceId(newData.getId());
item.setProjectId(importScenario.getProjectId());
item.setOriginProjectId(newData.getProjectId());
}
} else if (StringUtils.equalsIgnoreCase(item.getStepType(), ApiScenarioStepType.API_SCENARIO.name())) {
ApiScenarioImportDetail newData = replaceScenarioResource.getApiScenario(item.getResourceId());
if (newData != null) {
item.setResourceId(newData.getId());
item.setProjectId(importScenario.getProjectId());
item.setOriginProjectId(newData.getProjectId());
}
}
});
}
for (ApiScenarioImportDetail updateScenario : analysisResult.getUpdateApiScenarioData()) {
// 处理步骤里的关联资源文件
updateScenario.getSteps().forEach(item -> {
if (StringUtils.equalsIgnoreCase(item.getStepType(), ApiScenarioStepType.API.name())) {
ApiDefinitionDetail apiDetail = replaceScenarioResource.getApi(item.getResourceId());
if (apiDetail != null) {
item.setResourceId(apiDetail.getId());
item.setProjectId(updateScenario.getProjectId());
item.setOriginProjectId(apiDetail.getProjectId());
}
} else if (StringUtils.equalsIgnoreCase(item.getStepType(), ApiScenarioStepType.API_CASE.name())) {
ApiTestCaseDTO newData = replaceScenarioResource.getApiCase(item.getResourceId());
if (newData != null) {
item.setResourceId(newData.getId());
item.setProjectId(updateScenario.getProjectId());
item.setOriginProjectId(newData.getProjectId());
}
} else if (StringUtils.equalsIgnoreCase(item.getStepType(), ApiScenarioStepType.API_SCENARIO.name())) {
ApiScenarioImportDetail newData = replaceScenarioResource.getApiScenario(item.getResourceId());
if (newData != null) {
item.setResourceId(newData.getId());
item.setProjectId(updateScenario.getProjectId());
item.setOriginProjectId(newData.getProjectId());
}
}
});
}
return analysisResult; return analysisResult;
} }
private ReplaceScenarioResource parseRelatedDataToAnalysisResult(String operator, String projectId, ApiScenarioImportParseResult parseResult, ApiScenarioPreImportAnalysisResult analysisResult) {
/*
1.判断接口定义中要创建的部分然后进行新旧ID映射
2.判断用例中的接口定义是否在上一步中创建如果没有则进行筛选判断符合条件的接口定义若没有则新建并关联到其下面
*/
Map<String, List<ApiDefinitionDetail>> projectApiMap = new HashMap<>();
Map<String, List<ApiTestCaseDTO>> projectApiCaseMap = new HashMap<>();
Map<String, List<ApiScenarioImportDetail>> projectScenarioMap = new HashMap<>();
// 对导入文件中的数据进行分组
{
for (Map.Entry<String, List<ApiDefinitionDetail>> entry : parseResult.getRelatedApiDefinitions().stream().collect(Collectors.groupingBy(ApiDefinitionDetail::getProjectId)).entrySet()) {
String targetProjectId = entry.getKey();
List<ApiDefinitionDetail> apiDefinitionDetails = entry.getValue();
// 如果没有权限需要在当前项目进行校验
if (projectMapper.selectByPrimaryKey(targetProjectId) == null) {
targetProjectId = projectId;
} else if (!permissionCheckService.userHasProjectPermission(operator, targetProjectId, PermissionConstants.PROJECT_API_DEFINITION_ADD)) {
targetProjectId = projectId;
}
if (projectApiMap.containsKey(targetProjectId)) {
projectApiMap.get(targetProjectId).addAll(apiDefinitionDetails);
} else {
projectApiMap.put(targetProjectId, apiDefinitionDetails);
}
}
for (Map.Entry<String, List<ApiTestCaseDTO>> entry : parseResult.getRelatedApiTestCaseList().stream().collect(Collectors.groupingBy(ApiTestCaseDTO::getProjectId)).entrySet()) {
String targetProjectId = entry.getKey();
List<ApiTestCaseDTO> apiTestCaseList = entry.getValue();
// 如果没有权限需要在当前项目进行校验
if (projectMapper.selectByPrimaryKey(targetProjectId) == null) {
targetProjectId = projectId;
} else if (!permissionCheckService.userHasProjectPermission(operator, targetProjectId, PermissionConstants.PROJECT_API_DEFINITION_CASE_ADD)) {
targetProjectId = projectId;
}
if (projectApiCaseMap.containsKey(targetProjectId)) {
projectApiCaseMap.get(targetProjectId).addAll(apiTestCaseList);
} else {
projectApiCaseMap.put(targetProjectId, apiTestCaseList);
}
}
for (Map.Entry<String, List<ApiScenarioImportDetail>> entry : parseResult.getRelatedScenarioList().stream().collect(Collectors.groupingBy(ApiScenarioImportDetail::getProjectId)).entrySet()) {
String targetProjectId = entry.getKey();
List<ApiScenarioImportDetail> scenarioList = entry.getValue();
// 如果没有权限需要在当前项目进行校验
if (projectMapper.selectByPrimaryKey(targetProjectId) == null) {
targetProjectId = projectId;
} else if (!permissionCheckService.userHasProjectPermission(operator, targetProjectId, PermissionConstants.PROJECT_API_DEFINITION_CASE_ADD)) {
targetProjectId = projectId;
}
if (projectScenarioMap.containsKey(targetProjectId)) {
projectScenarioMap.get(targetProjectId).addAll(scenarioList);
} else {
projectScenarioMap.put(targetProjectId, scenarioList);
}
}
}
ReplaceScenarioResource returnResource = new ReplaceScenarioResource();
// 1.处理接口定义
{
for (Map.Entry<String, List<ApiDefinitionDetail>> entry : projectApiMap.entrySet()) {
String targetProjectId = entry.getKey();
List<ApiDefinitionDetail> apiDefinitionDetails = entry.getValue();
Map<String, List<ApiDefinitionDetail>> protocolImportMap = apiDefinitionDetails.stream().collect(Collectors.groupingBy(ApiDefinitionDetail::getProtocol));
ApiDefinitionPageRequest pageRequest = new ApiDefinitionPageRequest();
pageRequest.setProjectId(targetProjectId);
pageRequest.setProtocols(new ArrayList<>(protocolImportMap.keySet()));
List<ApiDefinitionDetail> existenceApiDefinitionList = extApiDefinitionMapper.importList(pageRequest);
Map<String, List<ApiDefinitionDetail>> existenceProtocolMap = existenceApiDefinitionList.stream().collect(Collectors.groupingBy(ApiDefinitionDetail::getProtocol));
for (Map.Entry<String, List<ApiDefinitionDetail>> protocolEntry : protocolImportMap.entrySet()) {
returnResource.getApiDefinitionIdMap().putAll(ApiScenarioImportUtils.getApiIdInTargetList(
protocolEntry.getValue(), existenceProtocolMap.get(protocolEntry.getKey()), protocolEntry.getKey(), projectId, analysisResult));
}
// 解析接口定义的模块
List<BaseTreeNode> apiModules = apiDefinitionImportService.buildTreeData(targetProjectId, null);
Map<String, BaseTreeNode> modulePathMap = apiModules.stream().collect(Collectors.toMap(BaseTreeNode::getPath, k -> k, (k1, k2) -> k1));
for (ApiDefinitionDetail apiDefinitionDetail : analysisResult.getInsertApiDefinitions()) {
List<BaseTreeNode> insertModuleList = TreeNodeParseUtils.getInsertNodeByPath(modulePathMap, apiDefinitionDetail.getModulePath());
insertModuleList.forEach(item -> item.setProjectId(targetProjectId));
analysisResult.getInsertApiModuleList().addAll(insertModuleList);
}
}
}
// 2.处理接口用例
{
for (Map.Entry<String, List<ApiTestCaseDTO>> entry : projectApiCaseMap.entrySet()) {
String targetProjectId = entry.getKey();
List<ApiTestCaseDTO> apiTestCaseDTOS = entry.getValue();
Map<String, List<ApiTestCaseDTO>> protocolMap = apiTestCaseDTOS.stream().collect(Collectors.groupingBy(ApiTestCaseDTO::getProtocol));
//将项目下的用例通过协议关键词进行分组处理
ApiDefinitionPageRequest pageRequest = new ApiDefinitionPageRequest();
pageRequest.setProjectId(targetProjectId);
pageRequest.setProtocols(new ArrayList<>(protocolMap.keySet()));
List<ApiDefinitionDetail> existenceApiDefinitionList = extApiDefinitionMapper.importList(pageRequest);
for (Map.Entry<String, List<ApiTestCaseDTO>> protocolEntry : protocolMap.entrySet()) {
String protocol = protocolEntry.getKey();
List<ApiTestCaseDTO> protocolList = protocolEntry.getValue();
Map<String, List<ApiTestCaseDTO>> apiIdMap = protocolList.stream().collect(Collectors.groupingBy(ApiTestCaseDTO::getApiDefinitionId));
for (Map.Entry<String, List<ApiTestCaseDTO>> apiIdEntry : apiIdMap.entrySet()) {
String replaceApiDefinitionId = returnResource.getApi(apiIdEntry.getKey()) == null ? StringUtils.EMPTY : returnResource.getApi(apiIdEntry.getKey()).getId();
List<ApiTestCaseDTO> testCaseList = apiIdEntry.getValue();
if (StringUtils.isBlank(replaceApiDefinitionId)) {
// 用例对应的接口在上述步骤中未处理
boolean apiExistence = ApiScenarioImportUtils.isApiExistence(
protocol, testCaseList.getFirst().getMethod(), testCaseList.getFirst().getPath(),
testCaseList.getFirst().getModulePath(), testCaseList.getFirst().getApiDefinitionName(), existenceApiDefinitionList);
if (apiExistence) {
Map<Long, ApiTestCaseDTO> existenceApiCaseNumMap = extApiTestCaseMapper.selectBaseInfoByProjectIdAndApiId(targetProjectId, apiIdEntry.getKey())
.stream().collect(Collectors.toMap(ApiTestCaseDTO::getNum, Function.identity(), (k1, k2) -> k1));
for (ApiTestCaseDTO apiTestCaseDTO : apiIdEntry.getValue()) {
if (existenceApiCaseNumMap.containsKey(apiTestCaseDTO.getNum())) {
returnResource.putApiTestCase(apiTestCaseDTO.getId(), existenceApiCaseNumMap.get(apiTestCaseDTO.getNum()));
} else {
apiTestCaseDTO.setId(IDGenerator.nextStr());
apiTestCaseDTO.setProjectId(targetProjectId);
returnResource.putApiTestCase(apiTestCaseDTO.getId(), apiTestCaseDTO);
analysisResult.setApiTestCase(apiTestCaseDTO);
}
}
} else {
// 同步创建API
ApiDefinitionDetail apiDefinitionDetail = new ApiDefinitionDetail();
apiDefinitionDetail.setProjectId(targetProjectId);
apiDefinitionDetail.setModulePath(testCaseList.getFirst().getModulePath());
apiDefinitionDetail.setId(IDGenerator.nextStr());
apiDefinitionDetail.setName(testCaseList.getFirst().getApiDefinitionName());
apiDefinitionDetail.setRequest(testCaseList.getFirst().getRequest());
apiDefinitionDetail.setProtocol(testCaseList.getFirst().getProtocol());
apiDefinitionDetail.setPath(testCaseList.getFirst().getPath());
apiDefinitionDetail.setMethod(testCaseList.getFirst().getMethod());
apiDefinitionDetail.setResponse(new ArrayList<>());
returnResource.putApiDefinitionId(apiIdEntry.getKey(), apiDefinitionDetail);
analysisResult.setApiDefinition(apiDefinitionDetail);
for (ApiTestCaseDTO apiTestCaseDTO : testCaseList) {
apiTestCaseDTO.setId(IDGenerator.nextStr());
apiTestCaseDTO.setProjectId(targetProjectId);
apiTestCaseDTO.setApiDefinitionId(apiDefinitionDetail.getId());
returnResource.putApiTestCase(apiTestCaseDTO.getId(), apiTestCaseDTO);
analysisResult.setApiTestCase(apiTestCaseDTO);
}
}
} else {
Map<Long, ApiTestCaseDTO> existenceApiCaseNumMap = extApiTestCaseMapper.selectBaseInfoByProjectIdAndApiId(targetProjectId, replaceApiDefinitionId)
.stream().collect(Collectors.toMap(ApiTestCaseDTO::getNum, Function.identity(), (k1, k2) -> k1));
for (ApiTestCaseDTO apiTestCaseDTO : testCaseList) {
apiTestCaseDTO.setApiDefinitionId(replaceApiDefinitionId);
if (existenceApiCaseNumMap.containsKey(apiTestCaseDTO.getNum())) {
returnResource.putApiTestCase(apiTestCaseDTO.getId(), existenceApiCaseNumMap.get(apiTestCaseDTO.getNum()));
} else {
apiTestCaseDTO.setProjectId(targetProjectId);
returnResource.putApiTestCase(apiTestCaseDTO.getId(), apiTestCaseDTO);
apiTestCaseDTO.setId(IDGenerator.nextStr());
analysisResult.setApiTestCase(apiTestCaseDTO);
}
}
}
}
}
}
}
// 3. 处理场景
{
for (Map.Entry<String, List<ApiScenarioImportDetail>> entry : projectScenarioMap.entrySet()) {
String targetProjectId = entry.getKey();
List<BaseTreeNode> apiScenarioModules = apiScenarioModuleService.getTree(targetProjectId);
Map<String, String> moduleIdPathMap = apiScenarioModules.stream().collect(Collectors.toMap(BaseTreeNode::getId, BaseTreeNode::getPath));
Map<String, BaseTreeNode> modulePathMap = apiScenarioModules.stream().collect(Collectors.toMap(BaseTreeNode::getPath, k -> k, (k1, k2) -> k1));
List<ApiScenarioImportDetail> importScenarios = parseResult.getRelatedScenarioList();
for (ApiScenarioImportDetail importScenario : importScenarios) {
importScenario.setProjectId(targetProjectId);
if (StringUtils.isBlank(importScenario.getModulePath()) || StringUtils.equals(importScenario.getModulePath().trim(), "/")) {
importScenario.setModuleId(ModuleConstants.DEFAULT_NODE_ID);
importScenario.setModulePath(moduleIdPathMap.get(ModuleConstants.DEFAULT_NODE_ID));
} else {
if (!StringUtils.startsWith(importScenario.getModulePath(), "/")) {
importScenario.setModulePath("/" + importScenario.getModulePath());
}
if (StringUtils.endsWith(importScenario.getModulePath(), "/")) {
importScenario.setModulePath(importScenario.getModulePath().substring(0, importScenario.getModulePath().length() - 1));
}
}
}
Map<String, List<ApiScenarioImportDetail>> modulePathScenario = importScenarios.stream().collect(Collectors.groupingBy(ApiScenarioImportDetail::getModulePath));
for (Map.Entry<String, List<ApiScenarioImportDetail>> modulePathEntry : modulePathScenario.entrySet()) {
String modulePath = modulePathEntry.getKey();
Map<String, ApiScenario> apiScenarioNameMap = new HashMap<>();
String moduleId = null;
// 设置moduleId
if (!modulePathMap.containsKey(modulePath)) {
List<BaseTreeNode> insertModuleList = TreeNodeParseUtils.getInsertNodeByPath(modulePathMap, modulePath);
insertModuleList.forEach(item -> item.setProjectId(targetProjectId));
analysisResult.getInsertScenarioModuleList().addAll(insertModuleList);
moduleId = modulePathMap.get(modulePath).getId();
} else {
moduleId = modulePathMap.get(modulePath).getId();
apiScenarioNameMap = extApiScenarioMapper.selectBaseInfoByModuleIdAndProjectId(moduleId, targetProjectId)
.stream().collect(Collectors.toMap(ApiScenario::getName, Function.identity(), (k1, k2) -> k1));
}
Map<String, ApiScenarioImportDetail> createdNameOldIds = new HashMap<>();
for (ApiScenarioImportDetail scenario : modulePathEntry.getValue()) {
scenario.setModuleId(moduleId);
if (createdNameOldIds.containsKey(scenario.getName())) {
returnResource.putApiScenarioId(scenario.getId(), createdNameOldIds.get(scenario.getName()));
} else {
if (apiScenarioNameMap.containsKey(scenario.getName())) {
ApiScenarioImportDetail oldData = new ApiScenarioImportDetail();
BeanUtils.copyBean(oldData, apiScenarioNameMap.get(scenario.getName()));
returnResource.putApiScenarioId(scenario.getId(), oldData);
createdNameOldIds.put(scenario.getName(), oldData);
} else {
String oldId = scenario.getId();
scenario.setId(IDGenerator.nextStr());
scenario.setProjectId(targetProjectId);
returnResource.getInsertRelatedApiScenarioData().add(scenario);
returnResource.putApiScenarioId(oldId, scenario);
}
}
}
}
}
}
return returnResource;
}
private String exportApiScenarioZip(ApiScenarioBatchExportRequest request, String exportType, String userId) throws Exception { private String exportApiScenarioZip(ApiScenarioBatchExportRequest request, String exportType, String userId) throws Exception {
File tmpDir = new File(LocalRepositoryDir.getSystemTempDir() + File.separatorChar + EXPORT_CASE_TMP_DIR + "_" + IDGenerator.nextStr()); File tmpDir = new File(LocalRepositoryDir.getSystemTempDir() + File.separatorChar + EXPORT_CASE_TMP_DIR + "_" + IDGenerator.nextStr());
if (!tmpDir.mkdirs()) { if (!tmpDir.mkdirs()) {
@ -659,29 +1090,21 @@ public class ApiScenarioDataTransferService {
} }
} }
Map<String, Map<String, String>> projectApiModuleIdMap = new HashMap<>(); Map<String, Map<String, String>> projectApiModuleIdMap = new HashMap<>();
if (CollectionUtils.isNotEmpty(apiDefinitionIdList)) { if (CollectionUtils.isNotEmpty(apiDefinitionIdList)) {
List<ApiDefinitionWithBlob> apiList = extApiDefinitionMapper.selectApiDefinitionWithBlob(apiDefinitionIdList); List<ApiDefinitionWithBlob> apiList = extApiDefinitionMapper.selectApiDefinitionWithBlob(apiDefinitionIdList);
List<String> projectIdList = apiList.stream().map(ApiDefinitionWithBlob::getProjectId).distinct().toList(); List<String> projectIdList = apiList.stream().map(ApiDefinitionWithBlob::getProjectId).distinct().toList();
projectIdList.forEach(projectId -> projectApiModuleIdMap.put(projectId, apiDefinitionExportService.buildModuleIdPathMap(request.getProjectId()))); projectIdList.forEach(projectId -> projectApiModuleIdMap.put(projectId, apiDefinitionExportService.buildModuleIdPathMap(request.getProjectId())));
for (ApiDefinitionWithBlob blob : apiList) { for (ApiDefinitionWithBlob blob : apiList) {
ApiDefinitionExportDetail detail = new ApiDefinitionExportDetail(); ApiDefinitionExportDetail detail = new ApiDefinitionExportDetail();
BeanUtils.copyBean(detail, blob);
if (blob.getRequest() != null) { if (blob.getRequest() != null) {
detail.setRequest(ApiDataUtils.parseObject(new String(blob.getRequest()), AbstractMsTestElement.class)); detail.setRequest(ApiDataUtils.parseObject(new String(blob.getRequest()), AbstractMsTestElement.class));
} }
if (blob.getResponse() != null) { if (blob.getResponse() != null) {
detail.setResponse(ApiDataUtils.parseArray(new String(blob.getResponse()), HttpResponse.class)); 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; String moduleName;
if (StringUtils.equals(blob.getModuleId(), ModuleConstants.DEFAULT_NODE_ID)) { if (StringUtils.equals(blob.getModuleId(), ModuleConstants.DEFAULT_NODE_ID)) {
moduleName = Translator.get("api_unplanned_request"); moduleName = Translator.get("api_unplanned_request");
@ -698,12 +1121,27 @@ public class ApiScenarioDataTransferService {
} }
if (CollectionUtils.isNotEmpty(apiTestCaseWithBlobs)) { if (CollectionUtils.isNotEmpty(apiTestCaseWithBlobs)) {
for (ApiTestCaseWithBlob apiTestCaseWithBlob : apiTestCaseWithBlobs) { for (ApiTestCaseWithBlob apiTestCaseWithBlob : apiTestCaseWithBlobs) {
String projectId = apiTestCaseWithBlob.getProjectId();
if (!projectApiModuleIdMap.containsKey(projectId)) {
projectApiModuleIdMap.put(projectId, apiDefinitionExportService.buildModuleIdPathMap(projectId));
}
String moduleId = apiTestCaseWithBlob.getModuleId();
ApiTestCaseDTO dto = new ApiTestCaseDTO(); ApiTestCaseDTO dto = new ApiTestCaseDTO();
dto.setName(apiTestCaseWithBlob.getName()); BeanUtils.copyBean(dto, apiTestCaseWithBlob);
dto.setPriority(apiTestCaseWithBlob.getPriority());
dto.setStatus(apiTestCaseWithBlob.getStatus());
dto.setTags(apiTestCaseWithBlob.getTags()); dto.setTags(apiTestCaseWithBlob.getTags());
dto.setRequest(ApiDataUtils.parseObject(new String(apiTestCaseWithBlob.getRequest()), AbstractMsTestElement.class)); dto.setRequest(ApiDataUtils.parseObject(new String(apiTestCaseWithBlob.getRequest()), AbstractMsTestElement.class));
String moduleName;
if (StringUtils.equals(moduleId, ModuleConstants.DEFAULT_NODE_ID)) {
moduleName = Translator.get("api_unplanned_request");
} else if (projectApiModuleIdMap.containsKey(projectId) && projectApiModuleIdMap.get(projectId).containsKey(moduleId)) {
moduleName = projectApiModuleIdMap.get(projectId).get(moduleId);
} else {
dto.setModuleId(ModuleConstants.DEFAULT_NODE_ID);
moduleName = Translator.get("api_unplanned_request");
}
dto.setModulePath(moduleName);
response.addExportApiCase(dto); response.addExportApiCase(dto);
} }
} }
@ -740,3 +1178,38 @@ public class ApiScenarioDataTransferService {
} }
} }
} }
@Data
class ReplaceScenarioResource {
private Map<String, ApiDefinitionDetail> apiDefinitionIdMap = new HashMap<>();
private Map<String, ApiTestCaseDTO> apiTestCaseIdMap = new HashMap<>();
private Map<String, ApiScenarioImportDetail> apiScenarioIdMap = new HashMap<>();
@Schema(description = "要新增的关联场景")
List<ApiScenarioImportDetail> insertRelatedApiScenarioData = new ArrayList<>();
public ApiDefinitionDetail getApi(String apiOldId) {
return apiDefinitionIdMap.get(apiOldId);
}
public ApiTestCaseDTO getApiCase(String oldId) {
return apiTestCaseIdMap.get(oldId);
}
public ApiScenarioImportDetail getApiScenario(String oldId) {
return apiScenarioIdMap.get(oldId);
}
public void putApiDefinitionId(String oldId, ApiDefinitionDetail newData) {
apiDefinitionIdMap.put(oldId, newData);
}
public void putApiTestCase(String oldId, ApiTestCaseDTO newData) {
apiTestCaseIdMap.put(oldId, newData);
}
public void putApiScenarioId(String oldId, ApiScenarioImportDetail newData) {
apiScenarioIdMap.put(oldId, newData);
}
}

View File

@ -405,7 +405,7 @@ public class ApiDefinitionImportService {
sqlSession.flushStatements(); sqlSession.flushStatements();
} }
private String getMsTestElementStr(Object request) { public String getMsTestElementStr(Object request) {
String requestStr = JSON.toJSONString(request); String requestStr = JSON.toJSONString(request);
AbstractMsTestElement msTestElement = ApiDataUtils.parseObject(requestStr, AbstractMsTestElement.class); AbstractMsTestElement msTestElement = ApiDataUtils.parseObject(requestStr, AbstractMsTestElement.class);
// 手动校验参数 // 手动校验参数

View File

@ -1823,7 +1823,7 @@ public class ApiScenarioService extends MoveNodeService {
steps.forEach(dto -> { steps.forEach(dto -> {
ApiScenarioStepDTO returnDTO = new ApiScenarioStepDTO(); ApiScenarioStepDTO returnDTO = new ApiScenarioStepDTO();
BeanUtils.copyBean(returnDTO, dto); BeanUtils.copyBean(returnDTO, dto);
if (!StringUtils.equalsIgnoreCase(parentId, dto.getId())) { if (StringUtils.isNotBlank(parentId) && !StringUtils.equalsIgnoreCase(parentId, dto.getId())) {
returnDTO.setParentId(parentId); returnDTO.setParentId(parentId);
} }
if (returnDTO.getConfig() != null && StringUtils.isNotBlank(returnDTO.getConfig().toString())) { if (returnDTO.getConfig() != null && StringUtils.isNotBlank(returnDTO.getConfig().toString())) {

View File

@ -1,13 +1,25 @@
package io.metersphere.api.utils; package io.metersphere.api.utils;
import io.metersphere.api.constants.ApiConstants;
import io.metersphere.api.dto.converter.ApiDefinitionDetail;
import io.metersphere.api.dto.converter.ApiScenarioPreImportAnalysisResult;
import io.metersphere.project.domain.Project; import io.metersphere.project.domain.Project;
import io.metersphere.sdk.constants.HttpMethodConstants; import io.metersphere.sdk.constants.HttpMethodConstants;
import io.metersphere.sdk.util.JSON; import io.metersphere.sdk.util.JSON;
import io.metersphere.system.log.dto.LogDTO; import io.metersphere.system.log.dto.LogDTO;
import io.metersphere.system.uid.IDGenerator;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class ApiScenarioImportUtils { public class ApiScenarioImportUtils {
public static LogDTO genImportLog(Project project, String dataId, String dataName, Object importData, String module, String operator, String operationType) { public static LogDTO genImportLog(Project project, String dataId, String dataName, Object targetList, String module, String operator, String operationType) {
LogDTO dto = new LogDTO( LogDTO dto = new LogDTO(
project.getId(), project.getId(),
project.getOrganizationId(), project.getOrganizationId(),
@ -19,7 +31,59 @@ public class ApiScenarioImportUtils {
dto.setHistory(true); dto.setHistory(true);
dto.setPath("/api/scenario/import"); dto.setPath("/api/scenario/import");
dto.setMethod(HttpMethodConstants.POST.name()); dto.setMethod(HttpMethodConstants.POST.name());
dto.setOriginalValue(JSON.toJSONBytes(importData)); dto.setOriginalValue(JSON.toJSONBytes(targetList));
return dto; return dto;
} }
public static Map<String, ApiDefinitionDetail> getApiIdInTargetList(List<ApiDefinitionDetail> compareList, List<ApiDefinitionDetail> targetList, String protocol, String projectId, ApiScenarioPreImportAnalysisResult analysisResult) {
if (CollectionUtils.isEmpty(compareList)) {
return new HashMap<>();
}
if (targetList == null) {
targetList = new ArrayList<>();
}
// API类型通过 Method & Path 组合判断接口是否存在
Map<String, ApiDefinitionDetail> targetApiIdMap = null;
if (StringUtils.equalsIgnoreCase(protocol, ApiConstants.HTTP_PROTOCOL)) {
targetApiIdMap = targetList.stream().collect(Collectors.toMap(t -> t.getMethod() + t.getPath(), t -> t, (oldValue, newValue) -> newValue));
} else {
targetApiIdMap = targetList.stream().collect(Collectors.toMap(t -> t.getModulePath() + t.getName(), t -> t, (oldValue, newValue) -> newValue));
}
Map<String, ApiDefinitionDetail> prepareInsertApi = new HashMap<>();
Map<String, ApiDefinitionDetail> apiIdDic = new HashMap<>();
for (ApiDefinitionDetail compareApi : compareList) {
String compareKey = StringUtils.equalsIgnoreCase(protocol, ApiConstants.HTTP_PROTOCOL) ?
compareApi.getMethod() + compareApi.getPath() : compareApi.getModulePath() + compareApi.getName();
// 去除文件中相同类型的接口
if (targetApiIdMap.containsKey(compareKey)) {
apiIdDic.put(compareApi.getId(), targetApiIdMap.get(compareKey));
} else {
if (prepareInsertApi.containsKey(compareKey)) {
apiIdDic.put(compareApi.getId(), prepareInsertApi.get(compareKey));
} else {
String oldId = compareApi.getId();
compareApi.setProjectId(projectId);
compareApi.setId(IDGenerator.nextStr());
analysisResult.setApiDefinition(compareApi);
apiIdDic.put(oldId, compareApi);
prepareInsertApi.put(compareKey, compareApi);
}
}
}
return apiIdDic;
}
public static boolean isApiExistence(String protocol, String method, String path, String modulePath, String apiDefinitionName, List<ApiDefinitionDetail> existenceApiDefinitionList) {
Map<String, ApiDefinitionDetail> existenceMap = null;
if (StringUtils.equalsIgnoreCase(protocol, ApiConstants.HTTP_PROTOCOL)) {
existenceMap = existenceApiDefinitionList.stream().collect(Collectors.toMap(t -> t.getMethod() + t.getPath(), t -> t, (oldValue, newValue) -> newValue));
return existenceMap.containsKey(method + path);
} else {
existenceMap = existenceApiDefinitionList.stream().collect(Collectors.toMap(t -> t.getModulePath() + t.getName(), t -> t, (oldValue, newValue) -> newValue));
return existenceMap.containsKey(modulePath + apiDefinitionName);
}
}
} }

View File

@ -76,14 +76,21 @@ public class ApiScenarioControllerImportAndExportTests extends BaseTest {
ApiScenarioImportRequest request = new ApiScenarioImportRequest(); ApiScenarioImportRequest request = new ApiScenarioImportRequest();
request.setProjectId(project.getId()); request.setProjectId(project.getId());
request.setType("jmeter"); request.setType("jmeter");
String importType = "jmeter"; FileInputStream inputStream = new FileInputStream(new File(Objects.requireNonNull(this.getClass().getClassLoader().getResource("file/import-scenario/jmeter/simple.jmx")).getPath()));
String fileSuffix = "jmx"; MockMultipartFile file = new MockMultipartFile("file", "simple.jmx", MediaType.APPLICATION_OCTET_STREAM_VALUE, inputStream);
FileInputStream inputStream = new FileInputStream(new File(Objects.requireNonNull(this.getClass().getClassLoader().getResource("file/import-scenario/" + importType + "/simple." + fileSuffix)).getPath()));
MockMultipartFile file = new MockMultipartFile("file", "simple." + fileSuffix, MediaType.APPLICATION_OCTET_STREAM_VALUE, inputStream);
MultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<>(); MultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<>();
paramMap.add("request", JSON.toJSONString(request)); paramMap.add("request", JSON.toJSONString(request));
paramMap.add("file", file); paramMap.add("file", file);
this.requestMultipartWithOkAndReturn(URL_POST_IMPORT, paramMap); this.requestMultipartWithOkAndReturn(URL_POST_IMPORT, paramMap);
request.setType("metersphere");
inputStream = new FileInputStream(new File(Objects.requireNonNull(this.getClass().getClassLoader().getResource("file/import-scenario/metersphere/simple.ms")).getPath()));
file = new MockMultipartFile("file", "simple.ms", MediaType.APPLICATION_OCTET_STREAM_VALUE, inputStream);
paramMap = new LinkedMultiValueMap<>();
paramMap.add("request", JSON.toJSONString(request));
paramMap.add("file", file);
this.requestMultipartWithOkAndReturn(URL_POST_IMPORT, paramMap);
} }
@Resource @Resource
@ -132,26 +139,11 @@ public class ApiScenarioControllerImportAndExportTests extends BaseTest {
MetersphereApiScenarioExportResponse exportResponse = ApiDataUtils.parseObject(fileContent, MetersphereApiScenarioExportResponse.class); MetersphereApiScenarioExportResponse exportResponse = ApiDataUtils.parseObject(fileContent, MetersphereApiScenarioExportResponse.class);
Assertions.assertEquals(exportResponse.getExportScenarioList().size(), 3); Assertions.assertEquals(exportResponse.getExportScenarioList().size(), 8);
MsFileUtils.deleteDir("/tmp/api-scenario-export/"); MsFileUtils.deleteDir("/tmp/api-scenario-export/");
} }
@Test
@Order(3)
public void testImportMs() throws Exception {
ApiScenarioImportRequest request = new ApiScenarioImportRequest();
request.setProjectId(project.getId());
request.setType("metersphere");
String importType = "metersphere";
FileInputStream inputStream = new FileInputStream(new File(Objects.requireNonNull(this.getClass().getClassLoader().getResource("file/import-scenario/" + importType + "/all.ms")).getPath()));
MockMultipartFile file = new MockMultipartFile("file", "all.ms", MediaType.APPLICATION_OCTET_STREAM_VALUE, inputStream);
MultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<>();
paramMap.add("request", JSON.toJSONString(request));
paramMap.add("file", file);
this.requestMultipartWithOkAndReturn(URL_POST_IMPORT, paramMap);
}
private MvcResult download(String projectId, String fileId) throws Exception { private MvcResult download(String projectId, String fileId) throws Exception {
return mockMvc.perform(MockMvcRequestBuilders.get("/api/scenario/download/file/" + projectId + "/" + fileId) return mockMvc.perform(MockMvcRequestBuilders.get("/api/scenario/download/file/" + projectId + "/" + fileId)
.header(SessionConstants.HEADER_TOKEN, sessionId) .header(SessionConstants.HEADER_TOKEN, sessionId)

File diff suppressed because one or more lines are too long

View File

@ -25,6 +25,9 @@ public class BaseTreeNode {
@Schema(description = "父节点ID") @Schema(description = "父节点ID")
private String parentId; private String parentId;
@Schema(description = "项目ID")
private String projectId;
@Schema(description = "子节点") @Schema(description = "子节点")
private List<BaseTreeNode> children = new ArrayList<>(); private List<BaseTreeNode> children = new ArrayList<>();