feat(接口测试): 环境变量增加导入导出 (#17018)
--story=1008922 --user=王孝刚 【接口测试】环境参数的查询功能,以及场景变量和环境参数的导入导出功能; https://www.tapd.cn/55049933/s/1220274 Co-authored-by: wxg0103 <727495428@qq.com>
This commit is contained in:
parent
07ea672ff9
commit
81955c649a
|
@ -65,16 +65,18 @@ public class ApiTestEnvironmentController {
|
|||
|
||||
@PostMapping("/add")
|
||||
@MsAuditLog(module = OperLogModule.PROJECT_ENVIRONMENT_SETTING, type = OperLogConstants.CREATE, content = "#msClass.getLogDetails(#apiTestEnvironmentWithBLOBs.id)", msClass = ApiTestEnvironmentService.class)
|
||||
public String create(@RequestPart("request") ApiTestEnvironmentDTO apiTestEnvironmentWithBLOBs, @RequestPart(value = "files", required = false) List<MultipartFile> sslFiles) {
|
||||
public String create(@RequestPart("request") ApiTestEnvironmentDTO apiTestEnvironmentWithBLOBs, @RequestPart(value = "files", required = false) List<MultipartFile> sslFiles,
|
||||
@RequestPart(value = "variablesFiles", required = false) List<MultipartFile> variableFile) {
|
||||
checkParams(apiTestEnvironmentWithBLOBs);
|
||||
return apiTestEnvironmentService.add(apiTestEnvironmentWithBLOBs, sslFiles);
|
||||
return apiTestEnvironmentService.add(apiTestEnvironmentWithBLOBs, sslFiles, variableFile);
|
||||
}
|
||||
|
||||
@PostMapping(value = "/update")
|
||||
@MsAuditLog(module = OperLogModule.PROJECT_ENVIRONMENT_SETTING, type = OperLogConstants.UPDATE, beforeEvent = "#msClass.getLogDetails(#apiTestEnvironment.id)", content = "#msClass.getLogDetails(#apiTestEnvironment.id)", msClass = ApiTestEnvironmentService.class)
|
||||
public void update(@RequestPart("request") ApiTestEnvironmentDTO apiTestEnvironment, @RequestPart(value = "files", required = false) List<MultipartFile> sslFiles) {
|
||||
public void update(@RequestPart("request") ApiTestEnvironmentDTO apiTestEnvironment, @RequestPart(value = "files", required = false) List<MultipartFile> sslFiles,
|
||||
@RequestPart(value = "variablesFiles", required = false) List<MultipartFile> variableFile) {
|
||||
checkParams(apiTestEnvironment);
|
||||
apiTestEnvironmentService.update(apiTestEnvironment, sslFiles);
|
||||
apiTestEnvironmentService.update(apiTestEnvironment, sslFiles, variableFile);
|
||||
}
|
||||
|
||||
private void checkParams(ApiTestEnvironmentDTO apiTestEnvironment) {
|
||||
|
|
|
@ -8,4 +8,5 @@ import java.util.List;
|
|||
@Data
|
||||
public class ApiTestEnvironmentDTO extends ApiTestEnvironmentWithBLOBs {
|
||||
private List<String> uploadIds;
|
||||
private List<String> variablesFilesIds;
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@ import io.metersphere.api.dto.EnvironmentType;
|
|||
import io.metersphere.api.dto.definition.request.controller.MsLoopController;
|
||||
import io.metersphere.api.dto.definition.request.sampler.MsHTTPSamplerProxy;
|
||||
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.dto.scenario.request.BodyFile;
|
||||
import io.metersphere.api.service.ApiTestEnvironmentService;
|
||||
|
@ -69,7 +68,7 @@ public class ElementUtil {
|
|||
arguments.setName(StringUtils.isNoneBlank(name) ? name : "Arguments");
|
||||
arguments.setProperty(TestElement.TEST_CLASS, Arguments.class.getName());
|
||||
arguments.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("ArgumentsPanel"));
|
||||
config.getConfig().get(projectId).getCommonConfig().getVariables().stream().filter(KeyValue::isValid).filter(KeyValue::isEnable).forEach(keyValue ->
|
||||
config.getConfig().get(projectId).getCommonConfig().getVariables().stream().filter(ScenarioVariable::isConstantValid).filter(ScenarioVariable::isEnable).forEach(keyValue ->
|
||||
arguments.addArgument(keyValue.getName(), keyValue.getValue(), "=")
|
||||
);
|
||||
if (arguments.getArguments().size() > 0) {
|
||||
|
@ -101,9 +100,9 @@ public class ElementUtil {
|
|||
|
||||
public static void addCsvDataSet(HashTree tree, List<ScenarioVariable> variables, ParameterConfig config, String shareMode) {
|
||||
if (CollectionUtils.isNotEmpty(variables)) {
|
||||
List<ScenarioVariable> list = variables.stream().filter(ScenarioVariable::isCSVValid).collect(Collectors.toList());
|
||||
List<ScenarioVariable> list = variables.stream().filter(ScenarioVariable::isCSVValid).filter(ScenarioVariable::isEnable).collect(Collectors.toList());
|
||||
if (CollectionUtils.isEmpty(list) && CollectionUtils.isNotEmpty(config.getTransferVariables())) {
|
||||
list = config.getTransferVariables().stream().filter(ScenarioVariable::isCSVValid).collect(Collectors.toList());
|
||||
list = config.getTransferVariables().stream().filter(ScenarioVariable::isCSVValid).filter(ScenarioVariable::isEnable).collect(Collectors.toList());
|
||||
}
|
||||
if (CollectionUtils.isNotEmpty(list)) {
|
||||
list.forEach(item -> {
|
||||
|
@ -140,7 +139,7 @@ public class ElementUtil {
|
|||
|
||||
public static void addCounter(HashTree tree, List<ScenarioVariable> variables, boolean isInternal) {
|
||||
if (CollectionUtils.isNotEmpty(variables)) {
|
||||
List<ScenarioVariable> list = variables.stream().filter(ScenarioVariable::isCounterValid).collect(Collectors.toList());
|
||||
List<ScenarioVariable> list = variables.stream().filter(ScenarioVariable::isCounterValid).filter(ScenarioVariable::isEnable).collect(Collectors.toList());
|
||||
if (CollectionUtils.isNotEmpty(list)) {
|
||||
list.forEach(item -> {
|
||||
CounterConfig counterConfig = new CounterConfig();
|
||||
|
@ -166,7 +165,7 @@ public class ElementUtil {
|
|||
|
||||
public static void addRandom(HashTree tree, List<ScenarioVariable> variables) {
|
||||
if (CollectionUtils.isNotEmpty(variables)) {
|
||||
List<ScenarioVariable> list = variables.stream().filter(ScenarioVariable::isRandom).collect(Collectors.toList());
|
||||
List<ScenarioVariable> list = variables.stream().filter(ScenarioVariable::isRandom).filter(ScenarioVariable::isEnable).collect(Collectors.toList());
|
||||
if (CollectionUtils.isNotEmpty(list)) {
|
||||
list.forEach(item -> {
|
||||
RandomVariableConfig randomVariableConfig = new RandomVariableConfig();
|
||||
|
@ -794,4 +793,62 @@ public class ElementUtil {
|
|||
return evlValue;
|
||||
}
|
||||
}
|
||||
|
||||
public static Arguments getConfigArguments(ParameterConfig config, String name, String projectId, List<ScenarioVariable> variables) {
|
||||
Arguments arguments = new Arguments();
|
||||
arguments.setEnabled(true);
|
||||
arguments.setName(StringUtils.isNotEmpty(name) ? name : "Arguments");
|
||||
arguments.setProperty(TestElement.TEST_CLASS, Arguments.class.getName());
|
||||
arguments.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("ArgumentsPanel"));
|
||||
|
||||
// 场景变量
|
||||
if (CollectionUtils.isNotEmpty(variables)) {
|
||||
variables.stream().filter(ScenarioVariable::isConstantValid).forEach(keyValue ->
|
||||
arguments.addArgument(keyValue.getName(), keyValue.getValue(), "=")
|
||||
);
|
||||
|
||||
List<ScenarioVariable> variableList = variables.stream().filter(ScenarioVariable::isListValid).collect(Collectors.toList());
|
||||
variableList.forEach(item -> {
|
||||
String[] arrays = item.getValue().split(",");
|
||||
for (int i = 0; i < arrays.length; i++) {
|
||||
arguments.addArgument(item.getName() + "_" + (i + 1), arrays[i], "=");
|
||||
}
|
||||
});
|
||||
}
|
||||
// 环境通用变量
|
||||
if (config.isEffective(projectId) && config.getConfig().get(projectId).getCommonConfig() != null
|
||||
&& CollectionUtils.isNotEmpty(config.getConfig().get(projectId).getCommonConfig().getVariables())) {
|
||||
//常量
|
||||
List<ScenarioVariable> constants = config.getConfig().get(projectId).getCommonConfig().getVariables().stream().filter(ScenarioVariable::isConstantValid).filter(ScenarioVariable::isEnable).collect(Collectors.toList());
|
||||
constants.forEach(keyValue ->
|
||||
arguments.addArgument(keyValue.getName(), keyValue.getValue(), "=")
|
||||
);
|
||||
// List类型的变量
|
||||
List<ScenarioVariable> variableList = config.getConfig().get(projectId).getCommonConfig().getVariables().stream().filter(ScenarioVariable::isListValid).filter(ScenarioVariable::isEnable).collect(Collectors.toList());
|
||||
variableList.forEach(item -> {
|
||||
String[] arrays = item.getValue().split(",");
|
||||
for (int i = 0; i < arrays.length; i++) {
|
||||
arguments.addArgument(item.getName() + "_" + (i + 1), arrays[i], "=");
|
||||
}
|
||||
});
|
||||
// 清空变量,防止重复添加
|
||||
config.getConfig().get(projectId).getCommonConfig().getVariables().remove(constants);
|
||||
config.getConfig().get(projectId).getCommonConfig().getVariables().remove(variableList);
|
||||
}
|
||||
|
||||
if (arguments.getArguments() != null && arguments.getArguments().size() > 0) {
|
||||
return arguments;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void addOtherVariables(ParameterConfig config, HashTree httpSamplerTree, String projectId) {
|
||||
if (config.isEffective(projectId) && config.getConfig().get(projectId).getCommonConfig() != null
|
||||
&& CollectionUtils.isNotEmpty(config.getConfig().get(projectId).getCommonConfig().getVariables())) {
|
||||
ElementUtil.addCsvDataSet(httpSamplerTree, config.getConfig().get(projectId).getCommonConfig().getVariables(), config, "shareMode.group");
|
||||
ElementUtil.addCounter(httpSamplerTree, config.getConfig().get(projectId).getCommonConfig().getVariables(), false);
|
||||
ElementUtil.addRandom(httpSamplerTree, config.getConfig().get(projectId).getCommonConfig().getVariables());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -41,7 +41,6 @@ import java.util.HashMap;
|
|||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
|
@ -145,7 +144,8 @@ public class MsScenario extends MsTestElement {
|
|||
scenarioTree = MsCriticalSectionController.createHashTree(tree, this.getName(), this.isEnable());
|
||||
}
|
||||
// 环境变量
|
||||
Arguments arguments = arguments(this.isEnvironmentEnable() ? newConfig : config);
|
||||
Arguments arguments = ElementUtil.getConfigArguments(this.isEnvironmentEnable() ? newConfig : config, this.getName(), this.getProjectId(), this.getVariables());
|
||||
|
||||
if (arguments != null && (this.variableEnable == null || this.variableEnable)) {
|
||||
Arguments valueSupposeMock = ParameterConfig.valueSupposeMock(arguments);
|
||||
// 这里加入自定义变量解决ForEach循环控制器取值问题,循环控制器无法从vars中取值
|
||||
|
@ -309,41 +309,6 @@ public class MsScenario extends MsTestElement {
|
|||
}
|
||||
}
|
||||
|
||||
private Arguments arguments(ParameterConfig config) {
|
||||
Arguments arguments = new Arguments();
|
||||
arguments.setEnabled(true);
|
||||
arguments.setName(StringUtils.isNotEmpty(this.getName()) ? this.getName() : "Arguments");
|
||||
arguments.setProperty(TestElement.TEST_CLASS, Arguments.class.getName());
|
||||
arguments.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("ArgumentsPanel"));
|
||||
// 场景变量
|
||||
if (CollectionUtils.isNotEmpty(this.getVariables())) {
|
||||
this.getVariables().stream().filter(ScenarioVariable::isConstantValid).forEach(keyValue ->
|
||||
arguments.addArgument(keyValue.getName(), keyValue.getValue(), "=")
|
||||
);
|
||||
|
||||
List<ScenarioVariable> variableList = this.getVariables().stream().filter(ScenarioVariable::isListValid).collect(Collectors.toList());
|
||||
variableList.forEach(item -> {
|
||||
String[] arrays = item.getValue().split(",");
|
||||
for (int i = 0; i < arrays.length; i++) {
|
||||
arguments.addArgument(item.getName() + "_" + (i + 1), arrays[i], "=");
|
||||
}
|
||||
});
|
||||
}
|
||||
// 环境通用变量
|
||||
if (config.isEffective(this.getProjectId()) && config.getConfig().get(this.getProjectId()).getCommonConfig() != null
|
||||
&& CollectionUtils.isNotEmpty(config.getConfig().get(this.getProjectId()).getCommonConfig().getVariables())) {
|
||||
config.getConfig().get(this.getProjectId()).getCommonConfig().getVariables().stream().filter(KeyValue::isValid).filter(KeyValue::isEnable).forEach(keyValue ->
|
||||
arguments.addArgument(keyValue.getName(), keyValue.getValue(), "=")
|
||||
);
|
||||
// 清空变量,防止重复添加
|
||||
config.getConfig().get(this.getProjectId()).getCommonConfig().getVariables().clear();
|
||||
}
|
||||
if (arguments.getArguments() != null && arguments.getArguments().size() > 0) {
|
||||
return arguments;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void setEnv(Map<String, String> environmentMap, Map<String, EnvironmentConfig> envConfig) {
|
||||
for (String projectId : environmentMap.keySet()) {
|
||||
ApiTestEnvironmentService environmentService = CommonBeanFactory.getBean(ApiTestEnvironmentService.class);
|
||||
|
|
|
@ -2,8 +2,8 @@ package io.metersphere.api.dto.definition.request.dns;
|
|||
|
||||
import com.alibaba.fastjson.annotation.JSONType;
|
||||
import io.metersphere.api.dto.definition.request.ParameterConfig;
|
||||
import io.metersphere.api.dto.definition.request.variable.ScenarioVariable;
|
||||
import io.metersphere.api.dto.scenario.HttpConfig;
|
||||
import io.metersphere.api.dto.scenario.KeyValue;
|
||||
import io.metersphere.api.dto.scenario.environment.EnvironmentConfig;
|
||||
import io.metersphere.api.dto.scenario.environment.Host;
|
||||
import io.metersphere.plugin.core.MsParameter;
|
||||
|
@ -65,14 +65,14 @@ public class MsDNSCacheManager extends MsTestElement {
|
|||
}
|
||||
}
|
||||
|
||||
private static Arguments arguments(String name, List<KeyValue> variables) {
|
||||
private static Arguments arguments(String name, List<ScenarioVariable> variables) {
|
||||
Arguments arguments = new Arguments();
|
||||
arguments.setEnabled(true);
|
||||
arguments.setName(name);
|
||||
arguments.setProperty(TestElement.TEST_CLASS, Arguments.class.getName());
|
||||
arguments.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("ArgumentsPanel"));
|
||||
variables.stream().filter(KeyValue::isValid).filter(KeyValue::isEnable).forEach(keyValue ->
|
||||
arguments.addArgument(keyValue.getName(), keyValue.getValue(), "=")
|
||||
variables.stream().filter(ScenarioVariable::isConstantValid).filter(ScenarioVariable::isEnable).forEach(ScenarioVariable ->
|
||||
arguments.addArgument(ScenarioVariable.getName(), ScenarioVariable.getValue(), "=")
|
||||
);
|
||||
return arguments;
|
||||
}
|
||||
|
|
|
@ -226,10 +226,12 @@ public class MsHTTPSamplerProxy extends MsTestElement {
|
|||
setHeader(httpSamplerTree, config.getHeaders());
|
||||
}
|
||||
// 环境通用请求头
|
||||
Arguments arguments = getConfigArguments(config);
|
||||
Arguments arguments = ElementUtil.getConfigArguments(config, this.getName(), this.getProjectId(), null);
|
||||
if (arguments != null) {
|
||||
httpSamplerTree.add(arguments);
|
||||
}
|
||||
//添加csv
|
||||
ElementUtil.addOtherVariables(config, httpSamplerTree, this.getProjectId());
|
||||
//判断是否要开启DNS
|
||||
if (config.isEffective(this.getProjectId()) && config.getConfig().get(this.getProjectId()).getCommonConfig() != null
|
||||
&& config.getConfig().get(this.getProjectId()).getCommonConfig().isEnableHost()) {
|
||||
|
@ -353,6 +355,7 @@ public class MsHTTPSamplerProxy extends MsTestElement {
|
|||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LogUtil.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
if (StringUtils.isNotEmpty(useEvnId) && !StringUtils.equals(useEvnId, this.getEnvironmentId())) {
|
||||
|
@ -682,7 +685,7 @@ public class MsHTTPSamplerProxy extends MsTestElement {
|
|||
arguments.addArgument(httpArgument);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
||||
LogUtil.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@ -728,30 +731,6 @@ public class MsHTTPSamplerProxy extends MsTestElement {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 环境通用变量,这里只适用用接口定义和用例,场景自动化会加到场景中
|
||||
*/
|
||||
private Arguments getConfigArguments(ParameterConfig config) {
|
||||
Arguments arguments = new Arguments();
|
||||
arguments.setEnabled(true);
|
||||
arguments.setName(StringUtils.isNotEmpty(this.getName()) ? this.getName() : "Arguments");
|
||||
arguments.setProperty(TestElement.TEST_CLASS, Arguments.class.getName());
|
||||
arguments.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("ArgumentsPanel"));
|
||||
// 环境通用变量
|
||||
if (config.isEffective(this.getProjectId()) && config.getConfig().get(this.getProjectId()).getCommonConfig() != null
|
||||
&& CollectionUtils.isNotEmpty(config.getConfig().get(this.getProjectId()).getCommonConfig().getVariables())) {
|
||||
config.getConfig().get(this.getProjectId()).getCommonConfig().getVariables().stream().filter(KeyValue::isValid).filter(KeyValue::isEnable).forEach(keyValue ->
|
||||
arguments.addArgument(keyValue.getName(), keyValue.getValue(), "=")
|
||||
);
|
||||
// 清空变量,防止重复添加
|
||||
config.getConfig().get(this.getProjectId()).getCommonConfig().getVariables().clear();
|
||||
}
|
||||
if (arguments.getArguments() != null && arguments.getArguments().size() > 0) {
|
||||
return arguments;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void addArguments(HashTree tree, String key, String value) {
|
||||
Arguments arguments = new Arguments();
|
||||
arguments.setEnabled(true);
|
||||
|
|
|
@ -165,11 +165,12 @@ public class MsJDBCSampler extends MsTestElement {
|
|||
tree.add(arguments);
|
||||
}
|
||||
// 环境通用请求头
|
||||
Arguments envArguments = getConfigArguments(config);
|
||||
Arguments envArguments = ElementUtil.getConfigArguments(config, this.getName(), this.getProjectId(), null);
|
||||
if (envArguments != null) {
|
||||
tree.add(envArguments);
|
||||
}
|
||||
|
||||
//添加csv
|
||||
ElementUtil.addOtherVariables(config, tree, this.getProjectId());
|
||||
//增加误报、全局断言
|
||||
HashTreeUtil.addPositive(envConfig, samplerHashTree, config, this.getProjectId());
|
||||
|
||||
|
@ -192,30 +193,6 @@ public class MsJDBCSampler extends MsTestElement {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* 环境通用变量
|
||||
*/
|
||||
private Arguments getConfigArguments(ParameterConfig config) {
|
||||
Arguments arguments = new Arguments();
|
||||
arguments.setEnabled(true);
|
||||
arguments.setName(StringUtils.isNotEmpty(this.getName()) ? this.getName() : "Arguments");
|
||||
arguments.setProperty(TestElement.TEST_CLASS, Arguments.class.getName());
|
||||
arguments.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("ArgumentsPanel"));
|
||||
// 环境通用变量
|
||||
if (config.isEffective(this.getProjectId()) && config.getConfig().get(this.getProjectId()).getCommonConfig() != null
|
||||
&& CollectionUtils.isNotEmpty(config.getConfig().get(this.getProjectId()).getCommonConfig().getVariables())) {
|
||||
config.getConfig().get(this.getProjectId()).getCommonConfig().getVariables().stream().filter(KeyValue::isValid).filter(KeyValue::isEnable).forEach(keyValue ->
|
||||
arguments.addArgument(keyValue.getName(), ElementUtil.getEvlValue(keyValue.getValue()), "=")
|
||||
);
|
||||
// 清空变量,防止重复添加
|
||||
config.getConfig().get(this.getProjectId()).getCommonConfig().getVariables().clear();
|
||||
}
|
||||
if (arguments.getArguments() != null && arguments.getArguments().size() > 0) {
|
||||
return arguments;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isDataSource(List<DatabaseConfig> databaseConfigs) {
|
||||
List<String> ids = databaseConfigs.stream().map(DatabaseConfig::getId).collect(Collectors.toList());
|
||||
if (StringUtils.isNotEmpty(this.dataSourceId) && ids.contains(this.dataSourceId)) {
|
||||
|
|
|
@ -171,11 +171,12 @@ public class MsTCPSampler extends MsTestElement {
|
|||
}
|
||||
|
||||
// 添加环境中的公共变量
|
||||
Arguments arguments = ElementUtil.addArguments(config, this.getProjectId(), this.getName());
|
||||
Arguments arguments = ElementUtil.getConfigArguments(config, this.getName(), this.getProjectId(), null);
|
||||
if (arguments != null) {
|
||||
tree.add(arguments);
|
||||
}
|
||||
|
||||
//添加csv
|
||||
ElementUtil.addOtherVariables(config, tree, this.getProjectId());
|
||||
final HashTree samplerHashTree = new ListedHashTree();
|
||||
samplerHashTree.add(tcpConfig());
|
||||
tree.set(tcpSampler(config), samplerHashTree);
|
||||
|
|
|
@ -13,7 +13,7 @@ public class ScenarioVariable {
|
|||
/**
|
||||
* CONSTANT LIST CSV COUNTER RANDOM
|
||||
*/
|
||||
private String type;
|
||||
private String type = VariableTypeConstants.CONSTANT.name();
|
||||
private String id;
|
||||
private String name;
|
||||
|
||||
|
@ -41,6 +41,8 @@ public class ScenarioVariable {
|
|||
private String minNumber;
|
||||
private String maxNumber;
|
||||
|
||||
private boolean enable = true;
|
||||
|
||||
public ScenarioVariable() {
|
||||
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
package io.metersphere.api.dto.scenario.environment;
|
||||
|
||||
import io.metersphere.api.dto.scenario.KeyValue;
|
||||
import io.metersphere.api.dto.definition.request.variable.ScenarioVariable;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class CommonConfig {
|
||||
private List<KeyValue> variables;
|
||||
private List<ScenarioVariable> variables;
|
||||
private boolean enableHost;
|
||||
private List<Host> hosts;
|
||||
private int requestTimeout;
|
||||
|
|
|
@ -8,9 +8,12 @@ import io.metersphere.api.dto.mockconfig.MockConfigStaticData;
|
|||
import io.metersphere.api.tcp.TCPPool;
|
||||
import io.metersphere.base.domain.ApiTestEnvironmentExample;
|
||||
import io.metersphere.base.domain.ApiTestEnvironmentWithBLOBs;
|
||||
import io.metersphere.base.domain.FileAssociationExample;
|
||||
import io.metersphere.base.domain.Project;
|
||||
import io.metersphere.base.mapper.ApiTestEnvironmentMapper;
|
||||
import io.metersphere.base.mapper.FileAssociationMapper;
|
||||
import io.metersphere.base.mapper.ext.ExtApiTestEnvironmentMapper;
|
||||
import io.metersphere.commons.constants.FileAssociationType;
|
||||
import io.metersphere.commons.constants.ProjectApplicationType;
|
||||
import io.metersphere.commons.exception.MSException;
|
||||
import io.metersphere.commons.utils.CommonBeanFactory;
|
||||
|
@ -25,6 +28,7 @@ import io.metersphere.log.utils.ReflexObjectUtil;
|
|||
import io.metersphere.log.vo.DetailColumn;
|
||||
import io.metersphere.log.vo.OperatingLogDetails;
|
||||
import io.metersphere.log.vo.system.SystemReference;
|
||||
import io.metersphere.metadata.service.FileAssociationService;
|
||||
import io.metersphere.service.EnvironmentGroupProjectService;
|
||||
import io.metersphere.service.ProjectApplicationService;
|
||||
import io.metersphere.service.ProjectService;
|
||||
|
@ -50,6 +54,10 @@ public class ApiTestEnvironmentService {
|
|||
private ProjectApplicationService projectApplicationService;
|
||||
@Resource
|
||||
private ExtApiTestEnvironmentMapper extApiTestEnvironmentMapper;
|
||||
@Resource
|
||||
private FileAssociationService fileAssociationService;
|
||||
@Resource
|
||||
private FileAssociationMapper fileAssociationMapper;
|
||||
|
||||
public List<ApiTestEnvironmentWithBLOBs> list(String projectId) {
|
||||
ApiTestEnvironmentExample example = new ApiTestEnvironmentExample();
|
||||
|
@ -85,6 +93,9 @@ public class ApiTestEnvironmentService {
|
|||
public void delete(String id) {
|
||||
apiTestEnvironmentMapper.deleteByPrimaryKey(id);
|
||||
environmentGroupProjectService.deleteRelateEnv(id);
|
||||
FileAssociationExample associationExample = new FileAssociationExample();
|
||||
associationExample.createCriteria().andSourceIdEqualTo(id);
|
||||
fileAssociationMapper.deleteByExample(associationExample);
|
||||
}
|
||||
|
||||
public void update(ApiTestEnvironmentWithBLOBs apiTestEnvironment) {
|
||||
|
@ -99,16 +110,19 @@ public class ApiTestEnvironmentService {
|
|||
return apiTestEnvironmentWithBLOBs.getId();
|
||||
}
|
||||
|
||||
public String add(ApiTestEnvironmentDTO request, List<MultipartFile> sslFiles) {
|
||||
public String add(ApiTestEnvironmentDTO request, List<MultipartFile> sslFiles, List<MultipartFile> variableFile) {
|
||||
request.setId(UUID.randomUUID().toString());
|
||||
request.setCreateUser(SessionUtils.getUserId());
|
||||
checkEnvironmentExist(request);
|
||||
FileUtils.createFiles(request.getUploadIds(), sslFiles, FileUtils.BODY_FILE_DIR + "/ssl");
|
||||
FileUtils.createBodyFiles(request.getVariablesFilesIds(), variableFile);
|
||||
//检查Config,判断isMock参数是否给True
|
||||
request = this.updateConfig(request, false);
|
||||
request.setCreateTime(System.currentTimeMillis());
|
||||
request.setUpdateTime(System.currentTimeMillis());
|
||||
apiTestEnvironmentMapper.insert(request);
|
||||
// 存储附件关系
|
||||
fileAssociationService.saveEnvironment(request.getId(), request.getConfig(), FileAssociationType.ENVIRONMENT.name());
|
||||
return request.getId();
|
||||
}
|
||||
|
||||
|
@ -128,10 +142,13 @@ public class ApiTestEnvironmentService {
|
|||
return request;
|
||||
}
|
||||
|
||||
public void update(ApiTestEnvironmentDTO apiTestEnvironment, List<MultipartFile> sslFiles) {
|
||||
public void update(ApiTestEnvironmentDTO apiTestEnvironment, List<MultipartFile> sslFiles, List<MultipartFile> variablesFiles) {
|
||||
checkEnvironmentExist(apiTestEnvironment);
|
||||
FileUtils.createFiles(apiTestEnvironment.getUploadIds(), sslFiles, FileUtils.BODY_FILE_DIR + "/ssl");
|
||||
FileUtils.createBodyFiles(apiTestEnvironment.getVariablesFilesIds(), variablesFiles);
|
||||
apiTestEnvironment.setUpdateTime(System.currentTimeMillis());
|
||||
// 存储附件关系
|
||||
fileAssociationService.saveEnvironment(apiTestEnvironment.getId(), apiTestEnvironment.getConfig(), FileAssociationType.ENVIRONMENT.name());
|
||||
apiTestEnvironmentMapper.updateByPrimaryKeyWithBLOBs(apiTestEnvironment);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
package io.metersphere.commons.constants;
|
||||
|
||||
public enum FileAssociationType {
|
||||
API, CASE, SCENARIO, UI
|
||||
API, CASE, SCENARIO, UI, ENVIRONMENT
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package io.metersphere.metadata.service;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import io.metersphere.api.dto.definition.request.MsScenario;
|
||||
import io.metersphere.api.dto.definition.request.sampler.MsHTTPSamplerProxy;
|
||||
import io.metersphere.api.dto.definition.request.variable.ScenarioVariable;
|
||||
|
@ -104,7 +105,7 @@ public class FileAssociationService {
|
|||
}
|
||||
}
|
||||
|
||||
public void saveApi(String id, MsTestElement request,String type) {
|
||||
public void saveApi(String id, MsTestElement request, String type) {
|
||||
this.deleteByResourceId(id);
|
||||
if (StringUtils.isNotEmpty(id) && request != null && StringUtils.equalsIgnoreCase(request.getType(), HTTPSamplerProxy.class.getSimpleName())) {
|
||||
MsHTTPSamplerProxy samplerProxy = (MsHTTPSamplerProxy) request;
|
||||
|
@ -134,6 +135,22 @@ public class FileAssociationService {
|
|||
}
|
||||
}
|
||||
|
||||
public void saveEnvironment(String id, String config, String type) {
|
||||
this.deleteByResourceId(id);
|
||||
List<BodyFile> files = new ArrayList<>();
|
||||
if (StringUtils.isNotEmpty(config)) {
|
||||
JSONObject commonConfig = JSONObject.parseObject(config).getJSONObject("commonConfig");
|
||||
List<ScenarioVariable> list = JSONObject.parseArray(commonConfig.getString("variables"), ScenarioVariable.class);
|
||||
list.stream().filter(ScenarioVariable::isCSVValid).forEach(keyValue -> {
|
||||
files.addAll(keyValue.getFiles().stream().filter(BodyFile::isRef).collect(Collectors.toList()));
|
||||
});
|
||||
}
|
||||
if (!CollectionUtils.isEmpty(files)) {
|
||||
List<BodyFile> list = files.stream().distinct().collect(Collectors.toList());
|
||||
this.save(list, type, id);
|
||||
}
|
||||
}
|
||||
|
||||
private void getHashTree(List<MsTestElement> testElements, List<BodyFile> files) {
|
||||
testElements.forEach(item -> {
|
||||
if (StringUtils.equalsIgnoreCase(item.getType(), HTTPSamplerProxy.class.getSimpleName())) {
|
||||
|
|
|
@ -129,11 +129,11 @@ export default {
|
|||
},
|
||||
download() {
|
||||
// 本地文件
|
||||
if (this.parameter.files && this.parameter.files.length > 0 && this.parameter.files[0].file) {
|
||||
if (this.parameter.files && this.parameter.files.length > 0 && this.parameter.files[0].file && this.parameter.files[0].file.name) {
|
||||
downloadFile(this.parameter.files[0].file.name, this.parameter.files[0].file);
|
||||
}
|
||||
// 远程下载文件
|
||||
if (this.parameter.files && this.parameter.files.length > 0 && !this.parameter.files[0].file) {
|
||||
if (this.parameter.files && this.parameter.files.length > 0 && (!this.parameter.files[0].file || !this.parameter.files[0].file.name)) {
|
||||
let file = this.parameter.files[0];
|
||||
let conf = {
|
||||
url: "/api/automation/file/download",
|
||||
|
@ -157,15 +157,15 @@ export default {
|
|||
}
|
||||
},
|
||||
handleRemove(file) {
|
||||
let fileName = file.file ? file.file.name : file.name
|
||||
this.$alert('是否确认删除CSV文件:【 ' + fileName + " 】?", '', {
|
||||
let fileName = file.name ? file.name : file.file.name
|
||||
this.$alert(this.$t('api_test.environment.csv_delete') + ':【 ' + fileName + " 】?", '', {
|
||||
confirmButtonText: this.$t('commons.confirm'),
|
||||
callback: (action) => {
|
||||
if (action === 'confirm') {
|
||||
this.$refs.upload.handleRemove(file);
|
||||
for (let i = 0; i < this.parameter.files.length; i++) {
|
||||
let paramFileName = this.parameter.files[i].file ?
|
||||
this.parameter.files[i].file.name : this.parameter.files[i].name;
|
||||
let paramFileName = this.parameter.files[i].name ?
|
||||
this.parameter.files[i].name : this.parameter.files[i].file.name;
|
||||
if (fileName === paramFileName) {
|
||||
this.parameter.files.splice(i, 1);
|
||||
this.$refs.upload.handleRemove(file);
|
||||
|
@ -180,6 +180,7 @@ export default {
|
|||
this.$warning(this.$t('test_track.case.import.upload_limit_count'));
|
||||
},
|
||||
upload(file) {
|
||||
this.parameter.files = [];
|
||||
this.parameter.files.push(file);
|
||||
},
|
||||
uploadValidate(file) {
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<el-tab-pane :label="$t('variables.config')" name="config">
|
||||
<el-row>
|
||||
<el-col :span="5" style="margin-top: 5px">
|
||||
<span>{{$t('variables.add_file')}}</span>
|
||||
<span>{{ $t('variables.add_file') }}</span>
|
||||
</el-col>
|
||||
<el-col :span="19">
|
||||
<ms-csv-file-upload :parameter="editData"/>
|
||||
|
@ -40,7 +40,7 @@
|
|||
</el-row>
|
||||
<el-row style="margin-top: 10px">
|
||||
<el-col :span="5" style="margin-top: 5px">
|
||||
<span>{{$t('variables.delimiter')}}</span>
|
||||
<span>{{ $t('variables.delimiter') }}</span>
|
||||
</el-col>
|
||||
<el-col :span="19">
|
||||
<el-input v-model="editData.delimiter" size="small" :disabled="disabled"/>
|
||||
|
@ -48,7 +48,7 @@
|
|||
</el-row>
|
||||
<el-row style="margin-top: 10px">
|
||||
<el-col :span="5" style="margin-top: 5px">
|
||||
<span>{{$t('variables.quoted_data')}}</span>
|
||||
<span>{{ $t('variables.quoted_data') }}</span>
|
||||
</el-col>
|
||||
<el-col :span="19">
|
||||
<el-select v-model="editData.quotedData" size="small" :disabled="disabled">
|
||||
|
@ -66,10 +66,11 @@
|
|||
height="200px"
|
||||
v-loading="loading">
|
||||
<!-- 自定义列的遍历-->
|
||||
<el-table-column v-for="(item, index) in columns" :key="index" :label="columns[index]" align="left" width="180">
|
||||
<el-table-column v-for="(item, index) in columns" :key="index" :label="columns[index]" align="left"
|
||||
width="180">
|
||||
<!-- 数据的遍历 scope.row就代表数据的每一个对象-->
|
||||
<template slot-scope="scope">
|
||||
<span>{{scope.row[index]}}</span>
|
||||
<span>{{ scope.row[index] }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
@ -79,9 +80,9 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import MsCsvFileUpload from "./CsvFileUpload";
|
||||
import MsCsvFileUpload from "./CsvFileUpload";
|
||||
|
||||
export default {
|
||||
export default {
|
||||
name: "MsEditCsv",
|
||||
components: {
|
||||
MsCsvFileUpload
|
||||
|
@ -152,12 +153,12 @@
|
|||
};
|
||||
this.allData = [];
|
||||
// 本地文件
|
||||
if (this.editData.files && this.editData.files.length > 0 && this.editData.files[0].file) {
|
||||
if (this.editData.files && this.editData.files.length > 0 && this.editData.files[0].file && this.editData.files[0].file.name) {
|
||||
this.loading = true;
|
||||
this.$papa.parse(this.editData.files[0].file, config);
|
||||
}
|
||||
// 远程下载文件
|
||||
if (this.editData.files && this.editData.files.length > 0 && !this.editData.files[0].file) {
|
||||
if (this.editData.files && this.editData.files.length > 0 && (!this.editData.files[0].file || !this.editData.files[0].file.name)) {
|
||||
let file = this.editData.files[0];
|
||||
let conf = {
|
||||
url: "/api/automation/file/download",
|
||||
|
@ -187,18 +188,18 @@
|
|||
},
|
||||
|
||||
querySearch(queryString, cb) {
|
||||
let restaurants = [{value: "UTF-8"}, {value: "UTF-16"},{value: "GB2312"}, {value: "ISO-8859-15"}, {value: "US-ASCll"}];
|
||||
let restaurants = [{value: "UTF-8"}, {value: "UTF-16"}, {value: "GB2312"}, {value: "ISO-8859-15"}, {value: "US-ASCll"}];
|
||||
let results = queryString ? restaurants.filter(this.createFilter(queryString)) : restaurants;
|
||||
// 调用 callback 返回建议列表的数据
|
||||
cb(results);
|
||||
},
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
ms-is-leaf >>> .is-leaf {
|
||||
ms-is-leaf >>> .is-leaf {
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -0,0 +1,160 @@
|
|||
<template>
|
||||
<el-dialog :visible="dialogVisible" :title="dialogTitle"
|
||||
@close="close" :close-on-click-modal="false" append-to-body
|
||||
width="35%">
|
||||
<el-form :rules="rules" label-width="80px" v-model="modeId">
|
||||
<el-form-item prop="modeId" :label="$t('commons.import_mode')">
|
||||
<el-select size="small" v-model="modeId">
|
||||
<el-option v-for="item in modeOptions" :key="item.id" :label="item.name" :value="item.id"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-upload
|
||||
class="api-upload" drag action="alert"
|
||||
:on-change="handleFileChange"
|
||||
:limit="1" :file-list="uploadFiles"
|
||||
:on-remove="handleRemove"
|
||||
:on-exceed="handleExceed"
|
||||
:auto-upload="false" accept=".json">
|
||||
<i class="el-icon-upload"></i>
|
||||
<div class="el-upload__text" v-html="$t('load_test.upload_tips')"></div>
|
||||
<div class="el-upload__tip" slot="tip">
|
||||
{{ $t('api_test.api_import.file_size_limit') }}
|
||||
{{ ',' + $t('api_test.api_import.ms_env_import_file_limit') }}
|
||||
</div>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template v-slot:footer>
|
||||
<el-button type="primary" @click="save">
|
||||
{{ $t('commons.confirm') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: "VariableImport",
|
||||
props: {
|
||||
projectList: {
|
||||
type: Array,
|
||||
default() {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
toImportProjectId: {
|
||||
type: String,
|
||||
default() {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
currentProjectId: '', //所选中环境的id
|
||||
uploadFiles: [],
|
||||
dialogTitle: this.$t('commons.import_variable'),
|
||||
dialogVisible: false,
|
||||
modeOptions: [
|
||||
{
|
||||
id: 'fullCoverage',
|
||||
name: this.$t('commons.cover')
|
||||
},
|
||||
{
|
||||
id: 'incrementalMerge',
|
||||
name: this.$t('commons.not_cover')
|
||||
}
|
||||
],
|
||||
modeId: 'fullCoverage',
|
||||
rules: {
|
||||
modeId: [
|
||||
{required: true, message: "", trigger: 'blur'},
|
||||
],
|
||||
},
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
//导入框激活时重置选中的项目和文件
|
||||
dialogVisible(val, oldVal) {
|
||||
if (oldVal === false) {
|
||||
this.currentProjectId = '';
|
||||
this.uploadFiles = [];
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
methods: {
|
||||
handleFileChange(file, uploadFiles) {
|
||||
this.uploadFiles = uploadFiles;
|
||||
},
|
||||
save() {
|
||||
if (this.uploadFiles.length > 0) {
|
||||
for (let i = 0; i < this.uploadFiles.length; i++) {
|
||||
this.uploadValidate(this.uploadFiles[i]);
|
||||
let file = this.uploadFiles[i];
|
||||
if (!file) {
|
||||
continue;
|
||||
}
|
||||
let reader = new FileReader();
|
||||
reader.readAsText(file.raw)
|
||||
reader.onload = (e) => {
|
||||
let fileString = e.target.result;
|
||||
let messages = '';
|
||||
try {
|
||||
JSON.parse(fileString).map(env => {
|
||||
if (!env.name) {
|
||||
messages = this.$t('api_test.automation.variable_warning')
|
||||
}
|
||||
})
|
||||
if (messages !== '') {
|
||||
this.$warning(messages);
|
||||
return;
|
||||
}
|
||||
this.$emit("mergeData", fileString, this.modeId);
|
||||
this.dialogVisible = false;
|
||||
this.$success(this.$t('commons.save_success'));
|
||||
} catch (exception) {
|
||||
this.$warning(this.$t('api_test.api_import.ms_env_import_file_limit'));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.$warning(this.$t('test_track.case.import.import_file_tips'));
|
||||
}
|
||||
},
|
||||
handleExceed() {
|
||||
this.$warning(this.$t('api_test.api_import.file_exceed_limit'));
|
||||
},
|
||||
handleRemove() {
|
||||
|
||||
},
|
||||
uploadValidate(file) { //判断文件扩展名是不是.json,以及文件大小是否超过20M
|
||||
const extension = file.name.substring(file.name.lastIndexOf('.') + 1);
|
||||
if (!(extension === 'json')) {
|
||||
this.$warning(this.$t('api_test.api_import.ms_env_import_file_limit'));
|
||||
}
|
||||
if (file.size / 1024 / 1024 > 20) {
|
||||
this.$warning(this.$t('api_test.api_import.file_size_limit'));
|
||||
}
|
||||
},
|
||||
open() {
|
||||
this.dialogVisible = true;
|
||||
},
|
||||
close() {
|
||||
this.dialogVisible = false;
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.project-item {
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
</style>
|
|
@ -37,9 +37,23 @@
|
|||
<el-button size="small" style="margin-left: 10px" type="primary" @click="addVariable">
|
||||
{{ $t('commons.add') }}
|
||||
</el-button>
|
||||
<el-link @click="batchAddParameter" type="primary" :disabled="disabled" style="margin-left: 10px">
|
||||
<el-dropdown style="margin-left: 10px">
|
||||
<el-button size="small">
|
||||
<span class="tip-font">{{ $t('commons.more_operator') }}</span>
|
||||
<i class="el-icon-arrow-down el-icon--right"/>
|
||||
</el-button>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item @click.native.stop="importVariable" @mergeData="mergeData" :disabled="disabled">
|
||||
{{ $t("commons.import_variable") }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item @click.native.stop="exportVariable" :disabled="disabled">
|
||||
{{ $t("commons.export_variable") }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item @click.native.stop="batchAddParameter" :disabled="disabled">
|
||||
{{ $t("commons.batch_add") }}
|
||||
</el-link>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</el-row>
|
||||
<el-row>
|
||||
|
@ -94,6 +108,13 @@
|
|||
:label="$t('api_test.value')"
|
||||
sortable>
|
||||
</ms-table-column>
|
||||
<ms-table-column
|
||||
prop="description"
|
||||
:field="item"
|
||||
:fields-width="fieldsWidth"
|
||||
:label="$t('commons.description')"
|
||||
sortable>
|
||||
</ms-table-column>
|
||||
</span>
|
||||
</ms-table>
|
||||
<batch-add-parameter @batchSave="batchSaveParameter" ref="batchAddParameter"/>
|
||||
|
@ -149,6 +170,7 @@
|
|||
</template>
|
||||
</el-collapse-transition>
|
||||
</fieldset>
|
||||
<variable-import ref="variableImport"></variable-import>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
|
@ -161,7 +183,7 @@ import MsEditCounter from "./EditCounter";
|
|||
import MsEditRandom from "./EditRandom";
|
||||
import MsEditListValue from "./EditListValue";
|
||||
import MsEditCsv from "./EditCsv";
|
||||
import {getUUID} from "@/common/js/utils";
|
||||
import {downloadFile, getUUID} from "@/common/js/utils";
|
||||
import MsApiKeyValue from "../../../definition/components/ApiKeyValue";
|
||||
import BatchAddParameter from "../../../definition/components/basis/BatchAddParameter";
|
||||
import {KeyValue} from "../../../definition/model/ApiTestModel";
|
||||
|
@ -170,6 +192,7 @@ import {REQUEST_HEADERS} from "@/common/js/constants";
|
|||
import MsTable from "@/business/components/common/components/table/MsTable";
|
||||
import MsTableColumn from "@/business/components/common/components/table/MsTableColumn";
|
||||
import {getCustomTableHeader, getCustomTableWidth} from "@/common/js/tableUtils";
|
||||
import VariableImport from "@/business/components/api/automation/scenario/variable/VariableImport";
|
||||
|
||||
const jsondiffpatch = require('jsondiffpatch');
|
||||
|
||||
|
@ -188,6 +211,7 @@ export default {
|
|||
BatchAddParameter,
|
||||
MsTableColumn,
|
||||
MsTable,
|
||||
VariableImport
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -230,6 +254,46 @@ export default {
|
|||
};
|
||||
},
|
||||
methods: {
|
||||
importVariable() {
|
||||
this.$refs.variableImport.open();
|
||||
},
|
||||
mergeData(data, modeId) {
|
||||
JSON.parse(data).map(importData => {
|
||||
importData.id = getUUID();
|
||||
importData.enable = true;
|
||||
importData.showMore = false;
|
||||
let sameNameIndex = this.variables.findIndex(d => d.name === importData.name);
|
||||
if (sameNameIndex !== -1) {
|
||||
if (modeId === 'fullCoverage') {
|
||||
this.variables.splice(sameNameIndex, 1, importData);
|
||||
}
|
||||
} else {
|
||||
this.variables.splice(this.variables.length - 1, 0, importData);
|
||||
}
|
||||
})
|
||||
},
|
||||
exportVariable() {
|
||||
if (this.$refs.variableTable.selectIds.length < 1) {
|
||||
this.$warning(this.$t('api_test.environment.select_environment'));
|
||||
return;
|
||||
}
|
||||
let variablesJson = [];
|
||||
let messages = '';
|
||||
let rows = this.$refs.variableTable.selectRows;
|
||||
rows.forEach(row => {
|
||||
if (row.type === 'CSV') {
|
||||
messages = this.$t('variables.csv_download')
|
||||
}
|
||||
if (row.name) {
|
||||
variablesJson.push(row);
|
||||
}
|
||||
})
|
||||
if (messages !== '') {
|
||||
this.$warning(messages);
|
||||
return;
|
||||
}
|
||||
downloadFile('MS_' + variablesJson.length + '_Environments_variables.json', JSON.stringify(variablesJson));
|
||||
},
|
||||
batchAddParameter() {
|
||||
this.$refs.batchAddParameter.open();
|
||||
},
|
||||
|
@ -241,20 +305,21 @@ export default {
|
|||
let params = data.split("\n");
|
||||
let keyValues = [];
|
||||
params.forEach(item => {
|
||||
if (item) {
|
||||
let line = item.split(/:|:/);
|
||||
let required = false;
|
||||
keyValues.unshift(new KeyValue({
|
||||
keyValues.push(new KeyValue({
|
||||
name: line[0],
|
||||
required: required,
|
||||
value: line[1],
|
||||
description: line[2],
|
||||
type: "text",
|
||||
type: "CONSTANT",
|
||||
valid: false,
|
||||
file: false,
|
||||
encode: true,
|
||||
enable: true,
|
||||
contentType: "text/plain"
|
||||
}));
|
||||
}
|
||||
})
|
||||
return keyValues;
|
||||
}
|
||||
|
@ -278,7 +343,7 @@ export default {
|
|||
}
|
||||
}
|
||||
if (isAdd) {
|
||||
this.headers.unshift(obj);
|
||||
this.headers.splice(this.headers.indexOf(h => !h.name), 0, obj);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -346,6 +411,18 @@ export default {
|
|||
this.disabled = disabled;
|
||||
this.$nextTick(() => {
|
||||
this.$refs.variableTable.doLayout();
|
||||
let variable = [];
|
||||
let messages = '';
|
||||
this.variables.forEach(item => {
|
||||
if (variable.indexOf(item.name) !== -1) {
|
||||
messages += item.name + ' ,';
|
||||
} else {
|
||||
variable.push(item.name);
|
||||
}
|
||||
})
|
||||
if (messages !== '') {
|
||||
this.$alert(this.$t('api_test.scenario.variables') + "【" + messages.substr(0, messages.length - 1) + "】" + this.$t('load_test.param_is_duplicate'));
|
||||
}
|
||||
});
|
||||
},
|
||||
save() {
|
||||
|
@ -382,6 +459,18 @@ export default {
|
|||
this.$warning(this.$t('api_test.automation.variable_warning'));
|
||||
return;
|
||||
}
|
||||
let repeatKey = "";
|
||||
if (!this.showDelete) {
|
||||
this.variables.forEach((item, index) => {
|
||||
if (item.name === this.editData.name) {
|
||||
repeatKey = item.name;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (repeatKey !== "") {
|
||||
this.$warning(this.$t('api_test.scenario.variables') + "【" + repeatKey + "】" + this.$t('load_test.param_is_duplicate'));
|
||||
return;
|
||||
}
|
||||
if (this.editData.type === 'CSV' && this.$refs.csv) {
|
||||
if (this.editData.files.length === 0) {
|
||||
this.$warning(this.$t('api_test.automation.csv_warning'));
|
||||
|
@ -407,7 +496,7 @@ export default {
|
|||
deleteVariable() {
|
||||
let ids = [this.editData.id];
|
||||
if (ids.length == 0) {
|
||||
this.$warning("请选择一条数据删除");
|
||||
this.$warning(this.$t('api_test.environment.delete_info'));
|
||||
return;
|
||||
}
|
||||
let message = "";
|
||||
|
@ -419,7 +508,7 @@ export default {
|
|||
});
|
||||
if (message !== "") {
|
||||
message = message.substr(0, message.length - 1);
|
||||
this.$alert('是否确认删除变量:【 ' + message + " 】?", '', {
|
||||
this.$alert(this.$t('api_test.environment.variables_delete_info') + ':【 ' + message + " 】?", '', {
|
||||
confirmButtonText: this.$t('commons.confirm'),
|
||||
callback: (action) => {
|
||||
if (action === 'confirm') {
|
||||
|
@ -442,7 +531,7 @@ export default {
|
|||
}
|
||||
},
|
||||
handleDeleteBatch() {
|
||||
this.$alert("是否确认删除所选变量" + ' ' + " ?", '', {
|
||||
this.$alert(this.$t('api_test.environment.variables_delete_info') + ' ' + " ?", '', {
|
||||
confirmButtonText: this.$t('commons.confirm'),
|
||||
callback: (action) => {
|
||||
if (action === 'confirm') {
|
||||
|
|
|
@ -8,12 +8,15 @@
|
|||
<div v-html="$t('api_test.batch_add_parameter')"/>
|
||||
</el-col>
|
||||
<el-col :span="10" class="buttons">
|
||||
<el-button size="mini" @click="handleClose">{{$t('commons.cancel')}}</el-button>
|
||||
<el-button type="primary" size="mini" @click="confirm" @keydown.enter.native.prevent>{{$t('commons.confirm')}}</el-button>
|
||||
<el-button size="mini" @click="handleClose">{{ $t('commons.cancel') }}</el-button>
|
||||
<el-button type="primary" size="mini" @click="confirm" @keydown.enter.native.prevent>
|
||||
{{ $t('commons.confirm') }}
|
||||
</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<div class="ms-code">
|
||||
<ms-code-edit class="ms-code" :enable-format="false" mode="text" :data.sync="parameters" theme="eclipse" :modes="['text']"
|
||||
<ms-code-edit class="ms-code" :enable-format="false" mode="text" :data.sync="parameters" theme="eclipse"
|
||||
:modes="['text']"
|
||||
ref="codeEdit"/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -22,12 +25,12 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import MsDialogFooter from "../../../../common/components/MsDialogFooter";
|
||||
import {listenGoBack, removeGoBackListener} from "@/common/js/utils";
|
||||
import MsCodeEdit from "../../../../common/components/MsCodeEdit";
|
||||
import MsDrawer from "../../../../common/components/MsDrawer";
|
||||
import MsDialogFooter from "../../../../common/components/MsDialogFooter";
|
||||
import {listenGoBack, removeGoBackListener} from "@/common/js/utils";
|
||||
import MsCodeEdit from "../../../../common/components/MsCodeEdit";
|
||||
import MsDrawer from "../../../../common/components/MsDrawer";
|
||||
|
||||
export default {
|
||||
export default {
|
||||
name: "BatchAddParameter",
|
||||
components: {
|
||||
MsDrawer,
|
||||
|
@ -52,29 +55,45 @@
|
|||
removeGoBackListener(this.handleClose);
|
||||
},
|
||||
confirm() {
|
||||
let params = this.parameters.split("\n");
|
||||
let index = 1;
|
||||
let isNormal = true;
|
||||
params.forEach(item => {
|
||||
if (item) {
|
||||
let line = item.split(/:|:/);
|
||||
if (!line[0]) {
|
||||
isNormal = false;
|
||||
this.$warning(this.$t('api_test.params_format_warning', [index]) + " :" + this.$t('api_test.automation.variable_warning'));
|
||||
return;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
});
|
||||
if (isNormal) {
|
||||
this.dialogVisible = false;
|
||||
this.$emit("batchSave", this.parameters);
|
||||
this.parameters = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.ms-drawer {
|
||||
.ms-drawer {
|
||||
padding: 10px 13px;
|
||||
}
|
||||
}
|
||||
|
||||
.ms-code {
|
||||
.ms-code {
|
||||
height: calc(100vh);
|
||||
}
|
||||
}
|
||||
|
||||
.buttons .el-button {
|
||||
.buttons .el-button {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
|
||||
.buttons .el-button:nth-child(2) {
|
||||
.buttons .el-button:nth-child(2) {
|
||||
margin-right: 15px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -302,7 +302,7 @@ export default {
|
|||
}
|
||||
}
|
||||
if (isAdd) {
|
||||
this.body.kvs.unshift(obj);
|
||||
this.body.kvs.splice(this.body.kvs.indexOf(kv => !kv.name), 0, obj);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -311,11 +311,12 @@ export default {
|
|||
let params = data.split("\n");
|
||||
let keyValues = [];
|
||||
params.forEach(item => {
|
||||
if (item) {
|
||||
let line = [];
|
||||
line[0] = item.substring(0, item.indexOf(":"));
|
||||
line[1] = item.substring(item.indexOf(":") + 1, item.length);
|
||||
let required = false;
|
||||
keyValues.unshift(new KeyValue({
|
||||
keyValues.push(new KeyValue({
|
||||
name: line[0],
|
||||
required: required,
|
||||
value: line[1],
|
||||
|
@ -327,6 +328,7 @@ export default {
|
|||
enable: true,
|
||||
contentType: "text/plain"
|
||||
}));
|
||||
}
|
||||
})
|
||||
keyValues.forEach(item => {
|
||||
this.format(this.body.kvs, item);
|
||||
|
|
|
@ -2,19 +2,22 @@
|
|||
<el-main v-loading="result.loading">
|
||||
<el-form :model="environment" :rules="rules" ref="environment">
|
||||
|
||||
<span>{{$t('api_test.environment.name')}}</span>
|
||||
<span>{{ $t('api_test.environment.name') }}</span>
|
||||
<el-form-item prop="name">
|
||||
<el-input v-model="environment.name" :disabled="isReadOnly" :placeholder="this.$t('commons.input_name')" clearable/>
|
||||
<el-input v-model="environment.name" :disabled="isReadOnly" :placeholder="this.$t('commons.input_name')"
|
||||
clearable/>
|
||||
</el-form-item>
|
||||
|
||||
|
||||
<el-tabs v-model="activeName">
|
||||
<el-tab-pane :label="$t('api_test.environment.common_config')" name="common">
|
||||
<ms-environment-common-config :common-config="environment.config.commonConfig" ref="commonConfig" :is-read-only="isReadOnly"/>
|
||||
<ms-environment-common-config :common-config="environment.config.commonConfig" ref="commonConfig"
|
||||
:is-read-only="isReadOnly"/>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane :label="$t('api_test.environment.http_config')" name="http">
|
||||
<ms-environment-http-config :http-config="environment.config.httpConfig" ref="httpConfig" :is-read-only="isReadOnly"/>
|
||||
<ms-environment-http-config :http-config="environment.config.httpConfig" ref="httpConfig"
|
||||
:is-read-only="isReadOnly"/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('api_test.environment.database_config')" name="sql">
|
||||
<ms-database-config :configs="environment.config.databaseConfigs" :is-read-only="isReadOnly"/>
|
||||
|
@ -34,24 +37,27 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import MsApiScenarioVariables from "../ApiScenarioVariables";
|
||||
import MsApiKeyValue from "../ApiKeyValue";
|
||||
import MsDialogFooter from "../../../../common/components/MsDialogFooter";
|
||||
import {REQUEST_HEADERS} from "@/common/js/constants";
|
||||
import {Environment} from "../../model/EnvironmentModel";
|
||||
import MsApiHostTable from "./ApiHostTable";
|
||||
import MsDatabaseConfig from "../request/database/DatabaseConfig";
|
||||
import MsEnvironmentHttpConfig from "../../../test/components/environment/EnvironmentHttpConfig";
|
||||
import MsEnvironmentCommonConfig from "./EnvironmentCommonConfig";
|
||||
import EnvironmentTcpConfig from "./EnvironmentTcpConfig";
|
||||
import MsApiScenarioVariables from "../ApiScenarioVariables";
|
||||
import MsApiKeyValue from "../ApiKeyValue";
|
||||
import MsDialogFooter from "../../../../common/components/MsDialogFooter";
|
||||
import {REQUEST_HEADERS} from "@/common/js/constants";
|
||||
import {Environment} from "../../model/EnvironmentModel";
|
||||
import MsApiHostTable from "./ApiHostTable";
|
||||
import MsDatabaseConfig from "../request/database/DatabaseConfig";
|
||||
import MsEnvironmentHttpConfig from "../../../test/components/environment/EnvironmentHttpConfig";
|
||||
import MsEnvironmentCommonConfig from "./EnvironmentCommonConfig";
|
||||
import EnvironmentTcpConfig from "./EnvironmentTcpConfig";
|
||||
import {getUploadConfig, request} from "@/common/js/ajax";
|
||||
import {getUUID} from "@/common/js/utils";
|
||||
|
||||
export default {
|
||||
export default {
|
||||
name: "EnvironmentEdit",
|
||||
components: {
|
||||
EnvironmentTcpConfig,
|
||||
MsEnvironmentCommonConfig,
|
||||
MsEnvironmentHttpConfig,
|
||||
MsDatabaseConfig, MsApiHostTable, MsDialogFooter, MsApiKeyValue, MsApiScenarioVariables},
|
||||
MsDatabaseConfig, MsApiHostTable, MsDialogFooter, MsApiKeyValue, MsApiScenarioVariables
|
||||
},
|
||||
props: {
|
||||
environment: new Environment(),
|
||||
isReadOnly: {
|
||||
|
@ -99,17 +105,115 @@
|
|||
return isValidate;
|
||||
},
|
||||
_save(environment) {
|
||||
if (!this.projectId) {
|
||||
this.$warning(this.$t('api_test.select_project'));
|
||||
return;
|
||||
}
|
||||
if (environment && environment.config && environment.config.commonConfig && environment.config.commonConfig.variables) {
|
||||
let repeatKey = this.check(environment.config.commonConfig && environment.config.commonConfig.variables);
|
||||
if (repeatKey !== "") {
|
||||
this.$warning(this.$t('api_test.environment.common_config') + "【" + repeatKey + "】" + this.$t('load_test.param_is_duplicate'));
|
||||
return;
|
||||
}
|
||||
}
|
||||
let message = '';
|
||||
if (environment && environment.config && environment.config.httpConfig && environment.config.httpConfig.conditions) {
|
||||
environment.config.httpConfig.conditions.forEach(env => {
|
||||
if (env.type === "MODULE" && env.details.length === 0) {
|
||||
message += this.$t('load_test.domain') + ":" + env.socket + ":" + this.$t('api_test.environment.module_warning');
|
||||
return;
|
||||
}
|
||||
if (env.type === "PATH" && env.details) {
|
||||
env.details.forEach(item => {
|
||||
if (!item.name) {
|
||||
message += this.$t('load_test.domain') + ":" + env.socket + ":" + this.$t('api_test.environment.path_warning');
|
||||
return;
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
environment.config.commonConfig.variables.forEach(variable => {
|
||||
if (variable.type === 'CSV' && variable.files.length === 0) {
|
||||
message = this.$t('api_test.automation.csv_warning');
|
||||
return;
|
||||
}
|
||||
})
|
||||
if (message) {
|
||||
this.$warning(message);
|
||||
return;
|
||||
}
|
||||
|
||||
let bodyFiles = this.geFiles(environment);
|
||||
let variablesFiles = this.getVariablesFiles(environment);
|
||||
let formData = new FormData();
|
||||
if (bodyFiles) {
|
||||
bodyFiles.forEach(f => {
|
||||
formData.append("files", f);
|
||||
})
|
||||
}
|
||||
if (variablesFiles) {
|
||||
variablesFiles.forEach(f => {
|
||||
formData.append("variablesFiles", f);
|
||||
})
|
||||
}
|
||||
let param = this.buildParam(environment);
|
||||
let url = '/api/environment/add';
|
||||
if (param.id) {
|
||||
url = '/api/environment/update';
|
||||
}
|
||||
this.result = this.$fileUpload(url, null, [], param, response => {
|
||||
if (!param.id) {
|
||||
environment.id = response.data;
|
||||
}
|
||||
formData.append('request', new Blob([JSON.stringify(param)], {type: "application/json"}));
|
||||
let axiosRequestConfig = getUploadConfig(url, formData);
|
||||
request(axiosRequestConfig, (response) => {
|
||||
if (response.success) {
|
||||
this.$success(this.$t('commons.save_success'));
|
||||
this.$emit('refreshAfterSave'); //在EnvironmentList.vue中监听,使在数据修改后进行刷新
|
||||
this.cancel()
|
||||
}
|
||||
}, error => {
|
||||
this.$emit('errorRefresh', error);
|
||||
});
|
||||
|
||||
},
|
||||
geFiles(obj) {
|
||||
let uploadFiles = [];
|
||||
obj.uploadIds = [];
|
||||
if (obj.config && obj.config.sslConfig && obj.config.sslConfig.files) {
|
||||
obj.config.sslConfig.files.forEach(item => {
|
||||
if (item.file && item.file.size > 0) {
|
||||
if (!item.id) {
|
||||
item.name = item.file.name;
|
||||
item.id = getUUID();
|
||||
}
|
||||
obj.uploadIds.push(item.id);
|
||||
uploadFiles.push(item.file);
|
||||
}
|
||||
})
|
||||
}
|
||||
return uploadFiles;
|
||||
},
|
||||
getVariablesFiles(obj) {
|
||||
let variablesFiles = [];
|
||||
obj.variablesFilesIds = [];
|
||||
// 场景变量csv 文件
|
||||
if (obj.config.commonConfig.variables) {
|
||||
obj.config.commonConfig.variables.forEach(param => {
|
||||
if (param.type === 'CSV' && param.files) {
|
||||
param.files.forEach(item => {
|
||||
if (item.file && item.file.name) {
|
||||
if (!item.id) {
|
||||
let fileId = getUUID().substring(0, 12);
|
||||
item.name = item.file.name;
|
||||
item.id = fileId;
|
||||
}
|
||||
obj.variablesFilesIds.push(item.id);
|
||||
variablesFiles.push(item.file);
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
return variablesFiles;
|
||||
},
|
||||
buildParam: function (environment) {
|
||||
let param = {};
|
||||
|
@ -135,35 +239,35 @@
|
|||
this.$refs["environment"].clearValidate();
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.el-main {
|
||||
.el-main {
|
||||
border: solid 1px #EBEEF5;
|
||||
margin-left: 200px;
|
||||
min-height: 400px;
|
||||
max-height: 700px;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.el-row {
|
||||
.el-row {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.environment-footer {
|
||||
.environment-footer {
|
||||
margin-top: 15px;
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
|
||||
span {
|
||||
span {
|
||||
display: block;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
span:not(:first-child) {
|
||||
span:not(:first-child) {
|
||||
margin-top: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
|
@ -274,11 +274,12 @@ export default {
|
|||
let params = data.split("\n");
|
||||
let keyValues = [];
|
||||
params.forEach(item => {
|
||||
if (item) {
|
||||
let line = [];
|
||||
line[0] = item.substring(0,item.indexOf(":"));
|
||||
line[1] = item.substring(item.indexOf(":")+1,item.length);
|
||||
line[0] = item.substring(0, item.indexOf(":"));
|
||||
line[1] = item.substring(item.indexOf(":") + 1, item.length);
|
||||
let required = false;
|
||||
keyValues.unshift(new KeyValue({
|
||||
keyValues.push(new KeyValue({
|
||||
name: line[0],
|
||||
required: required,
|
||||
value: line[1],
|
||||
|
@ -290,6 +291,7 @@ export default {
|
|||
enable: true,
|
||||
contentType: "text/plain"
|
||||
}));
|
||||
}
|
||||
})
|
||||
keyValues.forEach(item => {
|
||||
this.format(this.body.kvs, item);
|
||||
|
|
|
@ -309,11 +309,12 @@ export default {
|
|||
let params = data.split("\n");
|
||||
let keyValues = [];
|
||||
params.forEach(item => {
|
||||
if (item) {
|
||||
let line = [];
|
||||
line[0] = item.substring(0, item.indexOf(":"));
|
||||
line[1] = item.substring(item.indexOf(":") + 1, item.length);
|
||||
let required = false;
|
||||
keyValues.unshift(new KeyValue({
|
||||
keyValues.push(new KeyValue({
|
||||
name: line[0],
|
||||
required: required,
|
||||
value: line[1],
|
||||
|
@ -325,6 +326,7 @@ export default {
|
|||
enable: true,
|
||||
contentType: "text/plain"
|
||||
}));
|
||||
}
|
||||
})
|
||||
keyValues.forEach(item => {
|
||||
this.format(this.body.kvs, item);
|
||||
|
|
|
@ -424,13 +424,13 @@ export default {
|
|||
if (isAdd) {
|
||||
switch (this.activeName) {
|
||||
case "parameters":
|
||||
this.request.arguments.unshift(obj);
|
||||
this.request.arguments.splice(this.request.arguments.indexOf(h => !h.name), 0, obj);
|
||||
break;
|
||||
case "rest":
|
||||
this.request.rest.unshift(obj);
|
||||
this.request.rest.splice(this.request.rest.indexOf(h => !h.name), 0, obj);
|
||||
break;
|
||||
case "headers":
|
||||
this.request.headers.unshift(obj);
|
||||
this.request.headers.splice(this.request.headers.indexOf(h => !h.name), 0, obj);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -443,10 +443,11 @@ export default {
|
|||
let params = data.split("\n");
|
||||
let keyValues = [];
|
||||
params.forEach(item => {
|
||||
if (item) {
|
||||
let line = item.split(/:|:/);
|
||||
let values = item.split(line[0] + ":");
|
||||
let required = false;
|
||||
keyValues.unshift(new KeyValue({
|
||||
keyValues.push(new KeyValue({
|
||||
name: line[0],
|
||||
required: required,
|
||||
value: values[1],
|
||||
|
@ -457,6 +458,7 @@ export default {
|
|||
enable: true,
|
||||
contentType: "text/plain"
|
||||
}));
|
||||
}
|
||||
})
|
||||
|
||||
keyValues.forEach(item => {
|
||||
|
|
|
@ -8,9 +8,9 @@
|
|||
:data="environments" :item-operators="environmentOperators" :add-fuc="addEnvironment"
|
||||
:env-add-permission="ENV_CREATE"
|
||||
:delete-fuc="openDelEnv" @itemSelected="environmentSelected" ref="environmentItems"/>
|
||||
<environment-edit :if-create="ifCreate" :project-id="projectId" :environment="currentEnvironment"
|
||||
<environment-edit :if-create="ifCreate" :environment="currentEnvironment"
|
||||
ref="environmentEdit" :is-read-only="isReadOnly"
|
||||
@confirm="save"
|
||||
@confirm="save" :is-project="true"
|
||||
@close="close"/>
|
||||
</el-container>
|
||||
</el-dialog>
|
||||
|
|
|
@ -112,7 +112,7 @@ export default {
|
|||
let line = item.split(/:|:/);
|
||||
let values = item.split(line[0] + ":");
|
||||
let required = false;
|
||||
keyValues.unshift(new KeyValue({
|
||||
keyValues.push(new KeyValue({
|
||||
name: line[0],
|
||||
required: required,
|
||||
value: values[1],
|
||||
|
|
|
@ -0,0 +1,399 @@
|
|||
<template>
|
||||
<div>
|
||||
<div>
|
||||
<div style="padding-bottom: 10px;float: left">
|
||||
<el-input :placeholder="$t('commons.search_by_name')" size="mini" v-model="selectVariable"
|
||||
@change="filter"
|
||||
@keyup.enter="filter">
|
||||
</el-input>
|
||||
</div>
|
||||
<div style="padding-bottom: 10px; float: right;">
|
||||
<ms-table-button v-permission="['PROJECT_ENVIRONMENT:READ+IMPORT']" icon="el-icon-box"
|
||||
:content="$t('commons.import')" @click="importJSON"/>
|
||||
<ms-table-button v-permission="['PROJECT_ENVIRONMENT:READ+EXPORT']" icon="el-icon-box"
|
||||
:content="$t('commons.export')" @click="exportJSON"/>
|
||||
<el-link style="margin-left: 10px" @click="batchAdd" type="primary" :disabled="isReadOnly">
|
||||
{{ $t("commons.batch_add") }}
|
||||
</el-link>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
style="border:1px #DCDFE6 solid; min-height: 300px;border-radius: 4px ;width: 99% ;margin-top: 10px; clear: both">
|
||||
<ms-table
|
||||
v-loading="loading"
|
||||
row-key="id"
|
||||
:data="variables"
|
||||
:total="items.length"
|
||||
:screen-height="screenHeight"
|
||||
:batch-operators="batchButtons"
|
||||
:remember-order="true"
|
||||
:highlightCurrentRow="true"
|
||||
@refresh="onChange"
|
||||
ref="variableTable">
|
||||
|
||||
<ms-table-column
|
||||
prop="num"
|
||||
sortable
|
||||
label="ID"
|
||||
min-width="60">
|
||||
</ms-table-column>
|
||||
<ms-table-column prop="name" :label="$t('api_test.variable_name')" min-width="200" sortable>
|
||||
<template slot-scope="scope">
|
||||
<el-input
|
||||
v-model="scope.row.name" size="mini"
|
||||
maxlength="200" :placeholder="$t('api_test.variable_name')" show-word-limit
|
||||
@change="change"/>
|
||||
</template>
|
||||
</ms-table-column>
|
||||
<ms-table-column prop="type" :label="$t('test_track.case.type')" min-width="140" sortable>
|
||||
<template slot-scope="scope">
|
||||
<el-select v-model="scope.row.type" :placeholder="$t('commons.please_select')" size="mini"
|
||||
@change="changeType(scope.row)">
|
||||
<el-option v-for="item in typeSelectOptions " :key="item.value" :label="item.label" :value="item.value"/>
|
||||
</el-select>
|
||||
</template>
|
||||
</ms-table-column>
|
||||
|
||||
<ms-table-column prop="value" :label="$t('api_test.value')"
|
||||
min-width="200px" sortable>
|
||||
<template slot-scope="scope">
|
||||
<el-input v-model="scope.row.value" size="mini" v-if="scope.row.type !=='CSV'"
|
||||
:placeholder="valueText(scope.row)"
|
||||
:disabled="scope.row.type === 'COUNTER' || scope.row.type === 'RANDOM'"/>
|
||||
<csv-file-upload :parameter="scope.row" v-if="scope.row.type ==='CSV'"/>
|
||||
</template>
|
||||
</ms-table-column>
|
||||
<ms-table-column prop="description" :label="$t('commons.remark')"
|
||||
min-width="160" sortable>
|
||||
<template slot-scope="scope">
|
||||
<el-input v-model="scope.row.description" size="mini"/>
|
||||
</template>
|
||||
</ms-table-column>
|
||||
|
||||
<ms-table-column :label="$t('commons.operating')" width="150" fixed="right">
|
||||
<template v-slot:default="scope">
|
||||
<span>
|
||||
<el-switch v-model="scope.row.enable" size="mini"/>
|
||||
<el-tooltip effect="dark" :content="$t('commons.remove')" placement="top-start">
|
||||
<el-button icon="el-icon-delete" type="danger" circle size="mini" style="margin-left: 10px"
|
||||
@click="remove(scope.row)" v-if="scope.row.name"/>
|
||||
</el-tooltip>
|
||||
<el-tooltip effect="dark" :content="$t('schema.adv_setting')" placement="top-start">
|
||||
<el-button icon="el-icon-setting" circle size="mini" style="margin-left: 10px"
|
||||
@click="openSetting(scope.row)" v-if="scope.row.type !=='LIST'"/>
|
||||
</el-tooltip>
|
||||
|
||||
</span>
|
||||
</template>
|
||||
</ms-table-column>
|
||||
</ms-table>
|
||||
</div>
|
||||
<batch-add-parameter @batchSave="batchSave" ref="batchAdd"/>
|
||||
<api-variable-setting ref="apiVariableSetting"></api-variable-setting>
|
||||
<variable-import ref="variableImport" @mergeData="mergeData"></variable-import>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {KeyValue} from "@/business/components/api/test/model/ScenarioModel";
|
||||
import MsApiVariableInput from "@/business/components/api/automation/scenario/ApiVariableInput";
|
||||
import BatchAddParameter from "@/business/components/api/definition/components/basis/BatchAddParameter";
|
||||
import MsTableButton from "@/business/components/common/components/MsTableButton";
|
||||
import MsTable from "@/business/components/common/components/table/MsTable";
|
||||
import MsTableColumn from "@/business/components/common/components/table/MsTableColumn";
|
||||
import ApiVariableSetting from "@/business/components/api/test/components/environment/ApiVariableSetting";
|
||||
import CsvFileUpload from "@/business/components/api/test/components/environment/CsvFileUpload";
|
||||
import {downloadFile, getUUID} from "@/common/js/utils";
|
||||
import VariableImport from "@/business/components/api/test/components/environment/VariableImport";
|
||||
|
||||
export default {
|
||||
name: "MsApiScenarioVariables",
|
||||
components: {
|
||||
BatchAddParameter,
|
||||
MsApiVariableInput,
|
||||
MsTableButton,
|
||||
MsTable,
|
||||
MsTableColumn,
|
||||
ApiVariableSetting,
|
||||
CsvFileUpload,
|
||||
VariableImport
|
||||
},
|
||||
props: {
|
||||
description: String,
|
||||
items: Array,
|
||||
isReadOnly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
showVariable: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
showCopy: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
screenHeight: '400px',
|
||||
batchButtons: [
|
||||
{
|
||||
name: this.$t('api_test.definition.request.batch_delete'),
|
||||
handleClick: this.handleDeleteBatch,
|
||||
},
|
||||
],
|
||||
typeSelectOptions: [
|
||||
{value: 'CONSTANT', label: this.$t('api_test.automation.constant')},
|
||||
{value: 'LIST', label: this.$t('test_track.case.list')},
|
||||
{value: 'CSV', label: 'CSV'},
|
||||
{value: 'COUNTER', label: this.$t('api_test.automation.counter')},
|
||||
{value: 'RANDOM', label: this.$t('api_test.automation.random')},
|
||||
],
|
||||
variables: {},
|
||||
selectVariable: '',
|
||||
editData: {},
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
items: {
|
||||
handler(v) {
|
||||
this.variables = v;
|
||||
this.sortParameters();
|
||||
},
|
||||
immediate: true,
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
remove: function (index) {
|
||||
const dataIndex = this.variables.findIndex(d => d.name === index.name);
|
||||
this.variables.splice(dataIndex, 1);
|
||||
this.$emit('change', this.variables);
|
||||
},
|
||||
change: function () {
|
||||
let isNeedCreate = true;
|
||||
let removeIndex = -1;
|
||||
let repeatKey = "";
|
||||
this.variables.forEach((item, index) => {
|
||||
this.variables.forEach((row, rowIndex) => {
|
||||
if (item.name === row.name && index !== rowIndex) {
|
||||
repeatKey = item.name;
|
||||
}
|
||||
});
|
||||
if (!item.name && !item.value) {
|
||||
// 多余的空行
|
||||
if (index !== this.items.length - 1) {
|
||||
removeIndex = index;
|
||||
}
|
||||
// 没有空行,需要创建空行
|
||||
isNeedCreate = false;
|
||||
}
|
||||
});
|
||||
if (repeatKey !== "") {
|
||||
this.$warning(this.$t('api_test.environment.common_config') + "【" + repeatKey + "】" + this.$t('load_test.param_is_duplicate'));
|
||||
}
|
||||
if (isNeedCreate && !repeatKey) {
|
||||
this.variables.push(new KeyValue({enable: true, id: getUUID(), type: 'CONSTANT'}));
|
||||
}
|
||||
this.$emit('change', this.variables);
|
||||
// TODO 检查key重复
|
||||
|
||||
},
|
||||
changeType(data) {
|
||||
if (!data.delimiter || (!data.files && data.files.length === 0) || !data.quotedData) {
|
||||
data.delimiter = ',';
|
||||
data.files = [];
|
||||
data.quotedData = 'false';
|
||||
}
|
||||
},
|
||||
valueText(data) {
|
||||
switch (data.type) {
|
||||
case 'LIST':
|
||||
return this.$t('api_test.environment.list_info');
|
||||
case 'CONSTANT':
|
||||
return this.$t('api_test.value');
|
||||
case 'COUNTER':
|
||||
case 'RANDOM':
|
||||
return this.$t('api_test.environment.advanced_setting');
|
||||
default:
|
||||
return this.$t('api_test.value');
|
||||
}
|
||||
},
|
||||
querySearch(queryString, cb) {
|
||||
let restaurants = [{value: "UTF-8"}, {value: "UTF-16"}, {value: "GB2312"}, {value: "ISO-8859-15"}, {value: "US-ASCll"}];
|
||||
let results = queryString ? restaurants.filter(this.createFilter(queryString)) : restaurants;
|
||||
// 调用 callback 返回建议列表的数据
|
||||
cb(results);
|
||||
},
|
||||
sortParameters() {
|
||||
let index = 1;
|
||||
this.variables.forEach(item => {
|
||||
item.num = index;
|
||||
if (!item.type || item.type === 'text') {
|
||||
item.type = 'CONSTANT';
|
||||
}
|
||||
if (!item.id) {
|
||||
item.id = getUUID();
|
||||
}
|
||||
if (item.remark) {
|
||||
item.description = item.remark;
|
||||
}
|
||||
index++;
|
||||
});
|
||||
},
|
||||
handleDeleteBatch() {
|
||||
this.$alert(this.$t('api_test.environment.variables_delete_info') + ' ' + " ?", '', {
|
||||
confirmButtonText: this.$t('commons.confirm'),
|
||||
callback: (action) => {
|
||||
if (action === 'confirm') {
|
||||
let ids = this.$refs.variableTable.selectRows;
|
||||
ids.forEach(row => {
|
||||
if (row.name) {
|
||||
const index = this.variables.findIndex(d => d.name === row.name);
|
||||
this.variables.splice(index, 1);
|
||||
}
|
||||
});
|
||||
this.sortParameters();
|
||||
this.$refs.variableTable.cancelCurrentRow();
|
||||
this.$refs.variableTable.clear();
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
filter() {
|
||||
let datas = [];
|
||||
this.variables.forEach(item => {
|
||||
if (this.selectVariable && this.selectVariable != "" && item.name) {
|
||||
if (item.name.toLowerCase().indexOf(this.selectVariable.toLowerCase()) == -1) {
|
||||
item.hidden = true;
|
||||
} else {
|
||||
item.hidden = undefined;
|
||||
}
|
||||
} else {
|
||||
item.hidden = undefined;
|
||||
}
|
||||
datas.push(item);
|
||||
});
|
||||
this.variables = datas;
|
||||
},
|
||||
openSetting(data) {
|
||||
this.$refs.apiVariableSetting.open(data);
|
||||
},
|
||||
isDisable: function (index) {
|
||||
return this.items.length - 1 === index;
|
||||
},
|
||||
_handleBatchVars(data) {
|
||||
let params = data.split("\n");
|
||||
let keyValues = [];
|
||||
params.forEach(item => {
|
||||
if (item) {
|
||||
let line = item.split(/:|:/);
|
||||
let values = item.split(line[0] + ":");
|
||||
let required = false;
|
||||
keyValues.push(new KeyValue({
|
||||
name: line[0],
|
||||
required: required,
|
||||
value: values[1],
|
||||
type: 'CONSTANT',
|
||||
valid: false,
|
||||
file: false,
|
||||
encode: true,
|
||||
enable: true,
|
||||
description: undefined
|
||||
}));
|
||||
}
|
||||
});
|
||||
return keyValues;
|
||||
},
|
||||
batchAdd() {
|
||||
this.$refs.batchAdd.open();
|
||||
},
|
||||
batchSave(data) {
|
||||
if (data) {
|
||||
let keyValues = this._handleBatchVars(data);
|
||||
keyValues.forEach(keyValue => {
|
||||
let isAdd = true;
|
||||
this.variables.forEach(item => {
|
||||
if (item.name === keyValue.name) {
|
||||
item.value = keyValue.value;
|
||||
isAdd = false;
|
||||
}
|
||||
})
|
||||
if (isAdd) {
|
||||
this.variables.splice(this.variables.indexOf(i => !i.name), 0, keyValue);
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
onChange() {
|
||||
this.sortParameters();
|
||||
},
|
||||
exportJSON() {
|
||||
if (this.$refs.variableTable.selectIds.length < 1) {
|
||||
this.$warning(this.$t('api_test.environment.select_variable'));
|
||||
return;
|
||||
}
|
||||
let variablesJson = [];
|
||||
let messages = '';
|
||||
let rows = this.$refs.variableTable.selectRows;
|
||||
rows.forEach(row => {
|
||||
if (row.type === 'CSV') {
|
||||
messages = this.$t('variables.csv_download')
|
||||
}
|
||||
if (row.name) {
|
||||
variablesJson.push(row);
|
||||
}
|
||||
})
|
||||
if (messages !== '') {
|
||||
this.$warning(messages);
|
||||
return;
|
||||
}
|
||||
downloadFile('MS_' + variablesJson.length + '_Environments_variables.json', JSON.stringify(variablesJson));
|
||||
},
|
||||
importJSON() {
|
||||
this.$refs.variableImport.open();
|
||||
},
|
||||
mergeData(data, modeId) {
|
||||
JSON.parse(data).map(importData => {
|
||||
importData.id = getUUID();
|
||||
importData.enable = true;
|
||||
importData.showMore = false;
|
||||
let sameNameIndex = this.variables.findIndex(d => d.name === importData.name);
|
||||
if (sameNameIndex !== -1) {
|
||||
if (modeId === 'fullCoverage') {
|
||||
this.variables.splice(sameNameIndex, 1, importData);
|
||||
}
|
||||
} else {
|
||||
this.variables.splice(this.variables.length - 1, 0, importData);
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
if (this.items.length === 0) {
|
||||
this.items.push(new KeyValue({enable: true}));
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.kv-description {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.kv-checkbox {
|
||||
width: 20px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.kv-row {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.kv-delete {
|
||||
width: 60px;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,306 @@
|
|||
<template>
|
||||
<div>
|
||||
<el-tabs tab-position="top" @tab-click="selectTab">
|
||||
<el-tab-pane :label="$t('api_test.request.parameters_advance_mock')">
|
||||
<el-row type="flex" :gutter="20">
|
||||
<el-col :span="6" class="col-height">
|
||||
<div>
|
||||
<el-input size="small" v-model="filterText"
|
||||
:placeholder="$t('api_test.request.parameters_mock_filter_tips')"/>
|
||||
<el-tree class="filter-tree" ref="tree" :data="mockFuncs" :props="treeProps"
|
||||
default-expand-all @node-click="selectVariable"
|
||||
:filter-node-method="filterNode"></el-tree>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="6" v-for="(itemFunc, itemIndex) in mockVariableFuncs" :key="itemIndex">
|
||||
<div v-for="(func, funcIndex) in funcs"
|
||||
:key="`${itemIndex}-${funcIndex}`">
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-radio size="mini" v-model="itemFunc.name" :label="func.name"
|
||||
@change="methodChange(itemFunc, func)" @click.native.prevent="radioClick(itemFunc, func)"/>
|
||||
</el-col>
|
||||
<el-col :span="12" v-if="itemFunc.name === func.name">
|
||||
<div v-for="(p, pIndex) in itemFunc.params" :key="`${itemIndex}-${funcIndex}-${pIndex}`">
|
||||
<el-input :placeholder="p.name" size="mini" v-model="p.value" @change="showPreview"/>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('api_test.variable')">
|
||||
<el-row>
|
||||
<el-col :span="6" class="col-height">
|
||||
<div v-if="environment">
|
||||
<p>{{ $t('api_test.environment.environment') }}</p>
|
||||
<el-tree :data="environmentParams" :props="treeProps" @node-click="selectVariable"></el-tree>
|
||||
</div>
|
||||
<div v-if="scenario">
|
||||
<p>{{ $t('api_test.scenario.scenario') }}</p>
|
||||
<el-tree :data="scenarioParams" :props="treeProps" @node-click="selectVariable"></el-tree>
|
||||
</div>
|
||||
<div v-if="preRequestParams">
|
||||
<p>{{ $t('api_test.request.parameters_pre_request') }}</p>
|
||||
<el-tree :data="preRequestParams" :props="treeProps" @node-click="selectVariable"></el-tree>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="18" class="col-height">
|
||||
<div>
|
||||
<h1>{{ $t('api_test.request.jmeter_func') }}</h1>
|
||||
<el-table border :data="jmeterFuncs" class="adjust-table table-content" height="400">
|
||||
<el-table-column prop="type" label="Type" width="150"/>
|
||||
<el-table-column prop="name" label="Functions" width="250"/>
|
||||
<el-table-column prop="description" label="Description"/>
|
||||
</el-table>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<el-form>
|
||||
<el-form-item>
|
||||
<el-input :placeholder="valueText" size="small"
|
||||
v-model="itemValue"/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div style="padding-top: 10px;">
|
||||
<el-row type="flex" align="middle">
|
||||
<el-col :span="12">
|
||||
<el-button size="small" type="info" plain @click="addFunc()" v-if="currentTab === 0">
|
||||
{{ $t('api_test.request.parameters_advance_add_func') }}
|
||||
</el-button>
|
||||
<el-button size="small" type="success" plain @click="showPreview()" v-if="currentTab === 0">
|
||||
{{ $t('api_test.request.parameters_preview') }}
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col>
|
||||
<div> {{ itemValuePreview }}</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {calculate, Scenario} from "@/business/components/api/test/model/ScenarioModel";
|
||||
import {JMETER_FUNC, MOCKJS_FUNC} from "@/common/js/constants";
|
||||
|
||||
export default {
|
||||
name: "MsApiVariableAdvance",
|
||||
props: {
|
||||
parameters: Array,
|
||||
environment: Object,
|
||||
scenario: Scenario,
|
||||
currentItem: Object,
|
||||
appendToBody: {
|
||||
type: Boolean,
|
||||
default() {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
itemValueVisible: false,
|
||||
filterText: '',
|
||||
environmentParams: [],
|
||||
scenarioParams: [],
|
||||
preRequests: [],
|
||||
preRequestParams: [],
|
||||
treeProps: {children: 'children', label: 'name'},
|
||||
currentTab: 0,
|
||||
itemValue: null,
|
||||
itemValuePreview: null,
|
||||
funcs: [
|
||||
{name: "md5"},
|
||||
{name: "base64"},
|
||||
{name: "unbase64"},
|
||||
{
|
||||
name: "substr",
|
||||
params: [{name: "start"}, {name: "length"}]
|
||||
},
|
||||
{
|
||||
name: "concat",
|
||||
params: [{name: "suffix"}]
|
||||
},
|
||||
{name: "lconcat", params: [{name: "prefix"}]},
|
||||
{name: "sha1"},
|
||||
{name: "sha224"},
|
||||
{name: "sha256"},
|
||||
{name: "sha384"},
|
||||
{name: "sha512"},
|
||||
{name: "lower"},
|
||||
{name: "upper"},
|
||||
{name: "length"},
|
||||
{name: "number"}
|
||||
],
|
||||
mockFuncs: MOCKJS_FUNC.map(f => {
|
||||
return {name: f.name, value: f.name}
|
||||
}),
|
||||
jmeterFuncs: JMETER_FUNC,
|
||||
mockVariableFuncs: [],
|
||||
jmeterVariableFuncs: [],
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
valueText() {
|
||||
return this.valuePlaceholder || this.$t("api_test.value");
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.prepareData();
|
||||
},
|
||||
watch: {
|
||||
filterText(val) {
|
||||
this.$refs.tree.filter(val);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
open() {
|
||||
this.itemValueVisible = true;
|
||||
},
|
||||
prepareData() {
|
||||
if (this.scenario) {
|
||||
let variables = this.scenario.variables;
|
||||
this.scenarioParams = [
|
||||
{
|
||||
name: this.scenario.name,
|
||||
children: variables.filter(v => v.name).map(v => {
|
||||
return {name: v.name, value: '${' + v.name + '}'}
|
||||
}),
|
||||
}
|
||||
];
|
||||
if (this.environment) {
|
||||
let variables = this.environment.config.commonConfig.variables;
|
||||
this.environmentParams = [
|
||||
{
|
||||
name: this.environment.name,
|
||||
children: variables.filter(v => v.name).map(v => {
|
||||
return {name: v.name, value: '${' + v.name + '}'}
|
||||
}),
|
||||
}
|
||||
];
|
||||
}
|
||||
let i = this.scenario.requests.indexOf(this.request);
|
||||
this.preRequests = this.scenario.requests.slice(0, i);
|
||||
this.preRequests.forEach(r => {
|
||||
let js = r.extract.json.map(v => {
|
||||
return {name: v.variable, value: v.value}
|
||||
});
|
||||
let xs = r.extract.xpath.map(v => {
|
||||
return {name: v.variable, value: v.value}
|
||||
});
|
||||
let rx = r.extract.regex.map(v => {
|
||||
return {name: v.variable, value: v.value}
|
||||
});
|
||||
let vs = [...js, ...xs, ...rx];
|
||||
if (vs.length > 0) {
|
||||
this.preRequestParams.push({name: r.name, children: vs});
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
filterNode(value, data) {
|
||||
if (!value) {
|
||||
return true;
|
||||
}
|
||||
return data.name.indexOf(value) !== -1;
|
||||
},
|
||||
selectVariable(node) {
|
||||
this.itemValue = node.value;
|
||||
},
|
||||
selectTab(tab) {
|
||||
this.currentTab = +tab.index;
|
||||
this.itemValue = null;
|
||||
this.itemValuePreview = null;
|
||||
},
|
||||
showPreview() {
|
||||
// 找到变量本身
|
||||
if (!this.itemValue) {
|
||||
return;
|
||||
}
|
||||
let index = this.itemValue.indexOf("|");
|
||||
if (index > -1) {
|
||||
this.itemValue = this.itemValue.substring(0, index).trim();
|
||||
}
|
||||
|
||||
this.mockVariableFuncs.forEach(f => {
|
||||
if (!f.name) {
|
||||
return;
|
||||
}
|
||||
this.itemValue += "|" + f.name;
|
||||
if (f.params) {
|
||||
this.itemValue += ":" + f.params.map(p => p.value).join(",");
|
||||
}
|
||||
});
|
||||
|
||||
this.itemValuePreview = calculate(this.itemValue);
|
||||
},
|
||||
methodChange(itemFunc, func) {
|
||||
let index = this.mockVariableFuncs.indexOf(itemFunc);
|
||||
this.mockVariableFuncs = this.mockVariableFuncs.slice(0, index);
|
||||
// 这里要用 deep copy
|
||||
this.mockVariableFuncs.push(JSON.parse(JSON.stringify(func)));
|
||||
this.showPreview();
|
||||
},
|
||||
radioClick(itemFunc, func) {
|
||||
if (itemFunc.name === func.name) {
|
||||
let index = this.mockVariableFuncs.indexOf(itemFunc);
|
||||
this.mockVariableFuncs = this.mockVariableFuncs.slice(0, index);
|
||||
this.mockVariableFuncs.push({name: '', params: []});
|
||||
let valindex = this.itemValue.indexOf('|' + func.name);
|
||||
this.itemValue = this.itemValue.slice(0, valindex);
|
||||
} else {
|
||||
this.methodChange(itemFunc, func);
|
||||
}
|
||||
},
|
||||
addFunc() {
|
||||
if (this.itemValue.indexOf('@') == -1) {
|
||||
this.itemValue = '@' + this.itemValue;
|
||||
} else {
|
||||
this.itemValue = this.itemValue;
|
||||
}
|
||||
if (this.mockVariableFuncs.length > 4) {
|
||||
this.$info(this.$t('api_test.request.parameters_advance_add_func_limit'));
|
||||
return;
|
||||
}
|
||||
if (this.mockVariableFuncs.length > 0) {
|
||||
let func = this.mockVariableFuncs[this.mockVariableFuncs.length - 1];
|
||||
if (!func.name) {
|
||||
this.$warning(this.$t('api_test.request.parameters_advance_add_func_error'));
|
||||
return;
|
||||
}
|
||||
if (func.params) {
|
||||
for (let j = 0; j < func.params.length; j++) {
|
||||
if (!func.params[j].value) {
|
||||
this.$warning(this.$t('api_test.request.parameters_advance_add_param_error'));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.mockVariableFuncs.push({name: '', params: []});
|
||||
},
|
||||
saveAdvanced() {
|
||||
if (this.itemValue && this.itemValue.indexOf('@') == -1) {
|
||||
this.currentItem.value = '@' + this.itemValue;
|
||||
} else {
|
||||
this.currentItem.value = this.itemValue;
|
||||
}
|
||||
this.itemValueVisible = false;
|
||||
this.mockVariableFuncs = [];
|
||||
this.$emit('advancedRefresh', this.currentItem.value);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.col-height {
|
||||
height: 40vh;
|
||||
overflow: auto;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,88 @@
|
|||
<template>
|
||||
<ms-edit-dialog
|
||||
:visible.sync="visible"
|
||||
width="700px"
|
||||
:title="$t('schema.adv_setting')"
|
||||
:with-footer="false"
|
||||
append-to-body
|
||||
:close-on-click-modal="true">
|
||||
<ms-api-variable-advance v-if="editData.type=='CONSTANT'" ref="variableAdvance" :current-item="editData"
|
||||
@advancedRefresh="reload"/>
|
||||
<ms-edit-counter v-if="editData.type=='COUNTER'" ref="counter" :editData.sync="editData"/>
|
||||
<ms-edit-random v-if="editData.type=='RANDOM'" ref="random" :editData.sync="editData"/>
|
||||
<ms-edit-csv v-if="editData.type=='CSV'" ref="csv" :editData.sync="editData"/>
|
||||
|
||||
<template v-slot:footer>
|
||||
<ms-dialog-footer
|
||||
@cancel="handleCancel"
|
||||
@confirm="handleConfirm"
|
||||
/>
|
||||
</template>
|
||||
</ms-edit-dialog>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import MsEditDialog from "@/business/components/common/components/MsEditDialog";
|
||||
import MsEditConstant from "@/business/components/api/automation/scenario/variable/EditConstant";
|
||||
import MsEditCounter from "@/business/components/api/automation/scenario/variable/EditCounter";
|
||||
import MsEditRandom from "@/business/components/api/automation/scenario/variable/EditRandom";
|
||||
import MsEditListValue from "@/business/components/api/automation/scenario/variable/EditListValue";
|
||||
import MsEditCsv from "@/business/components/api/automation/scenario/variable/EditCsv";
|
||||
import MsApiVariableAdvance from "@/business/components/api/test/components/environment/ApiVariableAdvance";
|
||||
import MsDialogFooter from "@/business/components/common/components/MsDialogFooter";
|
||||
|
||||
export default {
|
||||
name: "ApiVariableSetting",
|
||||
components: {
|
||||
MsEditDialog,
|
||||
MsEditConstant,
|
||||
MsEditCounter,
|
||||
MsEditRandom,
|
||||
MsEditListValue,
|
||||
MsEditCsv,
|
||||
MsApiVariableAdvance,
|
||||
MsDialogFooter
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
data: {},
|
||||
editData: {}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
open(item) {
|
||||
this.visible = true;
|
||||
this.editData = item;
|
||||
},
|
||||
handleConfirm() {
|
||||
if (this.editData.type === 'CONSTANT') {
|
||||
this.$refs.variableAdvance.saveAdvanced();
|
||||
}
|
||||
if (this.editData.type === 'CSV' && this.$refs.csv) {
|
||||
if (this.editData.files.length === 0) {
|
||||
this.$warning(this.$t('api_test.automation.csv_warning'));
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.visible = false;
|
||||
},
|
||||
handleCancel() {
|
||||
this.visible = false;
|
||||
},
|
||||
reload() {
|
||||
this.isActive = false;
|
||||
this.$nextTick(() => {
|
||||
this.isActive = true;
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -0,0 +1,273 @@
|
|||
<template>
|
||||
<span>
|
||||
<el-row>
|
||||
<el-col :span="18">
|
||||
<el-upload
|
||||
action="#"
|
||||
class="api-body-upload"
|
||||
list-type="picture-card"
|
||||
:file-list="parameter.files"
|
||||
:beforeUpload="uploadValidate"
|
||||
:on-exceed="exceed"
|
||||
:limit="1"
|
||||
ref="upload">
|
||||
<div class="upload-default" @click.stop>
|
||||
<el-popover
|
||||
placement="right"
|
||||
trigger="hover">
|
||||
<div>
|
||||
<el-upload
|
||||
action="#"
|
||||
class="ms-body-upload"
|
||||
:http-request="upload"
|
||||
:limit="1"
|
||||
:on-exceed="exceed"
|
||||
:beforeUpload="uploadValidate"
|
||||
ref="uploadLocal">
|
||||
<el-button type="text"> {{ $t('permission.project_file.local_upload') }}</el-button>
|
||||
<span slot="file"/>
|
||||
</el-upload>
|
||||
</div>
|
||||
<el-button type="text" @click="associationFile">{{
|
||||
$t('permission.project_file.associated_files')
|
||||
}}</el-button>
|
||||
<i class="el-icon-plus" slot="reference"/>
|
||||
</el-popover>
|
||||
</div>
|
||||
<div class="upload-item" slot="file" slot-scope="{file}">
|
||||
<span>{{ file.file && file.file.name ? file.file.name : file.name }}</span>
|
||||
<span class="el-upload-list__item-actions" v-if="file.storage === 'FILE_REF'">
|
||||
<span v-if="!disabled" class="el-upload-list__item-delete" @click="handleUnlock(file)">
|
||||
<i class="el-icon-unlock"/>
|
||||
</span>
|
||||
</span>
|
||||
<span class="el-upload-list__item-actions" v-else>
|
||||
<span v-if="!disabled" class="el-upload-list__item-delete" @click="handleUpload(file)">
|
||||
<i class="el-icon-upload" style="font-size: 23px"/>
|
||||
</span>
|
||||
<span v-if="!disabled" class="el-upload-list__item-delete" @click="handleRemove(file)">
|
||||
<i class="el-icon-delete"/>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</el-upload>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<ms-file-batch-move ref="module" @setModuleId="setModuleId"/>
|
||||
<ms-file-metadata-list ref="metadataList" @checkRows="checkRows"/>
|
||||
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {downloadFile} from "@/common/js/utils";
|
||||
import MsFileBatchMove from "@/business/components/project/menu/file/module/FileBatchMove";
|
||||
import MsFileMetadataList from "@/business/components/project/menu/file/quote/QuoteFileList";
|
||||
import {getCurrentProjectID, getUUID} from "@/common/js/utils";
|
||||
|
||||
export default {
|
||||
name: "MsCsvFileUpload",
|
||||
data() {
|
||||
return {
|
||||
disabled: false,
|
||||
};
|
||||
},
|
||||
components: {
|
||||
MsFileBatchMove,
|
||||
MsFileMetadataList
|
||||
},
|
||||
props: {
|
||||
parameter: Object,
|
||||
default() {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setModuleId(moduleId) {
|
||||
let files = [];
|
||||
if (this.file && this.file.file) {
|
||||
files.push(this.file.file);
|
||||
}
|
||||
let request = {
|
||||
id: getUUID(),
|
||||
resourceId: this.id,
|
||||
moduleId: moduleId,
|
||||
projectId: getCurrentProjectID(),
|
||||
fileName: this.file.name
|
||||
};
|
||||
this.$fileUpload("/file/metadata/api/upload", null, files, request, (response) => {
|
||||
this.$success(this.$t("organization.integration.successful_operation"));
|
||||
});
|
||||
},
|
||||
handleUpload(file) {
|
||||
this.$refs.module.init();
|
||||
this.file = file;
|
||||
},
|
||||
associationFile() {
|
||||
this.$refs.metadataList.open();
|
||||
},
|
||||
checkRows(rows) {
|
||||
if (rows && rows.size !== 1 || this.parameter.files.length > 0) {
|
||||
this.$warning(this.$t('test_track.case.import.upload_limit_count'));
|
||||
return;
|
||||
}
|
||||
rows.forEach(item => {
|
||||
if (!item.type || item.type.toLowerCase() !== "csv") {
|
||||
this.$warning(this.$t('variables.cvs_info'));
|
||||
return;
|
||||
}
|
||||
let file = {
|
||||
name: item.name,
|
||||
id: getUUID(),
|
||||
fileId: item.id,
|
||||
storage: "FILE_REF",
|
||||
projectId: item.projectId,
|
||||
fileType: item.type
|
||||
};
|
||||
this.parameter.files.push(file);
|
||||
})
|
||||
},
|
||||
handleUnlock(file) {
|
||||
for (let i = 0; i < this.parameter.files.length; i++) {
|
||||
let fileName = file.file ? file.file.name : file.name;
|
||||
let paramFileName = this.parameter.files[i].file ?
|
||||
this.parameter.files[i].file.name : this.parameter.files[i].name;
|
||||
if (fileName === paramFileName) {
|
||||
this.parameter.files.splice(i, 1);
|
||||
this.$refs.upload.handleRemove(file);
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
download() {
|
||||
// 本地文件
|
||||
if (this.parameter.files && this.parameter.files.length > 0 && this.parameter.files[0].file) {
|
||||
downloadFile(this.parameter.files[0].file.name, this.parameter.files[0].file);
|
||||
}
|
||||
// 远程下载文件
|
||||
if (this.parameter.files && this.parameter.files.length > 0 && !this.parameter.files[0].file) {
|
||||
let file = this.parameter.files[0];
|
||||
let conf = {
|
||||
url: "/api/automation/file/download",
|
||||
method: 'post',
|
||||
data: file,
|
||||
responseType: 'blob',
|
||||
};
|
||||
if (file.storage === "FILE_REF") {
|
||||
conf = {
|
||||
url: "/file/metadata/download/" + file.fileId,
|
||||
method: 'get',
|
||||
responseType: 'blob',
|
||||
};
|
||||
}
|
||||
this.result = this.$request(conf).then(response => {
|
||||
const content = response.data;
|
||||
if (content && this.parameter.files[0]) {
|
||||
downloadFile(this.parameter.files[0].name, content);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
handleRemove(file) {
|
||||
let fileName = file.file.name ? file.file.name : file.name
|
||||
this.$alert(this.$t('api_test.environment.csv_delete') + ':【 ' + fileName + " 】?", '', {
|
||||
confirmButtonText: this.$t('commons.confirm'),
|
||||
callback: (action) => {
|
||||
if (action === 'confirm') {
|
||||
this.$refs.upload.handleRemove(file);
|
||||
for (let i = 0; i < this.parameter.files.length; i++) {
|
||||
let paramFileName = this.parameter.files[i].name ?
|
||||
this.parameter.files[i].name : this.parameter.files[i].file.name;
|
||||
if (fileName === paramFileName) {
|
||||
this.parameter.files.splice(i, 1);
|
||||
this.$refs.upload.handleRemove(file);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
exceed() {
|
||||
this.$warning(this.$t('test_track.case.import.upload_limit_count'));
|
||||
},
|
||||
upload(file) {
|
||||
this.parameter.files = [];
|
||||
this.parameter.files.push(file);
|
||||
},
|
||||
uploadValidate(file) {
|
||||
if (this.parameter.files.length > 0) {
|
||||
this.$warning(this.$t('test_track.case.import.upload_limit_count'));
|
||||
return false;
|
||||
}
|
||||
if (file.size / 1024 / 1024 > 500) {
|
||||
this.$warning(this.$t('api_test.request.body_upload_limit_size'));
|
||||
return false;
|
||||
}
|
||||
if (!file.name.endsWith(".csv")) {
|
||||
this.$warning(this.$t('variables.cvs_info'));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
},
|
||||
created() {
|
||||
if (!this.parameter.files) {
|
||||
this.parameter.files = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.el-upload {
|
||||
background-color: black;
|
||||
}
|
||||
|
||||
.api-body-upload >>> .el-upload {
|
||||
height: 30px;
|
||||
width: 32px;
|
||||
}
|
||||
|
||||
.upload-default {
|
||||
min-height: 30px;
|
||||
width: 32px;
|
||||
line-height: 32px;
|
||||
}
|
||||
|
||||
.el-icon-plus {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.api-body-upload >>> .el-upload-list__item {
|
||||
height: 30px;
|
||||
width: auto;
|
||||
padding: 2px 5px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
.api-body-upload >>> .el-upload-list--picture-card {
|
||||
}
|
||||
|
||||
.api-body-upload {
|
||||
min-height: 30px;
|
||||
border: 1px solid #EBEEF5;
|
||||
padding: 2px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.ms-body-upload {
|
||||
min-height: 0px;
|
||||
height: 30px;
|
||||
border: 0px;
|
||||
padding: 0px;
|
||||
border-radius: 0px;
|
||||
|
||||
}
|
||||
|
||||
.upload-item {
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
|
@ -30,7 +30,7 @@
|
|||
|
||||
<script>
|
||||
import {CommonConfig} from "../../model/EnvironmentModel";
|
||||
import MsApiScenarioVariables from "../ApiScenarioVariables";
|
||||
import MsApiScenarioVariables from "@/business/components/api/test/components/environment/ApiScenarioVariables";
|
||||
import MsApiHostTable from "../ApiHostTable";
|
||||
|
||||
export default {
|
||||
|
|
|
@ -2,19 +2,27 @@
|
|||
<el-main v-loading="result.loading" class="environment-edit" style="margin-left: 0px">
|
||||
<el-form :model="environment" :rules="rules" ref="environment" label-width="80px">
|
||||
<el-row>
|
||||
<el-col :span="20">
|
||||
<el-form-item prop="name" :label="$t('api_test.environment.name')">
|
||||
<el-input v-model="environment.name" :disabled="isReadOnly" :placeholder="this.$t('commons.input_name')"
|
||||
clearable/>
|
||||
<el-col :span="10" v-if="!isProject">
|
||||
<el-form-item class="project-item" prop="currentProjectId" :label="$t('project.select')">
|
||||
<el-select @change="handleProjectChange" v-model="environment.currentProjectId" filterable clearable
|
||||
size="small">
|
||||
<el-option v-for="item in projectList" :key="item.id" :label="item.name" :value="item.id"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="4" v-if="!hideButton">
|
||||
<el-col :span="10">
|
||||
<el-form-item prop="name" :label="$t('api_test.environment.name')">
|
||||
<el-input v-model="environment.name" :disabled="isReadOnly" :placeholder="this.$t('commons.input_name')"
|
||||
clearable size="small"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="4" v-if="!hideButton" :offset="isProject ? 10 : 0">
|
||||
<div style="float: right;width: fit-content;">
|
||||
<div style="float: left; margin-right: 8px;">
|
||||
<slot name="other"></slot>
|
||||
</div>
|
||||
<div class="ms_btn">
|
||||
<el-button type="primary" @click="confirm" @keydown.enter.native.prevent>
|
||||
<el-button type="primary" @click="confirm" @keydown.enter.native.prevent size="small">
|
||||
{{ $t('commons.confirm') }}
|
||||
</el-button>
|
||||
</div>
|
||||
|
@ -30,7 +38,7 @@
|
|||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane :label="$t('api_test.environment.http_config')" name="http">
|
||||
<ms-environment-http-config :project-id="projectId" :http-config="environment.config.httpConfig"
|
||||
<ms-environment-http-config :project-id="environment.projectId" :http-config="environment.config.httpConfig"
|
||||
ref="httpConfig" :is-read-only="isReadOnly"/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('api_test.environment.database_config')" name="sql">
|
||||
|
@ -40,7 +48,7 @@
|
|||
<ms-tcp-config :config="environment.config.tcpConfig" :is-read-only="isReadOnly"/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('commons.ssl.config')" name="ssl">
|
||||
<ms-environment-s-s-l-config :project-id="projectId" :ssl-config="environment.config.sslConfig"
|
||||
<ms-environment-s-s-l-config :project-id="environment.projectId" :ssl-config="environment.config.sslConfig"
|
||||
:is-read-only="isReadOnly"/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('api_test.definition.request.all_pre_script')" name="prescript">
|
||||
|
@ -97,7 +105,8 @@
|
|||
<el-form-item style="margin-bottom: 0px;"
|
||||
:label="$t('error_report_library.use_error_report')"
|
||||
prop="status">
|
||||
<el-switch v-model="environment.config.useErrorCode" style="margin-right: 10px" :disabled="isReadOnly"/>
|
||||
<el-switch v-model="environment.config.useErrorCode" style="margin-right: 10px"
|
||||
:disabled="isReadOnly"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
@ -128,11 +137,7 @@
|
|||
:is-show-json-path-suggest="false"/>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<!-- <div class="environment-footer">-->
|
||||
<!-- <ms-dialog-footer-->
|
||||
<!-- @cancel="cancel"-->
|
||||
<!-- @confirm="save()"/>-->
|
||||
<!-- </div>-->
|
||||
|
||||
</el-form>
|
||||
<ms-change-history ref="changeHistory"/>
|
||||
</el-main>
|
||||
|
@ -158,6 +163,7 @@ import EnvironmentGlobalScript from "@/business/components/api/test/components/e
|
|||
import GlobalAssertions from "@/business/components/api/definition/components/assertion/GlobalAssertions";
|
||||
import MsChangeHistory from "../../../../history/ChangeHistory";
|
||||
import MsDialogHeader from "../../../../common/components/MsDialogHeader";
|
||||
import {getUploadConfig, request} from "@/common/js/ajax";
|
||||
|
||||
export default {
|
||||
name: "EnvironmentEdit",
|
||||
|
@ -185,6 +191,13 @@ export default {
|
|||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
projectList: {
|
||||
type: Array,
|
||||
default() {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
isProject: Boolean
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -196,6 +209,9 @@ export default {
|
|||
{required: true, message: this.$t('commons.input_name'), trigger: 'blur'},
|
||||
{max: 64, message: this.$t('commons.input_limit', [1, 64]), trigger: 'blur'}
|
||||
],
|
||||
currentProjectId: [
|
||||
{required: true, message: "", trigger: 'blur'},
|
||||
],
|
||||
},
|
||||
headerSuggestions: REQUEST_HEADERS,
|
||||
activeName: 'common'
|
||||
|
@ -295,6 +311,15 @@ export default {
|
|||
this.isRefresh = true;
|
||||
});
|
||||
this.envEnable = o.enable;
|
||||
},
|
||||
//当创建及复制环境所选择的项目变化时,改变当前环境对应的projectId
|
||||
'environment.currentProjectId'() {
|
||||
// el-select什么都不选时值为'',为''的话也会被当成有效的projectId传给后端,转化使其无效
|
||||
if (!this.environment.currentProjectId) {
|
||||
this.environment.projectId = null;
|
||||
} else {
|
||||
this.environment.projectId = this.environment.currentProjectId;
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
@ -361,6 +386,29 @@ export default {
|
|||
}
|
||||
return uploadFiles;
|
||||
},
|
||||
getVariablesFiles(obj) {
|
||||
let variablesFiles = [];
|
||||
obj.variablesFilesIds = [];
|
||||
// 场景变量csv 文件
|
||||
if (obj.config.commonConfig.variables) {
|
||||
obj.config.commonConfig.variables.forEach(param => {
|
||||
if (param.type === 'CSV' && param.files) {
|
||||
param.files.forEach(item => {
|
||||
if (item.file && item.file.name) {
|
||||
if (!item.id) {
|
||||
let fileId = getUUID().substring(0, 12);
|
||||
item.name = item.file.name;
|
||||
item.id = fileId;
|
||||
}
|
||||
obj.variablesFilesIds.push(item.id);
|
||||
variablesFiles.push(item.file);
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
return variablesFiles;
|
||||
},
|
||||
check(items) {
|
||||
let repeatKey = "";
|
||||
items.forEach((item, index) => {
|
||||
|
@ -373,7 +421,7 @@ export default {
|
|||
return repeatKey;
|
||||
},
|
||||
_save(environment) {
|
||||
if (!this.projectId) {
|
||||
if (!environment.projectId) {
|
||||
this.$warning(this.$t('api_test.select_project'));
|
||||
return;
|
||||
}
|
||||
|
@ -401,25 +449,47 @@ export default {
|
|||
}
|
||||
})
|
||||
}
|
||||
environment.config.commonConfig.variables.forEach(variable => {
|
||||
if (variable.type === 'CSV' && variable.files.length === 0) {
|
||||
message = this.$t('api_test.automation.csv_warning');
|
||||
return;
|
||||
}
|
||||
})
|
||||
if (message) {
|
||||
this.$warning(message);
|
||||
return;
|
||||
}
|
||||
|
||||
let bodyFiles = this.geFiles(environment);
|
||||
let variablesFiles = this.getVariablesFiles(environment);
|
||||
let formData = new FormData();
|
||||
if (bodyFiles) {
|
||||
bodyFiles.forEach(f => {
|
||||
formData.append("files", f);
|
||||
})
|
||||
}
|
||||
if (variablesFiles) {
|
||||
variablesFiles.forEach(f => {
|
||||
formData.append("variablesFiles", f);
|
||||
})
|
||||
}
|
||||
let param = this.buildParam(environment);
|
||||
let url = '/api/environment/add';
|
||||
if (param.id) {
|
||||
url = '/api/environment/update';
|
||||
}
|
||||
this.$fileUpload(url, null, bodyFiles, param, response => {
|
||||
//this.result = this.$post(url, param, response => {
|
||||
if (!param.id) {
|
||||
environment.id = response.data;
|
||||
}
|
||||
formData.append('request', new Blob([JSON.stringify(param)], {type: "application/json"}));
|
||||
let axiosRequestConfig = getUploadConfig(url, formData);
|
||||
request(axiosRequestConfig, (response) => {
|
||||
if (response.success) {
|
||||
this.$success(this.$t('commons.save_success'));
|
||||
this.$emit('refreshAfterSave'); //在EnvironmentList.vue中监听,使在数据修改后进行刷新
|
||||
this.cancel();
|
||||
this.cancel()
|
||||
}
|
||||
}, error => {
|
||||
this.$emit('errorRefresh', error);
|
||||
});
|
||||
|
||||
},
|
||||
buildParam: function (environment) {
|
||||
let param = {};
|
||||
|
@ -447,6 +517,9 @@ export default {
|
|||
clearValidate() {
|
||||
this.$refs["environment"].clearValidate();
|
||||
},
|
||||
handleProjectChange() { //项目选择下拉框选择其他项目后清空“启用条件”,因为项目变了模块也就变了。
|
||||
this.environment.config.httpConfig.conditions = [];
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
@ -468,10 +541,11 @@ span:not(:first-child) {
|
|||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.errorReportConfigSwitch /deep/ .el-switch__label{
|
||||
.errorReportConfigSwitch /deep/ .el-switch__label {
|
||||
color: #D8DAE2;
|
||||
}
|
||||
.errorReportConfigSwitch /deep/ .is-active{
|
||||
|
||||
.errorReportConfigSwitch /deep/ .is-active {
|
||||
color: var(--count_number);
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
<el-row type="flex" justify="space-between">
|
||||
<el-col :span="14">
|
||||
<span class="ms-env-span" style="line-height: 30px;">{{ $t('api_test.environment.socket') }}</span>
|
||||
<el-input v-model="condition.socket" style="width: 85%" :placeholder="$t('api_test.request.url_description')" clearable size="small">
|
||||
<el-input v-model="condition.socket" style="width: 85%"
|
||||
:placeholder="$t('api_test.request.url_description')" clearable size="small">
|
||||
<template slot="prepend">
|
||||
<el-select v-model="condition.protocol" class="request-protocol-select" size="small">
|
||||
<el-option label="http://" value="http"/>
|
||||
|
@ -17,7 +18,8 @@
|
|||
</el-col>
|
||||
<el-col :span="10">
|
||||
<span style="margin-right: 12px; line-height: 30px;">{{ $t('commons.description') }}</span>
|
||||
<el-input v-model="condition.description" maxlength="200" :show-word-limit="true" size="small" style="width: 70%;"/>
|
||||
<el-input v-model="condition.description" maxlength="200" :show-word-limit="true" size="small"
|
||||
style="width: 70%;"/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form-item>
|
||||
|
@ -29,7 +31,8 @@
|
|||
<el-radio label="PATH">{{ $t('api_test.definition.api_path') }}</el-radio>
|
||||
</el-radio-group>
|
||||
<div v-if="condition.type === 'MODULE'" style="margin-top: 6px">
|
||||
<ms-select-tree size="small" :data="moduleOptions" :default-key="condition.ids" @getValue="setModule" :obj="moduleObj" clearable :checkStrictly="true" multiple v-if="!loading"/>
|
||||
<ms-select-tree size="small" :data="moduleOptions" :default-key="condition.ids" @getValue="setModule"
|
||||
:obj="moduleObj" clearable :checkStrictly="true" multiple v-if="!loading"/>
|
||||
</div>
|
||||
<div v-if="condition.type === 'PATH'" style="margin-top: 6px">
|
||||
<el-input v-model="pathDetails.name" :placeholder="$t('api_test.value')" clearable size="small">
|
||||
|
@ -70,7 +73,8 @@
|
|||
{{ getUrl(row) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="type" :label="$t('api_test.environment.condition_enable')" show-overflow-tooltip min-width="100px">
|
||||
<el-table-column prop="type" :label="$t('api_test.environment.condition_enable')" show-overflow-tooltip
|
||||
min-width="100px">
|
||||
<template v-slot:default="{row}">
|
||||
{{ getName(row) }}
|
||||
</template>
|
||||
|
@ -153,7 +157,15 @@ export default {
|
|||
},
|
||||
loading: false,
|
||||
pathDetails: new KeyValue({name: "", value: "contains"}),
|
||||
condition: {type: "NONE", details: [new KeyValue({name: "", value: "contains"})], protocol: "http", socket: "", domain: "", port: 0, headers: [new KeyValue()]},
|
||||
condition: {
|
||||
type: "NONE",
|
||||
details: [new KeyValue({name: "", value: "contains"})],
|
||||
protocol: "http",
|
||||
socket: "",
|
||||
domain: "",
|
||||
port: 0,
|
||||
headers: [new KeyValue()]
|
||||
},
|
||||
beforeCondition: {}
|
||||
};
|
||||
},
|
||||
|
@ -174,7 +186,16 @@ export default {
|
|||
this.condition.description = this.httpConfig.description;
|
||||
this.add();
|
||||
}
|
||||
this.condition = {id: undefined, type: "NONE", details: [new KeyValue({name: "", value: "contains"})], protocol: "http", socket: "", domain: "", port: 0, headers: [new KeyValue()]};
|
||||
this.condition = {
|
||||
id: undefined,
|
||||
type: "NONE",
|
||||
details: [new KeyValue({name: "", value: "contains"})],
|
||||
protocol: "http",
|
||||
socket: "",
|
||||
domain: "",
|
||||
port: 0,
|
||||
headers: [new KeyValue()]
|
||||
};
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
@ -216,7 +237,15 @@ export default {
|
|||
}
|
||||
},
|
||||
selectRow(row) {
|
||||
this.condition = {type: "NONE", details: [new KeyValue({name: "", value: "contains"})], protocol: "http", socket: "", domain: "", port: 0, headers: [new KeyValue()]};
|
||||
this.condition = {
|
||||
type: "NONE",
|
||||
details: [new KeyValue({name: "", value: "contains"})],
|
||||
protocol: "http",
|
||||
socket: "",
|
||||
domain: "",
|
||||
port: 0,
|
||||
headers: [new KeyValue()]
|
||||
};
|
||||
if (row) {
|
||||
this.httpConfig.socket = row.socket;
|
||||
this.httpConfig.protocol = row.protocol;
|
||||
|
@ -309,7 +338,14 @@ export default {
|
|||
this.$refs.envTable.setCurrentRow(0);
|
||||
},
|
||||
clear() {
|
||||
this.condition = {type: "NONE", details: [new KeyValue({name: "", value: "contains"})], protocol: "http", socket: "", domain: "", headers: [new KeyValue()]};
|
||||
this.condition = {
|
||||
type: "NONE",
|
||||
details: [new KeyValue({name: "", value: "contains"})],
|
||||
protocol: "http",
|
||||
socket: "",
|
||||
domain: "",
|
||||
headers: [new KeyValue()]
|
||||
};
|
||||
this.$refs.envTable.setCurrentRow(0);
|
||||
},
|
||||
reload() {
|
||||
|
@ -336,8 +372,15 @@ export default {
|
|||
}
|
||||
this.validateSocket();
|
||||
let obj = {
|
||||
id: getUUID(), type: this.condition.type, socket: this.condition.socket, protocol: this.condition.protocol, headers: this.condition.headers,
|
||||
domain: this.condition.domain, port: this.condition.port, time: new Date().getTime(), description: this.condition.description
|
||||
id: getUUID(),
|
||||
type: this.condition.type,
|
||||
socket: this.condition.socket,
|
||||
protocol: this.condition.protocol,
|
||||
headers: this.condition.headers,
|
||||
domain: this.condition.domain,
|
||||
port: this.condition.port,
|
||||
time: new Date().getTime(),
|
||||
description: this.condition.description
|
||||
};
|
||||
if (this.condition.type === "PATH") {
|
||||
obj.details = [JSON.parse(JSON.stringify(this.pathDetails))];
|
||||
|
@ -358,7 +401,16 @@ export default {
|
|||
return;
|
||||
}
|
||||
const index = this.httpConfig.conditions.findIndex((d) => d.id === row.id);
|
||||
let obj = {id: getUUID(), type: row.type, socket: row.socket, details: row.details, protocol: row.protocol, headers: JSON.parse(JSON.stringify(row.headers)), domain: row.domain, time: new Date().getTime()};
|
||||
let obj = {
|
||||
id: getUUID(),
|
||||
type: row.type,
|
||||
socket: row.socket,
|
||||
details: row.details,
|
||||
protocol: row.protocol,
|
||||
headers: JSON.parse(JSON.stringify(row.headers)),
|
||||
domain: row.domain,
|
||||
time: new Date().getTime()
|
||||
};
|
||||
if (index != -1) {
|
||||
this.httpConfig.conditions.splice(index, 0, obj);
|
||||
} else {
|
||||
|
@ -402,7 +454,7 @@ export default {
|
|||
let line = item.split(/:|:/);
|
||||
let values = item.split(line[0] + ":");
|
||||
let required = false;
|
||||
keyValues.unshift(new KeyValue({
|
||||
keyValues.push(new KeyValue({
|
||||
name: line[0],
|
||||
required: required,
|
||||
value: values[1],
|
||||
|
@ -429,7 +481,7 @@ export default {
|
|||
}
|
||||
}
|
||||
if (isAdd) {
|
||||
this.condition.headers.unshift(keyValue);
|
||||
this.condition.headers.splice(this.condition.headers.indexOf(h => !h.name), 0, keyValue);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -0,0 +1,162 @@
|
|||
<template>
|
||||
<el-dialog :visible="dialogVisible" :title="dialogTitle"
|
||||
@close="close" :close-on-click-modal="false" append-to-body
|
||||
width="35%">
|
||||
<el-form>
|
||||
<el-form :rules="rules" label-width="80px" v-model="modeId">
|
||||
<el-form-item prop="modeId" :label="$t('commons.import_mode')">
|
||||
<el-select size="small" v-model="modeId">
|
||||
<el-option v-for="item in modeOptions" :key="item.id" :label="item.name" :value="item.id"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-upload
|
||||
class="api-upload" drag action="alert"
|
||||
:on-change="handleFileChange"
|
||||
:limit="1" :file-list="uploadFiles"
|
||||
:on-remove="handleRemove"
|
||||
:on-exceed="handleExceed"
|
||||
:auto-upload="false" accept=".json">
|
||||
<i class="el-icon-upload"></i>
|
||||
<div class="el-upload__text" v-html="$t('load_test.upload_tips')"></div>
|
||||
<div class="el-upload__tip" slot="tip">
|
||||
{{ $t('api_test.api_import.file_size_limit') }}
|
||||
{{ ',' + $t('api_test.api_import.ms_env_import_file_limit') }}
|
||||
</div>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-form>
|
||||
<template v-slot:footer>
|
||||
<el-button type="primary" @click="save">
|
||||
{{ $t('commons.confirm') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: "VariableImport",
|
||||
props: {
|
||||
projectList: {
|
||||
type: Array,
|
||||
default() {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
toImportProjectId: {
|
||||
type: String,
|
||||
default() {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
currentProjectId: '', //所选中环境的id
|
||||
uploadFiles: [],
|
||||
dialogTitle: this.$t('commons.import_variable'),
|
||||
dialogVisible: false,
|
||||
modeOptions: [
|
||||
{
|
||||
id: 'fullCoverage',
|
||||
name: this.$t('commons.cover')
|
||||
},
|
||||
{
|
||||
id: 'incrementalMerge',
|
||||
name: this.$t('commons.not_cover')
|
||||
}
|
||||
],
|
||||
modeId: 'fullCoverage',
|
||||
rules: {
|
||||
modeId: [
|
||||
{required: true, message: "", trigger: 'blur'},
|
||||
],
|
||||
},
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
//导入框激活时重置选中的项目和文件
|
||||
dialogVisible(val, oldVal) {
|
||||
if (oldVal === false) {
|
||||
this.currentProjectId = '';
|
||||
this.uploadFiles = [];
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
methods: {
|
||||
handleFileChange(file, uploadFiles) {
|
||||
this.uploadFiles = uploadFiles;
|
||||
},
|
||||
save() {
|
||||
if (this.uploadFiles.length > 0) {
|
||||
for (let i = 0; i < this.uploadFiles.length; i++) {
|
||||
this.uploadValidate(this.uploadFiles[i]);
|
||||
let file = this.uploadFiles[i];
|
||||
if (!file) {
|
||||
continue;
|
||||
}
|
||||
let reader = new FileReader();
|
||||
reader.readAsText(file.raw)
|
||||
reader.onload = (e) => {
|
||||
let fileString = e.target.result;
|
||||
let messages = '';
|
||||
try {
|
||||
JSON.parse(fileString).map(env => {
|
||||
if (!env.name) {
|
||||
messages = this.$t('api_test.automation.variable_warning')
|
||||
}
|
||||
})
|
||||
if (messages !== '') {
|
||||
this.$warning(messages);
|
||||
return;
|
||||
}
|
||||
this.$emit("mergeData", fileString, this.modeId);
|
||||
this.dialogVisible = false;
|
||||
this.$success(this.$t('commons.save_success'));
|
||||
} catch (exception) {
|
||||
this.$warning(this.$t('api_test.api_import.ms_env_import_file_limit'));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.$warning(this.$t('test_track.case.import.import_file_tips'));
|
||||
}
|
||||
},
|
||||
handleExceed() {
|
||||
this.$warning(this.$t('api_test.api_import.file_exceed_limit'));
|
||||
},
|
||||
handleRemove() {
|
||||
|
||||
},
|
||||
uploadValidate(file) { //判断文件扩展名是不是.json,以及文件大小是否超过20M
|
||||
const extension = file.name.substring(file.name.lastIndexOf('.') + 1);
|
||||
if (!(extension === 'json')) {
|
||||
this.$warning(this.$t('api_test.api_import.ms_env_import_file_limit'));
|
||||
}
|
||||
if (file.size / 1024 / 1024 > 20) {
|
||||
this.$warning(this.$t('api_test.api_import.file_size_limit'));
|
||||
}
|
||||
},
|
||||
open() {
|
||||
this.dialogVisible = true;
|
||||
},
|
||||
close() {
|
||||
this.dialogVisible = false;
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.project-item {
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
</style>
|
|
@ -2,7 +2,12 @@
|
|||
|
||||
<div class="msDialogHeader">
|
||||
<span style="float: left;font-size: 18px;color: #303133;">{{ title }}</span>
|
||||
|
||||
<div style="float: right; margin-right: 30px">
|
||||
<el-tooltip effect="dark" :content="$t('commons.full_screen_editing')"
|
||||
placement="top-start">
|
||||
<font-awesome-icon class="alt-ico" :icon="['fa', 'expand-alt']" size="lg" @click="fullScreen"/>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div v-if="!hideButton" style="float: right;width: fit-content;">
|
||||
<div style="float: left; margin-right: 8px;">
|
||||
<slot name="other"></slot>
|
||||
|
@ -39,6 +44,9 @@ export default {
|
|||
},
|
||||
confirm() {
|
||||
this.$emit("confirm");
|
||||
},
|
||||
fullScreen() {
|
||||
this.$emit("fullScreen");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -53,4 +61,9 @@ export default {
|
|||
.msDialogHeader {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.alt-ico {
|
||||
font-size: 13px;
|
||||
color: #8c939d;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -32,7 +32,9 @@
|
|||
<el-table-column :label="$t('api_test.environment.socket')" show-overflow-tooltip>
|
||||
<template v-slot="scope">
|
||||
<span v-if="parseDomainName(scope.row)!='SHOW_INFO'">{{ parseDomainName(scope.row) }}</span>
|
||||
<el-button size="mini" icon="el-icon-s-data" @click="showInfo(scope.row)" v-else>{{ $t('workspace.env_group.view_details') }}</el-button>
|
||||
<el-button size="mini" icon="el-icon-s-data" @click="showInfo(scope.row)" v-else>
|
||||
{{ $t('workspace.env_group.view_details') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('commons.operating')">
|
||||
|
@ -57,18 +59,20 @@
|
|||
</el-card>
|
||||
|
||||
<!-- 创建、编辑、复制环境时的对话框 -->
|
||||
<el-dialog :visible.sync="dialogVisible" :close-on-click-modal="false" width="66%" top="50px">
|
||||
<el-dialog :visible.sync="dialogVisible" :close-on-click-modal="false" width="66%" top="50px"
|
||||
:fullscreen="isFullScreen">
|
||||
<template #title>
|
||||
<ms-dialog-header :title="dialogTitle" :hide-button="true"
|
||||
@cancel="dialogVisible = false"
|
||||
@confirm="save"/>
|
||||
@confirm="save" @fullScreen="fullScreen"/>
|
||||
</template>
|
||||
<environment-edit :if-create="ifCreate" :environment="currentEnvironment" ref="environmentEdit" @close="close"
|
||||
@confirm="save"
|
||||
:project-id="currentProjectId" @refreshAfterSave="refresh">
|
||||
@confirm="save" :is-project="true"
|
||||
:project-list="projectList" @refreshAfterSave="refresh">
|
||||
</environment-edit>
|
||||
</el-dialog>
|
||||
<environment-import :project-list="projectList" :to-import-project-id="currentProjectId" @refresh="refresh" ref="envImport"></environment-import>
|
||||
<environment-import :project-list="projectList" :to-import-project-id="currentProjectId" @refresh="refresh"
|
||||
ref="envImport"></environment-import>
|
||||
|
||||
<el-dialog title="域名列表" :visible.sync="domainVisible">
|
||||
<el-table :data="conditions">
|
||||
|
@ -94,12 +98,14 @@
|
|||
{{ row.conditionType ? "-" : getDetails(row) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="description" show-overflow-tooltip min-width="120px" :label="$t('commons.description')">
|
||||
<el-table-column prop="description" show-overflow-tooltip min-width="120px"
|
||||
:label="$t('commons.description')">
|
||||
<template v-slot:default="{row}">
|
||||
<span>{{ row.description ? row.description : "-" }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createTime" show-overflow-tooltip min-width="120px" :label="$t('commons.create_time')">
|
||||
<el-table-column prop="createTime" show-overflow-tooltip min-width="120px"
|
||||
:label="$t('commons.create_time')">
|
||||
<template v-slot:default="{row}">
|
||||
<span v-if="!row.conditionType">{{ row.time | timestampFormatDate }}</span>
|
||||
<span v-else>-</span>
|
||||
|
@ -170,6 +176,7 @@ export default {
|
|||
projectFilters: [],
|
||||
screenHeight: 'calc(100vh - 155px)',
|
||||
ifCreate: false, //是否是创建环境
|
||||
isFullScreen: false //是否全屏
|
||||
}
|
||||
},
|
||||
created() {
|
||||
|
@ -180,6 +187,9 @@ export default {
|
|||
},
|
||||
|
||||
methods: {
|
||||
fullScreen() {
|
||||
this.isFullScreen = !this.isFullScreen;
|
||||
},
|
||||
showInfo(row) {
|
||||
const config = JSON.parse(row.config);
|
||||
this.conditions = config.httpConfig.conditions;
|
||||
|
@ -193,7 +203,7 @@ export default {
|
|||
}
|
||||
this.domainVisible = true;
|
||||
},
|
||||
save(){
|
||||
save() {
|
||||
this.$refs.environmentEdit.save();
|
||||
},
|
||||
getName(row) {
|
||||
|
@ -387,8 +397,7 @@ export default {
|
|||
} else { //旧版本没有对应的config数据,其域名保存在protocol和domain中
|
||||
return environment.protocol + '://' + environment.domain;
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
|
|
|
@ -38,7 +38,9 @@
|
|||
<el-table-column :label="$t('api_test.environment.socket')" show-overflow-tooltip>
|
||||
<template v-slot="scope">
|
||||
<span v-if="parseDomainName(scope.row)!='SHOW_INFO'">{{ parseDomainName(scope.row) }}</span>
|
||||
<el-button size="mini" icon="el-icon-s-data" @click="showInfo(scope.row)" v-else>{{ $t('workspace.env_group.view_details') }}</el-button>
|
||||
<el-button size="mini" icon="el-icon-s-data" @click="showInfo(scope.row)" v-else>
|
||||
{{ $t('workspace.env_group.view_details') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('commons.operating')">
|
||||
|
@ -62,26 +64,16 @@
|
|||
</el-card>
|
||||
|
||||
<!-- 创建、编辑、复制环境时的对话框 -->
|
||||
<el-dialog :visible.sync="dialogVisible" :close-on-click-modal="false" top="50px" width="66%">
|
||||
<el-dialog :visible.sync="dialogVisible" :close-on-click-modal="false" top="50px" width="66%"
|
||||
:fullscreen="isFullScreen">
|
||||
<template #title>
|
||||
<ms-dialog-header :title="dialogTitle"
|
||||
@cancel="dialogVisible = false"
|
||||
@confirm="save"/>
|
||||
@cancel="dialogVisible = false" :hide-button="true"
|
||||
@confirm="save" @fullScreen="fullScreen"/>
|
||||
</template>
|
||||
<el-row>
|
||||
<el-col :span="20">
|
||||
<el-form label-width="80px" :rules="rules" style="display: flow-root">
|
||||
<el-form-item class="project-item" prop="currentProjectId" :label="$t('project.select')">
|
||||
<el-select @change="handleProjectChange" v-model="currentProjectId" filterable clearable>
|
||||
<el-option v-for="item in projectList" :key="item.id" :label="item.name" :value="item.id"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<environment-edit :if-create="ifCreate" :environment="currentEnvironment" ref="environmentEdit" @close="close"
|
||||
:hide-button="true"
|
||||
:project-id="currentProjectId" @refreshAfterSave="refresh">
|
||||
:project-list="projectList" @confirm="save" :is-project="false"
|
||||
@refreshAfterSave="refresh">
|
||||
</environment-edit>
|
||||
</el-dialog>
|
||||
<environment-import :project-list="projectList" @refresh="refresh" ref="envImport"></environment-import>
|
||||
|
@ -203,6 +195,7 @@ export default {
|
|||
},
|
||||
],
|
||||
ifCreate: false, //是否是创建环境
|
||||
isFullScreen: false
|
||||
};
|
||||
},
|
||||
created() {
|
||||
|
@ -227,6 +220,9 @@ export default {
|
|||
},
|
||||
|
||||
methods: {
|
||||
fullScreen() {
|
||||
this.isFullScreen = !this.isFullScreen;
|
||||
},
|
||||
showInfo(row) {
|
||||
const config = JSON.parse(row.config);
|
||||
this.conditions = config.httpConfig.conditions;
|
||||
|
@ -316,6 +312,7 @@ export default {
|
|||
editEnv(environment) {
|
||||
this.dialogTitle = this.$t('api_test.environment.config_environment');
|
||||
this.currentProjectId = environment.projectId;
|
||||
environment.currentProjectId = environment.projectId;
|
||||
const temEnv = {};
|
||||
Object.assign(temEnv, environment);
|
||||
parseEnvironment(temEnv); //parseEnvironment会改变环境对象的内部结构,从而影响前端列表的显示,所以复制一个环境对象作为代替
|
||||
|
@ -323,11 +320,12 @@ export default {
|
|||
this.dialogVisible = true;
|
||||
this.ifCreate = false;
|
||||
},
|
||||
save(){
|
||||
save() {
|
||||
this.$refs.environmentEdit.save();
|
||||
},
|
||||
copyEnv(environment) {
|
||||
this.currentProjectId = environment.projectId; //复制时默认选择所要复制环境对应的项目
|
||||
environment.currentProjectId = environment.projectId;
|
||||
this.dialogTitle = this.$t('api_test.environment.copy_environment');
|
||||
const temEnv = {};
|
||||
Object.assign(temEnv, environment);
|
||||
|
@ -458,7 +456,7 @@ export default {
|
|||
this.selectRow.forEach(row => {
|
||||
map.set(row.projectId, row.id);
|
||||
})
|
||||
this.$post("/environment/group/batch/add", {map: strMapToObj(map), groupIds:value}, () => {
|
||||
this.$post("/environment/group/batch/add", {map: strMapToObj(map), groupIds: value}, () => {
|
||||
this.$success(this.$t('commons.save_success'));
|
||||
this.getEnvironments();
|
||||
})
|
||||
|
|
|
@ -246,6 +246,7 @@ export let CUSTOM_TABLE_HEADER = {
|
|||
{id: 'name', key: '2', label: 'api_test.variable_name'},
|
||||
{id: 'type', key: '3', label: 'test_track.case.type'},
|
||||
{id: 'value', key: '4', label: 'api_test.value'},
|
||||
{id: 'description', key: '5', label: 'commons.description'}
|
||||
],
|
||||
|
||||
//缺陷列表
|
||||
|
|
|
@ -211,6 +211,8 @@ export default {
|
|||
modifier: 'Modifier',
|
||||
validate: "Validate",
|
||||
batch_add: "Batch add",
|
||||
import_variable: "Import variable",
|
||||
export_variable: "Export variable",
|
||||
batch_restore: "Batch restore",
|
||||
batch_gc: "Batch gc",
|
||||
check_project_tip: "Create or select the project first",
|
||||
|
@ -1193,7 +1195,8 @@ export default {
|
|||
copy: "Copy Test",
|
||||
please_select_case: "Please select case",
|
||||
fail_to_stop: "Fail to stop",
|
||||
batch_add_parameter: "Format: parameter name: parameter value <br/> like:Accept-Encoding:utf-8",
|
||||
batch_add_parameter: "Format: parameter name: parameter value <br/> like:Accept-Encoding:utf-8 <br/> Note: The parameter names in batch addition are repeated, and the last data is the latest data by default",
|
||||
params_format_warning: "Incorrect data format at line {0}",
|
||||
create_performance_test_tips: 'This operation cannot be completed without permission to create performance tests',
|
||||
jar_config: {
|
||||
title: "Upload jar package",
|
||||
|
@ -1510,9 +1513,15 @@ export default {
|
|||
environment_json: "Environment Config",
|
||||
environment_group_id: "Environment Group ID",
|
||||
select_environment: "Please select environment",
|
||||
select_variable: "Please select variable",
|
||||
please_save_test: "Please Save Test First",
|
||||
common_config: "Common Config",
|
||||
http_config: "HTTP Config",
|
||||
advanced_setting: "Click Advanced settings to add variable values",
|
||||
variables_delete_info: "Are you sure you want to delete the selected variable",
|
||||
csv_delete: "Are you sure you want to delete the CSV file",
|
||||
delete_info: "Please select a data deletion",
|
||||
list_info: "List data is separated by ,",
|
||||
database_config: "Database Config",
|
||||
tcp_config: "TCP Config",
|
||||
import: "Import Environment",
|
||||
|
@ -2792,6 +2801,7 @@ export default {
|
|||
delimiter: "Delimiter",
|
||||
format: "Output format",
|
||||
quoted_data: "Whether to allow quotes",
|
||||
csv_download: "CSV file does not support exporting"
|
||||
},
|
||||
auth_source: {
|
||||
delete_prompt: 'This operation will delete the authentication source, do you want to continue? ',
|
||||
|
|
|
@ -212,6 +212,8 @@ export default {
|
|||
modifier: '修改人',
|
||||
validate: "校验",
|
||||
batch_add: "批量添加",
|
||||
import_variable: "导入变量",
|
||||
export_variable: "导出变量",
|
||||
batch_restore: "批量恢复",
|
||||
batch_gc: "批量回收",
|
||||
check_project_tip: "请先创建或选择项目",
|
||||
|
@ -1203,7 +1205,8 @@ export default {
|
|||
copy: "复制测试",
|
||||
please_select_case: "请选择用例",
|
||||
fail_to_stop: "失败停止",
|
||||
batch_add_parameter: "格式:参数名:参数值 <br/> 如:Accept-Encoding:utf-8",
|
||||
batch_add_parameter: "格式:参数名:参数值 <br/> 如:Accept-Encoding:utf-8 <br/> 注:批量添加里的参数名重复,默认以最后一条数据为最新数据",
|
||||
params_format_warning: "第{0}行数据格式有误",
|
||||
create_performance_test_tips: '没有创建性能测试的权限,无法完成此操作',
|
||||
jar_config: {
|
||||
title: "上传jar包",
|
||||
|
@ -1519,8 +1522,14 @@ export default {
|
|||
environment_json: "环境配置",
|
||||
environment_group_id: "环境组ID",
|
||||
select_environment: "请选择环境",
|
||||
select_variable: "请选择变量",
|
||||
please_save_test: "请先保存测试",
|
||||
common_config: "通用配置",
|
||||
list_info: "列表数据用,分隔",
|
||||
advanced_setting: "点击高级设置,添加变量值",
|
||||
variables_delete_info: "是否确认删除所选变量",
|
||||
csv_delete: "是否确认删除CSV文件",
|
||||
delete_info: "请选择一条数据删除",
|
||||
http_config: "HTTP配置",
|
||||
database_config: "数据库配置",
|
||||
tcp_config: "TCP配置",
|
||||
|
@ -2801,6 +2810,7 @@ export default {
|
|||
delimiter: "分隔符",
|
||||
format: "输出格式",
|
||||
quoted_data: "是否允许带引号",
|
||||
csv_download: "CSV文件暂不支持导出"
|
||||
},
|
||||
auth_source: {
|
||||
delete_prompt: '此操作会删除认证源,是否继续?',
|
||||
|
|
|
@ -212,6 +212,8 @@ export default {
|
|||
modifier: '修改人',
|
||||
validate: "校驗",
|
||||
batch_add: "批量添加",
|
||||
import_variable: "導出變量",
|
||||
export_variable: "导出变量",
|
||||
batch_restore: "批量恢復",
|
||||
batch_gc: "批量回收",
|
||||
check_project_tip: "請先創建或選擇項目",
|
||||
|
@ -1200,7 +1202,8 @@ export default {
|
|||
copy: "復製測試",
|
||||
please_select_case: "請選擇用例",
|
||||
fail_to_stop: "失敗停止",
|
||||
batch_add_parameter: "格式:參數名:參數值 <br/> 如:Accept-Encoding:utf-8",
|
||||
batch_add_parameter: "格式:參數名:參數值 <br/> 如:Accept-Encoding:utf-8 <br/> 注:批量添加里的參數名重複,默認以最後一條數據為最新數據",
|
||||
params_format_warning: "第{0}行數據格式有誤",
|
||||
create_performance_test_tips: '沒有創建性能測試的權限,無法完成此操作',
|
||||
jar_config: {
|
||||
title: "上傳jar包",
|
||||
|
@ -1516,9 +1519,15 @@ export default {
|
|||
environment_json: "環境配置",
|
||||
environment_group_id: "環境組ID",
|
||||
select_environment: "請選擇環境",
|
||||
select_variable: "請選擇变量",
|
||||
please_save_test: "請先保存測試",
|
||||
common_config: "通用配置",
|
||||
http_config: "HTTP配置",
|
||||
list_info: "清單數據用,分隔",
|
||||
advanced_setting: "點擊高級設定,添加變數值",
|
||||
variables_delete_info: "是否確認刪除所選變量",
|
||||
csv_delete: "是否確認删除CSV檔案",
|
||||
delete_info: "請選擇一條數據刪除",
|
||||
database_config: "數據庫配置",
|
||||
tcp_config: "TCP配置",
|
||||
import: "導入環境",
|
||||
|
@ -2795,6 +2804,7 @@ export default {
|
|||
delimiter: "分隔符",
|
||||
format: "輸出格式",
|
||||
quoted_data: "是否允許帶引號",
|
||||
csv_download: "CSV文件暫不支持導出"
|
||||
},
|
||||
auth_source: {
|
||||
delete_prompt: '此操作會刪除認證源,是否繼續?',
|
||||
|
|
Loading…
Reference in New Issue