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 a16673a0b0..0f5a7accfc 100644 --- a/backend/src/main/java/io/metersphere/api/controller/ApiAutomationController.java +++ b/backend/src/main/java/io/metersphere/api/controller/ApiAutomationController.java @@ -381,5 +381,10 @@ public class ApiAutomationController { public void saveFollows(@PathVariable String scenarioId, @RequestBody List follows) { apiAutomationService.saveFollows(scenarioId, follows); } + + @PostMapping(value = "/env") + public List getEnvProjects(@RequestBody RunScenarioRequest request) { + return apiAutomationService.getProjects(request); + } } diff --git a/backend/src/main/java/io/metersphere/api/controller/ApiJmeterFileController.java b/backend/src/main/java/io/metersphere/api/controller/ApiJmeterFileController.java index f50e415443..254ed7c873 100644 --- a/backend/src/main/java/io/metersphere/api/controller/ApiJmeterFileController.java +++ b/backend/src/main/java/io/metersphere/api/controller/ApiJmeterFileController.java @@ -47,8 +47,9 @@ public class ApiJmeterFileController { } @GetMapping("download") - public ResponseEntity downloadJmeterFiles(@RequestParam("testId") String testId, @RequestParam("reportId") String reportId, @RequestParam("runMode") String runMode, @RequestParam("reportType") String reportType) { - byte[] bytes = apiJmeterFileService.downloadJmeterFiles(runMode, testId, reportId, reportType); + public ResponseEntity downloadJmeterFiles(@RequestParam("testId") String testId, @RequestParam("reportId") String reportId, + @RequestParam("runMode") String runMode, @RequestParam("reportType") String reportType, @RequestParam("queueId") String queueId) { + byte[] bytes = apiJmeterFileService.downloadJmeterFiles(runMode, testId, reportId, reportType, queueId); return ResponseEntity.ok() .contentType(MediaType.parseMediaType("application/octet-stream")) .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + reportId + "_" + testId + ".zip\"") diff --git a/backend/src/main/java/io/metersphere/api/controller/ApiTestCaseController.java b/backend/src/main/java/io/metersphere/api/controller/ApiTestCaseController.java index e0e7abcf8b..b174d51a73 100644 --- a/backend/src/main/java/io/metersphere/api/controller/ApiTestCaseController.java +++ b/backend/src/main/java/io/metersphere/api/controller/ApiTestCaseController.java @@ -6,6 +6,8 @@ import io.metersphere.api.dto.ApiCaseEditRequest; import io.metersphere.api.dto.ApiCaseRunRequest; import io.metersphere.api.dto.DeleteCheckResult; import io.metersphere.api.dto.definition.*; +import io.metersphere.api.exec.api.ApiCaseExecuteService; +import io.metersphere.api.exec.api.ApiExecuteService; import io.metersphere.api.service.ApiTestCaseService; import io.metersphere.base.domain.ApiTestCase; import io.metersphere.base.domain.ApiTestCaseWithBLOBs; @@ -36,6 +38,10 @@ public class ApiTestCaseController { private ApiTestCaseService apiTestCaseService; @Resource private TestPlanApiCaseService testPlanApiCaseService; + @Resource + private ApiCaseExecuteService apiCaseExecuteService; + @Resource + private ApiExecuteService apiExecuteService; @PostMapping("/list") public List list(@RequestBody ApiTestCaseRequest request) { @@ -194,13 +200,13 @@ public class ApiTestCaseController { @MsAuditLog(module = "api_definition_case", type = OperLogConstants.EXECUTE, content = "#msClass.getLogDetails(#request.caseId)", msClass = ApiTestCaseService.class) public void batchRun(@RequestBody ApiCaseRunRequest request) { request.setTriggerMode(ReportTriggerMode.BATCH.name()); - apiTestCaseService.batchRun(request); + apiCaseExecuteService.run(request); } @PostMapping(value = "/jenkins/run") @MsAuditLog(module = "api_definition_case", type = OperLogConstants.EXECUTE, content = "#msClass.getLogDetails(#request.caseId)", msClass = ApiTestCaseService.class) public MsExecResponseDTO jenkinsRun(@RequestBody RunCaseRequest request) { - return apiTestCaseService.jenkinsRun(request); + return apiExecuteService.jenkinsRun(request); } @GetMapping(value = "/jenkins/exec/result/{id}") diff --git a/backend/src/main/java/io/metersphere/api/dto/ApiCaseRunRequest.java b/backend/src/main/java/io/metersphere/api/dto/ApiCaseRunRequest.java index 65c3ecbbe3..38822cb5e1 100644 --- a/backend/src/main/java/io/metersphere/api/dto/ApiCaseRunRequest.java +++ b/backend/src/main/java/io/metersphere/api/dto/ApiCaseRunRequest.java @@ -2,6 +2,7 @@ package io.metersphere.api.dto; import io.metersphere.api.dto.definition.ApiTestCaseRequest; import io.metersphere.controller.request.OrderRequest; +import io.metersphere.dto.RunModeConfigDTO; import lombok.Getter; import lombok.Setter; @@ -17,5 +18,6 @@ public class ApiCaseRunRequest { private List orders; private String projectId; private String environmentId; + private RunModeConfigDTO config; private ApiTestCaseRequest condition; } diff --git a/backend/src/main/java/io/metersphere/api/exec/api/ApiCaseExecuteService.java b/backend/src/main/java/io/metersphere/api/exec/api/ApiCaseExecuteService.java new file mode 100644 index 0000000000..49d9415504 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/exec/api/ApiCaseExecuteService.java @@ -0,0 +1,196 @@ +package io.metersphere.api.exec.api; + +import com.alibaba.fastjson.JSON; +import io.metersphere.api.cache.TestPlanReportExecuteCatch; +import io.metersphere.api.dto.ApiCaseRunRequest; +import io.metersphere.api.dto.definition.ApiTestCaseRequest; +import io.metersphere.api.dto.definition.BatchRunDefinitionRequest; +import io.metersphere.api.exec.queue.DBTestQueue; +import io.metersphere.api.exec.scenario.ApiScenarioSerialService; +import io.metersphere.api.exec.utils.ApiDefinitionExecResultUtil; +import io.metersphere.api.service.ApiExecutionQueueService; +import io.metersphere.base.domain.*; +import io.metersphere.base.mapper.ApiDefinitionExecResultMapper; +import io.metersphere.base.mapper.ApiTestCaseMapper; +import io.metersphere.base.mapper.TestPlanApiCaseMapper; +import io.metersphere.base.mapper.ext.ExtApiTestCaseMapper; +import io.metersphere.commons.constants.APITestStatus; +import io.metersphere.commons.constants.ApiRunMode; +import io.metersphere.commons.constants.TriggerMode; +import io.metersphere.commons.utils.ServiceUtils; +import io.metersphere.constants.RunModeConstants; +import io.metersphere.dto.MsExecResponseDTO; +import io.metersphere.dto.RunModeConfigDTO; +import io.metersphere.service.EnvironmentGroupProjectService; +import io.metersphere.utils.LoggerUtil; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.ibatis.session.ExecutorType; +import org.apache.ibatis.session.SqlSession; +import org.apache.ibatis.session.SqlSessionFactory; +import org.mybatis.spring.SqlSessionUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +@Service +@Transactional(rollbackFor = Exception.class) +public class ApiCaseExecuteService { + @Resource + private TestPlanApiCaseMapper testPlanApiCaseMapper; + @Resource + private SqlSessionFactory sqlSessionFactory; + @Resource + private ApiScenarioSerialService apiScenarioSerialService; + @Resource + private ApiExecutionQueueService apiExecutionQueueService; + @Resource + private ApiCaseParallelExecuteService apiCaseParallelExecuteService; + @Resource + private ExtApiTestCaseMapper extApiTestCaseMapper; + @Resource + private ApiTestCaseMapper apiTestCaseMapper; + @Resource + private EnvironmentGroupProjectService environmentGroupProjectService; + + /** + * 测试计划case执行 + * + * @param request + * @return + */ + public List run(BatchRunDefinitionRequest request) { + List ids = request.getPlanIds(); + if (CollectionUtils.isEmpty(ids)) { + return new LinkedList<>(); + } + if (request.getConfig() == null) { + request.setConfig(new RunModeConfigDTO()); + } + if (StringUtils.equals("GROUP", request.getConfig().getEnvironmentType()) && StringUtils.isNotEmpty(request.getConfig().getEnvironmentGroupId())) { + request.getConfig().setEnvMap(environmentGroupProjectService.getEnvMap(request.getConfig().getEnvironmentGroupId())); + } + LoggerUtil.debug("开始查询测试计划用例"); + TestPlanApiCaseExample example = new TestPlanApiCaseExample(); + example.createCriteria().andIdIn(ids); + example.setOrderByClause("`order` DESC"); + List planApiCases = testPlanApiCaseMapper.selectByExample(example); + SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH); + ApiDefinitionExecResultMapper batchMapper = sqlSession.getMapper(ApiDefinitionExecResultMapper.class); + if (StringUtils.isEmpty(request.getTriggerMode())) { + request.setTriggerMode(ApiRunMode.API_PLAN.name()); + } + + List responseDTOS = new LinkedList<>(); + Map executeQueue = new HashMap<>(); + //记录案例线程结果以及执行失败的案例ID + Map executeThreadIdMap = new HashMap<>(); + String status = request.getConfig().getMode().equals(RunModeConstants.SERIAL.toString()) ? APITestStatus.Waiting.name() : APITestStatus.Running.name(); + planApiCases.forEach(testPlanApiCase -> { + ApiDefinitionExecResult report = ApiDefinitionExecResultUtil.addResult(request, testPlanApiCase, status, batchMapper); + executeQueue.put(testPlanApiCase.getId(), report); + executeThreadIdMap.put(testPlanApiCase.getId(), report.getId()); + responseDTOS.add(new MsExecResponseDTO(testPlanApiCase.getId(), report.getId(), request.getTriggerMode())); + }); + sqlSession.flushStatements(); + if (sqlSession != null && sqlSessionFactory != null) { + SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory); + } + + LoggerUtil.debug("开始生成测试计划队列"); + 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(); + DBTestQueue deQueue = apiExecutionQueueService.add(executeQueue, poolId, ApiRunMode.API_PLAN.name(), request.getPlanReportId(), reportType, runMode, request.getConfig().getEnvMap()); + + //如果是测试计划生成报告的执行,则更新执行信息、执行线程信息。 + if (TestPlanReportExecuteCatch.containsReport(request.getPlanReportId())) { + if (!executeThreadIdMap.isEmpty()) { + TestPlanReportExecuteCatch.updateTestPlanThreadInfo(request.getPlanReportId(), executeThreadIdMap, null, null); + } + } + // 开始选择执行模式 + if (request.getConfig() != null && request.getConfig().getMode().equals(RunModeConstants.SERIAL.toString())) { + LoggerUtil.debug("开始串行执行"); + if (deQueue != null && deQueue.getQueue() != null) { + apiScenarioSerialService.serial(deQueue, deQueue.getQueue()); + } + } else { + LoggerUtil.debug("开始并发执行"); + if (deQueue != null && deQueue.getQueue() != null) { + apiCaseParallelExecuteService.parallel(executeQueue, request.getConfig(), deQueue, runMode); + } + } + return responseDTOS; + } + + /** + * 接口定义case执行 + * + * @param request + * @return + */ + public List run(ApiCaseRunRequest request) { + if (LoggerUtil.getLogger().isDebugEnabled()) { + LoggerUtil.debug("进入执行方法,接收到参数:" + JSON.toJSONString(request)); + } + if (request.getConfig() == null) { + request.setConfig(new RunModeConfigDTO()); + } + + if (StringUtils.equals("GROUP", request.getConfig().getEnvironmentType()) && StringUtils.isNotEmpty(request.getConfig().getEnvironmentGroupId())) { + request.getConfig().setEnvMap(environmentGroupProjectService.getEnvMap(request.getConfig().getEnvironmentGroupId())); + } + + ServiceUtils.getSelectAllIds(request, request.getCondition(), + (query) -> extApiTestCaseMapper.selectIdsByQuery((ApiTestCaseRequest) query)); + + ApiTestCaseExample example = new ApiTestCaseExample(); + example.createCriteria().andIdIn(request.getIds()); + List list = apiTestCaseMapper.selectByExampleWithBLOBs(example); + LoggerUtil.debug("查询到执行数据:" + list.size()); + + SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH); + ApiDefinitionExecResultMapper batchMapper = sqlSession.getMapper(ApiDefinitionExecResultMapper.class); + if (StringUtils.isEmpty(request.getTriggerMode())) { + request.setTriggerMode(ApiRunMode.DEFINITION.name()); + } + + List responseDTOS = new LinkedList<>(); + Map executeQueue = new HashMap<>(); + String status = request.getConfig().getMode().equals(RunModeConstants.SERIAL.toString()) ? APITestStatus.Waiting.name() : APITestStatus.Running.name(); + list.forEach(caseWithBLOBs -> { + ApiDefinitionExecResult report = ApiDefinitionExecResultUtil.initBase(caseWithBLOBs.getId(), APITestStatus.Running.name(), null, request.getConfig()); + report.setStatus(status); + batchMapper.insert(report); + executeQueue.put(caseWithBLOBs.getId(), report); + responseDTOS.add(new MsExecResponseDTO(caseWithBLOBs.getId(), report.getId(), request.getTriggerMode())); + }); + sqlSession.flushStatements(); + if (sqlSession != null && sqlSessionFactory != null) { + SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory); + } + + String reportType = request.getConfig().getReportType(); + String poolId = request.getConfig().getResourcePoolId(); + DBTestQueue deQueue = apiExecutionQueueService.add(executeQueue, poolId, ApiRunMode.DEFINITION.name(), null, reportType, ApiRunMode.DEFINITION.name(), request.getConfig().getEnvMap()); + // 开始选择执行模式 + if (request.getConfig().getMode().equals(RunModeConstants.SERIAL.toString())) { + LoggerUtil.debug("开始串行执行"); + if (deQueue != null && deQueue.getQueue() != null) { + apiScenarioSerialService.serial(deQueue, deQueue.getQueue()); + } + } else { + LoggerUtil.debug("开始并发执行"); + if (deQueue != null && deQueue.getQueue() != null) { + apiCaseParallelExecuteService.parallel(executeQueue, request.getConfig(), deQueue, ApiRunMode.DEFINITION.name()); + } + } + return responseDTOS; + } +} diff --git a/backend/src/main/java/io/metersphere/api/exec/api/ApiCaseParallelExecuteService.java b/backend/src/main/java/io/metersphere/api/exec/api/ApiCaseParallelExecuteService.java new file mode 100644 index 0000000000..87a6332bd8 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/exec/api/ApiCaseParallelExecuteService.java @@ -0,0 +1,57 @@ +package io.metersphere.api.exec.api; + +import io.metersphere.api.exec.queue.DBTestQueue; +import io.metersphere.api.exec.scenario.ApiScenarioSerialService; +import io.metersphere.api.exec.utils.GenerateHashTreeUtil; +import io.metersphere.api.jmeter.JMeterService; +import io.metersphere.base.domain.ApiDefinitionExecResult; +import io.metersphere.constants.RunModeConstants; +import io.metersphere.dto.JmeterRunRequestDTO; +import io.metersphere.dto.RunModeConfigDTO; +import io.metersphere.utils.LoggerUtil; +import org.apache.jorphan.collections.HashTree; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.Map; + +@Service +public class ApiCaseParallelExecuteService { + @Resource + private ApiScenarioSerialService apiScenarioSerialService; + @Resource + private JMeterService jMeterService; + + public void parallel(Map executeQueue, RunModeConfigDTO config, DBTestQueue executionQueue, String runMode) { + Thread thread = new Thread(new Runnable() { + @Override + public void run() { + try { + Thread.sleep(5000); + Thread.currentThread().setName("API-CASE-THREAD"); + for (String testId : executeQueue.keySet()) { + ApiDefinitionExecResult result = executeQueue.get(testId); + String reportId = result.getId(); + HashTree hashTree = null; + if (!GenerateHashTreeUtil.isResourcePool(config.getResourcePoolId()).isPool()) { + hashTree = apiScenarioSerialService.generateHashTree(testId, executionQueue.getRunMode(), config.getEnvMap()); + } + JmeterRunRequestDTO runRequest = new JmeterRunRequestDTO(testId, reportId, runMode, hashTree); + runRequest.setPool(GenerateHashTreeUtil.isResourcePool(config.getResourcePoolId())); + runRequest.setPoolId(config.getResourcePoolId()); + runRequest.setReportType(executionQueue.getReportType()); + runRequest.setRunType(RunModeConstants.PARALLEL.toString()); + runRequest.setQueueId(executionQueue.getId()); + if (executionQueue.getQueue() != null) { + runRequest.setPlatformUrl(executionQueue.getQueue().getId()); + } + jMeterService.run(runRequest); + } + } catch (Exception e) { + LoggerUtil.error("并发执行用例失败:" + e.getMessage()); + } + } + }); + thread.start(); + } +} diff --git a/backend/src/main/java/io/metersphere/api/exec/api/ApiExecuteService.java b/backend/src/main/java/io/metersphere/api/exec/api/ApiExecuteService.java index b9228b5897..12434dd4b6 100644 --- a/backend/src/main/java/io/metersphere/api/exec/api/ApiExecuteService.java +++ b/backend/src/main/java/io/metersphere/api/exec/api/ApiExecuteService.java @@ -5,8 +5,6 @@ import com.alibaba.fastjson.JSONObject; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; -import io.metersphere.api.dto.ApiCaseRunRequest; -import io.metersphere.api.dto.definition.ApiTestCaseRequest; import io.metersphere.api.dto.definition.RunCaseRequest; import io.metersphere.api.dto.definition.RunDefinitionRequest; import io.metersphere.api.dto.definition.request.ElementUtil; @@ -30,19 +28,14 @@ import io.metersphere.commons.constants.ApiRunMode; import io.metersphere.commons.utils.CommonBeanFactory; import io.metersphere.commons.utils.FileUtils; import io.metersphere.commons.utils.LogUtil; -import io.metersphere.commons.utils.ServiceUtils; import io.metersphere.dto.JmeterRunRequestDTO; import io.metersphere.dto.MsExecResponseDTO; import io.metersphere.plugin.core.MsTestElement; import io.metersphere.utils.LoggerUtil; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; -import org.apache.ibatis.session.ExecutorType; -import org.apache.ibatis.session.SqlSession; -import org.apache.ibatis.session.SqlSessionFactory; import org.apache.jorphan.collections.HashTree; import org.apache.jorphan.collections.ListedHashTree; -import org.mybatis.spring.SqlSessionUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; @@ -56,10 +49,6 @@ public class ApiExecuteService { @Resource private ApiTestCaseMapper apiTestCaseMapper; @Resource - private SqlSessionFactory sqlSessionFactory; - @Resource - private ExtApiTestCaseMapper extApiTestCaseMapper; - @Resource private JMeterService jMeterService; @Resource private ApiDefinitionExecResultMapper apiDefinitionExecResultMapper; @@ -69,68 +58,45 @@ public class ApiExecuteService { private ApiTestEnvironmentService environmentService; @Resource private TcpApiParamService tcpApiParamService; + @Resource + private ExtApiTestCaseMapper extApiTestCaseMapper; - public List run(ApiCaseRunRequest request) { - if (LoggerUtil.getLogger().isDebugEnabled()) { - LoggerUtil.debug("进入执行方法,接收到参数:" + JSON.toJSONString(request)); + public MsExecResponseDTO jenkinsRun(RunCaseRequest request) { + ApiTestCaseWithBLOBs caseWithBLOBs = null; + if (request.getBloBs() == null) { + caseWithBLOBs = apiTestCaseMapper.selectByPrimaryKey(request.getCaseId()); + if (caseWithBLOBs == null) { + return null; + } + request.setBloBs(caseWithBLOBs); + } else { + caseWithBLOBs = request.getBloBs(); } - - ServiceUtils.getSelectAllIds(request, request.getCondition(), - (query) -> extApiTestCaseMapper.selectIdsByQuery((ApiTestCaseRequest) query)); - SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH); - - ApiTestCaseExample example = new ApiTestCaseExample(); - example.createCriteria().andIdIn(request.getIds()); - List list = apiTestCaseMapper.selectByExampleWithBLOBs(example); - - LoggerUtil.debug("查询到执行数据:" + list.size()); - - ApiTestCaseMapper sqlSessionMapper = sqlSession.getMapper(ApiTestCaseMapper.class); - ApiDefinitionExecResultMapper batchMapper = sqlSession.getMapper(ApiDefinitionExecResultMapper.class); - List executeQueue = new LinkedList<>(); - for (ApiTestCaseWithBLOBs caseWithBLOBs : list) { - ApiDefinitionExecResult report = ApiDefinitionExecResultUtil.initBase(caseWithBLOBs.getId(), APITestStatus.Running.name(), null); - report.setName(caseWithBLOBs.getName()); - report.setTriggerMode(request.getTriggerMode()); - caseWithBLOBs.setLastResultId(report.getId()); - caseWithBLOBs.setUpdateTime(System.currentTimeMillis()); - caseWithBLOBs.setStatus(APITestStatus.Running.name()); - sqlSessionMapper.updateByPrimaryKey(caseWithBLOBs); - - // 执行对象 - RunCaseRequest runCaseRequest = new RunCaseRequest(); - runCaseRequest.setRunMode(ApiRunMode.DEFINITION.name()); - runCaseRequest.setCaseId(caseWithBLOBs.getId()); - runCaseRequest.setReportId(report.getId()); - runCaseRequest.setEnvironmentId(request.getEnvironmentId()); - runCaseRequest.setBloBs(caseWithBLOBs); - runCaseRequest.setReport(report); - - batchMapper.insert(report); - executeQueue.add(runCaseRequest); + if (caseWithBLOBs == null) { + return null; } - sqlSession.flushStatements(); - if (sqlSession != null && sqlSessionFactory != null) { - SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory); + if (StringUtils.isBlank(request.getEnvironmentId())) { + request.setEnvironmentId(extApiTestCaseMapper.getApiCaseEnvironment(request.getCaseId())); } + //提前生成报告 + ApiDefinitionExecResult report = ApiDefinitionExecResultUtil.add(caseWithBLOBs.getId(), APITestStatus.Running.name(), request.getReportId()); + report.setName(caseWithBLOBs.getName()); + report.setTriggerMode(ApiRunMode.JENKINS.name()); + report.setType(ApiRunMode.JENKINS.name()); + apiDefinitionExecResultMapper.insert(report); + //更新接口案例的最后执行状态等信息 + caseWithBLOBs.setLastResultId(report.getId()); + caseWithBLOBs.setUpdateTime(System.currentTimeMillis()); + caseWithBLOBs.setStatus(APITestStatus.Running.name()); + apiTestCaseMapper.updateByPrimaryKey(caseWithBLOBs); + request.setReport(report); - LoggerUtil.info("生成执行队列:" + executeQueue.size()); - if (LoggerUtil.getLogger().isDebugEnabled()) { - LoggerUtil.debug("生成执行队列:" + JSON.toJSONString(executeQueue)); + if (StringUtils.isEmpty(request.getRunMode())) { + request.setRunMode(ApiRunMode.DEFINITION.name()); } - List responseDTOS = new LinkedList<>(); - for (RunCaseRequest runCaseRequest : executeQueue) { - responseDTOS.add(exec(runCaseRequest)); - } - return responseDTOS; + return this.exec(request); } - /** - * 单条执行 - * - * @param request - * @return - */ public MsExecResponseDTO exec(RunCaseRequest request) { ApiTestCaseWithBLOBs testCaseWithBLOBs = request.getBloBs(); if (StringUtils.equals(request.getRunMode(), ApiRunMode.JENKINS_API_PLAN.name())) { diff --git a/backend/src/main/java/io/metersphere/api/exec/api/TestPlanApiExecuteService.java b/backend/src/main/java/io/metersphere/api/exec/api/TestPlanApiExecuteService.java deleted file mode 100644 index c037e96286..0000000000 --- a/backend/src/main/java/io/metersphere/api/exec/api/TestPlanApiExecuteService.java +++ /dev/null @@ -1,146 +0,0 @@ -package io.metersphere.api.exec.api; - -import io.metersphere.api.cache.TestPlanReportExecuteCatch; -import io.metersphere.api.dto.definition.BatchRunDefinitionRequest; -import io.metersphere.api.exec.queue.DBTestQueue; -import io.metersphere.api.exec.scenario.ApiScenarioSerialService; -import io.metersphere.api.exec.utils.ApiDefinitionExecResultUtil; -import io.metersphere.api.exec.utils.GenerateHashTreeUtil; -import io.metersphere.api.jmeter.JMeterService; -import io.metersphere.api.service.ApiExecutionQueueService; -import io.metersphere.base.domain.ApiDefinitionExecResult; -import io.metersphere.base.domain.TestPlanApiCase; -import io.metersphere.base.domain.TestPlanApiCaseExample; -import io.metersphere.base.mapper.ApiDefinitionExecResultMapper; -import io.metersphere.base.mapper.TestPlanApiCaseMapper; -import io.metersphere.commons.constants.APITestStatus; -import io.metersphere.commons.constants.ApiRunMode; -import io.metersphere.commons.constants.TriggerMode; -import io.metersphere.constants.RunModeConstants; -import io.metersphere.dto.JmeterRunRequestDTO; -import io.metersphere.dto.MsExecResponseDTO; -import io.metersphere.utils.LoggerUtil; -import org.apache.commons.collections.CollectionUtils; -import org.apache.commons.lang3.StringUtils; -import org.apache.ibatis.session.ExecutorType; -import org.apache.ibatis.session.SqlSession; -import org.apache.ibatis.session.SqlSessionFactory; -import org.apache.jorphan.collections.HashTree; -import org.mybatis.spring.SqlSessionUtils; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import javax.annotation.Resource; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -@Service -@Transactional(rollbackFor = Exception.class) -public class TestPlanApiExecuteService { - @Resource - private TestPlanApiCaseMapper testPlanApiCaseMapper; - @Resource - private SqlSessionFactory sqlSessionFactory; - @Resource - private ApiScenarioSerialService apiScenarioSerialService; - @Resource - private ApiExecutionQueueService apiExecutionQueueService; - @Resource - private JMeterService jMeterService; - - public List run(BatchRunDefinitionRequest request) { - List ids = request.getPlanIds(); - if (CollectionUtils.isEmpty(ids)) { - return new LinkedList<>(); - } - LoggerUtil.debug("开始查询测试计划用例"); - TestPlanApiCaseExample example = new TestPlanApiCaseExample(); - example.createCriteria().andIdIn(ids); - example.setOrderByClause("`order` DESC"); - List planApiCases = testPlanApiCaseMapper.selectByExample(example); - SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH); - ApiDefinitionExecResultMapper batchMapper = sqlSession.getMapper(ApiDefinitionExecResultMapper.class); - if (StringUtils.isEmpty(request.getTriggerMode())) { - request.setTriggerMode(ApiRunMode.API_PLAN.name()); - } - - List responseDTOS = new LinkedList<>(); - Map executeQueue = new HashMap<>(); - //记录案例线程结果以及执行失败的案例ID - Map executeThreadIdMap = new HashMap<>(); - String status = request.getConfig() != null && request.getConfig().getMode().equals(RunModeConstants.SERIAL.toString()) ? APITestStatus.Waiting.name() : APITestStatus.Running.name(); - planApiCases.forEach(testPlanApiCase -> { - ApiDefinitionExecResult report = ApiDefinitionExecResultUtil.addResult(request, testPlanApiCase, status, batchMapper); - executeQueue.put(testPlanApiCase, report); - executeThreadIdMap.put(testPlanApiCase.getId(), report.getId()); - responseDTOS.add(new MsExecResponseDTO(testPlanApiCase.getId(), report.getId(), request.getTriggerMode())); - }); - sqlSession.flushStatements(); - if (sqlSession != null && sqlSessionFactory != null) { - SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory); - } - - LoggerUtil.debug("开始生成测试计划队列"); - String reportType = request.getConfig() != null ? request.getConfig().getReportType() : null; - String poolId = request.getConfig() != null ? request.getConfig().getResourcePoolId() : null; - String runMode = StringUtils.equals(request.getTriggerMode(), TriggerMode.MANUAL.name()) ? ApiRunMode.API_PLAN.name() : ApiRunMode.SCHEDULE_API_PLAN.name(); - DBTestQueue deQueue = apiExecutionQueueService.add(executeQueue, poolId, ApiRunMode.API_PLAN.name(), request.getPlanReportId(), reportType, runMode); - - //如果是测试计划生成报告的执行,则更新执行信息、执行线程信息。 - if (TestPlanReportExecuteCatch.containsReport(request.getPlanReportId())) { - if (!executeThreadIdMap.isEmpty()) { - TestPlanReportExecuteCatch.updateTestPlanThreadInfo(request.getPlanReportId(), executeThreadIdMap, null, null); - } - } - // 开始选择执行模式 - if (request.getConfig() != null && request.getConfig().getMode().equals(RunModeConstants.SERIAL.toString())) { - LoggerUtil.debug("开始串行执行"); - if (deQueue != null && deQueue.getQueue() != null) { - apiScenarioSerialService.serial(deQueue, deQueue.getQueue()); - } - } else { - LoggerUtil.debug("开始并发执行"); - if (deQueue != null && deQueue.getQueue() != null) { - parallel(executeQueue, request, deQueue); - } - } - return responseDTOS; - } - - private void parallel(Map executeQueue, BatchRunDefinitionRequest request, DBTestQueue executionQueue) { - Thread thread = new Thread(new Runnable() { - @Override - public void run() { - try { - Thread.sleep(5000); - Thread.currentThread().setName("测试计划入列线程"); - for (TestPlanApiCase testPlanApiCase : executeQueue.keySet()) { - ApiDefinitionExecResult result = executeQueue.get(testPlanApiCase); - String reportId = result.getId(); - HashTree hashTree = null; - if (request.getConfig() == null || !GenerateHashTreeUtil.isResourcePool(request.getConfig().getResourcePoolId()).isPool()) { - hashTree = apiScenarioSerialService.generateHashTree(testPlanApiCase.getId()); - } - String runMode = StringUtils.equals(request.getTriggerMode(), TriggerMode.MANUAL.name()) ? ApiRunMode.API_PLAN.name() : ApiRunMode.SCHEDULE_API_PLAN.name(); - JmeterRunRequestDTO runRequest = new JmeterRunRequestDTO(testPlanApiCase.getId(), reportId, runMode, hashTree); - if (request.getConfig() != null) { - runRequest.setPool(GenerateHashTreeUtil.isResourcePool(request.getConfig().getResourcePoolId())); - runRequest.setPoolId(request.getConfig().getResourcePoolId()); - } - runRequest.setTestPlanReportId(request.getPlanReportId()); - runRequest.setReportType(executionQueue.getReportType()); - runRequest.setTestPlanReportId(request.getPlanReportId()); - runRequest.setRunType(RunModeConstants.PARALLEL.toString()); - runRequest.setQueueId(executionQueue.getId()); - jMeterService.run(runRequest); - } - } catch (Exception e) { - LoggerUtil.error("并发执行测试计划用例失败:" + e.getMessage()); - } - } - }); - thread.start(); - } -} diff --git a/backend/src/main/java/io/metersphere/api/exec/queue/DBTestQueue.java b/backend/src/main/java/io/metersphere/api/exec/queue/DBTestQueue.java index 885573a3d1..088c14c7b0 100644 --- a/backend/src/main/java/io/metersphere/api/exec/queue/DBTestQueue.java +++ b/backend/src/main/java/io/metersphere/api/exec/queue/DBTestQueue.java @@ -4,7 +4,11 @@ import io.metersphere.base.domain.ApiExecutionQueue; import io.metersphere.base.domain.ApiExecutionQueueDetail; import lombok.Data; +import java.util.HashMap; +import java.util.Map; + @Data public class DBTestQueue extends ApiExecutionQueue { private ApiExecutionQueueDetail queue; + private Map detailMap = new HashMap<>(); } diff --git a/backend/src/main/java/io/metersphere/api/exec/scenario/ApiScenarioExecuteService.java b/backend/src/main/java/io/metersphere/api/exec/scenario/ApiScenarioExecuteService.java index 754fdc7e45..5a1c116762 100644 --- a/backend/src/main/java/io/metersphere/api/exec/scenario/ApiScenarioExecuteService.java +++ b/backend/src/main/java/io/metersphere/api/exec/scenario/ApiScenarioExecuteService.java @@ -32,6 +32,7 @@ import io.metersphere.commons.utils.SessionUtils; import io.metersphere.constants.RunModeConstants; import io.metersphere.dto.JmeterRunRequestDTO; import io.metersphere.dto.MsExecResponseDTO; +import io.metersphere.dto.RunModeConfigDTO; import io.metersphere.i18n.Translator; import io.metersphere.service.EnvironmentGroupProjectService; import io.metersphere.track.service.TestPlanScenarioCaseService; @@ -98,6 +99,14 @@ public class ApiScenarioExecuteService { if (StringUtils.isEmpty(request.getTriggerMode())) { request.setTriggerMode(ReportTriggerMode.MANUAL.name()); } + if (request.getConfig() == null) { + request.setConfig(new RunModeConfigDTO()); + } + + if (StringUtils.equals("GROUP", request.getConfig().getEnvironmentType()) && StringUtils.isNotEmpty(request.getConfig().getEnvironmentGroupId())) { + request.getConfig().setEnvMap(environmentGroupProjectService.getEnvMap(request.getConfig().getEnvironmentGroupId())); + } + // 生成集成报告 String serialReportId = null; LoggerUtil.info("Scenario run-执行脚本装载-根据条件查询所有场景 "); @@ -156,8 +165,7 @@ public class ApiScenarioExecuteService { // 开始执行 if (executeQueue != null && executeQueue.size() > 0) { String reportType = request.getConfig().getReportType(); - - DBTestQueue executionQueue = apiExecutionQueueService.add(executeQueue, request.getConfig().getResourcePoolId(), ApiRunMode.SCENARIO.name(), serialReportId, reportType, request.getRunMode()); + DBTestQueue executionQueue = apiExecutionQueueService.add(executeQueue, request.getConfig().getResourcePoolId(), ApiRunMode.SCENARIO.name(), serialReportId, reportType, request.getRunMode(), request.getConfig().getEnvMap()); if (request.getConfig() != null && request.getConfig().getMode().equals(RunModeConstants.SERIAL.toString())) { if (StringUtils.isNotEmpty(serialReportId)) { apiScenarioReportStructureService.save(apiScenarios, serialReportId, request.getConfig() != null ? request.getConfig().getReportType() : null); @@ -181,7 +189,7 @@ public class ApiScenarioExecuteService { apiScenarioSerialService.serial(executionQueue, executionQueue.getQueue()); } } else { - apiScenarioParallelService.parallel(executeQueue, request, serialReportId, responseDTOS, executionQueue.getId()); + apiScenarioParallelService.parallel(executeQueue, request, serialReportId, responseDTOS, executionQueue); } } return responseDTOS; @@ -309,6 +317,9 @@ public class ApiScenarioExecuteService { RunModeDataDTO runModeDataDTO = new RunModeDataDTO(); runModeDataDTO.setTestId(item.getId()); runModeDataDTO.setPlanEnvMap(new HashMap<>()); + if (request.getConfig().getEnvMap() != null) { + runModeDataDTO.setPlanEnvMap(request.getConfig().getEnvMap()); + } runModeDataDTO.setReport(report); runModeDataDTO.setReportId(report.getId()); executeQueue.put(report.getId(), runModeDataDTO); @@ -317,9 +328,10 @@ public class ApiScenarioExecuteService { try { RunModeDataDTO runModeDataDTO = new RunModeDataDTO(report, item.getId()); if (request.getConfig() != null && !request.getConfig().getMode().equals(RunModeConstants.SERIAL.toString())) { - HashTree hashTree = GenerateHashTreeUtil.generateHashTree(item, StringUtils.isNotEmpty(serialReportId) ? serialReportId + "-" + i : reportId, new HashMap<>(), request.getConfig().getReportType()); + HashTree hashTree = GenerateHashTreeUtil.generateHashTree(item, StringUtils.isNotEmpty(serialReportId) ? serialReportId + "-" + i : reportId, request.getConfig().getEnvMap(), request.getConfig().getReportType()); runModeDataDTO.setHashTree(hashTree); } + runModeDataDTO.setPlanEnvMap(request.getConfig().getEnvMap()); executeQueue.put(report.getId(), runModeDataDTO); } catch (Exception ex) { scenarioIds.remove(item.getId()); diff --git a/backend/src/main/java/io/metersphere/api/exec/scenario/ApiScenarioParallelService.java b/backend/src/main/java/io/metersphere/api/exec/scenario/ApiScenarioParallelService.java index 371a7f1276..ff36ce475e 100644 --- a/backend/src/main/java/io/metersphere/api/exec/scenario/ApiScenarioParallelService.java +++ b/backend/src/main/java/io/metersphere/api/exec/scenario/ApiScenarioParallelService.java @@ -4,6 +4,7 @@ import com.alibaba.fastjson.JSON; import io.metersphere.api.dto.RunModeDataDTO; import io.metersphere.api.dto.automation.APIScenarioReportResult; import io.metersphere.api.dto.automation.RunScenarioRequest; +import io.metersphere.api.exec.queue.DBTestQueue; import io.metersphere.api.exec.utils.GenerateHashTreeUtil; import io.metersphere.api.jmeter.JMeterService; import io.metersphere.base.mapper.ApiScenarioReportMapper; @@ -30,7 +31,7 @@ public class ApiScenarioParallelService { @Resource private JMeterService jMeterService; - public void parallel(Map executeQueue, RunScenarioRequest request, String serialReportId, List responseDTOS, String queueId) { + public void parallel(Map executeQueue, RunScenarioRequest request, String serialReportId, List responseDTOS, DBTestQueue executionQueue) { if (StringUtils.isEmpty(serialReportId)) { SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH); ApiScenarioReportMapper batchMapper = sqlSession.getMapper(ApiScenarioReportMapper.class); @@ -49,13 +50,14 @@ public class ApiScenarioParallelService { for (String reportId : executeQueue.keySet()) { JmeterRunRequestDTO runRequest = new JmeterRunRequestDTO(executeQueue.get(reportId).getTestId(), StringUtils.isNotEmpty(serialReportId) ? serialReportId : reportId, request.getRunMode(), executeQueue.get(reportId).getHashTree()); runRequest.setReportType(StringUtils.isNotEmpty(serialReportId) ? RunModeConstants.SET_REPORT.toString() : RunModeConstants.INDEPENDENCE.toString()); - runRequest.setQueueId(queueId); + runRequest.setQueueId(executionQueue.getId()); if (request.getConfig() != null) { runRequest.setPool(GenerateHashTreeUtil.isResourcePool(request.getConfig().getResourcePoolId())); runRequest.setPoolId(request.getConfig().getResourcePoolId()); } runRequest.setTestPlanReportId(request.getTestPlanReportId()); runRequest.setHashTree(executeQueue.get(reportId).getHashTree()); + runRequest.setPlatformUrl(executionQueue.getDetailMap().get(reportId)); if (LoggerUtil.getLogger().isDebugEnabled()) { LoggerUtil.debug("Scenario run-开始并发执行:" + JSON.toJSONString(request)); } diff --git a/backend/src/main/java/io/metersphere/api/exec/scenario/ApiScenarioSerialService.java b/backend/src/main/java/io/metersphere/api/exec/scenario/ApiScenarioSerialService.java index 0860218f64..5049de2592 100644 --- a/backend/src/main/java/io/metersphere/api/exec/scenario/ApiScenarioSerialService.java +++ b/backend/src/main/java/io/metersphere/api/exec/scenario/ApiScenarioSerialService.java @@ -100,7 +100,11 @@ public class ApiScenarioSerialService { } hashTree = GenerateHashTreeUtil.generateHashTree(scenario, queue.getReportId(), planEnvMap, executionQueue.getReportType()); } else { - hashTree = generateHashTree(queue.getTestId()); + Map map = new LinkedHashMap<>(); + if (StringUtils.isNotEmpty(queue.getEvnMap())) { + map = JSON.parseObject(queue.getEvnMap(), Map.class); + } + hashTree = generateHashTree(queue.getTestId(), queue.getType(), map); } // 更新环境变量 this.initEnv(hashTree); @@ -116,6 +120,9 @@ public class ApiScenarioSerialService { runRequest.setRunType(RunModeConstants.SERIAL.toString()); runRequest.setQueueId(executionQueue.getId()); runRequest.setPoolId(executionQueue.getPoolId()); + if (queue != null) { + runRequest.setPlatformUrl(queue.getId()); + } // 开始执行 jMeterService.run(runRequest); } catch (Exception e) { @@ -145,25 +152,36 @@ public class ApiScenarioSerialService { hashTreeUtil.mergeParamDataMap(null, envParamsMap); } - public HashTree generateHashTree(String testId) { - TestPlanApiCase apiCase = testPlanApiCaseMapper.selectByPrimaryKey(testId); - if (apiCase != null) { - ApiTestCaseWithBLOBs caseWithBLOBs = apiTestCaseMapper.selectByPrimaryKey(apiCase.getApiCaseId()); + public HashTree generateHashTree(String testId, String type, Map envMap) { + ApiTestCaseWithBLOBs caseWithBLOBs = null; + String envId = null; + if (StringUtils.equals(type, ApiRunMode.DEFINITION.name())) { + caseWithBLOBs = apiTestCaseMapper.selectByPrimaryKey(testId); + } else { + TestPlanApiCase apiCase = testPlanApiCaseMapper.selectByPrimaryKey(testId); + if (apiCase != null) { + caseWithBLOBs = apiTestCaseMapper.selectByPrimaryKey(apiCase.getApiCaseId()); + envId = apiCase.getEnvironmentId(); + } + } + if (envMap != null && envMap.containsKey(caseWithBLOBs.getProjectId())) { + envId = envMap.get(caseWithBLOBs.getProjectId()); + } + if (caseWithBLOBs != null) { HashTree jmeterHashTree = new HashTree(); MsTestPlan testPlan = new MsTestPlan(); testPlan.setHashTree(new LinkedList<>()); - if (caseWithBLOBs != null) { - try { - MsThreadGroup group = new MsThreadGroup(); - group.setLabel(caseWithBLOBs.getName()); - group.setName(caseWithBLOBs.getName()); - MsTestElement testElement = parse(caseWithBLOBs, testId); - group.setHashTree(new LinkedList<>()); - group.getHashTree().add(testElement); - testPlan.getHashTree().add(group); - } catch (Exception ex) { - MSException.throwException(ex.getMessage()); - } + try { + MsThreadGroup group = new MsThreadGroup(); + group.setLabel(caseWithBLOBs.getName()); + group.setName(caseWithBLOBs.getName()); + + MsTestElement testElement = parse(caseWithBLOBs, testId, envId); + group.setHashTree(new LinkedList<>()); + group.getHashTree().add(testElement); + testPlan.getHashTree().add(group); + } catch (Exception ex) { + MSException.throwException(ex.getMessage()); } testPlan.toHashTree(jmeterHashTree, testPlan.getHashTree(), new ParameterConfig()); return jmeterHashTree; @@ -171,7 +189,7 @@ public class ApiScenarioSerialService { return null; } - private MsTestElement parse(ApiTestCaseWithBLOBs caseWithBLOBs, String planId) { + private MsTestElement parse(ApiTestCaseWithBLOBs caseWithBLOBs, String planId, String envId) { ObjectMapper mapper = new ObjectMapper(); mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); try { @@ -186,31 +204,38 @@ public class ApiScenarioSerialService { }); list.addAll(elements); } - TestPlanApiCase apiCase = testPlanApiCaseMapper.selectByPrimaryKey(planId); if (element.getString("type").equals("HTTPSamplerProxy")) { MsHTTPSamplerProxy httpSamplerProxy = JSON.parseObject(api, MsHTTPSamplerProxy.class); httpSamplerProxy.setHashTree(list); httpSamplerProxy.setName(planId); - httpSamplerProxy.setUseEnvironment(apiCase.getEnvironmentId()); + if (StringUtils.isNotEmpty(envId)) { + httpSamplerProxy.setUseEnvironment(envId); + } return httpSamplerProxy; } if (element.getString("type").equals("TCPSampler")) { MsTCPSampler msTCPSampler = JSON.parseObject(api, MsTCPSampler.class); - msTCPSampler.setUseEnvironment(apiCase.getEnvironmentId()); + if (StringUtils.isNotEmpty(envId)) { + msTCPSampler.setUseEnvironment(envId); + } msTCPSampler.setHashTree(list); msTCPSampler.setName(planId); return msTCPSampler; } if (element.getString("type").equals("DubboSampler")) { MsDubboSampler dubboSampler = JSON.parseObject(api, MsDubboSampler.class); - dubboSampler.setUseEnvironment(apiCase.getEnvironmentId()); + if (StringUtils.isNotEmpty(envId)) { + dubboSampler.setUseEnvironment(envId); + } dubboSampler.setHashTree(list); dubboSampler.setName(planId); return dubboSampler; } if (element.getString("type").equals("JDBCSampler")) { MsJDBCSampler jDBCSampler = JSON.parseObject(api, MsJDBCSampler.class); - jDBCSampler.setUseEnvironment(apiCase.getEnvironmentId()); + if (StringUtils.isNotEmpty(envId)) { + jDBCSampler.setUseEnvironment(envId); + } jDBCSampler.setHashTree(list); jDBCSampler.setName(planId); return jDBCSampler; diff --git a/backend/src/main/java/io/metersphere/api/exec/utils/ApiDefinitionExecResultUtil.java b/backend/src/main/java/io/metersphere/api/exec/utils/ApiDefinitionExecResultUtil.java index d0a04d0e3d..4849bd557f 100644 --- a/backend/src/main/java/io/metersphere/api/exec/utils/ApiDefinitionExecResultUtil.java +++ b/backend/src/main/java/io/metersphere/api/exec/utils/ApiDefinitionExecResultUtil.java @@ -10,13 +10,14 @@ import io.metersphere.commons.constants.ApiRunMode; import io.metersphere.commons.constants.TriggerMode; import io.metersphere.commons.utils.CommonBeanFactory; import io.metersphere.commons.utils.SessionUtils; +import io.metersphere.dto.RunModeConfigDTO; import org.apache.commons.lang3.StringUtils; import java.util.Objects; import java.util.UUID; public class ApiDefinitionExecResultUtil { - public static ApiDefinitionExecResult initBase(String resourceId, String status, String reportId) { + public static ApiDefinitionExecResult initBase(String resourceId, String status, String reportId, RunModeConfigDTO config) { ApiDefinitionExecResult apiResult = new ApiDefinitionExecResult(); if (StringUtils.isEmpty(reportId)) { apiResult.setId(UUID.randomUUID().toString()); @@ -28,6 +29,9 @@ public class ApiDefinitionExecResultUtil { apiResult.setEndTime(System.currentTimeMillis()); apiResult.setTriggerMode(TriggerMode.BATCH.name()); apiResult.setActuator("LOCAL"); + if (config != null && GenerateHashTreeUtil.isResourcePool(config.getResourcePoolId()).isPool()) { + apiResult.setActuator(config.getResourcePoolId()); + } apiResult.setUserId(Objects.requireNonNull(SessionUtils.getUser()).getId()); apiResult.setResourceId(resourceId); apiResult.setStartTime(System.currentTimeMillis()); diff --git a/backend/src/main/java/io/metersphere/api/jmeter/JMeterService.java b/backend/src/main/java/io/metersphere/api/jmeter/JMeterService.java index 16d9765fc8..05ad757c59 100644 --- a/backend/src/main/java/io/metersphere/api/jmeter/JMeterService.java +++ b/backend/src/main/java/io/metersphere/api/jmeter/JMeterService.java @@ -107,11 +107,15 @@ public class JMeterService { if (baseInfo != null) { platformUrl = baseInfo.getUrl(); } + // 临时存放 + String queueDetailId = request.getPlatformUrl(); + platformUrl += "/api/jmeter/download?testId=" + request.getTestId() + "&reportId=" + request.getReportId() + "&runMode=" + request.getRunMode() - + "&reportType=" + request.getReportType(); + + "&reportType=" + request.getReportType() + + "&queueId=" + queueDetailId; request.setPlatformUrl(platformUrl); request.setKafkaConfig(KafkaConfig.getKafka()); 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 7d58220941..73230ff209 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java @@ -1899,4 +1899,20 @@ public class ApiAutomationService { public List run(RunScenarioRequest request) { return apiScenarioExecuteService.run(request); } + + public List getProjects(RunScenarioRequest request) { + ServiceUtils.getSelectAllIds(request, request.getCondition(), + (query) -> extApiScenarioMapper.selectIdsByQuery(query)); + List ids = request.getIds(); + ApiScenarioExample example = new ApiScenarioExample(); + example.createCriteria().andIdIn(ids); + List apiScenarios = apiScenarioMapper.selectByExample(example); + List strings = new LinkedList<>(); + apiScenarios.forEach(item -> { + if (!strings.contains(item.getProjectId())) { + strings.add(item.getProjectId()); + } + }); + return strings; + } } diff --git a/backend/src/main/java/io/metersphere/api/service/ApiDefinitionExecResultService.java b/backend/src/main/java/io/metersphere/api/service/ApiDefinitionExecResultService.java index be365dde21..9c388a970e 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiDefinitionExecResultService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiDefinitionExecResultService.java @@ -90,43 +90,47 @@ public class ApiDefinitionExecResultService { } private void sendNotice(ApiDefinitionExecResult result) { - String resourceId = result.getResourceId(); - ApiTestCaseWithBLOBs apiTestCaseWithBLOBs = apiTestCaseMapper.selectByPrimaryKey(resourceId); - // 接口定义直接执行不发通知 - if (apiTestCaseWithBLOBs == null) { - return; - } - BeanMap beanMap = new BeanMap(apiTestCaseWithBLOBs); + try { + String resourceId = result.getResourceId(); + ApiTestCaseWithBLOBs apiTestCaseWithBLOBs = apiTestCaseMapper.selectByPrimaryKey(resourceId); + // 接口定义直接执行不发通知 + if (apiTestCaseWithBLOBs == null) { + return; + } + BeanMap beanMap = new BeanMap(apiTestCaseWithBLOBs); - String event; - String status; - if (StringUtils.equals(result.getStatus(), "success")) { - event = NoticeConstants.Event.EXECUTE_SUCCESSFUL; - status = "成功"; - } else { - event = NoticeConstants.Event.EXECUTE_FAILED; - status = "失败"; - } + String event; + String status; + if (StringUtils.equals(result.getStatus(), "success")) { + event = NoticeConstants.Event.EXECUTE_SUCCESSFUL; + status = "成功"; + } else { + event = NoticeConstants.Event.EXECUTE_FAILED; + status = "失败"; + } - Map paramMap = new HashMap<>(beanMap); - paramMap.put("operator", SessionUtils.getUser().getName()); - paramMap.put("status", result.getStatus()); - String context = "${operator}执行接口用例" + status + ": ${name}"; - NoticeModel noticeModel = NoticeModel.builder() - .operator(SessionUtils.getUserId()) - .context(context) - .subject("接口用例通知") - .successMailTemplate("api/CaseResultSuccess") - .failedMailTemplate("api/CaseResultFailed") - .paramMap(paramMap) - .event(event) - .build(); + Map paramMap = new HashMap<>(beanMap); + paramMap.put("operator", SessionUtils.getUser().getName()); + paramMap.put("status", result.getStatus()); + String context = "${operator}执行接口用例" + status + ": ${name}"; + NoticeModel noticeModel = NoticeModel.builder() + .operator(SessionUtils.getUserId()) + .context(context) + .subject("接口用例通知") + .successMailTemplate("api/CaseResultSuccess") + .failedMailTemplate("api/CaseResultFailed") + .paramMap(paramMap) + .event(event) + .build(); - String taskType = NoticeConstants.TaskType.API_DEFINITION_TASK; - if (StringUtils.equals(ReportTriggerMode.API.name(), result.getTriggerMode())) { - noticeSendService.send(ReportTriggerMode.API.name(), taskType, noticeModel); - } else { - noticeSendService.send(taskType, noticeModel); + String taskType = NoticeConstants.TaskType.API_DEFINITION_TASK; + if (StringUtils.equals(ReportTriggerMode.API.name(), result.getTriggerMode())) { + noticeSendService.send(ReportTriggerMode.API.name(), taskType, noticeModel); + } else { + noticeSendService.send(taskType, noticeModel); + } + } catch (Exception e) { + LogUtil.error(e); } } diff --git a/backend/src/main/java/io/metersphere/api/service/ApiExecutionQueueService.java b/backend/src/main/java/io/metersphere/api/service/ApiExecutionQueueService.java index 8e607870e0..e2318fcd39 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiExecutionQueueService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiExecutionQueueService.java @@ -26,9 +26,7 @@ import org.mybatis.spring.SqlSessionUtils; import org.springframework.stereotype.Service; import javax.annotation.Resource; -import java.util.List; -import java.util.Map; -import java.util.UUID; +import java.util.*; import java.util.stream.Collectors; @Service @@ -50,7 +48,7 @@ public class ApiExecutionQueueService { @Resource private ExtApiExecutionQueueMapper extApiExecutionQueueMapper; - public DBTestQueue add(Object runObj, String poolId, String type, String reportId, String reportType, String runMode) { + public DBTestQueue add(Object runObj, String poolId, String type, String reportId, String reportType, String runMode, Map envMap) { ApiExecutionQueue executionQueue = new ApiExecutionQueue(); executionQueue.setId(UUID.randomUUID().toString()); executionQueue.setCreateTime(System.currentTimeMillis()); @@ -61,19 +59,25 @@ public class ApiExecutionQueueService { queueMapper.insert(executionQueue); DBTestQueue resQueue = new DBTestQueue(); BeanUtils.copyBean(resQueue, executionQueue); + Map detailMap = new HashMap<>(); SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH); ApiExecutionQueueDetailMapper batchMapper = sqlSession.getMapper(ApiExecutionQueueDetailMapper.class); - if (StringUtils.equals(type, ApiRunMode.API_PLAN.name())) { + if (StringUtils.equalsAny(type, ApiRunMode.DEFINITION.name(), ApiRunMode.API_PLAN.name())) { final int[] sort = {0}; - Map runMap = (Map) runObj; + Map runMap = (Map) runObj; + if (envMap == null) { + envMap = new LinkedHashMap<>(); + } + String envStr = JSON.toJSONString(envMap); runMap.forEach((k, v) -> { - ApiExecutionQueueDetail queue = detail(v.getId(), k.getId(), type, sort[0], executionQueue.getId(), null); + ApiExecutionQueueDetail queue = detail(v.getId(), k, type, sort[0], executionQueue.getId(), envStr); if (sort[0] == 0) { resQueue.setQueue(queue); } sort[0]++; batchMapper.insert(queue); + detailMap.put(k, queue.getId()); }); } else { Map runMap = (Map) runObj; @@ -86,12 +90,14 @@ public class ApiExecutionQueueService { } sort[0]++; batchMapper.insert(queue); + detailMap.put(k, queue.getId()); }); } sqlSession.flushStatements(); if (sqlSession != null && sqlSessionFactory != null) { SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory); } + resQueue.setDetailMap(detailMap); return resQueue; } @@ -117,7 +123,7 @@ public class ApiExecutionQueueService { ApiExecutionQueueDetailExample example = new ApiExecutionQueueDetailExample(); example.setOrderByClause("sort asc"); example.createCriteria().andQueueIdEqualTo(id); - List queues = executionQueueDetailMapper.selectByExample(example); + List queues = executionQueueDetailMapper.selectByExampleWithBLOBs(example); if (CollectionUtils.isNotEmpty(queues)) { List list = queues.stream().filter(item -> StringUtils.equals(item.getTestId(), testId)).collect(Collectors.toList()); if (CollectionUtils.isNotEmpty(list)) { diff --git a/backend/src/main/java/io/metersphere/api/service/ApiJmeterFileService.java b/backend/src/main/java/io/metersphere/api/service/ApiJmeterFileService.java index a03fd1074f..f11a6a212e 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiJmeterFileService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiJmeterFileService.java @@ -6,10 +6,8 @@ import io.metersphere.api.dto.definition.request.MsTestPlan; import io.metersphere.api.dto.scenario.request.BodyFile; import io.metersphere.api.exec.scenario.ApiScenarioSerialService; import io.metersphere.api.exec.utils.GenerateHashTreeUtil; -import io.metersphere.base.domain.ApiScenarioWithBLOBs; -import io.metersphere.base.domain.JarConfig; -import io.metersphere.base.domain.Plugin; -import io.metersphere.base.domain.TestPlanApiScenario; +import io.metersphere.base.domain.*; +import io.metersphere.base.mapper.ApiExecutionQueueDetailMapper; import io.metersphere.base.mapper.ApiScenarioMapper; import io.metersphere.base.mapper.TestPlanApiScenarioMapper; import io.metersphere.commons.constants.ApiRunMode; @@ -43,6 +41,9 @@ public class ApiJmeterFileService { private TestPlanApiScenarioMapper testPlanApiScenarioMapper; @Resource private ApiScenarioMapper apiScenarioMapper; + @Resource + private ApiExecutionQueueDetailMapper executionQueueDetailMapper; + @Resource private EnvironmentGroupProjectService environmentGroupProjectService; @@ -58,7 +59,7 @@ public class ApiJmeterFileService { return listBytesToZip(files); } - public byte[] downloadJmeterFiles(String runMode, String remoteTestId, String reportId, String reportType) { + public byte[] downloadJmeterFiles(String runMode, String remoteTestId, String reportId, String reportType, String queueId) { Map planEnvMap = new HashMap<>(); ApiScenarioWithBLOBs scenario = null; if (StringUtils.equalsAny(runMode, ApiRunMode.SCENARIO_PLAN.name(), ApiRunMode.JENKINS_SCENARIO_PLAN.name(), ApiRunMode.SCHEDULE_SCENARIO_PLAN.name())) { @@ -76,9 +77,14 @@ public class ApiJmeterFileService { scenario = apiScenarioMapper.selectByPrimaryKey(planApiScenario.getApiScenarioId()); } } + ApiExecutionQueueDetail detail = executionQueueDetailMapper.selectByPrimaryKey(queueId); + Map envMap = new LinkedHashMap<>(); + if (detail != null && StringUtils.isNotEmpty(detail.getEvnMap())) { + envMap = JSON.parseObject(detail.getEvnMap(), Map.class); + } HashTree hashTree; if (StringUtils.equalsAnyIgnoreCase(runMode, ApiRunMode.DEFINITION.name(), ApiRunMode.JENKINS_API_PLAN.name(), ApiRunMode.API_PLAN.name(), ApiRunMode.SCHEDULE_API_PLAN.name(), ApiRunMode.MANUAL_PLAN.name())) { - hashTree = apiScenarioSerialService.generateHashTree(remoteTestId); + hashTree = apiScenarioSerialService.generateHashTree(remoteTestId, runMode, envMap); } else { if (scenario == null) { scenario = apiScenarioMapper.selectByPrimaryKey(remoteTestId); @@ -86,7 +92,9 @@ public class ApiJmeterFileService { if (scenario == null) { MSException.throwException("未找到执行场景。"); } - if (!StringUtils.equalsAny(runMode, ApiRunMode.SCENARIO_PLAN.name(), ApiRunMode.SCHEDULE_SCENARIO_PLAN.name())) { + if (envMap != null && !envMap.isEmpty()) { + planEnvMap = envMap; + } else if (!StringUtils.equalsAny(runMode, ApiRunMode.SCENARIO_PLAN.name(), ApiRunMode.SCHEDULE_SCENARIO_PLAN.name())) { String envType = scenario.getEnvironmentType(); String envJson = scenario.getEnvironmentJson(); String envGroupId = scenario.getEnvironmentGroupId(); diff --git a/backend/src/main/java/io/metersphere/api/service/ApiTestCaseService.java b/backend/src/main/java/io/metersphere/api/service/ApiTestCaseService.java index 4fbf3c3e00..79d335349d 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiTestCaseService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiTestCaseService.java @@ -6,7 +6,6 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import io.metersphere.api.dto.ApiCaseEditRequest; -import io.metersphere.api.dto.ApiCaseRunRequest; import io.metersphere.api.dto.DeleteCheckResult; import io.metersphere.api.dto.JmxInfoDTO; import io.metersphere.api.dto.datacount.ApiDataCountResult; @@ -16,20 +15,16 @@ import io.metersphere.api.dto.definition.request.MsTestPlan; import io.metersphere.api.dto.definition.request.MsThreadGroup; import io.metersphere.api.dto.definition.request.sampler.MsHTTPSamplerProxy; import io.metersphere.api.dto.scenario.request.RequestType; -import io.metersphere.api.exec.api.ApiExecuteService; -import io.metersphere.api.exec.utils.ApiDefinitionExecResultUtil; import io.metersphere.base.domain.*; import io.metersphere.base.mapper.*; import io.metersphere.base.mapper.ext.*; import io.metersphere.commons.constants.APITestStatus; -import io.metersphere.commons.constants.ApiRunMode; import io.metersphere.commons.constants.MsTestElementConstants; import io.metersphere.commons.constants.TestPlanStatus; import io.metersphere.commons.exception.MSException; import io.metersphere.commons.utils.*; import io.metersphere.controller.request.OrderRequest; import io.metersphere.controller.request.ResetOrderRequest; -import io.metersphere.dto.MsExecResponseDTO; import io.metersphere.i18n.Translator; import io.metersphere.log.utils.ReflexObjectUtil; import io.metersphere.log.vo.DetailColumn; @@ -83,8 +78,6 @@ public class ApiTestCaseService { @Resource private ApiDefinitionMapper apiDefinitionMapper; @Resource - private ApiExecuteService apiCaseExecuteService; - @Resource private ApiDefinitionExecResultMapper apiDefinitionExecResultMapper; @Resource private EsbApiParamService esbApiParamService; @@ -744,47 +737,6 @@ public class ApiTestCaseService { return ids; } - public void batchRun(ApiCaseRunRequest request) { - apiCaseExecuteService.run(request); - } - - public MsExecResponseDTO jenkinsRun(RunCaseRequest request) { - ApiTestCaseWithBLOBs caseWithBLOBs = null; - if (request.getBloBs() == null) { - caseWithBLOBs = apiTestCaseMapper.selectByPrimaryKey(request.getCaseId()); - if (caseWithBLOBs == null) { - return null; - } - request.setBloBs(caseWithBLOBs); - } else { - caseWithBLOBs = request.getBloBs(); - } - if (caseWithBLOBs == null) { - return null; - } - if (StringUtils.isBlank(request.getEnvironmentId())) { - request.setEnvironmentId(extApiTestCaseMapper.getApiCaseEnvironment(request.getCaseId())); - } - //提前生成报告 - ApiDefinitionExecResult report = ApiDefinitionExecResultUtil.add(caseWithBLOBs.getId(), APITestStatus.Running.name(), request.getReportId()); - report.setName(caseWithBLOBs.getName()); - report.setTriggerMode(ApiRunMode.JENKINS.name()); - report.setType(ApiRunMode.JENKINS.name()); - apiDefinitionExecResultMapper.insert(report); - //更新接口案例的最后执行状态等信息 - caseWithBLOBs.setLastResultId(report.getId()); - caseWithBLOBs.setUpdateTime(System.currentTimeMillis()); - caseWithBLOBs.setStatus(APITestStatus.Running.name()); - apiTestCaseMapper.updateByPrimaryKey(caseWithBLOBs); - request.setReport(report); - - if (StringUtils.isEmpty(request.getRunMode())) { - request.setRunMode(ApiRunMode.DEFINITION.name()); - } - - return apiCaseExecuteService.exec(request); - } - public String getExecResult(String id) { String status = extApiDefinitionExecResultMapper.selectExecResult(id); return status; diff --git a/backend/src/main/java/io/metersphere/track/service/TestPlanApiCaseService.java b/backend/src/main/java/io/metersphere/track/service/TestPlanApiCaseService.java index 574d209007..88ba6588d9 100644 --- a/backend/src/main/java/io/metersphere/track/service/TestPlanApiCaseService.java +++ b/backend/src/main/java/io/metersphere/track/service/TestPlanApiCaseService.java @@ -9,7 +9,7 @@ import io.metersphere.api.dto.definition.ApiTestCaseDTO; import io.metersphere.api.dto.definition.ApiTestCaseRequest; import io.metersphere.api.dto.definition.BatchRunDefinitionRequest; import io.metersphere.api.dto.definition.TestPlanApiCaseDTO; -import io.metersphere.api.exec.api.TestPlanApiExecuteService; +import io.metersphere.api.exec.api.ApiCaseExecuteService; import io.metersphere.api.service.ApiDefinitionExecResultService; import io.metersphere.api.service.ApiTestCaseService; import io.metersphere.base.domain.*; @@ -60,7 +60,7 @@ public class TestPlanApiCaseService { @Resource ApiTestCaseMapper apiTestCaseMapper; @Resource - private TestPlanApiExecuteService testPlanApiCaseExecuteService; + private ApiCaseExecuteService testPlanApiCaseExecuteService; @Resource SqlSessionFactory sqlSessionFactory; @Resource diff --git a/frontend/src/business/components/api/automation/scenario/ApiScenarioList.vue b/frontend/src/business/components/api/automation/scenario/ApiScenarioList.vue index f659b0079d..0c873bff4a 100644 --- a/frontend/src/business/components/api/automation/scenario/ApiScenarioList.vue +++ b/frontend/src/business/components/api/automation/scenario/ApiScenarioList.vue @@ -141,9 +141,9 @@
- {{v}}: + {{ v }}: - {{k}} + {{ k }}
@@ -152,8 +152,8 @@ width="350" trigger="click">
- {{v}}: - {{k}}
+ {{ v }}: + {{ k }}
@@ -266,7 +266,7 @@ - + @@ -533,7 +533,8 @@ export default { graphData: {}, environmentType: "", envGroupId: "", - apiscenariofilters:{}, + apiscenariofilters: {}, + runRequest: {}, }; }, created() { @@ -832,6 +833,14 @@ export default { param.condition = this.condition; }, handleBatchExecute() { + let run = {}; + run.id = getUUID(); + //按照列表排序 + let ids = this.orderBySelectRows(); + run.ids = ids; + run.projectId = this.projectId; + run.condition = this.condition; + this.runRequest = run; this.$refs.runMode.open(); }, @@ -1249,7 +1258,7 @@ export default { vertical-align: middle; } -.project-env{ +.project-env { display: inline-block; white-space: nowrap; overflow: hidden; diff --git a/frontend/src/business/components/api/automation/scenario/common/RunMode.vue b/frontend/src/business/components/api/automation/scenario/common/RunMode.vue index f748b545cf..5ef032b027 100644 --- a/frontend/src/business/components/api/automation/scenario/common/RunMode.vue +++ b/frontend/src/business/components/api/automation/scenario/common/RunMode.vue @@ -2,9 +2,22 @@ +
+ {{ $t("commons.environment") }}: + +
{{ $t("run_mode.title") }}: @@ -58,27 +71,39 @@ diff --git a/frontend/src/business/components/api/definition/components/list/ApiCaseRunModeWithEnv.vue b/frontend/src/business/components/api/definition/components/list/ApiCaseRunModeWithEnv.vue new file mode 100644 index 0000000000..989c27ec88 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/list/ApiCaseRunModeWithEnv.vue @@ -0,0 +1,185 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/list/ApiCaseSimpleList.vue b/frontend/src/business/components/api/definition/components/list/ApiCaseSimpleList.vue index b57d60412a..f27c67617c 100644 --- a/frontend/src/business/components/api/definition/components/list/ApiCaseSimpleList.vue +++ b/frontend/src/business/components/api/definition/components/list/ApiCaseSimpleList.vue @@ -206,10 +206,10 @@ - - + + @@ -232,6 +232,8 @@ import MsContainer from "../../../../common/components/MsContainer"; import MsBottomContainer from "../BottomContainer"; import ShowMoreBtn from "../../../../track/case/components/ShowMoreBtn"; import MsBatchEdit from "../basis/BatchEdit"; +import MsApiCaseRunModeWithEnv from "./ApiCaseRunModeWithEnv"; + import {API_METHOD_COLOUR, CASE_PRIORITY, DUBBO_METHOD, REQ_METHOD, SQL_METHOD, TCP_METHOD} from "../../model/JsonData"; import {getBodyUploadFiles, getCurrentProjectID, getUUID, strMapToObj} from "@/common/js/utils"; @@ -247,7 +249,7 @@ import MsTableAdvSearchBar from "@/business/components/common/components/search/ import {API_CASE_CONFIGS} from "@/business/components/common/components/search/search-components"; import { _filter, - _sort, + _sort, buildBatchParam, getCustomTableHeader, getCustomTableWidth, getLastTableSortField, @@ -285,8 +287,9 @@ export default { MsTable, MsTableColumn, MsRequestResultTail, + MsApiCaseRunModeWithEnv, PlanStatusTableItem: () => import("../../../../track/common/tableItems/plan/PlanStatusTableItem"), - MsTaskCenter: () => import("../../../../task/TaskCenter"), + MsTaskCenter: () => import("@/business/components/task/TaskCenter"), }, data() { return { @@ -391,6 +394,7 @@ export default { resVisible: false, response: {}, timeoutIndex: 0, + runCaseIds: [] }; }, props: { @@ -537,17 +541,16 @@ export default { return this.$t('api_test.home_page.detail_card.unexecute'); } }, - handleRunBatch() { this.$refs.batchRun.open(); }, - runBatch(environment) { + runBatch(config) { let obj = {}; obj.projectId = this.projectId; obj.selectAllDate = this.selectAll; obj.unSelectIds = this.unSelection; obj.ids = Array.from(this.selectRows).map(row => row.id); - obj.environmentId = environment.id; + obj.config = config; obj.condition = this.condition; obj.condition.status = ""; this.$post('/api/testcase/batch/run', obj, () => { @@ -558,7 +561,7 @@ export default { } else { this.$store.state.currentApiCase = {case: true}; } - this.search(); + this.$refs.taskCenter.open(); }); }, customHeader() {