Merge remote-tracking branch 'remotes/origin/jmx-convert' into local-api-delimit

# Conflicts:
#	backend/src/main/java/io/metersphere/api/dto/scenario/KeyValue.java
#	backend/src/main/java/io/metersphere/api/dto/scenario/request/HttpRequest.java
#	frontend/src/business/components/api/test/ApiTestConfig.vue
#	frontend/src/business/components/api/test/model/ScenarioModel.js
This commit is contained in:
fit2-zhao 2020-11-13 10:43:37 +08:00
commit fccabe6311
30 changed files with 1396 additions and 594 deletions

View File

@ -19,7 +19,7 @@
<java.version>1.8</java.version>
<jmeter.version>5.2.1</jmeter.version>
<nacos.version>1.1.3</nacos.version>
<dubbo.version>2.7.7</dubbo.version>
<dubbo.version>2.7.8</dubbo.version>
<graalvm.version>20.1.0</graalvm.version>
</properties>

View File

@ -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<MultipartFile> bodyFiles) {
apiTestService.create(request, file, bodyFiles);
public void create(@RequestPart("request") SaveAPITestRequest request, @RequestPart(value = "files") List<MultipartFile> 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<String> selectIds) {
apiTestService.mergeCreate(request, file, selectIds);
public void mergeCreate(@RequestPart("request") SaveAPITestRequest request, @RequestPart(value = "selectIds") List<String> 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<MultipartFile> bodyFiles) {
public void update(@RequestPart("request") SaveAPITestRequest request, @RequestPart(value = "files") List<MultipartFile> bodyFiles) {
checkownerService.checkApiTestOwner(request.getId());
apiTestService.update(request, file, bodyFiles);
apiTestService.update(request, bodyFiles);
}
@PostMapping(value = "/copy")
@ -101,21 +101,25 @@ public class APITestController {
return apiTestService.get(testId);
}
@PostMapping("/delete")
public void delete(@RequestBody DeleteAPITestRequest request) {
checkownerService.checkApiTestOwner(request.getId());
apiTestService.delete(request);
}
@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<MultipartFile> bodyFiles) {
return apiTestService.runDebug(request, file, bodyFiles);
public String runDebug(@RequestPart("request") SaveAPITestRequest request, @RequestPart(value = "files") List<MultipartFile> bodyFiles) {
return apiTestService.runDebug(request, bodyFiles);
}
@PostMapping(value = "/checkName")
@ -136,7 +140,7 @@ public class APITestController {
@PostMapping("/list/schedule/{goPage}/{pageSize}")
public List<ScheduleDao> listSchedule(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody QueryScheduleRequest request) {
Page<Object> page = PageHelper.startPage(goPage, pageSize, true);
PageHelper.startPage(goPage, pageSize, true);
return apiTestService.listSchedule(request);
}

View File

@ -1,6 +1,7 @@
package io.metersphere.api.dto.scenario;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import java.util.List;
@ -12,4 +13,20 @@ public class Body {
private Object json;
private String xml;
private List<KeyValue> 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);
}
}

View File

@ -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<KeyValue> headers;
}

View File

@ -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;
private boolean required;
public KeyValue() {
@ -35,4 +37,12 @@ public class KeyValue {
this.description = description;
this.required = true;
}
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");
}
}

View File

@ -12,7 +12,7 @@ public class Scenario {
private String name;
private String url;
private String environmentId;
private Boolean enableCookieShare;
private boolean enableCookieShare;
private List<KeyValue> variables;
private List<KeyValue> headers;
private List<Request> requests;
@ -20,5 +20,5 @@ public class Scenario {
private DubboConfig dubboConfig;
private TCPConfig tcpConfig;
private List<DatabaseConfig> databaseConfigs;
private Boolean enable;
private boolean enable = true;
}

View File

@ -4,16 +4,16 @@ import lombok.Data;
@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 soLinger;
private String eolByte;
private String username;
private String password;
private String classname = "";
private String server = "";
private String port = "";
private String ctimeout = "";
private String timeout = "";
private boolean reUseConnection = true;
private boolean nodelay;
private boolean closeConnection;
private String soLinger = "";
private String eolByte = "";
private String username = "";
private String password = "";
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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 + ")}";
}
}

View File

@ -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<KeyValue> variables;
private boolean enableHost;
private List<Host> hosts;
}

View File

@ -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<DatabaseConfig> databaseConfigs;
private TCPConfig tcpConfig;
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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<KeyValue> args;
@JSONField(ordinal = 9)
@JSONField(ordinal = 59)
private List<KeyValue> attachmentArgs;
}

View File

@ -16,30 +16,28 @@ 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<KeyValue> parameters;
@JSONField(ordinal = 7)
@JSONField(ordinal = 54)
private List<KeyValue> 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 = 18)
@JSONField(ordinal = 56)
private String connectTimeout;
@JSONField(ordinal = 57)
private String responseTimeout;
@JSONField(ordinal = 58)
private boolean followRedirects;
@JSONField(ordinal = 59)
private boolean doMultipartPost;
@JSONField(ordinal = 60)
private List<KeyValue> rest;
@JSONField(ordinal = 19)
@JSONField(ordinal = 61)
private AuthConfig authConfig;
}

View File

@ -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;
}

View File

@ -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<KeyValue> variables;
}

View File

@ -11,32 +11,30 @@ 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;
private String classname = "";
@JSONField(ordinal = 52)
private String server;
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;
private String soLinger = "";
@JSONField(ordinal = 60)
private String eolByte;
private String eolByte = "";
@JSONField(ordinal = 61)
private String request;
private String request = "";
@JSONField(ordinal = 62)
private String username;
private String username = "";
@JSONField(ordinal = 63)
private String password;
private String password = "";
}

View File

@ -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;
}

View File

@ -42,14 +42,13 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl
private final List<SampleResult> 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<String> 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<String> userIds = new ArrayList<>();
List<MessageDetail> taskList = new ArrayList<>();

View File

@ -0,0 +1,771 @@
package io.metersphere.api.jmeter;
import com.alibaba.fastjson.JSONObject;
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.dto.scenario.request.dubbo.ConfigCenter;
import io.metersphere.api.dto.scenario.request.dubbo.ConsumerAndService;
import io.metersphere.api.dto.scenario.request.dubbo.RegistryCenter;
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.save.SaveService;
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<Scenario> 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<String, String> databaseConfigMap = new HashMap<>();
if (CollectionUtils.isNotEmpty(scenario.getDatabaseConfigs())) {
scenario.getDatabaseConfigs().forEach(databaseConfig -> {
threadGroupHashTree.add(jdbcDataSource(databaseConfig));
databaseConfigMap.put(databaseConfig.getId(), databaseConfig.getName());
});
}
// DUBBO Config
threadGroupHashTree.add(dubboConfig(scenario.getName() + "DUBBO Config", scenario.getDubboConfig()));
// 场景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<KeyValue> 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<KeyValue> headers, Body body) {
if (!body.isKV() && StringUtils.isNotBlank(body.getFormat())) {
Map<String, String> 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<Host> 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.class.getName());
testPlan.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("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.class.getName());
loopController.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("LoopControlPanel"));
loopController.setEnabled(true);
loopController.setLoops(1);
ThreadGroup threadGroup = new ThreadGroup();
threadGroup.setEnabled(true);
threadGroup.setName(name);
threadGroup.setProperty(TestElement.TEST_CLASS, ThreadGroup.class.getName());
threadGroup.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("ThreadGroupGui"));
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.setSamplerController(loopController);
return threadGroup;
}
private Arguments arguments(String name, List<KeyValue> variables) {
Arguments arguments = new Arguments();
arguments.setEnabled(true);
arguments.setName(name);
arguments.setProperty(TestElement.TEST_CLASS, Arguments.class.getName());
arguments.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("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<KeyValue> headers) {
HeaderManager headerManager = new HeaderManager();
headerManager.setEnabled(true);
headerManager.setName(name);
headerManager.setProperty(TestElement.TEST_CLASS, HeaderManager.class.getName());
headerManager.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("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.class.getName());
cookieManager.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("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, DataSourceElement.class.getName());
dataSourceElement.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("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 dubboConfig(String name, DubboConfig dubboConfig) {
ConfigTestElement configTestElement = new ConfigTestElement();
configTestElement.setEnabled(true);
configTestElement.setName(name);
configTestElement.setProperty(TestElement.TEST_CLASS, ConfigTestElement.class.getName());
configTestElement.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("DubboDefaultConfigGui"));
configTestElement.addConfigElement(configCenter(dubboConfig.getConfigCenter()));
configTestElement.addConfigElement(registryCenter(dubboConfig.getRegistryCenter()));
configTestElement.addConfigElement(consumerAndService(dubboConfig.getConsumerAndService()));
return configTestElement;
}
private ConfigTestElement configCenter(ConfigCenter configCenter) {
ConfigTestElement configTestElement = new ConfigTestElement();
Constants.setConfigCenterProtocol(configCenter.getProtocol(), configTestElement);
Constants.setConfigCenterGroup(configCenter.getGroup(), configTestElement);
Constants.setConfigCenterNamespace(configCenter.getNamespace(), configTestElement);
Constants.setConfigCenterUserName(configCenter.getUsername(), configTestElement);
Constants.setConfigCenterPassword(configCenter.getPassword(), configTestElement);
Constants.setConfigCenterAddress(configCenter.getAddress(), configTestElement);
Constants.setConfigCenterTimeout(configCenter.getTimeout(), configTestElement);
return configTestElement;
}
private ConfigTestElement registryCenter(RegistryCenter registryCenter) {
ConfigTestElement configTestElement = new ConfigTestElement();
Constants.setRegistryProtocol(registryCenter.getProtocol(), configTestElement);
Constants.setRegistryGroup(registryCenter.getGroup(), configTestElement);
Constants.setRegistryUserName(registryCenter.getUsername(), configTestElement);
Constants.setRegistryPassword(registryCenter.getPassword(), configTestElement);
Constants.setRegistryTimeout(registryCenter.getTimeout(), configTestElement);
Constants.setAddress(registryCenter.getAddress(), configTestElement);
return configTestElement;
}
private ConfigTestElement consumerAndService(ConsumerAndService consumerAndService) {
ConfigTestElement configTestElement = new ConfigTestElement();
Constants.setTimeout(consumerAndService.getTimeout(), configTestElement);
Constants.setVersion(consumerAndService.getVersion(), configTestElement);
Constants.setGroup(consumerAndService.getGroup(), configTestElement);
Constants.setConnections(consumerAndService.getConnections(), configTestElement);
Constants.setLoadbalance(consumerAndService.getLoadBalance(), configTestElement);
Constants.setAsync(consumerAndService.getAsync(), configTestElement);
Constants.setCluster(consumerAndService.getCluster(), configTestElement);
return configTestElement;
}
private ConfigTestElement tcpConfig(String name, TCPConfig tcpConfig) {
ConfigTestElement configTestElement = new ConfigTestElement();
configTestElement.setEnabled(true);
configTestElement.setName(name);
configTestElement.setProperty(TestElement.TEST_CLASS, ConfigTestElement.class.getName());
configTestElement.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("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.class.getName());
sampler.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("HttpTestSampleGui"));
sampler.setMethod(request.getMethod());
sampler.setContentEncoding("UTF-8");
sampler.setConnectTimeout(request.getConnectTimeout());
sampler.setResponseTimeout(request.getResponseTimeout());
sampler.setFollowRedirects(request.isFollowRedirects());
sampler.setUseKeepAlive(true);
sampler.setDoMultipart(request.isDoMultipartPost());
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);
}
// 请求参数
if (CollectionUtils.isNotEmpty(request.getParameters())) {
sampler.setArguments(httpArguments(request.getParameters()));
}
// 请求体
if (!StringUtils.equals(request.getMethod(), "GET")) {
List<KeyValue> body = new ArrayList<>();
if (request.getBody().isKV()) {
body = request.getBody().getKvs().stream().filter(KeyValue::isValid).collect(Collectors.toList());
sampler.setHTTPFiles(httpFileArgs(request, testId));
} else {
if (StringUtils.isNotBlank(request.getBody().getRaw())) {
sampler.setPostBodyRaw(true);
KeyValue keyValue = new KeyValue("", request.getBody().getRaw());
keyValue.setEnable(true);
keyValue.setEncode(false);
body.add(keyValue);
}
}
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<KeyValue> 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);
if (StringUtils.isNotBlank(keyValue.getContentType())) {
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<HTTPFileArg> 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<KeyValue> merge(List<KeyValue> list1, List<KeyValue> list2) {
Set<String> names = list1.stream().map(KeyValue::getName).collect(Collectors.toSet());
List<KeyValue> 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.class.getName());
tcpSampler.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("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<String, String> databaseConfigMap) {
JDBCSampler sampler = new JDBCSampler();
sampler.setName(request.getName());
sampler.setProperty(TestElement.TEST_CLASS, JDBCSampler.class.getName());
sampler.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("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, SaveService.aliasToClass("DubboSampleGui"));
sampler.addTestElement(configCenter(request.getConfigCenter()));
sampler.addTestElement(registryCenter(request.getRegistryCenter()));
sampler.addTestElement(consumerAndService(request.getConsumerAndService()));
Constants.setRpcProtocol(request.getProtocol(), sampler);
Constants.setInterfaceName(request.get_interface(), sampler);
Constants.setMethod(request.getMethod(), sampler);
List<MethodArgument> 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<MethodArgument> 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<Host> hosts) {
DNSCacheManager dnsCacheManager = new DNSCacheManager();
dnsCacheManager.setEnabled(true);
dnsCacheManager.setName(name);
dnsCacheManager.setProperty(TestElement.TEST_CLASS, DNSCacheManager.class.getName());
dnsCacheManager.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("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.class.getName());
assertion.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("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.class.getName());
assertion.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("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.class.getName());
assertion.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("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.class.getName());
assertion.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("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.class.getName());
extractor.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("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.class.getName());
extractor.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("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.class.getName());
extractor.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("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.class.getName());
processor.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("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.class.getName());
processor.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("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.class.getName());
constantTimer.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("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.class.getName());
ifController.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("IfControllerPanel"));
ifController.setEvaluateAll(false);
ifController.setUseExpression(true);
return ifController;
}
}

View File

@ -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,39 @@ 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<Scenario> scenarios) {
return jmxGenerator.parse(testId, testName, scenarios);
}
public void run(String testId, String testName, List<Scenario> scenarios, String debugReportId) {
try {
Object scriptWrapper = SaveService.loadElement(is);
HashTree testPlan = getHashTree(scriptWrapper);
init();
HashTree testPlan = getHashTree(testId, testName, scenarios);
JMeterVars.addJSR223PostProcessor(testPlan);
addBackendListener(testId, debugReportId, testPlan);
LocalRunner runner = new LocalRunner(testPlan);
@ -46,6 +58,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 +83,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);

View File

@ -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<MultipartFile> bodyFiles) {
public void create(SaveAPITestRequest request, List<MultipartFile> bodyFiles) {
List<String> 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<MultipartFile> bodyFiles) {
if (file == null) {
throw new IllegalArgumentException(Translator.get("file_cannot_be_null"));
}
public void update(SaveAPITestRequest request, List<MultipartFile> bodyFiles) {
deleteFileByTestId(request.getId());
List<String> 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<String> bodyUploadIds, List<MultipartFile> 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());
}
@ -214,26 +186,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> 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;
}
@ -274,6 +233,8 @@ public class APITestService {
}
private ApiTest createTest(SaveAPITestRequest request) {
checkQuota();
request.setBodyUploadIds(null);
checkNameExist(request);
final ApiTest test = new ApiTest();
test.setId(request.getId());
@ -288,14 +249,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);
@ -308,18 +261,6 @@ public class APITestService {
}
}
private ApiTestFile getFileByTestId(String testId) {
ApiTestFileExample ApiTestFileExample = new ApiTestFileExample();
ApiTestFileExample.createCriteria().andTestIdEqualTo(testId);
final List<ApiTestFile> 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);
@ -412,10 +353,7 @@ public class APITestService {
return schedules;
}
public String runDebug(SaveAPITestRequest request, MultipartFile file, List<MultipartFile> bodyFiles) {
if (file == null) {
throw new IllegalArgumentException(Translator.get("file_cannot_be_null"));
}
public String runDebug(SaveAPITestRequest request, List<MultipartFile> bodyFiles) {
updateTest(request);
APITestResult apiTest = get(request.getId());
List<String> bodyUploadIds = new ArrayList<>(request.getBodyUploadIds());
@ -426,17 +364,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;
}
@ -447,10 +375,12 @@ public class APITestService {
}
}
public void mergeCreate(SaveAPITestRequest request, MultipartFile file, List<String> selectIds) {
ApiTest test = createTest(request, file);
selectIds.forEach(sourceId -> {
copyBodyFiles(test.getId(), sourceId);
});
public void mergeCreate(SaveAPITestRequest request, List<String> 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()));
}
}

View File

@ -1,7 +1,6 @@
#---------------------------------------------------------
# SAVESERVICE PROPERTIES - JMETER INTERNAL USE ONLY
#---------------------------------------------------------
## Licensed to the Apache Software Foundation (ASF) under one or more
## contributor license agreements. See the NOTICE file distributed with
## this work for additional information regarding copyright ownership.
@ -16,16 +15,11 @@
## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
## See the License for the specific language governing permissions and
## limitations under the License.
# This file is used to define how XStream (de-)serializes classnames
# in JMX test plan files.
# FOR JMETER INTERNAL USE ONLY
#---------------------------------------------------------
# N.B. To ensure backward compatibility, please do NOT change or delete any entries
# New entries can be added as necessary.
#
# Note that keys starting with an underscore are special,
@ -132,6 +126,9 @@ DebugSampler=org.apache.jmeter.sampler.DebugSampler
DistributionGraphVisualizer=org.apache.jmeter.visualizers.DistributionGraphVisualizer
DNSCacheManager=org.apache.jmeter.protocol.http.control.DNSCacheManager
DNSCachePanel=org.apache.jmeter.protocol.http.gui.DNSCachePanel
DubboSample=io.github.ningyu.jmeter.plugin.dubbo.sample.DubboSample
DubboSampleGui=io.github.ningyu.jmeter.plugin.dubbo.gui.DubboSampleGui
DubboDefaultConfigGui=io.github.ningyu.jmeter.plugin.dubbo.gui.DubboDefaultConfigGui
DurationAssertion=org.apache.jmeter.assertions.DurationAssertion
DurationAssertionGui=org.apache.jmeter.assertions.gui.DurationAssertionGui
PreciseThroughputTimer=org.apache.jmeter.timers.poissonarrivals.PreciseThroughputTimer
@ -242,10 +239,8 @@ ModuleController=org.apache.jmeter.control.ModuleController
ModuleControllerGui=org.apache.jmeter.control.gui.ModuleControllerGui
MongoScriptSampler=org.apache.jmeter.protocol.mongodb.sampler.MongoScriptSampler
MongoSourceElement=org.apache.jmeter.protocol.mongodb.config.MongoSourceElement
# removed in 3.2, class was deleted in r
MonitorHealthVisualizer=org.apache.jmeter.visualizers.MonitorHealthVisualizer
NamePanel=org.apache.jmeter.gui.NamePanel
BoltSampler=org.apache.jmeter.protocol.bolt.sampler.BoltSampler
BoltConnectionElement=org.apache.jmeter.protocol.bolt.config.BoltConnectionElement
@ -301,12 +296,10 @@ SMIMEAssertion=org.apache.jmeter.assertions.SMIMEAssertionTestElement
SMIMEAssertionGui=org.apache.jmeter.assertions.gui.SMIMEAssertionGui
SmtpSampler=org.apache.jmeter.protocol.smtp.sampler.SmtpSampler
SmtpSamplerGui=org.apache.jmeter.protocol.smtp.sampler.gui.SmtpSamplerGui
# removed in 3.2, class was deleted in r
SoapSampler=org.apache.jmeter.protocol.http.sampler.SoapSampler
# removed in 3.2, class was deleted in r
SoapSamplerGui=org.apache.jmeter.protocol.http.control.gui.SoapSamplerGui
# removed in 3.1, class was deleted in r1763837
SplineVisualizer=org.apache.jmeter.visualizers.SplineVisualizer
# Originally deleted in r397955 as class is obsolete; needed for compat.
@ -375,7 +368,6 @@ XPathExtractor=org.apache.jmeter.extractor.XPathExtractor
XPathExtractorGui=org.apache.jmeter.extractor.gui.XPathExtractorGui
XPath2Extractor=org.apache.jmeter.extractor.XPath2Extractor
XPath2ExtractorGui=org.apache.jmeter.extractor.gui.XPath2ExtractorGui
# Properties - all start with lower case letter and end with Prop
#
boolProp=org.apache.jmeter.testelement.property.BooleanProperty
@ -400,7 +392,6 @@ httpSample=org.apache.jmeter.protocol.http.sampler.HTTPSampleResult
statSample=org.apache.jmeter.samplers.StatisticalSampleResult
testResults=org.apache.jmeter.save.TestResultWrapper
assertionResult=org.apache.jmeter.assertions.AssertionResult
# removed in 3.2, class was deleted in r
monitorStats=org.apache.jmeter.visualizers.MonitorStats
sampleEvent=org.apache.jmeter.samplers.SampleEvent

View File

@ -27,10 +27,6 @@
{{ $t('load_test.save_and_run') }}
</el-button>
<!-- <el-button :disabled="isReadOnly" type="primary" plain v-if="isShowRun" @click="runTest">-->
<!-- {{$t('api_test.run')}}-->
<!-- </el-button>-->
<el-button :disabled="isReadOnly" type="warning" plain @click="cancel">{{ $t('commons.cancel') }}
</el-button>
@ -99,331 +95,339 @@ export default {
props: ["id"],
data() {
return {
reportVisible: false,
create: false,
result: {},
projects: [],
change: false,
test: new Test(),
isReadOnly: false,
debugReportId: ''
}
},
data() {
return {
reportVisible: false,
create: false,
result: {},
projects: [],
change: false,
test: new Test(),
isReadOnly: false,
debugReportId: ''
}
},
watch: {
'$route': 'init',
test: {
handler: function () {
this.change = true;
},
deep: true
}
},
methods: {
init() {
let projectId;
this.isReadOnly = !checkoutTestManagerOrTestUser();
if (this.id) {
this.create = false;
this.getTest(this.id);
} else {
this.create = true;
this.test = new Test();
if (this.$refs.config) {
this.$refs.config.reset();
}
//
projectId = this.$store.state.common.projectId;
}
this.result = this.$get("/project/listAll", response => {
this.projects = response.data;
//
if (projectId) this.test.projectId = projectId;
})
watch: {
'$route': 'init',
test: {
handler: function () {
this.change = true;
},
updateReference() {
let updateIds = [];
deep: true
}
},
methods: {
init() {
let projectId;
this.isReadOnly = !checkoutTestManagerOrTestUser();
if (this.id) {
this.create = false;
this.getTest(this.id);
} else {
this.create = true;
this.test = new Test();
if (this.$refs.config) {
this.$refs.config.reset();
}
//
projectId = this.$store.state.common.projectId;
}
this.result = this.$get("/project/listAll", response => {
this.projects = response.data;
//
if (projectId) this.test.projectId = projectId;
})
},
updateReference() {
let updateIds = [];
this.test.scenarioDefinition.forEach(scenario => {
if (scenario.isReference()) {
updateIds.push(scenario.id.split("#")[0]);
}
})
if (updateIds.length === 0) return;
//
this.result = this.$post("/api/list/ids", {ids: updateIds}, response => {
let scenarioMap = {};
if (response.data) {
response.data.forEach(test => {
JSON.parse(test.scenarioDefinition).forEach(options => {
let referenceId = test.id + "#" + options.id;
scenarioMap[referenceId] = new Scenario(options);
scenarioMap[referenceId].id = referenceId;
})
})
}
let scenarios = [];
this.test.scenarioDefinition.forEach(scenario => {
if (scenario.isReference()) {
updateIds.push(scenario.id.split("#")[0]);
if (scenarioMap[scenario.id]) scenarios.push(scenarioMap[scenario.id]);
} else {
scenarios.push(scenario);
}
})
this.test.scenarioDefinition = scenarios;
})
},
getTest(id) {
this.result = this.$get("/api/get/" + id, response => {
if (response.data) {
let item = response.data;
if (updateIds.length === 0) return;
//
this.result = this.$post("/api/list/ids", {ids: updateIds}, response => {
let scenarioMap = {};
if (response.data) {
response.data.forEach(test => {
JSON.parse(test.scenarioDefinition).forEach(options => {
let referenceId = test.id + "#" + options.id;
scenarioMap[referenceId] = new Scenario(options);
scenarioMap[referenceId].id = referenceId;
})
})
}
this.test = new Test({
id: item.id,
projectId: item.projectId,
name: item.name,
status: item.status,
scenarioDefinition: JSON.parse(item.scenarioDefinition),
schedule: item.schedule ? item.schedule : {},
});
this.updateReference();
let scenarios = [];
this.test.scenarioDefinition.forEach(scenario => {
if (scenario.isReference()) {
if (scenarioMap[scenario.id]) scenarios.push(scenarioMap[scenario.id]);
} else {
scenarios.push(scenario);
}
})
this.test.scenarioDefinition = scenarios;
})
},
getTest(id) {
this.result = this.$get("/api/get/" + id, response => {
if (response.data) {
let item = response.data;
this.test = new Test({
id: item.id,
projectId: item.projectId,
name: item.name,
status: item.status,
scenarioDefinition: JSON.parse(item.scenarioDefinition),
schedule: item.schedule ? item.schedule : {},
});
this.updateReference();
this.$refs.config.reset();
}
});
},
save(callback) {
let validator = this.test.isValid();
if (!validator.isValid) {
this.$warning(this.$t(validator.info));
return;
this.$refs.config.reset();
}
this.change = false;
let bodyFiles = this.getBodyUploadFiles();
let url = this.create ? "/api/create" : "/api/update";
let jmx = this.test.toJMX();
let blob = new Blob([jmx.xml], {type: "application/octet-stream"});
let file = new File([blob], jmx.name);
this.result = this.$fileUpload(url, file, bodyFiles, this.test, () => {
if (callback) callback();
this.create = false;
this.resetBodyFile();
});
},
saveTest() {
this.save(() => {
this.$success(this.$t('commons.save_success'));
if (this.create) {
this.$router.push({
path: '/api/test/edit?id=' + this.test.id
})
}
// 广 head
ApiEvent.$emit(LIST_CHANGE);
})
},
runTest() {
this.result = this.$post("/api/run", {id: this.test.id, triggerMode: 'MANUAL'}, (response) => {
this.$success(this.$t('api_test.running'));
});
},
save(callback) {
let validator = this.test.isValid();
if (!validator.isValid) {
this.$warning(this.$t(validator.info));
return;
}
this.change = false;
let bodyFiles = this.getBodyUploadFiles();
let url = this.create ? "/api/create" : "/api/update";
this.result = this.$fileUpload(url, null, bodyFiles, this.test, () => {
if (callback) callback();
this.create = false;
this.resetBodyFile();
});
},
saveTest() {
this.save(() => {
this.$success(this.$t('commons.save_success'));
if (this.create) {
this.$router.push({
path: '/api/report/view/' + response.data
path: '/api/test/edit?id=' + this.test.id
})
});
},
saveRunTest() {
this.change = false;
if (!this.validateEnableTest()) {
this.$warning(this.$t('api_test.enable_validate_tip'));
return;
}
this.save(() => {
this.$success(this.$t('commons.save_success'));
this.runTest();
// 广 head
ApiEvent.$emit(LIST_CHANGE);
// 广 head
ApiEvent.$emit(LIST_CHANGE);
})
},
runTest() {
this.test.triggerMode = 'MANUAL';
this.result = this.$post("/api/run", this.test, (response) => {
this.$success(this.$t('api_test.running'));
this.$router.push({
path: '/api/report/view/' + response.data
})
},
getBodyUploadFiles() {
let bodyUploadFiles = [];
this.test.bodyUploadIds = [];
this.test.scenarioDefinition.forEach(scenario => {
scenario.requests.forEach(request => {
if (request.body) {
request.body.kvs.forEach(param => {
if (param.files) {
param.files.forEach(item => {
if (item.file) {
let fileId = getUUID().substring(0, 8);
item.name = item.file.name;
item.id = fileId;
this.test.bodyUploadIds.push(fileId);
bodyUploadFiles.push(item.file);
}
});
}
});
}
});
});
return bodyUploadFiles;
},
validateEnableTest() {
for (let scenario of this.test.scenarioDefinition) {
if (scenario.enable) {
for (let request of scenario.requests) {
if (request.enable) {
return true;
});
},
saveRunTest() {
this.change = false;
if (!this.validateEnableTest()) {
this.$warning(this.$t('api_test.enable_validate_tip'));
return;
}
this.save(() => {
this.$success(this.$t('commons.save_success'));
this.runTest();
// 广 head
ApiEvent.$emit(LIST_CHANGE);
})
},
getBodyUploadFiles() {
let bodyUploadFiles = [];
this.test.bodyUploadIds = [];
this.test.scenarioDefinition.forEach(scenario => {
scenario.requests.forEach(request => {
if (request.body) {
request.body.kvs.forEach(param => {
if (param.files) {
param.files.forEach(item => {
if (item.file) {
let fileId = getUUID().substring(0, 8);
item.name = item.file.name;
item.id = fileId;
this.test.bodyUploadIds.push(fileId);
bodyUploadFiles.push(item.file);
}
});
}
});
}
});
});
return bodyUploadFiles;
},
validateEnableTest() {
for (let scenario of this.test.scenarioDefinition) {
if (scenario.enable) {
for (let request of scenario.requests) {
if (request.enable) {
return true;
}
}
}
}
return false;
},
resetBodyFile() {
//
this.test.scenarioDefinition.forEach(scenario => {
scenario.requests.forEach(request => {
if (request.body) {
request.body.kvs.forEach(param => {
if (param.files) {
param.files.forEach(item => {
if (item.file) {
item.file = undefined;
}
});
}
});
}
});
});
},
cancel() {
this.$router.push('/api/test/list/all');
},
createPerformance() {
let validator = this.test.isValid();
if (!validator.isValid) {
this.$warning(this.$t(validator.info));
return;
}
this.result = this.$post("/api/jmx", this.test, response => {
let jmx = {
name: this.test.name + '.jmx',
xml: response.data
};
this.$store.commit('setTest', {
projectId: this.test.projectId,
name: this.test.name,
jmx: jmx
})
this.$router.push({
path: "/performance/test/create"
})
});
},
handleCommand(command) {
switch (command) {
case "report":
this.$refs.reportDialog.open();
break;
case "performance":
this.createPerformance();
break;
case "export":
downloadFile(this.test.name + ".json", this.test.export());
break;
case "jar":
this.$refs.jarConfig.open();
break;
case "import":
this.$refs.apiImport.open();
break;
}
},
saveCronExpression(cronExpression) {
this.test.schedule.enable = true;
this.test.schedule.value = cronExpression;
this.saveSchedule();
},
saveSchedule() {
this.checkScheduleEdit();
let param = {};
param = this.test.schedule;
param.resourceId = this.test.id;
let url = '/api/schedule/create';
if (param.id) {
url = '/api/schedule/update';
}
this.$post(url, param, () => {
this.$success(this.$t('commons.save_success'));
this.getTest(this.test.id);
});
},
checkScheduleEdit() {
if (this.create) {
this.$message(this.$t('api_test.environment.please_save_test'));
return false;
},
resetBodyFile() {
//
this.test.scenarioDefinition.forEach(scenario => {
scenario.requests.forEach(request => {
if (request.body) {
request.body.kvs.forEach(param => {
if (param.files) {
param.files.forEach(item => {
if (item.file) {
item.file = undefined;
}
});
}
});
}
});
});
},
cancel() {
this.$router.push('/api/test/list/all');
// console.log(this.test.toJMX().xml);
},
handleCommand(command) {
switch (command) {
case "report":
this.$refs.reportDialog.open();
break;
case "performance":
this.$store.commit('setTest', {
projectId: this.test.projectId,
name: this.test.name,
jmx: this.test.toJMX()
})
this.$router.push({
path: "/performance/test/create"
})
break;
case "export":
downloadFile(this.test.name + ".json", this.test.export());
break;
case "jar":
this.$refs.jarConfig.open();
break;
case "import":
this.$refs.apiImport.open();
break;
}
},
saveCronExpression(cronExpression) {
this.test.schedule.enable = true;
this.test.schedule.value = cronExpression;
this.saveSchedule();
},
saveSchedule() {
this.checkScheduleEdit();
let param = {};
param = this.test.schedule;
param.resourceId = this.test.id;
let url = '/api/schedule/create';
if (param.id) {
url = '/api/schedule/update';
}
this.$post(url, param, () => {
this.$success(this.$t('commons.save_success'));
this.getTest(this.test.id);
});
},
checkScheduleEdit() {
if (this.create) {
this.$message(this.$t('api_test.environment.please_save_test'));
return false;
}
return true;
},
runDebug(scenario) {
if (this.create) {
this.$warning(this.$t('api_test.environment.please_save_test'));
return;
}
let url = "/api/run/debug";
let runningTest = new Test();
Object.assign(runningTest, this.test);
let bodyFiles = this.getBodyUploadFiles();
runningTest.scenarioDefinition = [];
runningTest.scenarioDefinition.push(scenario);
let validator = runningTest.isValid();
if (!validator.isValid) {
this.$warning(this.$t(validator.info));
return;
}
let jmx = runningTest.toJMX();
let blob = new Blob([jmx.xml], {type: "application/octet-stream"});
let file = new File([blob], jmx.name);
this.$fileUpload(url, file, bodyFiles, this.test, response => {
this.debugReportId = response.data;
this.resetBodyFile();
});
},
handleEvent(event) {
if (event.keyCode === 83 && event.ctrlKey) {
console.log('拦截到 ctrl + s');//ctrl+s
this.saveTest();
event.preventDefault();
event.returnValue = false;
return false;
}
},
}
return true;
},
runDebug(scenario) {
if (this.create) {
this.$warning(this.$t('api_test.environment.please_save_test'));
return;
}
created() {
this.init();
//
document.addEventListener('keydown', this.handleEvent)
let url = "/api/run/debug";
let runningTest = new Test();
Object.assign(runningTest, this.test);
let bodyFiles = this.getBodyUploadFiles();
runningTest.scenarioDefinition = [];
runningTest.scenarioDefinition.push(scenario);
let validator = runningTest.isValid();
if (!validator.isValid) {
this.$warning(this.$t(validator.info));
return;
}
this.$fileUpload(url, null, bodyFiles, this.test, response => {
this.debugReportId = response.data;
this.resetBodyFile();
});
},
beforeDestroy() {
document.removeEventListener('keydown', this.handleEvent);
}
handleEvent(event) {
if (event.keyCode === 83 && event.ctrlKey) {
console.log('拦截到 ctrl + s');//ctrl+s
this.saveTest();
event.preventDefault();
event.returnValue = false;
return false;
}
},
},
created() {
this.init();
//
document.addEventListener('keydown', this.handleEvent)
},
beforeDestroy() {
document.removeEventListener('keydown', this.handleEvent);
}
}
</script>
<style scoped>
.test-container {
height: calc(100vh - 155px);
min-height: 600px;
}
.test-container {
height: calc(100vh - 155px);
min-height: 600px;
}
.test-name {
width: 600px;
margin-left: -20px;
margin-right: 20px;
}
.test-name {
width: 600px;
margin-left: -20px;
margin-right: 20px;
}
.test-project {
min-width: 150px;
}
.test-project {
min-width: 150px;
}
.test-container .more {
margin-left: 10px;
}
.test-container .more {
margin-left: 10px;
}
</style>

View File

@ -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,

View File

@ -1,6 +1,5 @@
import {
Arguments,
ConstantTimer as JMXConstantTimer,
CookieManager,
DNSCacheManager,
DubboSample,
@ -11,24 +10,22 @@ import {
HTTPSamplerArguments,
HTTPsamplerFiles,
HTTPSamplerProxy,
IfController as JMXIfController,
JDBCDataSource,
JDBCSampler,
JSONPathAssertion,
JSONPostProcessor,
JSR223Assertion,
JSR223PostProcessor,
JSR223PreProcessor,
RegexExtractor,
ResponseCodeAssertion,
ResponseDataAssertion,
ResponseHeadersAssertion,
TCPSampler,
TestElement,
TestPlan,
ThreadGroup,
XPath2Assertion,
XPath2Extractor,
IfController as JMXIfController,
ConstantTimer as JMXConstantTimer, TCPSampler, JSR223Assertion,
} from "./JMX";
import Mock from "mockjs";
import {funcFilters} from "@/common/js/func-filter";
@ -99,7 +96,6 @@ export const ASSERTION_TYPE = {
JSON_PATH: "JSON",
DURATION: "Duration",
JSR223: "JSR223",
XPATH2: "XPath2",
}
export const ASSERTION_REGEX_SUBJECT = {
@ -229,7 +225,6 @@ export class Scenario extends BaseConfig {
this.enable = true;
this.databaseConfigs = [];
this.tcpConfig = undefined;
this.assertions = undefined;
this.set(options);
this.sets({
@ -246,7 +241,6 @@ export class Scenario extends BaseConfig {
options.databaseConfigs = options.databaseConfigs || [];
options.dubboConfig = new DubboConfig(options.dubboConfig);
options.tcpConfig = new TCPConfig(options.tcpConfig);
options.assertions = new Assertions(options.assertions);
return options;
}
@ -747,11 +741,10 @@ export class Assertions extends BaseConfig {
this.regex = [];
this.jsonPath = [];
this.jsr223 = [];
this.xpath2 = [];
this.duration = undefined;
this.set(options);
this.sets({text: Text, regex: Regex, jsonPath: JSONPath, jsr223: AssertionJSR223, xpath2: XPath2}, options);
this.sets({text: Text, regex: Regex, jsonPath: JSONPath, jsr223: AssertionJSR223}, options);
}
initOptions(options) {
@ -833,23 +826,6 @@ export class JSONPath extends AssertionType {
}
}
export class XPath2 extends AssertionType {
constructor(options) {
super(ASSERTION_TYPE.XPATH2);
this.expression = undefined;
this.description = undefined;
this.set(options);
}
// setJSONPathDescription() {
// this.description = this.expression + " expect: " + (this.expect ? this.expect : '');
// }
isValid() {
return !!this.expression;
}
}
export class Duration extends AssertionType {
constructor(options) {
super(ASSERTION_TYPE.DURATION);
@ -1156,9 +1132,6 @@ class JMXGenerator {
this.addScenarioCookieManager(threadGroup, scenario);
this.addJDBCDataSources(threadGroup, scenario);
this.addAssertion(threadGroup, scenario);
scenario.requests.forEach(request => {
if (request.enable) {
if (!request.isValid()) return;
@ -1183,7 +1156,7 @@ class JMXGenerator {
this.addRequestExtractor(sampler, request);
this.addAssertion(sampler, request);
this.addRequestAssertion(sampler, request);
this.addJSR223PreProcessor(sampler, request);
@ -1206,25 +1179,16 @@ class JMXGenerator {
}
addEnvironments(environments, target) {
let targetMap = new Map();
let keys = new Set();
target.forEach(item => {
if (item.name) {
targetMap.set(item.name, item.enable);
}
keys.add(item.name);
});
let envArray = environments;
if (!(envArray instanceof Array)) {
envArray = JSON.parse(environments);
}
envArray.forEach(item => {
let targetItem = targetMap.get(item.name);
let hasItem = undefined;
if (targetItem) {
hasItem = (targetItem.enable === false ? false : true);
} else {
hasItem = false;
}
if (item.enable != false && item.name && !hasItem) {
if (item.enable !== false && item.name && !keys.has(item.name)) {
target.push(new KeyValue({name: item.name, value: item.value}));
}
})
@ -1328,10 +1292,7 @@ class JMXGenerator {
if (!(scenario.environment.config instanceof Object)) {
config = JSON.parse(scenario.environment.config);
}
this.addEnvironments(config.httpConfig.headers, request.headers);
if (request.doMultipartPost) {
this.removeContentType(request);
}
this.addEnvironments(config.httpConfig.headers, request.headers)
}
let name = request.name + " Headers";
this.addBodyFormat(request);
@ -1412,7 +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) {
if (request.headers[index].name === 'Content-Type' && request.headers[index].enable !== false) {
hasContentType = true;
break;
}
@ -1423,17 +1384,6 @@ class JMXGenerator {
}
}
removeContentType(request) {
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);
break;
}
}
}
}
addRequestArguments(httpSamplerProxy, request) {
let args = this.filterKV(request.parameters);
if (args.length > 0) {
@ -1475,7 +1425,7 @@ class JMXGenerator {
httpSamplerProxy.add(new HTTPsamplerFiles(files));
}
addAssertion(httpSamplerProxy, request) {
addRequestAssertion(httpSamplerProxy, request) {
let assertions = request.assertions;
if (assertions.regex.length > 0) {
assertions.regex.filter(this.filter).forEach(regex => {
@ -1489,12 +1439,6 @@ class JMXGenerator {
})
}
if (assertions.xpath2.length > 0) {
assertions.xpath2.filter(this.filter).forEach(item => {
httpSamplerProxy.put(this.getXpathAssertion(item));
})
}
if (assertions.jsr223.length > 0) {
assertions.jsr223.filter(this.filter).forEach(item => {
httpSamplerProxy.put(this.getJSR223Assertion(item));
@ -1517,14 +1461,9 @@ class JMXGenerator {
return new JSR223Assertion(name, item);
}
getXpathAssertion(item) {
let name = item.expression;
return new XPath2Assertion(name, item);
}
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) {