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 464eb869fb..fd7706dff5 100644 --- a/backend/src/main/java/io/metersphere/api/controller/ApiAutomationController.java +++ b/backend/src/main/java/io/metersphere/api/controller/ApiAutomationController.java @@ -5,37 +5,26 @@ import com.github.pagehelper.PageHelper; import io.metersphere.api.dto.JmxInfoDTO; import io.metersphere.api.dto.automation.*; import io.metersphere.api.dto.definition.RunDefinitionRequest; -import io.metersphere.api.dto.definition.request.*; -import io.metersphere.api.dto.scenario.KeyValue; import io.metersphere.api.service.ApiAutomationService; import io.metersphere.base.domain.ApiScenario; import io.metersphere.base.domain.ApiScenarioWithBLOBs; import io.metersphere.base.domain.Schedule; -import io.metersphere.commons.constants.ReportTriggerMode; import io.metersphere.commons.constants.RoleConstants; -import io.metersphere.commons.exception.MSException; -import io.metersphere.commons.utils.LogUtil; import io.metersphere.commons.utils.PageUtils; import io.metersphere.commons.utils.Pager; import io.metersphere.commons.utils.SessionUtils; -import io.metersphere.i18n.Translator; -import io.metersphere.performance.service.PerformanceTestService; import io.metersphere.track.request.testcase.ApiCaseRelevanceRequest; -import io.metersphere.track.request.testplan.SaveTestPlanRequest; -import org.apache.commons.lang3.StringUtils; -import org.apache.jmeter.save.SaveService; -import org.apache.jorphan.collections.HashTree; -import org.apache.jorphan.collections.ListedHashTree; +import io.metersphere.track.request.testplan.FileOperationRequest; import org.apache.shiro.authz.annotation.Logical; import org.apache.shiro.authz.annotation.RequiresRoles; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; -import java.io.ByteArrayOutputStream; -import java.util.LinkedList; import java.util.List; -import java.util.UUID; @RestController @RequestMapping(value = "/api/automation") @@ -44,9 +33,6 @@ public class ApiAutomationController { @Resource ApiAutomationService apiAutomationService; - @Resource - PerformanceTestService performanceTestService; - @PostMapping("/list/{goPage}/{pageSize}") @RequiresRoles(value = {RoleConstants.TEST_MANAGER, RoleConstants.TEST_USER, RoleConstants.TEST_VIEWER}, logical = Logical.OR) @@ -145,5 +131,14 @@ public class ApiAutomationController { runRequest.setExecuteType(ExecuteType.Completed.name()); return apiAutomationService.genPerformanceTestJmx(runRequest); } + + @PostMapping("/file/download") + public ResponseEntity download(@RequestBody FileOperationRequest fileOperationRequest) { + byte[] bytes = apiAutomationService.loadFileAsBytes(fileOperationRequest); + return ResponseEntity.ok() + .contentType(MediaType.parseMediaType("application/octet-stream")) + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileOperationRequest.getName() + "\"") + .body(bytes); + } } 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 19097a14bf..5d576dece2 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 @@ -7,6 +7,7 @@ import com.alibaba.fastjson.annotation.JSONType; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; +import io.metersphere.api.dto.definition.request.variable.ScenarioVariable; import io.metersphere.api.dto.scenario.KeyValue; import io.metersphere.api.dto.scenario.environment.EnvironmentConfig; import io.metersphere.api.service.ApiAutomationService; @@ -19,12 +20,16 @@ import lombok.EqualsAndHashCode; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.apache.jmeter.config.Arguments; +import org.apache.jmeter.config.CSVDataSet; +import org.apache.jmeter.config.RandomVariableConfig; +import org.apache.jmeter.modifiers.CounterConfig; import org.apache.jmeter.save.SaveService; import org.apache.jmeter.testelement.TestElement; import org.apache.jorphan.collections.HashTree; import java.util.LinkedList; import java.util.List; +import java.util.stream.Collectors; @Data @EqualsAndHashCode(callSuper = true) @@ -42,11 +47,13 @@ public class MsScenario extends MsTestElement { private String environmentId; @JSONField(ordinal = 23) - private List variables; + private List variables; @JSONField(ordinal = 24) private boolean enableCookieShare; + private static final String BODY_FILE_DIR = "/opt/metersphere/data/body"; + public void toHashTree(HashTree tree, List hashTree, ParameterConfig config) { if (!this.isEnable()) { return; @@ -84,7 +91,9 @@ public class MsScenario extends MsTestElement { } // 场景变量和环境变量 tree.add(arguments(config)); - + addCsvDataSet(tree); + addCounter(tree); + addRandom(tree); if (CollectionUtils.isNotEmpty(hashTree)) { for (MsTestElement el : hashTree) { el.toHashTree(tree, el.getHashTree(), config); @@ -92,6 +101,13 @@ public class MsScenario extends MsTestElement { } } + public void setOldVariables(List oldVariables) { + if (CollectionUtils.isNotEmpty(oldVariables)) { + String json = JSON.toJSONString(oldVariables); + this.variables = JSON.parseArray(json, ScenarioVariable.class); + } + } + private Arguments arguments(ParameterConfig config) { Arguments arguments = new Arguments(); arguments.setEnabled(true); @@ -99,7 +115,7 @@ public class MsScenario extends MsTestElement { arguments.setProperty(TestElement.TEST_CLASS, Arguments.class.getName()); arguments.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("ArgumentsPanel")); if (CollectionUtils.isNotEmpty(this.getVariables())) { - variables.stream().filter(KeyValue::isValid).filter(KeyValue::isEnable).forEach(keyValue -> + variables.stream().filter(ScenarioVariable::isConstantValid).forEach(keyValue -> arguments.addArgument(keyValue.getName(), keyValue.getValue(), "=") ); } @@ -111,4 +127,72 @@ public class MsScenario extends MsTestElement { } return arguments; } + + private void addCsvDataSet(HashTree tree) { + if (CollectionUtils.isNotEmpty(this.getVariables())) { + List list = variables.stream().filter(ScenarioVariable::isCSVValid).collect(Collectors.toList()); + if (CollectionUtils.isNotEmpty(list)) { + list.forEach(item -> { + CSVDataSet csvDataSet = new CSVDataSet(); + csvDataSet.setEnabled(true); + csvDataSet.setProperty(TestElement.TEST_CLASS, CSVDataSet.class.getName()); + csvDataSet.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("TestBeanGUI")); + csvDataSet.setName(item.getName()); + csvDataSet.setProperty("fileEncoding", item.getEncoding()); + csvDataSet.setProperty("variableNames", item.getName()); + if (CollectionUtils.isNotEmpty(item.getFiles())) { + csvDataSet.setProperty("filename", BODY_FILE_DIR + "/" + item.getFiles().get(0).getId() + "_" + item.getFiles().get(0).getName()); + } + csvDataSet.setIgnoreFirstLine(false); + csvDataSet.setProperty("delimiter", item.getDelimiter()); + csvDataSet.setComment(item.getDescription()); + tree.add(csvDataSet); + }); + } + } + } + + private void addCounter(HashTree tree) { + if (CollectionUtils.isNotEmpty(this.getVariables())) { + List list = variables.stream().filter(ScenarioVariable::isCounterValid).collect(Collectors.toList()); + if (CollectionUtils.isNotEmpty(list)) { + list.forEach(item -> { + CounterConfig counterConfig = new CounterConfig(); + counterConfig.setEnabled(true); + counterConfig.setProperty(TestElement.TEST_CLASS, CounterConfig.class.getName()); + counterConfig.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("CounterConfigGui")); + counterConfig.setName(item.getName()); + counterConfig.setStart(item.getStartNumber()); + counterConfig.setEnd(item.getEndNumber()); + counterConfig.setVarName(item.getName()); + counterConfig.setIncrement(item.getIncrement()); + counterConfig.setFormat(item.getValue()); + counterConfig.setComment(item.getDescription()); + tree.add(counterConfig); + }); + } + } + } + + private void addRandom(HashTree tree) { + if (CollectionUtils.isNotEmpty(this.getVariables())) { + List list = variables.stream().filter(ScenarioVariable::isRandom).collect(Collectors.toList()); + if (CollectionUtils.isNotEmpty(list)) { + list.forEach(item -> { + RandomVariableConfig randomVariableConfig = new RandomVariableConfig(); + randomVariableConfig.setEnabled(true); + randomVariableConfig.setProperty(TestElement.TEST_CLASS, RandomVariableConfig.class.getName()); + randomVariableConfig.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("TestBeanGUI")); + randomVariableConfig.setName(item.getName()); + randomVariableConfig.setProperty("variableName", item.getName()); + randomVariableConfig.setProperty("outputFormat", item.getValue()); + randomVariableConfig.setProperty("minimumValue", item.getMinNumber()); + randomVariableConfig.setProperty("maximumValue", item.getMaxNumber()); + randomVariableConfig.setComment(item.getDescription()); + tree.add(randomVariableConfig); + }); + } + } + } + } diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/ParameterConfig.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/ParameterConfig.java index fc938e4dbd..9f790cc7a9 100644 --- a/backend/src/main/java/io/metersphere/api/dto/definition/request/ParameterConfig.java +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/ParameterConfig.java @@ -1,6 +1,6 @@ package io.metersphere.api.dto.definition.request; -import io.metersphere.api.dto.scenario.KeyValue; +import io.metersphere.api.dto.definition.request.variable.ScenarioVariable; import io.metersphere.api.dto.scenario.environment.EnvironmentConfig; import lombok.Data; @@ -11,7 +11,7 @@ public class ParameterConfig { // 环境配置 private EnvironmentConfig config; // 公共场景参数 - private List variables; + private List variables; // 公共Cookie private boolean enableCookieShare; diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/variable/EnumVariableType.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/variable/EnumVariableType.java new file mode 100644 index 0000000000..062b67a356 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/variable/EnumVariableType.java @@ -0,0 +1,5 @@ +package io.metersphere.api.dto.definition.request.variable; + +public enum EnumVariableType { + CONSTANT, LIST, CSV, COUNTER, RANDOM, +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/variable/ScenarioVariable.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/variable/ScenarioVariable.java new file mode 100644 index 0000000000..b5fc7d4ee7 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/variable/ScenarioVariable.java @@ -0,0 +1,58 @@ +package io.metersphere.api.dto.definition.request.variable; + +import io.metersphere.api.dto.scenario.request.BodyFile; +import lombok.Data; +import org.apache.commons.lang3.StringUtils; + +import java.util.List; + +@Data +public class ScenarioVariable { + + // CONSTANT LIST CSV COUNTER RANDOM + private String type; + private String id; + private String name; + // 常量值,列表值[] ,计数器输出格式,随机数输出格式 + private String value; + private String description; + // csv + private List files; + private String delimiter; + private String encoding; + // counter + private int startNumber; + private int endNumber; + private int increment; + // random + private String minNumber; + private String maxNumber; + + public boolean isConstantValid() { + if ((StringUtils.equals(this.type, EnumVariableType.CONSTANT.name()) || StringUtils.equals(this.type, EnumVariableType.LIST.name())) && StringUtils.isNotEmpty(name) && StringUtils.isNotEmpty(value)) { + return true; + } + return false; + } + + public boolean isCSVValid() { + if (StringUtils.equals(this.type, EnumVariableType.CSV.name()) && StringUtils.isNotEmpty(name)) { + return true; + } + return false; + } + + public boolean isCounterValid() { + if (StringUtils.equals(this.type, EnumVariableType.COUNTER.name()) && StringUtils.isNotEmpty(name)) { + return true; + } + return false; + } + + public boolean isRandom() { + if (StringUtils.equals(this.type, EnumVariableType.RANDOM.name()) && StringUtils.isNotEmpty(name)) { + return true; + } + return false; + } +} 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 f45a8d38ba..7aab0f9c7b 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java @@ -11,7 +11,7 @@ import io.metersphere.api.dto.automation.*; import io.metersphere.api.dto.datacount.ApiDataCountResult; import io.metersphere.api.dto.definition.RunDefinitionRequest; import io.metersphere.api.dto.definition.request.*; -import io.metersphere.api.dto.scenario.KeyValue; +import io.metersphere.api.dto.definition.request.variable.ScenarioVariable; import io.metersphere.api.dto.scenario.environment.EnvironmentConfig; import io.metersphere.api.jmeter.JMeterService; import io.metersphere.base.domain.*; @@ -30,18 +30,16 @@ import io.metersphere.commons.utils.ServiceUtils; import io.metersphere.commons.utils.SessionUtils; import io.metersphere.i18n.Translator; import io.metersphere.job.sechedule.ApiScenarioTestJob; -import io.metersphere.performance.service.PerformanceTestService; import io.metersphere.service.ScheduleService; import io.metersphere.track.dto.TestPlanDTO; import io.metersphere.track.request.testcase.ApiCaseRelevanceRequest; import io.metersphere.track.request.testcase.QueryTestPlanRequest; -import io.metersphere.track.request.testplan.SaveTestPlanRequest; +import io.metersphere.track.request.testplan.FileOperationRequest; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.apache.ibatis.session.ExecutorType; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; -import org.apache.jmeter.save.SaveService; import org.apache.jorphan.collections.HashTree; import org.apache.jorphan.collections.ListedHashTree; import org.springframework.stereotype.Service; @@ -50,6 +48,8 @@ import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; import java.util.*; import java.util.stream.Collectors; @@ -78,15 +78,13 @@ public class ApiAutomationService { SqlSessionFactory sqlSessionFactory; @Resource private ApiScenarioReportMapper apiScenarioReportMapper; - @Resource - private PerformanceTestService performanceTestService; public List list(ApiScenarioRequest request) { request.setOrders(ServiceUtils.getDefaultOrder(request.getOrders())); - if(request.isSelectThisWeedData()){ + if (request.isSelectThisWeedData()) { Map weekFirstTimeAndLastTime = DateUtils.getWeedFirstTimeAndLastTime(new Date()); Date weekFirstTime = weekFirstTimeAndLastTime.get("firstTime"); - if(weekFirstTime!=null){ + if (weekFirstTime != null) { request.setCreateTime(weekFirstTime.getTime()); } } @@ -285,6 +283,22 @@ public class ApiAutomationService { return new ArrayList<>(); } + public byte[] loadFileAsBytes(FileOperationRequest fileOperationRequest) { + File file = new File("/opt/metersphere/data/body/" + fileOperationRequest.getId() + "_" + fileOperationRequest.getName()); + try (FileInputStream fis = new FileInputStream(file); + ByteArrayOutputStream bos = new ByteArrayOutputStream(1000);) { + byte[] b = new byte[1000]; + int n; + while ((n = fis.read(b)) != -1) { + bos.write(b, 0, n); + } + return bos.toByteArray(); + } catch (Exception ex) { + LogUtil.error(ex.getMessage()); + } + return null; + } + private void createScenarioReport(String id, String scenarioId, String scenarioName, String triggerMode, String execType, String projectId, String userID) { APIScenarioReportResult report = new APIScenarioReportResult(); report.setId(id); @@ -354,12 +368,14 @@ public class ApiAutomationService { // 多态JSON普通转换会丢失内容,需要通过 ObjectMapper 获取 if (element != null && StringUtils.isNotEmpty(element.getString("hashTree"))) { LinkedList elements = mapper.readValue(element.getString("hashTree"), - new TypeReference>() {}); + new TypeReference>() { + }); scenario.setHashTree(elements); } if (StringUtils.isNotEmpty(element.getString("variables"))) { - LinkedList variables = mapper.readValue(element.getString("variables"), - new TypeReference>() {}); + LinkedList variables = mapper.readValue(element.getString("variables"), + new TypeReference>() { + }); scenario.setVariables(variables); } group.setEnableCookieShare(scenario.isEnableCookieShare()); @@ -592,8 +608,8 @@ public class ApiAutomationService { scenario.setHashTree(elements); } if (StringUtils.isNotEmpty(element.getString("variables"))) { - LinkedList variables = mapper.readValue(element.getString("variables"), - new TypeReference>() { + LinkedList variables = mapper.readValue(element.getString("variables"), + new TypeReference>() { }); scenario.setVariables(variables); } @@ -602,7 +618,6 @@ public class ApiAutomationService { scenarios.add(scenario); group.setHashTree(scenarios); testPlan.getHashTree().add(group); - } } catch (Exception ex) { MSException.throwException(ex.getMessage()); @@ -615,6 +630,6 @@ public class ApiAutomationService { JmxInfoDTO dto = new JmxInfoDTO(); dto.setName(name); dto.setXml(jmx); - return dto; + return dto; } } diff --git a/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java b/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java index 5e27a4e8c8..bfe437de64 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java @@ -13,7 +13,7 @@ import io.metersphere.api.dto.datacount.ApiDataCountResult; import io.metersphere.api.dto.definition.*; import io.metersphere.api.dto.definition.parse.ApiDefinitionImport; import io.metersphere.api.dto.definition.request.*; -import io.metersphere.api.dto.scenario.KeyValue; +import io.metersphere.api.dto.definition.request.variable.ScenarioVariable; import io.metersphere.api.dto.scenario.request.RequestType; import io.metersphere.api.jmeter.JMeterService; import io.metersphere.api.jmeter.TestResult; @@ -367,8 +367,8 @@ public class ApiDefinitionService { scenario.setHashTree(elements); } if (StringUtils.isNotEmpty(element.getString("variables"))) { - LinkedList variables = mapper.readValue(element.getString("variables"), - new TypeReference>() { + LinkedList variables = mapper.readValue(element.getString("variables"), + new TypeReference>() { }); scenario.setVariables(variables); } @@ -624,4 +624,4 @@ public class ApiDefinitionService { } } } -} +} \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/api/service/HistoricalDataUpgradeService.java b/backend/src/main/java/io/metersphere/api/service/HistoricalDataUpgradeService.java index f9b028c29a..1b0e36675b 100644 --- a/backend/src/main/java/io/metersphere/api/service/HistoricalDataUpgradeService.java +++ b/backend/src/main/java/io/metersphere/api/service/HistoricalDataUpgradeService.java @@ -66,7 +66,7 @@ public class HistoricalDataUpgradeService { private MsScenario createScenario(Scenario oldScenario) { MsScenario scenario = new MsScenario(); - scenario.setVariables(oldScenario.getVariables()); + scenario.setOldVariables(oldScenario.getVariables()); scenario.setName(oldScenario.getName()); scenario.setEnableCookieShare(oldScenario.isEnableCookieShare()); scenario.setEnvironmentId(oldScenario.getEnvironmentId()); diff --git a/backend/src/main/java/io/metersphere/controller/UserController.java b/backend/src/main/java/io/metersphere/controller/UserController.java index 07317ffa39..0548ee872f 100644 --- a/backend/src/main/java/io/metersphere/controller/UserController.java +++ b/backend/src/main/java/io/metersphere/controller/UserController.java @@ -136,6 +136,10 @@ public class UserController { @PostMapping("/update/current") public UserDTO updateCurrentUser(@RequestBody User user) { + String currentUserId = SessionUtils.getUserId(); + if (!StringUtils.equals(currentUserId, user.getId())) { + MSException.throwException(Translator.get("not_authorized")); + } userService.updateUser(user); UserDTO userDTO = userService.getUserDTO(user.getId()); SessionUtils.putUser(SessionUser.fromUser(userDTO)); diff --git a/backend/src/main/java/io/metersphere/service/UserService.java b/backend/src/main/java/io/metersphere/service/UserService.java index c0e1228fc4..dc851daff0 100644 --- a/backend/src/main/java/io/metersphere/service/UserService.java +++ b/backend/src/main/java/io/metersphere/service/UserService.java @@ -332,7 +332,7 @@ public class UserService { MSException.throwException(Translator.get("user_email_already_exists")); } } - + user.setPassword(null); user.setUpdateTime(System.currentTimeMillis()); userMapper.updateByPrimaryKeySelective(user); // 禁用用户之后,剔除在线用户 diff --git a/backend/src/main/java/io/metersphere/xpack b/backend/src/main/java/io/metersphere/xpack index 068127ce59..9f4a9bbf46 160000 --- a/backend/src/main/java/io/metersphere/xpack +++ b/backend/src/main/java/io/metersphere/xpack @@ -1 +1 @@ -Subproject commit 068127ce59ea8b016434ed52a9de4a7a4b13bdb4 +Subproject commit 9f4a9bbf46fc1333dbcccea21f83e27e3ec10b1f diff --git a/frontend/package.json b/frontend/package.json index fc9ac7b6bd..a0f2d44465 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -39,6 +39,7 @@ "vue-float-action-button": "^0.6.6", "vue-i18n": "^8.15.3", "vue-input-tag": "^2.0.7", + "vue-papa-parse": "^2.0.0", "vue-pdf": "^4.2.0", "vue-router": "^3.1.3", "vuedraggable": "^2.23.2", diff --git a/frontend/src/business/components/api/automation/scenario/EditApiScenario.vue b/frontend/src/business/components/api/automation/scenario/EditApiScenario.vue index 6c54d52d4b..6587cdbc44 100644 --- a/frontend/src/business/components/api/automation/scenario/EditApiScenario.vue +++ b/frontend/src/business/components/api/automation/scenario/EditApiScenario.vue @@ -107,7 +107,7 @@ {{$t('api_test.automation.scenario_total')}} - :{{this.currentScenario.variables!=undefined?this.currentScenario.variables.length-1: 0}} + :{{this.currentScenario.variables!=undefined?this.currentScenario.variables.length: 0}} 共享cookie @@ -164,9 +164,9 @@ - + - + @@ -218,7 +218,7 @@ - + @@ -245,7 +245,7 @@ import MsLoopController from "./LoopController"; import MsApiScenarioComponent from "./ApiScenarioComponent"; import MsApiReportDetail from "../report/ApiReportDetail"; - import MsScenarioParameters from "./ScenarioParameters"; + import MsVariableList from "./variable/VariableList"; import ApiImport from "../../definition/components/import/ApiImport"; import InputTag from 'vue-input-tag' import "@/common/css/material-icons.css" @@ -260,10 +260,10 @@ currentScenario: {}, }, components: { + MsVariableList, ScenarioRelevance, ScenarioApiRelevance, ApiEnvironmentConfig, - MsScenarioParameters, MsApiReportDetail, MsInputTag, MsRun, MsApiScenarioComponent, @@ -319,7 +319,8 @@ enableCookieShare: false, globalOptions: { spacing: 30 - } + }, + response: {} } } , @@ -331,10 +332,7 @@ this.operatingElements = ELEMENTS.get("ALL"); this.getMaintainerOptions(); this.getApiScenario(); - } - , - watch: {} - , + }, directives: {OutsideClick}, computed: { buttons() { @@ -455,6 +453,13 @@ getIdx(index) { return index - 0.33 }, + setVariables(v) { + this.currentScenario.variables = v; + if (this.path.endsWith("/update")) { + // 直接更新场景防止编辑内容丢失 + this.editScenario(); + } + }, showButton(...names) { for (const name of names) { if (this.operatingElements.includes(name)) { @@ -530,6 +535,12 @@ this.selectedTreeNode = e; } , + suggestClick(node) { + this.response = {}; + if (node.parent && node.parent.data.requestResult) { + this.response = node.parent.data.requestResult; + } + }, showAll() { this.operatingElements = ELEMENTS.get("ALL"); this.selectedTreeNode = undefined; @@ -675,12 +686,17 @@ this.$error(this.$t('api_test.environment.select_environment')); return; } - this.debugData = { - id: this.currentScenario.id, name: this.currentScenario.name, type: "scenario", - variables: this.currentScenario.variables, referenced: 'Created', enableCookieShare: this.enableCookieShare, - environmentId: this.currentEnvironmentId, hashTree: this.scenarioDefinition - }; - this.reportId = getUUID().substring(0, 8); + this.$refs['currentScenario'].validate((valid) => { + if (valid) { + this.editScenario(); + this.debugData = { + id: this.currentScenario.id, name: this.currentScenario.name, type: "scenario", + variables: this.currentScenario.variables, referenced: 'Created', enableCookieShare: this.enableCookieShare, + environmentId: this.currentEnvironmentId, hashTree: this.scenarioDefinition + }; + this.reportId = getUUID().substring(0, 8); + } + }) }, getEnvironments() { if (this.projectId) { @@ -801,15 +817,33 @@ this.recursiveFile(item.hashTree, bodyUploadFiles, obj); } }) + // 场景变量csv 文件 + this.currentScenario.variables.forEach(param => { + if (param.type === 'CSV' && param.files) { + param.files.forEach(item => { + if (item.file) { + if (!item.id) { + let fileId = getUUID().substring(0, 12); + item.name = item.file.name; + item.id = fileId; + } + obj.bodyUploadIds.push(item.id); + bodyUploadFiles.push(item.file); + } + }) + } + }) return bodyUploadFiles; }, - editScenario() { + editScenario(showMessage) { this.$refs['currentScenario'].validate((valid) => { if (valid) { this.setParameter(); let bodyFiles = this.getBodyUploadFiles(this.currentScenario); this.$fileUpload(this.path, null, bodyFiles, this.currentScenario, response => { - this.$success(this.$t('commons.save_success')); + if (showMessage) { + this.$success(this.$t('commons.save_success')); + } this.path = "/api/automation/update"; if (response.data) { this.currentScenario.id = response.data.id; @@ -834,7 +868,20 @@ let obj = JSON.parse(response.data.scenarioDefinition); if (obj) { this.currentEnvironmentId = obj.environmentId; - this.currentScenario.variables = obj.variables; + this.currentScenario.variables = []; + let index = 1; + obj.variables.forEach(item => { + // 兼容历史数据 + if (item.name) { + if (!item.type) { + item.type = "CONSTANT"; + item.id = getUUID(); + } + item.num = index; + this.currentScenario.variables.push(item); + index++; + } + }) this.enableCookieShare = obj.enableCookieShare; this.scenarioDefinition = obj.hashTree; } @@ -846,8 +893,7 @@ this.getEnvironments(); }) } - } - , + }, setParameter() { this.currentScenario.stepTotal = this.scenarioDefinition.length; this.currentScenario.projectId = getCurrentProjectID(); @@ -871,17 +917,10 @@ runRefresh() { this.debugVisible = true; this.loading = false; - } - , + }, showScenarioParameters() { this.$refs.scenarioParameters.open(this.currentScenario.variables); - } - , - addParameters(data) { - this.currentScenario.variables = data; - this.reload(); - } - , + }, apiImport(importData) { if (importData && importData.data) { importData.data.forEach(item => { @@ -909,11 +948,6 @@ background-color: white; } - .ms-opt-btn { - float: right; - margin-right: 20px; - } - .ms-debug-div { border: 1px #DCDFE6 solid; border-radius: 4px; @@ -997,9 +1031,8 @@ font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", Arial, sans-serif; font-size: 13px; } - .ms-opt-btn { position: fixed; - right: 20px; + right: 50px; } diff --git a/frontend/src/business/components/api/automation/scenario/ScenarioParameters.vue b/frontend/src/business/components/api/automation/scenario/ScenarioParameters.vue deleted file mode 100644 index b250fbd08d..0000000000 --- a/frontend/src/business/components/api/automation/scenario/ScenarioParameters.vue +++ /dev/null @@ -1,55 +0,0 @@ - - - - - diff --git a/frontend/src/business/components/api/automation/scenario/api/RelevanceApiList.vue b/frontend/src/business/components/api/automation/scenario/api/RelevanceApiList.vue index e1bc26836a..e7ff7740f0 100644 --- a/frontend/src/business/components/api/automation/scenario/api/RelevanceApiList.vue +++ b/frontend/src/business/components/api/automation/scenario/api/RelevanceApiList.vue @@ -185,7 +185,6 @@ export default { this.condition.projectId = this.projectId; } else { this.condition.projectId = getCurrentProjectID(); - } if (this.currentProtocol != null) { this.condition.protocol = this.currentProtocol; diff --git a/frontend/src/business/components/api/automation/scenario/variable/CsvFileUpload.vue b/frontend/src/business/components/api/automation/scenario/variable/CsvFileUpload.vue new file mode 100644 index 0000000000..cc1876beda --- /dev/null +++ b/frontend/src/business/components/api/automation/scenario/variable/CsvFileUpload.vue @@ -0,0 +1,125 @@ + + + + + diff --git a/frontend/src/business/components/api/automation/scenario/variable/EditConstant.vue b/frontend/src/business/components/api/automation/scenario/variable/EditConstant.vue new file mode 100644 index 0000000000..092979f24c --- /dev/null +++ b/frontend/src/business/components/api/automation/scenario/variable/EditConstant.vue @@ -0,0 +1,33 @@ + + + + + diff --git a/frontend/src/business/components/api/automation/scenario/variable/EditCounter.vue b/frontend/src/business/components/api/automation/scenario/variable/EditCounter.vue new file mode 100644 index 0000000000..866ca7a900 --- /dev/null +++ b/frontend/src/business/components/api/automation/scenario/variable/EditCounter.vue @@ -0,0 +1,41 @@ + + + + + diff --git a/frontend/src/business/components/api/automation/scenario/variable/EditCsv.vue b/frontend/src/business/components/api/automation/scenario/variable/EditCsv.vue new file mode 100644 index 0000000000..54529a99fc --- /dev/null +++ b/frontend/src/business/components/api/automation/scenario/variable/EditCsv.vue @@ -0,0 +1,129 @@ + + + + + diff --git a/frontend/src/business/components/api/automation/scenario/variable/EditListValue.vue b/frontend/src/business/components/api/automation/scenario/variable/EditListValue.vue new file mode 100644 index 0000000000..7dcdf99c62 --- /dev/null +++ b/frontend/src/business/components/api/automation/scenario/variable/EditListValue.vue @@ -0,0 +1,33 @@ + + + + + diff --git a/frontend/src/business/components/api/automation/scenario/variable/EditRandom.vue b/frontend/src/business/components/api/automation/scenario/variable/EditRandom.vue new file mode 100644 index 0000000000..d93d29ad16 --- /dev/null +++ b/frontend/src/business/components/api/automation/scenario/variable/EditRandom.vue @@ -0,0 +1,40 @@ + + + + + diff --git a/frontend/src/business/components/api/automation/scenario/variable/VariableList.vue b/frontend/src/business/components/api/automation/scenario/variable/VariableList.vue new file mode 100644 index 0000000000..271b815c87 --- /dev/null +++ b/frontend/src/business/components/api/automation/scenario/variable/VariableList.vue @@ -0,0 +1,156 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/assertion/ApiAssertions.vue b/frontend/src/business/components/api/definition/components/assertion/ApiAssertions.vue index 556a826cc6..da1b8a8856 100644 --- a/frontend/src/business/components/api/definition/components/assertion/ApiAssertions.vue +++ b/frontend/src/business/components/api/definition/components/assertion/ApiAssertions.vue @@ -57,7 +57,7 @@ - + @@ -111,7 +111,6 @@ type: "", loading: false, reloadData: "", - suggestData: {} } }, @@ -125,17 +124,14 @@ this.$emit('copyRow', this.assertions, this.node); }, suggestJsonOpen() { - if (!this.response || !this.response.responseResult || !this.response.responseResult.body) { - this.$message(this.$t('api_test.request.assertions.debug_first')); - return; - } - try { - this.suggestData = JSON.parse(this.response.responseResult.body); - } catch (e) { - this.$error(this.$t('api_test.request.assertions.json_path_err')); - return; - } - this.$refs.jsonpathSuggest.open(); + this.$emit('suggestClick'); + this.$nextTick(() => { + if (!this.response || !this.response.responseResult || !this.response.responseResult.body) { + this.$message(this.$t('api_test.request.assertions.debug_first')); + return; + } + this.$refs.jsonpathSuggest.open(this.response.responseResult.body); + }) }, reload() { this.loading = true diff --git a/frontend/src/business/components/api/definition/components/assertion/ApiJsonpathSuggest.vue b/frontend/src/business/components/api/definition/components/assertion/ApiJsonpathSuggest.vue index 1ca9c8f22a..631d3cc043 100644 --- a/frontend/src/business/components/api/definition/components/assertion/ApiJsonpathSuggest.vue +++ b/frontend/src/business/components/api/definition/components/assertion/ApiJsonpathSuggest.vue @@ -19,22 +19,34 @@ return { visible: false, isCheckAll: false, + data: {}, }; }, props: { - data: {}, tip: { type: String, default() { return "" } - } + }, }, methods: { close() { this.visible = false; }, - open() { + open(objStr) { + this.data = {}; + try { + let param = JSON.parse(objStr); + if (param instanceof Array) { + this.$warning(this.$t('api_test.request.assertions.json_path_err')); + return; + } + this.data = param; + } catch (e) { + this.$warning(this.$t('api_test.request.assertions.json_path_err')); + return; + } this.visible = true; }, pathChangeHandler(data) { @@ -64,8 +76,8 @@ if (index === params.length - 1) { if (childObj instanceof Object) { childObj = JSON.stringify(childObj); - } else if (childObj == null) { - childObj = "null"; + } else { + childObj = childObj + ""; } return { key: param, diff --git a/frontend/src/business/components/api/definition/components/extract/ApiExtract.vue b/frontend/src/business/components/api/definition/components/extract/ApiExtract.vue index d29ca874e3..0191720131 100644 --- a/frontend/src/business/components/api/definition/components/extract/ApiExtract.vue +++ b/frontend/src/business/components/api/definition/components/extract/ApiExtract.vue @@ -46,7 +46,7 @@ - + @@ -91,7 +91,6 @@ type: "", reloadData: "", loading: false, - suggestData: {} } }, @@ -117,17 +116,14 @@ this.reload(); }, suggestJsonOpen() { - if (!this.response || !this.response.responseResult || !this.response.responseResult.body) { - this.$message(this.$t('api_test.request.assertions.debug_first')); - return; - } - try { - this.suggestData = JSON.parse(this.response.responseResult.body); - } catch (e) { - this.$error(this.$t('api_test.request.assertions.json_path_err')); - return; - } - this.$refs.jsonpathSuggest.open(); + this.$emit('suggestClick'); + this.$nextTick(() => { + if (!this.response || !this.response.responseResult || !this.response.responseResult.body) { + this.$message(this.$t('api_test.request.assertions.debug_first')); + return; + } + this.$refs.jsonpathSuggest.open(this.response.responseResult.body); + }) }, addJsonPathSuggest(data) { let option = {}; diff --git a/frontend/src/business/components/xpack b/frontend/src/business/components/xpack index 7d43154a7c..010ad7a5f0 160000 --- a/frontend/src/business/components/xpack +++ b/frontend/src/business/components/xpack @@ -1 +1 @@ -Subproject commit 7d43154a7c19732407a8e9ace8a7d1ea13c91f36 +Subproject commit 010ad7a5f072a5e9d368c756a2473bbd20781433 diff --git a/frontend/src/business/main.js b/frontend/src/business/main.js index 18a00ce479..87b3862555 100644 --- a/frontend/src/business/main.js +++ b/frontend/src/business/main.js @@ -23,6 +23,9 @@ import JsonSchemaEditor from './components/common/json-schema/schema/index'; import JSONPathPicker from 'vue-jsonpath-picker'; Vue.use(JsonSchemaEditor); +import VuePapaParse from 'vue-papa-parse' +Vue.use(VuePapaParse) + Vue.config.productionTip = false; Vue.use(icon); Vue.use(ElementUI, { diff --git a/frontend/src/i18n/en-US.js b/frontend/src/i18n/en-US.js index 51ea943bd3..15294bee00 100644 --- a/frontend/src/i18n/en-US.js +++ b/frontend/src/i18n/en-US.js @@ -571,7 +571,7 @@ export default { create_tag: "Create tag", scenario_step: "Ccenario step", step_total: "Step total", - scenario_total: "Scenario total", + scenario_total: "Scenario variable", api_list_import: "Api list import", external_import: "External import", wait_controller: "Wait controller", diff --git a/frontend/src/i18n/zh-CN.js b/frontend/src/i18n/zh-CN.js index 1efd1b99f4..d0e6662eec 100644 --- a/frontend/src/i18n/zh-CN.js +++ b/frontend/src/i18n/zh-CN.js @@ -570,7 +570,7 @@ export default { create_tag: "创建Tag", scenario_step: "场景步骤", step_total: "步骤总数", - scenario_total: "场景公共参数", + scenario_total: "场景变量", api_list_import: "接口列表导入", external_import: "外部导入", wait_controller: "等待控制器", diff --git a/frontend/src/i18n/zh-TW.js b/frontend/src/i18n/zh-TW.js index 11ed3503f5..0967c2e45e 100644 --- a/frontend/src/i18n/zh-TW.js +++ b/frontend/src/i18n/zh-TW.js @@ -570,7 +570,7 @@ export default { create_tag: "創建Tag", scenario_step: "場景步驟", step_total: "步驟總數", - scenario_total: "場景公共參數", + scenario_total: "場景變量", api_list_import: "接口列表導入", external_import: "外部導入", wait_controller: "等待控制器",