From 4a9ccc2ea591b2c0c1c4de868e69af00384dd7c7 Mon Sep 17 00:00:00 2001 From: "Captain.B" Date: Tue, 2 Mar 2021 17:20:22 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E6=80=A7=E8=83=BD=E6=B5=8B=E8=AF=95):=20?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E7=BB=84=E5=90=88=E5=9C=BA=E6=99=AF=E5=9C=BA?= =?UTF-8?q?=E6=99=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../base/mapper/ext/ExtLoadTestMapper.java | 4 + .../base/mapper/ext/ExtLoadTestMapper.xml | 12 + .../commons/utils/UrlTestUtils.java | 20 +- .../io/metersphere/dto/LoadTestFileDTO.java | 12 + .../controller/PerformanceTestController.java | 9 + .../xml/reader/jmx/JmeterDocumentParser.java | 20 + .../service/PerformanceTestService.java | 81 +++- .../io/metersphere/service/FileService.java | 6 + .../request/testplan/SaveTestPlanRequest.java | 10 + .../request/testplan/TestPlanRequest.java | 2 +- .../components/PerformancePressureConfig.vue | 171 ++++---- .../report/components/TestOverview.vue | 2 +- .../performance/test/EditPerformanceTest.vue | 7 +- .../components/PerformanceBasicConfig.vue | 384 ++++++++++++++++-- .../components/PerformancePressureConfig.vue | 99 ++--- .../performance/test/model/ThreadGroup.js | 17 +- frontend/src/i18n/en-US.js | 19 +- frontend/src/i18n/zh-CN.js | 19 +- frontend/src/i18n/zh-TW.js | 19 +- 19 files changed, 681 insertions(+), 232 deletions(-) create mode 100644 backend/src/main/java/io/metersphere/dto/LoadTestFileDTO.java diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtLoadTestMapper.java b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtLoadTestMapper.java index 9ea652a93f..6c95dc1a2b 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtLoadTestMapper.java +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtLoadTestMapper.java @@ -2,6 +2,7 @@ package io.metersphere.base.mapper.ext; import io.metersphere.base.domain.LoadTest; import io.metersphere.dto.LoadTestDTO; +import io.metersphere.dto.LoadTestFileDTO; import io.metersphere.track.request.testplan.QueryTestPlanRequest; import org.apache.ibatis.annotations.Param; @@ -16,4 +17,7 @@ public interface ExtLoadTestMapper { int checkLoadTestOwner(@Param("testId") String testId, @Param("workspaceIds") Set workspaceIds); LoadTest getNextNum(@Param("projectId") String projectId); + + List getProjectFiles(@Param("projectId") String projectId, @Param("loadTypes") List loadType); + } diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtLoadTestMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtLoadTestMapper.xml index 0282187863..885b380e80 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtLoadTestMapper.xml +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtLoadTestMapper.xml @@ -131,4 +131,16 @@ + + \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/commons/utils/UrlTestUtils.java b/backend/src/main/java/io/metersphere/commons/utils/UrlTestUtils.java index d536cf484d..ce72089a50 100644 --- a/backend/src/main/java/io/metersphere/commons/utils/UrlTestUtils.java +++ b/backend/src/main/java/io/metersphere/commons/utils/UrlTestUtils.java @@ -1,20 +1,24 @@ package io.metersphere.commons.utils; +import java.net.HttpURLConnection; import java.net.URL; -import java.net.URLConnection; public class UrlTestUtils { - public static boolean testUrlWithTimeOut(String urlString, int timeOutMillSeconds) { + public static boolean testUrlWithTimeOut(String address, int timeOutMillSeconds) { try { - URL url = new URL(urlString); - URLConnection co = url.openConnection(); - co.setConnectTimeout(timeOutMillSeconds); - co.connect(); - return true; + URL urlObj = new URL(address); + HttpURLConnection oc = (HttpURLConnection) urlObj.openConnection(); + oc.setUseCaches(false); + oc.setConnectTimeout(timeOutMillSeconds); // 设置超时时间 + int status = oc.getResponseCode();// 请求状态 + if (200 == status) { + return true; + } } catch (Exception e) { - LogUtil.error(e); + e.printStackTrace(); return false; } + return false; } } diff --git a/backend/src/main/java/io/metersphere/dto/LoadTestFileDTO.java b/backend/src/main/java/io/metersphere/dto/LoadTestFileDTO.java new file mode 100644 index 0000000000..a5f9522e10 --- /dev/null +++ b/backend/src/main/java/io/metersphere/dto/LoadTestFileDTO.java @@ -0,0 +1,12 @@ +package io.metersphere.dto; + +import io.metersphere.base.domain.FileMetadata; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class LoadTestFileDTO extends FileMetadata { + private String testId; + private String testName; +} diff --git a/backend/src/main/java/io/metersphere/performance/controller/PerformanceTestController.java b/backend/src/main/java/io/metersphere/performance/controller/PerformanceTestController.java index 5abb10fc2f..30d23051a3 100644 --- a/backend/src/main/java/io/metersphere/performance/controller/PerformanceTestController.java +++ b/backend/src/main/java/io/metersphere/performance/controller/PerformanceTestController.java @@ -12,6 +12,7 @@ import io.metersphere.commons.utils.SessionUtils; import io.metersphere.controller.request.QueryScheduleRequest; import io.metersphere.dto.DashboardTestDTO; import io.metersphere.dto.LoadTestDTO; +import io.metersphere.dto.LoadTestFileDTO; import io.metersphere.dto.ScheduleDao; import io.metersphere.performance.service.PerformanceTestService; import io.metersphere.service.CheckPermissionService; @@ -112,6 +113,14 @@ public class PerformanceTestController { return performanceTestService.getJmxContent(testId); } + @GetMapping("/project/{loadType}/{projectId}/{goPage}/{pageSize}") + public Pager> getProjectFiles(@PathVariable String projectId, @PathVariable String loadType, + @PathVariable int goPage, @PathVariable int pageSize) { + checkPermissionService.checkProjectOwner(projectId); + Page page = PageHelper.startPage(goPage, pageSize, true); + return PageUtils.setPageInfo(page, performanceTestService.getProjectFiles(projectId, loadType)); + } + @PostMapping("/delete") public void delete(@RequestBody DeleteTestPlanRequest request) { checkPermissionService.checkPerformanceTestOwner(request.getId()); diff --git a/backend/src/main/java/io/metersphere/performance/parse/xml/reader/jmx/JmeterDocumentParser.java b/backend/src/main/java/io/metersphere/performance/parse/xml/reader/jmx/JmeterDocumentParser.java index 8c956b18f5..0af5537b3d 100644 --- a/backend/src/main/java/io/metersphere/performance/parse/xml/reader/jmx/JmeterDocumentParser.java +++ b/backend/src/main/java/io/metersphere/performance/parse/xml/reader/jmx/JmeterDocumentParser.java @@ -9,6 +9,7 @@ import io.metersphere.config.KafkaProperties; import io.metersphere.i18n.Translator; import io.metersphere.performance.engine.EngineContext; import io.metersphere.performance.parse.xml.reader.DocumentParser; +import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -639,6 +640,25 @@ public class JmeterDocumentParser implements DocumentParser { ((List) holds).remove(0); hold = o.toString(); } + Object deleteds = context.getProperty("deleted"); + String deleted = "false"; + if (deleteds instanceof List) { + Object o = ((List) deleteds).get(0); + ((List) deleteds).remove(0); + deleted = o.toString(); + } + Object enableds = context.getProperty("enabled"); + String enabled = "true"; + if (enableds instanceof List) { + Object o = ((List) enableds).get(0); + ((List) enableds).remove(0); + enabled = o.toString(); + } + + threadGroup.setAttribute("enabled", enabled); + if (BooleanUtils.toBoolean(deleted)) { + threadGroup.setAttribute("enabled", "false"); + } Element elementProp = document.createElement("elementProp"); elementProp.setAttribute("name", "ThreadGroup.main_controller"); elementProp.setAttribute("elementType", "com.blazemeter.jmeter.control.VirtualUserController"); diff --git a/backend/src/main/java/io/metersphere/performance/service/PerformanceTestService.java b/backend/src/main/java/io/metersphere/performance/service/PerformanceTestService.java index f6e66c6347..f8e2ccf152 100644 --- a/backend/src/main/java/io/metersphere/performance/service/PerformanceTestService.java +++ b/backend/src/main/java/io/metersphere/performance/service/PerformanceTestService.java @@ -16,6 +16,7 @@ import io.metersphere.controller.request.OrderRequest; import io.metersphere.controller.request.QueryScheduleRequest; import io.metersphere.dto.DashboardTestDTO; import io.metersphere.dto.LoadTestDTO; +import io.metersphere.dto.LoadTestFileDTO; import io.metersphere.dto.ScheduleDao; import io.metersphere.i18n.Translator; import io.metersphere.job.sechedule.PerformanceTestJob; @@ -38,6 +39,7 @@ import javax.annotation.Resource; import java.io.OutputStream; import java.net.InetSocketAddress; import java.net.Socket; +import java.nio.charset.StandardCharsets; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.*; @@ -126,21 +128,54 @@ public class PerformanceTestService { } public String save(SaveTestPlanRequest request, List files) { - if (files == null) { - throw new IllegalArgumentException(Translator.get("file_cannot_be_null")); - } checkQuota(request, true); - final LoadTestWithBLOBs loadTest = saveLoadTest(request); - files.forEach(file -> { - final FileMetadata fileMetadata = fileService.saveFile(file); + LoadTestWithBLOBs loadTest = saveLoadTest(request); + + // 新选择了一个文件,删除原来的文件 + List importFiles = request.getUpdatedFileList(); + List importFileIds = importFiles.stream().map(FileMetadata::getId).collect(Collectors.toList()); + // 导入项目里其他的文件 + this.importFiles(importFileIds, loadTest.getId()); + // 保存上传的问题件 + this.saveUploadFiles(files, loadTest.getId()); + // 直接上传了jmx,用于API导入的场景 + String jmx = request.getJmx(); + if (StringUtils.isNotBlank(jmx)) { + byte[] bytes = jmx.getBytes(StandardCharsets.UTF_8); + FileMetadata fileMetadata = fileService.saveFile(bytes, request.getName() + ".jmx", (long) bytes.length); LoadTestFile loadTestFile = new LoadTestFile(); loadTestFile.setTestId(loadTest.getId()); loadTestFile.setFileId(fileMetadata.getId()); loadTestFileMapper.insert(loadTestFile); - }); + } return loadTest.getId(); } + private void saveUploadFiles(List files, String testId) { + if (files != null) { + files.forEach(file -> { + final FileMetadata fileMetadata = fileService.saveFile(file); + LoadTestFile loadTestFile = new LoadTestFile(); + loadTestFile.setTestId(testId); + loadTestFile.setFileId(fileMetadata.getId()); + loadTestFileMapper.insert(loadTestFile); + }); + } + } + + private void importFiles(List importFileIds, String testId) { + importFileIds.forEach(fileId -> { + if (StringUtils.isBlank(fileId)) { + return; + } + FileMetadata fileMetadata = fileService.copyFile(fileId); + LoadTestFile loadTestFile = new LoadTestFile(); + loadTestFile.setTestId(testId); + loadTestFile.setFileId(fileMetadata.getId()); + loadTestFileMapper.insert(loadTestFile); + }); + } + private LoadTestWithBLOBs saveLoadTest(SaveTestPlanRequest request) { LoadTestExample example = new LoadTestExample(); @@ -183,15 +218,19 @@ public class PerformanceTestService { // 相减 List deleteFileIds = ListUtils.subtract(originFileIds, updatedFileIds); fileService.deleteFileByIds(deleteFileIds); - - if (files != null) { - files.forEach(file -> { - final FileMetadata fileMetadata = fileService.saveFile(file); - LoadTestFile loadTestFile = new LoadTestFile(); - loadTestFile.setTestId(request.getId()); - loadTestFile.setFileId(fileMetadata.getId()); - loadTestFileMapper.insert(loadTestFile); - }); + // 导入项目里其他的文件 + List addFileIds = ListUtils.subtract(updatedFileIds, originFileIds); + this.importFiles(addFileIds, request.getId()); + this.saveUploadFiles(files, request.getId()); + // 直接上传了jmx,用于API导入的场景 + String jmx = request.getJmx(); + if (StringUtils.isNotBlank(jmx)) { + byte[] bytes = jmx.getBytes(StandardCharsets.UTF_8); + FileMetadata fileMetadata = fileService.saveFile(bytes, request.getName() + ".jmx", (long) bytes.length); + LoadTestFile loadTestFile = new LoadTestFile(); + loadTestFile.setTestId(request.getId()); + loadTestFile.setFileId(fileMetadata.getId()); + loadTestFileMapper.insert(loadTestFile); } loadTest.setName(request.getName()); @@ -508,4 +547,14 @@ public class PerformanceTestService { return Optional.of(loadTest.getNum() + 1).orElse(100001); } } + + public List getProjectFiles(String projectId, String loadType) { + List loadTypes = new ArrayList<>(); + loadTypes.add(StringUtils.upperCase(loadType)); + if (StringUtils.equalsIgnoreCase(loadType, "resource")) { + loadTypes.add(FileType.CSV.name()); + loadTypes.add(FileType.JAR.name()); + } + return extLoadTestMapper.getProjectFiles(projectId, loadTypes); + } } diff --git a/backend/src/main/java/io/metersphere/service/FileService.java b/backend/src/main/java/io/metersphere/service/FileService.java index 08d3fa0748..4938f950e9 100644 --- a/backend/src/main/java/io/metersphere/service/FileService.java +++ b/backend/src/main/java/io/metersphere/service/FileService.java @@ -169,4 +169,10 @@ public class FileService { public FileMetadata getFileMetadataById(String fileId) { return fileMetadataMapper.selectByPrimaryKey(fileId); } + + public List getProjectJMXs(String projectId) { + FileMetadataExample example = new FileMetadataExample(); + fileMetadataMapper.selectByExample(example); + return null; + } } \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/track/request/testplan/SaveTestPlanRequest.java b/backend/src/main/java/io/metersphere/track/request/testplan/SaveTestPlanRequest.java index 60481f480e..cf7753a964 100644 --- a/backend/src/main/java/io/metersphere/track/request/testplan/SaveTestPlanRequest.java +++ b/backend/src/main/java/io/metersphere/track/request/testplan/SaveTestPlanRequest.java @@ -1,4 +1,14 @@ package io.metersphere.track.request.testplan; +import io.metersphere.base.domain.FileMetadata; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Setter +@Getter public class SaveTestPlanRequest extends TestPlanRequest { + private List updatedFileList; + } diff --git a/backend/src/main/java/io/metersphere/track/request/testplan/TestPlanRequest.java b/backend/src/main/java/io/metersphere/track/request/testplan/TestPlanRequest.java index 4155783918..89de645106 100644 --- a/backend/src/main/java/io/metersphere/track/request/testplan/TestPlanRequest.java +++ b/backend/src/main/java/io/metersphere/track/request/testplan/TestPlanRequest.java @@ -16,7 +16,7 @@ public class TestPlanRequest { private String description; - private String scenarioDefinition; + private String jmx; private Long createTime; diff --git a/frontend/src/business/components/performance/report/components/PerformancePressureConfig.vue b/frontend/src/business/components/performance/report/components/PerformancePressureConfig.vue index 234783b9d2..2592f89233 100644 --- a/frontend/src/business/components/performance/report/components/PerformancePressureConfig.vue +++ b/frontend/src/business/components/performance/report/components/PerformancePressureConfig.vue @@ -8,17 +8,17 @@ + :disabled="true" + :placeholder="$t('load_test.input_thread_num')" + v-model="threadGroup.threadNumber" + :min="1" + size="mini"/>
@@ -31,72 +31,72 @@
+ :disabled="true" + v-model="threadGroup.duration" + :min="1" + @change="calculateChart(threadGroup)" + size="mini"/>
  + :disabled="true " + v-model="threadGroup.rpsLimit" + @change="calculateChart(threadGroup)" + :min="1" + size="mini"/>
+ :disabled="true" + :min="1" + :max="threadGroup.duration" + v-model="threadGroup.rampUpTime" + @change="calculateChart(threadGroup)" + size="mini"/> + :disabled="true" + :min="1" + :max="Math.min(threadGroup.threadNumber, threadGroup.rampUpTime)" + v-model="threadGroup.step" + @change="calculateChart(threadGroup)" + size="mini"/>
+ :disabled="true" + v-model="threadGroup.iterateNum" + :min="1" + @change="calculateChart(threadGroup)" + size="mini"/>
  + :disabled="true || !threadGroup.rpsLimitEnable" + v-model="threadGroup.rpsLimit" + @change="calculateChart(threadGroup)" + :min="1" + size="mini"/>
+ :disabled="true" + :min="1" + v-model="threadGroup.iterateRampUp" + @change="calculateChart(threadGroup)" + size="mini"/>
@@ -126,14 +126,16 @@ const RPS_LIMIT_ENABLE = "rpsLimitEnable"; const THREAD_TYPE = "threadType"; const ITERATE_NUM = "iterateNum"; const ITERATE_RAMP_UP = "iterateRampUpTime"; +const ENABLED = "enabled"; +const DELETED = "deleted"; const hexToRgba = function (hex, opacity) { return 'rgba(' + parseInt('0x' + hex.slice(1, 3)) + ',' + parseInt('0x' + hex.slice(3, 5)) + ',' - + parseInt('0x' + hex.slice(5, 7)) + ',' + opacity + ')'; + + parseInt('0x' + hex.slice(5, 7)) + ',' + opacity + ')'; } const hexToRgb = function (hex) { return 'rgb(' + parseInt('0x' + hex.slice(1, 3)) + ',' + parseInt('0x' + hex.slice(3, 5)) - + ',' + parseInt('0x' + hex.slice(5, 7)) + ')'; + + ',' + parseInt('0x' + hex.slice(5, 7)) + ')'; } export default { @@ -163,83 +165,51 @@ export default { calculateLoadConfiguration: function (data) { for (let i = 0; i < data.length; i++) { let d = data[i]; - if (d instanceof Array) { - d.forEach(item => { - switch (item.key) { - case TARGET_LEVEL: - this.threadGroups[i].threadNumber = item.value; - break; - case RAMP_UP: - this.threadGroups[i].rampUpTime = item.value; - break; - case ITERATE_RAMP_UP: - this.threadGroups[i].iterateRampUp = item.value; - break; - case DURATION: - if (item.unit) { - this.threadGroups[i].duration = item.value; - } else { - this.threadGroups[i].duration = item.value * 60; - } - break; - case STEPS: - this.threadGroups[i].step = item.value; - break; - case RPS_LIMIT: - this.threadGroups[i].rpsLimit = item.value; - break; - case RPS_LIMIT_ENABLE: - this.threadGroups[i].rpsLimitEnable = item.value; - break; - case THREAD_TYPE: - this.threadGroups[i].threadType = item.value; - break; - case ITERATE_NUM: - this.threadGroups[i].iterateNum = item.value; - break; - default: - break; - } - }) - this.calculateChart(this.threadGroups[i]); - } else { - switch (d.key) { + d.forEach(item => { + switch (item.key) { case TARGET_LEVEL: - this.threadGroups[0].threadNumber = d.value; + this.threadGroups[i].threadNumber = item.value; break; case RAMP_UP: - this.threadGroups[0].rampUpTime = d.value; + this.threadGroups[i].rampUpTime = item.value; break; case ITERATE_RAMP_UP: - this.threadGroups[0].iterateRampUp = d.value; + this.threadGroups[i].iterateRampUp = item.value; break; case DURATION: - if (d.unit) { - this.threadGroups[0].duration = d.value; + if (item.unit) { + this.threadGroups[i].duration = item.value; } else { - this.threadGroups[0].duration = d.value * 60; + this.threadGroups[i].duration = item.value * 60; } break; case STEPS: - this.threadGroups[0].step = d.value; + this.threadGroups[i].step = item.value; break; case RPS_LIMIT: - this.threadGroups[0].rpsLimit = d.value; + this.threadGroups[i].rpsLimit = item.value; break; case RPS_LIMIT_ENABLE: - this.threadGroups[0].rpsLimitEnable = d.value; + this.threadGroups[i].rpsLimitEnable = item.value; break; case THREAD_TYPE: - this.threadGroups[0].threadType = d.value; + this.threadGroups[i].threadType = item.value; break; case ITERATE_NUM: - this.threadGroups[0].iterateNum = d.value; + this.threadGroups[i].iterateNum = item.value; + break; + case ENABLED: + this.threadGroups[i].enabled = item.value; + break; + case DELETED: + this.threadGroups[i].deleted = item.value; break; default: break; } - this.calculateChart(this.threadGroups[0]); - } + }) + this.calculateChart(this.threadGroups[i]); + } }, getLoadConfig() { @@ -307,6 +277,9 @@ export default { for (let i = 0; i < handler.threadGroups.length; i++) { + if (handler.threadGroups[i].enabled === 'false' || handler.threadGroups[i].deleted === 'true') { + continue; + } let seriesData = { name: handler.threadGroups[i].attributes.testname, data: [], diff --git a/frontend/src/business/components/performance/report/components/TestOverview.vue b/frontend/src/business/components/performance/report/components/TestOverview.vue index 4f72deedd4..50574c67d4 100644 --- a/frontend/src/business/components/performance/report/components/TestOverview.vue +++ b/frontend/src/business/components/performance/report/components/TestOverview.vue @@ -14,7 +14,7 @@ {{ avgTransactions }} - Transactions/s + TPS Avg.Transactions diff --git a/frontend/src/business/components/performance/test/EditPerformanceTest.vue b/frontend/src/business/components/performance/test/EditPerformanceTest.vue index 61c6f721bb..499b82bbd8 100644 --- a/frontend/src/business/components/performance/test/EditPerformanceTest.vue +++ b/frontend/src/business/components/performance/test/EditPerformanceTest.vue @@ -8,7 +8,7 @@ class="input-with-select" maxlength="30" show-word-limit > - +
@@ -32,6 +32,7 @@ @@ -305,6 +306,9 @@ export default { this.$set(handler, "threadGroups", threadGroups); + this.$refs.basicConfig.threadGroups = threadGroups; + this.$refs.pressureConfig.threadGroups = threadGroups; + threadGroups.forEach(tg => { handler.calculateChart(tg); }) @@ -317,7 +321,6 @@ export default { .testplan-config { margin-top: 15px; - text-align: center; } .el-select { diff --git a/frontend/src/business/components/performance/test/components/PerformanceBasicConfig.vue b/frontend/src/business/components/performance/test/components/PerformanceBasicConfig.vue index a88dcc6448..1c3849924d 100644 --- a/frontend/src/business/components/performance/test/components/PerformanceBasicConfig.vue +++ b/frontend/src/business/components/performance/test/components/PerformanceBasicConfig.vue @@ -1,25 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +