diff --git a/backend/src/main/java/io/metersphere/api/controller/APIScenarioReportController.java b/backend/src/main/java/io/metersphere/api/controller/APIScenarioReportController.java new file mode 100644 index 0000000000..2021a1fdc5 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/controller/APIScenarioReportController.java @@ -0,0 +1,27 @@ +package io.metersphere.api.controller; + +import io.metersphere.api.dto.APIReportResult; +import io.metersphere.api.service.ApiScenarioReportService; +import io.metersphere.commons.constants.RoleConstants; +import org.apache.shiro.authz.annotation.Logical; +import org.apache.shiro.authz.annotation.RequiresRoles; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; + +@RestController +@RequestMapping(value = "/api/scenario/report") +@RequiresRoles(value = {RoleConstants.TEST_MANAGER, RoleConstants.TEST_USER, RoleConstants.TEST_VIEWER}, logical = Logical.OR) +public class APIScenarioReportController { + + @Resource + private ApiScenarioReportService apiReportService; + + @GetMapping("/get/{reportId}") + public APIReportResult get(@PathVariable String reportId) { + return apiReportService.getCacheResult(reportId); + } +} diff --git a/backend/src/main/java/io/metersphere/api/controller/ApiAutomationController.java b/backend/src/main/java/io/metersphere/api/controller/ApiAutomationController.java index 6934b8067e..71adcdd5ab 100644 --- a/backend/src/main/java/io/metersphere/api/controller/ApiAutomationController.java +++ b/backend/src/main/java/io/metersphere/api/controller/ApiAutomationController.java @@ -70,10 +70,9 @@ public class ApiAutomationController { return apiAutomationService.getApiScenarios(ids); } - @PostMapping(value = "/run/debug") + @PostMapping(value = "/run") public void runDebug(@RequestPart("request") RunDefinitionRequest request, @RequestPart(value = "files") List bodyFiles) { apiAutomationService.run(request, bodyFiles); } - } diff --git a/backend/src/main/java/io/metersphere/api/controller/ApiMonitorController.java b/backend/src/main/java/io/metersphere/api/controller/ApiMonitorController.java new file mode 100644 index 0000000000..2384d0b7f2 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/controller/ApiMonitorController.java @@ -0,0 +1,55 @@ +package io.metersphere.api.controller; + +import io.metersphere.api.dto.ApiMonitorSearch; +import io.metersphere.api.dto.ApiResponseCodeMonitor; +import io.metersphere.api.dto.ApiResponseTimeMonitor; +import io.metersphere.api.service.APIMonitorService; +import io.metersphere.commons.constants.RoleConstants; +import org.apache.shiro.authz.annotation.Logical; +import org.apache.shiro.authz.annotation.RequiresRoles; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import java.util.List; + +@RestController +@RequestMapping(value = "/api/monitor") +@RequiresRoles(value = {RoleConstants.TEST_MANAGER, RoleConstants.TEST_USER, RoleConstants.TEST_VIEWER}, logical = Logical.OR) +public class ApiMonitorController { + + @Resource + private APIMonitorService apiMonitorService; + + /** + * 查询所有接口 + */ + @GetMapping("/list") + public List apiList() { + return apiMonitorService.list(); + } + + + /** + * 查询响应时间 + */ + @GetMapping("/getResponseTime") + public List responseTimeData(@RequestHeader("apiUrl") String url, String startTime, String endTime) { + return apiMonitorService.getApiResponseTimeData(url, startTime, endTime); + } + + /** + * 查询状态码 + */ + @GetMapping("/getResponseCode") + public List responseCodeData(@RequestHeader("apiUrl") String url, String startTime, String endTime) { + return apiMonitorService.getApiResponseCodeData(url, startTime, endTime); + } + + /** + * 查询reportId + */ + @GetMapping("/getReportId") + public String searchReportId(@RequestHeader("apiUrl") String url, @RequestParam("startTime") String startTime) { + return apiMonitorService.getReportId(url, startTime); + } +} diff --git a/backend/src/main/java/io/metersphere/api/dto/ApiMonitorSearch.java b/backend/src/main/java/io/metersphere/api/dto/ApiMonitorSearch.java new file mode 100644 index 0000000000..9ed3e0fc31 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/ApiMonitorSearch.java @@ -0,0 +1,10 @@ +package io.metersphere.api.dto; + +import lombok.Data; + +@Data +public class ApiMonitorSearch { + + private String url; + +} \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/api/dto/ApiResponseCodeMonitor.java b/backend/src/main/java/io/metersphere/api/dto/ApiResponseCodeMonitor.java new file mode 100644 index 0000000000..8fc6c1bd10 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/ApiResponseCodeMonitor.java @@ -0,0 +1,20 @@ +package io.metersphere.api.dto; + +import lombok.Data; + +@Data +public class ApiResponseCodeMonitor { + + private String id; + + private String reportId; + + private String url; + + private String apiName; + + private String startTime; + + private String responseCode; + +} diff --git a/backend/src/main/java/io/metersphere/api/dto/ApiResponseTimeMonitor.java b/backend/src/main/java/io/metersphere/api/dto/ApiResponseTimeMonitor.java new file mode 100644 index 0000000000..80ba42b930 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/ApiResponseTimeMonitor.java @@ -0,0 +1,20 @@ +package io.metersphere.api.dto; + +import lombok.Data; + +@Data +public class ApiResponseTimeMonitor { + + private String id; + + private String reportId; + + private String url; + + private String apiName; + + private String startTime; + + private String responseTime; + +} diff --git a/backend/src/main/java/io/metersphere/api/dto/automation/ScenarioStatus.java b/backend/src/main/java/io/metersphere/api/dto/automation/ScenarioStatus.java index ac3a2a6d66..d2e33af03a 100644 --- a/backend/src/main/java/io/metersphere/api/dto/automation/ScenarioStatus.java +++ b/backend/src/main/java/io/metersphere/api/dto/automation/ScenarioStatus.java @@ -1,5 +1,5 @@ package io.metersphere.api.dto.automation; public enum ScenarioStatus { - Saved, Success, Fail, Trash + Saved, Success, Fail, Trash,Underway } diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/RunDefinitionRequest.java b/backend/src/main/java/io/metersphere/api/dto/definition/RunDefinitionRequest.java index 169d0450a4..5a5d2bb923 100644 --- a/backend/src/main/java/io/metersphere/api/dto/definition/RunDefinitionRequest.java +++ b/backend/src/main/java/io/metersphere/api/dto/definition/RunDefinitionRequest.java @@ -15,6 +15,8 @@ public class RunDefinitionRequest { private String reportId; + private String environmentId; + private MsTestElement testElement; private Response response; diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/MsScenario.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/MsScenario.java index acda9214b8..5a2ff7a466 100644 --- a/backend/src/main/java/io/metersphere/api/dto/definition/request/MsScenario.java +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/MsScenario.java @@ -1,10 +1,14 @@ package io.metersphere.api.dto.definition.request; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.annotation.JSONField; import com.alibaba.fastjson.annotation.JSONType; -import com.google.gson.Gson; +import io.metersphere.api.dto.scenario.environment.EnvironmentConfig; import io.metersphere.api.service.ApiAutomationService; +import io.metersphere.api.service.ApiTestEnvironmentService; import io.metersphere.base.domain.ApiScenario; +import io.metersphere.base.domain.ApiTestEnvironmentWithBLOBs; import io.metersphere.commons.utils.CommonBeanFactory; import lombok.Data; import lombok.EqualsAndHashCode; @@ -21,23 +25,36 @@ public class MsScenario extends MsTestElement { private String type = "scenario"; @JSONField(ordinal = 10) private String name; + @JSONField(ordinal = 11) private String referenced; - public void toHashTree(HashTree tree, List hashTree) { + @JSONField(ordinal = 12) + private String environmentId; + + public void toHashTree(HashTree tree, List hashTree, EnvironmentConfig config) { + if (environmentId != null) { + ApiTestEnvironmentService environmentService = CommonBeanFactory.getBean(ApiTestEnvironmentService.class); + ApiTestEnvironmentWithBLOBs environment = environmentService.get(environmentId); + config = JSONObject.parseObject(environment.getConfig(), EnvironmentConfig.class); + } if (this.getReferenced() != null && this.getReferenced().equals("Deleted")) { return; } else if (this.getReferenced() != null && this.getReferenced().equals("REF")) { ApiAutomationService apiAutomationService = CommonBeanFactory.getBean(ApiAutomationService.class); ApiScenario scenario = apiAutomationService.getApiScenario(this.getId()); - Gson gs = new Gson(); - MsTestElement element = gs.fromJson(scenario.getScenarioDefinition(), MsTestElement.class); - hashTree.add(element); + JSONObject element = JSON.parseObject(scenario.getScenarioDefinition()); + List dataArr = JSON.parseArray(element.getString("hashTree"), MsTestElement.class); + if (hashTree == null) { + hashTree = dataArr; + } else { + hashTree.addAll(dataArr); + } } if (CollectionUtils.isNotEmpty(hashTree)) { - hashTree.forEach(el -> { - el.toHashTree(tree, el.getHashTree()); - }); + for (MsTestElement el : hashTree) { + el.toHashTree(tree, el.getHashTree(), config); + } } } } diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/MsTestElement.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/MsTestElement.java index baa63f87ba..1f70213d34 100644 --- a/backend/src/main/java/io/metersphere/api/dto/definition/request/MsTestElement.java +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/MsTestElement.java @@ -16,6 +16,7 @@ import io.metersphere.api.dto.definition.request.sampler.MsHTTPSamplerProxy; import io.metersphere.api.dto.definition.request.sampler.MsJDBCSampler; import io.metersphere.api.dto.definition.request.sampler.MsTCPSampler; import io.metersphere.api.dto.definition.request.timer.MsConstantTimer; +import io.metersphere.api.dto.scenario.environment.EnvironmentConfig; import io.metersphere.commons.utils.LogUtil; import lombok.Data; import org.apache.jmeter.protocol.http.control.AuthManager; @@ -61,9 +62,10 @@ public abstract class MsTestElement { @JSONField(ordinal = 4) private LinkedList hashTree; - public void toHashTree(HashTree tree, List hashTree) { + // 公共环境逐层传递,如果自身有环境 以自身引用环境为准否则以公共环境作为请求环境 + public void toHashTree(HashTree tree, List hashTree, EnvironmentConfig config) { for (MsTestElement el : hashTree) { - el.toHashTree(tree, el.hashTree); + el.toHashTree(tree, el.hashTree, config); } } @@ -85,9 +87,15 @@ public abstract class MsTestElement { return null; } + public HashTree generateHashTree(EnvironmentConfig config) { + HashTree jmeterTestPlanHashTree = new ListedHashTree(); + this.toHashTree(jmeterTestPlanHashTree, this.hashTree, config); + return jmeterTestPlanHashTree; + } + public HashTree generateHashTree() { HashTree jmeterTestPlanHashTree = new ListedHashTree(); - this.toHashTree(jmeterTestPlanHashTree, this.hashTree); + this.toHashTree(jmeterTestPlanHashTree, this.hashTree, null); return jmeterTestPlanHashTree; } diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/MsTestPlan.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/MsTestPlan.java index 9e041cbc73..4af1e721e8 100644 --- a/backend/src/main/java/io/metersphere/api/dto/definition/request/MsTestPlan.java +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/MsTestPlan.java @@ -1,6 +1,7 @@ package io.metersphere.api.dto.definition.request; import com.alibaba.fastjson.annotation.JSONType; +import io.metersphere.api.dto.scenario.environment.EnvironmentConfig; import lombok.Data; import lombok.EqualsAndHashCode; import org.apache.commons.collections.CollectionUtils; @@ -18,11 +19,11 @@ import java.util.List; public class MsTestPlan extends MsTestElement { private String type = "TestPlan"; - public void toHashTree(HashTree tree, List hashTree) { + public void toHashTree(HashTree tree, List hashTree, EnvironmentConfig config) { final HashTree testPlanTree = tree.add(getPlan()); if (CollectionUtils.isNotEmpty(hashTree)) { hashTree.forEach(el -> { - el.toHashTree(testPlanTree, el.getHashTree()); + el.toHashTree(testPlanTree, el.getHashTree(), config); }); } } diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/MsThreadGroup.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/MsThreadGroup.java index 7443d3a3c5..d8e31fbd7f 100644 --- a/backend/src/main/java/io/metersphere/api/dto/definition/request/MsThreadGroup.java +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/MsThreadGroup.java @@ -1,6 +1,7 @@ package io.metersphere.api.dto.definition.request; import com.alibaba.fastjson.annotation.JSONType; +import io.metersphere.api.dto.scenario.environment.EnvironmentConfig; import lombok.Data; import lombok.EqualsAndHashCode; import org.apache.commons.collections.CollectionUtils; @@ -18,11 +19,11 @@ import java.util.List; public class MsThreadGroup extends MsTestElement { private String type = "ThreadGroup"; - public void toHashTree(HashTree tree, List hashTree) { + public void toHashTree(HashTree tree, List hashTree, EnvironmentConfig config) { final HashTree groupTree = tree.add(getThreadGroup()); if (CollectionUtils.isNotEmpty(hashTree)) { hashTree.forEach(el -> { - el.toHashTree(groupTree, el.getHashTree()); + el.toHashTree(groupTree, el.getHashTree(), config); }); } } @@ -37,7 +38,7 @@ public class MsThreadGroup extends MsTestElement { ThreadGroup threadGroup = new ThreadGroup(); threadGroup.setEnabled(true); - threadGroup.setName(this.getName() + "ThreadGroup"); + threadGroup.setName(this.getName()); threadGroup.setProperty(TestElement.TEST_CLASS, ThreadGroup.class.getName()); threadGroup.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("ThreadGroupGui")); threadGroup.setNumThreads(1); diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertions.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertions.java index d266d7e0a5..2deb0a4091 100644 --- a/backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertions.java +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertions.java @@ -2,6 +2,7 @@ package io.metersphere.api.dto.definition.request.assertions; import com.alibaba.fastjson.annotation.JSONType; import io.metersphere.api.dto.definition.request.MsTestElement; +import io.metersphere.api.dto.scenario.environment.EnvironmentConfig; import lombok.Data; import lombok.EqualsAndHashCode; import org.apache.commons.collections.CollectionUtils; @@ -23,10 +24,10 @@ public class MsAssertions extends MsTestElement { private MsAssertionDuration duration; private String type = "Assertions"; - public void toHashTree(HashTree tree, List hashTree) { + public void toHashTree(HashTree tree, List hashTree, EnvironmentConfig config) { addAssertions(tree); - } + private void addAssertions(HashTree hashTree) { if (CollectionUtils.isNotEmpty(this.getRegex())) { this.getRegex().stream().filter(MsAssertionRegex::isValid).forEach(assertion -> diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/auth/MsAuthManager.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/auth/MsAuthManager.java index d82d2519d6..48652c4bcd 100644 --- a/backend/src/main/java/io/metersphere/api/dto/definition/request/auth/MsAuthManager.java +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/auth/MsAuthManager.java @@ -50,7 +50,7 @@ public class MsAuthManager extends MsTestElement { @JSONField(ordinal = 18) private String environment; - public void toHashTree(HashTree tree, List hashTree) { + public void toHashTree(HashTree tree, List hashTree, EnvironmentConfig config) { AuthManager authManager = new AuthManager(); authManager.setEnabled(true); authManager.setName(this.getUsername() + "AuthManager"); @@ -63,7 +63,7 @@ public class MsAuthManager extends MsTestElement { if (environment != null) { ApiTestEnvironmentService environmentService = CommonBeanFactory.getBean(ApiTestEnvironmentService.class); ApiTestEnvironmentWithBLOBs environmentWithBLOBs = environmentService.get(environment); - EnvironmentConfig config = JSONObject.parseObject(environmentWithBLOBs.getConfig(), EnvironmentConfig.class); + config = JSONObject.parseObject(environmentWithBLOBs.getConfig(), EnvironmentConfig.class); this.url = config.getHttpConfig().getProtocol() + "://" + config.getHttpConfig().getSocket(); } } diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/configurations/MsHeaderManager.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/configurations/MsHeaderManager.java index 3c56b89316..82ec171102 100644 --- a/backend/src/main/java/io/metersphere/api/dto/definition/request/configurations/MsHeaderManager.java +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/configurations/MsHeaderManager.java @@ -4,6 +4,7 @@ import com.alibaba.fastjson.annotation.JSONField; import com.alibaba.fastjson.annotation.JSONType; import io.metersphere.api.dto.definition.request.MsTestElement; import io.metersphere.api.dto.scenario.KeyValue; +import io.metersphere.api.dto.scenario.environment.EnvironmentConfig; import lombok.Data; import lombok.EqualsAndHashCode; import org.apache.commons.collections.CollectionUtils; @@ -24,7 +25,7 @@ public class MsHeaderManager extends MsTestElement { @JSONField(ordinal = 10) private List headers; - public void toHashTree(HashTree tree, List hashTree) { + public void toHashTree(HashTree tree, List hashTree, EnvironmentConfig config) { HeaderManager headerManager = new HeaderManager(); headerManager.setEnabled(true); headerManager.setName(this.getName() + "Headers"); @@ -36,7 +37,7 @@ public class MsHeaderManager extends MsTestElement { final HashTree headersTree = tree.add(headerManager); if (CollectionUtils.isNotEmpty(hashTree)) { hashTree.forEach(el -> { - el.toHashTree(headersTree, el.getHashTree()); + el.toHashTree(headersTree, el.getHashTree(), config); }); } } diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/controller/MsIfController.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/controller/MsIfController.java index 15da6801ef..59e69fbe7d 100644 --- a/backend/src/main/java/io/metersphere/api/dto/definition/request/controller/MsIfController.java +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/controller/MsIfController.java @@ -2,6 +2,7 @@ package io.metersphere.api.dto.definition.request.controller; import com.alibaba.fastjson.annotation.JSONType; import io.metersphere.api.dto.definition.request.MsTestElement; +import io.metersphere.api.dto.scenario.environment.EnvironmentConfig; import lombok.Data; import lombok.EqualsAndHashCode; import org.apache.commons.collections.CollectionUtils; @@ -24,11 +25,11 @@ public class MsIfController extends MsTestElement { private String operator; private String value; - public void toHashTree(HashTree tree, List hashTree) { + public void toHashTree(HashTree tree, List hashTree, EnvironmentConfig config) { final HashTree groupTree = tree.add(ifController()); if (CollectionUtils.isNotEmpty(hashTree)) { hashTree.forEach(el -> { - el.toHashTree(groupTree, el.getHashTree()); + el.toHashTree(groupTree, el.getHashTree(), config); }); } } diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/dns/MsDNSCacheManager.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/dns/MsDNSCacheManager.java index 696217eeb6..f5d88f6aff 100644 --- a/backend/src/main/java/io/metersphere/api/dto/definition/request/dns/MsDNSCacheManager.java +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/dns/MsDNSCacheManager.java @@ -23,9 +23,9 @@ import java.util.List; @JSONType(typeName = "DNSCacheManager") public class MsDNSCacheManager extends MsTestElement { - public void toHashTree(HashTree tree, List hashTree) { + public void toHashTree(HashTree tree, List hashTree, EnvironmentConfig config) { for (MsTestElement el : hashTree) { - el.toHashTree(tree, el.getHashTree()); + el.toHashTree(tree, el.getHashTree(), config); } } diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/extract/MsExtract.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/extract/MsExtract.java index e44152b641..06e3cc00fa 100644 --- a/backend/src/main/java/io/metersphere/api/dto/definition/request/extract/MsExtract.java +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/extract/MsExtract.java @@ -2,6 +2,7 @@ package io.metersphere.api.dto.definition.request.extract; import com.alibaba.fastjson.annotation.JSONType; import io.metersphere.api.dto.definition.request.MsTestElement; +import io.metersphere.api.dto.scenario.environment.EnvironmentConfig; import lombok.Data; import lombok.EqualsAndHashCode; import org.apache.commons.collections.CollectionUtils; @@ -23,7 +24,7 @@ public class MsExtract extends MsTestElement { private List xpath; private String type = "Extract"; - public void toHashTree(HashTree tree, List hashTree) { + public void toHashTree(HashTree tree, List hashTree, EnvironmentConfig config) { addRequestExtractors(tree); } diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/processors/MsJSR223Processor.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/processors/MsJSR223Processor.java index d20b6e3801..83d0a2205a 100644 --- a/backend/src/main/java/io/metersphere/api/dto/definition/request/processors/MsJSR223Processor.java +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/processors/MsJSR223Processor.java @@ -3,13 +3,14 @@ package io.metersphere.api.dto.definition.request.processors; import com.alibaba.fastjson.annotation.JSONField; import com.alibaba.fastjson.annotation.JSONType; import io.metersphere.api.dto.definition.request.MsTestElement; +import io.metersphere.api.dto.scenario.environment.EnvironmentConfig; import lombok.Data; import lombok.EqualsAndHashCode; import org.apache.commons.collections.CollectionUtils; +import org.apache.jmeter.protocol.java.sampler.JSR223Sampler; import org.apache.jmeter.save.SaveService; import org.apache.jmeter.testelement.TestElement; import org.apache.jorphan.collections.HashTree; -import org.apache.jmeter.protocol.java.sampler.JSR223Sampler; import java.util.List; @@ -25,7 +26,7 @@ public class MsJSR223Processor extends MsTestElement { @JSONField(ordinal = 11) private String scriptLanguage; - public void toHashTree(HashTree tree, List hashTree) { + public void toHashTree(HashTree tree, List hashTree, EnvironmentConfig config) { JSR223Sampler processor = new JSR223Sampler(); processor.setEnabled(true); processor.setName(this.getName() + "JSR223Processor"); @@ -38,7 +39,7 @@ public class MsJSR223Processor extends MsTestElement { final HashTree jsr223PreTree = tree.add(processor); if (CollectionUtils.isNotEmpty(hashTree)) { hashTree.forEach(el -> { - el.toHashTree(jsr223PreTree, el.getHashTree()); + el.toHashTree(jsr223PreTree, el.getHashTree(), config); }); } } diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/processors/post/MsJSR223PostProcessor.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/processors/post/MsJSR223PostProcessor.java index 47321b3e6b..0776127b7a 100644 --- a/backend/src/main/java/io/metersphere/api/dto/definition/request/processors/post/MsJSR223PostProcessor.java +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/processors/post/MsJSR223PostProcessor.java @@ -3,6 +3,7 @@ package io.metersphere.api.dto.definition.request.processors.post; import com.alibaba.fastjson.annotation.JSONField; import com.alibaba.fastjson.annotation.JSONType; import io.metersphere.api.dto.definition.request.MsTestElement; +import io.metersphere.api.dto.scenario.environment.EnvironmentConfig; import lombok.Data; import lombok.EqualsAndHashCode; import org.apache.commons.collections.CollectionUtils; @@ -26,7 +27,7 @@ public class MsJSR223PostProcessor extends MsTestElement { private String scriptLanguage; - public void toHashTree(HashTree tree, List hashTree) { + public void toHashTree(HashTree tree, List hashTree, EnvironmentConfig config) { JSR223PostProcessor processor = new JSR223PostProcessor(); processor.setEnabled(true); processor.setName(this.getName() + "JSR223PostProcessor"); @@ -39,7 +40,7 @@ public class MsJSR223PostProcessor extends MsTestElement { final HashTree jsr223PostTree = tree.add(processor); if (CollectionUtils.isNotEmpty(hashTree)) { hashTree.forEach(el -> { - el.toHashTree(jsr223PostTree, el.getHashTree()); + el.toHashTree(jsr223PostTree, el.getHashTree(), config); }); } } diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/processors/pre/MsJSR223PreProcessor.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/processors/pre/MsJSR223PreProcessor.java index e6102392b6..631188bbe3 100644 --- a/backend/src/main/java/io/metersphere/api/dto/definition/request/processors/pre/MsJSR223PreProcessor.java +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/processors/pre/MsJSR223PreProcessor.java @@ -3,6 +3,7 @@ package io.metersphere.api.dto.definition.request.processors.pre; import com.alibaba.fastjson.annotation.JSONField; import com.alibaba.fastjson.annotation.JSONType; import io.metersphere.api.dto.definition.request.MsTestElement; +import io.metersphere.api.dto.scenario.environment.EnvironmentConfig; import lombok.Data; import lombok.EqualsAndHashCode; import org.apache.commons.collections.CollectionUtils; @@ -25,7 +26,7 @@ public class MsJSR223PreProcessor extends MsTestElement { @JSONField(ordinal = 11) private String scriptLanguage; - public void toHashTree(HashTree tree, List hashTree) { + public void toHashTree(HashTree tree, List hashTree, EnvironmentConfig config) { JSR223PreProcessor processor = new JSR223PreProcessor(); processor.setEnabled(true); processor.setName(this.getName() + "JSR223PreProcessor"); @@ -38,7 +39,7 @@ public class MsJSR223PreProcessor extends MsTestElement { final HashTree jsr223PreTree = tree.add(processor); if (CollectionUtils.isNotEmpty(hashTree)) { hashTree.forEach(el -> { - el.toHashTree(jsr223PreTree, el.getHashTree()); + el.toHashTree(jsr223PreTree, el.getHashTree(), config); }); } } diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsDubboSampler.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsDubboSampler.java index 600833239a..7f3a6c8d50 100644 --- a/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsDubboSampler.java +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsDubboSampler.java @@ -11,6 +11,7 @@ import io.metersphere.api.dto.definition.request.sampler.dubbo.MsConfigCenter; import io.metersphere.api.dto.definition.request.sampler.dubbo.MsConsumerAndService; import io.metersphere.api.dto.definition.request.sampler.dubbo.MsRegistryCenter; import io.metersphere.api.dto.scenario.KeyValue; +import io.metersphere.api.dto.scenario.environment.EnvironmentConfig; import lombok.Data; import lombok.EqualsAndHashCode; import org.apache.commons.collections.CollectionUtils; @@ -50,13 +51,13 @@ public class MsDubboSampler extends MsTestElement { @JSONField(ordinal = 59) private List attachmentArgs; - public void toHashTree(HashTree tree, List hashTree) { + public void toHashTree(HashTree tree, List hashTree, EnvironmentConfig config) { final HashTree testPlanTree = new ListedHashTree(); testPlanTree.add(dubboConfig()); tree.set(dubboSample(), testPlanTree); if (CollectionUtils.isNotEmpty(hashTree)) { hashTree.forEach(el -> { - el.toHashTree(testPlanTree, el.getHashTree()); + el.toHashTree(testPlanTree, el.getHashTree(), config); }); } } diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsHTTPSamplerProxy.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsHTTPSamplerProxy.java index 2bf62c351b..4b31329eb7 100644 --- a/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsHTTPSamplerProxy.java +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsHTTPSamplerProxy.java @@ -52,12 +52,12 @@ public class MsHTTPSamplerProxy extends MsTestElement { @JSONField(ordinal = 15) private String connectTimeout; + @JSONField(ordinal = 16) - private String responseTimeout; - @JSONField(ordinal = 17) - private List arguments; + @JSONField(ordinal = 17) + private List headers; @JSONField(ordinal = 18) private Body body; @@ -78,9 +78,9 @@ public class MsHTTPSamplerProxy extends MsTestElement { private String useEnvironment; @JSONField(ordinal = 24) - private List headers; + private List arguments; - public void toHashTree(HashTree tree, List hashTree) { + public void toHashTree(HashTree tree, List hashTree, EnvironmentConfig config) { HTTPSamplerProxy sampler = new HTTPSamplerProxy(); sampler.setEnabled(true); sampler.setName(this.getName()); @@ -93,7 +93,6 @@ public class MsHTTPSamplerProxy extends MsTestElement { sampler.setFollowRedirects(this.isFollowRedirects()); sampler.setUseKeepAlive(true); sampler.setDoMultipart(this.isDoMultipartPost()); - EnvironmentConfig config = null; if (useEnvironment != null) { ApiTestEnvironmentService environmentService = CommonBeanFactory.getBean(ApiTestEnvironmentService.class); ApiTestEnvironmentWithBLOBs environment = environmentService.get(useEnvironment); @@ -145,16 +144,18 @@ public class MsHTTPSamplerProxy extends MsTestElement { } final HashTree httpSamplerTree = tree.add(sampler); - setHeader(httpSamplerTree); + if (CollectionUtils.isNotEmpty(this.headers)) { + setHeader(httpSamplerTree); + } //判断是否要开启DNS if (config != null && config.getCommonConfig() != null && config.getCommonConfig().isEnableHost()) { MsDNSCacheManager.addEnvironmentVariables(httpSamplerTree, this.getName(), config); MsDNSCacheManager.addEnvironmentDNS(httpSamplerTree, this.getName(), config); } if (CollectionUtils.isNotEmpty(hashTree)) { - hashTree.forEach(el -> { - el.toHashTree(httpSamplerTree, el.getHashTree()); - }); + for (MsTestElement el : hashTree) { + el.toHashTree(httpSamplerTree, el.getHashTree(), config); + } } } diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsJDBCSampler.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsJDBCSampler.java index c37cc2a3c0..2b4330fc64 100644 --- a/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsJDBCSampler.java +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsJDBCSampler.java @@ -5,6 +5,7 @@ import com.alibaba.fastjson.annotation.JSONType; import io.metersphere.api.dto.definition.request.MsTestElement; import io.metersphere.api.dto.scenario.DatabaseConfig; import io.metersphere.api.dto.scenario.KeyValue; +import io.metersphere.api.dto.scenario.environment.EnvironmentConfig; import lombok.Data; import lombok.EqualsAndHashCode; import org.apache.commons.collections.CollectionUtils; @@ -38,13 +39,13 @@ public class MsJDBCSampler extends MsTestElement { @JSONField(ordinal = 16) private String environmentId; - public void toHashTree(HashTree tree, List hashTree) { + public void toHashTree(HashTree tree, List hashTree, EnvironmentConfig config) { final HashTree samplerHashTree = tree.add(jdbcSampler()); tree.add(jdbcDataSource()); tree.add(arguments(this.getName() + " Variables", this.getVariables())); if (CollectionUtils.isNotEmpty(hashTree)) { hashTree.forEach(el -> { - el.toHashTree(samplerHashTree, el.getHashTree()); + el.toHashTree(samplerHashTree, el.getHashTree(), config); }); } } diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsTCPSampler.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsTCPSampler.java index 53bde7df7b..fee1ea7efb 100644 --- a/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsTCPSampler.java +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsTCPSampler.java @@ -3,6 +3,7 @@ package io.metersphere.api.dto.definition.request.sampler; import com.alibaba.fastjson.annotation.JSONField; import com.alibaba.fastjson.annotation.JSONType; import io.metersphere.api.dto.definition.request.MsTestElement; +import io.metersphere.api.dto.scenario.environment.EnvironmentConfig; import lombok.Data; import lombok.EqualsAndHashCode; import org.apache.commons.collections.CollectionUtils; @@ -48,13 +49,13 @@ public class MsTCPSampler extends MsTestElement { @JSONField(ordinal = 23) private String request; - public void toHashTree(HashTree tree, List hashTree) { + public void toHashTree(HashTree tree, List hashTree, EnvironmentConfig config) { final HashTree samplerHashTree = new ListedHashTree(); samplerHashTree.add(tcpConfig()); tree.set(tcpSampler(), samplerHashTree); if (CollectionUtils.isNotEmpty(hashTree)) { hashTree.forEach(el -> { - el.toHashTree(samplerHashTree, el.getHashTree()); + el.toHashTree(samplerHashTree, el.getHashTree(), config); }); } } diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/timer/MsConstantTimer.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/timer/MsConstantTimer.java index 51e65fcbbf..a1057b6647 100644 --- a/backend/src/main/java/io/metersphere/api/dto/definition/request/timer/MsConstantTimer.java +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/timer/MsConstantTimer.java @@ -3,6 +3,7 @@ package io.metersphere.api.dto.definition.request.timer; import com.alibaba.fastjson.annotation.JSONField; import com.alibaba.fastjson.annotation.JSONType; import io.metersphere.api.dto.definition.request.MsTestElement; +import io.metersphere.api.dto.scenario.environment.EnvironmentConfig; import lombok.Data; import lombok.EqualsAndHashCode; import org.apache.commons.collections.CollectionUtils; @@ -25,11 +26,11 @@ public class MsConstantTimer extends MsTestElement { @JSONField(ordinal = 12) private String delay; - public void toHashTree(HashTree tree, List hashTree) { + public void toHashTree(HashTree tree, List hashTree, EnvironmentConfig config) { final HashTree groupTree = tree.add(constantTimer()); if (CollectionUtils.isNotEmpty(hashTree)) { hashTree.forEach(el -> { - el.toHashTree(groupTree, el.getHashTree()); + el.toHashTree(groupTree, el.getHashTree(), config); }); } } 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 46dd2f08b8..b65ef3acba 100644 --- a/backend/src/main/java/io/metersphere/api/jmeter/APIBackendListenerClient.java +++ b/backend/src/main/java/io/metersphere/api/jmeter/APIBackendListenerClient.java @@ -168,13 +168,9 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl apiDefinitionExecResultService.saveApiResult(testResult); } } else if (StringUtils.equals(this.runMode, ApiRunMode.SCENARIO.name())) { - // 调试操作,不需要存储结果 - if (StringUtils.isBlank(debugReportId)) { - apiScenarioReportService.addResult(testResult); - } else { - apiScenarioReportService.addResult(testResult); - //apiScenarioReportService.saveApiResult(testResult); - } + // 执行报告不需要存储,由用户确认后在存储 + testResult.setTestId(debugReportId); + apiScenarioReportService.complete(testResult); } else { apiTestService.changeStatus(testId, APITestStatus.Completed); report = apiReportService.getRunningReport(testResult.getTestId()); diff --git a/backend/src/main/java/io/metersphere/api/service/APIMonitorService.java b/backend/src/main/java/io/metersphere/api/service/APIMonitorService.java new file mode 100644 index 0000000000..d8e942a01a --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/service/APIMonitorService.java @@ -0,0 +1,35 @@ +package io.metersphere.api.service; + +import io.metersphere.api.dto.ApiMonitorSearch; +import io.metersphere.api.dto.ApiResponseCodeMonitor; +import io.metersphere.api.dto.ApiResponseTimeMonitor; +import io.metersphere.base.mapper.ApiDataViewMapper; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.util.List; + +@Service +@Transactional(rollbackFor = Exception.class) +public class APIMonitorService { + + @Resource + private ApiDataViewMapper apiDataViewMapper; + + public List list() { + return apiDataViewMapper.selectAll(); + } + + public List getApiResponseTimeData(String apiUrl, String startTime, String endTime) { + return apiDataViewMapper.selectResponseTimeByUrl(apiUrl, startTime, endTime); + } + + public List getApiResponseCodeData(String apiUrl, String startTime, String endTime) { + return apiDataViewMapper.selectResponseCodeByUrl(apiUrl, startTime, endTime); + } + + public String getReportId(String apiUrl, String startTime) { + return apiDataViewMapper.selectReportIdByUrlAndStartTime(apiUrl, startTime); + } +} diff --git a/backend/src/main/java/io/metersphere/api/service/APIReportService.java b/backend/src/main/java/io/metersphere/api/service/APIReportService.java index cc4367eb8b..a8ce783493 100644 --- a/backend/src/main/java/io/metersphere/api/service/APIReportService.java +++ b/backend/src/main/java/io/metersphere/api/service/APIReportService.java @@ -1,17 +1,21 @@ package io.metersphere.api.service; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import io.metersphere.api.dto.APIReportResult; import io.metersphere.api.dto.DeleteAPIReportRequest; import io.metersphere.api.dto.QueryAPIReportRequest; import io.metersphere.api.jmeter.TestResult; import io.metersphere.base.domain.*; +import io.metersphere.base.mapper.ApiDataViewMapper; import io.metersphere.base.mapper.ApiTestReportDetailMapper; import io.metersphere.base.mapper.ApiTestReportMapper; import io.metersphere.base.mapper.ext.ExtApiTestReportMapper; import io.metersphere.commons.constants.APITestStatus; import io.metersphere.commons.constants.ReportTriggerMode; import io.metersphere.commons.exception.MSException; +import io.metersphere.commons.utils.LogUtil; import io.metersphere.commons.utils.ServiceUtils; import io.metersphere.dto.DashboardTestDTO; import io.metersphere.i18n.Translator; @@ -21,8 +25,11 @@ import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; import java.nio.charset.StandardCharsets; +import java.text.SimpleDateFormat; import java.time.Instant; import java.time.temporal.ChronoUnit; +import java.util.ArrayList; +import java.util.Date; import java.util.List; import java.util.UUID; @@ -36,6 +43,8 @@ public class APIReportService { private ApiTestReportDetailMapper apiTestReportDetailMapper; @Resource private ExtApiTestReportMapper extApiTestReportMapper; + @Resource + private ApiDataViewMapper apiDataViewMapper; public List list(QueryAPIReportRequest request) { request.setOrders(ServiceUtils.getDefaultOrder(request.getOrders())); @@ -63,6 +72,8 @@ public class APIReportService { public void delete(DeleteAPIReportRequest request) { apiTestReportDetailMapper.deleteByPrimaryKey(request.getId()); apiTestReportMapper.deleteByPrimaryKey(request.getId()); + apiDataViewMapper.deleteByReportId(request.getId()); + } public void deleteByTestId(String testId) { @@ -89,6 +100,8 @@ public class APIReportService { // report report.setUpdateTime(System.currentTimeMillis()); if (!StringUtils.equals(report.getStatus(), APITestStatus.Debug.name())) { + //新增每一条接口记录到api_data_view表中 + creatApiDataView(new String(detail.getContent(), StandardCharsets.UTF_8), report.getId()); if (result.getError() > 0) { report.setStatus(APITestStatus.Error.name()); } else { @@ -99,6 +112,44 @@ public class APIReportService { apiTestReportMapper.updateByPrimaryKeySelective(report); } + private void creatApiDataView(String jsonString, String reportId) { + List listApiDataView = new ArrayList<>(); + JSONObject jsonObject = JSON.parseObject(jsonString, JSONObject.class); + JSONArray jsonArray = jsonObject.getJSONArray("scenarios"); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + try { + for (int i = 0; i < jsonArray.size(); i++) { + JSONObject jsonInArray = jsonArray.getJSONObject(i); + JSONArray jsonRequestResults = jsonInArray.getJSONArray("requestResults"); + for (int j = 0; j < jsonRequestResults.size(); j++) { + JSONObject jsonInResponseResult = jsonRequestResults.getJSONObject(j).getJSONObject("responseResult"); + String responseTime = jsonInResponseResult.getString("responseTime"); + String responseCode = jsonInResponseResult.getString("responseCode"); + String startTime = jsonRequestResults.getJSONObject(j).getString("startTime"); + String name = jsonRequestResults.getJSONObject(j).getString("name"); + String url = jsonRequestResults.getJSONObject(j).getString("url"); + if (StringUtils.isBlank(url)){ + //如果非http请求不入库 + continue; + } + ApiDataView apiDataView = new ApiDataView(); + apiDataView.setId(UUID.randomUUID().toString()); + apiDataView.setReportId(reportId); + apiDataView.setApiName(name); + apiDataView.setUrl(StringUtils.substringBefore(url,"?")); + apiDataView.setResponseTime(responseTime); + apiDataView.setStartTime(sdf.format(new Date(Long.parseLong(startTime)))); + apiDataView.setResponseCode(responseCode); + listApiDataView.add(apiDataView); + } + } + } catch (Exception e) { + LogUtil.error(e); + } + apiDataViewMapper.insertListApiData(listApiDataView); + + } + public String create(ApiTest test, String triggerMode) { ApiTestReport running = getRunningReport(test.getId()); if (running != null) { diff --git a/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java b/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java index 53a1176c8f..422987be8d 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java @@ -1,26 +1,28 @@ package io.metersphere.api.service; import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; import com.google.gson.Gson; +import io.metersphere.api.dto.APIReportResult; import io.metersphere.api.dto.automation.ApiScenarioDTO; import io.metersphere.api.dto.automation.ApiScenarioRequest; import io.metersphere.api.dto.automation.SaveApiScenarioRequest; import io.metersphere.api.dto.automation.ScenarioStatus; import io.metersphere.api.dto.definition.RunDefinitionRequest; +import io.metersphere.api.dto.scenario.environment.EnvironmentConfig; import io.metersphere.api.jmeter.JMeterService; -import io.metersphere.base.domain.ApiScenario; -import io.metersphere.base.domain.ApiScenarioExample; -import io.metersphere.base.domain.ApiTag; -import io.metersphere.base.domain.ApiTagExample; +import io.metersphere.base.domain.*; import io.metersphere.base.mapper.ApiScenarioMapper; import io.metersphere.base.mapper.ApiTagMapper; import io.metersphere.base.mapper.ext.ExtApiScenarioMapper; +import io.metersphere.commons.constants.APITestStatus; import io.metersphere.commons.constants.ApiRunMode; import io.metersphere.commons.exception.MSException; import io.metersphere.commons.utils.LogUtil; import io.metersphere.commons.utils.SessionUtils; import io.metersphere.i18n.Translator; import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.jorphan.collections.HashTree; import org.aspectj.util.FileUtil; import org.springframework.stereotype.Service; @@ -32,6 +34,7 @@ import java.io.*; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.UUID; import java.util.stream.Collectors; @Service @@ -45,6 +48,10 @@ public class ApiAutomationService { private ApiTagMapper apiTagMapper; @Resource private JMeterService jMeterService; + @Resource + private ApiTestEnvironmentService environmentService; + @Resource + private ApiScenarioReportService apiReportService; private static final String BODY_FILE_DIR = "/opt/metersphere/data/body"; @@ -92,7 +99,11 @@ public class ApiAutomationService { scenario.setScenarioDefinition(request.getScenarioDefinition()); scenario.setCreateTime(System.currentTimeMillis()); scenario.setUpdateTime(System.currentTimeMillis()); - scenario.setStatus(ScenarioStatus.Saved.name()); + if (StringUtils.isNotEmpty(request.getStatus())) { + scenario.setStatus(request.getStatus()); + } else { + scenario.setStatus(ScenarioStatus.Underway.name()); + } if (request.getUserId() == null) { scenario.setUserId(SessionUtils.getUserId()); } else { @@ -117,7 +128,11 @@ public class ApiAutomationService { scenario.setStepTotal(request.getStepTotal()); scenario.setScenarioDefinition(request.getScenarioDefinition()); scenario.setUpdateTime(System.currentTimeMillis()); - scenario.setStatus(ScenarioStatus.Saved.name()); + if (StringUtils.isNotEmpty(request.getStatus())) { + scenario.setStatus(request.getStatus()); + } else { + scenario.setStatus(ScenarioStatus.Underway.name()); + } scenario.setUserId(request.getUserId()); scenario.setDescription(request.getDescription()); apiScenarioMapper.updateByPrimaryKeySelective(scenario); @@ -143,8 +158,7 @@ public class ApiAutomationService { private void checkNameExist(SaveApiScenarioRequest request) { ApiScenarioExample example = new ApiScenarioExample(); - example.createCriteria().andNameEqualTo(request.getName()).andProjectIdEqualTo(request.getProjectId()) - .andApiScenarioModuleIdEqualTo(request.getApiScenarioModuleId()).andIdNotEqualTo(request.getId()); + example.createCriteria().andNameEqualTo(request.getName()).andProjectIdEqualTo(request.getProjectId()).andIdNotEqualTo(request.getId()); if (apiScenarioMapper.countByExample(example) > 0) { MSException.throwException(Translator.get("automation_name_already_exists")); } @@ -204,10 +218,26 @@ public class ApiAutomationService { public String run(RunDefinitionRequest request, List bodyFiles) { List bodyUploadIds = new ArrayList<>(request.getBodyUploadIds()); createBodyFiles(bodyUploadIds, bodyFiles); - HashTree hashTree = request.getTestElement().generateHashTree(); + EnvironmentConfig config = null; + if (request.getEnvironmentId() != null) { + ApiTestEnvironmentWithBLOBs environment = environmentService.get(request.getEnvironmentId()); + config = JSONObject.parseObject(environment.getConfig(), EnvironmentConfig.class); + } + HashTree hashTree = request.getTestElement().generateHashTree(config); request.getTestElement().getJmx(hashTree); + // 调用执行方法 jMeterService.runDefinition(request.getId(), hashTree, request.getReportId(), ApiRunMode.SCENARIO.name()); + APIReportResult report = new APIReportResult(); + report.setId(UUID.randomUUID().toString()); + report.setTestId(request.getReportId()); + report.setName("RUN"); + report.setTriggerMode(null); + report.setCreateTime(System.currentTimeMillis()); + report.setUpdateTime(System.currentTimeMillis()); + report.setStatus(APITestStatus.Running.name()); + report.setUserId(SessionUtils.getUserId()); + apiReportService.addResult(report); return request.getId(); } diff --git a/backend/src/main/java/io/metersphere/api/service/ApiScenarioReportService.java b/backend/src/main/java/io/metersphere/api/service/ApiScenarioReportService.java index 98a4830085..9879e89bb2 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiScenarioReportService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiScenarioReportService.java @@ -1,24 +1,67 @@ package io.metersphere.api.service; +import com.alibaba.fastjson.JSONObject; +import io.metersphere.api.dto.APIReportResult; import io.metersphere.api.jmeter.TestResult; +import io.metersphere.base.domain.ApiTestReportDetail; +import io.metersphere.commons.constants.APITestStatus; import io.metersphere.commons.exception.MSException; import io.metersphere.i18n.Translator; +import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import sun.security.util.Cache; +import java.nio.charset.StandardCharsets; + @Service @Transactional(rollbackFor = Exception.class) public class ApiScenarioReportService { private static Cache cache = Cache.newHardMemoryCache(0, 3600 * 24); - public void addResult(TestResult res) { - if (!res.getScenarios().isEmpty()) { - cache.put(res.getTestId(), res); - } else { - MSException.throwException(Translator.get("test_not_found")); + public void complete(TestResult result) { + Object obj = cache.get(result.getTestId()); + if (obj == null) { + MSException.throwException(Translator.get("api_report_is_null")); } + APIReportResult report = (APIReportResult) obj; + // report detail + ApiTestReportDetail detail = new ApiTestReportDetail(); + detail.setReportId(result.getTestId()); + detail.setTestId(report.getTestId()); + detail.setContent(JSONObject.toJSONString(result).getBytes(StandardCharsets.UTF_8)); + // report + report.setUpdateTime(System.currentTimeMillis()); + if (!StringUtils.equals(report.getStatus(), APITestStatus.Debug.name())) { + if (result.getError() > 0) { + report.setStatus(APITestStatus.Error.name()); + } else { + report.setStatus(APITestStatus.Success.name()); + } + } + report.setContent(new String(detail.getContent(), StandardCharsets.UTF_8)); + cache.put(report.getTestId(), report); } + public void addResult(APIReportResult res) { + cache.put(res.getTestId(), res); + } + + /** + * 获取零时执行报告 + * + * @param testId + */ + public APIReportResult getCacheResult(String testId) { + Object res = cache.get(testId); + if (res != null) { + APIReportResult reportResult = (APIReportResult) res; + if (!reportResult.getStatus().equals(APITestStatus.Running.name())) { + cache.remove(testId); + } + return reportResult; + } + return null; + } } diff --git a/backend/src/main/java/io/metersphere/base/domain/ApiDataView.java b/backend/src/main/java/io/metersphere/base/domain/ApiDataView.java new file mode 100644 index 0000000000..8399077381 --- /dev/null +++ b/backend/src/main/java/io/metersphere/base/domain/ApiDataView.java @@ -0,0 +1,24 @@ +package io.metersphere.base.domain; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class ApiDataView implements Serializable { + private String id; + + private String reportId; + + private String url; + + private String apiName; + + private String startTime; + + private String responseCode; + + private String responseTime; + + private static final long serialVersionUID = 1L; +} diff --git a/backend/src/main/java/io/metersphere/base/mapper/ApiDataViewMapper.java b/backend/src/main/java/io/metersphere/base/mapper/ApiDataViewMapper.java new file mode 100644 index 0000000000..48cf86c598 --- /dev/null +++ b/backend/src/main/java/io/metersphere/base/mapper/ApiDataViewMapper.java @@ -0,0 +1,23 @@ +package io.metersphere.base.mapper; + +import io.metersphere.api.dto.ApiMonitorSearch; +import io.metersphere.api.dto.ApiResponseCodeMonitor; +import io.metersphere.api.dto.ApiResponseTimeMonitor; +import io.metersphere.base.domain.ApiDataView; + +import java.util.List; + +public interface ApiDataViewMapper { + + List selectAll(); + + List selectResponseTimeByUrl(String url,String startTime,String endTime); + + List selectResponseCodeByUrl(String url,String startTime,String endTime); + + Integer insertListApiData(List list); + + Integer deleteByReportId(String reportId); + + String selectReportIdByUrlAndStartTime(String apiUrl,String startTime); +} \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/mapper/ApiDataViewMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ApiDataViewMapper.xml new file mode 100644 index 0000000000..671a97fdb5 --- /dev/null +++ b/backend/src/main/java/io/metersphere/base/mapper/ApiDataViewMapper.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + delete from api_data_view where report_id = #{reportId,jdbcType=VARCHAR} + + + + + + + + + + + insert into api_data_view(id, report_id, api_name,url, response_code, start_time,response_time) + values + + ( + #{item.id,jdbcType=VARCHAR}, + #{item.reportId,jdbcType=VARCHAR}, + #{item.apiName,jdbcType=VARCHAR}, + #{item.url,jdbcType=VARCHAR}, + #{item.responseCode,jdbcType=VARCHAR}, + #{item.startTime,jdbcType=VARCHAR}, + #{item.responseTime,jdbcType=VARCHAR} + ) + + + \ No newline at end of file diff --git a/backend/src/main/resources/db/migration/V47__api_data_view.sql b/backend/src/main/resources/db/migration/V47__api_data_view.sql new file mode 100644 index 0000000000..74ff59be4f --- /dev/null +++ b/backend/src/main/resources/db/migration/V47__api_data_view.sql @@ -0,0 +1,12 @@ +CREATE TABLE IF NOT EXISTS `api_data_view` +( + id varchar(50) NOT NULL primary key, + report_id varchar(255) NOT NULL, + api_name varchar(200) NULL, + url varchar(255) NULL, + response_code varchar(100) NULL, + start_time varchar(20) NULL, + response_time varchar(20) default '0' NULL, + create_time timestamp default CURRENT_TIMESTAMP NOT NULL, + update_time timestamp default CURRENT_TIMESTAMP NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; \ No newline at end of file diff --git a/frontend/src/business/components/api/automation/ApiAutomation.vue b/frontend/src/business/components/api/automation/ApiAutomation.vue index 4562ef85d4..d2fc8f5ead 100644 --- a/frontend/src/business/components/api/automation/ApiAutomation.vue +++ b/frontend/src/business/components/api/automation/ApiAutomation.vue @@ -21,7 +21,7 @@ :name="item.name" closable>
- +
@@ -37,77 +37,82 @@ diff --git a/frontend/src/business/components/api/automation/report/ApiReportDetail.vue b/frontend/src/business/components/api/automation/report/ApiReportDetail.vue new file mode 100644 index 0000000000..12a6858936 --- /dev/null +++ b/frontend/src/business/components/api/automation/report/ApiReportDetail.vue @@ -0,0 +1,220 @@ + + + + + + diff --git a/frontend/src/business/components/api/automation/report/ApiReportExport.vue b/frontend/src/business/components/api/automation/report/ApiReportExport.vue new file mode 100644 index 0000000000..5e4c6f54b8 --- /dev/null +++ b/frontend/src/business/components/api/automation/report/ApiReportExport.vue @@ -0,0 +1,131 @@ + + + + + diff --git a/frontend/src/business/components/api/automation/report/ApiReportReqestHeaderItem.vue b/frontend/src/business/components/api/automation/report/ApiReportReqestHeaderItem.vue new file mode 100644 index 0000000000..a9d096095d --- /dev/null +++ b/frontend/src/business/components/api/automation/report/ApiReportReqestHeaderItem.vue @@ -0,0 +1,31 @@ + + + + + diff --git a/frontend/src/business/components/api/automation/report/ApiReportStatus.vue b/frontend/src/business/components/api/automation/report/ApiReportStatus.vue new file mode 100644 index 0000000000..06e2464a07 --- /dev/null +++ b/frontend/src/business/components/api/automation/report/ApiReportStatus.vue @@ -0,0 +1,41 @@ + + + + + diff --git a/frontend/src/business/components/api/automation/report/ApiReportViewHeader.vue b/frontend/src/business/components/api/automation/report/ApiReportViewHeader.vue new file mode 100644 index 0000000000..ba02542881 --- /dev/null +++ b/frontend/src/business/components/api/automation/report/ApiReportViewHeader.vue @@ -0,0 +1,52 @@ + + + + + diff --git a/frontend/src/business/components/api/automation/report/components/AssertionResults.vue b/frontend/src/business/components/api/automation/report/components/AssertionResults.vue new file mode 100644 index 0000000000..f33a998781 --- /dev/null +++ b/frontend/src/business/components/api/automation/report/components/AssertionResults.vue @@ -0,0 +1,36 @@ + + + + + diff --git a/frontend/src/business/components/api/automation/report/components/MetricChart.vue b/frontend/src/business/components/api/automation/report/components/MetricChart.vue new file mode 100644 index 0000000000..3643ca5e4e --- /dev/null +++ b/frontend/src/business/components/api/automation/report/components/MetricChart.vue @@ -0,0 +1,255 @@ + + + + + diff --git a/frontend/src/business/components/api/automation/report/components/RequestMetric.vue b/frontend/src/business/components/api/automation/report/components/RequestMetric.vue new file mode 100644 index 0000000000..cdffc5d23d --- /dev/null +++ b/frontend/src/business/components/api/automation/report/components/RequestMetric.vue @@ -0,0 +1,106 @@ + + + + + diff --git a/frontend/src/business/components/api/automation/report/components/RequestResult.vue b/frontend/src/business/components/api/automation/report/components/RequestResult.vue new file mode 100644 index 0000000000..7a05c45919 --- /dev/null +++ b/frontend/src/business/components/api/automation/report/components/RequestResult.vue @@ -0,0 +1,144 @@ + + + + + diff --git a/frontend/src/business/components/api/automation/report/components/RequestResultTail.vue b/frontend/src/business/components/api/automation/report/components/RequestResultTail.vue new file mode 100644 index 0000000000..049a419b3f --- /dev/null +++ b/frontend/src/business/components/api/automation/report/components/RequestResultTail.vue @@ -0,0 +1,195 @@ + + + + + diff --git a/frontend/src/business/components/api/automation/report/components/RequestText.vue b/frontend/src/business/components/api/automation/report/components/RequestText.vue new file mode 100644 index 0000000000..5ade3d474d --- /dev/null +++ b/frontend/src/business/components/api/automation/report/components/RequestText.vue @@ -0,0 +1,73 @@ + + + + + diff --git a/frontend/src/business/components/api/automation/report/components/ResponseText.vue b/frontend/src/business/components/api/automation/report/components/ResponseText.vue new file mode 100644 index 0000000000..122bee9412 --- /dev/null +++ b/frontend/src/business/components/api/automation/report/components/ResponseText.vue @@ -0,0 +1,135 @@ + + + + + diff --git a/frontend/src/business/components/api/automation/report/components/ScenarioResult.vue b/frontend/src/business/components/api/automation/report/components/ScenarioResult.vue new file mode 100644 index 0000000000..34d3b2f4ac --- /dev/null +++ b/frontend/src/business/components/api/automation/report/components/ScenarioResult.vue @@ -0,0 +1,71 @@ + + + + + diff --git a/frontend/src/business/components/api/automation/report/components/ScenarioResults.vue b/frontend/src/business/components/api/automation/report/components/ScenarioResults.vue new file mode 100644 index 0000000000..a6b3585f13 --- /dev/null +++ b/frontend/src/business/components/api/automation/report/components/ScenarioResults.vue @@ -0,0 +1,35 @@ + + + + + diff --git a/frontend/src/business/components/api/automation/report/components/SqlResultTable.vue b/frontend/src/business/components/api/automation/report/components/SqlResultTable.vue new file mode 100644 index 0000000000..5406710eef --- /dev/null +++ b/frontend/src/business/components/api/automation/report/components/SqlResultTable.vue @@ -0,0 +1,121 @@ + + + + + diff --git a/frontend/src/business/components/api/automation/scenario/ApiScenarioList.vue b/frontend/src/business/components/api/automation/scenario/ApiScenarioList.vue index 676f4ab8c1..f4837a1249 100644 --- a/frontend/src/business/components/api/automation/scenario/ApiScenarioList.vue +++ b/frontend/src/business/components/api/automation/scenario/ApiScenarioList.vue @@ -64,6 +64,7 @@ import MsTablePagination from "@/business/components/common/pagination/TablePagination"; import ShowMoreBtn from "@/business/components/track/case/components/ShowMoreBtn"; import MsTag from "../../../common/components/MsTag"; + import {getUUID} from "@/common/js/utils"; export default { name: "MsApiScenarioList", @@ -101,7 +102,7 @@ }, methods: { search() { - this.condition.filters = ["Saved", "Success", "Fail"]; + this.condition.filters = ["Prepare", "Underway", "Completed"]; if (this.currentModule != null) { if (this.currentModule.id === "root") { this.condition.moduleIds = []; @@ -158,10 +159,11 @@ }, copy(row) { - + row.id = getUUID(); + this.$emit('edit', row); }, remove(row) { - if (this.currentModule !== undefined && this.currentModule.id === "gc") { + if (this.currentModule !== undefined && this.currentModule != null && this.currentModule.id === "gc") { this.$get('/api/automation/delete/' + row.id, () => { this.$success(this.$t('commons.delete_success')); this.search(); diff --git a/frontend/src/business/components/api/automation/scenario/EditApiScenario.vue b/frontend/src/business/components/api/automation/scenario/EditApiScenario.vue index eb347d45e2..13f9682169 100644 --- a/frontend/src/business/components/api/automation/scenario/EditApiScenario.vue +++ b/frontend/src/business/components/api/automation/scenario/EditApiScenario.vue @@ -132,9 +132,9 @@ {{$t('api_test.definition.request.run_env')}}: - + clearable> @@ -237,8 +237,8 @@ - {{$t('api_test.scenario.reference')}} - {{ $t('commons.copy') }} + + {{ $t('commons.copy') }} @@ -257,8 +257,12 @@ - + + + + @@ -283,6 +287,8 @@ import MsRun from "./Run"; import MsImportApiScenario from "./ImportApiScenario"; import MsApiScenarioComponent from "./ApiScenarioComponent"; + import MsApiReportDetail from "../report/ApiReportDetail"; + export default { name: "EditApiScenario", @@ -291,7 +297,7 @@ currentProject: {}, currentScenario: {}, }, - components: {ApiEnvironmentConfig, MsAddTag, MsRun, MsApiScenarioComponent, MsImportApiScenario, MsJsr233Processor, MsConstantTimer, MsIfController, MsApiAssertions, MsApiExtract, MsApiDefinition, MsApiComponent, MsApiCustomize}, + components: {ApiEnvironmentConfig, MsApiReportDetail, MsAddTag, MsRun, MsApiScenarioComponent, MsImportApiScenario, MsJsr233Processor, MsConstantTimer, MsIfController, MsApiAssertions, MsApiExtract, MsApiDefinition, MsApiComponent, MsApiCustomize}, data() { return { props: { @@ -309,7 +315,7 @@ }, environments: [], tags: [], - currentEnvironment: {}, + currentEnvironmentId: "", maintainerOptions: [], value: API_STATUS[0].id, options: API_STATUS, @@ -319,6 +325,7 @@ apiListVisible: false, customizeVisible: false, scenarioVisible: false, + debugVisible: false, customizeRequest: {protocol: "HTTP", type: "API", hashTree: [], referenced: 'Created', active: false}, operatingElements: [], currentRow: {cases: [], apis: []}, @@ -326,6 +333,7 @@ expandedNode: [], scenarioDefinition: [], path: "/api/automation/create", + debugData: [], reportId: "", } }, @@ -527,8 +535,14 @@ }, runDebug() { /*触发执行操作*/ + if (!this.currentEnvironmentId) { + this.$error(this.$t('api_test.environment.select_environment')); + return; + } + let scenario = {id: this.currentScenario.id, name: this.currentScenario.name, type: "scenario", referenced: 'Created', environmentId: this.currentEnvironmentId, hashTree: this.scenarioDefinition}; + this.debugData = []; + this.debugData.push(scenario); this.reportId = getUUID().substring(0, 8); - //this.isReloadData = true; }, getEnvironments() { if (this.currentProject) { @@ -547,14 +561,6 @@ } this.$refs.environmentConfig.open(this.currentProject.id); }, - environmentChange(value) { - for (let i in this.environments) { - if (this.environments[i].id === value) { - this.currentEnvironment = this.environments[i]; - break; - } - } - }, environmentConfigClose() { this.getEnvironments(); }, @@ -599,14 +605,18 @@ }) }, getApiScenario() { - if (this.currentScenario.tagId != undefined) { + if (this.currentScenario.tagId != undefined && !(this.currentScenario.tagId instanceof Array)) { this.currentScenario.tagId = JSON.parse(this.currentScenario.tagId); } if (this.currentScenario.id) { - this.path = "/api/automation/update"; this.result = this.$get("/api/automation/getApiScenario/" + this.currentScenario.id, response => { if (response.data) { - this.scenarioDefinition = JSON.parse(response.data.scenarioDefinition); + this.path = "/api/automation/update"; + if (response.data.scenarioDefinition != null) { + let obj = JSON.parse(response.data.scenarioDefinition); + this.currentEnvironmentId = obj.environmentId; + this.scenarioDefinition = obj.hashTree; + } } }) } @@ -614,11 +624,13 @@ setParameter() { this.currentScenario.projectId = this.currentProject.id; if (!this.currentScenario.id) { - this.currentScenario.id = getUUID().substring(0, 8); + this.currentScenario.id = getUUID(); } this.currentScenario.stepTotal = this.scenarioDefinition.length; this.currentScenario.modulePath = this.getPath(this.currentScenario.apiScenarioModuleId); - this.currentScenario.scenarioDefinition = JSON.stringify(this.scenarioDefinition); + // 构建一个场景对象 方便引用处理 + let scenario = {id: this.currentScenario.id, name: this.currentScenario.name, type: "scenario", referenced: 'Created', environmentId: this.currentEnvironmentId, hashTree: this.scenarioDefinition}; + this.currentScenario.scenarioDefinition = JSON.stringify(scenario); this.currentScenario.tagId = JSON.stringify(this.currentScenario.tagId); if (this.currentModule != null) { this.currentScenario.modulePath = this.currentModule.method !== undefined ? this.currentModule.method : null; @@ -626,6 +638,7 @@ } }, runRefresh() { + this.debugVisible = true; this.isReloadData = false; } } diff --git a/frontend/src/business/components/api/automation/scenario/ImportApiScenario.vue b/frontend/src/business/components/api/automation/scenario/ImportApiScenario.vue index a1750aa22c..713283bd41 100644 --- a/frontend/src/business/components/api/automation/scenario/ImportApiScenario.vue +++ b/frontend/src/business/components/api/automation/scenario/ImportApiScenario.vue @@ -56,6 +56,7 @@ importApiScenario() { let scenarios = []; if (this.currentScenario) { + console.log(this.currentScenario) this.currentScenario.forEach(item => { let obj = {id: item.id, name: item.name, type: "scenario", referenced: 'REF', resourceId: getUUID()}; scenarios.push(obj); diff --git a/frontend/src/business/components/api/automation/scenario/Run.vue b/frontend/src/business/components/api/automation/scenario/Run.vue index fd7f1f36a4..055123b5df 100644 --- a/frontend/src/business/components/api/automation/scenario/Run.vue +++ b/frontend/src/business/components/api/automation/scenario/Run.vue @@ -10,7 +10,7 @@ name: 'MsRun', components: {}, props: { - environment: Object, + environment: String, debug: Boolean, reportId: String, runData: Array, @@ -96,26 +96,21 @@ }, run() { let testPlan = new TestPlan(); - let threadGroup = new ThreadGroup(); - threadGroup.hashTree = []; - testPlan.hashTree = [threadGroup]; this.runData.forEach(item => { + let threadGroup = new ThreadGroup(); + threadGroup.hashTree = []; + threadGroup.name = item.name; threadGroup.hashTree.push(item); + testPlan.hashTree.push(threadGroup); }) - let reqObj = {id: this.reportId, testElement: testPlan}; + console.log("====",testPlan) + let reqObj = {id: this.reportId, reportId: this.reportId, environmentId: this.environment, testElement: testPlan}; let bodyFiles = this.getBodyUploadFiles(reqObj); - let url = ""; - if (this.debug) { - url = "/api/automation/run/debug"; - } else { - reqObj.reportId = "run"; - url = "/api/definition/run"; - } + let url = "/api/automation/run"; this.$fileUpload(url, null, bodyFiles, reqObj, response => { this.runId = response.data; - this.getResult(); - }, erro => { this.$emit('runRefresh', {}); + }, erro => { }); } } diff --git a/frontend/src/business/components/api/definition/components/jmeter/components/test-plan/index.js b/frontend/src/business/components/api/definition/components/jmeter/components/test-plan/index.js index 6d0caf218b..d849d93f1e 100644 --- a/frontend/src/business/components/api/definition/components/jmeter/components/test-plan/index.js +++ b/frontend/src/business/components/api/definition/components/jmeter/components/test-plan/index.js @@ -18,7 +18,7 @@ export default class TestPlan extends HashTreeElement { this.serializeThreadGroups = this.initBoolProp('TestPlan.serialize_threadgroups', false); this.tearDownOnShutdown = this.initBoolProp('TestPlan.tearDown_on_shutdown', true); this.userDefineClasspath = this.initStringProp('TestPlan.user_define_classpath'); - + this.hashTree = []; this.userDefinedVariables = []; let elementProp = this.initElementProp('TestPlan.user_defined_variables', 'Arguments'); diff --git a/frontend/src/business/components/api/head/ApiHeaderMenus.vue b/frontend/src/business/components/api/head/ApiHeaderMenus.vue index 532283844c..6450671fe2 100644 --- a/frontend/src/business/components/api/head/ApiHeaderMenus.vue +++ b/frontend/src/business/components/api/head/ApiHeaderMenus.vue @@ -40,6 +40,11 @@ + + + + {{ $t('commons.monitor') }} + diff --git a/frontend/src/business/components/api/monitor/ApiMonitor.vue b/frontend/src/business/components/api/monitor/ApiMonitor.vue new file mode 100644 index 0000000000..e5dab060db --- /dev/null +++ b/frontend/src/business/components/api/monitor/ApiMonitor.vue @@ -0,0 +1,168 @@ + + + + + diff --git a/frontend/src/business/components/api/monitor/ApiMonitorChart.vue b/frontend/src/business/components/api/monitor/ApiMonitorChart.vue new file mode 100644 index 0000000000..c7e2b1a3e8 --- /dev/null +++ b/frontend/src/business/components/api/monitor/ApiMonitorChart.vue @@ -0,0 +1,46 @@ + + + + + diff --git a/frontend/src/business/components/api/monitor/ApiMonitorSearch.vue b/frontend/src/business/components/api/monitor/ApiMonitorSearch.vue new file mode 100644 index 0000000000..d97a9a2d33 --- /dev/null +++ b/frontend/src/business/components/api/monitor/ApiMonitorSearch.vue @@ -0,0 +1,100 @@ + + + + + diff --git a/frontend/src/business/components/api/monitor/components/ApiErrorMonitorChart.vue b/frontend/src/business/components/api/monitor/components/ApiErrorMonitorChart.vue new file mode 100644 index 0000000000..ca7903e471 --- /dev/null +++ b/frontend/src/business/components/api/monitor/components/ApiErrorMonitorChart.vue @@ -0,0 +1,93 @@ + + + + + diff --git a/frontend/src/business/components/api/monitor/components/ApiResponseTimeMonitorChart.vue b/frontend/src/business/components/api/monitor/components/ApiResponseTimeMonitorChart.vue new file mode 100644 index 0000000000..dccd45060e --- /dev/null +++ b/frontend/src/business/components/api/monitor/components/ApiResponseTimeMonitorChart.vue @@ -0,0 +1,64 @@ + + + + + diff --git a/frontend/src/business/components/api/monitor/components/CommonMonitorChart.vue b/frontend/src/business/components/api/monitor/components/CommonMonitorChart.vue new file mode 100644 index 0000000000..868373a31e --- /dev/null +++ b/frontend/src/business/components/api/monitor/components/CommonMonitorChart.vue @@ -0,0 +1,33 @@ + + + + + + + diff --git a/frontend/src/business/components/api/report/components/ResponseText.vue b/frontend/src/business/components/api/report/components/ResponseText.vue index d960e179f8..9cf4cb00b1 100644 --- a/frontend/src/business/components/api/report/components/ResponseText.vue +++ b/frontend/src/business/components/api/report/components/ResponseText.vue @@ -81,7 +81,7 @@ export default { if (!this.response.headers) { return; } - if (this.response.headers.indexOf("Content-Type: application/json") > 0) { + if (this.response.headers.indexOf("application/json") > 0) { this.mode = BODY_FORMAT.JSON; } }, diff --git a/frontend/src/business/components/api/report/components/SqlResultTable.vue b/frontend/src/business/components/api/report/components/SqlResultTable.vue index 5406710eef..20c499c0f8 100644 --- a/frontend/src/business/components/api/report/components/SqlResultTable.vue +++ b/frontend/src/business/components/api/report/components/SqlResultTable.vue @@ -118,4 +118,12 @@ max-height: 500px; } + .el-table >>> th { + -webkit-user-select: text; + -khtml-user-select: text; + -moz-user-select: text; + -ms-user-select: text; + user-select: text; + } + diff --git a/frontend/src/business/components/performance/report/components/PerformancePressureConfig.vue b/frontend/src/business/components/performance/report/components/PerformancePressureConfig.vue index 096146e3d3..18c67b8af8 100644 --- a/frontend/src/business/components/performance/report/components/PerformancePressureConfig.vue +++ b/frontend/src/business/components/performance/report/components/PerformancePressureConfig.vue @@ -226,8 +226,9 @@ export default { if (handler.rampUpTime < handler.step) { handler.step = handler.rampUpTime; } + let color = ['#60acfc', '#32d3eb', '#5bc49f', '#feb64d', '#ff7c7c', '#9287e7', '#ca8622', '#bda29a', '#6e7074', '#546570', '#c4ccd3']; handler.options = { - color: ['#60acfc', '#32d3eb', '#5bc49f', '#feb64d', '#ff7c7c', '#9287e7', '#ca8622', '#bda29a', '#6e7074', '#546570', '#c4ccd3'], + color: color, xAxis: { type: 'category', boundaryGap: false, @@ -261,10 +262,10 @@ export default { normal: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ offset: 0, - color: hexToRgba(handler.options.color[i], 0.3), + color: hexToRgba(color[i % color.length], 0.3), }, { offset: 0.8, - color: hexToRgba(handler.options.color[i], 0), + color: hexToRgba(color[i % color.length], 0), }], false), shadowColor: 'rgba(0, 0, 0, 0.1)', shadowBlur: 10 @@ -272,7 +273,7 @@ export default { }, itemStyle: { normal: { - color: hexToRgb(handler.options.color[i]), + color: hexToRgb(color[i % color.length]), borderColor: 'rgba(137,189,2,0.27)', borderWidth: 12 } diff --git a/frontend/src/business/components/performance/test/components/PerformancePressureConfig.vue b/frontend/src/business/components/performance/test/components/PerformancePressureConfig.vue index 5d98032542..c531212baa 100644 --- a/frontend/src/business/components/performance/test/components/PerformancePressureConfig.vue +++ b/frontend/src/business/components/performance/test/components/PerformancePressureConfig.vue @@ -268,8 +268,9 @@ export default { if (handler.rampUpTime < handler.step) { handler.step = handler.rampUpTime; } + let color = ['#60acfc', '#32d3eb', '#5bc49f', '#feb64d', '#ff7c7c', '#9287e7', '#ca8622', '#bda29a', '#6e7074', '#546570', '#c4ccd3']; handler.options = { - color: ['#60acfc', '#32d3eb', '#5bc49f', '#feb64d', '#ff7c7c', '#9287e7', '#ca8622', '#bda29a', '#6e7074', '#546570', '#c4ccd3'], + color: color, xAxis: { type: 'category', boundaryGap: false, @@ -302,10 +303,10 @@ export default { normal: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ offset: 0, - color: hexToRgba(handler.options.color[i], 0.3), + color: hexToRgba(color[i % color.length], 0.3), }, { offset: 0.8, - color: hexToRgba(handler.options.color[i], 0), + color: hexToRgba(color[i % color.length], 0), }], false), shadowColor: 'rgba(0, 0, 0, 0.1)', shadowBlur: 10 @@ -313,7 +314,7 @@ export default { }, itemStyle: { normal: { - color: hexToRgb(handler.options.color[i]), + color: hexToRgb(color[i % color.length]), borderColor: 'rgba(137,189,2,0.27)', borderWidth: 12 } diff --git a/frontend/src/common/js/ajax.js b/frontend/src/common/js/ajax.js index 619bd04afd..e8c3e7f929 100644 --- a/frontend/src/common/js/ajax.js +++ b/frontend/src/common/js/ajax.js @@ -74,12 +74,12 @@ export default { } } - Vue.prototype.$$get = function (url, data, success) { + Vue.prototype.$$get = function (url, data, header, success) { let result = {loading: true}; if (!success) { - return axios.get(url, {params: data}); + return axios.get(url, {params: data, headers: header}); } else { - axios.get(url, {params: data}).then(response => { + axios.get(url, {params: data, headers: header}).then(response => { then(success, response, result); }).catch(error => { exception(error, result, url); diff --git a/frontend/src/common/js/chart.js b/frontend/src/common/js/chart.js index f2ec0b5317..5e26e1bcc6 100644 --- a/frontend/src/common/js/chart.js +++ b/frontend/src/common/js/chart.js @@ -4,6 +4,8 @@ import 'echarts/lib/chart/bar' import 'echarts/lib/chart/pie' import 'echarts/lib/component/tooltip' import 'echarts/lib/component/title' +import 'echarts/lib/component/toolbox'; +import 'echarts/lib/component/dataZoom'; import 'zrender/lib/svg/svg' export default { diff --git a/frontend/src/common/js/format-utils.js b/frontend/src/common/js/format-utils.js index 1c1206b6fd..e207b6df90 100644 --- a/frontend/src/common/js/format-utils.js +++ b/frontend/src/common/js/format-utils.js @@ -120,6 +120,42 @@ export function formatXml(text) { return outputText.replace(/\s+$/g, '').replace(/\r/g, '\r\n'); } +/** + * @param time 时间 + * @param cFormat 格式 + * @returns {string|null} 字符串 + * @example formatTime('2018-1-29', '{y}/{m}/{d} {h}:{i}:{s}') // -> 2018/01/29 00:00:00 + */ +export function formatTime(time, cFormat) { + if (arguments.length === 0) return null; + if ((time + '').length === 10) { + time = +time * 1000; + } + let format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}', date; + if (typeof time === 'object') { + date = time; + } else { + date = new Date(time); + } + let formatObj = { + y: date.getFullYear(), + m: date.getMonth() + 1, + d: date.getDate(), + h: date.getHours(), + i: date.getMinutes(), + s: date.getSeconds(), + a: date.getDay() + }; + return format.replace(/{([ymdhisa])+}/g, (result, key) => { + let value = formatObj[key]; + if (key === 'a') return ['一', '二', '三', '四', '五', '六', '日'][value - 1]; + if (result.length > 0 && value < 10) { + value = '0' + value; + } + return value || 0; + }); +} + function getPrefix(prefixIndex) { var span = ' '; var output = []; diff --git a/frontend/src/i18n/en-US.js b/frontend/src/i18n/en-US.js index 349a3fb058..4b38945ed0 100644 --- a/frontend/src/i18n/en-US.js +++ b/frontend/src/i18n/en-US.js @@ -162,7 +162,8 @@ export default { between: "Between", current_user: "Current user" } - } + }, + monitor:"monitor" }, license: { title: 'Authorization management', @@ -777,6 +778,16 @@ export default { running: "The test is reporting", not_exist: "Test report does not exist", }, + api_monitor: { + to:"to", + start_time:"Start Time", + end_time:"End Time", + today:"Today", + this_week:"This Week", + this_mouth:"This Mouth", + please_search:"Please Search", + date:"Date" + }, test_track: { test_track: "Track", confirm: "Confirm", diff --git a/frontend/src/i18n/zh-CN.js b/frontend/src/i18n/zh-CN.js index af7b53fe08..b5e4c1fdbd 100644 --- a/frontend/src/i18n/zh-CN.js +++ b/frontend/src/i18n/zh-CN.js @@ -162,7 +162,8 @@ export default { between: "之间", current_user: "是当前用户" } - } + }, + monitor: "监控" }, license: { title: '授权管理', @@ -807,6 +808,16 @@ export default { running: "测试报告导出中", not_exist: "测试报告不存在", }, + api_monitor: { + to:"至", + start_time:"开始日期", + end_time:"结束日期", + today:"今日", + this_week:"本周", + this_mouth:"本月", + please_search:"请搜索", + date:"日期" + }, test_track: { test_track: "测试跟踪", confirm: "确 定", diff --git a/frontend/src/i18n/zh-TW.js b/frontend/src/i18n/zh-TW.js index 92aa5e92fe..eaf646e206 100644 --- a/frontend/src/i18n/zh-TW.js +++ b/frontend/src/i18n/zh-TW.js @@ -162,7 +162,8 @@ export default { between: "之間", current_user: "是當前用戶" } - } + }, + monitor:"監控" }, license: { title: '授權管理', @@ -780,6 +781,16 @@ export default { running: "測試報告導出中", not_exist: "測試報告不存在", }, + api_monitor: { + to:"到", + start_time:"開始時間", + end_time:"結束時間", + today:"今天", + this_week:"本週", + this_mouth:"本月", + please_search:"請搜索", + date:"日期" + }, test_track: { test_track: "測試跟蹤", confirm: "確 定",