From 5a3310a837e410290a886bd4d966d59197197ea1 Mon Sep 17 00:00:00 2001 From: q4speed Date: Wed, 11 Nov 2020 13:40:24 +0800 Subject: [PATCH] =?UTF-8?q?refactor(=E6=8E=A5=E5=8F=A3=E6=B5=8B=E8=AF=95):?= =?UTF-8?q?=20=E5=90=8E=E5=8F=B0=E8=BD=AC=E6=8D=A2JMX?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/pom.xml | 2 +- .../api/controller/APITestController.java | 32 +- .../io/metersphere/api/dto/scenario/Body.java | 17 + .../api/dto/scenario/HttpConfig.java | 14 + .../api/dto/scenario/KeyValue.java | 10 + .../api/dto/scenario/Scenario.java | 4 +- .../api/dto/scenario/TCPConfig.java | 12 +- .../assertions/AssertionDuration.java | 5 + .../scenario/assertions/AssertionJSR223.java | 5 + .../assertions/AssertionJsonPath.java | 5 + .../scenario/assertions/AssertionRegex.java | 7 +- .../dto/scenario/controller/IfController.java | 45 +- .../scenario/environment/CommonConfig.java | 13 + .../environment/EnvironmentConfig.java | 16 + .../api/dto/scenario/environment/Host.java | 12 + .../dto/scenario/extract/ExtractCommon.java | 7 +- .../dto/scenario/request/DubboRequest.java | 16 +- .../api/dto/scenario/request/HttpRequest.java | 30 +- .../api/dto/scenario/request/Request.java | 15 +- .../api/dto/scenario/request/SqlRequest.java | 16 +- .../api/dto/scenario/request/TCPRequest.java | 14 +- .../api/dto/scenario/timer/ConstantTimer.java | 2 +- .../api/jmeter/APIBackendListenerClient.java | 27 +- .../metersphere/api/jmeter/JMXGenerator.java | 730 ++++++++++++++++++ .../metersphere/api/jmeter/JMeterService.java | 40 +- .../api/service/APITestService.java | 110 +-- .../components/api/test/ApiTestConfig.vue | 648 ++++++++-------- .../components/api/test/OneClickOperation.vue | 4 - .../api/test/model/ScenarioModel.js | 23 +- 29 files changed, 1356 insertions(+), 525 deletions(-) create mode 100644 backend/src/main/java/io/metersphere/api/dto/scenario/HttpConfig.java create mode 100644 backend/src/main/java/io/metersphere/api/dto/scenario/environment/CommonConfig.java create mode 100644 backend/src/main/java/io/metersphere/api/dto/scenario/environment/EnvironmentConfig.java create mode 100644 backend/src/main/java/io/metersphere/api/dto/scenario/environment/Host.java create mode 100644 backend/src/main/java/io/metersphere/api/jmeter/JMXGenerator.java diff --git a/backend/pom.xml b/backend/pom.xml index 5df379de4c..28225acbca 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -19,7 +19,7 @@ 1.8 5.2.1 1.1.3 - 2.7.7 + 2.7.8 20.1.0 diff --git a/backend/src/main/java/io/metersphere/api/controller/APITestController.java b/backend/src/main/java/io/metersphere/api/controller/APITestController.java index 0b73d1f183..ae210389fe 100644 --- a/backend/src/main/java/io/metersphere/api/controller/APITestController.java +++ b/backend/src/main/java/io/metersphere/api/controller/APITestController.java @@ -1,5 +1,7 @@ package io.metersphere.api.controller; +import static io.metersphere.commons.utils.JsonPathUtils.getListJson; + import com.github.pagehelper.Page; import com.github.pagehelper.PageHelper; import io.metersphere.api.dto.*; @@ -14,18 +16,15 @@ import io.metersphere.commons.utils.SessionUtils; import io.metersphere.controller.request.QueryScheduleRequest; import io.metersphere.dto.ScheduleDao; import io.metersphere.service.CheckOwnerService; - import org.apache.shiro.authz.annotation.Logical; import org.apache.shiro.authz.annotation.RequiresRoles; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; -import javax.annotation.Resource; - import java.util.HashMap; import java.util.List; -import static io.metersphere.commons.utils.JsonPathUtils.getListJson; +import javax.annotation.Resource; @RestController @@ -76,18 +75,19 @@ public class APITestController { } @PostMapping(value = "/create", consumes = {"multipart/form-data"}) - public void create(@RequestPart("request") SaveAPITestRequest request, @RequestPart(value = "file") MultipartFile file, @RequestPart(value = "files") List bodyFiles) { - apiTestService.create(request, file, bodyFiles); + public void create(@RequestPart("request") SaveAPITestRequest request, @RequestPart(value = "files") List bodyFiles) { + apiTestService.create(request, bodyFiles); } @PostMapping(value = "/create/merge", consumes = {"multipart/form-data"}) - public void mergeCreate(@RequestPart("request") SaveAPITestRequest request, @RequestPart(value = "file") MultipartFile file, @RequestPart(value = "selectIds") List selectIds) { - apiTestService.mergeCreate(request, file, selectIds); + public void mergeCreate(@RequestPart("request") SaveAPITestRequest request, @RequestPart(value = "selectIds") List selectIds) { + apiTestService.mergeCreate(request, selectIds); } + @PostMapping(value = "/update", consumes = {"multipart/form-data"}) - public void update(@RequestPart("request") SaveAPITestRequest request, @RequestPart(value = "file") MultipartFile file, @RequestPart(value = "files") List bodyFiles) { + public void update(@RequestPart("request") SaveAPITestRequest request, @RequestPart(value = "files") List bodyFiles) { checkownerService.checkApiTestOwner(request.getId()); - apiTestService.update(request, file, bodyFiles); + apiTestService.update(request, bodyFiles); } @PostMapping(value = "/copy") @@ -101,7 +101,6 @@ public class APITestController { return apiTestService.get(testId); } - @PostMapping("/delete") public void delete(@RequestBody DeleteAPITestRequest request) { String testId = request.getId(); @@ -109,14 +108,19 @@ public class APITestController { apiTestService.delete(testId); } + @PostMapping(value = "/jmx") + public String getJMX(@RequestBody SaveAPITestRequest request) { + return apiTestService.getJMX(request); + } + @PostMapping(value = "/run") public String run(@RequestBody SaveAPITestRequest request) { return apiTestService.run(request); } @PostMapping(value = "/run/debug", consumes = {"multipart/form-data"}) - public String runDebug(@RequestPart("request") SaveAPITestRequest request, @RequestPart(value = "file") MultipartFile file, @RequestPart(value = "files") List bodyFiles) { - return apiTestService.runDebug(request, file, bodyFiles); + public String runDebug(@RequestPart("request") SaveAPITestRequest request, @RequestPart(value = "files") List bodyFiles) { + return apiTestService.runDebug(request, bodyFiles); } @PostMapping(value = "/checkName") @@ -137,7 +141,7 @@ public class APITestController { @PostMapping("/list/schedule/{goPage}/{pageSize}") public List listSchedule(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody QueryScheduleRequest request) { - Page page = PageHelper.startPage(goPage, pageSize, true); + PageHelper.startPage(goPage, pageSize, true); return apiTestService.listSchedule(request); } diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/Body.java b/backend/src/main/java/io/metersphere/api/dto/scenario/Body.java index 5073eeefbf..6bcb5f2b01 100644 --- a/backend/src/main/java/io/metersphere/api/dto/scenario/Body.java +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/Body.java @@ -1,6 +1,7 @@ package io.metersphere.api.dto.scenario; import lombok.Data; +import org.apache.commons.lang3.StringUtils; import java.util.List; @@ -10,4 +11,20 @@ public class Body { private String raw; private String format; private List kvs; + + private final static String KV = "KeyValue"; + private final static String FORM_DATA = "Form Data"; + private final static String RAW = "Raw"; + + public boolean isValid() { + if (this.isKV()) { + return kvs.stream().anyMatch(KeyValue::isValid); + } else { + return StringUtils.isNotBlank(raw); + } + } + + public boolean isKV() { + return StringUtils.equals(type, KV); + } } diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/HttpConfig.java b/backend/src/main/java/io/metersphere/api/dto/scenario/HttpConfig.java new file mode 100644 index 0000000000..7a1aaf68f2 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/HttpConfig.java @@ -0,0 +1,14 @@ +package io.metersphere.api.dto.scenario; + +import lombok.Data; + +import java.util.List; + +@Data +public class HttpConfig { + private String socket; + private String domain; + private String protocol = "https"; + private int port; + private List headers; +} 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 45ab26da49..f36a74d57d 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 @@ -2,6 +2,7 @@ package io.metersphere.api.dto.scenario; import io.metersphere.api.dto.scenario.request.BodyFile; import lombok.Data; +import org.apache.commons.lang3.StringUtils; import java.util.List; @@ -14,6 +15,7 @@ public class KeyValue { private String description; private String contentType; private boolean enable; + private boolean encode = true; public KeyValue() { this.enable = true; @@ -31,4 +33,12 @@ public class KeyValue { this.enable = true; this.description = description; } + + public boolean isValid() { + return (StringUtils.isNotBlank(name) || StringUtils.isNotBlank(value)) && !StringUtils.equalsIgnoreCase(type, "file"); + } + + public boolean isFile() { + return (StringUtils.isNotBlank(name) || StringUtils.isNotBlank(value)) && StringUtils.equalsIgnoreCase(type, "file"); + } } 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 c66d3df040..03d4331b47 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 @@ -11,12 +11,12 @@ public class Scenario { private String name; private String url; private String environmentId; - private Boolean enableCookieShare; + private boolean enableCookieShare; private List variables; private List headers; private List requests; private DubboConfig dubboConfig; private TCPConfig tcpConfig; private List databaseConfigs; - private Boolean enable; + private boolean enable = true; } diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/TCPConfig.java b/backend/src/main/java/io/metersphere/api/dto/scenario/TCPConfig.java index 62a34fd9a9..27062e3475 100644 --- a/backend/src/main/java/io/metersphere/api/dto/scenario/TCPConfig.java +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/TCPConfig.java @@ -6,12 +6,12 @@ import lombok.Data; public class TCPConfig { private String classname; private String server; - private Integer port; - private Integer ctimeout; - private Integer timeout; - private Boolean reUseConnection; - private Boolean nodelay; - private Boolean closeConnection; + private String port; + private String ctimeout; + private String timeout; + private boolean reUseConnection; + private boolean nodelay; + private boolean closeConnection; private String soLinger; private String eolByte; private String username; diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/assertions/AssertionDuration.java b/backend/src/main/java/io/metersphere/api/dto/scenario/assertions/AssertionDuration.java index af9f5ebbf1..1477f2e244 100644 --- a/backend/src/main/java/io/metersphere/api/dto/scenario/assertions/AssertionDuration.java +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/assertions/AssertionDuration.java @@ -2,6 +2,7 @@ package io.metersphere.api.dto.scenario.assertions; import lombok.Data; import lombok.EqualsAndHashCode; +import org.apache.commons.lang3.StringUtils; @EqualsAndHashCode(callSuper = true) @Data @@ -11,4 +12,8 @@ public class AssertionDuration extends AssertionType { public AssertionDuration() { setType(AssertionType.DURATION); } + + public boolean isValid() { + return value > 0; + } } diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/assertions/AssertionJSR223.java b/backend/src/main/java/io/metersphere/api/dto/scenario/assertions/AssertionJSR223.java index e492a46a59..46b6a39325 100644 --- a/backend/src/main/java/io/metersphere/api/dto/scenario/assertions/AssertionJSR223.java +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/assertions/AssertionJSR223.java @@ -2,6 +2,7 @@ package io.metersphere.api.dto.scenario.assertions; import lombok.Data; import lombok.EqualsAndHashCode; +import org.apache.commons.lang3.StringUtils; @EqualsAndHashCode(callSuper = true) @Data @@ -17,4 +18,8 @@ public class AssertionJSR223 extends AssertionType { public AssertionJSR223() { setType(AssertionType.JSR223); } + + public boolean isValid() { + return StringUtils.isNotBlank(script) && StringUtils.isNotBlank(language); + } } diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/assertions/AssertionJsonPath.java b/backend/src/main/java/io/metersphere/api/dto/scenario/assertions/AssertionJsonPath.java index c17a893559..b774725d78 100644 --- a/backend/src/main/java/io/metersphere/api/dto/scenario/assertions/AssertionJsonPath.java +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/assertions/AssertionJsonPath.java @@ -2,6 +2,7 @@ package io.metersphere.api.dto.scenario.assertions; import lombok.Data; import lombok.EqualsAndHashCode; +import org.apache.commons.lang3.StringUtils; @EqualsAndHashCode(callSuper = true) @Data @@ -13,4 +14,8 @@ public class AssertionJsonPath extends AssertionType { public AssertionJsonPath() { setType(AssertionType.JSON_PATH); } + + public boolean isValid() { + return StringUtils.isNotBlank(expression); + } } diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/assertions/AssertionRegex.java b/backend/src/main/java/io/metersphere/api/dto/scenario/assertions/AssertionRegex.java index 190a93e741..63dbc1b8a1 100644 --- a/backend/src/main/java/io/metersphere/api/dto/scenario/assertions/AssertionRegex.java +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/assertions/AssertionRegex.java @@ -2,6 +2,7 @@ package io.metersphere.api.dto.scenario.assertions; import lombok.Data; import lombok.EqualsAndHashCode; +import org.apache.commons.lang3.StringUtils; @EqualsAndHashCode(callSuper = true) @Data @@ -9,9 +10,13 @@ public class AssertionRegex extends AssertionType { private String subject; private String expression; private String description; - private Boolean assumeSuccess; + private boolean assumeSuccess; public AssertionRegex() { setType(AssertionType.REGEX); } + + public boolean isValid() { + return StringUtils.isNotBlank(subject) && StringUtils.isNotBlank(expression); + } } diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/controller/IfController.java b/backend/src/main/java/io/metersphere/api/dto/scenario/controller/IfController.java index e90289334b..0e7a89e69a 100644 --- a/backend/src/main/java/io/metersphere/api/dto/scenario/controller/IfController.java +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/controller/IfController.java @@ -1,13 +1,56 @@ package io.metersphere.api.dto.scenario.controller; import lombok.Data; +import org.apache.commons.lang3.StringUtils; @Data public class IfController { private String type; private String id; - private Boolean enable; + private boolean enable = true; private String variable; private String operator; private String value; + + public boolean isValid() { + if (StringUtils.contains(operator, "empty")) { + return StringUtils.isNotBlank(variable); + } + return StringUtils.isNotBlank(variable) && StringUtils.isNotBlank(operator) && StringUtils.isNotBlank(value); + } + + public String getLabel() { + if (isValid()) { + String label = variable + " " + operator; + if (StringUtils.isNotBlank(value)) { + label += " " + this.value; + } + return label; + } + return ""; + } + + public String getCondition() { + String variable = "\"" + this.variable + "\""; + String operator = this.operator; + String value = "\"" + this.value + "\""; + + if (StringUtils.contains(operator, "~")) { + value = "\".*" + this.value + ".*\""; + } + + if (StringUtils.equals(operator, "is empty")) { + variable = "empty(" + variable + ")"; + operator = ""; + value = ""; + } + + if (StringUtils.equals(operator, "is not empty")) { + variable = "!empty(" + variable + ")"; + operator = ""; + value = ""; + } + + return "${__jexl3(" + variable + operator + value + ")}"; + } } diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/environment/CommonConfig.java b/backend/src/main/java/io/metersphere/api/dto/scenario/environment/CommonConfig.java new file mode 100644 index 0000000000..6670bcfccc --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/environment/CommonConfig.java @@ -0,0 +1,13 @@ +package io.metersphere.api.dto.scenario.environment; + +import io.metersphere.api.dto.scenario.KeyValue; +import lombok.Data; + +import java.util.List; + +@Data +public class CommonConfig { + private List variables; + private boolean enableHost; + private List hosts; +} diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/environment/EnvironmentConfig.java b/backend/src/main/java/io/metersphere/api/dto/scenario/environment/EnvironmentConfig.java new file mode 100644 index 0000000000..8cf75f14d5 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/environment/EnvironmentConfig.java @@ -0,0 +1,16 @@ +package io.metersphere.api.dto.scenario.environment; + +import io.metersphere.api.dto.scenario.DatabaseConfig; +import io.metersphere.api.dto.scenario.HttpConfig; +import io.metersphere.api.dto.scenario.TCPConfig; +import lombok.Data; + +import java.util.List; + +@Data +public class EnvironmentConfig { + private CommonConfig commonConfig; + private HttpConfig httpConfig; + private List databaseConfigs; + private TCPConfig tcpConfig; +} diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/environment/Host.java b/backend/src/main/java/io/metersphere/api/dto/scenario/environment/Host.java new file mode 100644 index 0000000000..d1b20e9e5a --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/environment/Host.java @@ -0,0 +1,12 @@ +package io.metersphere.api.dto.scenario.environment; + +import lombok.Data; + +@Data +public class Host { + private String ip; + private String domain; + private String status; + private String annotation; + private String uuid; +} diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/extract/ExtractCommon.java b/backend/src/main/java/io/metersphere/api/dto/scenario/extract/ExtractCommon.java index efd38b6a49..20d8f051a8 100644 --- a/backend/src/main/java/io/metersphere/api/dto/scenario/extract/ExtractCommon.java +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/extract/ExtractCommon.java @@ -2,6 +2,7 @@ package io.metersphere.api.dto.scenario.extract; import lombok.Data; import lombok.EqualsAndHashCode; +import org.apache.commons.lang3.StringUtils; @EqualsAndHashCode(callSuper = true) @Data @@ -10,5 +11,9 @@ public class ExtractCommon extends ExtractType { private String value; // value: ${variable} private String expression; private String description; - private Boolean multipleMatching; + private boolean multipleMatching; + + public boolean isValid() { + return StringUtils.isNotBlank(variable) && StringUtils.isNotBlank(expression); + } } diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/request/DubboRequest.java b/backend/src/main/java/io/metersphere/api/dto/scenario/request/DubboRequest.java index a615b8dd98..1a7a6fa0bf 100644 --- a/backend/src/main/java/io/metersphere/api/dto/scenario/request/DubboRequest.java +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/request/DubboRequest.java @@ -18,23 +18,23 @@ import java.util.List; public class DubboRequest extends Request { // type 必须放最前面,以便能够转换正确的类 private String type = RequestType.DUBBO; - @JSONField(ordinal = 2) + @JSONField(ordinal = 52) private String protocol; @JsonProperty(value = "interface") - @JSONField(ordinal = 3, name = "interface") + @JSONField(ordinal = 53, name = "interface") private String _interface; - @JSONField(ordinal = 4) + @JSONField(ordinal = 54) private String method; - @JSONField(ordinal = 5) + @JSONField(ordinal = 55) private ConfigCenter configCenter; - @JSONField(ordinal = 6) + @JSONField(ordinal = 56) private RegistryCenter registryCenter; - @JSONField(ordinal = 7) + @JSONField(ordinal = 57) private ConsumerAndService consumerAndService; - @JSONField(ordinal = 8) + @JSONField(ordinal = 58) private List args; - @JSONField(ordinal = 9) + @JSONField(ordinal = 59) private List attachmentArgs; } diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/request/HttpRequest.java b/backend/src/main/java/io/metersphere/api/dto/scenario/request/HttpRequest.java index 74b497d07c..e716b0b030 100644 --- a/backend/src/main/java/io/metersphere/api/dto/scenario/request/HttpRequest.java +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/request/HttpRequest.java @@ -15,26 +15,24 @@ import java.util.List; public class HttpRequest extends Request { // type 必须放最前面,以便能够转换正确的类 private String type = RequestType.HTTP; - @JSONField(ordinal = 2) + @JSONField(ordinal = 50) private String url; - @JSONField(ordinal = 3) + @JSONField(ordinal = 51) private String method; - @JSONField(ordinal = 4) + @JSONField(ordinal = 52) private String path; - @JSONField(ordinal = 5) - private Boolean useEnvironment; - @JSONField(ordinal = 6) + @JSONField(ordinal = 53) private List parameters; - @JSONField(ordinal = 7) + @JSONField(ordinal = 54) private List headers; - @JSONField(ordinal = 8) + @JSONField(ordinal = 55) private Body body; - @JSONField(ordinal = 14) - private Long connectTimeout; - @JSONField(ordinal = 15) - private Long responseTimeout; - @JSONField(ordinal = 16) - private Boolean followRedirects; - @JSONField(ordinal = 17) - private Boolean doMultipartPost; + @JSONField(ordinal = 56) + private String connectTimeout; + @JSONField(ordinal = 57) + private String responseTimeout; + @JSONField(ordinal = 58) + private boolean followRedirects; + @JSONField(ordinal = 59) + private boolean doMultipartPost; } 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 0d8276b0fc..7d1d4eb070 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 @@ -22,22 +22,25 @@ import lombok.Data; @JSONType(seeAlso = {HttpRequest.class, DubboRequest.class, SqlRequest.class, TCPRequest.class}, typeKey = "type") @Data public abstract class Request { + private String type; @JSONField(ordinal = 1) private String id; @JSONField(ordinal = 2) private String name; @JSONField(ordinal = 3) - private Boolean enable; + private boolean enable = true; @JSONField(ordinal = 4) - private Assertions assertions; + private boolean useEnvironment; @JSONField(ordinal = 5) - private Extract extract; + private Assertions assertions; @JSONField(ordinal = 6) - private JSR223PreProcessor jsr223PreProcessor; + private Extract extract; @JSONField(ordinal = 7) - private JSR223PostProcessor jsr223PostProcessor; + private JSR223PreProcessor jsr223PreProcessor; @JSONField(ordinal = 8) - private IfController controller; + private JSR223PostProcessor jsr223PostProcessor; @JSONField(ordinal = 9) + private IfController controller; + @JSONField(ordinal = 10) private ConstantTimer timer; } 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 index b116326049..4fdf82c28c 100644 --- 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 @@ -14,20 +14,16 @@ import java.util.List; public class SqlRequest extends Request { // type 必须放最前面,以便能够转换正确的类 private String type = RequestType.SQL; - @JSONField(ordinal = 3) + @JSONField(ordinal = 50) private String dataSource; - @JSONField(ordinal = 4) + @JSONField(ordinal = 51) private String query; - @JSONField(ordinal = 5) + @JSONField(ordinal = 52) private long queryTimeout; - @JSONField(ordinal = 6) - private Boolean useEnvironment; - @JSONField(ordinal = 7) - private Boolean followRedirects; - @JSONField(ordinal = 13) + @JSONField(ordinal = 53) private String resultVariable; - @JSONField(ordinal = 14) + @JSONField(ordinal = 54) private String variableNames; - @JSONField(ordinal = 15) + @JSONField(ordinal = 55) private List variables; } diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/request/TCPRequest.java b/backend/src/main/java/io/metersphere/api/dto/scenario/request/TCPRequest.java index cce0363da4..882792cfb3 100644 --- a/backend/src/main/java/io/metersphere/api/dto/scenario/request/TCPRequest.java +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/request/TCPRequest.java @@ -11,24 +11,22 @@ import lombok.EqualsAndHashCode; public class TCPRequest extends Request { // type 必须放最前面,以便能够转换正确的类 private String type = RequestType.TCP; - @JSONField(ordinal = 50) - private Boolean useEnvironment; @JSONField(ordinal = 51) private String classname; @JSONField(ordinal = 52) private String server; @JSONField(ordinal = 53) - private Integer port; + private String port; @JSONField(ordinal = 54) - private Integer ctimeout; + private String ctimeout; @JSONField(ordinal = 55) - private Integer timeout; + private String timeout; @JSONField(ordinal = 56) - private Boolean reUseConnection; + private boolean reUseConnection; @JSONField(ordinal = 57) - private Boolean nodelay; + private boolean nodelay; @JSONField(ordinal = 58) - private Boolean closeConnection; + private boolean closeConnection; @JSONField(ordinal = 59) private String soLinger; @JSONField(ordinal = 60) diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/timer/ConstantTimer.java b/backend/src/main/java/io/metersphere/api/dto/scenario/timer/ConstantTimer.java index 8823337b0c..fa233cf046 100644 --- a/backend/src/main/java/io/metersphere/api/dto/scenario/timer/ConstantTimer.java +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/timer/ConstantTimer.java @@ -6,6 +6,6 @@ import lombok.Data; public class ConstantTimer { private String type; private String id; - private Boolean enable; + private boolean enable = true; private String delay; } diff --git a/backend/src/main/java/io/metersphere/api/jmeter/APIBackendListenerClient.java b/backend/src/main/java/io/metersphere/api/jmeter/APIBackendListenerClient.java index cc3ad10c74..3c0dd44e55 100644 --- a/backend/src/main/java/io/metersphere/api/jmeter/APIBackendListenerClient.java +++ b/backend/src/main/java/io/metersphere/api/jmeter/APIBackendListenerClient.java @@ -42,14 +42,13 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl private final List queue = new ArrayList<>(); private APITestService apiTestService; - private APIReportService apiReportService; - private TestPlanTestCaseService testPlanTestCaseService; - private NoticeService noticeService; - private MailService mailService; + private DingTaskService dingTaskService; + private WxChatTaskService wxChatTaskService; + private SystemParameterService systemParameterService; public String runMode = ApiRunMode.RUN.name(); @@ -82,6 +81,18 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl if (mailService == null) { LogUtil.error("mailService is required"); } + dingTaskService = CommonBeanFactory.getBean(DingTaskService.class); + if (dingTaskService == null) { + LogUtil.error("dingTaskService is required"); + } + wxChatTaskService = CommonBeanFactory.getBean(WxChatTaskService.class); + if (wxChatTaskService == null) { + LogUtil.error("wxChatTaskService is required"); + } + systemParameterService = CommonBeanFactory.getBean(SystemParameterService.class); + if (systemParameterService == null) { + LogUtil.error("systemParameterService is required"); + } super.setupTest(context); } @@ -146,7 +157,6 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl queue.clear(); super.teardownTest(context); - TestPlanTestCaseService testPlanTestCaseService = CommonBeanFactory.getBean(TestPlanTestCaseService.class); List ids = testPlanTestCaseService.getTestPlanTestCaseIds(testResult.getTestId()); if (ids.size() > 0) { try { @@ -167,12 +177,7 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl } - private static void sendTask(ApiTestReport report, TestResult testResult) { - NoticeService noticeService = CommonBeanFactory.getBean(NoticeService.class); - MailService mailService = CommonBeanFactory.getBean(MailService.class); - DingTaskService dingTaskService = CommonBeanFactory.getBean(DingTaskService.class); - WxChatTaskService wxChatTaskService = CommonBeanFactory.getBean(WxChatTaskService.class); - SystemParameterService systemParameterService = CommonBeanFactory.getBean(SystemParameterService.class); + private void sendTask(ApiTestReport report, TestResult testResult) { if (StringUtils.equals(NoticeConstants.API, report.getTriggerMode()) || StringUtils.equals(NoticeConstants.SCHEDULE, report.getTriggerMode())) { List userIds = new ArrayList<>(); List taskList = new ArrayList<>(); diff --git a/backend/src/main/java/io/metersphere/api/jmeter/JMXGenerator.java b/backend/src/main/java/io/metersphere/api/jmeter/JMXGenerator.java new file mode 100644 index 0000000000..dc39052673 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/jmeter/JMXGenerator.java @@ -0,0 +1,730 @@ +package io.metersphere.api.jmeter; + +import com.alibaba.fastjson.JSONObject; +import io.github.ningyu.jmeter.plugin.dubbo.gui.DubboSampleGui; +import io.github.ningyu.jmeter.plugin.dubbo.sample.DubboSample; +import io.github.ningyu.jmeter.plugin.dubbo.sample.MethodArgument; +import io.github.ningyu.jmeter.plugin.util.Constants; +import io.metersphere.api.dto.scenario.*; +import io.metersphere.api.dto.scenario.assertions.*; +import io.metersphere.api.dto.scenario.environment.EnvironmentConfig; +import io.metersphere.api.dto.scenario.environment.Host; +import io.metersphere.api.dto.scenario.extract.*; +import io.metersphere.api.dto.scenario.request.*; +import io.metersphere.api.service.ApiTestEnvironmentService; +import io.metersphere.base.domain.ApiTestEnvironmentWithBLOBs; +import io.metersphere.commons.utils.LogUtil; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.http.protocol.HTTP; +import org.apache.jmeter.assertions.DurationAssertion; +import org.apache.jmeter.assertions.JSONPathAssertion; +import org.apache.jmeter.assertions.JSR223Assertion; +import org.apache.jmeter.assertions.ResponseAssertion; +import org.apache.jmeter.config.Arguments; +import org.apache.jmeter.config.ConfigTestElement; +import org.apache.jmeter.control.IfController; +import org.apache.jmeter.control.LoopController; +import org.apache.jmeter.extractor.JSR223PostProcessor; +import org.apache.jmeter.extractor.RegexExtractor; +import org.apache.jmeter.extractor.XPath2Extractor; +import org.apache.jmeter.extractor.json.jsonpath.JSONPostProcessor; +import org.apache.jmeter.modifiers.JSR223PreProcessor; +import org.apache.jmeter.protocol.http.control.CookieManager; +import org.apache.jmeter.protocol.http.control.DNSCacheManager; +import org.apache.jmeter.protocol.http.control.Header; +import org.apache.jmeter.protocol.http.control.HeaderManager; +import org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy; +import org.apache.jmeter.protocol.http.util.HTTPArgument; +import org.apache.jmeter.protocol.http.util.HTTPFileArg; +import org.apache.jmeter.protocol.jdbc.config.DataSourceElement; +import org.apache.jmeter.protocol.jdbc.sampler.JDBCSampler; +import org.apache.jmeter.protocol.tcp.sampler.TCPSampler; +import org.apache.jmeter.testelement.TestElement; +import org.apache.jmeter.testelement.TestPlan; +import org.apache.jmeter.threads.ThreadGroup; +import org.apache.jmeter.timers.ConstantTimer; +import org.apache.jorphan.collections.HashTree; +import org.apache.jorphan.collections.ListedHashTree; +import org.springframework.stereotype.Component; + +import java.net.URL; +import java.net.URLDecoder; +import java.util.*; +import java.util.stream.Collectors; + +import javax.annotation.Resource; + +@Component +public class JMXGenerator { + + @Resource + private ApiTestEnvironmentService environmentService; + + public HashTree parse(String testId, String testName, List scenarios) { + HashTree jmeterTestPlanHashTree = new ListedHashTree(); + final HashTree testPlanHashTree = jmeterTestPlanHashTree.add(testPlan(testName)); + + scenarios.stream().filter(Scenario::isEnable).forEach(scenario -> { + final HashTree threadGroupHashTree = testPlanHashTree.add(threadGroup(scenario.getName())); + + EnvironmentConfig config = getEnvironmentConfig(scenario.getEnvironmentId()); + + // 场景变量 + if (CollectionUtils.isNotEmpty(scenario.getVariables())) { + threadGroupHashTree.add(arguments(scenario.getName() + " Variables", scenario.getVariables())); + } + // 场景请求头 + if (CollectionUtils.isNotEmpty(scenario.getHeaders())) { + threadGroupHashTree.add(headerManager(scenario.getName() + " Headers", scenario.getHeaders())); + } + // 共享Cookie + if (scenario.isEnableCookieShare()) { + threadGroupHashTree.add(cookieManager(scenario)); + } + // 场景JDBCDataSource + final Map databaseConfigMap = new HashMap<>(); + if (CollectionUtils.isNotEmpty(scenario.getDatabaseConfigs())) { + scenario.getDatabaseConfigs().forEach(databaseConfig -> { + threadGroupHashTree.add(jdbcDataSource(databaseConfig)); + databaseConfigMap.put(databaseConfig.getId(), databaseConfig.getName()); + }); + } + // 场景TCP Config + threadGroupHashTree.add(tcpConfig(scenario.getName() + "TCP Config", scenario.getTcpConfig())); + + // 请求 + scenario.getRequests().stream().filter(Request::isEnable).forEach(request -> { + final HashTree samplerHashTree = new ListedHashTree(); + Object sampler; + switch (request.getType()) { + case RequestType.TCP: + sampler = tcpSampler((TCPRequest) request); + // 引用环境的TCP Config + if (request.isUseEnvironment() && config != null) { + samplerHashTree.add(tcpConfig(request.getName() + "TCP Config", config.getTcpConfig())); + } + break; + case RequestType.DUBBO: + sampler = dubboSample((DubboRequest) request); + break; + case RequestType.SQL: + SqlRequest sqlRequest = (SqlRequest) request; + // 引用环境的JDBCDataSource + if (request.isUseEnvironment() && config != null) { + config.getDatabaseConfigs().forEach(databaseConfig -> { + if (!databaseConfigMap.containsValue(databaseConfig.getName())) { + samplerHashTree.add(jdbcDataSource(databaseConfig)); + databaseConfigMap.put(databaseConfig.getId(), databaseConfig.getName()); + } + }); + } + samplerHashTree.add(arguments(sqlRequest.getName() + " Variables", sqlRequest.getVariables())); + sampler = jdbcSampler(sqlRequest, databaseConfigMap); + break; + default: + HttpRequest httpRequest = (HttpRequest) request; + sampler = httpSamplerProxy(httpRequest, config, testId); + // 请求头(包括引用环境里设置的请求头) + List headers = httpRequest.getHeaders(); + if (request.isUseEnvironment() && config != null) { + headers = merge(headers, config.getHttpConfig().getHeaders()); + } + // 根据请求内容给请求头添加Content-Type + addContentType(headers, httpRequest.getBody()); + samplerHashTree.add(headerManager(request.getName() + " Headers", headers)); + break; + } + + if (request.getController() != null && request.getController().isEnable() && request.getController().isValid()) { + threadGroupHashTree.add(ifController(request)).set(sampler, samplerHashTree); + } else { + threadGroupHashTree.set(sampler, samplerHashTree); + } + + if (request.isUseEnvironment() && config != null && config.getCommonConfig() != null) { + addEnvironmentVariables(samplerHashTree, request, config); + addEnvironmentDNS(samplerHashTree, request, config); + } + + addRequestAssertions(samplerHashTree, request); + addRequestExtractors(samplerHashTree, request); + addJSR223Processors(samplerHashTree, request); + + if (request.getTimer() != null && request.getTimer().isEnable()) { + if (StringUtils.isNotBlank(request.getTimer().getDelay())) { + samplerHashTree.add(constantTimer(request)); + } + } + }); + }); + return jmeterTestPlanHashTree; + } + + private void addContentType(List headers, Body body) { + if (!body.isKV() && StringUtils.isNotBlank(body.getFormat())) { + Map map = new HashMap<>(); + map.put("json", "application/json"); + map.put("html", "text/html"); + map.put("xml", "text/xml"); + String contentType = map.get(body.getFormat()); + boolean hasContentType = headers.stream().filter(KeyValue::isEnable).anyMatch(keyValue -> keyValue.getName().equals(HTTP.CONTENT_TYPE)); + if (contentType != null && !hasContentType) { + headers.add(new KeyValue(HTTP.CONTENT_TYPE, contentType)); + } + } + } + + private void addEnvironmentVariables(HashTree samplerHashTree, Request request, EnvironmentConfig config) { + String name = request.getName() + "Environment Variables"; + samplerHashTree.add(arguments(name, config.getCommonConfig().getVariables())); + } + + private void addEnvironmentDNS(HashTree samplerHashTree, Request request, EnvironmentConfig config) { + if (config.getCommonConfig().isEnableHost() && CollectionUtils.isNotEmpty(config.getCommonConfig().getHosts())) { + String domain = config.getHttpConfig().getDomain().trim(); + List hosts = new ArrayList<>(); + config.getCommonConfig().getHosts().forEach(host -> { + if (StringUtils.isNotBlank(host.getDomain())) { + String hostDomain = host.getDomain().trim().replace("http://", "").replace("https://", ""); + if (StringUtils.equals(hostDomain, domain)) { + host.setDomain(hostDomain); // 域名去掉协议 + hosts.add(host); + } + } + }); + samplerHashTree.add(dnsCacheManager(request.getName() + " DNSCacheManager", hosts)); + } + } + + private void addRequestAssertions(HashTree samplerHashTree, Request request) { + Assertions assertions = request.getAssertions(); + if (CollectionUtils.isNotEmpty(assertions.getRegex())) { + assertions.getRegex().stream().filter(AssertionRegex::isValid).forEach(assertion -> + samplerHashTree.add(responseAssertion(assertion)) + ); + } + + if (CollectionUtils.isNotEmpty(assertions.getJsonPath())) { + assertions.getJsonPath().stream().filter(AssertionJsonPath::isValid).forEach(assertion -> + samplerHashTree.add(jsonPathAssertion(assertion)) + ); + } + + if (CollectionUtils.isNotEmpty(assertions.getJsr223())) { + assertions.getJsr223().stream().filter(AssertionJSR223::isValid).forEach(assertion -> + samplerHashTree.add(jsr223Assertion(assertion)) + ); + } + + if (assertions.getDuration().isValid()) { + samplerHashTree.add(durationAssertion(assertions.getDuration())); + } + } + + private void addRequestExtractors(HashTree samplerHashTree, Request request) { + Extract extract = request.getExtract(); + if (CollectionUtils.isNotEmpty(extract.getRegex())) { + extract.getRegex().stream().filter(ExtractCommon::isValid).forEach(extractRegex -> + samplerHashTree.add(regexExtractor(extractRegex)) + ); + } + if (CollectionUtils.isNotEmpty(extract.getXpath())) { + extract.getXpath().stream().filter(ExtractCommon::isValid).forEach(extractXPath -> + samplerHashTree.add(xPath2Extractor(extractXPath)) + ); + } + if (CollectionUtils.isNotEmpty(extract.getJson())) { + extract.getJson().stream().filter(ExtractCommon::isValid).forEach(extractJSONPath -> + samplerHashTree.add(jsonPostProcessor(extractJSONPath)) + ); + } + } + + private void addJSR223Processors(HashTree samplerHashTree, Request request) { + if (request.getJsr223PreProcessor() != null) { + if (StringUtils.isNotBlank(request.getJsr223PreProcessor().getScript())) { + samplerHashTree.add(jsr223PreProcessor(request)); + } + } + if (request.getJsr223PostProcessor() != null) { + if (StringUtils.isNotBlank(request.getJsr223PostProcessor().getScript())) { + samplerHashTree.add(jsr223PostProcessor(request)); + } + } + } + + private EnvironmentConfig getEnvironmentConfig(String id) { + if (StringUtils.isBlank(id)) return null; + ApiTestEnvironmentWithBLOBs environment = environmentService.get(id); + if (environment != null) { + return JSONObject.parseObject(environment.getConfig(), EnvironmentConfig.class); + } + return null; + } + + private TestPlan testPlan(String testName) { + TestPlan testPlan = new TestPlan(testName); + testPlan.setProperty(TestElement.TEST_CLASS, "TestPlan"); + testPlan.setProperty(TestElement.GUI_CLASS, "TestPlanGui"); + testPlan.setEnabled(true); + testPlan.setFunctionalMode(false); + testPlan.setSerialized(true); + testPlan.setTearDownOnShutdown(true); + testPlan.setUserDefinedVariables(new Arguments()); + return testPlan; + } + + private ThreadGroup threadGroup(String name) { + LoopController loopController = new LoopController(); + loopController.setName("LoopController"); + loopController.setProperty(TestElement.TEST_CLASS, "LoopController"); + loopController.setProperty(TestElement.GUI_CLASS, "LoopControlPanel"); + loopController.setEnabled(true); + loopController.setLoops(1); + + ThreadGroup threadGroup = new ThreadGroup(); + threadGroup.setNumThreads(1); + threadGroup.setRampUp(1); + threadGroup.setDelay(0); + threadGroup.setDuration(0); + threadGroup.setProperty(ThreadGroup.ON_SAMPLE_ERROR, ThreadGroup.ON_SAMPLE_ERROR_CONTINUE); + threadGroup.setScheduler(false); + threadGroup.setName(name); + threadGroup.setProperty(TestElement.TEST_CLASS, "ThreadGroup"); + threadGroup.setProperty(TestElement.GUI_CLASS, "ThreadGroupGui"); + threadGroup.setEnabled(true); + threadGroup.setSamplerController(loopController); + + return threadGroup; + } + + private Arguments arguments(String name, List variables) { + Arguments arguments = new Arguments(); + arguments.setEnabled(true); + arguments.setName(name); + arguments.setProperty(TestElement.TEST_CLASS, "Arguments"); + arguments.setProperty(TestElement.GUI_CLASS, "ArgumentsPanel"); + variables.stream().filter(KeyValue::isValid).filter(KeyValue::isEnable).forEach(keyValue -> + arguments.addArgument(keyValue.getName(), keyValue.getValue(), "=") + ); + return arguments; + } + + private HeaderManager headerManager(String name, List headers) { + HeaderManager headerManager = new HeaderManager(); + headerManager.setEnabled(true); + headerManager.setName(name); + headerManager.setProperty(TestElement.TEST_CLASS, "HeaderManager"); + headerManager.setProperty(TestElement.GUI_CLASS, "HeaderPanel"); + headers.stream().filter(KeyValue::isValid).filter(KeyValue::isEnable).forEach(keyValue -> + headerManager.add(new Header(keyValue.getName(), keyValue.getValue())) + ); + return headerManager; + } + + private CookieManager cookieManager(Scenario scenario) { + CookieManager cookieManager = new CookieManager(); + cookieManager.setEnabled(true); + cookieManager.setName(scenario.getName() + " Cookie"); + cookieManager.setProperty(TestElement.TEST_CLASS, "CookieManager"); + cookieManager.setProperty(TestElement.GUI_CLASS, "CookiePanel"); + cookieManager.setClearEachIteration(false); + cookieManager.setControlledByThread(false); + return cookieManager; + } + + private DataSourceElement jdbcDataSource(DatabaseConfig databaseConfig) { + DataSourceElement dataSourceElement = new DataSourceElement(); + dataSourceElement.setEnabled(true); + dataSourceElement.setName(databaseConfig.getName() + " JDBCDataSource"); + dataSourceElement.setProperty(TestElement.TEST_CLASS, "JDBCDataSource"); + dataSourceElement.setProperty(TestElement.GUI_CLASS, "TestBeanGUI"); + dataSourceElement.setAutocommit(true); + dataSourceElement.setKeepAlive(true); + dataSourceElement.setPreinit(true); + dataSourceElement.setDataSource(databaseConfig.getName()); + dataSourceElement.setDbUrl(databaseConfig.getDbUrl()); + dataSourceElement.setDriver(databaseConfig.getDriver()); + dataSourceElement.setUsername(databaseConfig.getUsername()); + dataSourceElement.setPassword(databaseConfig.getPassword()); + dataSourceElement.setPoolMax(String.valueOf(databaseConfig.getPoolMax())); + dataSourceElement.setTimeout(String.valueOf(databaseConfig.getTimeout())); + dataSourceElement.setConnectionAge("5000"); + dataSourceElement.setTrimInterval("60000"); + dataSourceElement.setTransactionIsolation("DEFAULT"); + return dataSourceElement; + } + + private ConfigTestElement tcpConfig(String name, TCPConfig tcpConfig) { + ConfigTestElement configTestElement = new ConfigTestElement(); + configTestElement.setEnabled(true); + configTestElement.setName(name); + configTestElement.setProperty(TestElement.TEST_CLASS, "ConfigTestElement"); + configTestElement.setProperty(TestElement.GUI_CLASS, "TCPConfigGui"); + configTestElement.setProperty(TCPSampler.CLASSNAME, tcpConfig.getClassname()); + configTestElement.setProperty(TCPSampler.SERVER, tcpConfig.getServer()); + configTestElement.setProperty(TCPSampler.PORT, tcpConfig.getPort()); + configTestElement.setProperty(TCPSampler.TIMEOUT_CONNECT, tcpConfig.getCtimeout()); + configTestElement.setProperty(TCPSampler.RE_USE_CONNECTION, tcpConfig.isReUseConnection()); + configTestElement.setProperty(TCPSampler.NODELAY, tcpConfig.isNodelay()); + configTestElement.setProperty(TCPSampler.CLOSE_CONNECTION, tcpConfig.isCloseConnection()); + configTestElement.setProperty(TCPSampler.SO_LINGER, tcpConfig.getSoLinger()); + configTestElement.setProperty(TCPSampler.EOL_BYTE, tcpConfig.getEolByte()); + configTestElement.setProperty(TCPSampler.SO_LINGER, tcpConfig.getSoLinger()); + configTestElement.setProperty(ConfigTestElement.USERNAME, tcpConfig.getUsername()); + configTestElement.setProperty(ConfigTestElement.PASSWORD, tcpConfig.getPassword()); + return configTestElement; + } + + private HTTPSamplerProxy httpSamplerProxy(HttpRequest request, EnvironmentConfig config, String testId) { + HTTPSamplerProxy sampler = new HTTPSamplerProxy(); + sampler.setEnabled(true); + sampler.setName(request.getName()); + sampler.setProperty(TestElement.TEST_CLASS, "HTTPSamplerProxy"); + sampler.setProperty(TestElement.GUI_CLASS, "HttpTestSampleGui"); + sampler.setMethod(request.getMethod()); + + try { + if (request.isUseEnvironment()) { + sampler.setDomain(config.getHttpConfig().getDomain()); + sampler.setPort(config.getHttpConfig().getPort()); + sampler.setProtocol(config.getHttpConfig().getProtocol()); + String url = config.getHttpConfig().getProtocol() + "://" + config.getHttpConfig().getSocket(); + URL urlObject = new URL(url); + sampler.setDomain(config.getHttpConfig().getDomain()); + String envPath = StringUtils.equals(urlObject.getPath(), "/") ? "" : urlObject.getPath(); + if (StringUtils.isNotBlank(request.getPath())) { + envPath += request.getPath(); + } + sampler.setPath(getPostQueryParameters(request, URLDecoder.decode(envPath, "UTF-8"))); + } else { + String url = request.getUrl(); + if (!url.startsWith("http://") && !url.startsWith("https://")) { + url = "http://" + url; + } + URL urlObject = new URL(url); + sampler.setDomain(URLDecoder.decode(urlObject.getHost(), "UTF-8")); + sampler.setPort(urlObject.getPort()); + sampler.setProtocol(urlObject.getProtocol()); + sampler.setPath(getPostQueryParameters(request, URLDecoder.decode(urlObject.getPath(), "UTF-8"))); + } + } catch (Exception e) { + LogUtil.error(e); + } + + sampler.setConnectTimeout(request.getConnectTimeout()); + sampler.setResponseTimeout(request.getResponseTimeout()); + sampler.setFollowRedirects(request.isFollowRedirects()); + sampler.setDoBrowserCompatibleMultipart(request.isDoMultipartPost()); + // 请求参数 + sampler.setArguments(httpArguments(request.getParameters())); + // 请求体 + if (!StringUtils.equals(request.getMethod(), "GET")) { + List body = new ArrayList<>(); + if (request.getBody().isKV()) { + body = request.getBody().getKvs().stream().filter(KeyValue::isValid).collect(Collectors.toList()); + sampler.setHTTPFiles(httpFileArgs(request, testId)); + } else { + KeyValue keyValue = new KeyValue("", request.getBody().getRaw()); + keyValue.setEnable(true); + keyValue.setEncode(false); + body.add(keyValue); + } + sampler.setPostBodyRaw(true); + sampler.setArguments(httpArguments(body)); + } + return sampler; + } + + private String getPostQueryParameters(HttpRequest request, String path) { + if (!StringUtils.equals(request.getMethod(), "GET")) { + StringBuffer stringBuffer = new StringBuffer(); + stringBuffer.append(path); + stringBuffer.append("?"); + request.getParameters().stream().filter(KeyValue::isEnable).filter(KeyValue::isValid).forEach(keyValue -> + stringBuffer.append(keyValue.getName()).append("=").append(keyValue.getValue()).append("&") + ); + return stringBuffer.substring(0, stringBuffer.length() - 1); + } + return ""; + } + + 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()); + httpArgument.setAlwaysEncoded(keyValue.isEncode()); + httpArgument.setUseEquals(true); + httpArgument.setContentType(keyValue.getContentType()); + arguments.addArgument(httpArgument); + } + ); + return arguments; + } + + private HTTPFileArg[] httpFileArgs(HttpRequest request, String testId) { + final String BODY_FILE_DIR = "/opt/metersphere/data/body"; + List list = new ArrayList<>(); + request.getBody().getKvs().stream().filter(KeyValue::isFile).filter(KeyValue::isEnable).forEach(keyValue -> { + if (keyValue.getFiles() != null) { + keyValue.getFiles().forEach(file -> { + String paramName = keyValue.getName(); + String path = BODY_FILE_DIR + '/' + testId + '/' + file.getId() + '_' + file.getName(); + String mimetype = keyValue.getContentType(); + list.add(new HTTPFileArg(path, paramName, mimetype)); + }); + } + }); + return list.toArray(new HTTPFileArg[0]); + } + + private List merge(List list1, List list2) { + Set names = list1.stream().map(KeyValue::getName).collect(Collectors.toSet()); + List list = new ArrayList<>(list1); + list2.stream().filter(keyValue -> !names.contains(keyValue.getName())).forEach(list::add); + return list; + } + + private TCPSampler tcpSampler(TCPRequest request) { + TCPSampler tcpSampler = new TCPSampler(); + tcpSampler.setName(request.getName()); + tcpSampler.setProperty(TestElement.TEST_CLASS, "TCPSampler"); + tcpSampler.setProperty(TestElement.GUI_CLASS, "TCPSamplerGui"); + tcpSampler.setClassname(request.getClassname()); + tcpSampler.setServer(request.getServer()); + tcpSampler.setPort(request.getPort()); + tcpSampler.setConnectTimeout(request.getCtimeout()); + tcpSampler.setProperty(TCPSampler.RE_USE_CONNECTION, request.isReUseConnection()); + tcpSampler.setProperty(TCPSampler.NODELAY, request.isNodelay()); + tcpSampler.setCloseConnection(String.valueOf(request.isCloseConnection())); + tcpSampler.setSoLinger(request.getSoLinger()); + tcpSampler.setEolByte(request.getEolByte()); + tcpSampler.setRequestData(request.getRequest()); + tcpSampler.setProperty(ConfigTestElement.USERNAME, request.getUsername()); + tcpSampler.setProperty(ConfigTestElement.PASSWORD, request.getPassword()); + + return tcpSampler; + } + + private JDBCSampler jdbcSampler(SqlRequest request, Map databaseConfigMap) { + JDBCSampler sampler = new JDBCSampler(); + sampler.setName(request.getName()); + sampler.setProperty(TestElement.TEST_CLASS, "JDBCSampler"); + sampler.setProperty(TestElement.GUI_CLASS, "TestBeanGUI"); + // request.getDataSource() 是ID,需要转换为Name + sampler.setDataSource(databaseConfigMap.get(request.getDataSource())); + sampler.setQuery(request.getQuery()); + sampler.setQueryTimeout(String.valueOf(request.getQueryTimeout())); + sampler.setResultVariable(request.getResultVariable()); + sampler.setVariableNames(request.getVariableNames()); + sampler.setResultSetHandler("Store as String"); + sampler.setQueryType("Callable Statement"); + return sampler; + } + + private DubboSample dubboSample(DubboRequest request) { + DubboSample sampler = new DubboSample(); + sampler.setName(request.getName()); + sampler.setProperty(TestElement.TEST_CLASS, DubboSample.class.getName()); + sampler.setProperty(TestElement.GUI_CLASS, DubboSampleGui.class.getName()); + + Constants.setConfigCenterProtocol(request.getConfigCenter().getProtocol(), sampler); + Constants.setConfigCenterGroup(request.getConfigCenter().getGroup(), sampler); + Constants.setConfigCenterNamespace(request.getConfigCenter().getNamespace(), sampler); + Constants.setConfigCenterUserName(request.getConfigCenter().getUsername(), sampler); + Constants.setConfigCenterPassword(request.getConfigCenter().getPassword(), sampler); + Constants.setConfigCenterAddress(request.getConfigCenter().getAddress(), sampler); + Constants.setConfigCenterTimeout(request.getConfigCenter().getTimeout(), sampler); + + Constants.setRegistryProtocol(request.getRegistryCenter().getProtocol(), sampler); + Constants.setRegistryGroup(request.getRegistryCenter().getGroup(), sampler); + Constants.setRegistryUserName(request.getRegistryCenter().getUsername(), sampler); + Constants.setRegistryPassword(request.getRegistryCenter().getPassword(), sampler); + Constants.setRegistryTimeout(request.getRegistryCenter().getTimeout(), sampler); + Constants.setAddress(request.getRegistryCenter().getAddress(), sampler); + + Constants.setTimeout(request.getConsumerAndService().getTimeout(), sampler); + Constants.setVersion(request.getConsumerAndService().getVersion(), sampler); + Constants.setGroup(request.getConsumerAndService().getGroup(), sampler); + Constants.setConnections(request.getConsumerAndService().getConnections(), sampler); + Constants.setLoadbalance(request.getConsumerAndService().getLoadBalance(), sampler); + Constants.setAsync(request.getConsumerAndService().getAsync(), sampler); + Constants.setCluster(request.getConsumerAndService().getCluster(), sampler); + + Constants.setRpcProtocol(request.getProtocol(), sampler); + Constants.setInterfaceName(request.get_interface(), sampler); + Constants.setMethod(request.getMethod(), sampler); + + List methodArgs = request.getArgs().stream().filter(KeyValue::isValid).filter(KeyValue::isEnable) + .map(keyValue -> new MethodArgument(keyValue.getName(), keyValue.getValue())).collect(Collectors.toList()); + Constants.setMethodArgs(methodArgs, sampler); + + List attachmentArgs = request.getAttachmentArgs().stream().filter(KeyValue::isValid).filter(KeyValue::isEnable) + .map(keyValue -> new MethodArgument(keyValue.getName(), keyValue.getValue())).collect(Collectors.toList()); + Constants.setAttachmentArgs(attachmentArgs, sampler); + + return sampler; + } + + private DNSCacheManager dnsCacheManager(String name, List hosts) { + DNSCacheManager dnsCacheManager = new DNSCacheManager(); + dnsCacheManager.setEnabled(true); + dnsCacheManager.setName(name); + dnsCacheManager.setProperty(TestElement.TEST_CLASS, "DNSCacheManager"); + dnsCacheManager.setProperty(TestElement.GUI_CLASS, "DNSCachePanel"); + dnsCacheManager.setCustomResolver(true); + hosts.forEach(host -> dnsCacheManager.addHost(host.getDomain(), host.getIp())); + + return dnsCacheManager; + } + + private ResponseAssertion responseAssertion(AssertionRegex assertionRegex) { + ResponseAssertion assertion = new ResponseAssertion(); + assertion.setEnabled(true); + assertion.setName(assertionRegex.getDescription()); + assertion.setProperty(TestElement.TEST_CLASS, "ResponseAssertion"); + assertion.setProperty(TestElement.GUI_CLASS, "AssertionGui"); + assertion.setAssumeSuccess(assertionRegex.isAssumeSuccess()); + assertion.setToContainsType(); + switch (assertionRegex.getSubject()) { + case "Response Code": + assertion.setTestFieldResponseCode(); + break; + case "Response Headers": + assertion.setTestFieldResponseHeaders(); + break; + case "Response Data": + assertion.setTestFieldResponseData(); + break; + } + return assertion; + } + + private JSONPathAssertion jsonPathAssertion(AssertionJsonPath assertionJsonPath) { + JSONPathAssertion assertion = new JSONPathAssertion(); + assertion.setEnabled(true); + assertion.setName(assertionJsonPath.getDescription()); + assertion.setProperty(TestElement.TEST_CLASS, "JSONPathAssertion"); + assertion.setProperty(TestElement.GUI_CLASS, "JSONPathAssertionGui"); + assertion.setJsonPath(assertionJsonPath.getExpression()); + assertion.setExpectedValue(assertionJsonPath.getExpect()); + assertion.setJsonValidationBool(true); + assertion.setExpectNull(false); + assertion.setInvert(false); + assertion.setIsRegex(true); + return assertion; + } + + private DurationAssertion durationAssertion(AssertionDuration assertionDuration) { + DurationAssertion assertion = new DurationAssertion(); + assertion.setEnabled(true); + assertion.setName("Response In Time: " + assertionDuration.getValue()); + assertion.setProperty(TestElement.TEST_CLASS, "DurationAssertion"); + assertion.setProperty(TestElement.GUI_CLASS, "DurationAssertionGui"); + assertion.setAllowedDuration(assertionDuration.getValue()); + return assertion; + } + + private JSR223Assertion jsr223Assertion(AssertionJSR223 assertionJSR223) { + JSR223Assertion assertion = new JSR223Assertion(); + assertion.setEnabled(true); + assertion.setName(assertionJSR223.getDesc()); + assertion.setProperty(TestElement.TEST_CLASS, "JSR223Assertion"); + assertion.setProperty(TestElement.GUI_CLASS, "TestBeanGUI"); + assertion.setProperty("cacheKey", "true"); + assertion.setProperty("scriptLanguage", assertionJSR223.getLanguage()); + assertion.setProperty("script", assertionJSR223.getScript()); + return assertion; + } + + private RegexExtractor regexExtractor(ExtractRegex extractRegex) { + RegexExtractor extractor = new RegexExtractor(); + extractor.setEnabled(true); + extractor.setName(extractRegex.getVariable() + " RegexExtractor"); + extractor.setProperty(TestElement.TEST_CLASS, "RegexExtractor"); + extractor.setProperty(TestElement.GUI_CLASS, "RegexExtractorGui"); + extractor.setRefName(extractRegex.getVariable()); + extractor.setRegex(extractRegex.getExpression()); + extractor.setUseField(extractRegex.getUseHeaders()); + if (extractRegex.isMultipleMatching()) { + extractor.setMatchNumber(-1); + } + extractor.setTemplate("$1$"); + return extractor; + } + + private JSONPostProcessor jsonPostProcessor(ExtractJSONPath extractJSONPath) { + JSONPostProcessor extractor = new JSONPostProcessor(); + extractor.setEnabled(true); + extractor.setName(extractJSONPath.getVariable() + " JSONExtractor"); + extractor.setProperty(TestElement.TEST_CLASS, "JSONPostProcessor"); + extractor.setProperty(TestElement.GUI_CLASS, "JSONPostProcessorGui"); + extractor.setRefNames(extractJSONPath.getVariable()); + extractor.setJsonPathExpressions(extractJSONPath.getExpression()); + if (extractJSONPath.isMultipleMatching()) { + extractor.setMatchNumbers("-1"); + } + return extractor; + } + + private XPath2Extractor xPath2Extractor(ExtractXPath extractXPath) { + XPath2Extractor extractor = new XPath2Extractor(); + extractor.setEnabled(true); + extractor.setName(extractXPath.getVariable() + " XPath2Extractor"); + extractor.setProperty(TestElement.TEST_CLASS, "XPath2Extractor"); + extractor.setProperty(TestElement.GUI_CLASS, "XPath2ExtractorGui"); + extractor.setRefName(extractXPath.getVariable()); + extractor.setXPathQuery(extractXPath.getExpression()); + if (extractXPath.isMultipleMatching()) { + extractor.setMatchNumber(-1); + } + return extractor; + } + + private JSR223PreProcessor jsr223PreProcessor(Request request) { + JSR223PreProcessor processor = new JSR223PreProcessor(); + processor.setEnabled(true); + processor.setName(request.getName()); + processor.setProperty(TestElement.TEST_CLASS, "JSR223PreProcessor"); + processor.setProperty(TestElement.GUI_CLASS, "TestBeanGUI"); + processor.setProperty("cacheKey", "true"); + processor.setProperty("scriptLanguage", request.getJsr223PreProcessor().getLanguage()); + processor.setProperty("script", request.getJsr223PreProcessor().getScript()); + return processor; + } + + private JSR223PostProcessor jsr223PostProcessor(Request request) { + JSR223PostProcessor processor = new JSR223PostProcessor(); + processor.setEnabled(true); + processor.setName(request.getName()); + processor.setProperty(TestElement.TEST_CLASS, "JSR223PostProcessor"); + processor.setProperty(TestElement.GUI_CLASS, "TestBeanGUI"); + processor.setProperty("cacheKey", "true"); + processor.setProperty("scriptLanguage", request.getJsr223PostProcessor().getLanguage()); + processor.setProperty("script", request.getJsr223PostProcessor().getScript()); + return processor; + } + + private ConstantTimer constantTimer(Request request) { + ConstantTimer constantTimer = new ConstantTimer(); + constantTimer.setEnabled(true); + constantTimer.setName(request.getTimer().getDelay() + " ms"); + constantTimer.setProperty(TestElement.TEST_CLASS, "ConstantTimer"); + constantTimer.setProperty(TestElement.GUI_CLASS, "ConstantTimerGui"); + constantTimer.setDelay(request.getTimer().getDelay()); + return constantTimer; + } + + private IfController ifController(Request request) { + IfController ifController = new IfController(); + ifController.setEnabled(true); + ifController.setName(request.getController().getLabel()); + ifController.setCondition(request.getController().getCondition()); + ifController.setProperty(TestElement.TEST_CLASS, "IfController"); + ifController.setProperty(TestElement.GUI_CLASS, "IfControllerPanel"); + ifController.setEvaluateAll(false); + ifController.setUseExpression(true); + return ifController; + } +} diff --git a/backend/src/main/java/io/metersphere/api/jmeter/JMeterService.java b/backend/src/main/java/io/metersphere/api/jmeter/JMeterService.java index 220d4c7e25..e978336073 100644 --- a/backend/src/main/java/io/metersphere/api/jmeter/JMeterService.java +++ b/backend/src/main/java/io/metersphere/api/jmeter/JMeterService.java @@ -1,5 +1,6 @@ package io.metersphere.api.jmeter; +import io.metersphere.api.dto.scenario.Scenario; import io.metersphere.commons.constants.ApiRunMode; import io.metersphere.commons.exception.MSException; import io.metersphere.commons.utils.LogUtil; @@ -14,28 +15,38 @@ import org.apache.jorphan.collections.HashTree; import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.stereotype.Service; -import javax.annotation.Resource; +import java.io.ByteArrayOutputStream; import java.io.File; -import java.io.InputStream; -import java.lang.reflect.Field; +import java.util.List; + +import javax.annotation.PostConstruct; +import javax.annotation.Resource; @Service public class JMeterService { @Resource private JmeterProperties jmeterProperties; + @Resource + private JMXGenerator jmxGenerator; - public void run(String testId, String debugReportId, InputStream is) { + @PostConstruct + public void init() { String JMETER_HOME = getJmeterHome(); String JMETER_PROPERTIES = JMETER_HOME + "/bin/jmeter.properties"; JMeterUtils.loadJMeterProperties(JMETER_PROPERTIES); JMeterUtils.setJMeterHome(JMETER_HOME); JMeterUtils.setLocale(LocaleContextHolder.getLocale()); + } + public HashTree getHashTree(String testId, String testName, List scenarios) { + return jmxGenerator.parse(testId, testName, scenarios); + } + + public void run(String testId, String testName, List scenarios, String debugReportId) { try { - Object scriptWrapper = SaveService.loadElement(is); - HashTree testPlan = getHashTree(scriptWrapper); + HashTree testPlan = getHashTree(testId, testName, scenarios); JMeterVars.addJSR223PostProcessor(testPlan); addBackendListener(testId, debugReportId, testPlan); LocalRunner runner = new LocalRunner(testPlan); @@ -46,6 +57,17 @@ public class JMeterService { } } + public String getJMX(HashTree hashTree) { + try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { + SaveService.saveTree(hashTree, baos); + LogUtil.debug(baos.toString()); + return baos.toString(); + } catch (Exception e) { + LogUtil.warn("HashTree error, can't log jmx content"); + } + return null; + } + public String getJmeterHome() { String home = getClass().getResource("/").getPath() + "jmeter"; try { @@ -60,12 +82,6 @@ public class JMeterService { } } - private HashTree getHashTree(Object scriptWrapper) throws Exception { - Field field = scriptWrapper.getClass().getDeclaredField("testPlan"); - field.setAccessible(true); - return (HashTree) field.get(scriptWrapper); - } - private void addBackendListener(String testId, String debugReportId, HashTree testPlan) { BackendListener backendListener = new BackendListener(); backendListener.setName(testId); 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 cc9238c205..f092212e0a 100644 --- a/backend/src/main/java/io/metersphere/api/service/APITestService.java +++ b/backend/src/main/java/io/metersphere/api/service/APITestService.java @@ -8,7 +8,6 @@ import io.metersphere.api.dto.scenario.request.dubbo.RegistryCenter; import io.metersphere.api.jmeter.JMeterService; import io.metersphere.api.parse.ApiImportParser; import io.metersphere.api.parse.ApiImportParserFactory; -import io.metersphere.api.parse.JmeterDocumentParser; import io.metersphere.base.domain.*; import io.metersphere.base.mapper.ApiTestFileMapper; import io.metersphere.base.mapper.ApiTestMapper; @@ -23,9 +22,9 @@ 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.*; +import io.metersphere.service.FileService; +import io.metersphere.service.QuotaService; +import io.metersphere.service.ScheduleService; import io.metersphere.track.service.TestCaseService; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.constants.CommonConstants; @@ -35,16 +34,15 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; import org.springframework.web.multipart.MultipartFile; -import javax.annotation.Resource; import java.io.*; import java.util.*; import java.util.stream.Collectors; +import javax.annotation.Resource; + @Service @Transactional(rollbackFor = Exception.class) public class APITestService { - @Resource - private UserService userService; @Resource private ApiTestMapper apiTestMapper; @Resource @@ -61,10 +59,6 @@ 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"; @@ -82,33 +76,19 @@ public class APITestService { return extApiTestMapper.listByIds(request.getIds()); } - public void create(SaveAPITestRequest request, MultipartFile file, List bodyFiles) { + public void create(SaveAPITestRequest request, List bodyFiles) { List bodyUploadIds = new ArrayList<>(request.getBodyUploadIds()); - ApiTest test = createTest(request, file); + ApiTest test = createTest(request); createBodyFiles(test, bodyUploadIds, bodyFiles); } - private ApiTest createTest(SaveAPITestRequest request, MultipartFile file) { - if (file == null) { - throw new IllegalArgumentException(Translator.get("file_cannot_be_null")); - } - checkQuota(); - request.setBodyUploadIds(null); - ApiTest test = createTest(request); - saveFile(test.getId(), file); - return test; - } - public void update(SaveAPITestRequest request, MultipartFile file, List bodyFiles) { - if (file == null) { - throw new IllegalArgumentException(Translator.get("file_cannot_be_null")); - } + public void update(SaveAPITestRequest request, List bodyFiles) { deleteFileByTestId(request.getId()); List bodyUploadIds = new ArrayList<>(request.getBodyUploadIds()); request.setBodyUploadIds(null); ApiTest test = updateTest(request); createBodyFiles(test, bodyUploadIds, bodyFiles); - saveFile(test.getId(), file); } private void createBodyFiles(ApiTest test, List bodyUploadIds, List bodyFiles) { @@ -151,14 +131,6 @@ public class APITestService { copy.setStatus(APITestStatus.Saved.name()); copy.setUserId(Objects.requireNonNull(SessionUtils.getUser()).getId()); apiTestMapper.insert(copy); - // copy test file - ApiTestFile apiTestFile = getFileByTestId(request.getId()); - if (apiTestFile != null) { - FileMetadata fileMetadata = fileService.copyFile(apiTestFile.getFileId()); - apiTestFile.setTestId(copy.getId()); - apiTestFile.setFileId(fileMetadata.getId()); - apiTestFileMapper.insert(apiTestFile); - } copyBodyFiles(copy.getId(), request.getId()); } @@ -211,26 +183,13 @@ public class APITestService { } public String run(SaveAPITestRequest request) { - ApiTestFile file = getFileByTestId(request.getId()); - if (file == null) { - MSException.throwException(Translator.get("file_cannot_be_null")); - } - byte[] bytes = fileService.loadFileAsBytes(file.getFileId()); - // 解析 xml 处理 mock 数据 - bytes = JmeterDocumentParser.parse(bytes); - InputStream is = new ByteArrayInputStream(bytes); - APITestResult apiTest = get(request.getId()); if (SessionUtils.getUser() == null) { 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); + jMeterService.run(request.getId(), request.getName(), request.getScenarioDefinition(), null); return reportId; } @@ -271,6 +230,8 @@ public class APITestService { } private ApiTest createTest(SaveAPITestRequest request) { + checkQuota(); + request.setBodyUploadIds(null); checkNameExist(request); final ApiTest test = new ApiTest(); test.setId(request.getId()); @@ -285,14 +246,6 @@ public class APITestService { return test; } - private void saveFile(String testId, MultipartFile file) { - final FileMetadata fileMetadata = fileService.saveFile(file); - ApiTestFile apiTestFile = new ApiTestFile(); - apiTestFile.setTestId(testId); - apiTestFile.setFileId(fileMetadata.getId()); - apiTestFileMapper.insert(apiTestFile); - } - private void deleteFileByTestId(String testId) { ApiTestFileExample ApiTestFileExample = new ApiTestFileExample(); ApiTestFileExample.createCriteria().andTestIdEqualTo(testId); @@ -305,18 +258,6 @@ public class APITestService { } } - private ApiTestFile getFileByTestId(String testId) { - ApiTestFileExample ApiTestFileExample = new ApiTestFileExample(); - ApiTestFileExample.createCriteria().andTestIdEqualTo(testId); - final List ApiTestFiles = apiTestFileMapper.selectByExample(ApiTestFileExample); - apiTestFileMapper.selectByExample(ApiTestFileExample); - if (!CollectionUtils.isEmpty(ApiTestFiles)) { - return ApiTestFiles.get(0); - } else { - return null; - } - } - public void updateSchedule(Schedule request) { scheduleService.editSchedule(request); addOrUpdateApiTestCronJob(request); @@ -409,10 +350,7 @@ public class APITestService { return schedules; } - public String runDebug(SaveAPITestRequest request, MultipartFile file, List bodyFiles) { - if (file == null) { - throw new IllegalArgumentException(Translator.get("file_cannot_be_null")); - } + public String runDebug(SaveAPITestRequest request, List bodyFiles) { updateTest(request); APITestResult apiTest = get(request.getId()); List bodyUploadIds = new ArrayList<>(request.getBodyUploadIds()); @@ -423,17 +361,7 @@ public class APITestService { } String reportId = apiReportService.createDebugReport(apiTest); - InputStream is = null; - try { - byte[] bytes = file.getBytes(); - // 解析 xml 处理 mock 数据 - bytes = JmeterDocumentParser.parse(bytes); - is = new ByteArrayInputStream(bytes); - } catch (IOException e) { - LogUtil.error(e); - } - - jMeterService.run(request.getId(), reportId, is); + jMeterService.run(request.getId(), request.getName(), request.getScenarioDefinition(), reportId); return reportId; } @@ -444,10 +372,12 @@ public class APITestService { } } - public void mergeCreate(SaveAPITestRequest request, MultipartFile file, List selectIds) { - ApiTest test = createTest(request, file); - selectIds.forEach(sourceId -> { - copyBodyFiles(test.getId(), sourceId); - }); + public void mergeCreate(SaveAPITestRequest request, List selectIds) { + ApiTest test = createTest(request); + selectIds.forEach(sourceId -> copyBodyFiles(test.getId(), sourceId)); + } + + public String getJMX(SaveAPITestRequest request) { + return jMeterService.getJMX(jMeterService.getHashTree(request.getId(), request.getName(), request.getScenarioDefinition())); } } diff --git a/frontend/src/business/components/api/test/ApiTestConfig.vue b/frontend/src/business/components/api/test/ApiTestConfig.vue index b53019010a..a18b183f7a 100644 --- a/frontend/src/business/components/api/test/ApiTestConfig.vue +++ b/frontend/src/business/components/api/test/ApiTestConfig.vue @@ -27,10 +27,6 @@ {{ $t('load_test.save_and_run') }} - - - - {{ $t('commons.cancel') }} @@ -76,353 +72,363 @@ diff --git a/frontend/src/business/components/api/test/OneClickOperation.vue b/frontend/src/business/components/api/test/OneClickOperation.vue index d2f5ecf3f8..c97edf8fed 100644 --- a/frontend/src/business/components/api/test/OneClickOperation.vue +++ b/frontend/src/business/components/api/test/OneClickOperation.vue @@ -26,7 +26,6 @@ import MsApiScenarioConfig from "./components/ApiScenarioConfig"; import MsApiReportStatus from "../report/ApiReportStatus"; import MsApiReportDialog from "./ApiReportDialog"; - import {getUUID} from "@/common/js/utils"; import {parseEnvironment} from "./model/EnvironmentModel"; @@ -181,9 +180,6 @@ type: "application/json" })); - let jmx = this.test.toJMX(); - let blob = new Blob([jmx.xml], {type: "application/octet-stream"}); - formData.append("file", new File([blob], jmx.name)); return { method: 'POST', url: url, diff --git a/frontend/src/business/components/api/test/model/ScenarioModel.js b/frontend/src/business/components/api/test/model/ScenarioModel.js index c37693a87f..4d3f1df135 100644 --- a/frontend/src/business/components/api/test/model/ScenarioModel.js +++ b/frontend/src/business/components/api/test/model/ScenarioModel.js @@ -201,12 +201,12 @@ export class Test extends BaseConfig { return {isValid: true}; } - toJMX() { - return { - name: this.name + '.jmx', - xml: new JMXGenerator(this).toXML() - }; - } + // toJMX() { + // return { + // name: this.name + '.jmx', + // xml: new JMXGenerator(this).toXML() + // }; + // } } export class Scenario extends BaseConfig { @@ -973,7 +973,7 @@ export class ConstantTimer extends Timer { } } -/** ------------------------------------------------------------------------ **/ +/** ------------------------------------------------------------------------ const JMX_ASSERTION_CONDITION = { MATCH: 1, CONTAINS: 1 << 1, @@ -1188,7 +1188,7 @@ class JMXGenerator { envArray = JSON.parse(environments); } envArray.forEach(item => { - if (item.enable != false && item.name && !keys.has(item.name)) { + if (item.enable !== false && item.name && !keys.has(item.name)) { target.push(new KeyValue({name: item.name, value: item.value})); } }) @@ -1373,8 +1373,7 @@ class JMXGenerator { let hasContentType = false; for (let index in request.headers) { if (request.headers.hasOwnProperty(index)) { - if (request.headers[index].name === 'Content-Type' && request.headers[index].enable != false) { - request.headers.splice(index, 1); + if (request.headers[index].name === 'Content-Type' && request.headers[index].enable !== false) { hasContentType = true; break; } @@ -1462,7 +1461,7 @@ class JMXGenerator { getResponseAssertion(regex) { let name = regex.description; - let type = JMX_ASSERTION_CONDITION.CONTAINS; // 固定用Match,自己写正则 + let type = JMX_ASSERTION_CONDITION.CONTAINS; let value = regex.expression; let assumeSuccess = regex.assumeSuccess; switch (regex.subject) { @@ -1539,4 +1538,4 @@ class JMXGenerator { } } - + **/