This commit is contained in:
liqiang-fit2cloud 2023-01-05 10:13:51 +08:00
commit 5dc33c1121
56 changed files with 1131 additions and 448 deletions

View File

@ -1,5 +1,6 @@
package io.metersphere.api.dto.automation;
import io.metersphere.commons.vo.RunPlanScenarioVO;
import io.metersphere.dto.RunModeConfigDTO;
import lombok.Getter;
import lombok.Setter;
@ -14,8 +15,6 @@ public class RunScenarioRequest {
private String reportId;
private String environmentId;
private String projectId;
private String triggerMode;
@ -24,25 +23,14 @@ public class RunScenarioRequest {
private String runMode;
/**
* 测试情景和测试计划的关联ID
*/
private String planScenarioId;
private List<String> planCaseIds;
private List<String> ids;
private String reportUserID;
private Map<String, String> scenarioTestPlanIdMap;
private ApiScenarioRequest condition;
private RunModeConfigDTO config;
private boolean isTestPlanScheduleJob = false;
//生成测试报告当isTestPlanScheduleJob为true时使用
private String testPlanReportId;
@ -53,6 +41,10 @@ public class RunScenarioRequest {
private String serialReportId;
private Map<String, ApiScenarioReportResult> reportMap;
private String versionId;
private String testPlanId;
// 过程数据整个测试计划执行
private RunPlanScenarioVO processVO;
// 测试计划批量执行传入id
private List<String> planScenarioIds;
}

View File

@ -0,0 +1,17 @@
package io.metersphere.api.dto.definition;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Getter
@Setter
public class BatchDataCopyRequest {
private List<String> ids;
private boolean copyCase;
private boolean copyMock;
private String versionId;
private ApiDefinitionRequest condition;
}

View File

@ -13,7 +13,7 @@ import java.util.Map;
public class BatchRunDefinitionRequest {
private String id;
private List<String> planIds;
private List<String> planCaseIds;
private String triggerMode;
@ -27,6 +27,9 @@ public class BatchRunDefinitionRequest {
private String planReportId;
// 失败重跑
private boolean rerun;
private String testPlanId;
private Map<String, ApiDefinitionExecResultWithBLOBs> executeQueue;
}

View File

@ -6,6 +6,7 @@ import io.metersphere.api.dto.EnvironmentType;
import io.metersphere.api.dto.RunModeConfigWithEnvironmentDTO;
import io.metersphere.api.dto.definition.ApiTestCaseRequest;
import io.metersphere.api.dto.definition.BatchRunDefinitionRequest;
import io.metersphere.api.dto.plan.TestPlanApiCaseInfoDTO;
import io.metersphere.api.dto.scenario.DatabaseConfig;
import io.metersphere.api.dto.scenario.environment.EnvironmentConfig;
import io.metersphere.api.exec.queue.DBTestQueue;
@ -14,7 +15,7 @@ import io.metersphere.base.domain.*;
import io.metersphere.base.mapper.ApiDefinitionExecResultMapper;
import io.metersphere.base.mapper.ApiTestCaseMapper;
import io.metersphere.base.mapper.ext.ExtApiTestCaseMapper;
import io.metersphere.base.mapper.plan.TestPlanApiCaseMapper;
import io.metersphere.base.mapper.plan.ext.ExtTestPlanApiCaseMapper;
import io.metersphere.commons.constants.*;
import io.metersphere.commons.enums.ApiReportStatus;
import io.metersphere.commons.exception.MSException;
@ -62,11 +63,11 @@ public class ApiCaseExecuteService {
@Resource
private ApiDefinitionExecResultMapper apiDefinitionExecResultMapper;
@Resource
private TestPlanApiCaseMapper testPlanApiCaseMapper;
@Resource
private JMeterService jMeterService;
@Resource
private BaseEnvironmentService baseEnvironmentService;
@Resource
private ExtTestPlanApiCaseMapper extTestPlanApiCaseMapper;
/**
* 测试计划case执行
@ -76,33 +77,34 @@ public class ApiCaseExecuteService {
*/
public List<MsExecResponseDTO> run(BatchRunDefinitionRequest request) {
List<MsExecResponseDTO> responseDTOS = new LinkedList<>();
if (CollectionUtils.isEmpty(request.getPlanIds())) {
return responseDTOS;
}
if (request.getConfig() == null) {
request.setConfig(new RunModeConfigDTO());
}
if (StringUtils.equals(EnvironmentType.GROUP.toString(), request.getConfig().getEnvironmentType()) && StringUtils.isNotEmpty(request.getConfig().getEnvironmentGroupId())) {
if (StringUtils.equals(EnvironmentType.GROUP.name(), request.getConfig().getEnvironmentType())
&& StringUtils.isNotEmpty(request.getConfig().getEnvironmentGroupId())) {
request.getConfig().setEnvMap(environmentGroupProjectService.getEnvMap(request.getConfig().getEnvironmentGroupId()));
}
LoggerUtil.info("开始查询测试计划用例", request.getPlanIds().size());
List<TestPlanApiCase> planApiCases = this.selectByPlanApiCaseIds(request.getPlanIds());
List<TestPlanApiCaseInfoDTO> planApiCases;
if (CollectionUtils.isNotEmpty(request.getPlanCaseIds())) {
planApiCases = extTestPlanApiCaseMapper.selectByPlanCaseIds(request.getPlanCaseIds());
} else {
planApiCases = extTestPlanApiCaseMapper.selectLegalDataByTestPlanId(request.getTestPlanId());
}
if (CollectionUtils.isEmpty(planApiCases)) {
return responseDTOS;
}
LoggerUtil.info("查询到测试计划用例:" + planApiCases.size(), request.getPlanReportId());
if (StringUtils.isEmpty(request.getTriggerMode())) {
request.setTriggerMode(ApiRunMode.API_PLAN.name());
}
LoggerUtil.info("查询到测试计划用例 " + planApiCases.size());
Map<String, ApiDefinitionExecResultWithBLOBs> executeQueue = request.isRerun() ? request.getExecuteQueue() : new LinkedHashMap<>();
String status = StringUtils.equals(request.getConfig().getMode(), RunModeConstants.SERIAL.toString())
? ApiReportStatus.PENDING.name() : ApiReportStatus.RUNNING.name();
// 查出用例
List<String> apiCaseIds = planApiCases.stream().map(TestPlanApiCase::getApiCaseId).collect(Collectors.toList());
List<String> apiCaseIds = planApiCases.stream().map(TestPlanApiCaseInfoDTO::getApiCaseId).collect(Collectors.toList());
ApiTestCaseExample caseExample = new ApiTestCaseExample();
caseExample.createCriteria().andIdIn(apiCaseIds);
List<ApiTestCase> apiTestCases = apiTestCaseMapper.selectByExample(caseExample);
@ -113,7 +115,7 @@ public class ApiCaseExecuteService {
resourcePoolId = request.getConfig().getResourcePoolId();
}
if (!request.isRerun()) {
for (TestPlanApiCase testPlanApiCase : planApiCases) {
for (TestPlanApiCaseInfoDTO testPlanApiCase : planApiCases) {
//处理环境配置为空时的情况
RunModeConfigDTO runModeConfigDTO = new RunModeConfigDTO();
BeanUtils.copyBean(runModeConfigDTO, request.getConfig());
@ -134,7 +136,7 @@ public class ApiCaseExecuteService {
apiCaseResultService.batchSave(executeQueue);
}
LoggerUtil.info("开始生成测试计划队列");
LoggerUtil.info("开始生成测试计划队列", request.getPlanReportId());
String reportType = request.getConfig().getReportType();
String poolId = request.getConfig().getResourcePoolId();
String runMode = StringUtils.equals(request.getTriggerMode(), TriggerMode.MANUAL.name()) ? ApiRunMode.API_PLAN.name() : ApiRunMode.SCHEDULE_API_PLAN.name();
@ -155,16 +157,6 @@ public class ApiCaseExecuteService {
return responseDTOS;
}
public List<TestPlanApiCase> selectByPlanApiCaseIds(List<String> planApiCaseIds) {
if (CollectionUtils.isEmpty(planApiCaseIds)) {
return new ArrayList<>();
}
TestPlanApiCaseExample example = new TestPlanApiCaseExample();
example.createCriteria().andIdIn(planApiCaseIds);
example.setOrderByClause("`order` DESC");
return testPlanApiCaseMapper.selectByExample(example);
}
public Map<String, List<String>> getRequestEnv(List<ApiTestCaseWithBLOBs> caseList) {
Map<String, List<String>> projectEnvMap = new HashMap<>();
if (CollectionUtils.isEmpty(caseList)) {
@ -247,7 +239,8 @@ public class ApiCaseExecuteService {
}
jMeterService.verifyPool(request.getProjectId(), request.getConfig());
if (StringUtils.equals(EnvironmentType.GROUP.toString(), request.getConfig().getEnvironmentType()) && StringUtils.isNotEmpty(request.getConfig().getEnvironmentGroupId())) {
if (StringUtils.equals(EnvironmentType.GROUP.name(), request.getConfig().getEnvironmentType())
&& StringUtils.isNotEmpty(request.getConfig().getEnvironmentGroupId())) {
request.getConfig().setEnvMap(environmentGroupProjectService.getEnvMap(request.getConfig().getEnvironmentGroupId()));
}

View File

@ -9,6 +9,7 @@ import io.metersphere.api.dto.definition.request.ElementUtil;
import io.metersphere.api.dto.definition.request.MsScenario;
import io.metersphere.api.dto.definition.request.ParameterConfig;
import io.metersphere.api.dto.definition.request.sampler.MsHTTPSamplerProxy;
import io.metersphere.api.dto.plan.TestPlanApiScenarioInfoDTO;
import io.metersphere.api.dto.scenario.environment.EnvironmentConfig;
import io.metersphere.api.dto.scenario.environment.item.MsScenarioEnv;
import io.metersphere.base.domain.*;
@ -254,7 +255,7 @@ public class ApiScenarioEnvService {
return isEnv;
}
public boolean verifyPlanScenarioEnv(ApiScenarioWithBLOBs apiScenarioWithBLOBs, TestPlanApiScenario testPlanApiScenarios) {
public boolean verifyPlanScenarioEnv(ApiScenarioWithBLOBs apiScenarioWithBLOBs, TestPlanApiScenarioInfoDTO testPlanApiScenarios) {
if (apiScenarioWithBLOBs != null) {
String definition = apiScenarioWithBLOBs.getScenarioDefinition();
MsScenarioEnv scenario = JSON.parseObject(definition, MsScenarioEnv.class);
@ -285,7 +286,7 @@ public class ApiScenarioEnvService {
*/
public Map<String, List<String>> checkEnv(RunScenarioRequest request, List<ApiScenarioWithBLOBs> apiScenarios) {
Map<String, List<String>> projectEnvMap = new HashMap<>();
if (StringUtils.equals(request.getRequestOriginator(), "TEST_PLAN")) {
if (StringUtils.equals(request.getRequestOriginator(), CommonConstants.TEST_PLAN)) {
this.checkPlanScenarioEnv(request);
} else if (StringUtils.isNotBlank(request.getRunMode()) && StringUtils.equalsAny(request.getRunMode(), ApiRunMode.SCENARIO.name(), ApiRunMode.SCENARIO_PLAN.name(), ApiRunMode.JENKINS_SCENARIO_PLAN.name())) {
StringBuilder builder = new StringBuilder();
@ -301,7 +302,7 @@ public class ApiScenarioEnvService {
}
}
if (builder.length() > 0) {
MSException.throwException("场景:" + builder.toString() + "运行环境未配置,请检查!");
MSException.throwException("场景:" + builder + "运行环境未配置,请检查!");
}
} else if (StringUtils.equals(request.getRunMode(), ApiRunMode.SCHEDULE_SCENARIO.name())) {
for (ApiScenarioWithBLOBs apiScenarioWithBLOBs : apiScenarios) {
@ -317,24 +318,23 @@ public class ApiScenarioEnvService {
public void checkPlanScenarioEnv(RunScenarioRequest request) {
StringBuilder builder = new StringBuilder();
List<String> planCaseIds = request.getPlanCaseIds();
if (CollectionUtils.isNotEmpty(planCaseIds)) {
TestPlanApiScenarioExample example = new TestPlanApiScenarioExample();
example.createCriteria().andIdIn(planCaseIds);
List<TestPlanApiScenario> testPlanApiScenarios = testPlanApiScenarioMapper.selectByExampleWithBLOBs(example);
for (TestPlanApiScenario testPlanApiScenario : testPlanApiScenarios) {
if (request.getProcessVO() != null &&
MapUtils.isNotEmpty(request.getProcessVO().getTestPlanScenarioMap())
&& MapUtils.isNotEmpty(request.getProcessVO().getTestPlanScenarioMap())) {
for (String key : request.getProcessVO().getTestPlanScenarioMap().keySet()) {
try {
ApiScenarioWithBLOBs apiScenarioWithBLOBs = apiScenarioMapper.selectByPrimaryKey(testPlanApiScenario.getApiScenarioId());
boolean haveEnv = this.verifyPlanScenarioEnv(apiScenarioWithBLOBs, testPlanApiScenario);
TestPlanApiScenarioInfoDTO dto = request.getProcessVO().getTestPlanScenarioMap().get(key);
ApiScenarioWithBLOBs apiScenarioWithBLOBs = request.getProcessVO().getScenarioMap().get(dto.getApiScenarioId());
boolean haveEnv = this.verifyPlanScenarioEnv(apiScenarioWithBLOBs, dto);
if (!haveEnv) {
builder.append(apiScenarioWithBLOBs.getName()).append("; ");
}
} catch (Exception e) {
MSException.throwException("场景:" + builder.toString() + "运行环境未配置,请检查!");
MSException.throwException("场景:" + builder + "运行环境未配置,请检查!");
}
}
if (builder.length() > 0) {
MSException.throwException("场景:" + builder.toString() + "运行环境未配置,请检查!");
MSException.throwException("场景:" + builder + "运行环境未配置,请检查!");
}
}
}
@ -522,7 +522,8 @@ public class ApiScenarioEnvService {
JSONObject jsonObject = JSONUtil.parseObject(envConfig);
if (jsonObject.has("executionEnvironmentMap")) {
RunModeConfigWithEnvironmentDTO configWithEnvironment = JSON.parseObject(envConfig, RunModeConfigWithEnvironmentDTO.class);
if (StringUtils.equals("GROUP", configWithEnvironment.getEnvironmentType()) && StringUtils.isNotEmpty(configWithEnvironment.getEnvironmentGroupId())) {
if (StringUtils.equals(EnvironmentType.GROUP.name(), configWithEnvironment.getEnvironmentType())
&& StringUtils.isNotEmpty(configWithEnvironment.getEnvironmentGroupId())) {
groupId = configWithEnvironment.getEnvironmentGroupId();
}
if (MapUtils.isNotEmpty(configWithEnvironment.getExecutionEnvironmentMap())) {
@ -532,7 +533,8 @@ public class ApiScenarioEnvService {
}
} else {
RunModeConfigDTO config = JSON.parseObject(envConfig, RunModeConfigDTO.class);
if (StringUtils.equals("GROUP", config.getEnvironmentType()) && StringUtils.isNotEmpty(config.getEnvironmentGroupId())) {
if (StringUtils.equals(EnvironmentType.GROUP.name(), config.getEnvironmentType())
&& StringUtils.isNotEmpty(config.getEnvironmentGroupId())) {
groupId = config.getEnvironmentGroupId();
}
envMapByRunConfig = config.getEnvMap();
@ -576,4 +578,29 @@ public class ApiScenarioEnvService {
return project.getName();
}
}
public void setScenarioEnv(RunScenarioRequest request, Map<String, String> configEnvMap) {
if (StringUtils.equals(request.getConfig().getEnvironmentType(), EnvironmentType.JSON.toString())
&& MapUtils.isNotEmpty(request.getConfig().getEnvMap())) {
configEnvMap.putAll(request.getConfig().getEnvMap());
} else if (StringUtils.equals(request.getConfig().getEnvironmentType(), EnvironmentType.GROUP.toString())
&& StringUtils.isNotBlank(request.getConfig().getEnvironmentGroupId())) {
configEnvMap.putAll(environmentGroupProjectService.getEnvMap(request.getConfig().getEnvironmentGroupId()));
}
}
public Map<String, String> getPlanScenarioEnv(TestPlanApiScenarioInfoDTO planApiScenario, Map<String, String> configEnvMap) {
Map<String, String> planEnvMap = new LinkedHashMap<>();
if (StringUtils.equals(planApiScenario.getEnvironmentType(), EnvironmentType.JSON.toString())
&& StringUtils.isNotBlank(planApiScenario.getEnvironment())) {
planEnvMap.putAll(JSON.parseObject(planApiScenario.getEnvironment(), Map.class));
} else if (StringUtils.equals(planApiScenario.getEnvironmentType(), EnvironmentType.GROUP.toString())
&& StringUtils.isNotBlank(planApiScenario.getEnvironmentGroupId())) {
planEnvMap.putAll(environmentGroupProjectService.getEnvMap(planApiScenario.getEnvironmentGroupId()));
}
if (MapUtils.isNotEmpty(configEnvMap)) {
planEnvMap.putAll(configEnvMap);
}
return planEnvMap;
}
}

View File

@ -8,15 +8,18 @@ import io.metersphere.api.dto.automation.ExecuteType;
import io.metersphere.api.dto.automation.RunScenarioRequest;
import io.metersphere.api.dto.definition.RunDefinitionRequest;
import io.metersphere.api.dto.definition.request.ParameterConfig;
import io.metersphere.api.dto.plan.TestPlanApiScenarioInfoDTO;
import io.metersphere.api.exec.api.ApiCaseExecuteService;
import io.metersphere.api.exec.queue.DBTestQueue;
import io.metersphere.api.jmeter.JMeterService;
import io.metersphere.api.jmeter.NewDriverManager;
import io.metersphere.base.domain.*;
import io.metersphere.base.domain.ApiScenario;
import io.metersphere.base.domain.ApiScenarioExample;
import io.metersphere.base.domain.ApiScenarioReportWithBLOBs;
import io.metersphere.base.domain.ApiScenarioWithBLOBs;
import io.metersphere.base.mapper.ApiScenarioMapper;
import io.metersphere.base.mapper.ApiScenarioReportMapper;
import io.metersphere.base.mapper.ext.ExtApiScenarioMapper;
import io.metersphere.base.mapper.plan.TestPlanApiScenarioMapper;
import io.metersphere.base.mapper.plan.ext.ExtTestPlanScenarioCaseMapper;
import io.metersphere.commons.constants.ApiRunMode;
import io.metersphere.commons.constants.ExtendedParameter;
@ -24,6 +27,7 @@ import io.metersphere.commons.constants.ReportTriggerMode;
import io.metersphere.commons.enums.ApiReportStatus;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.*;
import io.metersphere.commons.vo.RunPlanScenarioVO;
import io.metersphere.constants.RunModeConstants;
import io.metersphere.dto.BaseSystemConfigDTO;
import io.metersphere.dto.JmeterRunRequestDTO;
@ -85,8 +89,6 @@ public class ApiScenarioExecuteService {
@Resource
protected JMeterService jMeterService;
@Resource
protected TestPlanApiScenarioMapper testPlanApiScenarioMapper;
@Resource
private ExtTestPlanScenarioCaseMapper extTestPlanScenarioCaseMapper;
@Resource
private SystemParameterService systemParameterService;
@ -102,7 +104,8 @@ public class ApiScenarioExecuteService {
request.setConfig(new RunModeConfigDTO());
}
if (StringUtils.equals("GROUP", request.getConfig().getEnvironmentType()) && StringUtils.isNotEmpty(request.getConfig().getEnvironmentGroupId())) {
if (StringUtils.equals(EnvironmentType.GROUP.name(), request.getConfig().getEnvironmentType())
&& StringUtils.isNotEmpty(request.getConfig().getEnvironmentGroupId())) {
request.getConfig().setEnvMap(environmentGroupProjectService.getEnvMap(request.getConfig().getEnvironmentGroupId()));
}
@ -131,9 +134,10 @@ public class ApiScenarioExecuteService {
Map<String, RunModeDataDTO> executeQueue = new LinkedHashMap<>();
LoggerUtil.info("Scenario run-执行脚本装载-初始化执行队列");
if (StringUtils.equalsAny(request.getRunMode(), ApiRunMode.SCENARIO_PLAN.name(), ApiRunMode.SCHEDULE_SCENARIO_PLAN.name(), ApiRunMode.JENKINS_SCENARIO_PLAN.name())) {
if (StringUtils.equalsAny(request.getRunMode(), ApiRunMode.SCENARIO_PLAN.name(),
ApiRunMode.SCHEDULE_SCENARIO_PLAN.name(), ApiRunMode.JENKINS_SCENARIO_PLAN.name())) {
//测试计划执行
assemblyPlanScenario(apiScenarios, request, executeQueue);
assemblyPlanScenario(request, executeQueue);
} else {
// 按照场景执行
assemblyScenario(apiScenarios, request, executeQueue);
@ -141,47 +145,19 @@ public class ApiScenarioExecuteService {
LoggerUtil.info("Scenario run-执行脚本装载-初始化执行队列完成:" + executeQueue.size());
List<MsExecResponseDTO> responseDTOS = new LinkedList<>();
if (executeQueue.isEmpty()) {
if (MapUtils.isEmpty(executeQueue)) {
return responseDTOS;
}
// 集合报告处理
if (GenerateHashTreeUtil.isSetReport(request.getConfig())) {
// 失败重跑更新报告状态
if (request.isRerun()) {
ApiScenarioReportWithBLOBs report = new ApiScenarioReportWithBLOBs();
report.setId(serialReportId);
report.setStatus(ApiReportStatus.RERUNNING.name());
apiScenarioReportMapper.updateByPrimaryKeySelective(report);
} else {
LoggerUtil.info("Scenario run-执行脚本装载-初始化集成报告:" + serialReportId);
request.getConfig().setReportId(UUID.randomUUID().toString());
List<String> scenarioIds = new ArrayList<>();
String reportScenarioIds = generateScenarioIds(scenarioIds);
if (request.getConfig() == null) {
request.setConfig(new RunModeConfigWithEnvironmentDTO());
}
if (MapUtils.isEmpty(request.getConfig().getEnvMap())) {
RunModeConfigWithEnvironmentDTO runModeConfig = new RunModeConfigWithEnvironmentDTO();
BeanUtils.copyBean(runModeConfig, request.getConfig());
Map<String, List<String>> projectEnvMap = apiScenarioEnvService.selectApiScenarioEnv(apiScenarios);
apiCaseExecuteService.setExecutionEnvironment(runModeConfig, projectEnvMap);
request.setConfig(runModeConfig);
}
// 生成集合报告
String names = apiScenarios.stream().map(ApiScenario::getName).collect(Collectors.joining(","));
ApiScenarioReportResult report = apiScenarioReportService.getApiScenarioReportResult(request, serialReportId, names, reportScenarioIds);
report.setVersionId(apiScenarios.get(0).getVersionId());
apiScenarioReportMapper.insert(report);
responseDTOS.add(new MsExecResponseDTO(JSON.toJSONString(scenarioIds), serialReportId, request.getRunMode()));
apiScenarioReportStructureService.save(apiScenarios, serialReportId, request.getConfig().getReportType());
}
this.setReport(request, serialReportId, apiScenarios, responseDTOS);
}
String reportType = request.getConfig().getReportType();
String planReportId = StringUtils.isNotEmpty(request.getTestPlanReportId()) ? request.getTestPlanReportId() : serialReportId;
// 生成执行队列
DBTestQueue executionQueue = apiExecutionQueueService.add(executeQueue, request.getConfig().getResourcePoolId(), ApiRunMode.SCENARIO.name(), planReportId, reportType, request.getRunMode(), request.getConfig());
DBTestQueue executionQueue = apiExecutionQueueService.add(
executeQueue, request.getConfig().getResourcePoolId(), ApiRunMode.SCENARIO.name(), planReportId, reportType, request.getRunMode(), request.getConfig());
// 预生成报告
if (!request.isRerun() && !GenerateHashTreeUtil.isSetReport(request.getConfig())) {
@ -215,7 +191,25 @@ public class ApiScenarioExecuteService {
}
public List<ApiScenarioWithBLOBs> get(RunScenarioRequest request) {
ServiceUtils.getSelectAllIds(request, request.getCondition(), (query) -> extApiScenarioMapper.selectIdsByQuery(query));
RunPlanScenarioVO vo = new RunPlanScenarioVO();
if (StringUtils.equalsAny(request.getRunMode(),
ApiRunMode.SCENARIO_PLAN.name(), ApiRunMode.SCHEDULE_SCENARIO_PLAN.name(), ApiRunMode.JENKINS_SCENARIO_PLAN.name())) {
// 测试计划执行数据查询
List<TestPlanApiScenarioInfoDTO> testPlanApiScenarioList;
if (CollectionUtils.isNotEmpty(request.getPlanScenarioIds())) {
testPlanApiScenarioList = extTestPlanScenarioCaseMapper.selectByPlanIds(request.getPlanScenarioIds());
} else {
testPlanApiScenarioList = extTestPlanScenarioCaseMapper.selectLegalDataByTestPlanId(request.getTestPlanId());
}
if (CollectionUtils.isNotEmpty(testPlanApiScenarioList)) {
List<String> ids = testPlanApiScenarioList.stream().map(TestPlanApiScenarioInfoDTO::getApiScenarioId).collect(Collectors.toList());
request.setIds(ids);
vo.setTestPlanScenarioMap(testPlanApiScenarioList.stream()
.collect(Collectors.toMap(TestPlanApiScenarioInfoDTO::getId, Function.identity(), (t1, t2) -> t1)));
}
} else {
ServiceUtils.getSelectAllIds(request, request.getCondition(), (query) -> extApiScenarioMapper.selectIdsByQuery(query));
}
List<String> ids = request.getIds();
ApiScenarioExample example = new ApiScenarioExample();
example.createCriteria().andIdIn(ids);
@ -226,9 +220,44 @@ public class ApiScenarioExecuteService {
sortById(ids, apiScenarios);
}
}
vo.setScenarioMap(apiScenarios.stream().collect(Collectors.toMap(ApiScenarioWithBLOBs::getId, Function.identity(), (t1, t2) -> t1)));
request.setProcessVO(vo);
return apiScenarios;
}
private void setReport(RunScenarioRequest request, String serialReportId, List<ApiScenarioWithBLOBs> apiScenarios, List<MsExecResponseDTO> responseDTOS) {
// 失败重跑更新报告状态
if (request.isRerun()) {
ApiScenarioReportWithBLOBs report = new ApiScenarioReportWithBLOBs();
report.setId(serialReportId);
report.setStatus(ApiReportStatus.RERUNNING.name());
apiScenarioReportMapper.updateByPrimaryKeySelective(report);
} else {
LoggerUtil.info("Scenario run-执行脚本装载-初始化集成报告:" + serialReportId);
request.getConfig().setReportId(UUID.randomUUID().toString());
List<String> scenarioIds = new ArrayList<>();
String reportScenarioIds = generateScenarioIds(scenarioIds);
if (request.getConfig() == null) {
request.setConfig(new RunModeConfigWithEnvironmentDTO());
}
if (MapUtils.isEmpty(request.getConfig().getEnvMap())) {
RunModeConfigWithEnvironmentDTO runModeConfig = new RunModeConfigWithEnvironmentDTO();
BeanUtils.copyBean(runModeConfig, request.getConfig());
Map<String, List<String>> projectEnvMap = apiScenarioEnvService.selectApiScenarioEnv(apiScenarios);
apiCaseExecuteService.setExecutionEnvironment(runModeConfig, projectEnvMap);
request.setConfig(runModeConfig);
}
// 生成集合报告
String names = apiScenarios.stream().map(ApiScenario::getName).collect(Collectors.joining(","));
ApiScenarioReportResult report = apiScenarioReportService.getApiScenarioReportResult(request, serialReportId, names, reportScenarioIds);
report.setVersionId(apiScenarios.get(0).getVersionId());
apiScenarioReportMapper.insert(report);
responseDTOS.add(new MsExecResponseDTO(JSON.toJSONString(scenarioIds), serialReportId, request.getRunMode()));
apiScenarioReportStructureService.save(apiScenarios, serialReportId, request.getConfig().getReportType());
}
}
protected void sortById(List<String> ids, List apiScenarios) {
FixedOrderComparator<String> fixedOrderComparator = new FixedOrderComparator<String>(ids);
fixedOrderComparator.setUnknownObjectBehavior(FixedOrderComparator.UnknownObjectBehavior.BEFORE);
@ -236,30 +265,20 @@ public class ApiScenarioExecuteService {
Collections.sort(apiScenarios, beanComparator);
}
/**
* 测试计划接口场景的预执行生成场景报告
*/
private void assemblyPlanScenario(List<ApiScenarioWithBLOBs> apiScenarios, RunScenarioRequest request, Map<String, RunModeDataDTO> executeQueue) {
private void assemblyPlanScenario(RunScenarioRequest request, Map<String, RunModeDataDTO> executeQueue) {
String reportId = request.getId();
Map<String, String> planScenarioIdMap = request.getScenarioTestPlanIdMap();
if (MapUtils.isEmpty(planScenarioIdMap)) {
return;
}
Map<String, ApiScenarioWithBLOBs> scenarioMap = apiScenarios.stream().collect(Collectors.toMap(ApiScenarioWithBLOBs::getId, Function.identity(), (t1, t2) -> t1));
Map<String, ApiScenarioWithBLOBs> scenarioMap = request.getProcessVO().getScenarioMap();
//检查环境组
Map<String, String> configEnvMap = new HashMap<>();
if (request.getConfig() != null) {
if (StringUtils.equals(request.getConfig().getEnvironmentType(), EnvironmentType.JSON.toString()) && MapUtils.isNotEmpty(request.getConfig().getEnvMap())) {
configEnvMap = request.getConfig().getEnvMap();
} else if (StringUtils.equals(request.getConfig().getEnvironmentType(), EnvironmentType.GROUP.toString()) && StringUtils.isNotBlank(request.getConfig().getEnvironmentGroupId())) {
configEnvMap = environmentGroupProjectService.getEnvMap(request.getConfig().getEnvironmentGroupId());
}
apiScenarioEnvService.setScenarioEnv(request, configEnvMap);
}
for (String testPlanScenarioId : planScenarioIdMap.keySet()) {
String scenarioId = planScenarioIdMap.get(testPlanScenarioId);
ApiScenarioWithBLOBs scenario = scenarioMap.get(scenarioId);
for (String testPlanScenarioId : request.getProcessVO().getTestPlanScenarioMap().keySet()) {
TestPlanApiScenarioInfoDTO planApiScenario = request.getProcessVO().getTestPlanScenarioMap().get(testPlanScenarioId);
ApiScenarioWithBLOBs scenario = scenarioMap.get(planApiScenario.getApiScenarioId());
if (scenario.getStepTotal() == null || scenario.getStepTotal() == 0) {
continue;
}
@ -267,39 +286,30 @@ public class ApiScenarioExecuteService {
request.setProjectId(scenario.getProjectId());
}
// 获取场景用例单独的执行环境
Map<String, String> planEnvMap = new HashMap<>();
TestPlanApiScenario planApiScenario = testPlanApiScenarioMapper.selectByPrimaryKey(testPlanScenarioId);
if (planApiScenario == null) {
continue;
}
// 环境处理
if (StringUtils.equals(planApiScenario.getEnvironmentType(), EnvironmentType.JSON.toString()) && StringUtils.isNotBlank(planApiScenario.getEnvironment())) {
planEnvMap = JSON.parseObject(planApiScenario.getEnvironment(), Map.class);
} else if (StringUtils.equals(planApiScenario.getEnvironmentType(), EnvironmentType.GROUP.toString()) && StringUtils.isNotBlank(planApiScenario.getEnvironmentGroupId())) {
planEnvMap = environmentGroupProjectService.getEnvMap(planApiScenario.getEnvironmentGroupId());
}
planEnvMap.putAll(configEnvMap);
Map<String, String> planEnvMap = apiScenarioEnvService.getPlanScenarioEnv(planApiScenario, configEnvMap);
if (StringUtils.isEmpty(request.getProjectId())) {
request.setProjectId(extTestPlanScenarioCaseMapper.getProjectIdById(testPlanScenarioId));
}
ApiScenarioReportResult report = apiScenarioReportService.initResult(reportId, testPlanScenarioId, scenario.getName(), request);
if (request.isRerun()) {
if (request.getReportMap().containsKey(scenarioId)) {
report = request.getReportMap().get(scenarioId);
if (request.getReportMap().containsKey(planApiScenario.getApiScenarioId())) {
report = request.getReportMap().get(planApiScenario.getApiScenarioId());
} else if (request.getReportMap().containsKey(testPlanScenarioId)) {
report = request.getReportMap().get(testPlanScenarioId);
}
if (report == null) {
return;
}
}
if (report == null) {
return;
}
report.setVersionId(scenario.getVersionId());
RunModeDataDTO runModeDataDTO = getRunModeDataDTO(testPlanScenarioId, report);
runModeDataDTO.setPlanEnvMap(planEnvMap);
runModeDataDTO.setScenario(scenario);
executeQueue.put(report.getId(), runModeDataDTO);
if (ObjectUtils.isNotEmpty(request.getConfig())) {
RunModeConfigWithEnvironmentDTO runModeConfig = new RunModeConfigWithEnvironmentDTO();
BeanUtils.copyBean(runModeConfig, request.getConfig());
@ -311,7 +321,6 @@ public class ApiScenarioExecuteService {
Map<String, String> envMap = runModeConfig.getEnvMap();
planEnvMap.forEach((k, v) -> {
if (envMap != null && envMap.get(k) != null) {
diffEnvMap.put(k, envMap.get(k));
}
});

View File

@ -1,74 +0,0 @@
package io.metersphere.api.parse.api.har.command;///**
// *
// * har - HAR file reader, writer and viewer
// * Copyright (c) 2014, Sandeep Gupta
// *
// * http://sangupta.com/projects/har
// *
// * Licensed under the Apache License, Version 2.0 (the "License");
// * you may not use this file except in compliance with the License.
// * You may obtain a copy of the License at
// *
// * http://www.apache.org/licenses/LICENSE-2.0
// *
// * Unless required by applicable law or agreed to in writing, software
// * distributed under the License is distributed on an "AS IS" BASIS,
// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// * See the License for the specific language governing permissions and
// * limitations under the License.
// *
// */
//
//package io.metersphere.api.dto.parse.api.har.command;
//
//import java.io.File;
//
//import com.sangupta.har.HarUtils;
//import com.sangupta.har.model.Har;
//import com.sangupta.har.model.HarEntry;
//import com.sangupta.har.model.HarPage;
//import com.sangupta.jerry.util.AssertUtils;
//
//import io.airlift.command.Arguments;
//import io.airlift.command.Command;
//
//@Command(name = "view", description = "View HAR file")
//public class ViewHar implements Runnable {
//
// @Arguments
// private String file;
//
// @Override
// public void run() {
// Har har = null;
//
// try {
// har = HarUtils.read(new File(this.file));
// } catch(Exception e) {
// System.out.println("Error reading HAR file: " + e.getMessage());
// return;
// }
//
// if(har.log == null || AssertUtils.isEmpty(har.log.pages)) {
// System.out.println("HAR file has no pages!");
// return;
// }
//
// // connect references
// HarUtils.connectReferences(har);
//
// // start displaying
// System.out.println("Number of pages viewed: " + har.log.pages.size());
// System.out.println();
//
// for(HarPage page : har.log.pages) {
// System.out.println(page);
//
// // output the calls for this page
// for(HarEntry entry : page.entries) {
// System.out.println("\t" + entry);
// }
// }
// }
//
//}

View File

@ -35,6 +35,8 @@ public interface ExtTestPlanApiCaseMapper {
List<TestPlanApiCaseInfoDTO> selectLegalDataByTestPlanId(String planId);
List<TestPlanApiCaseInfoDTO> selectByPlanCaseIds(List<String> planCaseIds);
List<Map> selectForPlanReport(String planId);
List<TestPlanFailureApiDTO> getFailureList(@Param("planId") String planId, @Param("status") String status);

View File

@ -544,4 +544,14 @@
#{v}
</foreach>
</select>
<select id="selectByPlanCaseIds" resultType="io.metersphere.api.dto.plan.TestPlanApiCaseInfoDTO">
SELECT a.project_id, t.*
FROM test_plan_api_case t
INNER JOIN api_test_case a ON t.api_case_id = a.id
WHERE t.id in
<foreach collection="planCaseIds" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</select>
</mapper>

View File

@ -1,12 +1,10 @@
package io.metersphere.base.mapper.plan.ext;
import io.metersphere.api.dto.plan.TestPlanApiScenarioInfoDTO;
import io.metersphere.api.dto.automation.ApiScenarioDTO;
import io.metersphere.api.dto.automation.TestPlanFailureScenarioDTO;
import io.metersphere.api.dto.automation.TestPlanScenarioRequest;
import io.metersphere.api.dto.plan.TestPlanApiScenarioInfoDTO;
import io.metersphere.base.domain.TestPlanApiScenario;
import io.metersphere.dto.PlanReportCaseDTO;
import org.apache.ibatis.annotations.Param;
@ -30,6 +28,8 @@ public interface ExtTestPlanScenarioCaseMapper {
List<TestPlanApiScenarioInfoDTO> selectLegalDataByTestPlanId(String planId);
List<TestPlanApiScenarioInfoDTO> selectByPlanIds(List<String> planScenarioIds);
List<PlanReportCaseDTO> selectForPlanReport(String planId);
List<TestPlanFailureScenarioDTO> getFailureList(@Param("planId") String planId, @Param("status") String status);

View File

@ -401,4 +401,20 @@
SELECT test_plan_id FROM test_plan_api_scenario WHERE id = #{0}
)
</select>
<select id="selectByPlanIds" resultType="io.metersphere.api.dto.plan.TestPlanApiScenarioInfoDTO">
SELECT tpas.id,
tpas.api_scenario_id,
tpas.environment,
tpas.environment_type,
tpas.environment_group_id,
apis.project_id
FROM test_plan_api_scenario tpas
INNER JOIN api_scenario apis ON tpas.api_scenario_id = apis.id
WHERE (apis.`status` IS NULL OR apis.`status` != 'Trash')
and tpas.id in
<foreach collection="planScenarioIds" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</select>
</mapper>

View File

@ -1,8 +1,8 @@
package io.metersphere.commons.constants;
public class CommonConstants {
public static final String TrashStatus = "Trash";
public static final String PROJECT_TEMPLATE = "PROJECT_TEMPLATE";
public static final String TRASH_STATUS = "Trash";
public static final String TEST_PLAN = "TEST_PLAN";
public static final String LOCAL_STATUS_KEY = "LOCAL_STATUS_KEY";
public static final String REPORT_STATUS = "REPORT_STATUS";
public static final String TRIGGER_MODE = "trigger_mode";

View File

@ -2,9 +2,9 @@ package io.metersphere.commons.utils;
import io.metersphere.api.dto.definition.BatchRunDefinitionRequest;
import io.metersphere.api.dto.plan.TestPlanApiCaseInfoDTO;
import io.metersphere.base.domain.ApiDefinitionExecResultWithBLOBs;
import io.metersphere.base.domain.ApiTestCase;
import io.metersphere.base.domain.TestPlanApiCase;
import io.metersphere.commons.constants.ApiRunMode;
import io.metersphere.commons.constants.ReportTypeConstants;
import io.metersphere.commons.constants.TriggerMode;
@ -45,7 +45,7 @@ public class ApiDefinitionExecResultUtil {
public static ApiDefinitionExecResultWithBLOBs addResult(
BatchRunDefinitionRequest request,
RunModeConfigDTO runModeConfigDTO,
TestPlanApiCase key,
TestPlanApiCaseInfoDTO key,
String status,
ApiTestCase testCase,
String poolId) {

View File

@ -0,0 +1,22 @@
package io.metersphere.commons.vo;
import io.metersphere.api.dto.plan.TestPlanApiScenarioInfoDTO;
import io.metersphere.base.domain.ApiScenarioWithBLOBs;
import lombok.Getter;
import lombok.Setter;
import java.util.LinkedHashMap;
import java.util.Map;
@Setter
@Getter
public class RunPlanScenarioVO {
private Map<String, TestPlanApiScenarioInfoDTO> testPlanScenarioMap;
private Map<String, ApiScenarioWithBLOBs> scenarioMap;
public RunPlanScenarioVO() {
this.testPlanScenarioMap = new LinkedHashMap<>();
this.scenarioMap = new LinkedHashMap<>();
}
}

View File

@ -139,6 +139,13 @@ public class ApiDefinitionController {
apiDefinitionService.deleteByParams(request);
}
@PostMapping("/copy/by/version")
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_READ_EDIT_API)
@MsAuditLog(module = OperLogModule.API_DEFINITION, type = OperLogConstants.UPDATE, beforeEvent = "#msClass.getLogDetails(#request.ids)", title = "#request.name", content = "#msClass.getLogDetails(#request.ids)", msClass = ApiDefinitionService.class)
public void copyByVersion(@RequestBody BatchDataCopyRequest request) {
apiDefinitionService.copyCaseOrMockByVersion(request);
}
@PostMapping("/move-gc")
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_READ_DELETE_API)
@MsAuditLog(module = OperLogModule.API_DEFINITION, type = OperLogConstants.GC, beforeEvent = "#msClass.getLogDetails(#ids)", msClass = ApiDefinitionService.class)

View File

@ -190,11 +190,6 @@ public class TestPlanScenarioCaseController {
return testPlanScenarioCaseService.buildExecuteApiReport(request);
}
@PostMapping("/plan/report/schedule/info/{planId}")
public TestPlanApiReportInfoDTO genApiReportInfoForSchedule(@PathVariable("planId") String planId, @RequestBody RunModeConfigDTO runModeConfigDTO) {
return testPlanScenarioCaseService.genApiReportInfoForSchedule(planId, runModeConfigDTO);
}
@GetMapping("/is/executing/{planId}")
public Boolean isExecuting(@PathVariable("planId") String planId) {
return testPlanScenarioCaseService.isExecuting(planId);

View File

@ -69,7 +69,7 @@ public class ApiExecutionQueueService {
@Transactional(propagation = Propagation.REQUIRES_NEW)
public DBTestQueue add(Object runObj, String poolId, String type, String reportId, String reportType, String runMode, RunModeConfigDTO config) {
LoggerUtil.info("报告【" + reportId + "】开始生成执行链");
LoggerUtil.info("报告【" + type + "】开始生成执行链", reportId);
if (config.getEnvMap() == null) {
config.setEnvMap(new LinkedHashMap<>());
}
@ -99,7 +99,7 @@ public class ApiExecutionQueueService {
extApiExecutionQueueMapper.sqlInsert(queueDetails);
}
resQueue.setDetailMap(detailMap);
LoggerUtil.info("报告【" + reportId + "】生成执行链结束");
LoggerUtil.info("报告【" + type + "】生成执行链结束", reportId);
return resQueue;
}

View File

@ -87,6 +87,12 @@ public class MockConfigService {
return extMockExpectConfigMapper.selectByApiId(apiId);
}
public List<MockExpectConfig> selectSimpleMockExpectConfigByMockConfigId(String mockConfigId) {
MockExpectConfigExample example = new MockExpectConfigExample();
example.createCriteria().andMockConfigIdEqualTo(mockConfigId);
return mockExpectConfigMapper.selectByExample(example);
}
public List<MockConfigImportDTO> selectMockExpectConfigByApiIdIn(List<String> apiIds) {
if (CollectionUtils.isNotEmpty(apiIds)) {
List<MockConfigImportDTO> returnDTO = new ArrayList<>();
@ -263,15 +269,22 @@ public class MockConfigService {
return mockExpectConfigMapper.selectByPrimaryKey(model.getId());
}
private String getMockExpectId(String mockConfigId) {
List<String> savedExpectNumber = extMockExpectConfigMapper.selectExlectNumByMockConfigId(mockConfigId);
public String getMockExpectId(String mockConfigId) {
List<String> savedExpectNumber = this.selectExpectNumberByConfigId(mockConfigId);
String apiNum = extMockExpectConfigMapper.selectApiNumberByMockConfigId(mockConfigId);
return this.getMockExpectId(apiNum, savedExpectNumber);
}
public List<String> selectExpectNumberByConfigId(String mockConfigId) {
return extMockExpectConfigMapper.selectExlectNumByMockConfigId(mockConfigId);
}
public String getMockExpectId(String apiNum, List<String> savedExpectNumber) {
if (StringUtils.isEmpty(apiNum)) {
apiNum = StringUtils.EMPTY;
} else {
apiNum = apiNum + "_";
}
int index = 1;
for (String expectNum : savedExpectNumber) {
if (StringUtils.startsWith(expectNum, apiNum)) {
@ -1153,7 +1166,7 @@ public class MockConfigService {
}
private MockConfig selectMockConfigByApiId(String apiId) {
public MockConfig selectMockConfigByApiId(String apiId) {
MockConfigExample example = new MockConfigExample();
example.createCriteria().andApiIdEqualTo(apiId);
List<MockConfig> mockConfigList = this.mockConfigMapper.selectByExample(example);

View File

@ -50,6 +50,7 @@ import io.metersphere.service.ext.ExtApiScheduleService;
import io.metersphere.service.ext.ExtFileAssociationService;
import io.metersphere.service.plan.TestPlanApiCaseService;
import io.metersphere.service.scenario.ApiScenarioService;
import io.metersphere.utils.BatchProcessingUtil;
import io.metersphere.xpack.api.service.ApiCaseBatchSyncService;
import io.metersphere.xpack.api.service.ApiDefinitionSyncService;
import io.metersphere.xpack.quota.service.QuotaService;
@ -133,6 +134,9 @@ public class ApiDefinitionService {
@Lazy
@Resource
private ApiModuleService apiModuleService;
@Lazy
@Resource
private MockConfigService mockConfigService;
@Resource
private BaseEnvironmentService apiTestEnvironmentService;
@Lazy
@ -2143,4 +2147,217 @@ public class ApiDefinitionService {
allSourceIdCount = apiExecutionInfoService.countSourceIdByProjectIdIsNull();
}
}
public void copyCaseOrMockByVersion(BatchDataCopyRequest request) {
if (!request.isCopyCase() && !request.isCopyMock()) {
return;
}
if (request.getCondition() == null) {
return;
}
ServiceUtils.getSelectAllIds(request, request.getCondition(), (query) -> extApiDefinitionMapper.selectIds(query));
// ServiceUtils.getSelectAllIds(request, request.getCondition(), (query) -> extApiDefinitionMapper.selectIds(query));
if (StringUtils.isBlank(request.getVersionId()) || CollectionUtils.isEmpty(request.getCondition().getIds())) {
MSException.throwException(Translator.get("invalid_parameter"));
}
request.setIds(request.getCondition().getIds());
//函数是批量操作可能会出现数据太多导致的sql过长错误采用批量处理
BatchProcessingUtil.batchProcessingByDataCopy(request, this::batchCopyCaseOrMockByVersion);
}
public void batchCopyCaseOrMockByVersion(BatchDataCopyRequest request) {
if (sqlSessionFactory != null && CollectionUtils.isNotEmpty(request.getIds())) {
Map<String, ApiDefinition> refIdMap = new HashMap<>();
List<ApiDefinition> apiDefinitionList = this.selectByIds(request.getIds());
apiDefinitionList.forEach(item -> {
//过滤到自身的引用
if (!StringUtils.equals(item.getVersionId(), request.getVersionId())) {
refIdMap.put(item.getRefId(), item);
}
});
if (MapUtils.isNotEmpty(refIdMap)) {
ApiDefinitionExample apiExample = new ApiDefinitionExample();
apiExample.createCriteria().andStatusNotEqualTo(ApiTestDataStatus.TRASH.getValue()).andRefIdIn(new ArrayList<>(refIdMap.keySet())).andVersionIdEqualTo(request.getVersionId());
List<ApiDefinition> versionApiList = apiDefinitionMapper.selectByExample(apiExample);
Map<String, String> sourceApiIdRefIdMap = new HashMap<>();
versionApiList.forEach(item -> sourceApiIdRefIdMap.put(item.getId(), item.getRefId()));
if (MapUtils.isEmpty(sourceApiIdRefIdMap)) {
return;
}
SqlSession batchSqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
if (request.isCopyCase()) {
this.copyCaseByVersion(request.getIds(), sourceApiIdRefIdMap, refIdMap, batchSqlSession);
}
if (request.isCopyMock()) {
this.copyMockByVersion(sourceApiIdRefIdMap, refIdMap, batchSqlSession);
}
batchSqlSession.flushStatements();
if (sqlSessionFactory != null) {
SqlSessionUtils.closeSqlSession(batchSqlSession, sqlSessionFactory);
}
}
}
}
private void copyMockByVersion(Map<String, String> sourceApiIdRefIdMap, Map<String, ApiDefinition> refIdMap, SqlSession batchSqlSession) {
long timeStamp = System.currentTimeMillis();
MockConfigExample mockConfigExample = new MockConfigExample();
mockConfigExample.createCriteria().andApiIdIn(new ArrayList<>(sourceApiIdRefIdMap.keySet()));
List<MockConfig> mockConfigList = mockConfigMapper.selectByExample(mockConfigExample);
if (CollectionUtils.isNotEmpty(mockConfigList)) {
List<String> mockIdList = mockConfigList.stream().map(MockConfig::getId).collect(Collectors.toList());
MockExpectConfigExample mockExpectConfigExample = new MockExpectConfigExample();
mockExpectConfigExample.createCriteria().andMockConfigIdIn(mockIdList);
List<MockExpectConfigWithBLOBs> mockExpectConfigWithBLOBsList = mockExpectConfigMapper.selectByExampleWithBLOBs(mockExpectConfigExample);
Map<String, List<MockExpectConfigWithBLOBs>> mockConfigIdExpectMap = mockExpectConfigWithBLOBsList.stream().collect(Collectors.groupingBy(MockExpectConfigWithBLOBs::getMockConfigId));
List<MockConfig> saveMockList = new ArrayList<>();
List<MockExpectConfigWithBLOBs> saveMockExpectList = new ArrayList<>();
List<MockExpectConfigWithBLOBs> updateMockExpectList = new ArrayList<>();
mockConfigList.forEach(item -> {
String oldApiId = item.getApiId();
String refId = sourceApiIdRefIdMap.get(oldApiId);
if (StringUtils.isNotBlank(refId)) {
ApiDefinition api = refIdMap.get(refId);
if (api != null) {
MockConfig baseMockConfig = mockConfigService.selectMockConfigByApiId(api.getId());
String mockConfigId = UUID.randomUUID().toString();
Map<String, MockExpectConfig> oldMockExpectConfig = new HashMap<>();
//已经存储的mock期望编号
List<String> saveExpectNumList = new ArrayList<>();
if (baseMockConfig == null) {
MockConfig mockConfig = new MockConfig();
BeanUtils.copyBean(mockConfig, item);
mockConfig.setApiId(api.getId());
mockConfig.setId(mockConfigId);
mockConfig.setCreateTime(timeStamp);
mockConfig.setUpdateTime(timeStamp);
saveMockList.add(mockConfig);
} else {
mockConfigId = baseMockConfig.getId();
saveExpectNumList = mockConfigService.selectExpectNumberByConfigId(mockConfigId);
List<MockExpectConfig> oldMockExpectList = mockConfigService.selectSimpleMockExpectConfigByMockConfigId(mockConfigId);
oldMockExpectList.forEach(mockExpectConfig -> {
oldMockExpectConfig.put(StringUtils.trim(mockExpectConfig.getName()), mockExpectConfig);
});
}
List<MockExpectConfigWithBLOBs> mockExpectConfigList = mockConfigIdExpectMap.get(item.getId());
if (CollectionUtils.isNotEmpty(mockExpectConfigList)) {
String finalMockConfigId = mockConfigId;
List<String> finalSaveExpectNumList = saveExpectNumList;
mockExpectConfigList.forEach(mockExpectConfigWithBLOBs -> {
MockExpectConfig oldExpect = oldMockExpectConfig.get(StringUtils.trim(mockExpectConfigWithBLOBs.getName()));
MockExpectConfigWithBLOBs expectConfigWithBLOBs = new MockExpectConfigWithBLOBs();
BeanUtils.copyBean(expectConfigWithBLOBs, mockExpectConfigWithBLOBs);
if (oldExpect == null) {
String newMockExpectNum = mockConfigService.getMockExpectId(String.valueOf(api.getNum()), finalSaveExpectNumList);
finalSaveExpectNumList.add(newMockExpectNum);
expectConfigWithBLOBs.setId(UUID.randomUUID().toString());
expectConfigWithBLOBs.setExpectNum(newMockExpectNum);
expectConfigWithBLOBs.setCreateTime(timeStamp);
expectConfigWithBLOBs.setUpdateTime(timeStamp);
expectConfigWithBLOBs.setMockConfigId(finalMockConfigId);
saveMockExpectList.add(expectConfigWithBLOBs);
} else {
expectConfigWithBLOBs.setId(oldExpect.getId());
expectConfigWithBLOBs.setCreateTime(oldExpect.getCreateTime());
expectConfigWithBLOBs.setUpdateTime(timeStamp);
updateMockExpectList.add(expectConfigWithBLOBs);
}
});
}
}
}
});
if (CollectionUtils.isNotEmpty(saveMockList)) {
MockConfigMapper mockConfigBatchMapper = batchSqlSession.getMapper(MockConfigMapper.class);
saveMockList.forEach(mockConfigBatchMapper::insert);
}
MockExpectConfigMapper mockExpectConfigBatchMapper = batchSqlSession.getMapper(MockExpectConfigMapper.class);
if (CollectionUtils.isNotEmpty(saveMockExpectList)) {
saveMockExpectList.forEach(mockExpectConfigBatchMapper::insert);
}
if (CollectionUtils.isNotEmpty(updateMockExpectList)) {
updateMockExpectList.forEach(mockExpectConfigBatchMapper::updateByPrimaryKey);
}
}
}
private void copyCaseByVersion(List<String> chooseApiIdList, Map<String, String> sourceApiIdRefIdMap, Map<String, ApiDefinition> refIdMap, SqlSession batchSqlSession) {
long timeStamp = System.currentTimeMillis();
List<ApiTestCaseWithBLOBs> sourceApiCaseList = apiTestCaseService.selectCasesBydApiIds(new ArrayList<>(sourceApiIdRefIdMap.keySet()));
List<ApiTestCase> caseInChooseApi = apiTestCaseService.selectSimpleCasesBydApiIds(chooseApiIdList);
Map<String, Map<String, ApiTestCase>> apiIdOldCaseMap = new HashMap<>();
caseInChooseApi.forEach(item -> {
String caseName = StringUtils.trim(item.getName());
if (StringUtils.isNotBlank(caseName)) {
if (apiIdOldCaseMap.containsKey(item.getApiDefinitionId())) {
apiIdOldCaseMap.get(item.getApiDefinitionId()).put(caseName, item);
} else {
apiIdOldCaseMap.put(item.getApiDefinitionId(), new HashMap<>() {{
this.put(caseName, item);
}});
}
}
});
List<ApiTestCaseWithBLOBs> saveCaseList = new ArrayList<>();
List<ApiTestCaseWithBLOBs> updateCaseList = new ArrayList<>();
Map<String, Integer> lastCaseNumMap = new LinkedHashMap<>();
sourceApiCaseList.forEach(item -> {
String oldApiId = item.getApiDefinitionId();
String refId = sourceApiIdRefIdMap.get(oldApiId);
if (StringUtils.isNotBlank(refId)) {
ApiDefinition api = refIdMap.get(refId);
if (api != null) {
//通过用例名称检查是否需要覆盖
ApiTestCase oldCase = null;
if (apiIdOldCaseMap.containsKey(api.getId())) {
oldCase = apiIdOldCaseMap.get(api.getId()).get(StringUtils.trim(item.getName()));
}
ApiTestCaseWithBLOBs newCase = new ApiTestCaseWithBLOBs();
BeanUtils.copyBean(newCase, item);
newCase.setApiDefinitionId(api.getId());
newCase.setVersionId(api.getVersionId());
if (oldCase == null) {
int lastCaseNum = 0;
if (lastCaseNumMap.containsKey(api.getId())) {
lastCaseNum = lastCaseNumMap.get(api.getId());
} else {
lastCaseNum = apiTestCaseService.getNextNum(api.getId());
}
int caseNum = apiTestCaseService.getNextNum(lastCaseNum);
newCase.setNum(caseNum);
newCase.setId(UUID.randomUUID().toString());
newCase.setCreateTime(timeStamp);
newCase.setUpdateTime(timeStamp);
lastCaseNumMap.put(api.getId(), caseNum);
saveCaseList.add(newCase);
} else {
newCase.setId(oldCase.getId());
newCase.setNum(oldCase.getNum());
newCase.setCreateTime(oldCase.getCreateTime());
newCase.setUpdateTime(timeStamp);
updateCaseList.add(newCase);
}
}
}
});
ApiTestCaseMapper apiTestCaseBatchMapper = batchSqlSession.getMapper(ApiTestCaseMapper.class);
if (CollectionUtils.isNotEmpty(saveCaseList)) {
saveCaseList.forEach(apiTestCaseBatchMapper::insert);
}
if (CollectionUtils.isNotEmpty(updateCaseList)) {
updateCaseList.forEach(apiTestCaseBatchMapper::updateByPrimaryKey);
}
}
}

View File

@ -480,15 +480,19 @@ public class ApiTestCaseService {
public int getNextNum(String definitionId) {
ApiTestCase apiTestCase = extApiTestCaseMapper.getNextNum(definitionId);
ApiDefinitionWithBLOBs apiDefinitionWithBLOBs = apiDefinitionMapper.selectByPrimaryKey(definitionId);
if (apiTestCase == null) {
ApiDefinitionWithBLOBs apiDefinitionWithBLOBs = apiDefinitionMapper.selectByPrimaryKey(definitionId);
int n = apiDefinitionWithBLOBs.getNum();
return n * 1000 + 1;
} else {
return Optional.of(apiTestCase.getNum() + 1).orElse(apiDefinitionWithBLOBs.getNum() * 1000 + 1);
return this.getNextNum(apiTestCase.getNum());
}
}
public int getNextNum(Integer lastApiCaseNum) {
return Optional.of(lastApiCaseNum + 1).orElse(lastApiCaseNum * 1000 + 1);
}
public int getNextNum(String definitionId, Integer definitionNum, String projectId) {
ApiTestCase apiTestCase = extApiTestCaseMapper.getNextNum(definitionId);
ApiDefinitionWithBLOBs apiDefinitionWithBLOBs = apiDefinitionMapper.selectByPrimaryKey(definitionId);
@ -607,6 +611,12 @@ public class ApiTestCaseService {
return apiTestCaseMapper.selectByExampleWithBLOBs(example);
}
public List<ApiTestCase> selectSimpleCasesBydApiIds(List<String> apiIds) {
ApiTestCaseExample example = new ApiTestCaseExample();
example.createCriteria().andApiDefinitionIdIn(apiIds).andStatusNotEqualTo("Trash");
return apiTestCaseMapper.selectByExample(example);
}
public Map<String, String> getRequest(ApiTestCaseRequest request) {
List<ApiTestCaseInfo> list = extApiTestCaseMapper.getRequest(request);
return list.stream().collect(Collectors.toMap(ApiTestCaseWithBLOBs::getId, ApiTestCaseWithBLOBs::getRequest));
@ -986,7 +996,7 @@ public class ApiTestCaseService {
if (CollectionUtils.isNotEmpty(apiCaseIds)) {
ApiTestCaseExample example = new ApiTestCaseExample();
example.createCriteria().andIdIn(apiCaseIds);
example.or().andStatusNotEqualTo(CommonConstants.TrashStatus).andStatusIsNull();
example.or().andStatusNotEqualTo(CommonConstants.TRASH_STATUS).andStatusIsNull();
return apiTestCaseMapper.selectByExample(example);
}
return new ArrayList<>();

View File

@ -734,9 +734,9 @@ public class TestPlanApiCaseService {
List<String> caseIds = apiCases.stream().map(TestPlanApiCase::getApiCaseId).collect(Collectors.toList());
List<String> definitionIds = apiTestCaseService.selectCasesBydIds(caseIds).stream().filter(apiTestCase -> apiTestCase.getStatus() == null || !CommonConstants.TrashStatus.equals(apiTestCase.getStatus())).map(ApiTestCase::getApiDefinitionId).collect(Collectors.toList());
List<String> definitionIds = apiTestCaseService.selectCasesBydIds(caseIds).stream().filter(apiTestCase -> apiTestCase.getStatus() == null || !CommonConstants.TRASH_STATUS.equals(apiTestCase.getStatus())).map(ApiTestCase::getApiDefinitionId).collect(Collectors.toList());
List<String> dataNodeIds = apiDefinitionService.selectApiDefinitionBydIds(definitionIds).stream().filter(apiDefinition -> apiDefinition.getStatus() == null || !CommonConstants.TrashStatus.equals(apiDefinition.getStatus())).map(ApiDefinition::getModuleId).collect(Collectors.toList());
List<String> dataNodeIds = apiDefinitionService.selectApiDefinitionBydIds(definitionIds).stream().filter(apiDefinition -> apiDefinition.getStatus() == null || !CommonConstants.TRASH_STATUS.equals(apiDefinition.getStatus())).map(ApiDefinition::getModuleId).collect(Collectors.toList());
List<ApiModuleDTO> nodeTrees = apiModuleService.getNodeTrees(testCaseNodes);

View File

@ -16,7 +16,6 @@ import io.metersphere.base.mapper.ApiScenarioMapper;
import io.metersphere.base.mapper.ApiTestEnvironmentMapper;
import io.metersphere.base.mapper.ext.ExtApiScenarioModuleMapper;
import io.metersphere.base.mapper.plan.TestPlanApiScenarioMapper;
import io.metersphere.base.mapper.plan.ext.ExtTestPlanApiCaseMapper;
import io.metersphere.base.mapper.plan.ext.ExtTestPlanApiScenarioMapper;
import io.metersphere.base.mapper.plan.ext.ExtTestPlanScenarioCaseMapper;
import io.metersphere.commons.constants.ApiRunMode;
@ -29,7 +28,6 @@ import io.metersphere.dto.MsExecResponseDTO;
import io.metersphere.dto.PlanReportCaseDTO;
import io.metersphere.dto.ProjectConfig;
import io.metersphere.dto.RunModeConfigDTO;
import io.metersphere.environment.service.BaseEnvGroupProjectService;
import io.metersphere.environment.service.BaseEnvironmentService;
import io.metersphere.i18n.Translator;
import io.metersphere.log.vo.OperatingLogDetails;
@ -70,8 +68,6 @@ public class TestPlanScenarioCaseService {
@Resource
ExtTestPlanScenarioCaseMapper extTestPlanScenarioCaseMapper;
@Resource
ExtTestPlanApiCaseMapper extTestPlanApiCaseMapper;
@Resource
ExtTestPlanApiScenarioMapper extTestPlanApiScenarioMapper;
@Resource
ApiScenarioReportService apiScenarioReportService;
@ -94,8 +90,6 @@ public class TestPlanScenarioCaseService {
@Resource
private ApiDefinitionExecResultService apiDefinitionExecResultService;
@Resource
private BaseEnvGroupProjectService environmentGroupProjectService;
@Resource
private BaseEnvironmentService apiTestEnvironmentService;
@Resource
private ExtApiScenarioModuleMapper extApiScenarioModuleMapper;
@ -293,14 +287,13 @@ public class TestPlanScenarioCaseService {
RunScenarioRequest request = new RunScenarioRequest();
request.setIds(scenarioIds);
request.setReportId(testPlanScenarioRequest.getId());
request.setScenarioTestPlanIdMap(scenarioPlanIdMap);
request.setRunMode(ApiRunMode.SCENARIO_PLAN.name());
request.setId(testPlanScenarioRequest.getId());
request.setExecuteType(ExecuteType.Saved.name());
request.setTriggerMode(testPlanScenarioRequest.getTriggerMode());
request.setConfig(testPlanScenarioRequest.getConfig());
request.setPlanCaseIds(planCaseIdList);
request.setRequestOriginator("TEST_PLAN");
request.setPlanScenarioIds(planCaseIdList);
request.setRequestOriginator(CommonConstants.TEST_PLAN);
return apiAutomationService.run(request);
}
@ -1009,70 +1002,6 @@ public class TestPlanScenarioCaseService {
return testPlanApiCaseService.buildCases(apiTestCases);
}
public TestPlanApiReportInfoDTO genApiReportInfoForSchedule(String planId, RunModeConfigDTO runModeConfigDTO) {
TestPlanApiReportInfoDTO testPlanApiReportInfo = new TestPlanApiReportInfoDTO();
Map<String, String> planApiCaseIdMap = new LinkedHashMap<>();
Map<String, String> planScenarioIdMap = new LinkedHashMap<>();
List<TestPlanApiScenarioInfoDTO> testPlanApiScenarioList = extTestPlanScenarioCaseMapper.selectLegalDataByTestPlanId(planId);
for (TestPlanApiScenarioInfoDTO model : testPlanApiScenarioList) {
planScenarioIdMap.put(model.getId(), model.getApiScenarioId());
}
List<TestPlanApiCaseInfoDTO> testPlanApiCaseList = extTestPlanApiCaseMapper.selectLegalDataByTestPlanId(planId);
for (TestPlanApiCaseInfoDTO model : testPlanApiCaseList) {
planApiCaseIdMap.put(model.getId(), model.getApiCaseId());
}
testPlanApiReportInfo.setPlanApiCaseIdMap(planApiCaseIdMap);
testPlanApiReportInfo.setPlanScenarioIdMap(planScenarioIdMap);
//解析运行环境信息
TestPlanReportRunInfoDTO runInfoDTO = this.parseTestPlanRunInfo(runModeConfigDTO, testPlanApiCaseList, testPlanApiScenarioList);
testPlanApiReportInfo.setRunInfoDTO(runInfoDTO);
return testPlanApiReportInfo;
}
public TestPlanReportRunInfoDTO parseTestPlanRunInfo(RunModeConfigDTO runModeConfigDTO, List<TestPlanApiCaseInfoDTO> testPlanApiCaseList, List<TestPlanApiScenarioInfoDTO> testPlanApiScenarioList) {
TestPlanReportRunInfoDTO runInfoDTO = new TestPlanReportRunInfoDTO();
Map<String, String> selectEnvMap = runModeConfigDTO.getEnvMap();
runInfoDTO.setRunMode(runModeConfigDTO.getMode());
if (StringUtils.equals("GROUP", runModeConfigDTO.getEnvironmentType()) && StringUtils.isNotEmpty(runModeConfigDTO.getEnvironmentGroupId())) {
selectEnvMap = environmentGroupProjectService.getEnvMap(runModeConfigDTO.getEnvironmentGroupId());
runInfoDTO.setEnvGroupId(runModeConfigDTO.getEnvironmentGroupId());
}
for (TestPlanApiScenarioInfoDTO model : testPlanApiScenarioList) {
Map<String, String> envMap = null;
if (StringUtils.equalsIgnoreCase("group", model.getEnvironmentType()) && StringUtils.isNotEmpty(model.getEnvironmentGroupId())) {
envMap = environmentGroupProjectService.getEnvMap(model.getEnvironmentGroupId());
} else {
if (MapUtils.isNotEmpty(selectEnvMap) && selectEnvMap.containsKey(model.getProjectId())) {
runInfoDTO.putScenarioRunInfo(model.getId(), model.getProjectId(), selectEnvMap.get(model.getProjectId()));
} else if (StringUtils.isNotEmpty(model.getEnvironment())) {
try {
envMap = JSON.parseObject(model.getEnvironment(), Map.class);
} catch (Exception e) {
LogUtil.error("解析场景环境失败!", e);
}
}
}
if (MapUtils.isNotEmpty(envMap)) {
for (Map.Entry<String, String> entry : envMap.entrySet()) {
String projectId = entry.getKey();
String envIdStr = entry.getValue();
runInfoDTO.putScenarioRunInfo(model.getId(), projectId, envIdStr);
}
}
}
for (TestPlanApiCaseInfoDTO model : testPlanApiCaseList) {
if (MapUtils.isNotEmpty(selectEnvMap) && selectEnvMap.containsKey(model.getProjectId())) {
runInfoDTO.putApiCaseRunInfo(model.getId(), model.getProjectId(), selectEnvMap.get(model.getProjectId()));
} else {
runInfoDTO.putApiCaseRunInfo(model.getId(), model.getProjectId(), model.getEnvironmentId());
}
}
return runInfoDTO;
}
public Boolean isExecuting(String planId) {
List<TestPlanApiScenarioInfoDTO> testPlanApiScenarioList = extTestPlanScenarioCaseMapper.selectLegalDataByTestPlanId(planId);
return !testPlanApiScenarioList.stream()
@ -1220,7 +1149,7 @@ public class TestPlanScenarioCaseService {
.collect(Collectors.toList());
List<String> dataNodeIds = apiAutomationService.selectByIds(caseIds).stream()
.filter(apiScenario -> apiScenario.getStatus() == null || !CommonConstants.TrashStatus.equals(apiScenario.getStatus()))
.filter(apiScenario -> apiScenario.getStatus() == null || !CommonConstants.TRASH_STATUS.equals(apiScenario.getStatus()))
.map(ApiScenario::getApiScenarioModuleId)
.collect(Collectors.toList());

View File

@ -16,6 +16,7 @@ import io.metersphere.base.mapper.ext.ExtApiScenarioReportResultMapper;
import io.metersphere.base.mapper.plan.ext.ExtTestPlanApiCaseMapper;
import io.metersphere.base.mapper.plan.ext.ExtTestPlanApiScenarioMapper;
import io.metersphere.commons.constants.ApiRunMode;
import io.metersphere.commons.constants.CommonConstants;
import io.metersphere.commons.constants.ElementConstants;
import io.metersphere.commons.constants.ReportTypeConstants;
import io.metersphere.commons.enums.ApiReportStatus;
@ -228,7 +229,7 @@ public class ApiScenarioRerunService {
BatchRunDefinitionRequest request = new BatchRunDefinitionRequest();
request.setTriggerMode(testPlanReport.getTriggerMode());
request.setRerun(true);
request.setPlanIds(ids);
request.setPlanCaseIds(ids);
request.setPlanReportId(parametersDTO.getReportId());
request.setUserId(parametersDTO.getCases().get(0).getUserId());
@ -271,9 +272,7 @@ public class ApiScenarioRerunService {
}
List<String> scenarioIds = apiScenarios.stream().map(TestPlanApiScenario::getApiScenarioId).collect(Collectors.toList());
LoggerUtil.info("重跑测试计划报告:【" + parametersDTO.getReportId() + "】,对应重跑场景资源:" + scenarioIds.size());
Map<String, String> scenarioTestPlanIdMap = apiScenarios.stream().collect(Collectors.toMap(TestPlanApiScenario::getId, a -> a.getApiScenarioId(), (k1, k2) -> k1));
List<String> planScenarioIds = apiScenarios.stream().map(TestPlanApiScenario::getId).collect(Collectors.toList());
// 查出原始报告
List<String> reportIds = apiScenarios.stream().map(TestPlanApiScenario::getReportId).collect(Collectors.toList());
if (com.alibaba.nacos.common.utils.CollectionUtils.isEmpty(reportIds)) {
@ -282,7 +281,7 @@ public class ApiScenarioRerunService {
ApiScenarioReportExample reportExample = new ApiScenarioReportExample();
reportExample.createCriteria().andIdIn(reportIds);
List<ApiScenarioReport> reports = apiScenarioReportMapper.selectByExample(reportExample);
if (com.alibaba.nacos.common.utils.CollectionUtils.isEmpty(reports)) {
if (CollectionUtils.isEmpty(reports)) {
return isStart;
}
// config
@ -312,7 +311,8 @@ public class ApiScenarioRerunService {
request.setExecuteType(ExecuteType.Completed.name());
request.setRunMode(ApiRunMode.SCENARIO_PLAN.name());
request.setIds(scenarioIds); // 场景IDS
request.setScenarioTestPlanIdMap(scenarioTestPlanIdMap);//场景id和计划场景id映射关系
request.setPlanScenarioIds(planScenarioIds);
// 一批执行的报告配置都是相同的
ApiScenarioReportWithBLOBs scenario = apiScenarioReportMapper.selectByPrimaryKey(reportIds.get(0));
if (scenario != null && StringUtils.isNotEmpty(scenario.getEnvConfig())) {
@ -322,7 +322,7 @@ public class ApiScenarioRerunService {
}
request.setTestPlanReportId(parametersDTO.getReportId());
request.setId(UUID.randomUUID().toString());
request.setRequestOriginator("TEST_PLAN");
request.setRequestOriginator(CommonConstants.TEST_PLAN);
LoggerUtil.info("清理原始报告对应结果:【" + request.getTestPlanReportId() + "】," + reportIds.size());
ApiScenarioReportResultExample reportResultExample = new ApiScenarioReportResultExample();

View File

@ -2222,7 +2222,7 @@ public class ApiScenarioService {
public List<ApiScenario> getScenarioCaseByIds(List<String> ids) {
if (CollectionUtils.isNotEmpty(ids)) {
ApiScenarioExample example = new ApiScenarioExample();
example.createCriteria().andIdIn(ids).andStatusNotEqualTo(CommonConstants.TrashStatus);
example.createCriteria().andIdIn(ids).andStatusNotEqualTo(CommonConstants.TRASH_STATUS);
return apiScenarioMapper.selectByExample(example);
}
return new ArrayList<>();

View File

@ -0,0 +1,49 @@
package io.metersphere.utils;
import io.metersphere.api.dto.definition.BatchDataCopyRequest;
import io.metersphere.commons.utils.BeanUtils;
import org.apache.commons.collections.CollectionUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
/**
* 批量处理工具
*/
public class BatchProcessingUtil {
private static final int BATCH_PROCESS_QUANTITY = 1000;
public static void batchProcessingByDataCopy(BatchDataCopyRequest paramRequest, Consumer<BatchDataCopyRequest> func) {
List<String> paramList = paramRequest.getIds();
if (CollectionUtils.isNotEmpty(paramList)) {
BatchDataCopyRequest queryRequest = new BatchDataCopyRequest();
BeanUtils.copyBean(queryRequest, paramRequest);
int unProcessingCount = paramList.size();
while (paramList.size() > BATCH_PROCESS_QUANTITY) {
List<String> processingList = new ArrayList<>();
for (int i = 0; i < BATCH_PROCESS_QUANTITY; i++) {
processingList.add(paramList.get(i));
}
//函数处理
queryRequest.setIds(processingList);
func.accept(queryRequest);
paramList.removeAll(processingList);
if (paramList.size() == unProcessingCount) {
//如果剩余数量没有发生变化则跳出循环防止出现死循环的情况
break;
} else {
unProcessingCount = paramList.size();
}
}
if (CollectionUtils.isNotEmpty(paramList)) {
//剩余待处理数据进行处理
queryRequest.setIds(paramList);
func.accept(queryRequest);
}
}
}
}

View File

@ -18,22 +18,22 @@ public class MsWebSocketClient extends WebSocketClient {
LogUtil.info("握手...");
for (Iterator<String> it = shake.iterateHttpFields(); it.hasNext(); ) {
String key = it.next();
System.out.println(key + ":" + shake.getFieldValue(key));
LogUtil.info(key + ":" + shake.getFieldValue(key));
}
}
@Override
public void onMessage(String paramString) {
System.out.println("接收到消息:" + paramString);
LogUtil.info("接收到消息:" + paramString);
}
@Override
public void onClose(int paramInt, String paramString, boolean paramBoolean) {
System.out.println("关闭...");
LogUtil.info("关闭...");
}
@Override
public void onError(Exception e) {
System.out.println("异常" + e);
LogUtil.info("异常" + e);
}
}

View File

@ -1,4 +1,4 @@
import { fileDownload, fileUpload } from '@/api/base-network';
import { fileUpload } from '@/api/base-network';
import { get, post } from 'metersphere-frontend/src/plugins/request';
export function getRelationshipCountApi(id) {
@ -137,6 +137,10 @@ export function removeToGcByParams(params) {
return post('/api/definition/move-gc-batch', params);
}
export function copyDataByVersion(params) {
return post('/api/definition/copy/by/version', params);
}
export function removeToGcByIds(params) {
return post('/api/definition/move-gc', params);
}

View File

@ -369,6 +369,7 @@ export default {
file: false,
encode: true,
enable: true,
isEdit: false,
contentType: 'text/plain',
})
);

View File

@ -225,6 +225,8 @@
:data-count="$refs.table ? $refs.table.selectDataCounts : 0"
:typeArr="typeArr"
:value-arr="valueArr" />
<!--从指定版本复制数据-->
<version-selector @handleSave="handleCopyDataFromVersion" ref="versionSelector" />
<!--高级搜索-->
<ms-table-adv-search-bar :condition.sync="condition" :showLink="false" ref="searchBar" @search="search" />
<case-batch-move @refresh="initTable" @moveSave="moveSave" ref="testCaseBatchMove" />
@ -239,6 +241,7 @@
import {
batchCopyByParams,
batchEditByParams,
copyDataByVersion,
definitionReduction,
delDefinition,
delDefinitionByRefId,
@ -292,6 +295,7 @@ import { getGraphByCondition } from '@/api/graph';
import ListItemDeleteConfirm from 'metersphere-frontend/src/components/ListItemDeleteConfirm';
import MsSearch from 'metersphere-frontend/src/components/search/MsSearch';
import { buildNodePath } from 'metersphere-frontend/src/model/NodeTree';
import VersionSelector from '@/business/definition/components/version/VersionSelector';
export default {
name: 'ApiList',
@ -316,6 +320,7 @@ export default {
MsTable,
MsTableColumn,
MsSearch,
VersionSelector,
MsApiReportStatus: () => import('../../../automation/report/ApiReportStatus'),
MxRelationshipGraphDrawer: () => import('metersphere-frontend/src/components/graph/MxRelationshipGraphDrawer'),
},
@ -357,6 +362,12 @@ export default {
handleClick: this.handleBatchCopy,
permissions: ['PROJECT_API_DEFINITION:READ+CREATE_API'],
},
{
name: this.$t('api_definition.copy_data_from_other_version'),
isXPack: true,
handleClick: this.batchCopyDataFromVersion,
permissions: ['PROJECT_API_DEFINITION:READ+EDIT_API'],
},
{
name: this.$t('test_track.case.generate_dependencies'),
isXPack: true,
@ -646,6 +657,11 @@ export default {
this.isMoveBatch = false;
this.$refs.testCaseBatchMove.open(this.moduleTree, this.$refs.table.selectIds, this.moduleOptionsNew);
},
batchCopyDataFromVersion() {
if (this.$refs.versionSelector) {
this.$refs.versionSelector.open(this.projectId);
}
},
closeCaseModel() {
//
if (this.$refs.caseList) {
@ -849,7 +865,7 @@ export default {
this.search();
},
search() {
this.$EventBus.$emit("apiConditionBus", this.condition)
this.$EventBus.$emit('apiConditionBus', this.condition);
this.changeSelectDataRangeAll();
this.initTable();
},
@ -1181,6 +1197,35 @@ export default {
return '';
}
},
//
handleCopyDataFromVersion(selectVersionId, copyCase, copyMock) {
let copyParam = {};
// let param = {};
// if (vueObj.selectRows) {
// param.ids = selectIds ? selectIds : Array.from(vueObj.selectRows).map(row => row.id);
// } else {
// param.ids = selectIds;
// }
// param.projectId = projectId ? projectId : getCurrentProjectID();
// param.condition = vueObj.condition;
// return param;
// copyParam.versionId = selectVersionId;
copyParam.copyCase = copyCase;
copyParam.copyMock = copyMock;
copyParam.versionId = selectVersionId;
copyParam.condition = this.condition;
copyParam.condition.ids = this.$refs.table.selectIds;
copyParam.condition.protocol = this.currentProtocol;
copyDataByVersion(copyParam).then(() => {
this.$success(this.$t('commons.copy_success'));
if (this.$refs.versionSelector) {
this.$refs.versionSelector.handleClose();
}
this.initTable();
});
},
},
};
</script>

View File

@ -525,6 +525,7 @@ export default {
file: false,
encode: true,
enable: true,
isEdit: false,
contentType: 'text/plain',
})

View File

@ -0,0 +1,92 @@
<template>
<ms-edit-dialog
:visible.sync="visible"
width="300px"
:title="$t('commons.delete_all_version')"
:with-footer="false"
:close-on-click-modal="false"
@close="handleClose">
<el-row>
<div v-loading="loading">
<el-row>
<el-select v-model="versionId" size="small" :placeholder="$t('project.version.please_input_version')">
<el-option v-for="v in versionData" :key="v.id" :label="v.name" :value="v.id" />
</el-select>
</el-row>
<el-row style="margin-top: 10px">
<el-checkbox v-model="selectCase" v-permission="['PROJECT_API_DEFINITION:READ+CREATE_CASE']">{{ $t('commons.api_case') }}</el-checkbox>
<el-checkbox v-model="selectMock">{{ $t('commons.mock') }}</el-checkbox>
</el-row>
</div>
</el-row>
<template v-slot:footer>
<el-button type="primary" :loading="saving" size="small" @click="save" @keydown.enter.native.prevent>{{
$t('commons.save')
}}</el-button>
</template>
</ms-edit-dialog>
</template>
<script>
import MsEditDialog from '@/business/commons/MsEditDialog';
import { getProjectVersions } from '@/api/xpack';
export default {
name: 'VersionSelector',
components: { MsEditDialog },
data() {
return {
loading: false,
visible: false,
saving:false,
versionId: '',
versionData: [],
selectCase: true,
selectMock: true,
};
},
props: {
tips: String,
projectId: String,
},
methods: {
open(projectId) {
this.saving = false;
this.selectMock = true;
this.selectCase = true;
this.versionId = '';
this.versionData = [];
this.visible = true;
this.loading = true;
getProjectVersions(projectId)
.then((response) => {
if (response.data) {
this.versionData = response.data;
} else {
this.versionData = [];
}
this.loading = false;
})
.catch(() => {
this.loading = false;
});
},
handleClose() {
this.saving = false;
this.$emit('handleClose');
this.visible = false;
},
save() {
if (!this.versionId || this.versionId === '') {
this.$error(this.$t('project.version.please_input_version'));
} else {
this.saving = true;
this.$emit('handleSave', this.versionId,this.selectCase,this.selectMock);
}
},
},
};
</script>
<style scoped></style>

View File

@ -18,6 +18,7 @@ const message = {
type: 'Type',
default_value: 'Default value',
},
copy_data_from_other_version: 'Copy data from other version',
body: {
json_format_error: 'JSON format error',
},

View File

@ -17,6 +17,7 @@ const message = {
type: '类型',
default_value: '默认值',
},
copy_data_from_other_version: '复制版本数据',
body: {
json_format_error: 'JSON格式错误',
},

View File

@ -17,6 +17,7 @@ const message = {
type: '類型',
default_value: '默認值',
},
copy_data_from_other_version: '複製版本數據',
body: {
json_format_error: 'JSON格式錯誤',
},

View File

@ -1,95 +1,159 @@
<template>
<div>
<div style="float: right;">
<el-button size="mini" @click="open" :disabled="isReadOnly">{{ $t('test_track.case.import.click_upload') }}
<div style="float: right">
<el-button size="mini" @click="open" :disabled="isReadOnly"
>{{ $t("test_track.case.import.click_upload") }}
</el-button>
</div>
<div class="tip">{{ this.$t('commons.ssl.files') }}
</div>
<div class="tip">{{ this.$t("commons.ssl.files") }}</div>
<div class="ms-border">
<el-table :data="sslConfig.files" highlight-current-row>
<el-table-column prop="name" :label="$t('load_test.file_name')" show-overflow-tooltip width="180"/>
<el-table-column prop="type" :label="$t('api_test.definition.request.esb_table.type')" show-overflow-tooltip
min-width="100px"/>
<el-table-column prop="password" show-overflow-tooltip min-width="120px" :label="$t('commons.password')">
<template v-slot:default="{row}">
<el-input size="small" v-model="row.password" clearable show-password/>
<el-table-column
prop="name"
:label="$t('load_test.file_name')"
show-overflow-tooltip
width="180"
/>
<el-table-column
prop="type"
:label="$t('api_test.definition.request.esb_table.type')"
show-overflow-tooltip
min-width="100px"
/>
<el-table-column
prop="password"
show-overflow-tooltip
min-width="120px"
:label="$t('commons.password')"
>
<template v-slot:default="{ row }">
<el-input
size="small"
v-model="row.password"
clearable
show-password
/>
</template>
</el-table-column>
<el-table-column prop="updateTime" show-overflow-tooltip min-width="120px"
:label="$t('load_test.last_modify_time')">
<template v-slot:default="{row}">
<el-table-column
prop="updateTime"
show-overflow-tooltip
min-width="120px"
:label="$t('load_test.last_modify_time')"
>
<template v-slot:default="{ row }">
<span>{{ row.updateTime | datetimeFormat }}</span>
</template>
</el-table-column>
<el-table-column :label="$t('commons.operating')" width="100px">
<template v-slot:default="{row}">
<template v-slot:default="{ row }">
<div>
<ms-table-operator-button :tip="$t('commons.update')" icon="el-icon-edit"
type="primary" @exec="edit(row)"/>
<ms-table-operator-button :tip="$t('api_test.automation.remove')"
icon="el-icon-delete" @exec="remove(row)" type="danger"/>
<ms-table-operator-button
:tip="$t('commons.update')"
icon="el-icon-edit"
type="primary"
@exec="edit(row)"
/>
<ms-table-operator-button
:tip="$t('api_test.automation.remove')"
icon="el-icon-delete"
@exec="remove(row)"
type="danger"
/>
</div>
</template>
</el-table-column>
</el-table>
</div>
<p class="tip">{{ this.$t('commons.ssl.entry') }} </p>
<p class="tip">{{ this.$t("commons.ssl.entry") }}</p>
<div class="ms-border">
<el-table :data="sslConfig.entry" highlight-current-row v-if="!loading">
<el-table-column prop="originalAsName" :label="$t('commons.ssl.original_as_name')" show-overflow-tooltip
width="180"/>
<el-table-column prop="newAsName" :label="$t('commons.ssl.new_as_name')" show-overflow-tooltip
min-width="100px">
<template v-slot:default="{row}">
<el-table-column
prop="originalAsName"
:label="$t('commons.ssl.original_as_name')"
show-overflow-tooltip
width="180"
/>
<el-table-column
prop="newAsName"
:label="$t('commons.ssl.new_as_name')"
show-overflow-tooltip
min-width="100px"
>
<template v-slot:default="{ row }">
<el-input size="mini" v-model="row.newAsName"></el-input>
</template>
</el-table-column>
<el-table-column prop="type" show-overflow-tooltip min-width="120px"
:label="$t('api_test.definition.request.esb_table.type')"/>
<el-table-column prop="password" show-overflow-tooltip min-width="120px" :label="$t('commons.password')">
<template v-slot:default="{row}">
<el-input size="mini" v-model="row.password" show-password></el-input>
<el-table-column
prop="type"
show-overflow-tooltip
min-width="120px"
:label="$t('api_test.definition.request.esb_table.type')"
/>
<el-table-column
prop="password"
show-overflow-tooltip
min-width="120px"
:label="$t('commons.password')"
>
<template v-slot:default="{ row }">
<el-input
size="mini"
v-model="row.password"
show-password
></el-input>
</template>
</el-table-column>
<el-table-column prop="sourceName" show-overflow-tooltip min-width="120px" :label="$t('commons.ssl.source')"/>
<el-table-column
prop="sourceName"
show-overflow-tooltip
min-width="120px"
:label="$t('commons.ssl.source')"
/>
<el-table-column :label="$t('commons.ssl.default')" width="100px">
<template v-slot:default="{row}">
<el-checkbox v-model="row.default" @change="changeCheck(row)"/>
<template v-slot:default="{ row }">
<el-checkbox v-model="row.default" @change="changeCheck(row)" />
</template>
</el-table-column>
</el-table>
</div>
<ms-s-s-l-file-upload :config="fileConfig" :sslConfig="sslConfig" :callback="addConfig" ref="sslConfigUpload"/>
<ms-s-s-l-file-upload
:config="fileConfig"
:sslConfig="sslConfig"
:callback="addConfig"
ref="sslConfigUpload"
/>
</div>
</template>
<script>
import {environmentEntry} from "../../api/environment";
import {SSLConfig} from "../../model/EnvironmentModel";
import { environmentEntry } from "../../api/environment";
import { SSLConfig } from "../../model/EnvironmentModel";
import MsApiKeyValue from "./commons/ApiKeyValue";
import {REQUEST_HEADERS} from "../../utils/constants";
import MsSelectTree from "../select-tree/SelectTree";
import MsTableOperatorButton from "../MsTableOperatorButton";
import {getUUID} from "../../utils";
import { getUUID } from "../../utils";
import MsSSLFileUpload from "./SSLFileUpload";
export default {
name: "MsEnvironmentSSLConfig",
components: {MsApiKeyValue, MsSelectTree, MsTableOperatorButton, MsSSLFileUpload},
components: {
MsApiKeyValue,
MsSelectTree,
MsTableOperatorButton,
MsSSLFileUpload,
},
props: {
sslConfig: new SSLConfig(),
projectId: String,
isReadOnly: {
type: Boolean,
default: false
default: false,
},
},
created() {
},
created() {},
data() {
return {
loading: false,
@ -97,8 +161,7 @@ export default {
};
},
watch: {
projectId() {
},
projectId() {},
},
methods: {
open() {
@ -111,7 +174,7 @@ export default {
type: file.type,
updateTime: new Date().getTime(),
password: config.password,
file: file
file: file,
};
if (!sslFile.type && sslFile.name) {
let type = sslFile.name.substr(sslFile.name.lastIndexOf(".") + 1);
@ -125,46 +188,48 @@ export default {
this.$refs.sslConfigUpload.open(row);
},
reload() {
this.loading = true
this.loading = true;
this.$nextTick(() => {
this.loading = false
this.loading = false;
});
},
getEntry(sslFile) {
environmentEntry(sslFile.file, null, sslFile.password).then(response => {
let data = response.data;
if (data) {
if (!sslFile.id) {
sslFile.id = getUUID();
data.forEach(item => {
if (item) {
item.id = getUUID();
item.sourceId = sslFile.id;
item.sourceName = sslFile.name;
}
item.password = "";
item.default = false;
environmentEntry(sslFile.file, null, sslFile.password).then(
(response) => {
let data = response.data.data;
if (data) {
if (!sslFile.id) {
sslFile.id = getUUID();
data.forEach((item) => {
if (item) {
item.id = getUUID();
item.sourceId = sslFile.id;
item.sourceName = sslFile.name;
}
item.password = "";
item.default = false;
this.sslConfig.entry.unshift(item);
})
this.sslConfig.files.unshift(sslFile);
} else {
//
this.remove(sslFile);
data.forEach(item => {
if (item) {
item.id = getUUID();
item.sourceId = sslFile.id;
item.sourceName = sslFile.name;
}
item.password = "";
item.default = false;
this.sslConfig.entry.unshift(item);
})
this.sslConfig.files.unshift(sslFile);
this.sslConfig.entry.unshift(item);
});
this.sslConfig.files.unshift(sslFile);
} else {
//
this.remove(sslFile);
data.forEach((item) => {
if (item) {
item.id = getUUID();
item.sourceId = sslFile.id;
item.sourceName = sslFile.name;
}
item.password = "";
item.default = false;
this.sslConfig.entry.unshift(item);
});
this.sslConfig.files.unshift(sslFile);
}
}
}
});
);
},
remove(row) {
const index = this.sslConfig.files.findIndex((d) => d.id === row.id);
@ -172,22 +237,24 @@ export default {
//
if (this.sslConfig.entry) {
let removeKeys = [];
this.sslConfig.entry.forEach(item => {
this.sslConfig.entry.forEach((item) => {
if (item && item.sourceId === row.id) {
const index = this.sslConfig.entry.findIndex((d) => d.sourceId === row.id);
const index = this.sslConfig.entry.findIndex(
(d) => d.sourceId === row.id
);
removeKeys.push(index);
}
});
removeKeys.forEach(index => {
removeKeys.forEach((index) => {
if (index !== -1) {
this.sslConfig.entry.splice(index, 1);
}
})
});
}
},
changeCheck(row) {
if (row.default) {
this.sslConfig.entry.forEach(item => {
this.sslConfig.entry.forEach((item) => {
if (item && item.sourceId !== row.id) {
item.default = false;
}
@ -195,8 +262,8 @@ export default {
row.default = true;
}
},
}
}
},
};
</script>
<style scoped>

View File

@ -35,7 +35,7 @@ public class RestTemplateConfig implements WebMvcConfigurer {
private final static int MAX_PER_ROUTE = 500;
private final static int CONN_REQUEST_TIMEOUT = 5000;
private final static int CONNECT_TIMEOUT = 8000;
private final static int SOCKET_TIMEOUT = 20 * 1000;
private final static int SOCKET_TIMEOUT = 200 * 1000;
@Bean
public RestTemplate restTemplate() {

View File

@ -374,10 +374,20 @@
#{id}
</foreach>
AND project_id = #{projectId,jdbcType=VARCHAR}
<if test="request.name!=null">
<if test="request.name != null">
AND file_metadata.name LIKE CONCAT('%', #{request.name}, '%')
</if>
order by update_time DESC
<if test="request.ids != null and request.ids.size() > 0">
AND file_metadata.id NOT IN (
SELECT file_id
FROM load_test_file t
WHERE t.test_id IN
<foreach collection="request.ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
)
</if>
ORDER BY update_time DESC
</select>
<select id="selectProjectIds" resultType="java.lang.String">

View File

@ -102,11 +102,19 @@
import MsDialogFooter from "metersphere-frontend/src/components/MsDialogFooter";
import MsTablePagination from "metersphere-frontend/src/components/pagination/TablePagination";
import {getCurrentProjectID} from "metersphere-frontend/src/utils/token";
import {findThreadGroup} from "../../../business/test/model/ThreadGroup";
import {findThreadGroup} from "@/business/test/model/ThreadGroup";
import MsTableButton from "metersphere-frontend/src/components/MsTableButton";
import MsTableHeader from "metersphere-frontend/src/components/MsTableHeader";
import MsTableOperatorButton from "metersphere-frontend/src/components/MsTableOperatorButton";
import {checkFileIsRelated, deleteFile, getJmxContents, getProjectFileByName, getProjectFiles, updateFile, uploadFiles} from "../../../api/performance";
import {
checkFileIsRelated,
deleteFile,
getJmxContents,
getProjectFileByName,
getProjectFiles,
updateFile,
uploadFiles
} from "@/api/performance";
export default {
name: "ExistFiles",
@ -117,6 +125,7 @@ export default {
uploadList: Array,
scenarios: Array,
isReadOnly: Boolean,
testId: String
},
inject: [
'reload'
@ -131,6 +140,7 @@ export default {
loadType: 'jmx',
existFiles: [],
selectIds: new Set,
selectFiles: [],
condition: {},
projectId: getCurrentProjectID()
};
@ -144,16 +154,23 @@ export default {
close() {
this.loadFileVisible = false;
this.selectIds.clear();
this.selectFiles = [];
},
handleSelectAll(selection) {
if (selection.length > 0) {
this.existFiles.forEach(item => {
this.selectIds.add(item.id);
this.selectFiles.push(item);
});
} else {
this.existFiles.forEach(item => {
if (this.selectIds.has(item.id)) {
this.selectIds.delete(item.id);
for (let i = 0; i < this.selectFiles.length - 1; i++) {
if (this.selectFiles[i].id === item.id) {
this.selectFiles.splice(i, 1);
}
}
}
});
}
@ -161,11 +178,21 @@ export default {
handleSelectionChange(selection, row) {
if (this.selectIds.has(row.id)) {
this.selectIds.delete(row.id);
for (let i = 0; i < this.selectFiles.length - 1; i++) {
if (this.selectFiles[i].id === row.id) {
this.selectFiles.splice(i, 1);
}
}
} else {
this.selectIds.add(row.id);
this.selectFiles.push(row);
}
},
getProjectFiles() {
if (this.testId) {
this.condition.ids = [];
this.condition.ids.push(this.testId);
}
this.loading = getProjectFiles(this.loadType, this.projectId, this.currentPage, this.pageSize, this.condition)
.then(res => {
let data = res.data;
@ -176,6 +203,7 @@ export default {
handleImport(file) {
if (file) { //
this.selectIds.add(file.id);
this.selectFiles.push(file)
this.getJmxContents();
return;
}
@ -183,11 +211,9 @@ export default {
this.loadFileVisible = false;
return;
}
let rows = this.existFiles.filter(f => this.selectIds.has(f.id));
let jmxIds = [];
for (let i = 0; i < rows.length; i++) {
let row = rows[i];
for (let i = 0; i < this.selectFiles.length; i++) {
let row = this.selectFiles[i];
if (this.tableData.filter(f => f.name === row.name).length > 0) {
setTimeout(() => {
this.$warning(this.$t('load_test.delete_file') + 'name: ' + row.name);

View File

@ -110,6 +110,7 @@
<exist-files ref="existFiles"
@fileChange="fileChange"
:test-id="test.id"
:file-list="fileList"
:table-data="tableData"
:upload-list="uploadList"

View File

@ -6,7 +6,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.5</version>
<version>2.7.7</version>
<relativePath/>
</parent>
@ -21,7 +21,6 @@
<revision>main</revision>
<java.version>11</java.version>
<spring-cloud.version>2021.0.5</spring-cloud.version>
<spring-security.version>5.7.5</spring-security.version>
<dubbo.version>2.7.18</dubbo.version>
<platform-plugin-sdk.version>1.2.0</platform-plugin-sdk.version>
<flyway.version>7.15.0</flyway.version>

View File

@ -11,10 +11,12 @@ import io.metersphere.commons.utils.FileUtils;
import io.metersphere.commons.utils.LogUtil;
import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.dto.PluginResourceDTO;
import io.metersphere.plugin.core.api.UiScriptApi;
import io.metersphere.plugin.core.ui.PluginResource;
import io.metersphere.utils.CommonUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.jorphan.reflect.ClassFinder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
@ -84,7 +86,29 @@ public class ApiPluginService {
private List<PluginResourceDTO> getMethod(String path, String fileName) {
List<PluginResourceDTO> resources = new LinkedList<>();
this.loadJar(path);
try {
this.loadJar(path);
String jarPath[] = new String[]{path};
List<String> classes = ClassFinder.findClassesThatExtend(jarPath, new Class[]{UiScriptApi.class}, true);
for (String clazzName : classes) {
UiScriptApi uiScriptApi = Class.forName(clazzName)
.asSubclass(UiScriptApi.class)
.getDeclaredConstructor().newInstance();
PluginResource pluginObj = uiScriptApi.init();
if (pluginObj != null) {
PluginResourceDTO pluginResourceDTO = new PluginResourceDTO();
BeanUtils.copyBean(pluginResourceDTO, pluginObj);
pluginResourceDTO.setEntry(clazzName);
resources.add(pluginResourceDTO);
}
}
} catch (Exception e) {
this.init(fileName, resources);
}
return resources;
}
private void init(String fileName, List<PluginResourceDTO> resources) {
List<Class<?>> classes = CommonUtil.getSubClass(fileName);
try {
for (Class<?> aClass : classes) {
@ -101,7 +125,6 @@ public class ApiPluginService {
LogUtil.error("初始化脚本异常:" + e.getMessage());
MSException.throwException("调用插件初始化脚本失败");
}
return resources;
}
private void create(PluginResourceDTO resource, String path, String name, List<PluginWithBLOBs> addPlugins) {

View File

@ -0,0 +1,10 @@
package io.metersphere.base.mapper.ext;
import io.metersphere.plan.dto.TestPlanApiCaseInfoDTO;
import java.util.List;
public interface ExtTestPlanApiCaseMapper {
List<TestPlanApiCaseInfoDTO> selectLegalDataByTestPlanId(String planId);
}

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="io.metersphere.base.mapper.ext.ExtTestPlanApiCaseMapper">
<select id="selectLegalDataByTestPlanId" resultType="io.metersphere.plan.dto.TestPlanApiCaseInfoDTO">
SELECT a.project_id, t.*
FROM test_plan_api_case t
INNER JOIN api_test_case a ON t.api_case_id = a.id
WHERE t.test_plan_id = #{0}
AND (a.status IS NULL OR a.status != 'Trash')
ORDER BY t.`order` DESC
</select>
</mapper>

View File

@ -0,0 +1,9 @@
package io.metersphere.base.mapper.ext;
import io.metersphere.plan.dto.TestPlanApiScenarioInfoDTO;
import java.util.List;
public interface ExtTestPlanScenarioCaseMapper {
List<TestPlanApiScenarioInfoDTO> selectLegalDataByTestPlanId(String planId);
}

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="io.metersphere.base.mapper.ext.ExtTestPlanScenarioCaseMapper">
<select id="selectLegalDataByTestPlanId" resultType="io.metersphere.plan.dto.TestPlanApiScenarioInfoDTO">
SELECT tpas.id,
tpas.api_scenario_id,
tpas.environment,
tpas.environment_type,
tpas.environment_group_id,
apis.project_id
FROM test_plan_api_scenario tpas
INNER JOIN api_scenario apis ON tpas.api_scenario_id = apis.id
WHERE (apis.`status` IS NULL OR apis.`status` != 'Trash')
AND tpas.test_plan_id = #{0}
ORDER BY tpas.`order` DESC;
</select>
</mapper>

View File

@ -3,20 +3,21 @@ package io.metersphere.controller;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import io.metersphere.base.domain.TestPlanReport;
import io.metersphere.base.domain.TestPlanReportContentWithBLOBs;
import io.metersphere.commons.constants.NoticeConstants;
import io.metersphere.commons.constants.OperLogConstants;
import io.metersphere.commons.constants.OperLogModule;
import io.metersphere.commons.utils.PageUtils;
import io.metersphere.commons.utils.Pager;
import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.log.annotation.MsAuditLog;
import io.metersphere.notice.annotation.SendNotice;
import io.metersphere.dto.TestPlanReportDTO;
import io.metersphere.dto.TestPlanScheduleReportInfoDTO;
import io.metersphere.log.annotation.MsAuditLog;
import io.metersphere.notice.annotation.SendNotice;
import io.metersphere.plan.dto.TestPlanSimpleReportDTO;
import io.metersphere.plan.request.TestPlanReportSaveRequest;
import io.metersphere.request.report.QueryTestPlanReportRequest;
import io.metersphere.plan.service.TestPlanReportService;
import io.metersphere.request.report.QueryTestPlanReportRequest;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
@ -86,4 +87,9 @@ public class TestPlanReportController {
public void reName(@RequestBody TestPlanReport request) {
testPlanReportService.reName(request.getId(), request.getName());
}
@PostMapping("/edit/report")
public void editReport(@RequestBody TestPlanReportContentWithBLOBs reportContentWithBLOBs) {
testPlanReportService.editReport(reportContentWithBLOBs);
}
}

View File

@ -25,6 +25,8 @@ public class BatchRunDefinitionRequest {
private String planReportId;
// 失败重跑
private boolean rerun;
private Map<String, ApiDefinitionExecResultWithBLOBs> executeQueue;
private String testPlanId;
}

View File

@ -53,4 +53,6 @@ public class RunScenarioRequest {
private String serialReportId;
// private Map<String, ApiScenarioReportResult> reportMap;
private String testPlanId;
}

View File

@ -4,14 +4,12 @@ package io.metersphere.plan.service;
import com.fasterxml.jackson.core.JsonProcessingException;
import io.metersphere.base.domain.*;
import io.metersphere.base.mapper.*;
import io.metersphere.base.mapper.ext.ExtTestPlanMapper;
import io.metersphere.base.mapper.ext.ExtTestPlanReportContentMapper;
import io.metersphere.base.mapper.ext.ExtTestPlanReportMapper;
import io.metersphere.base.mapper.ext.ExtTestPlanTestCaseMapper;
import io.metersphere.base.mapper.ext.*;
import io.metersphere.commons.constants.*;
import io.metersphere.commons.utils.*;
import io.metersphere.constants.RunModeConstants;
import io.metersphere.dto.*;
import io.metersphere.environment.service.BaseEnvGroupProjectService;
import io.metersphere.excel.constants.TestPlanTestCaseStatus;
import io.metersphere.log.vo.OperatingLogDetails;
import io.metersphere.plan.constant.ApiReportStatus;
@ -80,8 +78,12 @@ public class TestPlanReportService {
private TestPlanPrincipalMapper testPlanPrincipalMapper;
@Resource
ExtTestPlanTestCaseMapper extTestPlanTestCaseMapper;
// @Resource
// private PerformanceTestService performanceTestService;
@Resource
private ExtTestPlanApiCaseMapper extTestPlanApiCaseMapper;
@Resource
private ExtTestPlanScenarioCaseMapper extTestPlanScenarioCaseMapper;
@Resource
private BaseEnvGroupProjectService baseEnvGroupProjectService;
@Resource
private PlanApiDefinitionExecResultService planApiDefinitionExecResultService;
@Resource
@ -103,6 +105,8 @@ public class TestPlanReportService {
@Resource
private BaseUserService baseUserService;
private final String GROUP = "GROUP";
public List<TestPlanReportDTO> list(QueryTestPlanReportRequest request) {
List<TestPlanReportDTO> list = new ArrayList<>();
request.setOrders(ServiceUtils.getDefaultOrder(request.getOrders()));
@ -231,6 +235,78 @@ public class TestPlanReportService {
}
}
public TestPlanApiReportInfoDTO genApiReportInfoForSchedule(String planId, RunModeConfigDTO runModeConfigDTO) {
TestPlanApiReportInfoDTO testPlanApiReportInfo = new TestPlanApiReportInfoDTO();
Map<String, String> planApiCaseIdMap = new LinkedHashMap<>();
Map<String, String> planScenarioIdMap = new LinkedHashMap<>();
List<TestPlanApiScenarioInfoDTO> testPlanApiScenarioList = extTestPlanScenarioCaseMapper.selectLegalDataByTestPlanId(planId);
for (TestPlanApiScenarioInfoDTO model : testPlanApiScenarioList) {
planScenarioIdMap.put(model.getId(), model.getApiScenarioId());
}
List<TestPlanApiCaseInfoDTO> testPlanApiCaseList = extTestPlanApiCaseMapper.selectLegalDataByTestPlanId(planId);
for (TestPlanApiCaseInfoDTO model : testPlanApiCaseList) {
planApiCaseIdMap.put(model.getId(), model.getApiCaseId());
}
testPlanApiReportInfo.setPlanApiCaseIdMap(planApiCaseIdMap);
testPlanApiReportInfo.setPlanScenarioIdMap(planScenarioIdMap);
//解析运行环境信息
TestPlanReportRunInfoDTO runInfoDTO = this.parseTestPlanRunInfo(runModeConfigDTO, testPlanApiCaseList, testPlanApiScenarioList);
testPlanApiReportInfo.setRunInfoDTO(runInfoDTO);
return testPlanApiReportInfo;
}
public TestPlanReportRunInfoDTO parseTestPlanRunInfo(
RunModeConfigDTO config,
List<TestPlanApiCaseInfoDTO> cases,
List<TestPlanApiScenarioInfoDTO> scenarios) {
TestPlanReportRunInfoDTO runInfoDTO = new TestPlanReportRunInfoDTO();
final Map<String, String> runEnvMap = config.getEnvMap();
runInfoDTO.setRunMode(config.getMode());
if (StringUtils.equals(GROUP, config.getEnvironmentType()) && StringUtils.isNotEmpty(config.getEnvironmentGroupId())) {
runEnvMap.putAll(baseEnvGroupProjectService.getEnvMap(config.getEnvironmentGroupId()));
runInfoDTO.setEnvGroupId(config.getEnvironmentGroupId());
}
// 场景环境处理
scenarios.forEach(item -> {
Map<String, String> envMap = null;
if (StringUtils.equalsIgnoreCase(GROUP, item.getEnvironmentType())
&& StringUtils.isNotEmpty(item.getEnvironmentGroupId())) {
envMap = baseEnvGroupProjectService.getEnvMap(item.getEnvironmentGroupId());
} else {
if (MapUtils.isNotEmpty(runEnvMap) && runEnvMap.containsKey(item.getProjectId())) {
runInfoDTO.putScenarioRunInfo(item.getId(), item.getProjectId(), runEnvMap.get(item.getProjectId()));
} else if (StringUtils.isNotEmpty(item.getEnvironment())) {
try {
envMap = JSON.parseObject(item.getEnvironment(), Map.class);
} catch (Exception e) {
LogUtil.error("解析场景环境失败!", e);
}
}
}
if (MapUtils.isNotEmpty(envMap)) {
for (Map.Entry<String, String> entry : envMap.entrySet()) {
String projectId = entry.getKey();
String envIdStr = entry.getValue();
runInfoDTO.putScenarioRunInfo(item.getId(), projectId, envIdStr);
}
}
});
// 用例环境处理
cases.forEach(item -> {
if (MapUtils.isNotEmpty(runEnvMap) && runEnvMap.containsKey(item.getProjectId())) {
runInfoDTO.putApiCaseRunInfo(item.getId(), item.getProjectId(), runEnvMap.get(item.getProjectId()));
} else {
runInfoDTO.putApiCaseRunInfo(item.getId(), item.getProjectId(), item.getEnvironmentId());
}
});
return runInfoDTO;
}
public TestPlanScheduleReportInfoDTO genTestPlanReportBySchedule(String planReportId, String planId, String userId, String triggerMode, RunModeConfigDTO runModeConfigDTO) {
TestPlanReport testPlanReport = this.getTestPlanReport(planReportId);
TestPlanScheduleReportInfoDTO returnDTO = new TestPlanScheduleReportInfoDTO();
@ -250,7 +326,8 @@ public class TestPlanReportService {
Set<String> serviceIdSet = DiscoveryUtil.getServiceIdSet();
if (serviceIdSet.contains(MicroServiceName.API_TEST)) {
TestPlanApiReportInfoDTO testPlanApiReportInfoDTO = planTestPlanScenarioCaseService.genApiReportInfoForSchedule(planId, runModeConfigDTO);
// 这里不再调用API服务生成相关数据否则当关联用例/场景量大时会超时
TestPlanApiReportInfoDTO testPlanApiReportInfoDTO = this.genApiReportInfoForSchedule(planId, runModeConfigDTO);
Map<String, String> planScenarioIdMap = testPlanApiReportInfoDTO.getPlanScenarioIdMap();
Map<String, String> planApiCaseIdMap = testPlanApiReportInfoDTO.getPlanApiCaseIdMap();
runInfoDTO = testPlanApiReportInfoDTO.getRunInfoDTO();
@ -1496,4 +1573,12 @@ public class TestPlanReportService {
testPlanReportDTO.setName(testPlanReport.getName());
return testPlanReportDTO;
}
public void editReport(TestPlanReportContentWithBLOBs reportContentWithBLOBs) {
if (StringUtils.isNotBlank(reportContentWithBLOBs.getTestPlanReportId())) {
TestPlanReportContentExample example = new TestPlanReportContentExample();
example.createCriteria().andTestPlanReportIdEqualTo(reportContentWithBLOBs.getTestPlanReportId());
testPlanReportContentMapper.updateByExampleSelective(reportContentWithBLOBs, example);
}
}
}

View File

@ -155,12 +155,7 @@ public class TestPlanService {
@Resource
private TestResourcePoolMapper testResourcePoolMapper;
@Resource
private SystemParameterService systemParameterService;
@Resource
private BaseProjectApplicationService projectApplicationService;
@Resource
private TestPlanReportMapper testPlanReportMapper;
public static final String POOL = "POOL";
public synchronized TestPlan addTestPlan(AddTestPlanRequest testPlan) {
if (getTestPlanByName(testPlan.getName()).size() > 0) {
@ -792,23 +787,26 @@ public class TestPlanService {
RunScenarioRequest request = new RunScenarioRequest();
request.setReportId(planScenarioExecuteRequest.getReportId());
request.setTestPlanId(entry.getKey());
request.setEnvironmentId(planScenarioExecuteRequest.getEnvironmentId());
request.setTriggerMode(planScenarioExecuteRequest.getTriggerMode());
request.setExecuteType(planScenarioExecuteRequest.getExecuteType());
request.setRunMode(planScenarioExecuteRequest.getRunMode());
request.setIds(new ArrayList<>(scenarioMap.values()));//场景IDS
request.setReportUserID(planScenarioExecuteRequest.getReportUserID());
request.setScenarioTestPlanIdMap(scenarioMap);//未知
request.setConfig(planScenarioExecuteRequest.getConfig());
request.setTestPlanScheduleJob(true);
request.setTestPlanReportId(planScenarioExecuteRequest.getTestPlanReportId());
request.setId(UUID.randomUUID().toString());
request.setProjectId(planScenarioExecuteRequest.getProjectId());
request.setRequestOriginator("TEST_PLAN");
request.setPlanCaseIds(new ArrayList<>(testPlanScenarioIdMap.keySet()));
if ("api".equalsIgnoreCase(planScenarioExecuteRequest.getType())) {
list.addAll(planApiAutomationService.run(request));
} else {
// 以下三个参数接口测试未使用到暂时迁移到这里
request.setIds(new ArrayList<>(scenarioMap.values()));//场景IDS
request.setScenarioTestPlanIdMap(scenarioMap);//未知
request.setPlanCaseIds(new ArrayList<>(testPlanScenarioIdMap.keySet()));
RunUiScenarioRequest runUiScenarioRequest = new RunUiScenarioRequest();
BeanUtils.copyBean(runUiScenarioRequest, request);
RunModeConfigDTO configDTO = new RunModeConfigDTO();
@ -887,7 +885,7 @@ public class TestPlanService {
if (MapUtils.isNotEmpty(reportInfoDTO.getApiTestCaseDataMap())) {
//执行接口案例任务
LoggerUtil.info("开始执行测试计划接口用例 " + planReportId);
apiCaseReportMap = this.executeApiTestCase(triggerMode, planReportId, userId, new ArrayList<>(reportInfoDTO.getApiTestCaseDataMap().keySet()), runModeConfig);
apiCaseReportMap = this.executeApiTestCase(triggerMode, planReportId, userId, testPlanId, runModeConfig);
}
if (MapUtils.isNotEmpty(reportInfoDTO.getPlanScenarioIdMap())) {
//执行场景执行任务
@ -946,13 +944,13 @@ public class TestPlanService {
return runModeConfig;
}
private Map<String, String> executeApiTestCase(String triggerMode, String planReportId, String userId, List<String> planCaseIds, RunModeConfigDTO runModeConfig) {
private Map<String, String> executeApiTestCase(String triggerMode, String planReportId, String userId, String testPlanId, RunModeConfigDTO runModeConfig) {
BatchRunDefinitionRequest request = new BatchRunDefinitionRequest();
request.setTriggerMode(triggerMode);
request.setPlanIds(planCaseIds);
request.setPlanReportId(planReportId);
request.setConfig(runModeConfig);
request.setUserId(userId);
request.setTestPlanId(testPlanId);
List<MsExecResponseDTO> dtoList = planTestPlanApiCaseService.run(request);
return this.parseMsExecResponseDTOToTestIdReportMap(dtoList);
}

View File

@ -1,11 +1,13 @@
package io.metersphere.plan.service.remote.api;
import io.metersphere.base.domain.ApiScenarioWithBLOBs;
import io.metersphere.commons.utils.LogUtil;
import io.metersphere.dto.MsExecResponseDTO;
import io.metersphere.plan.request.api.RunScenarioRequest;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.PathVariable;
import java.util.ArrayList;
import java.util.List;
@Service
@ -15,7 +17,12 @@ public class PlanApiAutomationService extends ApiTestService {
public List<MsExecResponseDTO> run(RunScenarioRequest request) {
return microService.postForDataArray(serviceName, BASE_UEL + "/plan/run", request, MsExecResponseDTO.class);
try {
return microService.postForDataArray(serviceName, BASE_UEL + "/plan/run", request, MsExecResponseDTO.class);
} catch (Exception e) {
LogUtil.error("调用API服务执行场景用例失败", e);
return new ArrayList<>();
}
}
public ApiScenarioWithBLOBs get(@PathVariable String id) {

View File

@ -93,7 +93,12 @@ public class PlanTestPlanApiCaseService extends ApiTestService {
}
public List<MsExecResponseDTO> run(BatchRunDefinitionRequest request) {
return microService.postForDataArray(serviceName, BASE_UEL + "/run", request, MsExecResponseDTO.class);
try {
return microService.postForDataArray(serviceName, BASE_UEL + "/run", request, MsExecResponseDTO.class);
} catch (Exception e) {
LogUtil.info("调用API服务执行用例失败", e);
return new ArrayList<>();
}
}
public RunModeConfigDTO setApiCaseEnv(String planId, RunModeConfigDTO runModeConfig) {

View File

@ -155,10 +155,6 @@ public class PlanTestPlanScenarioCaseService extends ApiTestService {
return microService.postForData(serviceName, BASE_UEL + "/plan/execute/report", request, ApiPlanReportDTO.class);
}
public TestPlanApiReportInfoDTO genApiReportInfoForSchedule(String planId, RunModeConfigDTO runModeConfigDTO) {
return microService.postForData(serviceName, BASE_UEL + "/plan/report/schedule/info/" + planId, runModeConfigDTO, TestPlanApiReportInfoDTO.class);
}
public Boolean isCaseExecuting(String planId) {
return microService.getForData(serviceName, BASE_UEL + "/is/executing/" + planId, Boolean.class);
}

View File

@ -22,3 +22,7 @@ export function testPlanReportReName(param) {
export function testPlanReportGetDb(id) {
return get(BASE_URL + `db/${id}`);
}
export function testPlanDbReportEdit(param) {
return post(BASE_URL + 'edit/report', param);
}

View File

@ -16,6 +16,9 @@
<ms-table :data="tableData" :select-node-ids="selectNodeIds" :condition="condition" :page-size="pageSize"
:total="total" enableSelection
:screenHeight="screenHeight"
row-key="id"
:reserve-option="true"
:page-refresh="pageRefresh"
@refresh="initTable"
@selectCountChange="selectCountChange"
operator-width="170px"
@ -89,7 +92,7 @@
</template>
</ms-table-column>
</ms-table>
<ms-table-pagination :change="initTable" :current-page.sync="currentPage" :page-size.sync="pageSize"
<ms-table-pagination :change="pageChange" :current-page.sync="currentPage" :page-size.sync="pageSize"
:total="total"/>
</api-list-container>
@ -157,6 +160,7 @@ export default {
total: 0,
environmentId: "",
versionEnable: false,
pageRefresh: false
};
},
props: {
@ -220,7 +224,11 @@ export default {
selectCountChange(data) {
this.$emit('selectCountChange', data);
},
initTable(projectId) {
pageChange() {
this.initTable(null, "page");
},
initTable(projectId, data) {
this.pageRefresh = data === "page";
this.condition.status = "";
this.condition.moduleIds = this.selectNodeIds;
if (projectId != null && typeof projectId === 'string') {

View File

@ -1,10 +1,10 @@
<template>
<test-plan-report-container id='summary' :title="$t('test_track.report.report_summary')">
<template v-slot:title>
<el-link class="edit-link" v-if="!isTemplate && !isShare && !isDb && !isEdit" @click="isEdit = true">
<el-link class="edit-link" v-if="showEdit" @click="isEdit = true">
<i class="el-icon-edit">{{ $t('commons.edit') }}</i>
</el-link>
<el-link class="edit-link" v-if="!isTemplate && !isShare && !isDb && isEdit" @click="saveSummary">
<el-link class="edit-link" v-if="showEdit" @click="saveSummary">
<i class="el-icon-circle-check">{{ $t('commons.save') }}</i>
</el-link>
</template>
@ -25,6 +25,7 @@ import MsFormDivider from "metersphere-frontend/src/components/MsFormDivider";
import {editPlanReport} from "@/api/remote/plan/test-plan";
import TestPlanReportContainer from "@/business/plan/view/comonents/report/detail/TestPlanReportContainer";
import MsRichText from "@/business/case/components/MsRichText";
import {testPlanDbReportEdit} from "@/api/remote/plan/test-plan-report";
export default {
name: "TestPlanSummaryReport",
components: {MsRichText, TestPlanReportContainer, MsFormDivider},
@ -41,12 +42,24 @@ export default {
isEdit: false
}
},
computed: {
showEdit() {
return !this.isTemplate && !this.isShare && !this.isEdit;
}
},
methods: {
saveSummary() {
editPlanReport({
id: this.planId,
reportSummary: this.report.summary ? this.report.summary : ''
});
if (this.isDb) {
testPlanDbReportEdit({
testPlanReportId: this.report.id,
summary: this.report.summary || ''
});
} else {
editPlanReport({
id: this.planId,
reportSummary: this.report.summary || ''
});
}
this.isEdit = false;
}
}