diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsHTTPSamplerProxy.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsHTTPSamplerProxy.java index dbfa8b8173..967bedb446 100644 --- a/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsHTTPSamplerProxy.java +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsHTTPSamplerProxy.java @@ -265,7 +265,7 @@ public class MsHTTPSamplerProxy extends MsTestElement { private Arguments httpArguments(List list) { Arguments arguments = new Arguments(); list.stream().filter(KeyValue::isValid).filter(KeyValue::isEnable).forEach(keyValue -> { - HTTPArgument httpArgument = new HTTPArgument(keyValue.getName(), keyValue.getValue() != null && keyValue.getValue().startsWith("@") ? ScriptEngineUtils.calculate(keyValue.getValue()) : keyValue.getValue()); + HTTPArgument httpArgument = new HTTPArgument(keyValue.getName(), StringUtils.isNotEmpty(keyValue.getValue()) && keyValue.getValue().startsWith("@") ? ScriptEngineUtils.calculate(keyValue.getValue()) : keyValue.getValue()); httpArgument.setAlwaysEncoded(keyValue.isEncode()); if (StringUtils.isNotBlank(keyValue.getContentType())) { httpArgument.setContentType(keyValue.getContentType()); diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsJDBCSampler.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsJDBCSampler.java index a943d13ccb..d9b3ecd4ae 100644 --- a/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsJDBCSampler.java +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsJDBCSampler.java @@ -1,14 +1,21 @@ package io.metersphere.api.dto.definition.request.sampler; +import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.annotation.JSONField; import com.alibaba.fastjson.annotation.JSONType; import io.metersphere.api.dto.definition.request.MsTestElement; import io.metersphere.api.dto.definition.request.ParameterConfig; import io.metersphere.api.dto.scenario.DatabaseConfig; import io.metersphere.api.dto.scenario.KeyValue; +import io.metersphere.api.dto.scenario.environment.EnvironmentConfig; +import io.metersphere.api.service.ApiTestEnvironmentService; +import io.metersphere.base.domain.ApiTestEnvironmentWithBLOBs; +import io.metersphere.commons.exception.MSException; +import io.metersphere.commons.utils.CommonBeanFactory; import lombok.Data; import lombok.EqualsAndHashCode; import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.jmeter.config.Arguments; import org.apache.jmeter.protocol.jdbc.config.DataSourceElement; import org.apache.jmeter.protocol.jdbc.sampler.JDBCSampler; @@ -17,6 +24,7 @@ import org.apache.jmeter.testelement.TestElement; import org.apache.jorphan.collections.HashTree; import java.util.List; +import java.util.stream.Collectors; @Data @EqualsAndHashCode(callSuper = true) @@ -40,6 +48,8 @@ public class MsJDBCSampler extends MsTestElement { private String environmentId; @JSONField(ordinal = 27) private Object requestResult; + @JSONField(ordinal = 28) + private String dataSourceId; public void toHashTree(HashTree tree, List hashTree, ParameterConfig config) { if (!this.isEnable()) { @@ -48,6 +58,12 @@ public class MsJDBCSampler extends MsTestElement { if (this.getReferenced() != null && this.getReferenced().equals("REF")) { this.getRefElement(this); } + if (StringUtils.isNotEmpty(dataSourceId)) { + initDataSource(); + } + if (this.dataSource == null) { + MSException.throwException("数据源为空无法执行"); + } final HashTree samplerHashTree = tree.add(jdbcSampler()); tree.add(jdbcDataSource()); tree.add(arguments(this.getName() + " Variables", this.getVariables())); @@ -58,6 +74,20 @@ public class MsJDBCSampler extends MsTestElement { } } + private void initDataSource() { + ApiTestEnvironmentService environmentService = CommonBeanFactory.getBean(ApiTestEnvironmentService.class); + ApiTestEnvironmentWithBLOBs environment = environmentService.get(this.dataSourceId); + if (environment != null && environment.getConfig() != null) { + EnvironmentConfig config = JSONObject.parseObject(environment.getConfig(), EnvironmentConfig.class); + if (CollectionUtils.isNotEmpty(config.getDatabaseConfigs())) { + List databaseConfigs = config.getDatabaseConfigs().stream().filter((DatabaseConfig d) -> this.dataSourceId.equals(d.getId())).collect(Collectors.toList()); + if (CollectionUtils.isNotEmpty(databaseConfigs)) { + this.dataSource = databaseConfigs.get(0); + } + } + } + } + private Arguments arguments(String name, List variables) { Arguments arguments = new Arguments(); if (!variables.isEmpty()) { diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/KeyValue.java b/backend/src/main/java/io/metersphere/api/dto/scenario/KeyValue.java index 2d5191ac4e..5a7e978ac6 100644 --- a/backend/src/main/java/io/metersphere/api/dto/scenario/KeyValue.java +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/KeyValue.java @@ -49,7 +49,7 @@ public class KeyValue { } public boolean isValid() { - return (StringUtils.isNotBlank(name) || StringUtils.isNotBlank(value)) && !StringUtils.equalsIgnoreCase(type, "file"); + return (StringUtils.isNotBlank(name) && StringUtils.isNotBlank(value)) && !StringUtils.equalsIgnoreCase(type, "file"); } public boolean isFile() { 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 574dd2b54e..84c2f8b83d 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java @@ -528,15 +528,12 @@ public class ApiAutomationService { } public void createSchedule(Schedule request) { - Schedule schedule = scheduleService.buildApiTestSchedule(request); schedule.setJob(ApiScenarioTestJob.class.getName()); schedule.setGroup(ScheduleGroup.API_SCENARIO_TEST.name()); schedule.setType(ScheduleType.CRON.name()); - scheduleService.addSchedule(schedule); this.addOrUpdateApiScenarioCronJob(request); - } public void updateSchedule(Schedule request) { diff --git a/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java b/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java index 570aa5d17b..247da9e765 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java @@ -2,6 +2,9 @@ package io.metersphere.api.service; import com.alibaba.fastjson.JSON; 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.APIReportResult; import io.metersphere.api.dto.ApiTestImportRequest; import io.metersphere.api.dto.automation.ApiScenarioRequest; @@ -9,6 +12,8 @@ import io.metersphere.api.dto.automation.ReferenceDTO; import io.metersphere.api.dto.datacount.ApiDataCountResult; import io.metersphere.api.dto.definition.*; import io.metersphere.api.dto.definition.parse.ApiDefinitionImport; +import io.metersphere.api.dto.definition.request.*; +import io.metersphere.api.dto.scenario.KeyValue; import io.metersphere.api.dto.scenario.request.RequestType; import io.metersphere.api.jmeter.JMeterService; import io.metersphere.api.jmeter.TestResult; @@ -24,6 +29,7 @@ import io.metersphere.base.mapper.ext.ExtApiScenarioMapper; import io.metersphere.base.mapper.ext.ExtTestPlanMapper; import io.metersphere.commons.constants.APITestStatus; import io.metersphere.commons.constants.ApiRunMode; +import io.metersphere.commons.constants.ReportTriggerMode; import io.metersphere.commons.exception.MSException; import io.metersphere.commons.utils.*; import io.metersphere.i18n.Translator; @@ -36,6 +42,7 @@ 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.aspectj.util.FileUtil; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -333,6 +340,55 @@ public class ApiDefinitionService { return request.getId(); } + /** + * 内部构建HashTree 定时任务发起的执行 + * @param request + * @return + */ + public String run(RunDefinitionRequest request,ApiTestCaseWithBLOBs item) { + MsTestPlan testPlan = new MsTestPlan(); + testPlan.setHashTree(new LinkedList<>()); + HashTree jmeterHashTree = new ListedHashTree(); + try { + MsThreadGroup group = new MsThreadGroup(); + group.setLabel(item.getName()); + group.setName(UUID.randomUUID().toString()); + ObjectMapper mapper = new ObjectMapper(); + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + + JSONObject element = JSON.parseObject(item.getRequest()); + MsScenario scenario = JSONObject.parseObject(item.getRequest(), 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 (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); + group.setHashTree(scenarios); + testPlan.getHashTree().add(group); + } catch (Exception ex) { + MSException.throwException(ex.getMessage()); + } + + testPlan.toHashTree(jmeterHashTree, testPlan.getHashTree(), new ParameterConfig()); + String runMode = ApiRunMode.DELIMIT.name(); + if (StringUtils.isNotBlank(request.getType()) && StringUtils.equals(request.getType(), ApiRunMode.API_PLAN.name())) { + runMode = ApiRunMode.API_PLAN.name(); + } + // 调用执行方法 + jMeterService.runDefinition(request.getId(), jmeterHashTree, request.getReportId(), runMode); + return request.getId(); + } + public void addResult(TestResult res) { if (!res.getScenarios().isEmpty() && !res.getScenarios().get(0).getRequestResults().isEmpty()) { cache.put(res.getTestId(), res.getScenarios().get(0).getRequestResults().get(0)); diff --git a/backend/src/main/java/io/metersphere/api/service/HistoricalDataUpgradeService.java b/backend/src/main/java/io/metersphere/api/service/HistoricalDataUpgradeService.java index cb8aeb52fa..f9b028c29a 100644 --- a/backend/src/main/java/io/metersphere/api/service/HistoricalDataUpgradeService.java +++ b/backend/src/main/java/io/metersphere/api/service/HistoricalDataUpgradeService.java @@ -134,6 +134,7 @@ public class HistoricalDataUpgradeService { EnvironmentDTO dto = environmentDTOMap.get(request1.getDataSource()); if (dto != null) { ((MsJDBCSampler) element).setEnvironmentId(dto.getEnvironmentId()); + ((MsJDBCSampler) element).setDataSourceId(dto.getDatabaseConfig().getId()); ((MsJDBCSampler) element).setDataSource(dto.getDatabaseConfig()); } element.setType("JDBCSampler"); diff --git a/backend/src/main/java/io/metersphere/base/domain/Schedule.java b/backend/src/main/java/io/metersphere/base/domain/Schedule.java index d38b68ed63..7637d9f699 100644 --- a/backend/src/main/java/io/metersphere/base/domain/Schedule.java +++ b/backend/src/main/java/io/metersphere/base/domain/Schedule.java @@ -32,4 +32,7 @@ public class Schedule implements Serializable { private String customData; private static final long serialVersionUID = 1L; + + //定时任务来源: 测试计划/测试场景 + private String scheduleFrom; } \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/domain/TestPlanLoadCase.java b/backend/src/main/java/io/metersphere/base/domain/TestPlanLoadCase.java new file mode 100644 index 0000000000..a57ce07329 --- /dev/null +++ b/backend/src/main/java/io/metersphere/base/domain/TestPlanLoadCase.java @@ -0,0 +1,21 @@ +package io.metersphere.base.domain; + +import java.io.Serializable; +import lombok.Data; + +@Data +public class TestPlanLoadCase implements Serializable { + private String id; + + private String testPlanId; + + private String loadCaseId; + + private String status; + + private Long createTime; + + private Long updateTime; + + private static final long serialVersionUID = 1L; +} \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/domain/TestPlanLoadCaseExample.java b/backend/src/main/java/io/metersphere/base/domain/TestPlanLoadCaseExample.java new file mode 100644 index 0000000000..1184a849c1 --- /dev/null +++ b/backend/src/main/java/io/metersphere/base/domain/TestPlanLoadCaseExample.java @@ -0,0 +1,600 @@ +package io.metersphere.base.domain; + +import java.util.ArrayList; +import java.util.List; + +public class TestPlanLoadCaseExample { + protected String orderByClause; + + protected boolean distinct; + + protected List oredCriteria; + + public TestPlanLoadCaseExample() { + oredCriteria = new ArrayList(); + } + + public void setOrderByClause(String orderByClause) { + this.orderByClause = orderByClause; + } + + public String getOrderByClause() { + return orderByClause; + } + + public void setDistinct(boolean distinct) { + this.distinct = distinct; + } + + public boolean isDistinct() { + return distinct; + } + + public List getOredCriteria() { + return oredCriteria; + } + + public void or(Criteria criteria) { + oredCriteria.add(criteria); + } + + public Criteria or() { + Criteria criteria = createCriteriaInternal(); + oredCriteria.add(criteria); + return criteria; + } + + public Criteria createCriteria() { + Criteria criteria = createCriteriaInternal(); + if (oredCriteria.size() == 0) { + oredCriteria.add(criteria); + } + return criteria; + } + + protected Criteria createCriteriaInternal() { + Criteria criteria = new Criteria(); + return criteria; + } + + public void clear() { + oredCriteria.clear(); + orderByClause = null; + distinct = false; + } + + protected abstract static class GeneratedCriteria { + protected List criteria; + + protected GeneratedCriteria() { + super(); + criteria = new ArrayList(); + } + + public boolean isValid() { + return criteria.size() > 0; + } + + public List getAllCriteria() { + return criteria; + } + + public List getCriteria() { + return criteria; + } + + protected void addCriterion(String condition) { + if (condition == null) { + throw new RuntimeException("Value for condition cannot be null"); + } + criteria.add(new Criterion(condition)); + } + + protected void addCriterion(String condition, Object value, String property) { + if (value == null) { + throw new RuntimeException("Value for " + property + " cannot be null"); + } + criteria.add(new Criterion(condition, value)); + } + + protected void addCriterion(String condition, Object value1, Object value2, String property) { + if (value1 == null || value2 == null) { + throw new RuntimeException("Between values for " + property + " cannot be null"); + } + criteria.add(new Criterion(condition, value1, value2)); + } + + public Criteria andIdIsNull() { + addCriterion("id is null"); + return (Criteria) this; + } + + public Criteria andIdIsNotNull() { + addCriterion("id is not null"); + return (Criteria) this; + } + + public Criteria andIdEqualTo(String value) { + addCriterion("id =", value, "id"); + return (Criteria) this; + } + + public Criteria andIdNotEqualTo(String value) { + addCriterion("id <>", value, "id"); + return (Criteria) this; + } + + public Criteria andIdGreaterThan(String value) { + addCriterion("id >", value, "id"); + return (Criteria) this; + } + + public Criteria andIdGreaterThanOrEqualTo(String value) { + addCriterion("id >=", value, "id"); + return (Criteria) this; + } + + public Criteria andIdLessThan(String value) { + addCriterion("id <", value, "id"); + return (Criteria) this; + } + + public Criteria andIdLessThanOrEqualTo(String value) { + addCriterion("id <=", value, "id"); + return (Criteria) this; + } + + public Criteria andIdLike(String value) { + addCriterion("id like", value, "id"); + return (Criteria) this; + } + + public Criteria andIdNotLike(String value) { + addCriterion("id not like", value, "id"); + return (Criteria) this; + } + + public Criteria andIdIn(List values) { + addCriterion("id in", values, "id"); + return (Criteria) this; + } + + public Criteria andIdNotIn(List values) { + addCriterion("id not in", values, "id"); + return (Criteria) this; + } + + public Criteria andIdBetween(String value1, String value2) { + addCriterion("id between", value1, value2, "id"); + return (Criteria) this; + } + + public Criteria andIdNotBetween(String value1, String value2) { + addCriterion("id not between", value1, value2, "id"); + return (Criteria) this; + } + + public Criteria andTestPlanIdIsNull() { + addCriterion("test_plan_id is null"); + return (Criteria) this; + } + + public Criteria andTestPlanIdIsNotNull() { + addCriterion("test_plan_id is not null"); + return (Criteria) this; + } + + public Criteria andTestPlanIdEqualTo(String value) { + addCriterion("test_plan_id =", value, "testPlanId"); + return (Criteria) this; + } + + public Criteria andTestPlanIdNotEqualTo(String value) { + addCriterion("test_plan_id <>", value, "testPlanId"); + return (Criteria) this; + } + + public Criteria andTestPlanIdGreaterThan(String value) { + addCriterion("test_plan_id >", value, "testPlanId"); + return (Criteria) this; + } + + public Criteria andTestPlanIdGreaterThanOrEqualTo(String value) { + addCriterion("test_plan_id >=", value, "testPlanId"); + return (Criteria) this; + } + + public Criteria andTestPlanIdLessThan(String value) { + addCriterion("test_plan_id <", value, "testPlanId"); + return (Criteria) this; + } + + public Criteria andTestPlanIdLessThanOrEqualTo(String value) { + addCriterion("test_plan_id <=", value, "testPlanId"); + return (Criteria) this; + } + + public Criteria andTestPlanIdLike(String value) { + addCriterion("test_plan_id like", value, "testPlanId"); + return (Criteria) this; + } + + public Criteria andTestPlanIdNotLike(String value) { + addCriterion("test_plan_id not like", value, "testPlanId"); + return (Criteria) this; + } + + public Criteria andTestPlanIdIn(List values) { + addCriterion("test_plan_id in", values, "testPlanId"); + return (Criteria) this; + } + + public Criteria andTestPlanIdNotIn(List values) { + addCriterion("test_plan_id not in", values, "testPlanId"); + return (Criteria) this; + } + + public Criteria andTestPlanIdBetween(String value1, String value2) { + addCriterion("test_plan_id between", value1, value2, "testPlanId"); + return (Criteria) this; + } + + public Criteria andTestPlanIdNotBetween(String value1, String value2) { + addCriterion("test_plan_id not between", value1, value2, "testPlanId"); + return (Criteria) this; + } + + public Criteria andLoadCaseIdIsNull() { + addCriterion("load_case_id is null"); + return (Criteria) this; + } + + public Criteria andLoadCaseIdIsNotNull() { + addCriterion("load_case_id is not null"); + return (Criteria) this; + } + + public Criteria andLoadCaseIdEqualTo(String value) { + addCriterion("load_case_id =", value, "loadCaseId"); + return (Criteria) this; + } + + public Criteria andLoadCaseIdNotEqualTo(String value) { + addCriterion("load_case_id <>", value, "loadCaseId"); + return (Criteria) this; + } + + public Criteria andLoadCaseIdGreaterThan(String value) { + addCriterion("load_case_id >", value, "loadCaseId"); + return (Criteria) this; + } + + public Criteria andLoadCaseIdGreaterThanOrEqualTo(String value) { + addCriterion("load_case_id >=", value, "loadCaseId"); + return (Criteria) this; + } + + public Criteria andLoadCaseIdLessThan(String value) { + addCriterion("load_case_id <", value, "loadCaseId"); + return (Criteria) this; + } + + public Criteria andLoadCaseIdLessThanOrEqualTo(String value) { + addCriterion("load_case_id <=", value, "loadCaseId"); + return (Criteria) this; + } + + public Criteria andLoadCaseIdLike(String value) { + addCriterion("load_case_id like", value, "loadCaseId"); + return (Criteria) this; + } + + public Criteria andLoadCaseIdNotLike(String value) { + addCriterion("load_case_id not like", value, "loadCaseId"); + return (Criteria) this; + } + + public Criteria andLoadCaseIdIn(List values) { + addCriterion("load_case_id in", values, "loadCaseId"); + return (Criteria) this; + } + + public Criteria andLoadCaseIdNotIn(List values) { + addCriterion("load_case_id not in", values, "loadCaseId"); + return (Criteria) this; + } + + public Criteria andLoadCaseIdBetween(String value1, String value2) { + addCriterion("load_case_id between", value1, value2, "loadCaseId"); + return (Criteria) this; + } + + public Criteria andLoadCaseIdNotBetween(String value1, String value2) { + addCriterion("load_case_id not between", value1, value2, "loadCaseId"); + return (Criteria) this; + } + + public Criteria andStatusIsNull() { + addCriterion("`status` is null"); + return (Criteria) this; + } + + public Criteria andStatusIsNotNull() { + addCriterion("`status` is not null"); + return (Criteria) this; + } + + public Criteria andStatusEqualTo(String value) { + addCriterion("`status` =", value, "status"); + return (Criteria) this; + } + + public Criteria andStatusNotEqualTo(String value) { + addCriterion("`status` <>", value, "status"); + return (Criteria) this; + } + + public Criteria andStatusGreaterThan(String value) { + addCriterion("`status` >", value, "status"); + return (Criteria) this; + } + + public Criteria andStatusGreaterThanOrEqualTo(String value) { + addCriterion("`status` >=", value, "status"); + return (Criteria) this; + } + + public Criteria andStatusLessThan(String value) { + addCriterion("`status` <", value, "status"); + return (Criteria) this; + } + + public Criteria andStatusLessThanOrEqualTo(String value) { + addCriterion("`status` <=", value, "status"); + return (Criteria) this; + } + + public Criteria andStatusLike(String value) { + addCriterion("`status` like", value, "status"); + return (Criteria) this; + } + + public Criteria andStatusNotLike(String value) { + addCriterion("`status` not like", value, "status"); + return (Criteria) this; + } + + public Criteria andStatusIn(List values) { + addCriterion("`status` in", values, "status"); + return (Criteria) this; + } + + public Criteria andStatusNotIn(List values) { + addCriterion("`status` not in", values, "status"); + return (Criteria) this; + } + + public Criteria andStatusBetween(String value1, String value2) { + addCriterion("`status` between", value1, value2, "status"); + return (Criteria) this; + } + + public Criteria andStatusNotBetween(String value1, String value2) { + addCriterion("`status` not between", value1, value2, "status"); + return (Criteria) this; + } + + public Criteria andCreateTimeIsNull() { + addCriterion("create_time is null"); + return (Criteria) this; + } + + public Criteria andCreateTimeIsNotNull() { + addCriterion("create_time is not null"); + return (Criteria) this; + } + + public Criteria andCreateTimeEqualTo(Long value) { + addCriterion("create_time =", value, "createTime"); + return (Criteria) this; + } + + public Criteria andCreateTimeNotEqualTo(Long value) { + addCriterion("create_time <>", value, "createTime"); + return (Criteria) this; + } + + public Criteria andCreateTimeGreaterThan(Long value) { + addCriterion("create_time >", value, "createTime"); + return (Criteria) this; + } + + public Criteria andCreateTimeGreaterThanOrEqualTo(Long value) { + addCriterion("create_time >=", value, "createTime"); + return (Criteria) this; + } + + public Criteria andCreateTimeLessThan(Long value) { + addCriterion("create_time <", value, "createTime"); + return (Criteria) this; + } + + public Criteria andCreateTimeLessThanOrEqualTo(Long value) { + addCriterion("create_time <=", value, "createTime"); + return (Criteria) this; + } + + public Criteria andCreateTimeIn(List values) { + addCriterion("create_time in", values, "createTime"); + return (Criteria) this; + } + + public Criteria andCreateTimeNotIn(List values) { + addCriterion("create_time not in", values, "createTime"); + return (Criteria) this; + } + + public Criteria andCreateTimeBetween(Long value1, Long value2) { + addCriterion("create_time between", value1, value2, "createTime"); + return (Criteria) this; + } + + public Criteria andCreateTimeNotBetween(Long value1, Long value2) { + addCriterion("create_time not between", value1, value2, "createTime"); + return (Criteria) this; + } + + public Criteria andUpdateTimeIsNull() { + addCriterion("update_time is null"); + return (Criteria) this; + } + + public Criteria andUpdateTimeIsNotNull() { + addCriterion("update_time is not null"); + return (Criteria) this; + } + + public Criteria andUpdateTimeEqualTo(Long value) { + addCriterion("update_time =", value, "updateTime"); + return (Criteria) this; + } + + public Criteria andUpdateTimeNotEqualTo(Long value) { + addCriterion("update_time <>", value, "updateTime"); + return (Criteria) this; + } + + public Criteria andUpdateTimeGreaterThan(Long value) { + addCriterion("update_time >", value, "updateTime"); + return (Criteria) this; + } + + public Criteria andUpdateTimeGreaterThanOrEqualTo(Long value) { + addCriterion("update_time >=", value, "updateTime"); + return (Criteria) this; + } + + public Criteria andUpdateTimeLessThan(Long value) { + addCriterion("update_time <", value, "updateTime"); + return (Criteria) this; + } + + public Criteria andUpdateTimeLessThanOrEqualTo(Long value) { + addCriterion("update_time <=", value, "updateTime"); + return (Criteria) this; + } + + public Criteria andUpdateTimeIn(List values) { + addCriterion("update_time in", values, "updateTime"); + return (Criteria) this; + } + + public Criteria andUpdateTimeNotIn(List values) { + addCriterion("update_time not in", values, "updateTime"); + return (Criteria) this; + } + + public Criteria andUpdateTimeBetween(Long value1, Long value2) { + addCriterion("update_time between", value1, value2, "updateTime"); + return (Criteria) this; + } + + public Criteria andUpdateTimeNotBetween(Long value1, Long value2) { + addCriterion("update_time not between", value1, value2, "updateTime"); + return (Criteria) this; + } + } + + public static class Criteria extends GeneratedCriteria { + + protected Criteria() { + super(); + } + } + + public static class Criterion { + private String condition; + + private Object value; + + private Object secondValue; + + private boolean noValue; + + private boolean singleValue; + + private boolean betweenValue; + + private boolean listValue; + + private String typeHandler; + + public String getCondition() { + return condition; + } + + public Object getValue() { + return value; + } + + public Object getSecondValue() { + return secondValue; + } + + public boolean isNoValue() { + return noValue; + } + + public boolean isSingleValue() { + return singleValue; + } + + public boolean isBetweenValue() { + return betweenValue; + } + + public boolean isListValue() { + return listValue; + } + + public String getTypeHandler() { + return typeHandler; + } + + protected Criterion(String condition) { + super(); + this.condition = condition; + this.typeHandler = null; + this.noValue = true; + } + + protected Criterion(String condition, Object value, String typeHandler) { + super(); + this.condition = condition; + this.value = value; + this.typeHandler = typeHandler; + if (value instanceof List) { + this.listValue = true; + } else { + this.singleValue = true; + } + } + + protected Criterion(String condition, Object value) { + this(condition, value, null); + } + + protected Criterion(String condition, Object value, Object secondValue, String typeHandler) { + super(); + this.condition = condition; + this.value = value; + this.secondValue = secondValue; + this.typeHandler = typeHandler; + this.betweenValue = true; + } + + protected Criterion(String condition, Object value, Object secondValue) { + this(condition, value, secondValue, null); + } + } +} \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/mapper/TestPlanLoadCaseMapper.java b/backend/src/main/java/io/metersphere/base/mapper/TestPlanLoadCaseMapper.java new file mode 100644 index 0000000000..e7e7a608d7 --- /dev/null +++ b/backend/src/main/java/io/metersphere/base/mapper/TestPlanLoadCaseMapper.java @@ -0,0 +1,30 @@ +package io.metersphere.base.mapper; + +import io.metersphere.base.domain.TestPlanLoadCase; +import io.metersphere.base.domain.TestPlanLoadCaseExample; +import java.util.List; +import org.apache.ibatis.annotations.Param; + +public interface TestPlanLoadCaseMapper { + long countByExample(TestPlanLoadCaseExample example); + + int deleteByExample(TestPlanLoadCaseExample example); + + int deleteByPrimaryKey(String id); + + int insert(TestPlanLoadCase record); + + int insertSelective(TestPlanLoadCase record); + + List selectByExample(TestPlanLoadCaseExample example); + + TestPlanLoadCase selectByPrimaryKey(String id); + + int updateByExampleSelective(@Param("record") TestPlanLoadCase record, @Param("example") TestPlanLoadCaseExample example); + + int updateByExample(@Param("record") TestPlanLoadCase record, @Param("example") TestPlanLoadCaseExample example); + + int updateByPrimaryKeySelective(TestPlanLoadCase record); + + int updateByPrimaryKey(TestPlanLoadCase record); +} \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/mapper/TestPlanLoadCaseMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/TestPlanLoadCaseMapper.xml new file mode 100644 index 0000000000..1d2b1cea37 --- /dev/null +++ b/backend/src/main/java/io/metersphere/base/mapper/TestPlanLoadCaseMapper.xml @@ -0,0 +1,228 @@ + + + + + + + + + + + + + + + + + + + + and ${criterion.condition} + + + and ${criterion.condition} #{criterion.value} + + + and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} + + + and ${criterion.condition} + + #{listItem} + + + + + + + + + + + + + + + + + + and ${criterion.condition} + + + and ${criterion.condition} #{criterion.value} + + + and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} + + + and ${criterion.condition} + + #{listItem} + + + + + + + + + + + id, test_plan_id, load_case_id, `status`, create_time, update_time + + + + + delete from test_plan_load_case + where id = #{id,jdbcType=VARCHAR} + + + delete from test_plan_load_case + + + + + + insert into test_plan_load_case (id, test_plan_id, load_case_id, + `status`, create_time, update_time + ) + values (#{id,jdbcType=VARCHAR}, #{testPlanId,jdbcType=VARCHAR}, #{loadCaseId,jdbcType=VARCHAR}, + #{status,jdbcType=VARCHAR}, #{createTime,jdbcType=BIGINT}, #{updateTime,jdbcType=BIGINT} + ) + + + insert into test_plan_load_case + + + id, + + + test_plan_id, + + + load_case_id, + + + `status`, + + + create_time, + + + update_time, + + + + + #{id,jdbcType=VARCHAR}, + + + #{testPlanId,jdbcType=VARCHAR}, + + + #{loadCaseId,jdbcType=VARCHAR}, + + + #{status,jdbcType=VARCHAR}, + + + #{createTime,jdbcType=BIGINT}, + + + #{updateTime,jdbcType=BIGINT}, + + + + + + update test_plan_load_case + + + id = #{record.id,jdbcType=VARCHAR}, + + + test_plan_id = #{record.testPlanId,jdbcType=VARCHAR}, + + + load_case_id = #{record.loadCaseId,jdbcType=VARCHAR}, + + + `status` = #{record.status,jdbcType=VARCHAR}, + + + create_time = #{record.createTime,jdbcType=BIGINT}, + + + update_time = #{record.updateTime,jdbcType=BIGINT}, + + + + + + + + update test_plan_load_case + set id = #{record.id,jdbcType=VARCHAR}, + test_plan_id = #{record.testPlanId,jdbcType=VARCHAR}, + load_case_id = #{record.loadCaseId,jdbcType=VARCHAR}, + `status` = #{record.status,jdbcType=VARCHAR}, + create_time = #{record.createTime,jdbcType=BIGINT}, + update_time = #{record.updateTime,jdbcType=BIGINT} + + + + + + update test_plan_load_case + + + test_plan_id = #{testPlanId,jdbcType=VARCHAR}, + + + load_case_id = #{loadCaseId,jdbcType=VARCHAR}, + + + `status` = #{status,jdbcType=VARCHAR}, + + + create_time = #{createTime,jdbcType=BIGINT}, + + + update_time = #{updateTime,jdbcType=BIGINT}, + + + where id = #{id,jdbcType=VARCHAR} + + + update test_plan_load_case + set test_plan_id = #{testPlanId,jdbcType=VARCHAR}, + load_case_id = #{loadCaseId,jdbcType=VARCHAR}, + `status` = #{status,jdbcType=VARCHAR}, + create_time = #{createTime,jdbcType=BIGINT}, + update_time = #{updateTime,jdbcType=BIGINT} + where id = #{id,jdbcType=VARCHAR} + + \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanLoadCaseMapper.java b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanLoadCaseMapper.java new file mode 100644 index 0000000000..590e9605d7 --- /dev/null +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanLoadCaseMapper.java @@ -0,0 +1,12 @@ +package io.metersphere.base.mapper.ext; + +import io.metersphere.track.dto.TestPlanLoadCaseDTO; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +public interface ExtTestPlanLoadCaseMapper { + + List selectIdsNotInPlan(@Param("projectId") String projectId, @Param("planId") String planId); + List selectTestPlanLoadCaseList(@Param("planId") String planId); +} diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanLoadCaseMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanLoadCaseMapper.xml new file mode 100644 index 0000000000..ab78f44e71 --- /dev/null +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanLoadCaseMapper.xml @@ -0,0 +1,21 @@ + + + + + + + \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/commons/constants/ScheduleGroup.java b/backend/src/main/java/io/metersphere/commons/constants/ScheduleGroup.java index 4a21f86850..90b1c5aba7 100644 --- a/backend/src/main/java/io/metersphere/commons/constants/ScheduleGroup.java +++ b/backend/src/main/java/io/metersphere/commons/constants/ScheduleGroup.java @@ -1,5 +1,5 @@ package io.metersphere.commons.constants; public enum ScheduleGroup { - API_TEST, PERFORMANCE_TEST, API_SCENARIO_TEST + API_TEST, PERFORMANCE_TEST, API_SCENARIO_TEST,TEST_PLAN_TEST } diff --git a/backend/src/main/java/io/metersphere/controller/ScheduleController.java b/backend/src/main/java/io/metersphere/controller/ScheduleController.java index 26e0f7084b..23ae6a9c4d 100644 --- a/backend/src/main/java/io/metersphere/controller/ScheduleController.java +++ b/backend/src/main/java/io/metersphere/controller/ScheduleController.java @@ -2,6 +2,7 @@ package io.metersphere.controller; import com.github.pagehelper.Page; import com.github.pagehelper.PageHelper; +import io.metersphere.api.service.ApiAutomationService; import io.metersphere.base.domain.Schedule; import io.metersphere.controller.request.QueryScheduleRequest; import io.metersphere.dto.ScheduleDao; @@ -16,6 +17,8 @@ import java.util.List; public class ScheduleController { @Resource private ScheduleService scheduleService; + @Resource + private ApiAutomationService apiAutomationService; @PostMapping("/list/{goPage}/{pageSize}") public List list(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody QueryScheduleRequest request) { @@ -28,4 +31,19 @@ public class ScheduleController { Schedule schedule = scheduleService.getScheduleByResource(testId,group); return schedule; } + + @PostMapping(value = "/update") + public void updateSchedule(@RequestBody Schedule request) { + scheduleService.updateSchedule(request); + } + + @PostMapping(value = "/create") + public void createSchedule(@RequestBody Schedule request) { + scheduleService.createSchedule(request); + } + + @GetMapping(value = "/getTaskInfo") + public Object getTaskInfo() { + return scheduleService.getCurrentlyExecutingJobs(); + } } diff --git a/backend/src/main/java/io/metersphere/job/sechedule/ScheduleManager.java b/backend/src/main/java/io/metersphere/job/sechedule/ScheduleManager.java index d37956b7a9..7c64968203 100644 --- a/backend/src/main/java/io/metersphere/job/sechedule/ScheduleManager.java +++ b/backend/src/main/java/io/metersphere/job/sechedule/ScheduleManager.java @@ -7,7 +7,9 @@ import org.quartz.*; import org.springframework.stereotype.Component; import javax.annotation.Resource; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Set; @Component @@ -296,4 +298,21 @@ public class ScheduleManager { return jobDataMap; } + public Object getCurrentlyExecutingJobs(){ + Map returnMap = new HashMap<>(); + try { + List currentJobs = scheduler.getCurrentlyExecutingJobs(); + for (JobExecutionContext jobCtx : currentJobs) { + String jobName = jobCtx.getJobDetail().getKey().getName(); + String groupName = jobCtx.getJobDetail().getJobClass().getName(); + + returnMap.put("jobName", jobName); + returnMap.put("groupName", groupName); + } + }catch (Exception e){ + e.printStackTrace(); + } + + return returnMap; + } } diff --git a/backend/src/main/java/io/metersphere/job/sechedule/TestPlanTestJob.java b/backend/src/main/java/io/metersphere/job/sechedule/TestPlanTestJob.java new file mode 100644 index 0000000000..2d4b293867 --- /dev/null +++ b/backend/src/main/java/io/metersphere/job/sechedule/TestPlanTestJob.java @@ -0,0 +1,130 @@ +package io.metersphere.job.sechedule; + +import io.metersphere.api.dto.automation.ExecuteType; +import io.metersphere.api.dto.automation.RunScenarioRequest; +import io.metersphere.api.dto.definition.RunDefinitionRequest; +import io.metersphere.api.service.ApiAutomationService; +import io.metersphere.api.service.ApiDefinitionService; +import io.metersphere.api.service.ApiTestCaseService; +import io.metersphere.base.domain.ApiTestCaseWithBLOBs; +import io.metersphere.base.domain.TestPlanApiCase; +import io.metersphere.base.domain.TestPlanApiScenario; +import io.metersphere.commons.constants.ApiRunMode; +import io.metersphere.commons.constants.ReportTriggerMode; +import io.metersphere.commons.constants.ScheduleGroup; +import io.metersphere.commons.utils.CommonBeanFactory; +import io.metersphere.commons.utils.LogUtil; +import io.metersphere.performance.service.PerformanceTestService; +import io.metersphere.track.request.testplan.RunTestPlanRequest; +import io.metersphere.track.service.TestPlanApiCaseService; +import io.metersphere.track.service.TestPlanScenarioCaseService; +import org.quartz.*; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +/** + * 情景测试Job + * @author song.tianyang + * @Date 2020/12/22 2:59 下午 + * @Description + */ +public class TestPlanTestJob extends MsScheduleJob { + private String projectID; + private List scenarioIds; + private List apiTestCaseIds; + private List performanceIds; + + private ApiAutomationService apiAutomationService; + private PerformanceTestService performanceTestService; + private TestPlanScenarioCaseService testPlanScenarioCaseService; + private TestPlanApiCaseService testPlanApiCaseService; + private ApiTestCaseService apiTestCaseService; + private ApiDefinitionService apiDefinitionService; + + public TestPlanTestJob() { + this.apiAutomationService = CommonBeanFactory.getBean(ApiAutomationService.class); + this.performanceTestService = CommonBeanFactory.getBean(PerformanceTestService.class); + this.testPlanScenarioCaseService = CommonBeanFactory.getBean(TestPlanScenarioCaseService.class); + this.testPlanApiCaseService = CommonBeanFactory.getBean(TestPlanApiCaseService.class); + this.apiTestCaseService = CommonBeanFactory.getBean(ApiTestCaseService.class); + apiDefinitionService = CommonBeanFactory.getBean(ApiDefinitionService.class); + } + + /** + * 情景部分的准备工作 + * @param context + * @throws JobExecutionException + */ + @Override + public void execute(JobExecutionContext context) throws JobExecutionException { + JobKey jobKey = context.getTrigger().getJobKey(); + JobDataMap jobDataMap = context.getJobDetail().getJobDataMap(); + + this.userId = jobDataMap.getString("userId"); + this.expression = jobDataMap.getString("expression"); + this.projectID = jobDataMap.getString("projectId"); + scenarioIds = new ArrayList<>(); + + String testPlanID = jobDataMap.getString("resourceId"); + List testPlanApiScenarioList = testPlanScenarioCaseService.getCasesByPlanId(testPlanID); + for (TestPlanApiScenario model: + testPlanApiScenarioList) { + scenarioIds.add(model.getApiScenarioId()); + } + List testPlanApiCaseList = testPlanApiCaseService.getCasesByPlanId(testPlanID); + for (TestPlanApiCase model : + testPlanApiCaseList) { + apiTestCaseIds.add(model.getApiCaseId()); + } + + businessExecute(context); + } + + @Override + void businessExecute(JobExecutionContext context) { + LogUtil.info("-------------- start testplan schedule ----------"); + //执行接口案例任务 + for (String apiCaseID:apiTestCaseIds) { + ApiTestCaseWithBLOBs blobs = apiTestCaseService.get(apiCaseID); + String caseReportID = UUID.randomUUID().toString(); + RunDefinitionRequest apiCaseReqeust = new RunDefinitionRequest(); + apiCaseReqeust.setId(apiCaseID); + apiCaseReqeust.setReportId(caseReportID); + apiCaseReqeust.setProjectId(projectID); + apiCaseReqeust.setExecuteType(ExecuteType.Saved.name()); + apiCaseReqeust.setType(ApiRunMode.API_PLAN.name()); + apiDefinitionService.run(apiCaseReqeust,blobs); + } + LogUtil.info("-------------- testplan schedule ---------- api case over -----------------"); + //执行场景执行任务 + RunScenarioRequest scenarioRequest = new RunScenarioRequest(); + String senarionReportID = UUID.randomUUID().toString(); + scenarioRequest.setId(senarionReportID); + scenarioRequest.setReportId(senarionReportID); + scenarioRequest.setProjectId(projectID); + scenarioRequest.setTriggerMode(ReportTriggerMode.SCHEDULE.name()); + scenarioRequest.setExecuteType(ExecuteType.Saved.name()); + scenarioRequest.setScenarioIds(this.scenarioIds); + scenarioRequest.setReportUserID(this.userId); + scenarioRequest.setRunMode(ApiRunMode.SCENARIO_PLAN.name()); + String reportID = apiAutomationService.run(scenarioRequest); + LogUtil.info("-------------- testplan schedule ---------- scenario case over -----------------"); + + //执行性能测试任务 --- 保留,待功能实现后再继续 +// RunTestPlanRequest performanceRequest = new RunTestPlanRequest(); +// performanceRequest.setId(resourceId); +// performanceRequest.setUserId(userId); +// performanceRequest.setTriggerMode(ReportTriggerMode.SCHEDULE.name()); +// performanceTestService.run(performanceRequest); + } + + public static JobKey getJobKey(String testId) { + return new JobKey(testId, ScheduleGroup.TEST_PLAN_TEST.name()); + } + + public static TriggerKey getTriggerKey(String testId) { + return new TriggerKey(testId, ScheduleGroup.TEST_PLAN_TEST.name()); + } +} diff --git a/backend/src/main/java/io/metersphere/performance/service/PerformanceTestService.java b/backend/src/main/java/io/metersphere/performance/service/PerformanceTestService.java index 609830785e..191ec24fa2 100644 --- a/backend/src/main/java/io/metersphere/performance/service/PerformanceTestService.java +++ b/backend/src/main/java/io/metersphere/performance/service/PerformanceTestService.java @@ -481,4 +481,14 @@ public class PerformanceTestService { quotaService.checkLoadTestQuota(request, create); } } + + public List getLoadTestListByIds(List ids) { + if (CollectionUtils.isEmpty(ids)) { + return new ArrayList<>(); + } + LoadTestExample loadTestExample = new LoadTestExample(); + loadTestExample.createCriteria().andIdIn(ids); + List loadTests = loadTestMapper.selectByExample(loadTestExample); + return Optional.ofNullable(loadTests).orElse(new ArrayList<>()); + } } diff --git a/backend/src/main/java/io/metersphere/service/ScheduleService.java b/backend/src/main/java/io/metersphere/service/ScheduleService.java index 87b0f7f763..0b387b9f72 100644 --- a/backend/src/main/java/io/metersphere/service/ScheduleService.java +++ b/backend/src/main/java/io/metersphere/service/ScheduleService.java @@ -9,6 +9,8 @@ import io.metersphere.base.domain.UserExample; import io.metersphere.base.mapper.ScheduleMapper; import io.metersphere.base.mapper.UserMapper; import io.metersphere.base.mapper.ext.ExtScheduleMapper; +import io.metersphere.commons.constants.ScheduleGroup; +import io.metersphere.commons.constants.ScheduleType; import io.metersphere.commons.exception.MSException; import io.metersphere.commons.utils.DateUtils; import io.metersphere.commons.utils.LogUtil; @@ -17,8 +19,10 @@ import io.metersphere.commons.utils.SessionUtils; import io.metersphere.controller.request.OrderRequest; import io.metersphere.controller.request.QueryScheduleRequest; import io.metersphere.dto.ScheduleDao; +import io.metersphere.job.sechedule.ApiScenarioTestJob; import io.metersphere.job.sechedule.ApiTestJob; import io.metersphere.job.sechedule.ScheduleManager; +import io.metersphere.job.sechedule.TestPlanTestJob; import org.apache.commons.lang3.StringUtils; import org.quartz.JobKey; import org.quartz.SchedulerException; @@ -188,4 +192,54 @@ public class ScheduleService { List runningTaskInfoList = extScheduleMapper.findRunningTaskInfoByProjectID(projectID); return runningTaskInfoList; } + + public void createSchedule(Schedule request) { + Schedule schedule = this.buildApiTestSchedule(request); + schedule.setJob(ApiScenarioTestJob.class.getName()); + + JobKey jobKey = null; + TriggerKey triggerKey = null; + Class clazz = null; + if("testPlan".equals(request.getScheduleFrom())){ + schedule.setGroup(ScheduleGroup.TEST_PLAN_TEST.name()); + schedule.setType(ScheduleType.CRON.name()); + jobKey = TestPlanTestJob.getJobKey(request.getResourceId()); + triggerKey = TestPlanTestJob.getTriggerKey(request.getResourceId()); + clazz = TestPlanTestJob.class; + }else { + //默认为情景 + schedule.setGroup(ScheduleGroup.API_SCENARIO_TEST.name()); + schedule.setType(ScheduleType.CRON.name()); + jobKey = ApiScenarioTestJob.getJobKey(request.getResourceId()); + triggerKey = ApiScenarioTestJob.getTriggerKey(request.getResourceId()); + clazz = ApiScenarioTestJob.class; + } + this.addSchedule(schedule); + + this.addOrUpdateCronJob(request,jobKey ,triggerKey , clazz); + } + + public void updateSchedule(Schedule request) { + this.editSchedule(request); + + JobKey jobKey = null; + TriggerKey triggerKey = null; + Class clazz = null; + if(ScheduleGroup.TEST_PLAN_TEST.name().equals(request.getGroup())){ + jobKey = TestPlanTestJob.getJobKey(request.getResourceId()); + triggerKey = TestPlanTestJob.getTriggerKey(request.getResourceId()); + clazz = TestPlanTestJob.class; + }else { + //默认为情景 + jobKey = ApiScenarioTestJob.getJobKey(request.getResourceId()); + triggerKey = ApiScenarioTestJob.getTriggerKey(request.getResourceId()); + clazz = ApiScenarioTestJob.class; + } + + this.addOrUpdateCronJob(request,jobKey ,triggerKey , clazz); + } + + public Object getCurrentlyExecutingJobs() { + return scheduleManager.getCurrentlyExecutingJobs(); + } } diff --git a/backend/src/main/java/io/metersphere/track/controller/TestPlanLoadCaseController.java b/backend/src/main/java/io/metersphere/track/controller/TestPlanLoadCaseController.java new file mode 100644 index 0000000000..299ded472b --- /dev/null +++ b/backend/src/main/java/io/metersphere/track/controller/TestPlanLoadCaseController.java @@ -0,0 +1,43 @@ +package io.metersphere.track.controller; + +import com.github.pagehelper.Page; +import com.github.pagehelper.PageHelper; +import io.metersphere.base.domain.LoadTest; +import io.metersphere.commons.utils.PageUtils; +import io.metersphere.commons.utils.Pager; +import io.metersphere.track.dto.TestPlanLoadCaseDTO; +import io.metersphere.track.request.testplan.LoadCaseRequest; +import io.metersphere.track.service.TestPlanLoadCaseService; +import org.springframework.web.bind.annotation.*; +import javax.annotation.Resource; +import java.util.List; + +@RequestMapping("/test/plan/load/case") +@RestController +public class TestPlanLoadCaseController { + + @Resource + private TestPlanLoadCaseService testPlanLoadCaseService; + + @PostMapping("/relevance/list/{goPage}/{pageSize}") + public Pager> relevanceList(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody LoadCaseRequest request) { + Page page = PageHelper.startPage(goPage, pageSize, true); + return PageUtils.setPageInfo(page, testPlanLoadCaseService.relevanceList(request)); + } + + @PostMapping("/relevance") + public void relevanceCase(@RequestBody LoadCaseRequest request) { + testPlanLoadCaseService.relevanceCase(request); + } + + @PostMapping("/list/{goPage}/{pageSize}") + public Pager> list(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody LoadCaseRequest request) { + Page page = PageHelper.startPage(goPage, pageSize, true); + return PageUtils.setPageInfo(page, testPlanLoadCaseService.list(request)); + } + + @GetMapping("/delete/{id}") + public void delete(@PathVariable String id) { + testPlanLoadCaseService.delete(id); + } +} diff --git a/backend/src/main/java/io/metersphere/track/dto/TestPlanLoadCaseDTO.java b/backend/src/main/java/io/metersphere/track/dto/TestPlanLoadCaseDTO.java new file mode 100644 index 0000000000..3e08fed138 --- /dev/null +++ b/backend/src/main/java/io/metersphere/track/dto/TestPlanLoadCaseDTO.java @@ -0,0 +1,12 @@ +package io.metersphere.track.dto; + +import io.metersphere.base.domain.TestPlanLoadCase; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class TestPlanLoadCaseDTO extends TestPlanLoadCase { + private String userName; + private String caseName; +} diff --git a/backend/src/main/java/io/metersphere/track/request/testplan/LoadCaseRequest.java b/backend/src/main/java/io/metersphere/track/request/testplan/LoadCaseRequest.java new file mode 100644 index 0000000000..cfc5b7e5dc --- /dev/null +++ b/backend/src/main/java/io/metersphere/track/request/testplan/LoadCaseRequest.java @@ -0,0 +1,14 @@ +package io.metersphere.track.request.testplan; + +import io.metersphere.base.domain.TestPlanLoadCase; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +public class LoadCaseRequest extends TestPlanLoadCase { + private String projectId; + private List caseIds; +} 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 fd83651fcf..db89c1962e 100644 --- a/backend/src/main/java/io/metersphere/track/service/TestPlanApiCaseService.java +++ b/backend/src/main/java/io/metersphere/track/service/TestPlanApiCaseService.java @@ -1,23 +1,34 @@ package io.metersphere.track.service; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; import io.metersphere.api.dto.definition.ApiTestCaseDTO; import io.metersphere.api.dto.definition.ApiTestCaseRequest; +import io.metersphere.api.dto.definition.RunDefinitionRequest; import io.metersphere.api.dto.definition.TestPlanApiCaseDTO; +import io.metersphere.api.dto.definition.request.MsTestElement; +import io.metersphere.api.dto.definition.request.MsTestPlan; +import io.metersphere.api.dto.definition.request.MsThreadGroup; import io.metersphere.api.service.ApiDefinitionExecResultService; +import io.metersphere.api.service.ApiDefinitionService; import io.metersphere.api.service.ApiTestCaseService; +import io.metersphere.base.domain.ApiTestCaseWithBLOBs; import io.metersphere.base.domain.TestPlanApiCase; import io.metersphere.base.domain.TestPlanApiCaseExample; import io.metersphere.base.mapper.TestPlanApiCaseMapper; import io.metersphere.base.mapper.ext.ExtTestPlanApiCaseMapper; import io.metersphere.commons.utils.ServiceUtils; import io.metersphere.track.request.testcase.TestPlanApiCaseBatchRequest; +import org.apache.jmeter.testelement.TestElement; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; +import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; import java.util.ArrayList; +import java.util.LinkedList; import java.util.List; @Service @@ -30,6 +41,8 @@ public class TestPlanApiCaseService { ApiTestCaseService apiTestCaseService; @Resource ExtTestPlanApiCaseMapper extTestPlanApiCaseMapper; + @Resource + ApiDefinitionService apiDefinitionService; @Lazy @Resource ApiDefinitionExecResultService apiDefinitionExecResultService; diff --git a/backend/src/main/java/io/metersphere/track/service/TestPlanLoadCaseService.java b/backend/src/main/java/io/metersphere/track/service/TestPlanLoadCaseService.java new file mode 100644 index 0000000000..c3a76171fb --- /dev/null +++ b/backend/src/main/java/io/metersphere/track/service/TestPlanLoadCaseService.java @@ -0,0 +1,71 @@ +package io.metersphere.track.service; + +import io.metersphere.base.domain.LoadTest; +import io.metersphere.base.domain.TestPlanLoadCase; +import io.metersphere.base.domain.TestPlanLoadCaseExample; +import io.metersphere.base.mapper.TestPlanLoadCaseMapper; +import io.metersphere.base.mapper.ext.ExtTestPlanLoadCaseMapper; +import io.metersphere.performance.service.PerformanceTestService; +import io.metersphere.track.dto.TestPlanLoadCaseDTO; +import io.metersphere.track.request.testplan.LoadCaseRequest; +import org.apache.ibatis.session.ExecutorType; +import org.apache.ibatis.session.SqlSession; +import org.apache.ibatis.session.SqlSessionFactory; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; + +import javax.annotation.Resource; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +@Service +@Transactional(rollbackFor = Exception.class) +public class TestPlanLoadCaseService { + + @Resource + private TestPlanLoadCaseMapper testPlanLoadCaseMapper; + @Resource + private ExtTestPlanLoadCaseMapper extTestPlanLoadCaseMapper; + @Resource + private PerformanceTestService performanceTestService; + @Resource + private SqlSessionFactory sqlSessionFactory; + + public List relevanceList(LoadCaseRequest request) { + List ids = extTestPlanLoadCaseMapper.selectIdsNotInPlan(request.getProjectId(), request.getTestPlanId()); + if (CollectionUtils.isEmpty(ids)) { + return new ArrayList<>(); + } + return performanceTestService.getLoadTestListByIds(ids); + } + + public List list(LoadCaseRequest request) { + return extTestPlanLoadCaseMapper.selectTestPlanLoadCaseList(request.getTestPlanId()); + } + + public void relevanceCase(LoadCaseRequest request) { + List caseIds = request.getCaseIds(); + String planId = request.getTestPlanId(); + SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH); + + TestPlanLoadCaseMapper testPlanLoadCaseMapper = sqlSession.getMapper(TestPlanLoadCaseMapper.class); + caseIds.forEach(id -> { + TestPlanLoadCase t = new TestPlanLoadCase(); + t.setId(UUID.randomUUID().toString()); + t.setTestPlanId(planId); + t.setLoadCaseId(id); + t.setCreateTime(System.currentTimeMillis()); + t.setUpdateTime(System.currentTimeMillis()); + testPlanLoadCaseMapper.insert(t); + }); + sqlSession.flushStatements(); + } + + public void delete(String id) { + TestPlanLoadCaseExample testPlanLoadCaseExample = new TestPlanLoadCaseExample(); + testPlanLoadCaseExample.createCriteria().andIdEqualTo(id); + testPlanLoadCaseMapper.deleteByExample(testPlanLoadCaseExample); + } +} diff --git a/backend/src/main/resources/db/migration/V62__add_test_plan_load_case.sql b/backend/src/main/resources/db/migration/V62__add_test_plan_load_case.sql new file mode 100644 index 0000000000..d952934a88 --- /dev/null +++ b/backend/src/main/resources/db/migration/V62__add_test_plan_load_case.sql @@ -0,0 +1,12 @@ +CREATE TABLE IF NOT EXISTS `test_plan_load_case` +( + `id` varchar(50) NOT NULL COMMENT 'ID', + `test_plan_id` varchar(50) NOT NULL COMMENT 'Test plan ID', + `load_case_id` varchar(50) NOT NULL COMMENT 'Load test case ID', + `status` varchar(50) DEFAULT NULL COMMENT 'Load case status', + `create_time` bigint(13) NOT NULL COMMENT 'Create timestamp', + `update_time` bigint(13) NOT NULL COMMENT 'Update timestamp', + PRIMARY KEY (`id`), + UNIQUE KEY `plan_load_case_id` (`test_plan_id`, `load_case_id`) +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4; \ No newline at end of file diff --git a/frontend/src/business/components/api/automation/schedule/ScheduleMaintain.vue b/frontend/src/business/components/api/automation/schedule/ScheduleMaintain.vue index d29ce8a103..5202195d4a 100644 --- a/frontend/src/business/components/api/automation/schedule/ScheduleMaintain.vue +++ b/frontend/src/business/components/api/automation/schedule/ScheduleMaintain.vue @@ -30,7 +30,7 @@ + :schedule-receiver-options="scheduleReceiverOptions"/> @@ -55,7 +55,7 @@ const noticeTemplate = requireComponent.keys().length > 0 ? requireComponent("./ export default { name: "MsScheduleMaintain", - components: {CrontabResult, Crontab, MsScheduleNotification,"NoticeTemplate": noticeTemplate.default}, + components: {CrontabResult, Crontab, MsScheduleNotification, "NoticeTemplate": noticeTemplate.default}, props: { customValidate: { @@ -81,8 +81,7 @@ export default { callback(new Error(this.$t('commons.input_content'))); } else if (!cronValidate(cronValue)) { callback(new Error(this.$t('schedule.cron_expression_format_error'))); - } - else if (!customValidate.pass) { + } else if (!customValidate.pass) { callback(new Error(customValidate.info)); } else { callback(); @@ -93,9 +92,10 @@ export default { operation: true, dialogVisible: false, schedule: { - value : "", + value: "", }, - testId:String, + scheduleTaskType: "", + testId: String, showCron: false, form: { cronValue: "" @@ -130,20 +130,30 @@ export default { return param; }, open(row) { - this.testId = row.id; - this.findSchedule(row.id); + //测试计划页面跳转来的 + let paramTestId = ""; + if (row.redirectFrom == 'testPlan') { + paramTestId = row.id; + this.scheduleTaskType = "TEST_PLAN_TEST"; + } else { + paramTestId = row.id; + this.scheduleTaskType = "API_SCENARIO_TEST"; + } + this.testId = paramTestId; + this.findSchedule(paramTestId); this.initUserList(); this.dialogVisible = true; this.form.cronValue = this.schedule.value; listenGoBack(this.close); this.activeName = 'first' }, - findSchedule(){ - var scenarioID = this.testId; - this.result = this.$get("/schedule/findOne/"+scenarioID+"/API_SCENARIO_TEST", response => { - if(response.data!=null){ + findSchedule() { + var scheduleResourceID = this.testId; + var taskType = this.scheduleTaskType; + this.result = this.$get("/schedule/findOne/" + scheduleResourceID + "/" +taskType, response => { + if (response.data != null) { this.schedule = response.data; - }else { + } else { this.schedule = {}; } }); @@ -177,9 +187,20 @@ export default { param = this.schedule; param.resourceId = this.testId; let url = '/api/automation/schedule/create'; - if (param.id) { - url = '/api/automation/schedule/update'; + if(this.scheduleTaskType === "TEST_PLAN_TEST"){ + param.scheduleFrom = "testPlan"; + //测试计划页面跳转的创建 + url = '/schedule/create'; + if (param.id) { + url = '/schedule/update'; + } + }else { + param.scheduleFrom = "scenario"; + if (param.id) { + url = '/api/automation/schedule/update'; + } } + this.$post(url, param, () => { this.$success(this.$t('commons.save_success')); }); diff --git a/frontend/src/business/components/api/definition/components/jmeter/components/sampler/jdbc-sampler/index.js b/frontend/src/business/components/api/definition/components/jmeter/components/sampler/jdbc-sampler/index.js index 571925c643..1ae222608b 100644 --- a/frontend/src/business/components/api/definition/components/jmeter/components/sampler/jdbc-sampler/index.js +++ b/frontend/src/business/components/api/definition/components/jmeter/components/sampler/jdbc-sampler/index.js @@ -17,7 +17,9 @@ export default class JDBCSampler extends Sampler { this.type = "JDBCSampler"; this.hashTree = []; this.variables = []; + this.environmentId = undefined; this.dataSource = undefined; + this.dataSourceId = undefined; this.query = undefined; this.queryType = undefined; this.queryArguments = undefined; diff --git a/frontend/src/business/components/api/definition/components/request/database/BasisParameters.vue b/frontend/src/business/components/api/definition/components/request/database/BasisParameters.vue index 9402456eee..eead171abd 100644 --- a/frontend/src/business/components/api/definition/components/request/database/BasisParameters.vue +++ b/frontend/src/business/components/api/definition/components/request/database/BasisParameters.vue @@ -247,6 +247,7 @@ }, environmentChange(value) { this.request.dataSource = undefined; + this.request.dataSourceId = ""; for (let i in this.environments) { if (this.environments[i].id === value) { this.databaseConfigsOptions = []; diff --git a/frontend/src/business/components/common/components/MsTableOperator.vue b/frontend/src/business/components/common/components/MsTableOperator.vue index 7477ba8c3a..0da52f1145 100644 --- a/frontend/src/business/components/common/components/MsTableOperator.vue +++ b/frontend/src/business/components/common/components/MsTableOperator.vue @@ -4,7 +4,7 @@ - + diff --git a/frontend/src/business/components/track/plan/components/TestPlanList.vue b/frontend/src/business/components/track/plan/components/TestPlanList.vue index 5922c1c469..936002e99f 100644 --- a/frontend/src/business/components/track/plan/components/TestPlanList.vue +++ b/frontend/src/business/components/track/plan/components/TestPlanList.vue @@ -128,19 +128,21 @@ @exec="openReport(scope.row.id, scope.row.reportId)"/> + - {{$t('test_track.plan.plan_delete_tip')}} - + @@ -160,6 +162,7 @@ import MsDeleteConfirm from "../../../common/components/MsDeleteConfirm"; import {TEST_PLAN_CONFIGS} from "../../../common/components/search/search-components"; import {LIST_CHANGE, TrackEvent} from "@/business/components/common/head/ListEvent"; import {getCurrentProjectID} from "../../../../../common/js/utils"; +import MsScheduleMaintain from "@/business/components/api/automation/schedule/ScheduleMaintain" export default { name: "TestPlanList", @@ -169,6 +172,7 @@ export default { TestReportTemplateList, PlanStageTableItem, PlanStatusTableItem, + MsScheduleMaintain, MsTableOperator, MsTableOperatorButton, MsDialogFooter, MsTableHeader, MsCreateBox, MsTablePagination }, data() { @@ -289,6 +293,10 @@ export default { this.$refs.testCaseReportView.open(planId, reportId); } }, + scheduleTask(row){ + row.redirectFrom = "testPlan"; + this.$refs.scheduleMaintain.open(row); + }, } } diff --git a/frontend/src/business/components/track/plan/view/TestPlanView.vue b/frontend/src/business/components/track/plan/view/TestPlanView.vue index 9824c999b8..7f28b8501f 100644 --- a/frontend/src/business/components/track/plan/view/TestPlanView.vue +++ b/frontend/src/business/components/track/plan/view/TestPlanView.vue @@ -14,12 +14,14 @@ class="el-menu-demo header-menu" mode="horizontal" @select="handleSelect"> 功能测试用例 接口测试用例 + 性能测试用例 报告统计 + @@ -42,6 +44,7 @@ import TestPlanApi from "./comonents/api/TestPlanApi"; import TestCaseStatisticsReportView from "./comonents/report/statistics/TestCaseStatisticsReportView"; import TestReportTemplateList from "./comonents/TestReportTemplateList"; + import TestPlanLoad from "@/business/components/track/plan/view/comonents/load/TestPlanLoad"; export default { name: "TestPlanView", @@ -52,7 +55,7 @@ TestPlanFunctional, MsTestPlanHeaderBar, MsMainContainer, - MsAsideContainer, MsContainer, NodeTree, TestPlanTestCaseList, TestCaseRelevance, SelectMenu}, + MsAsideContainer, MsContainer, NodeTree, TestPlanTestCaseList, TestCaseRelevance, SelectMenu, TestPlanLoad}, data() { return { testPlans: [], diff --git a/frontend/src/business/components/track/plan/view/comonents/load/TestCaseLoadRelevance.vue b/frontend/src/business/components/track/plan/view/comonents/load/TestCaseLoadRelevance.vue new file mode 100644 index 0000000000..8d78de1771 --- /dev/null +++ b/frontend/src/business/components/track/plan/view/comonents/load/TestCaseLoadRelevance.vue @@ -0,0 +1,241 @@ + + + + + diff --git a/frontend/src/business/components/track/plan/view/comonents/load/TestPlanLoad.vue b/frontend/src/business/components/track/plan/view/comonents/load/TestPlanLoad.vue new file mode 100644 index 0000000000..fd76aa1724 --- /dev/null +++ b/frontend/src/business/components/track/plan/view/comonents/load/TestPlanLoad.vue @@ -0,0 +1,96 @@ + + + + + diff --git a/frontend/src/business/components/track/plan/view/comonents/load/TestPlanLoadCaseList.vue b/frontend/src/business/components/track/plan/view/comonents/load/TestPlanLoadCaseList.vue new file mode 100644 index 0000000000..92652e83f9 --- /dev/null +++ b/frontend/src/business/components/track/plan/view/comonents/load/TestPlanLoadCaseList.vue @@ -0,0 +1,220 @@ + + + + + diff --git a/frontend/src/business/components/track/plan/view/comonents/load/TestPlanLoadCaseListHeader.vue b/frontend/src/business/components/track/plan/view/comonents/load/TestPlanLoadCaseListHeader.vue new file mode 100644 index 0000000000..442b5b278d --- /dev/null +++ b/frontend/src/business/components/track/plan/view/comonents/load/TestPlanLoadCaseListHeader.vue @@ -0,0 +1,32 @@ + + + + +