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/MsScenario.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/MsScenario.java index 6303166c99..642b71b976 100644 --- a/backend/src/main/java/io/metersphere/api/dto/definition/request/MsScenario.java +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/MsScenario.java @@ -85,6 +85,7 @@ public class MsScenario extends MsTestElement { JSONObject element = JSON.parseObject(scenario.getScenarioDefinition()); hashTree = mapper.readValue(element.getString("hashTree"), new TypeReference>() { }); + OldVersionUtil.transferHashTree(hashTree); // 场景变量 if (StringUtils.isNotEmpty(element.getString("variables"))) { LinkedList variables = mapper.readValue(element.getString("variables"), diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/MsTestElement.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/MsTestElement.java index 182dbf5512..8238f27752 100644 --- a/backend/src/main/java/io/metersphere/api/dto/definition/request/MsTestElement.java +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/MsTestElement.java @@ -161,6 +161,7 @@ public abstract class MsTestElement { element = mapper.readValue(apiDefinition.getRequest(), new TypeReference() { }); hashTree.add(element); + OldVersionUtil.transferHashTree(hashTree); } } catch (Exception ex) { ex.printStackTrace(); diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/OldVersionUtil.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/OldVersionUtil.java new file mode 100644 index 0000000000..943f5502a8 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/OldVersionUtil.java @@ -0,0 +1,23 @@ +package io.metersphere.api.dto.definition.request; + + +import io.metersphere.commons.utils.SessionUtils; +import org.apache.commons.lang3.StringUtils; + +import java.util.List; + +public class OldVersionUtil { + + public static void transferHashTree(List hashTree) { + for (int i = 0; i < hashTree.size(); i++) { + MsTestElement element = hashTree.get(i); + if (StringUtils.isBlank(element.getProjectId())) { + element.setProjectId(SessionUtils.getCurrentProjectId()); + } + if (element.getHashTree() != null && element.getHashTree().size() > 0) { + transferHashTree(element.getHashTree()); + } + } + } + +} 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 277b22e204..d6a92e7d44 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,55 @@ 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 +439,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 +474,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 +575,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 +588,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,13 +739,12 @@ 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); - - JmxInfoDTO dto = apiTestService.updateJmxString(jmx, testName, true); + JmxInfoDTO dto = apiTestService.updateJmxString(generateJmx(apiScenarios.get(0)), testName, true); String name = request.getName() + ".jmx"; dto.setName(name); @@ -931,7 +908,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/base/mapper/ext/ExtTestReviewCaseMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestReviewCaseMapper.xml index e0988937dc..00f773ad2b 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestReviewCaseMapper.xml +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestReviewCaseMapper.xml @@ -100,7 +100,7 @@ select test_case_review_test_case.id as id, test_case.id as caseId, test_case.name, test_case.priority, test_case.type, test_case.node_path, test_case.method, test_case.num, test_case_review_test_case.reviewer, test_case.review_status, test_case_review_test_case.update_time, test_case_node.name as model, - project.name as projectName, test_case_review_test_case.review_id as reviewId + project.name as projectName, test_case_review_test_case.review_id as reviewId,test_case.test_id as testId from test_case_review_test_case inner join test_case on test_case_review_test_case.case_id = test_case.id left join test_case_node on test_case_node.id=test_case.node_id diff --git a/backend/src/main/java/io/metersphere/track/controller/TestCaseReviewController.java b/backend/src/main/java/io/metersphere/track/controller/TestCaseReviewController.java index 6ea0f97fb8..a2e3b0daea 100644 --- a/backend/src/main/java/io/metersphere/track/controller/TestCaseReviewController.java +++ b/backend/src/main/java/io/metersphere/track/controller/TestCaseReviewController.java @@ -68,8 +68,8 @@ public class TestCaseReviewController { @PostMapping("/edit") @RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR) - public void editCaseReview(@RequestBody SaveTestCaseReviewRequest testCaseReview) { - testCaseReviewService.editCaseReview(testCaseReview); + public String editCaseReview(@RequestBody SaveTestCaseReviewRequest testCaseReview) { + return testCaseReviewService.editCaseReview(testCaseReview); } @GetMapping("/delete/{reviewId}") diff --git a/backend/src/main/java/io/metersphere/track/issue/AbstractIssuePlatform.java b/backend/src/main/java/io/metersphere/track/issue/AbstractIssuePlatform.java index c754a0e10a..6c89ff1732 100644 --- a/backend/src/main/java/io/metersphere/track/issue/AbstractIssuePlatform.java +++ b/backend/src/main/java/io/metersphere/track/issue/AbstractIssuePlatform.java @@ -8,6 +8,7 @@ import io.metersphere.commons.exception.MSException; import io.metersphere.commons.user.SessionUser; import io.metersphere.commons.utils.CommonBeanFactory; import io.metersphere.commons.utils.EncryptUtils; +import io.metersphere.commons.utils.LogUtil; import io.metersphere.commons.utils.SessionUtils; import io.metersphere.controller.request.IntegrationRequest; import io.metersphere.service.IntegrationService; @@ -15,10 +16,21 @@ import io.metersphere.service.ProjectService; import io.metersphere.track.request.testcase.IssuesRequest; import io.metersphere.track.service.TestCaseService; import org.apache.commons.lang3.StringUtils; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.conn.ssl.TrustStrategy; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; import org.springframework.http.HttpHeaders; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.web.client.RestTemplate; + +import javax.net.ssl.SSLContext; +import java.security.cert.X509Certificate; public abstract class AbstractIssuePlatform implements IssuesPlatform { + private static RestTemplate restTemplate; + protected IntegrationService integrationService; protected TestCaseIssuesMapper testCaseIssuesMapper; protected ProjectService projectService; @@ -26,8 +38,29 @@ public abstract class AbstractIssuePlatform implements IssuesPlatform { protected IssuesMapper issuesMapper; protected ExtIssuesMapper extIssuesMapper; + protected RestTemplate restTemplateIgnoreSSL; + protected String testCaseId; + static { + try { + TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true; + SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom() + .loadTrustMaterial(null, acceptingTrustStrategy) + .build(); + SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext); + CloseableHttpClient httpClient = HttpClients.custom() + .setSSLSocketFactory(csf) + .build(); + HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(); + requestFactory.setHttpClient(httpClient); + + restTemplate = new RestTemplate(requestFactory); + } catch (Exception e) { + LogUtil.error(e); + } + } + public AbstractIssuePlatform(IssuesRequest issuesRequest) { this.integrationService = CommonBeanFactory.getBean(IntegrationService.class); this.testCaseIssuesMapper = CommonBeanFactory.getBean(TestCaseIssuesMapper.class); @@ -36,6 +69,8 @@ public abstract class AbstractIssuePlatform implements IssuesPlatform { this.issuesMapper = CommonBeanFactory.getBean(IssuesMapper.class); this.extIssuesMapper = CommonBeanFactory.getBean(ExtIssuesMapper.class); this.testCaseId = issuesRequest.getTestCaseId(); + // + this.restTemplateIgnoreSSL = restTemplate; } protected String getPlatformConfig(String platform) { @@ -64,6 +99,7 @@ public abstract class AbstractIssuePlatform implements IssuesPlatform { /** * 获取平台与项目相关的属性 + * * @return 其他平台和本地项目绑定的属性值 */ abstract String getProjectId(); diff --git a/backend/src/main/java/io/metersphere/track/issue/JiraPlatform.java b/backend/src/main/java/io/metersphere/track/issue/JiraPlatform.java index a43465c48a..204366015e 100644 --- a/backend/src/main/java/io/metersphere/track/issue/JiraPlatform.java +++ b/backend/src/main/java/io/metersphere/track/issue/JiraPlatform.java @@ -186,8 +186,8 @@ public class JiraPlatform extends AbstractIssuePlatform { String url = object.getString("url"); HttpHeaders headers = auth(account, password); HttpEntity requestEntity = new HttpEntity<>(headers); - RestTemplate restTemplate = new RestTemplate(); - restTemplate.exchange(url + "rest/api/2/issue/createmeta", HttpMethod.GET, requestEntity, String.class); + // 忽略ssl + restTemplateIgnoreSSL.exchange(url + "rest/api/2/issue/createmeta", HttpMethod.GET, requestEntity, String.class); } catch (Exception e) { LogUtil.error(e.getMessage(), e); MSException.throwException("验证失败!"); diff --git a/backend/src/main/java/io/metersphere/track/service/TestCaseReviewService.java b/backend/src/main/java/io/metersphere/track/service/TestCaseReviewService.java index 2857fecd68..948ae0af0f 100644 --- a/backend/src/main/java/io/metersphere/track/service/TestCaseReviewService.java +++ b/backend/src/main/java/io/metersphere/track/service/TestCaseReviewService.java @@ -203,7 +203,7 @@ public class TestCaseReviewService { return extTestCaseReviewMapper.listByWorkspaceId(currentWorkspaceId, SessionUtils.getUserId(), SessionUtils.getCurrentProjectId()); } - public void editCaseReview(SaveTestCaseReviewRequest testCaseReview) { + public String editCaseReview(SaveTestCaseReviewRequest testCaseReview) { editCaseReviewer(testCaseReview); testCaseReview.setUpdateTime(System.currentTimeMillis()); checkCaseReviewExist(testCaseReview); @@ -221,6 +221,7 @@ public class TestCaseReviewService { .event(NoticeConstants.Event.UPDATE) .build(); noticeSendService.send(NoticeConstants.TaskType.REVIEW_TASK, noticeModel); + return testCaseReview.getId(); } private void editCaseReviewer(SaveTestCaseReviewRequest testCaseReview) { 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/backend/src/main/resources/db/migration/V78__v1.8_release.sql b/backend/src/main/resources/db/migration/V78__v1.8_release.sql index bb4704ecb4..c75d13da28 100644 --- a/backend/src/main/resources/db/migration/V78__v1.8_release.sql +++ b/backend/src/main/resources/db/migration/V78__v1.8_release.sql @@ -33,7 +33,7 @@ alter table swagger_url_project -- add_test_case alter table test_case - add demand_id varchar(50) null; + add demand_id varchar(120) null; alter table test_case add demand_name varchar(999) null; diff --git a/frontend/src/business/components/api/automation/scenario/EditApiScenario.vue b/frontend/src/business/components/api/automation/scenario/EditApiScenario.vue index b0b2fbfd21..af479b9619 100644 --- a/frontend/src/business/components/api/automation/scenario/EditApiScenario.vue +++ b/frontend/src/business/components/api/automation/scenario/EditApiScenario.vue @@ -1,15 +1,18 @@ @@ -222,6 +230,7 @@ props: { moduleOptions: Array, currentScenario: {}, + type: String }, components: { MsVariableList, @@ -532,6 +541,9 @@ if (arr[i].type === ELEMENT_TYPE.LoopController && arr[i].hashTree && arr[i].hashTree.length > 1) { arr[i].countController.proceed = true; } + if (!arr[i].projectId) { + arr[i].projectId = getCurrentProjectID(); + } if (arr[i].hashTree != undefined && arr[i].hashTree.length > 0) { this.recursiveSorting(arr[i].hashTree); } @@ -544,6 +556,9 @@ && this.scenarioDefinition[i].hashTree.length > 1) { this.scenarioDefinition[i].countController.proceed = true; } + if (!this.scenarioDefinition[i].projectId) { + this.scenarioDefinition.projectId = getCurrentProjectID(); + } if (this.scenarioDefinition[i].hashTree != undefined && this.scenarioDefinition[i].hashTree.length > 0) { this.recursiveSorting(this.scenarioDefinition[i].hashTree); } @@ -919,14 +934,15 @@ } this.enableCookieShare = obj.enableCookieShare; this.scenarioDefinition = obj.hashTree; - this.initProjectIds(); } } if (this.currentScenario.copy) { this.path = "/api/automation/create"; } } - this.getEnvironments(); + this.sort(); + this.initProjectIds(); + // this.getEnvironments(); }) } }, diff --git a/frontend/src/business/components/api/automation/scenario/component/ApiComponent.vue b/frontend/src/business/components/api/automation/scenario/component/ApiComponent.vue index 1f48fe7912..81103b6beb 100644 --- a/frontend/src/business/components/api/automation/scenario/component/ApiComponent.vue +++ b/frontend/src/business/components/api/automation/scenario/component/ApiComponent.vue @@ -251,16 +251,19 @@ this.reload(); }, run() { - if (!this.envMap || this.envMap.size === 0) { - this.$warning("请在环境配置中为该步骤所属项目选择运行环境!"); - return false; - } else if (this.envMap && this.envMap.size > 0) { - const env = this.envMap.get(this.request.projectId); - if (!env) { + if (this.isApiImport) { + if (!this.envMap || this.envMap.size === 0) { this.$warning("请在环境配置中为该步骤所属项目选择运行环境!"); return false; + } else if (this.envMap && this.envMap.size > 0) { + const env = this.envMap.get(this.request.projectId); + if (!env) { + this.$warning("请在环境配置中为该步骤所属项目选择运行环境!"); + return false; + } } } + this.request.active = true; this.loading = true; this.runData = []; diff --git a/frontend/src/business/components/api/automation/scenario/component/ApiScenarioComponent.vue b/frontend/src/business/components/api/automation/scenario/component/ApiScenarioComponent.vue index a0b2873f78..932ad0f4f8 100644 --- a/frontend/src/business/components/api/automation/scenario/component/ApiScenarioComponent.vue +++ b/frontend/src/business/components/api/automation/scenario/component/ApiScenarioComponent.vue @@ -108,6 +108,9 @@ recursive(arr) { for (let i in arr) { arr[i].disabled = true; + if (!arr[i].projectId) { + arr[i].projectId = getCurrentProjectID(); + } if (arr[i].hashTree != undefined && arr[i].hashTree.length > 0) { this.recursive(arr[i].hashTree); } @@ -116,6 +119,9 @@ setDisabled(scenarioDefinition) { for (let i in scenarioDefinition) { scenarioDefinition[i].disabled = true; + if (!scenarioDefinition[i].projectId) { + scenarioDefinition[i].projectId = getCurrentProjectID(); + } if (scenarioDefinition[i].hashTree != undefined && scenarioDefinition[i].hashTree.length > 0) { this.recursive(scenarioDefinition[i].hashTree); } diff --git a/frontend/src/business/components/api/automation/scenario/testplan/TestPlanList.vue b/frontend/src/business/components/api/automation/scenario/testplan/TestPlanList.vue index ea2aceb4f9..7e631ce3a3 100644 --- a/frontend/src/business/components/api/automation/scenario/testplan/TestPlanList.vue +++ b/frontend/src/business/components/api/automation/scenario/testplan/TestPlanList.vue @@ -231,14 +231,6 @@ export default { let data = response.data; this.total = data.itemCount; this.tableData = data.listObject; - for (let i = 0; i < this.tableData.length; i++) { - let path = "/test/plan/project"; - this.$post(path, {planId: this.tableData[i].id}, res => { - let arr = res.data; - let projectIds = arr.map(data => data.id); - this.$set(this.tableData[i], "projectIds", projectIds); - }) - } }); }, buildPagePath(path) { diff --git a/frontend/src/business/components/api/definition/components/case/ApiCaseItem.vue b/frontend/src/business/components/api/definition/components/case/ApiCaseItem.vue index bd54431588..76c6bb56f7 100644 --- a/frontend/src/business/components/api/definition/components/case/ApiCaseItem.vue +++ b/frontend/src/business/components/api/definition/components/case/ApiCaseItem.vue @@ -1,6 +1,6 @@ diff --git a/frontend/src/business/components/track/review/view/components/TestReviewTestCaseEdit.vue b/frontend/src/business/components/track/review/view/components/TestReviewTestCaseEdit.vue index 2760c085f3..55604d3485 100644 --- a/frontend/src/business/components/track/review/view/components/TestReviewTestCaseEdit.vue +++ b/frontend/src/business/components/track/review/view/components/TestReviewTestCaseEdit.vue @@ -39,11 +39,14 @@ + :disabled="isReadOnly" :icon="testCase.reviewStatus === 'Pass' ? 'el-icon-check' : ''" + @click="saveCase('Pass')"> {{ $t('test_track.review.pass') }} + :disabled="isReadOnly" + :icon="testCase.reviewStatus === 'UnPass' ? 'el-icon-check' : ''" + @click="saveCase('UnPass')"> {{ $t('test_track.review.un_pass') }} @@ -66,29 +69,22 @@ {{ $t('test_track.case.case_type') }}: - {{ - $t('commons.functional') - }} + + 场景用例 + {{ $t('commons.performance') }} {{ $t('commons.api') }} + 接口用例 + - - {{ $t('test_track.case.method') }}: - {{ $t('test_track.case.manual') }} - {{ $t('test_track.case.auto') }} - - + {{ $t('test_track.case.module') }}: {{ testCase.nodePath }} - - {{ $t('test_track.plan.plan_project') }}: - {{ testCase.projectName }} - @@ -98,7 +94,7 @@ - + @@ -108,12 +104,17 @@ :is-read-only="true" :id="testCase.testId" ref="performanceTestDetail"/> + + + - +
{{ $t('test_track.case.steps') }}: @@ -229,7 +230,8 @@ - +
@@ -248,9 +250,11 @@ import PerformanceTestDetail from "../../../plan/view/comonents/test/Performance import ApiTestResult from "../../../plan/view/comonents/test/ApiTestResult"; import ApiTestDetail from "../../../plan/view/comonents/test/ApiTestDetail"; import TestPlanTestCaseStatusButton from "../../../plan/common/TestPlanTestCaseStatusButton"; -import {listenGoBack, removeGoBackListener} from "@/common/js/utils"; +import {getCurrentProjectID, listenGoBack, removeGoBackListener} from "@/common/js/utils"; import ReviewComment from "../../commom/ReviewComment"; import TestCaseAttachment from "@/business/components/track/case/components/TestCaseAttachment"; +import ApiCaseItem from "@/business/components/api/definition/components/case/ApiCaseItem"; +import MsEditApiScenario from "@/business/components/api/automation/scenario/EditApiScenario" export default { name: "TestReviewTestCaseEdit", @@ -261,7 +265,10 @@ export default { ApiTestDetail, TestPlanTestCaseStatusButton, ReviewComment, - TestCaseAttachment + TestCaseAttachment, + ApiCaseItem, + MsEditApiScenario + }, data() { return { @@ -277,7 +284,11 @@ export default { users: [], activeName: 'comment', comments: [], - tableData: [] + tableData: [], + currentScenario: {}, + mark: 'detail', + api: {}, + apiCase: {}, }; }, props: { @@ -378,16 +389,55 @@ export default { listenGoBack(this.handleClose); this.initData(testCase); this.getComments(testCase); + this.getApiTestCase(testCase); + this.getCurrentScenario(testCase) + }, + getApiTestCase(testCase) { + let param = {} + param.projectId = getCurrentProjectID(); + param.id = testCase.testId; + this.result = this.$post("/api/testcase/list", param, response => { + let apiCaseList = [] + this.apiCaseList = response.data; + this.apiCaseList.forEach(apiCase => { + if (apiCase.tags && apiCase.tags.length > 0) { + apiCase.tags = JSON.parse(apiCase.tags); + this.$set(apiCase, 'selected', false); + } + if (Object.prototype.toString.call(apiCase.request).match(/\[object (\w+)\]/)[1].toLowerCase() != 'object') { + apiCase.request = JSON.parse(apiCase.request); + } + if (!apiCase.request.hashTree) { + apiCase.request.hashTree = []; + } + this.apiCase = apiCase + this.handleTestCase(apiCase) + }) + + }); + }, + getCurrentScenario(testCase) { + this.result = this.$get("/api/automation/getApiScenario/" + testCase.testId, response => { + this.currentScenario=response.data + }); + }, + + handleTestCase(testCase) { + this.$get('/api/definition/get/' + testCase.apiDefinitionId, (response) => { + this.api = response.data; + }); }, initTest() { this.$nextTick(() => { if (this.testCase.testId && this.testCase.testId !== 'other') { - if (this.testCase.method === 'auto') { - if (this.$refs.apiTestDetail && this.testCase.type === 'api') { - this.$refs.apiTestDetail.init(); - } else if (this.testCase.type === 'performance') { - this.$refs.performanceTestDetail.init(); - } + if (this.$refs.apiTestDetail && this.testCase.type === 'api') { + this.$refs.apiTestDetail.init(); + } else if (this.testCase.type === 'performance') { + this.$refs.performanceTestDetail.init(); + } else if (this.testCase.type === 'testcase') { + this.$refs.apiCaseConfig.active(this.api); + } else if (this.testCase.type === 'automation') { + this.$refs.autoScenarioConfig.showAll(); } } }); diff --git a/frontend/src/business/components/track/review/view/components/TestReviewTestCaseList.vue b/frontend/src/business/components/track/review/view/components/TestReviewTestCaseList.vue index a3c5e0e88b..213b447120 100644 --- a/frontend/src/business/components/track/review/view/components/TestReviewTestCaseList.vue +++ b/frontend/src/business/components/track/review/view/components/TestReviewTestCaseList.vue @@ -84,7 +84,7 @@ - - + --> .container { - width: 1440px; + width: 1200px; height: 810px; margin: calc((100vh - 810px) / 2) auto 0; background-color: #FFFFFF; @@ -294,6 +294,7 @@ body { color: #2B415C; -webkit-font-smoothing: antialiased; margin: 0; + height: auto; } .form .el-input > .el-input__inner {