diff --git a/backend/src/main/java/io/metersphere/api/controller/ApiAutomationController.java b/backend/src/main/java/io/metersphere/api/controller/ApiAutomationController.java index 3a824bc6c1..586e45523a 100644 --- a/backend/src/main/java/io/metersphere/api/controller/ApiAutomationController.java +++ b/backend/src/main/java/io/metersphere/api/controller/ApiAutomationController.java @@ -11,6 +11,7 @@ import io.metersphere.api.service.ApiAutomationService; import io.metersphere.base.domain.ApiScenario; import io.metersphere.base.domain.ApiScenarioWithBLOBs; import io.metersphere.base.domain.Schedule; +import io.metersphere.commons.constants.ApiRunMode; import io.metersphere.commons.constants.RoleConstants; import io.metersphere.commons.utils.PageUtils; import io.metersphere.commons.utils.Pager; @@ -102,12 +103,16 @@ public class ApiAutomationController { @PostMapping(value = "/run") public String run(@RequestBody RunScenarioRequest request) { request.setExecuteType(ExecuteType.Completed.name()); + request.setTriggerMode(ApiRunMode.SCENARIO.name()); + request.setRunMode(ApiRunMode.SCENARIO.name()); return apiAutomationService.run(request); } @PostMapping(value = "/run/batch") public String runBatch(@RequestBody RunScenarioRequest request) { request.setExecuteType(ExecuteType.Saved.name()); + request.setTriggerMode(ApiRunMode.SCENARIO.name()); + request.setRunMode(ApiRunMode.SCENARIO.name()); return apiAutomationService.run(request); } diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertions.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertions.java index af6754e309..1dfbe65be0 100644 --- a/backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertions.java +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertions.java @@ -96,7 +96,7 @@ public class MsAssertions extends MsTestElement { assertion.setJsonValidationBool(true); assertion.setExpectNull(false); assertion.setInvert(false); - assertion.setIsRegex(false); + assertion.setIsRegex(true); return assertion; } diff --git a/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java b/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java index af33dbf273..2084a7eb85 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java @@ -157,17 +157,6 @@ public class ApiAutomationService { return request; } - - public List selectIdsNotExistsInPlan(String projectId, String planId) { - return extApiScenarioMapper.selectIdsNotExistsInPlan(projectId, planId); - } - - public void deleteByIds(List nodeIds) { - ApiScenarioExample example = new ApiScenarioExample(); - example.createCriteria().andApiScenarioModuleIdIn(nodeIds); - apiScenarioMapper.deleteByExample(example); - } - public void removeToGcByIds(List nodeIds) { ApiScenarioExample example = new ApiScenarioExample(); example.createCriteria().andApiScenarioModuleIdIn(nodeIds); @@ -347,7 +336,7 @@ public class ApiAutomationService { } public byte[] loadFileAsBytes(FileOperationRequest fileOperationRequest) { - File file = new File("/opt/metersphere/data/body/" + fileOperationRequest.getId() + "_" + fileOperationRequest.getName()); + File file = new File(FileUtils.BODY_FILE_DIR + fileOperationRequest.getId() + "_" + fileOperationRequest.getName()); try (FileInputStream fis = new FileInputStream(file); ByteArrayOutputStream bos = new ByteArrayOutputStream(1000);) { byte[] b = new byte[1000]; @@ -362,7 +351,7 @@ public class ApiAutomationService { return null; } - public void createScenarioReport(String id, String scenarioId, String scenarioName, String triggerMode, String execType, String projectId, String userID) { + public APIScenarioReportResult createScenarioReport(String id, String scenarioId, String scenarioName, String triggerMode, String execType, String projectId, String userID) { APIScenarioReportResult report = new APIScenarioReportResult(); report.setId(id); report.setTestId(id); @@ -384,87 +373,54 @@ public class ApiAutomationService { report.setProjectId(projectId); report.setScenarioName(scenarioName); report.setScenarioId(scenarioId); - apiScenarioReportMapper.insert(report); + return report; } - /** - * 生成HashTree - * - * @param apiScenarios 场景 - * @param request 请求参数 - * @param reportIds 报告ID - * @return hashTree - */ - private HashTree generateHashTree(List apiScenarios, RunScenarioRequest request, List reportIds) { - HashTree jmeterHashTree = new ListedHashTree(); + private void parse(String scenarioDefinition, MsScenario scenario) { + ObjectMapper mapper = new ObjectMapper(); + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + try { + JSONObject element = JSON.parseObject(scenarioDefinition); + // 多态JSON普通转换会丢失内容,需要通过 ObjectMapper 获取 + if (element != null && StringUtils.isNotEmpty(element.getString("hashTree"))) { + LinkedList elements = mapper.readValue(element.getString("hashTree"), + new TypeReference>() { + }); + scenario.setHashTree(elements); + } + if (element != null && StringUtils.isNotEmpty(element.getString("variables"))) { + LinkedList variables = mapper.readValue(element.getString("variables"), + new TypeReference>() { + }); + scenario.setVariables(variables); + } + } catch (Exception e) { + e.printStackTrace(); + LogUtil.error(e.getMessage()); + } + } + + private HashTree generateHashTree(ApiScenarioWithBLOBs item, String reportId, Map planEnvMap) { + HashTree jmeterHashTree = new HashTree(); MsTestPlan testPlan = new MsTestPlan(); testPlan.setHashTree(new LinkedList<>()); try { - boolean isFirst = true; - for (ApiScenarioWithBLOBs item : apiScenarios) { - if (item.getStepTotal() == null || item.getStepTotal() == 0) { - // 只有一个场景且没有测试步骤,则提示 - if (apiScenarios.size() == 1) { - MSException.throwException((item.getName() + "," + Translator.get("automation_exec_info"))); - } - LogUtil.warn(item.getName() + "," + Translator.get("automation_exec_info")); - continue; - } - MsThreadGroup group = new MsThreadGroup(); - group.setLabel(item.getName()); - group.setName(UUID.randomUUID().toString()); - // 批量执行的结果直接存储为报告 - if (isFirst) { - group.setName(request.getId()); - isFirst = false; - } - ObjectMapper mapper = new ObjectMapper(); - mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - JSONObject element = JSON.parseObject(item.getScenarioDefinition()); - MsScenario scenario = JSONObject.parseObject(item.getScenarioDefinition(), MsScenario.class); + MsThreadGroup group = new MsThreadGroup(); + group.setLabel(item.getName()); + group.setName(reportId); - // 多态JSON普通转换会丢失内容,需要通过 ObjectMapper 获取 - if (element != null && StringUtils.isNotEmpty(element.getString("hashTree"))) { - LinkedList elements = mapper.readValue(element.getString("hashTree"), - new TypeReference>() { - }); - scenario.setHashTree(elements); - } - if (StringUtils.isNotEmpty(element.getString("variables"))) { - LinkedList variables = mapper.readValue(element.getString("variables"), - new TypeReference>() { - }); - scenario.setVariables(variables); - } - group.setEnableCookieShare(scenario.isEnableCookieShare()); - LinkedList scenarios = new LinkedList<>(); - scenarios.add(scenario); - // 创建场景报告 - if (reportIds != null) { - //如果是测试计划页面触发的执行方式,生成报告时createScenarioReport第二个参数需要特殊处理 - if (StringUtils.equals(request.getRunMode(), ApiRunMode.SCENARIO_PLAN.name())) { - String testPlanScenarioId = item.getId(); - if (request.getScenarioTestPlanIdMap() != null && request.getScenarioTestPlanIdMap().containsKey(item.getId())) { - testPlanScenarioId = request.getScenarioTestPlanIdMap().get(item.getId()); - // 获取场景用例单独的执行环境 - TestPlanApiScenario planApiScenario = testPlanApiScenarioMapper.selectByPrimaryKey(testPlanScenarioId); - String environment = planApiScenario.getEnvironment(); - if (StringUtils.isNotBlank(environment)) { - scenario.setEnvironmentMap(JSON.parseObject(environment, Map.class)); - } - } - createScenarioReport(group.getName(), testPlanScenarioId, item.getName(), request.getTriggerMode() == null ? ReportTriggerMode.MANUAL.name() : request.getTriggerMode(), - request.getExecuteType(), item.getProjectId(), request.getReportUserID()); - } else { - createScenarioReport(group.getName(), item.getId(), item.getName(), request.getTriggerMode() == null ? ReportTriggerMode.MANUAL.name() : request.getTriggerMode(), - request.getExecuteType(), item.getProjectId(), request.getReportUserID()); - } - - reportIds.add(group.getName()); - } - group.setHashTree(scenarios); - testPlan.getHashTree().add(group); + MsScenario scenario = JSONObject.parseObject(item.getScenarioDefinition(), MsScenario.class); + if (planEnvMap.size() > 0) { + scenario.setEnvironmentMap(planEnvMap); } + parse(item.getScenarioDefinition(), scenario); + + group.setEnableCookieShare(scenario.isEnableCookieShare()); + LinkedList scenarios = new LinkedList<>(); + scenarios.add(scenario); + + group.setHashTree(scenarios); + testPlan.getHashTree().add(group); } catch (Exception ex) { MSException.throwException(ex.getMessage()); } @@ -482,27 +438,11 @@ public class ApiAutomationService { config.setOperating(true); try { - ObjectMapper mapper = new ObjectMapper(); - mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - JSONObject element = JSON.parseObject(apiScenario.getScenarioDefinition()); MsScenario scenario = JSONObject.parseObject(apiScenario.getScenarioDefinition(), MsScenario.class); - - // 多态JSON普通转换会丢失内容,需要通过 ObjectMapper 获取 - if (element != null && StringUtils.isNotEmpty(element.getString("hashTree"))) { - LinkedList elements = mapper.readValue(element.getString("hashTree"), - new TypeReference>() { - }); - scenario.setHashTree(elements); - } - if (element != null && StringUtils.isNotEmpty(element.getString("variables"))) { - LinkedList variables = mapper.readValue(element.getString("variables"), - new TypeReference>() { - }); - scenario.setVariables(variables); - } if (scenario == null) { return null; } + parse(apiScenario.getScenarioDefinition(), scenario); // 针对导入的jmx 处理 if (CollectionUtils.isNotEmpty(scenario.getHashTree()) && (scenario.getHashTree().get(0) instanceof MsJmeterElement)) { scenario.toHashTree(jmeterHashTree, scenario.getHashTree(), config); @@ -533,26 +473,62 @@ public class ApiAutomationService { * @return */ public String run(RunScenarioRequest request) { - ServiceUtils.getSelectAllIds(request, request.getCondition(), (query) -> extApiScenarioMapper.selectIdsByQuery((ApiScenarioRequest) query)); List ids = request.getIds(); //检查是否有正在执行中的情景 this.checkScenarioIsRunning(ids); - List apiScenarios = extApiScenarioMapper.selectIds(ids); - String runMode = ApiRunMode.SCENARIO.name(); - if (StringUtils.isNotBlank(request.getRunMode()) && StringUtils.equals(request.getRunMode(), ApiRunMode.SCENARIO_PLAN.name())) { - runMode = ApiRunMode.SCENARIO_PLAN.name(); + List apiScenarios = extApiScenarioMapper.selectIds(ids); + // 只有一个场景且没有测试步骤,则提示 + if (apiScenarios != null && apiScenarios.size() == 1 && (apiScenarios.get(0).getStepTotal() == null || apiScenarios.get(0).getStepTotal() == 0)) { + MSException.throwException((apiScenarios.get(0).getName() + "," + Translator.get("automation_exec_info"))); } - if (StringUtils.isNotBlank(request.getRunMode()) && StringUtils.equals(request.getRunMode(), ApiRunMode.DEFINITION.name())) { - runMode = ApiRunMode.DEFINITION.name(); + if (StringUtils.isEmpty(request.getTriggerMode())) { + request.setTriggerMode(ReportTriggerMode.MANUAL.name()); } - // 调用执行方法 - List reportIds = new LinkedList<>(); - HashTree hashTree = generateHashTree(apiScenarios, request, reportIds); - jMeterService.runDefinition(JSON.toJSONString(reportIds), hashTree, request.getReportId(), runMode); + SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH); + ApiScenarioReportMapper batchMapper = sqlSession.getMapper(ApiScenarioReportMapper.class); + String reportId = request.getId(); + // 按照场景执行 + for (ApiScenarioWithBLOBs item : apiScenarios) { + if (item.getStepTotal() == null || item.getStepTotal() == 0) { + continue; + } + APIScenarioReportResult report; + Map planEnvMap = new HashMap<>(); + //如果是测试计划页面触发的执行方式,生成报告时createScenarioReport第二个参数需要特殊处理 + if (StringUtils.equals(request.getRunMode(), ApiRunMode.SCENARIO_PLAN.name())) { + String testPlanScenarioId = item.getId(); + if (request.getScenarioTestPlanIdMap() != null && request.getScenarioTestPlanIdMap().containsKey(item.getId())) { + testPlanScenarioId = request.getScenarioTestPlanIdMap().get(item.getId()); + // 获取场景用例单独的执行环境 + TestPlanApiScenario planApiScenario = testPlanApiScenarioMapper.selectByPrimaryKey(testPlanScenarioId); + String environment = planApiScenario.getEnvironment(); + if (StringUtils.isNotBlank(environment)) { + planEnvMap = JSON.parseObject(environment, Map.class); + } + } + report = createScenarioReport(reportId, testPlanScenarioId, item.getName(), request.getTriggerMode(), + request.getExecuteType(), item.getProjectId(), request.getReportUserID()); + } else { + report = createScenarioReport(reportId, item.getId(), item.getName(), request.getTriggerMode(), + request.getExecuteType(), item.getProjectId(), request.getReportUserID()); + } + + // 生成报告和HashTree + HashTree hashTree = generateHashTree(item, reportId, planEnvMap); + + //存储报告 + batchMapper.insert(report); + + // 调用执行方法 + jMeterService.runDefinition(report.getId(), hashTree, request.getReportId(), request.getRunMode()); + // 重置报告ID + reportId = UUID.randomUUID().toString(); + } + sqlSession.flushStatements(); return request.getId(); } @@ -598,7 +574,7 @@ public class ApiAutomationService { public String debugRun(RunDefinitionRequest request, List bodyFiles) { List bodyUploadIds = new ArrayList<>(request.getBodyUploadIds()); FileUtils.createBodyFiles(bodyUploadIds, bodyFiles); - Map envConfig = new HashMap<>(); + Map envConfig = new HashMap<>(); Map map = request.getEnvironmentMap(); if (map != null) { map.keySet().forEach(id -> { @@ -611,8 +587,9 @@ public class ApiAutomationService { config.setConfig(envConfig); HashTree hashTree = request.getTestElement().generateHashTree(config); // 调用执行方法 - createScenarioReport(request.getId(), request.getScenarioId(), request.getScenarioName(), ReportTriggerMode.MANUAL.name(), request.getExecuteType(), request.getProjectId(), + APIScenarioReportResult reportResult = createScenarioReport(request.getId(), request.getScenarioId(), request.getScenarioName(), ReportTriggerMode.MANUAL.name(), request.getExecuteType(), request.getProjectId(), SessionUtils.getUserId()); + apiScenarioReportMapper.insert(reportResult); // 调用执行方法 jMeterService.runDefinition(request.getId(), hashTree, request.getReportId(), ApiRunMode.SCENARIO.name()); return request.getId(); @@ -761,25 +738,14 @@ public class ApiAutomationService { if (!apiScenarios.isEmpty()) { testName = apiScenarios.get(0).getName(); } + if (CollectionUtils.isEmpty(apiScenarios)) { + return null; + } MsTestPlan testPlan = new MsTestPlan(); testPlan.setHashTree(new LinkedList<>()); - - HashTree jmeterHashTree = generateHashTree(apiScenarios, request, null); - String jmx = testPlan.getJmx(jmeterHashTree); - - jmx = apiTestService.updateJmxString(jmx, testName, true); - - //将ThreadGroup的testname改为接口名称 -// Document doc = DocumentHelper.parseText(jmx);// 获取可续保保单列表报文模板 -// Element root = doc.getRootElement(); -// Element rootHashTreeElement = root.element("hashTree"); -// Element innerHashTreeElement = rootHashTreeElement.elements("hashTree").get(0); -// Element theadGroupElement = innerHashTreeElement.elements("ThreadGroup").get(0); -// theadGroupElement.attribute("testname").setText(testName); -// jmx = root.asXML(); + String jmx = apiTestService.updateJmxString(generateJmx(apiScenarios.get(0)), testName, true); String name = request.getName() + ".jmx"; - JmxInfoDTO dto = new JmxInfoDTO(); dto.setName(name); dto.setXml(jmx); @@ -943,7 +909,9 @@ public class ApiAutomationService { Map envMap = request.getEnvMap(); Map> mapping = request.getMapping(); Set set = mapping.keySet(); - if (set.isEmpty()) { return; } + if (set.isEmpty()) { + return; + } set.forEach(id -> { Map newEnvMap = new HashMap<>(16); if (envMap != null && !envMap.isEmpty()) { diff --git a/backend/src/main/java/io/metersphere/api/service/ApiScenarioReportService.java b/backend/src/main/java/io/metersphere/api/service/ApiScenarioReportService.java index 7469f8a987..adb9a5f556 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiScenarioReportService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiScenarioReportService.java @@ -152,7 +152,7 @@ public class ApiScenarioReportService { apiScenarioReportDetailMapper.insert(detail); TestPlanApiScenario testPlanApiScenario = testPlanApiScenarioMapper.selectByPrimaryKey(report.getScenarioId()); - if(testPlanApiScenario!=null){ + if (testPlanApiScenario != null) { report.setScenarioId(testPlanApiScenario.getApiScenarioId()); apiScenarioReportMapper.updateByPrimaryKeySelective(report); if (scenarioResult.getError() > 0) { @@ -236,17 +236,12 @@ public class ApiScenarioReportService { /** * 批量更新状态,防止重复刷新报告 * - * @param reportIds + * @param reportId */ - private void updateScenarioStatus(String reportIds) { - - if (StringUtils.isNotEmpty(reportIds)) { + private void updateScenarioStatus(String reportId) { + if (StringUtils.isNotEmpty(reportId)) { List list = new ArrayList<>(); - try { - list = JSON.parseArray(reportIds, String.class); - } catch (Exception e) { - list.add(reportIds); - } + list.add(reportId); ApiScenarioReportExample scenarioReportExample = new ApiScenarioReportExample(); scenarioReportExample.createCriteria().andIdIn(list); List reportList = apiScenarioReportMapper.selectByExample(scenarioReportExample); @@ -350,13 +345,13 @@ public class ApiScenarioReportService { int handleCount = 7000; //每次处理的集合 List handleIdList = new ArrayList<>(handleCount); - while (ids.size() > handleCount){ + while (ids.size() > handleCount) { handleIdList = new ArrayList<>(handleCount); List otherIdList = new ArrayList<>(); - for (int index = 0;index < ids.size();index++){ - if(index selectLastReportByIds(List ids) { - if(!ids.isEmpty()){ + if (!ids.isEmpty()) { return extApiScenarioReportMapper.selectLastReportByIds(ids); - }else { + } else { return new ArrayList<>(0); } } diff --git a/backend/src/main/java/io/metersphere/xpack b/backend/src/main/java/io/metersphere/xpack index 3f497f88eb..d2fc4b4211 160000 --- a/backend/src/main/java/io/metersphere/xpack +++ b/backend/src/main/java/io/metersphere/xpack @@ -1 +1 @@ -Subproject commit 3f497f88ebbd312a3b7637c1b694a8e28c68c287 +Subproject commit d2fc4b42117be97c679b4d15d6f979923e598f7f diff --git a/frontend/src/business/components/xpack b/frontend/src/business/components/xpack index 62ca85d34f..360d7214d1 160000 --- a/frontend/src/business/components/xpack +++ b/frontend/src/business/components/xpack @@ -1 +1 @@ -Subproject commit 62ca85d34fdb89663cce69c9c694cf368e7bb3e6 +Subproject commit 360d7214d15951ae11b3973add795305a5c3d035 diff --git a/frontend/src/login/Login.vue b/frontend/src/login/Login.vue index 6dec041ed8..6bf4035b18 100644 --- a/frontend/src/login/Login.vue +++ b/frontend/src/login/Login.vue @@ -187,7 +187,7 @@ export default {