diff --git a/backend/pom.xml b/backend/pom.xml index 0efd3ee955..28caf81dc7 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -165,6 +165,12 @@ ${jmeter.version} + + org.apache.jmeter + ApacheJMeter_jdbc + ${jmeter.version} + + org.apache.dubbo diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/DatabaseConfig.java b/backend/src/main/java/io/metersphere/api/dto/scenario/DatabaseConfig.java new file mode 100644 index 0000000000..e9f557b7d7 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/DatabaseConfig.java @@ -0,0 +1,16 @@ +package io.metersphere.api.dto.scenario; + +import lombok.Data; + +@Data +public class DatabaseConfig { + + private String id; + private String name; + private long poolMax; + private long timeout; + private String driver; + private String dbUrl; + private String username; + private String password; +} diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/Scenario.java b/backend/src/main/java/io/metersphere/api/dto/scenario/Scenario.java index 8418015b40..cad9efb3d8 100644 --- a/backend/src/main/java/io/metersphere/api/dto/scenario/Scenario.java +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/Scenario.java @@ -16,5 +16,6 @@ public class Scenario { private List headers; private List requests; private DubboConfig dubboConfig; + private List databaseConfigs; private Boolean enable; } diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/request/Request.java b/backend/src/main/java/io/metersphere/api/dto/scenario/request/Request.java index 58d0baf1a3..94be3e48d1 100644 --- a/backend/src/main/java/io/metersphere/api/dto/scenario/request/Request.java +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/request/Request.java @@ -7,8 +7,9 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "type") @JsonSubTypes({ @JsonSubTypes.Type(value = HttpRequest.class, name = RequestType.HTTP), - @JsonSubTypes.Type(value = DubboRequest.class, name = RequestType.DUBBO) + @JsonSubTypes.Type(value = DubboRequest.class, name = RequestType.DUBBO), + @JsonSubTypes.Type(value = SqlRequest.class, name = RequestType.SQL) }) -@JSONType(seeAlso = {HttpRequest.class, DubboRequest.class}, typeKey = "type") +@JSONType(seeAlso = {HttpRequest.class, DubboRequest.class, SqlRequest.class}, typeKey = "type") public interface Request { } diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/request/RequestType.java b/backend/src/main/java/io/metersphere/api/dto/scenario/request/RequestType.java index 16d151f95e..3be7d36eb6 100644 --- a/backend/src/main/java/io/metersphere/api/dto/scenario/request/RequestType.java +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/request/RequestType.java @@ -5,4 +5,6 @@ public class RequestType { public static final String HTTP = "HTTP"; public static final String DUBBO = "DUBBO"; + + public static final String SQL = "SQL"; } diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/request/SqlRequest.java b/backend/src/main/java/io/metersphere/api/dto/scenario/request/SqlRequest.java new file mode 100644 index 0000000000..bcebba7ccd --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/request/SqlRequest.java @@ -0,0 +1,40 @@ +package io.metersphere.api.dto.scenario.request; + +import com.alibaba.fastjson.annotation.JSONField; +import com.alibaba.fastjson.annotation.JSONType; +import io.metersphere.api.dto.scenario.assertions.Assertions; +import io.metersphere.api.dto.scenario.extract.Extract; +import io.metersphere.api.dto.scenario.processor.JSR223PostProcessor; +import io.metersphere.api.dto.scenario.processor.JSR223PreProcessor; +import lombok.Data; + +@Data +@JSONType(typeName = RequestType.SQL) +public class SqlRequest implements Request { + // type 必须放最前面,以便能够转换正确的类 + private String type = RequestType.SQL; + @JSONField(ordinal = 1) + private String id; + @JSONField(ordinal = 2) + private String name; + @JSONField(ordinal = 3) + private String dataSource; + @JSONField(ordinal = 4) + private String query; + @JSONField(ordinal = 5) + private long queryTimeout; + @JSONField(ordinal = 6) + private Boolean useEnvironment; + @JSONField(ordinal = 7) + private Assertions assertions; + @JSONField(ordinal = 8) + private Extract extract; + @JSONField(ordinal = 9) + private Boolean enable; + @JSONField(ordinal = 10) + private Boolean followRedirects; + @JSONField(ordinal = 11) + private JSR223PreProcessor jsr223PreProcessor; + @JSONField(ordinal = 12) + private JSR223PostProcessor jsr223PostProcessor; +} diff --git a/backend/src/main/java/io/metersphere/api/service/APITestService.java b/backend/src/main/java/io/metersphere/api/service/APITestService.java index 70c46f1f9e..003f7b213d 100644 --- a/backend/src/main/java/io/metersphere/api/service/APITestService.java +++ b/backend/src/main/java/io/metersphere/api/service/APITestService.java @@ -12,6 +12,7 @@ import io.metersphere.api.parse.JmeterDocumentParser; import io.metersphere.base.domain.*; import io.metersphere.base.mapper.ApiTestFileMapper; import io.metersphere.base.mapper.ApiTestMapper; +import io.metersphere.base.mapper.UserMapper; import io.metersphere.base.mapper.ext.ExtApiTestMapper; import io.metersphere.commons.constants.APITestStatus; import io.metersphere.commons.constants.FileType; @@ -23,9 +24,12 @@ import io.metersphere.controller.request.QueryScheduleRequest; import io.metersphere.dto.ScheduleDao; import io.metersphere.i18n.Translator; import io.metersphere.job.sechedule.ApiTestJob; +import io.metersphere.notice.service.MailService; +import io.metersphere.notice.service.NoticeService; import io.metersphere.service.FileService; import io.metersphere.service.QuotaService; import io.metersphere.service.ScheduleService; +import io.metersphere.service.UserService; import io.metersphere.track.service.TestCaseService; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.constants.CommonConstants; @@ -44,7 +48,8 @@ import java.util.stream.Collectors; @Service @Transactional(rollbackFor = Exception.class) public class APITestService { - + @Resource + private UserService userService; @Resource private ApiTestMapper apiTestMapper; @Resource @@ -61,6 +66,10 @@ public class APITestService { private ScheduleService scheduleService; @Resource private TestCaseService testCaseService; + @Resource + private MailService mailService; + @Resource + private NoticeService noticeService; private static final String BODY_FILE_DIR = "/opt/metersphere/data/body"; @@ -214,8 +223,11 @@ public class APITestService { apiTest.setUserId(request.getUserId()); } String reportId = apiReportService.create(apiTest, request.getTriggerMode()); + if (request.getTriggerMode().equals("SCHEDULE")) { + List notice = noticeService.queryNotice(request.getId()); + mailService.sendHtml(reportId,notice,"api"); + } changeStatus(request.getId(), APITestStatus.Running); - jMeterService.run(request.getId(), null, is); return reportId; } diff --git a/backend/src/main/java/io/metersphere/base/domain/Notice.java b/backend/src/main/java/io/metersphere/base/domain/Notice.java index 3096905b54..7d00dd04e0 100644 --- a/backend/src/main/java/io/metersphere/base/domain/Notice.java +++ b/backend/src/main/java/io/metersphere/base/domain/Notice.java @@ -15,5 +15,9 @@ public class Notice implements Serializable { private String enable; + private String[] names; + + private String[] emails; + private static final long serialVersionUID = 1L; } \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/domain/TestPlan.java b/backend/src/main/java/io/metersphere/base/domain/TestPlan.java index 6fc474d077..9e73da87bc 100644 --- a/backend/src/main/java/io/metersphere/base/domain/TestPlan.java +++ b/backend/src/main/java/io/metersphere/base/domain/TestPlan.java @@ -7,8 +7,6 @@ import lombok.Data; public class TestPlan implements Serializable { private String id; - private String projectId; - private String workspaceId; private String reportId; diff --git a/backend/src/main/java/io/metersphere/base/domain/TestPlanExample.java b/backend/src/main/java/io/metersphere/base/domain/TestPlanExample.java index 3c75a52576..f7ecdc3069 100644 --- a/backend/src/main/java/io/metersphere/base/domain/TestPlanExample.java +++ b/backend/src/main/java/io/metersphere/base/domain/TestPlanExample.java @@ -174,76 +174,6 @@ public class TestPlanExample { return (Criteria) this; } - public Criteria andProjectIdIsNull() { - addCriterion("project_id is null"); - return (Criteria) this; - } - - public Criteria andProjectIdIsNotNull() { - addCriterion("project_id is not null"); - return (Criteria) this; - } - - public Criteria andProjectIdEqualTo(String value) { - addCriterion("project_id =", value, "projectId"); - return (Criteria) this; - } - - public Criteria andProjectIdNotEqualTo(String value) { - addCriterion("project_id <>", value, "projectId"); - return (Criteria) this; - } - - public Criteria andProjectIdGreaterThan(String value) { - addCriterion("project_id >", value, "projectId"); - return (Criteria) this; - } - - public Criteria andProjectIdGreaterThanOrEqualTo(String value) { - addCriterion("project_id >=", value, "projectId"); - return (Criteria) this; - } - - public Criteria andProjectIdLessThan(String value) { - addCriterion("project_id <", value, "projectId"); - return (Criteria) this; - } - - public Criteria andProjectIdLessThanOrEqualTo(String value) { - addCriterion("project_id <=", value, "projectId"); - return (Criteria) this; - } - - public Criteria andProjectIdLike(String value) { - addCriterion("project_id like", value, "projectId"); - return (Criteria) this; - } - - public Criteria andProjectIdNotLike(String value) { - addCriterion("project_id not like", value, "projectId"); - return (Criteria) this; - } - - public Criteria andProjectIdIn(List values) { - addCriterion("project_id in", values, "projectId"); - return (Criteria) this; - } - - public Criteria andProjectIdNotIn(List values) { - addCriterion("project_id not in", values, "projectId"); - return (Criteria) this; - } - - public Criteria andProjectIdBetween(String value1, String value2) { - addCriterion("project_id between", value1, value2, "projectId"); - return (Criteria) this; - } - - public Criteria andProjectIdNotBetween(String value1, String value2) { - addCriterion("project_id not between", value1, value2, "projectId"); - return (Criteria) this; - } - public Criteria andWorkspaceIdIsNull() { addCriterion("workspace_id is null"); return (Criteria) this; @@ -385,72 +315,72 @@ public class TestPlanExample { } public Criteria andNameIsNull() { - addCriterion("name is null"); + addCriterion("`name` is null"); return (Criteria) this; } public Criteria andNameIsNotNull() { - addCriterion("name is not null"); + addCriterion("`name` is not null"); return (Criteria) this; } public Criteria andNameEqualTo(String value) { - addCriterion("name =", value, "name"); + addCriterion("`name` =", value, "name"); return (Criteria) this; } public Criteria andNameNotEqualTo(String value) { - addCriterion("name <>", value, "name"); + addCriterion("`name` <>", value, "name"); return (Criteria) this; } public Criteria andNameGreaterThan(String value) { - addCriterion("name >", value, "name"); + addCriterion("`name` >", value, "name"); return (Criteria) this; } public Criteria andNameGreaterThanOrEqualTo(String value) { - addCriterion("name >=", value, "name"); + addCriterion("`name` >=", value, "name"); return (Criteria) this; } public Criteria andNameLessThan(String value) { - addCriterion("name <", value, "name"); + addCriterion("`name` <", value, "name"); return (Criteria) this; } public Criteria andNameLessThanOrEqualTo(String value) { - addCriterion("name <=", value, "name"); + addCriterion("`name` <=", value, "name"); return (Criteria) this; } public Criteria andNameLike(String value) { - addCriterion("name like", value, "name"); + addCriterion("`name` like", value, "name"); return (Criteria) this; } public Criteria andNameNotLike(String value) { - addCriterion("name not like", value, "name"); + addCriterion("`name` not like", value, "name"); return (Criteria) this; } public Criteria andNameIn(List values) { - addCriterion("name in", values, "name"); + addCriterion("`name` in", values, "name"); return (Criteria) this; } public Criteria andNameNotIn(List values) { - addCriterion("name not in", values, "name"); + addCriterion("`name` not in", values, "name"); return (Criteria) this; } public Criteria andNameBetween(String value1, String value2) { - addCriterion("name between", value1, value2, "name"); + addCriterion("`name` between", value1, value2, "name"); return (Criteria) this; } public Criteria andNameNotBetween(String value1, String value2) { - addCriterion("name not between", value1, value2, "name"); + addCriterion("`name` not between", value1, value2, "name"); return (Criteria) this; } @@ -525,72 +455,72 @@ public class TestPlanExample { } public Criteria andStatusIsNull() { - addCriterion("status is null"); + addCriterion("`status` is null"); return (Criteria) this; } public Criteria andStatusIsNotNull() { - addCriterion("status is not null"); + addCriterion("`status` is not null"); return (Criteria) this; } public Criteria andStatusEqualTo(String value) { - addCriterion("status =", value, "status"); + addCriterion("`status` =", value, "status"); return (Criteria) this; } public Criteria andStatusNotEqualTo(String value) { - addCriterion("status <>", value, "status"); + addCriterion("`status` <>", value, "status"); return (Criteria) this; } public Criteria andStatusGreaterThan(String value) { - addCriterion("status >", value, "status"); + addCriterion("`status` >", value, "status"); return (Criteria) this; } public Criteria andStatusGreaterThanOrEqualTo(String value) { - addCriterion("status >=", value, "status"); + addCriterion("`status` >=", value, "status"); return (Criteria) this; } public Criteria andStatusLessThan(String value) { - addCriterion("status <", value, "status"); + addCriterion("`status` <", value, "status"); return (Criteria) this; } public Criteria andStatusLessThanOrEqualTo(String value) { - addCriterion("status <=", value, "status"); + addCriterion("`status` <=", value, "status"); return (Criteria) this; } public Criteria andStatusLike(String value) { - addCriterion("status like", value, "status"); + addCriterion("`status` like", value, "status"); return (Criteria) this; } public Criteria andStatusNotLike(String value) { - addCriterion("status not like", value, "status"); + addCriterion("`status` not like", value, "status"); return (Criteria) this; } public Criteria andStatusIn(List values) { - addCriterion("status in", values, "status"); + addCriterion("`status` in", values, "status"); return (Criteria) this; } public Criteria andStatusNotIn(List values) { - addCriterion("status not in", values, "status"); + addCriterion("`status` not in", values, "status"); return (Criteria) this; } public Criteria andStatusBetween(String value1, String value2) { - addCriterion("status between", value1, value2, "status"); + addCriterion("`status` between", value1, value2, "status"); return (Criteria) this; } public Criteria andStatusNotBetween(String value1, String value2) { - addCriterion("status not between", value1, value2, "status"); + addCriterion("`status` not between", value1, value2, "status"); return (Criteria) this; } diff --git a/backend/src/main/java/io/metersphere/base/mapper/TestPlanMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/TestPlanMapper.xml index a8d810bf95..5d92075a51 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/TestPlanMapper.xml +++ b/backend/src/main/java/io/metersphere/base/mapper/TestPlanMapper.xml @@ -3,7 +3,6 @@ - @@ -78,8 +77,8 @@ - id, project_id, workspace_id, report_id, name, description, status, stage, principal, - test_case_match_rule, executor_match_rule, create_time, update_time + id, workspace_id, report_id, `name`, description, `status`, stage, principal, test_case_match_rule, + executor_match_rule, create_time, update_time tags @@ -133,16 +132,16 @@ - insert into test_plan (id, project_id, workspace_id, - report_id, name, description, - status, stage, principal, - test_case_match_rule, executor_match_rule, create_time, - update_time, tags) - values (#{id,jdbcType=VARCHAR}, #{projectId,jdbcType=VARCHAR}, #{workspaceId,jdbcType=VARCHAR}, - #{reportId,jdbcType=VARCHAR}, #{name,jdbcType=VARCHAR}, #{description,jdbcType=VARCHAR}, - #{status,jdbcType=VARCHAR}, #{stage,jdbcType=VARCHAR}, #{principal,jdbcType=VARCHAR}, - #{testCaseMatchRule,jdbcType=VARCHAR}, #{executorMatchRule,jdbcType=VARCHAR}, #{createTime,jdbcType=BIGINT}, - #{updateTime,jdbcType=BIGINT}, #{tags,jdbcType=LONGVARCHAR}) + insert into test_plan (id, workspace_id, report_id, + `name`, description, `status`, + stage, principal, test_case_match_rule, + executor_match_rule, create_time, update_time, + tags) + values (#{id,jdbcType=VARCHAR}, #{workspaceId,jdbcType=VARCHAR}, #{reportId,jdbcType=VARCHAR}, + #{name,jdbcType=VARCHAR}, #{description,jdbcType=VARCHAR}, #{status,jdbcType=VARCHAR}, + #{stage,jdbcType=VARCHAR}, #{principal,jdbcType=VARCHAR}, #{testCaseMatchRule,jdbcType=VARCHAR}, + #{executorMatchRule,jdbcType=VARCHAR}, #{createTime,jdbcType=BIGINT}, #{updateTime,jdbcType=BIGINT}, + #{tags,jdbcType=LONGVARCHAR}) insert into test_plan @@ -150,9 +149,6 @@ id, - - project_id, - workspace_id, @@ -160,13 +156,13 @@ report_id, - name, + `name`, description, - status, + `status`, stage, @@ -194,9 +190,6 @@ #{id,jdbcType=VARCHAR}, - - #{projectId,jdbcType=VARCHAR}, - #{workspaceId,jdbcType=VARCHAR}, @@ -247,9 +240,6 @@ id = #{record.id,jdbcType=VARCHAR}, - - project_id = #{record.projectId,jdbcType=VARCHAR}, - workspace_id = #{record.workspaceId,jdbcType=VARCHAR}, @@ -257,13 +247,13 @@ report_id = #{record.reportId,jdbcType=VARCHAR}, - name = #{record.name,jdbcType=VARCHAR}, + `name` = #{record.name,jdbcType=VARCHAR}, description = #{record.description,jdbcType=VARCHAR}, - status = #{record.status,jdbcType=VARCHAR}, + `status` = #{record.status,jdbcType=VARCHAR}, stage = #{record.stage,jdbcType=VARCHAR}, @@ -294,12 +284,11 @@ update test_plan set id = #{record.id,jdbcType=VARCHAR}, - project_id = #{record.projectId,jdbcType=VARCHAR}, workspace_id = #{record.workspaceId,jdbcType=VARCHAR}, report_id = #{record.reportId,jdbcType=VARCHAR}, - name = #{record.name,jdbcType=VARCHAR}, + `name` = #{record.name,jdbcType=VARCHAR}, description = #{record.description,jdbcType=VARCHAR}, - status = #{record.status,jdbcType=VARCHAR}, + `status` = #{record.status,jdbcType=VARCHAR}, stage = #{record.stage,jdbcType=VARCHAR}, principal = #{record.principal,jdbcType=VARCHAR}, test_case_match_rule = #{record.testCaseMatchRule,jdbcType=VARCHAR}, @@ -314,12 +303,11 @@ update test_plan set id = #{record.id,jdbcType=VARCHAR}, - project_id = #{record.projectId,jdbcType=VARCHAR}, workspace_id = #{record.workspaceId,jdbcType=VARCHAR}, report_id = #{record.reportId,jdbcType=VARCHAR}, - name = #{record.name,jdbcType=VARCHAR}, + `name` = #{record.name,jdbcType=VARCHAR}, description = #{record.description,jdbcType=VARCHAR}, - status = #{record.status,jdbcType=VARCHAR}, + `status` = #{record.status,jdbcType=VARCHAR}, stage = #{record.stage,jdbcType=VARCHAR}, principal = #{record.principal,jdbcType=VARCHAR}, test_case_match_rule = #{record.testCaseMatchRule,jdbcType=VARCHAR}, @@ -333,9 +321,6 @@ update test_plan - - project_id = #{projectId,jdbcType=VARCHAR}, - workspace_id = #{workspaceId,jdbcType=VARCHAR}, @@ -343,13 +328,13 @@ report_id = #{reportId,jdbcType=VARCHAR}, - name = #{name,jdbcType=VARCHAR}, + `name` = #{name,jdbcType=VARCHAR}, description = #{description,jdbcType=VARCHAR}, - status = #{status,jdbcType=VARCHAR}, + `status` = #{status,jdbcType=VARCHAR}, stage = #{stage,jdbcType=VARCHAR}, @@ -377,12 +362,11 @@ update test_plan - set project_id = #{projectId,jdbcType=VARCHAR}, - workspace_id = #{workspaceId,jdbcType=VARCHAR}, + set workspace_id = #{workspaceId,jdbcType=VARCHAR}, report_id = #{reportId,jdbcType=VARCHAR}, - name = #{name,jdbcType=VARCHAR}, + `name` = #{name,jdbcType=VARCHAR}, description = #{description,jdbcType=VARCHAR}, - status = #{status,jdbcType=VARCHAR}, + `status` = #{status,jdbcType=VARCHAR}, stage = #{stage,jdbcType=VARCHAR}, principal = #{principal,jdbcType=VARCHAR}, test_case_match_rule = #{testCaseMatchRule,jdbcType=VARCHAR}, @@ -394,12 +378,11 @@ update test_plan - set project_id = #{projectId,jdbcType=VARCHAR}, - workspace_id = #{workspaceId,jdbcType=VARCHAR}, + set workspace_id = #{workspaceId,jdbcType=VARCHAR}, report_id = #{reportId,jdbcType=VARCHAR}, - name = #{name,jdbcType=VARCHAR}, + `name` = #{name,jdbcType=VARCHAR}, description = #{description,jdbcType=VARCHAR}, - status = #{status,jdbcType=VARCHAR}, + `status` = #{status,jdbcType=VARCHAR}, stage = #{stage,jdbcType=VARCHAR}, principal = #{principal,jdbcType=VARCHAR}, test_case_match_rule = #{testCaseMatchRule,jdbcType=VARCHAR}, diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiTestReportMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiTestReportMapper.xml index 8b539e9659..13d49c2f48 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiTestReportMapper.xml +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiTestReportMapper.xml @@ -57,7 +57,16 @@ - + + SELECT r.*, t.name AS test_name, project.name AS project_name, user.name AS user_name + FROM api_test_report r JOIN api_test t ON r.test_id = t.id + LEFT JOIN project ON project.id = t.project_id + LEFT JOIN user ON user.id = r.user_id + + r.id = #{id} + + ORDER BY r.update_time DESC + SELECT t.name AS test_name, r.name, r.description, r.id, r.test_id, r.create_time, r.update_time, r.status, r.trigger_mode, @@ -125,16 +134,7 @@ ORDER BY r.update_time DESC - - SELECT r.*, t.name AS test_name, project.name AS project_name, user.name AS user_name - FROM api_test_report r JOIN api_test t ON r.test_id = t.id - LEFT JOIN project ON project.id = t.project_id - LEFT JOIN user ON user.id = r.user_id - - r.id = #{id} - - ORDER BY r.update_time DESC - + SELECT min(create_time) AS date, count(api_test_report.id) AS count, diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtSystemParameterMapper.java b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtSystemParameterMapper.java new file mode 100644 index 0000000000..7fd5f72e47 --- /dev/null +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtSystemParameterMapper.java @@ -0,0 +1,5 @@ +package io.metersphere.base.mapper.ext; + +public interface ExtSystemParameterMapper { + String email(); +} diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtSystemParameterMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtSystemParameterMapper.xml new file mode 100644 index 0000000000..db788581c5 --- /dev/null +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtSystemParameterMapper.xml @@ -0,0 +1,8 @@ + + + + + + select param_value from system_parameter where param_key=#{smtp.account} + + \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanMapper.java b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanMapper.java index a72875174c..53e57c3256 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanMapper.java +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanMapper.java @@ -11,4 +11,6 @@ public interface ExtTestPlanMapper { List list(@Param("request") QueryTestPlanRequest params); List listRelate(@Param("request") QueryTestPlanRequest params); + + List planList(@Param("request") QueryTestPlanRequest params); } diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanMapper.xml index ff443b4b68..2b2a4a80c1 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanMapper.xml +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanMapper.xml @@ -143,6 +143,18 @@ + + SELECT * FROM TEST_PLAN p LEFT JOIN test_plan_project t ON t.test_plan_id=p.ID + + + AND p.workspace_id = #{request.workspaceId} + + + AND t.project_id = #{request.projectId} + + + select test_plan.* from test_plan diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtUserMapper.java b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtUserMapper.java index 504860cc92..2a87ace5af 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtUserMapper.java +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtUserMapper.java @@ -16,4 +16,6 @@ public interface ExtUserMapper { List searchUser(String condition); + List queryEmails(String[] names); + } diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtUserMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtUserMapper.xml index 7fd23b5be4..c65b4abb7c 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtUserMapper.xml +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtUserMapper.xml @@ -33,6 +33,18 @@ order by u.update_time desc + + + SELECT + email + from user + WHERE name IN + + #{id} + + + diff --git a/backend/src/main/java/io/metersphere/job/sechedule/ApiTestJob.java b/backend/src/main/java/io/metersphere/job/sechedule/ApiTestJob.java index bb4ce5bcb0..bb855d77e7 100644 --- a/backend/src/main/java/io/metersphere/job/sechedule/ApiTestJob.java +++ b/backend/src/main/java/io/metersphere/job/sechedule/ApiTestJob.java @@ -2,6 +2,7 @@ package io.metersphere.job.sechedule; import io.metersphere.api.dto.SaveAPITestRequest; import io.metersphere.api.service.APITestService; +import io.metersphere.notice.service.MailService; import io.metersphere.commons.constants.ReportTriggerMode; import io.metersphere.commons.constants.ScheduleGroup; import io.metersphere.commons.utils.CommonBeanFactory; @@ -13,7 +14,7 @@ import org.quartz.TriggerKey; public class ApiTestJob extends MsScheduleJob { private APITestService apiTestService; - + private MailService mailService; public ApiTestJob() { apiTestService = (APITestService) CommonBeanFactory.getBean(APITestService.class); } diff --git a/backend/src/main/java/io/metersphere/notice/controller/NoticeController.java b/backend/src/main/java/io/metersphere/notice/controller/NoticeController.java new file mode 100644 index 0000000000..34b210f703 --- /dev/null +++ b/backend/src/main/java/io/metersphere/notice/controller/NoticeController.java @@ -0,0 +1,28 @@ +package io.metersphere.notice.controller; + +import io.metersphere.base.domain.Notice; +import io.metersphere.notice.controller.request.NoticeRequest; +import io.metersphere.notice.service.MailService; +import io.metersphere.notice.service.NoticeService; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import java.util.List; + +@RestController +@RequestMapping("notice") +public class NoticeController { + @Resource + private NoticeService noticeService; + + @PostMapping("/save") + public void saveNotice(@RequestBody NoticeRequest noticeRequest) { + noticeService.saveNotice(noticeRequest); + } + + @GetMapping("/query/{testId}") + public List queryNotice(@PathVariable String testId) { + return noticeService.queryNotice(testId); + } + +} diff --git a/backend/src/main/java/io/metersphere/notice/controller/request/NoticeRequest.java b/backend/src/main/java/io/metersphere/notice/controller/request/NoticeRequest.java new file mode 100644 index 0000000000..9206cfe465 --- /dev/null +++ b/backend/src/main/java/io/metersphere/notice/controller/request/NoticeRequest.java @@ -0,0 +1,12 @@ +package io.metersphere.notice.controller.request; + +import io.metersphere.base.domain.Notice; +import lombok.Data; + +import java.util.List; + +@Data +public class NoticeRequest extends Notice { + private String testId; + private List notices; +} diff --git a/backend/src/main/java/io/metersphere/notice/domain/Mail.java b/backend/src/main/java/io/metersphere/notice/domain/Mail.java new file mode 100644 index 0000000000..126e903e93 --- /dev/null +++ b/backend/src/main/java/io/metersphere/notice/domain/Mail.java @@ -0,0 +1,18 @@ +package io.metersphere.notice.domain; + +import lombok.Data; + +@Data +public class Mail { + // 发送给谁 + private String to; + + // 发送主题 + private String subject; + + // 发送内容 + private String content; + + // 附件地址 + private String filePath; +} diff --git a/backend/src/main/java/io/metersphere/notice/service/ApiAndPerformanceHelper.java b/backend/src/main/java/io/metersphere/notice/service/ApiAndPerformanceHelper.java new file mode 100644 index 0000000000..f74f04bc49 --- /dev/null +++ b/backend/src/main/java/io/metersphere/notice/service/ApiAndPerformanceHelper.java @@ -0,0 +1,54 @@ +package io.metersphere.notice.service; + +import io.metersphere.api.dto.APIReportResult; +import io.metersphere.base.domain.ApiTestReportDetail; +import io.metersphere.base.domain.Schedule; +import io.metersphere.base.mapper.ApiTestReportDetailMapper; +import io.metersphere.base.mapper.ext.ExtApiTestReportMapper; +import io.metersphere.base.mapper.ext.ExtLoadTestMapper; +import io.metersphere.commons.constants.ScheduleGroup; +import io.metersphere.dto.LoadTestDTO; +import io.metersphere.service.ScheduleService; +import io.metersphere.track.request.testplan.QueryTestPlanRequest; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import javax.annotation.Resource; +import java.nio.charset.StandardCharsets; +import java.util.List; + + +@Service +public class ApiAndPerformanceHelper { + @Resource + private ExtLoadTestMapper extLoadTestMapper; + @Resource + private ExtApiTestReportMapper extApiTestReportMapper; + @Resource + private ApiTestReportDetailMapper apiTestReportDetailMapper; + @Resource + private ScheduleService scheduleService; + + public APIReportResult getApi(String reportId) { + APIReportResult result = extApiTestReportMapper.get(reportId); + ApiTestReportDetail detail = apiTestReportDetailMapper.selectByPrimaryKey(reportId); + if (detail != null) { + result.setContent(new String(detail.getContent(), StandardCharsets.UTF_8)); + } + return result; + } + + public LoadTestDTO getPerformance(String testId) { + QueryTestPlanRequest request = new QueryTestPlanRequest(); + request.setId(testId); + List testDTOS = extLoadTestMapper.list(request); + if (!CollectionUtils.isEmpty(testDTOS)) { + LoadTestDTO loadTestDTO = testDTOS.get(0); + Schedule schedule = scheduleService.getScheduleByResource(loadTestDTO.getId(), ScheduleGroup.PERFORMANCE_TEST.name()); + loadTestDTO.setSchedule(schedule); + return loadTestDTO; + } + return null; + } +} + diff --git a/backend/src/main/java/io/metersphere/notice/service/MailService.java b/backend/src/main/java/io/metersphere/notice/service/MailService.java new file mode 100644 index 0000000000..efee1a4519 --- /dev/null +++ b/backend/src/main/java/io/metersphere/notice/service/MailService.java @@ -0,0 +1,136 @@ +package io.metersphere.notice.service; + +import io.metersphere.api.dto.APIReportResult; +import io.metersphere.base.domain.Notice; +import io.metersphere.base.domain.SystemParameter; +import io.metersphere.commons.constants.ParamConstants; +import io.metersphere.commons.utils.EncryptUtils; +import io.metersphere.commons.utils.LogUtil; +import io.metersphere.dto.LoadTestDTO; +import io.metersphere.service.SystemParameterService; +import io.metersphere.service.UserService; +import org.springframework.mail.javamail.JavaMailSenderImpl; +import org.springframework.mail.javamail.MimeMessageHelper; +import org.springframework.stereotype.Service; + + +import javax.annotation.Resource; +import javax.mail.MessagingException; +import javax.mail.internet.MimeMessage; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; + +@Service +public class MailService { + @Resource + private ApiAndPerformanceHelper apiAndPerformanceHelper; + @Resource + private UserService userService; + @Resource + private SystemParameterService systemParameterService; + + public void sendHtml(String id, List notice,String type) { + JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl(); + List paramList=systemParameterService.getParamList(ParamConstants.Classify.MAIL.getValue()); + javaMailSender.setDefaultEncoding("UTF-8"); + javaMailSender.setProtocol("smtps"); + for(SystemParameter p:paramList){ + if(p.getParamKey().equals("smtp.host")){ + javaMailSender.setHost(p.getParamValue()); + } + if(p.getParamKey().equals("smtp.port")){ + javaMailSender.setPort(Integer.parseInt(p.getParamValue())); + } + if(p.getParamKey().equals("smtp.account")){ + javaMailSender.setUsername(p.getParamValue()); + } + if(p.getParamKey().equals("smtp.password")){ + javaMailSender.setPassword(EncryptUtils.aesDecrypt(p.getParamValue()).toString()); + } + } + Properties props = new Properties(); + props.put("mail.smtp.auth", "true"); + props.put("mail.smtp.starttls.enable", "true"); + props.put("mail.smtp.starttls.required", "true"); + props.put("mail.smtp.timeout", "30000"); + props.put("mail.smtp.connectiontimeout", "5000"); + javaMailSender.setJavaMailProperties(props); + MimeMessage mimeMessage = javaMailSender.createMimeMessage(); + String testName = ""; + String state = ""; + if (type.equals("api")) { + APIReportResult reportResult = apiAndPerformanceHelper.getApi(id); + testName = reportResult.getTestName(); + state = reportResult.getStatus(); + } else if (type.equals("performance")) { + LoadTestDTO performanceResult = apiAndPerformanceHelper.getPerformance(id); + testName = performanceResult.getName(); + state = performanceResult.getStatus(); + } + String html1 = "\n" + + "\n" + + "\n" + + " \n" + + " MeterSphere\n" + + "\n" + + "\n" + + " \n" + + " "+type+"定时任务结果通知\n" + + " 尊敬的用户:您好,您所执行的"+testName+"运行失败,请点击报告链接查看\n" + + " \n" + + "\n" + + ""; + String html2="\n" + + "\n" + + "\n" + + " \n" + + " MeterSphere\n" + + "\n" + + "\n" + + " \n" + + " "+type+"定时任务结果通知\n" + + " 尊敬的用户:您好,"+testName+"运行成功,请点击报告链接查看\n" + + " \n" + + "\n" + + ""; + try { + MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true); + helper.setFrom(javaMailSender.getUsername()); + helper.setSubject("MeterSphere定时任务结果通知"); + String users[] = {}; + List successEmailList=new ArrayList<>(); + List failEmailList=new ArrayList<>(); + if(notice.size()>0){ + for(Notice n:notice){ + if (n.getEnable().equals("true")&&n.getEvent().equals("执行成功")) { + successEmailList=userService.queryEmail(n.getNames()); + } + if (n.getEnable().equals("true")&&n.getEvent().equals("执行失败")) { + failEmailList=userService.queryEmail(n.getNames()); + } + + } + }else{ + LogUtil.error("Recipient information is empty"); + } + + if(state.equals("success")){ + users=successEmailList.toArray(new String[successEmailList.size()]); + helper.setText(html2,true); + }else{ + users=failEmailList.toArray(new String[failEmailList.size()]); + helper.setText(html1,true); + + } + helper.setTo(users); + javaMailSender.send(mimeMessage); + + } catch (MessagingException e) { + e.printStackTrace(); + } + } + + +} + diff --git a/backend/src/main/java/io/metersphere/notice/service/NoticeService.java b/backend/src/main/java/io/metersphere/notice/service/NoticeService.java new file mode 100644 index 0000000000..cbac52e361 --- /dev/null +++ b/backend/src/main/java/io/metersphere/notice/service/NoticeService.java @@ -0,0 +1,83 @@ +package io.metersphere.notice.service; + +import io.metersphere.base.domain.Notice; +import io.metersphere.base.domain.NoticeExample; +import io.metersphere.base.mapper.NoticeMapper; +import io.metersphere.notice.controller.request.NoticeRequest; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +@Service +public class NoticeService { + @Resource + private NoticeMapper noticeMapper; + public void saveNotice(NoticeRequest noticeRequest) { + NoticeExample example = new NoticeExample(); + example.createCriteria().andTestIdEqualTo(noticeRequest.getTestId()); + List notices = noticeMapper.selectByExample(example); + if (notices != null) { + noticeMapper.deleteByExample(example); + noticeRequest.getNotices().forEach(notice -> { + for(String x:notice.getNames()){ + notice.setEvent(notice.getEvent()); + notice.setEmail(notice.getEmail()); + notice.setEnable(notice.getEnable()); + notice.setTestId(noticeRequest.getTestId()); + notice.setName(x); + noticeMapper.insert(notice); + } + }); + } else { + noticeRequest.getNotices().forEach(notice -> { + notice.setEvent(noticeRequest.getEvent()); + notice.setTestId(noticeRequest.getTestId()); + noticeMapper.insert(notice); + }); + } + + } + + public List queryNotice(String id) { + NoticeExample example = new NoticeExample(); + example.createCriteria().andTestIdEqualTo(id); + List notices=noticeMapper.selectByExample(example); + List notice=new ArrayList<>(); + List success=new ArrayList<>(); + List fail=new ArrayList<>(); + String[] successArray=new String[success.size()]; + String[] failArray=new String[fail.size()]; + Notice notice1=new Notice(); + Notice notice2=new Notice(); + if(notices.size()>0){ + for(Notice n:notices){ + if(n.getEvent().equals("执行成功")){ + success.add(n.getName()); + notice1.setEnable(n.getEnable()); + notice1.setTestId(id); + notice1.setEvent(n.getEvent()); + notice1.setEmail(n.getEmail()); + } + if(n.getEvent().equals("执行失败")){ + fail.add(n.getName()); + notice2.setEnable(n.getEnable()); + notice2.setTestId(id); + notice2.setEvent(n.getEvent()); + notice2.setEmail(n.getEmail()); + } + } + successArray=success.toArray(new String[success.size()]); + failArray=fail.toArray(new String[fail.size()]); + notice1.setNames(successArray); + notice2.setNames(failArray); + notice.add(notice1); + notice.add(notice2); + } + return notice; + } + +} 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 64e71fa08a..97b73d1b87 100644 --- a/backend/src/main/java/io/metersphere/performance/service/PerformanceTestService.java +++ b/backend/src/main/java/io/metersphere/performance/service/PerformanceTestService.java @@ -19,6 +19,8 @@ import io.metersphere.dto.LoadTestDTO; import io.metersphere.dto.ScheduleDao; import io.metersphere.i18n.Translator; import io.metersphere.job.sechedule.PerformanceTestJob; +import io.metersphere.notice.service.MailService; +import io.metersphere.notice.service.NoticeService; import io.metersphere.performance.engine.Engine; import io.metersphere.performance.engine.EngineFactory; import io.metersphere.service.FileService; @@ -80,6 +82,10 @@ public class PerformanceTestService { private TestCaseMapper testCaseMapper; @Resource private TestCaseService testCaseService; + @Resource + private NoticeService noticeService; + @Resource + private MailService mailService; public List list(QueryTestPlanRequest request) { request.setOrders(ServiceUtils.getDefaultOrder(request.getOrders())); @@ -231,7 +237,10 @@ public class PerformanceTestService { } startEngine(loadTest, engine, request.getTriggerMode()); - + if (request.getTriggerMode().equals("SCHEDULE")) { + List notice = noticeService.queryNotice(request.getId()); + mailService.sendHtml(engine.getReportId(),notice,"performance"); + } return engine.getReportId(); } diff --git a/backend/src/main/java/io/metersphere/service/SystemParameterService.java b/backend/src/main/java/io/metersphere/service/SystemParameterService.java index 3fdc9e461f..4042a65832 100644 --- a/backend/src/main/java/io/metersphere/service/SystemParameterService.java +++ b/backend/src/main/java/io/metersphere/service/SystemParameterService.java @@ -3,6 +3,7 @@ package io.metersphere.service; import io.metersphere.base.domain.SystemParameter; import io.metersphere.base.domain.SystemParameterExample; import io.metersphere.base.mapper.SystemParameterMapper; +import io.metersphere.base.mapper.ext.ExtSystemParameterMapper; import io.metersphere.commons.constants.ParamConstants; import io.metersphere.commons.exception.MSException; import io.metersphere.commons.utils.EncryptUtils; @@ -24,7 +25,12 @@ public class SystemParameterService { @Resource private SystemParameterMapper systemParameterMapper; + @Resource + private ExtSystemParameterMapper extSystemParameterMapper; + public String searchEmail(){ + return extSystemParameterMapper.email(); + } public String getSystemLanguage() { String result = StringUtils.EMPTY; SystemParameterExample example = new SystemParameterExample(); diff --git a/backend/src/main/java/io/metersphere/service/UserService.java b/backend/src/main/java/io/metersphere/service/UserService.java index ebeb4155b9..98312d541a 100644 --- a/backend/src/main/java/io/metersphere/service/UserService.java +++ b/backend/src/main/java/io/metersphere/service/UserService.java @@ -61,6 +61,9 @@ public class UserService { @Resource private WorkspaceService workspaceService; + public List queryEmail(String[] names){ + return extUserMapper.queryEmails(names); + } public UserDTO insert(UserRequest user) { checkUserParam(user); // diff --git a/backend/src/main/java/io/metersphere/track/controller/TestCaseIssuesController.java b/backend/src/main/java/io/metersphere/track/controller/TestCaseIssuesController.java index 79a51257f5..905b408b5e 100644 --- a/backend/src/main/java/io/metersphere/track/controller/TestCaseIssuesController.java +++ b/backend/src/main/java/io/metersphere/track/controller/TestCaseIssuesController.java @@ -1,6 +1,7 @@ package io.metersphere.track.controller; import io.metersphere.base.domain.Issues; +import io.metersphere.track.domain.TapdUser; import io.metersphere.track.service.IssuesService; import io.metersphere.track.request.testcase.IssuesRequest; import org.springframework.web.bind.annotation.*; @@ -35,4 +36,9 @@ public class TestCaseIssuesController { issuesService.closeLocalIssue(id); } + @GetMapping("/tapd/user/{caseId}") + public List getTapdUsers(@PathVariable String caseId) { + return issuesService.getTapdProjectUsers(caseId); + } + } diff --git a/backend/src/main/java/io/metersphere/track/controller/TestPlanController.java b/backend/src/main/java/io/metersphere/track/controller/TestPlanController.java index 4f43c564d1..6fbea3d40d 100644 --- a/backend/src/main/java/io/metersphere/track/controller/TestPlanController.java +++ b/backend/src/main/java/io/metersphere/track/controller/TestPlanController.java @@ -47,7 +47,7 @@ public class TestPlanController { QueryTestPlanRequest request = new QueryTestPlanRequest(); request.setWorkspaceId(workspaceId); request.setProjectId(projectId); - return testPlanService.listTestPlan(request); + return testPlanService.listTestPlanByProject(request); } @PostMapping("/list/all") diff --git a/backend/src/main/java/io/metersphere/track/domain/TapdUser.java b/backend/src/main/java/io/metersphere/track/domain/TapdUser.java new file mode 100644 index 0000000000..53d193c088 --- /dev/null +++ b/backend/src/main/java/io/metersphere/track/domain/TapdUser.java @@ -0,0 +1,12 @@ +package io.metersphere.track.domain; + +import lombok.Data; +import java.io.Serializable; +import java.util.List; + +@Data +public class TapdUser implements Serializable { + private List roleId; + private String name; + private String user; +} diff --git a/backend/src/main/java/io/metersphere/track/request/testcase/IssuesRequest.java b/backend/src/main/java/io/metersphere/track/request/testcase/IssuesRequest.java index e5658e3684..88fb097185 100644 --- a/backend/src/main/java/io/metersphere/track/request/testcase/IssuesRequest.java +++ b/backend/src/main/java/io/metersphere/track/request/testcase/IssuesRequest.java @@ -3,6 +3,8 @@ package io.metersphere.track.request.testcase; import lombok.Getter; import lombok.Setter; +import java.util.List; + @Getter @Setter public class IssuesRequest { @@ -10,4 +12,5 @@ public class IssuesRequest { private String content; private String projectId; private String testCaseId; + private List tapdUsers; } diff --git a/backend/src/main/java/io/metersphere/track/request/testcase/QueryTestPlanRequest.java b/backend/src/main/java/io/metersphere/track/request/testcase/QueryTestPlanRequest.java index d03e3da703..6997e42691 100644 --- a/backend/src/main/java/io/metersphere/track/request/testcase/QueryTestPlanRequest.java +++ b/backend/src/main/java/io/metersphere/track/request/testcase/QueryTestPlanRequest.java @@ -21,4 +21,6 @@ public class QueryTestPlanRequest extends TestPlan { private Map> filters; private Map combine; + + private String projectId; } diff --git a/backend/src/main/java/io/metersphere/track/service/IssuesService.java b/backend/src/main/java/io/metersphere/track/service/IssuesService.java index 3426bd6084..b46ffc2496 100644 --- a/backend/src/main/java/io/metersphere/track/service/IssuesService.java +++ b/backend/src/main/java/io/metersphere/track/service/IssuesService.java @@ -1,6 +1,7 @@ package io.metersphere.track.service; import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import io.metersphere.base.domain.*; import io.metersphere.base.mapper.IssuesMapper; @@ -17,6 +18,7 @@ import io.metersphere.controller.ResultHolder; import io.metersphere.controller.request.IntegrationRequest; import io.metersphere.service.IntegrationService; import io.metersphere.service.ProjectService; +import io.metersphere.track.domain.TapdUser; import io.metersphere.track.request.testcase.IssuesRequest; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; @@ -188,10 +190,17 @@ public class IssuesService { MSException.throwException("未关联Tapd 项目ID"); } + List tapdUsers = issuesRequest.getTapdUsers(); + String usersStr = String.join(";", tapdUsers); + + String username = SessionUtils.getUser().getName(); + MultiValueMap paramMap = new LinkedMultiValueMap<>(); paramMap.add("title", issuesRequest.getTitle()); paramMap.add("workspace_id", tapdId); paramMap.add("description", issuesRequest.getContent()); + paramMap.add("reporter", username); + paramMap.add("current_owner", usersStr); ResultHolder result = call(url, HttpMethod.POST, paramMap); @@ -535,4 +544,19 @@ public class IssuesService { issuesMapper.updateByPrimaryKeySelective(issues); } + public List getTapdProjectUsers(String caseId) { + List users = new ArrayList<>(); + String projectId = getTapdProjectId(caseId); + String url = "https://api.tapd.cn/workspaces/users?workspace_id=" + projectId; + ResultHolder call = call(url); + String listJson = JSON.toJSONString(call.getData()); + JSONArray jsonArray = JSON.parseArray(listJson); + for (int i = 0; i < jsonArray.size(); i++) { + JSONObject o = jsonArray.getJSONObject(i); + TapdUser user = o.getObject("UserWorkspace", TapdUser.class); + users.add(user); + } + return users; + } + } diff --git a/backend/src/main/java/io/metersphere/track/service/TestPlanService.java b/backend/src/main/java/io/metersphere/track/service/TestPlanService.java index c7b378f935..bdd198c510 100644 --- a/backend/src/main/java/io/metersphere/track/service/TestPlanService.java +++ b/backend/src/main/java/io/metersphere/track/service/TestPlanService.java @@ -146,6 +146,10 @@ public class TestPlanService { return extTestPlanMapper.list(request); } + public List listTestPlanByProject(QueryTestPlanRequest request) { + return extTestPlanMapper.planList(request); + } + public void testPlanRelevance(PlanCaseRelevanceRequest request) { List testCaseIds = request.getTestCaseIds(); diff --git a/backend/src/main/resources/application.properties b/backend/src/main/resources/application.properties index e94db0ae4d..a3a405615e 100644 --- a/backend/src/main/resources/application.properties +++ b/backend/src/main/resources/application.properties @@ -69,13 +69,13 @@ jmeter.home=/opt/jmeter # quartz quartz.enabled=true quartz.scheduler-name=msServerJob - # file upload spring.servlet.multipart.max-file-size=500MB spring.servlet.multipart.max-request-size=500MB - # actuator management.server.port=8083 management.endpoints.web.exposure.include=* +#spring.freemarker.checkTemplateLocation=false + + -spring.freemarker.checkTemplateLocation=false diff --git a/backend/src/main/resources/db/migration/V21__modify_test_plan.sql b/backend/src/main/resources/db/migration/V21__modify_test_plan.sql new file mode 100644 index 0000000000..a8e42eafde --- /dev/null +++ b/backend/src/main/resources/db/migration/V21__modify_test_plan.sql @@ -0,0 +1 @@ +alter table test_plan drop column project_id; \ No newline at end of file diff --git a/backend/src/main/resources/i18n/messages_en_US.properties b/backend/src/main/resources/i18n/messages_en_US.properties index ee6353c9f8..6cbce8b777 100644 --- a/backend/src/main/resources/i18n/messages_en_US.properties +++ b/backend/src/main/resources/i18n/messages_en_US.properties @@ -22,7 +22,7 @@ user_already_exists=The user already exists in the current member list cannot_remove_current=Unable to remove the currently logged in user password_is_incorrect=Incorrect password user_not_exist=user does not exist: -user_has_been_disabled=the user has been disabled. +user_has_been_disabled=the user has been disabled. excessive_attempts=Excessive attempts user_locked=the user has been locked. user_expires=user expires. @@ -151,5 +151,5 @@ quota_max_threads_excess_workspace=The maximum number of concurrent threads exce quota_max_threads_excess_organization=The maximum number of concurrent threads exceeds the organization quota quota_duration_excess_workspace=The stress test duration exceeds the work space quota quota_duration_excess_organization=The stress test duration exceeds the organization quota -license_valid_license_error=valid license error -license_valid_license_code=The authorization code already exists + +email_subject=Metersphere timing task result notification diff --git a/backend/src/main/resources/i18n/messages_zh_CN.properties b/backend/src/main/resources/i18n/messages_zh_CN.properties index b30883829a..dba0ab0432 100644 --- a/backend/src/main/resources/i18n/messages_zh_CN.properties +++ b/backend/src/main/resources/i18n/messages_zh_CN.properties @@ -151,7 +151,7 @@ quota_max_threads_excess_workspace=最大并发数超过工作空间限额 quota_max_threads_excess_organization=最大并发数超过组织限额 quota_duration_excess_workspace=压测时长超过工作空间限额 quota_duration_excess_organization=压测时长超过组织限额 -license_valid_license_error=授权验证失败 -license_valid_license_code=授权码已经存在 +email_subject=MeterSphere定时任务结果通知 + diff --git a/backend/src/main/resources/i18n/messages_zh_TW.properties b/backend/src/main/resources/i18n/messages_zh_TW.properties index 985bb05a70..1ba3befebc 100644 --- a/backend/src/main/resources/i18n/messages_zh_TW.properties +++ b/backend/src/main/resources/i18n/messages_zh_TW.properties @@ -154,3 +154,5 @@ quota_duration_excess_organization=壓測時長超過組織限額 license_valid_license_error=授權驗證失敗 license_valid_license_code=授權碼已經存在 + +email_subject=MeterSphere定時任務結果通知 diff --git a/docker/jmeter-base/Dockerfile b/docker/jmeter-base/Dockerfile index 5faaa2b149..f5723be152 100644 --- a/docker/jmeter-base/Dockerfile +++ b/docker/jmeter-base/Dockerfile @@ -1,29 +1,29 @@ FROM alpine:latest LABEL maintainer="support@fit2cloud.com" -ENV JMETER_VERSION "5.2.1" - +ENV JMETER_VERSION "5.3" +ENV KAFKA_BACKEND_LISTENER_VERSION "1.0.4" #定义时区参数 ENV TZ=Asia/Shanghai RUN apk update && \ apk upgrade && \ - apk add --update openjdk8-jre wget tar bash && \ + apk add --update openjdk8 wget tar bash && \ wget https://mirrors.tuna.tsinghua.edu.cn/apache/jmeter/binaries/apache-jmeter-${JMETER_VERSION}.tgz && \ wget https://jmeter-plugins.org/files/packages/jpgc-casutg-2.9.zip && \ wget https://jmeter-plugins.org/files/packages/jpgc-tst-2.5.zip && \ - wget https://github.com/metersphere/jmeter-backend-listener-kafka/releases/download/v1.0.2/jmeter.backendlistener.kafka-1.0.2.jar && \ + wget https://github.com/metersphere/jmeter-backend-listener-kafka/releases/download/v${KAFKA_BACKEND_LISTENER_VERSION}/jmeter.backendlistener.kafka-${KAFKA_BACKEND_LISTENER_VERSION}.jar && \ wget https://github.com/metersphere/jmeter-plugins-for-apache-dubbo/releases/download/2.7.7/jmeter-plugins-dubbo-2.7.7-jar-with-dependencies.jar && \ mkdir -p /opt/jmeter && \ tar -zxf apache-jmeter-${JMETER_VERSION}.tgz -C /opt/jmeter/ --strip-components=1 && \ unzip -o jpgc-casutg-2.9.zip -d /tmp/ && mv /tmp/lib/ext/jmeter-plugins-casutg-2.9.jar /opt/jmeter/lib/ext && \ unzip -o jpgc-tst-2.5.zip -d /tmp/ && mv /tmp/lib/ext/jmeter-plugins-tst-2.5.jar /opt/jmeter/lib/ext && \ - mv jmeter.backendlistener.kafka-1.0.2.jar /opt/jmeter/lib/ext && \ + mv jmeter.backendlistener.kafka-${KAFKA_BACKEND_LISTENER_VERSION}.jar /opt/jmeter/lib/ext && \ mv jmeter-plugins-dubbo-2.7.7-jar-with-dependencies.jar /opt/jmeter/lib/ext && \ rm -rf apache-jmeter-${JMETER_VERSION}.tgz && \ rm -rf jpgc-casutg-2.9.zip && \ rm -rf jpgc-tst-2.5.zip && \ - rm -rf jmeter.backendlistener.kafka-1.0.2.jar && \ + rm -rf jmeter.backendlistener.kafka-${KAFKA_BACKEND_LISTENER_VERSION}.jar && \ rm -rf jmeter-plugins-dubbo-2.7.7-jar-with-dependencies.jar && \ rm -rf /var/cache/apk/* && \ wget -O /usr/bin/tpl https://github.com/schneidexe/tpl/releases/download/v0.5.0/tpl-linux-amd64 && \ @@ -31,7 +31,7 @@ RUN apk update && \ ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo "$TZ" > /etc/timezone ENV JMETER_HOME /opt/jmeter -ENV PATH $PATH:$JMETER_HOME/bin +ENV PATH $PATH:$JMETER_HOME/bin:/usr/lib/jvm/java-1.8-openjdk/bin ADD log4j2.xml $JMETER_HOME/bin/log4j2.xml ADD jmeter.properties $JMETER_HOME/bin/jmeter.properties \ No newline at end of file diff --git a/docker/jmeter-master/Dockerfile b/docker/jmeter-master/Dockerfile index d88bbbba44..372f8784d9 100644 --- a/docker/jmeter-master/Dockerfile +++ b/docker/jmeter-master/Dockerfile @@ -1,4 +1,4 @@ -FROM registry.fit2cloud.com/metersphere/jmeter-base:latest +FROM registry.fit2cloud.com/metersphere/jmeter-base:0.0.1 LABEL maintainer="support@fit2cloud.com" EXPOSE 60000 diff --git a/frontend/src/business/components/api/test/ApiTestConfig.vue b/frontend/src/business/components/api/test/ApiTestConfig.vue index e0e7ec9c20..d937b9e94d 100644 --- a/frontend/src/business/components/api/test/ApiTestConfig.vue +++ b/frontend/src/business/components/api/test/ApiTestConfig.vue @@ -53,7 +53,7 @@ + @scheduleChange="saveSchedule" :test-id="id" :check-open="checkScheduleEdit"/> HTTP DUBBO + SQL diff --git a/frontend/src/business/components/api/test/components/request/ApiRequestForm.vue b/frontend/src/business/components/api/test/components/request/ApiRequestForm.vue index 486021ee66..76db3cfeeb 100644 --- a/frontend/src/business/components/api/test/components/request/ApiRequestForm.vue +++ b/frontend/src/business/components/api/test/components/request/ApiRequestForm.vue @@ -9,14 +9,15 @@ + + diff --git a/frontend/src/business/components/api/test/components/request/database/DatabaseFrom.vue b/frontend/src/business/components/api/test/components/request/database/DatabaseFrom.vue index f9adfa69f7..7b02fa7cd6 100644 --- a/frontend/src/business/components/api/test/components/request/database/DatabaseFrom.vue +++ b/frontend/src/business/components/api/test/components/request/database/DatabaseFrom.vue @@ -8,7 +8,7 @@ - @@ -24,7 +24,7 @@ - @@ -67,7 +67,6 @@ data() { return { drivers: DatabaseConfig.DRIVER_CLASS, - // config: new DatabaseConfig(), rules: { name: [ {required: true, message: this.$t('commons.input_name'), trigger: 'blur'}, @@ -95,7 +94,6 @@ this.$refs['databaseFrom'].validate((valid) => { if (valid) { this.$emit('save', this.config); - // this.config = new DatabaseConfig(); } else { return false; } diff --git a/frontend/src/business/components/api/test/model/JMX.js b/frontend/src/business/components/api/test/model/JMX.js index 90cd6cc076..5147c939e2 100644 --- a/frontend/src/business/components/api/test/model/JMX.js +++ b/frontend/src/business/components/api/test/model/JMX.js @@ -274,6 +274,38 @@ export class DubboSample extends DefaultTestElement { } } +export class JDBCSampler extends DefaultTestElement { + constructor(testName, request = {}) { + super('JDBCSampler', 'TestBeanGUI', 'JDBCSampler', testName); + + this.stringProp("dataSource", request.dataSource); + this.stringProp("query", request.query); + this.stringProp("queryTimeout", request.queryTimeout); + this.stringProp("queryArguments"); + this.stringProp("queryArgumentsTypes"); + this.stringProp("resultSetMaxRows"); + this.stringProp("resultVariable"); + this.stringProp("variableNames"); + this.stringProp("resultSetHandler", 'Store as String'); + this.stringProp("queryType", 'Callable Statement'); + } +} + +// +// test +// select id from test_plan; +// select name from test_plan; +// +// +// +// +// Callable Statement +// Store as String +// +// +// +// + export class HTTPSamplerProxy extends DefaultTestElement { constructor(testName, options = {}) { super('HTTPSamplerProxy', 'HttpTestSampleGui', 'HTTPSamplerProxy', testName); @@ -515,6 +547,29 @@ export class DNSCacheManager extends DefaultTestElement { } } +export class JDBCDataSource extends DefaultTestElement { + constructor(testName, datasource) { + super('JDBCDataSource', 'TestBeanGUI', 'JDBCDataSource', testName); + + this.boolProp('autocommit', true); + this.boolProp('keepAlive', true); + this.boolProp('preinit', false); + this.stringProp('dataSource', datasource.name); + this.stringProp('dbUrl', datasource.dbUrl); + this.stringProp('driver', datasource.driver); + this.stringProp('username', datasource.username); + this.stringProp('password', datasource.password); + this.stringProp('poolMax', datasource.poolMax); + this.stringProp('timeout', datasource.timeout); + this.stringProp('connectionAge', '5000'); + this.stringProp('trimInterval', '60000'); + this.stringProp('transactionIsolation', 'DEFAULT'); + this.stringProp('checkQuery'); + this.stringProp('initQuery'); + this.stringProp('connectionProperties'); + } +} + export class Arguments extends DefaultTestElement { constructor(testName, args) { super('Arguments', 'ArgumentsPanel', 'Arguments', testName); diff --git a/frontend/src/business/components/api/test/model/ScenarioModel.js b/frontend/src/business/components/api/test/model/ScenarioModel.js index d632da62d5..60fd5cd00c 100644 --- a/frontend/src/business/components/api/test/model/ScenarioModel.js +++ b/frontend/src/business/components/api/test/model/ScenarioModel.js @@ -8,7 +8,7 @@ import { HashTree, HeaderManager, HTTPSamplerArguments, HTTPsamplerFiles, - HTTPSamplerProxy, + HTTPSamplerProxy, JDBCDataSource, JDBCSampler, JSONPathAssertion, JSONPostProcessor, JSR223PostProcessor, JSR223PreProcessor, RegexExtractor, @@ -211,7 +211,7 @@ export class Scenario extends BaseConfig { this.environment = undefined; this.enableCookieShare = false; this.enable = true; - this.databaseConfigs = undefined; + this.databaseConfigs = []; this.set(options); this.sets({variables: KeyValue, headers: KeyValue, requests: RequestFactory, databaseConfigs: DatabaseConfig}, options); @@ -273,6 +273,7 @@ export class RequestFactory { static TYPES = { HTTP: "HTTP", DUBBO: "DUBBO", + SQL: "SQL", } constructor(options = {}) { @@ -280,6 +281,8 @@ export class RequestFactory { switch (options.type) { case RequestFactory.TYPES.DUBBO: return new DubboRequest(options); + case RequestFactory.TYPES.SQL: + return new SqlRequest(options); default: return new HttpRequest(options); } @@ -460,6 +463,60 @@ export class DubboRequest extends Request { } } +export class SqlRequest extends Request { + + constructor(options = {}) { + super(RequestFactory.TYPES.SQL); + this.id = options.id || uuid(); + this.name = options.name; + this.dataSource = options.dataSource; + this.query = options.query; + // this.queryType = options.queryType; + this.queryTimeout = options.queryTimeout; + this.enable = options.enable === undefined ? true : options.enable; + this.assertions = new Assertions(options.assertions); + this.extract = new Extract(options.extract); + this.jsr223PreProcessor = new JSR223Processor(options.jsr223PreProcessor); + this.jsr223PostProcessor = new JSR223Processor(options.jsr223PostProcessor); + + this.sets({args: KeyValue, attachmentArgs: KeyValue}, options); + + } + + isValid() { + if (this.enable) { + if (!this.name) { + return { + isValid: false, + info: 'name' + } + } + if (!this.dataSource) { + return { + isValid: false, + info: 'dataSource' + } + } + } + return { + isValid: true + } + } + + showType() { + return "SQL"; + } + + showMethod() { + return "SQL"; + } + + clone() { + return new SqlRequest(this); + } +} + + export class ConfigCenter extends BaseConfig { static PROTOCOLS = ["zookeeper", "nacos", "apollo"]; @@ -502,24 +559,6 @@ export class DatabaseConfig extends BaseConfig { // options.id = options.id || uuid(); return options; } -// -// true -// -// 5000 -// -// test -// jdbc:mysql://localhost:3306/metersphere?autoReconnect=false&useUnicode=true&characterEncoding=UTF-8&characterSetResults=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true -// com.mysql.jdbc.Driver -// -// true -// root -// 10 -// false -// 10000 -// DEFAULT -// 60000 -// root -// isValid() { return !!this.name || !!this.poolMax || !!this.timeout || !!this.driver || !!this.dbUrl || !!this.username || !!this.password; @@ -929,6 +968,8 @@ class JMXGenerator { // 放在计划或线程组中,不建议放具体某个请求中 this.addDNSCacheManager(threadGroup, scenario.requests[0]); + this.addJDBCDataSource(threadGroup, scenario); + scenario.requests.forEach(request => { if (request.enable) { if (!request.isValid()) return; @@ -936,9 +977,7 @@ class JMXGenerator { if (request instanceof DubboRequest) { sampler = new DubboSample(request.name || "", new JMXDubboRequest(request, scenario.dubboConfig)); - } - - if (request instanceof HttpRequest) { + } else if (request instanceof HttpRequest) { sampler = new HTTPSamplerProxy(request.name || "", new JMXHttpRequest(request, scenario.environment)); this.addRequestHeader(sampler, request); if (request.method.toUpperCase() === 'GET') { @@ -946,6 +985,8 @@ class JMXGenerator { } else { this.addRequestBody(sampler, request, testId); } + } else if (request instanceof SqlRequest) { + sampler = new JDBCSampler(request.name || "", request); } this.addRequestExtractor(sampler, request); @@ -1009,6 +1050,13 @@ class JMXGenerator { } } + addJDBCDataSource(threadGroup, scenario) { + scenario.databaseConfigs.forEach(config => { + let name = config.name + "JDBCDataSource"; + threadGroup.put(new JDBCDataSource(name, config)); + }); + } + addScenarioHeaders(threadGroup, scenario) { let environment = scenario.environment; if (environment) { diff --git a/frontend/src/business/components/common/components/MsScheduleConfig.vue b/frontend/src/business/components/common/components/MsScheduleConfig.vue index 10aedf82dd..fe55e28700 100644 --- a/frontend/src/business/components/common/components/MsScheduleConfig.vue +++ b/frontend/src/business/components/common/components/MsScheduleConfig.vue @@ -6,7 +6,7 @@ SCHEDULER - @@ -38,6 +38,7 @@ export default { } }, props: { + testId:String, save: Function, schedule: {}, checkOpen: { diff --git a/frontend/src/business/components/common/components/MsScheduleEdit.vue b/frontend/src/business/components/common/components/MsScheduleEdit.vue index b7b0f1d542..4da1b2c248 100644 --- a/frontend/src/business/components/common/components/MsScheduleEdit.vue +++ b/frontend/src/business/components/common/components/MsScheduleEdit.vue @@ -4,6 +4,7 @@ + - - - - + + - - + + + + - - - - + > - + prop="enable" + > + - - - - - - - {{$t('commons.save')}} @@ -111,19 +81,20 @@
尊敬的用户:您好,您所执行的"+testName+"运行失败,请点击报告链接查看
尊敬的用户:您好,"+testName+"运行成功,请点击报告链接查看