refactor(接口测试): 支持跨项目场景可以走当前项目环境执行

Signed-off-by: fit2-zhao <yong.zhao@fit2cloud.com>
This commit is contained in:
fit2-zhao 2023-07-26 13:32:04 +08:00 committed by fit2-zhao
parent 9cb5163212
commit a85a59475f
65 changed files with 794 additions and 1947 deletions

View File

@ -8,7 +8,7 @@ import java.util.Map;
@Getter @Getter
@Setter @Setter
public class ApiScenarioEnvRequest { public class ApiScenarioEnvRequest {
private String projectId;
private String definition; private String definition;
private String environmentType; private String environmentType;
private String environmentGroupId; private String environmentGroupId;

View File

@ -8,7 +8,7 @@ import java.util.Set;
@Getter @Getter
@Setter @Setter
public class ScenarioEnv { public class EnvironmentCheckDTO {
private Set<String> projectIds = new HashSet<>(); private Set<String> projectIds = new HashSet<>();
private Boolean fullUrl = true; private Boolean fullUrl = true;

View File

@ -49,6 +49,7 @@ import io.metersphere.utils.LoggerUtil;
import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils; import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.config.Arguments; import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.config.CSVDataSet; import org.apache.jmeter.config.CSVDataSet;
@ -85,7 +86,7 @@ public class ElementUtil {
private static final String BODY_FILE_DIR = FileUtils.BODY_FILE_DIR; private static final String BODY_FILE_DIR = FileUtils.BODY_FILE_DIR;
private static final String TEST_BEAN_GUI = "TestBeanGUI"; private static final String TEST_BEAN_GUI = "TestBeanGUI";
private final static String SCENARIO_REF = "SCENARIO-REF-STEP"; private final static String SCENARIO_REF = "SCENARIO-REF-STEP";
private final static String MS_DEFAULT = "ms-default-data-source";
public final static List<String> scriptList = new ArrayList<String>() {{ public final static List<String> scriptList = new ArrayList<String>() {{
this.add(ElementConstants.JSR223); this.add(ElementConstants.JSR223);
this.add(ElementConstants.JSR223_PRE); this.add(ElementConstants.JSR223_PRE);
@ -349,7 +350,7 @@ public class ElementUtil {
} }
} }
public static void dataSetDomain(JSONArray hashTree, MsParameter msParameter) { public static void dataSetDomain(JSONArray hashTree, ParameterConfig msParameter) {
try { try {
ApiScenarioMapper apiScenarioMapper = CommonBeanFactory.getBean(ApiScenarioMapper.class); ApiScenarioMapper apiScenarioMapper = CommonBeanFactory.getBean(ApiScenarioMapper.class);
BaseEnvGroupProjectService environmentGroupProjectService = CommonBeanFactory.getBean(BaseEnvGroupProjectService.class); BaseEnvGroupProjectService environmentGroupProjectService = CommonBeanFactory.getBean(BaseEnvGroupProjectService.class);
@ -358,7 +359,7 @@ public class ElementUtil {
for (int i = 0; i < hashTree.length(); i++) { for (int i = 0; i < hashTree.length(); i++) {
JSONObject element = hashTree.optJSONObject(i); JSONObject element = hashTree.optJSONObject(i);
boolean isScenarioEnv = false; boolean isScenarioEnv = false;
ParameterConfig config = new ParameterConfig(); ParameterConfig config = new ParameterConfig(msParameter.getCurrentProjectId(), false);
if (element != null && element.get(PropertyConstant.TYPE).toString().equals(ElementConstants.SCENARIO)) { if (element != null && element.get(PropertyConstant.TYPE).toString().equals(ElementConstants.SCENARIO)) {
MsScenario scenario = JSON.parseObject(element.toString(), MsScenario.class); MsScenario scenario = JSON.parseObject(element.toString(), MsScenario.class);
if (scenario.isEnvironmentEnable()) { if (scenario.isEnvironmentEnable()) {
@ -829,10 +830,10 @@ public class ElementUtil {
} }
// 环境通用变量 // 环境通用变量
if (config.isEffective(projectId) if (config.isEffective(projectId)
&& config.getConfig().get(projectId).getCommonConfig() != null && config.get(projectId).getCommonConfig() != null
&& CollectionUtils.isNotEmpty(config.getConfig().get(projectId).getCommonConfig().getVariables())) { && CollectionUtils.isNotEmpty(config.get(projectId).getCommonConfig().getVariables())) {
//常量 //常量
List<ScenarioVariable> constants = config.getConfig().get(projectId).getCommonConfig().getVariables().stream() List<ScenarioVariable> constants = config.get(projectId).getCommonConfig().getVariables().stream()
.filter(ScenarioVariable::isConstantValid) .filter(ScenarioVariable::isConstantValid)
.filter(ScenarioVariable::isEnable) .filter(ScenarioVariable::isEnable)
.collect(Collectors.toList()); .collect(Collectors.toList());
@ -844,7 +845,7 @@ public class ElementUtil {
? keyValue.getValue().replaceAll("[\r\n]", "") ? keyValue.getValue().replaceAll("[\r\n]", "")
: keyValue.getValue()), "=")); : keyValue.getValue()), "="));
// List类型的变量 // List类型的变量
List<ScenarioVariable> variableList = config.getConfig().get(projectId).getCommonConfig().getVariables().stream() List<ScenarioVariable> variableList = config.get(projectId).getCommonConfig().getVariables().stream()
.filter(ScenarioVariable::isListValid) .filter(ScenarioVariable::isListValid)
.filter(ScenarioVariable::isEnable) .filter(ScenarioVariable::isEnable)
.collect(Collectors.toList()); .collect(Collectors.toList());
@ -855,8 +856,8 @@ public class ElementUtil {
} }
}); });
// 清空变量防止重复添加 // 清空变量防止重复添加
config.getConfig().get(projectId).getCommonConfig().getVariables().removeAll(constants); config.get(projectId).getCommonConfig().getVariables().removeAll(constants);
config.getConfig().get(projectId).getCommonConfig().getVariables().removeAll(variableList); config.get(projectId).getCommonConfig().getVariables().removeAll(variableList);
} }
if (arguments.getArguments() != null && arguments.getArguments().size() > 0) { if (arguments.getArguments() != null && arguments.getArguments().size() > 0) {
@ -866,14 +867,14 @@ public class ElementUtil {
} }
public static void addApiVariables(ParameterConfig config, HashTree httpSamplerTree, String projectId) { public static void addApiVariables(ParameterConfig config, HashTree httpSamplerTree, String projectId) {
if (config.isEffective(projectId) && config.getConfig().get(projectId).getCommonConfig() != null && CollectionUtils.isNotEmpty(config.getConfig().get(projectId).getCommonConfig().getVariables())) { if (config.isEffective(projectId) && config.get(projectId).getCommonConfig() != null && CollectionUtils.isNotEmpty(config.get(projectId).getCommonConfig().getVariables())) {
ElementUtil.addApiCsvDataSet(httpSamplerTree, ElementUtil.addApiCsvDataSet(httpSamplerTree,
config.getConfig().get(projectId).getCommonConfig().getVariables(), config.get(projectId).getCommonConfig().getVariables(),
config, "shareMode.group"); config, "shareMode.group");
ElementUtil.addCounter(httpSamplerTree, ElementUtil.addCounter(httpSamplerTree,
config.getConfig().get(projectId).getCommonConfig().getVariables()); config.get(projectId).getCommonConfig().getVariables());
ElementUtil.addRandom(httpSamplerTree, ElementUtil.addRandom(httpSamplerTree,
config.getConfig().get(projectId).getCommonConfig().getVariables()); config.get(projectId).getCommonConfig().getVariables());
} }
} }
@ -968,7 +969,7 @@ public class ElementUtil {
if (StringUtils.isEmpty(environmentId)) { if (StringUtils.isEmpty(environmentId)) {
if (config.getConfig() != null) { if (config.getConfig() != null) {
if (StringUtils.isNotBlank(projectId) && config.getConfig().containsKey(projectId)) { if (StringUtils.isNotBlank(projectId) && config.getConfig().containsKey(projectId)) {
return config.getConfig().get(projectId).getEnvironmentId(); return config.get(projectId).getEnvironmentId();
} else { } else {
if (CollectionUtils.isNotEmpty(config.getConfig().values())) { if (CollectionUtils.isNotEmpty(config.getConfig().values())) {
Optional<EnvironmentConfig> values = config.getConfig().entrySet().stream().findFirst().map(Map.Entry::getValue); Optional<EnvironmentConfig> values = config.getConfig().entrySet().stream().findFirst().map(Map.Entry::getValue);
@ -1004,7 +1005,9 @@ public class ElementUtil {
jdbcProcessor.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass(TEST_BEAN_GUI)); jdbcProcessor.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass(TEST_BEAN_GUI));
ElementUtil.setBaseParams(jdbcProcessor, vo.getParent(), config, vo.getId(), vo.getIndex()); ElementUtil.setBaseParams(jdbcProcessor, vo.getParent(), config, vo.getId(), vo.getIndex());
if (ObjectUtils.isNotEmpty(vo.getDataSource())) {
jdbcProcessor.setDataSource(ElementUtil.getDataSourceName(vo.getDataSource().getName())); jdbcProcessor.setDataSource(ElementUtil.getDataSourceName(vo.getDataSource().getName()));
}
jdbcProcessor.setProperty("dataSource", jdbcProcessor.getDataSource()); jdbcProcessor.setProperty("dataSource", jdbcProcessor.getDataSource());
jdbcProcessor.setProperty("query", vo.getQuery()); jdbcProcessor.setProperty("query", vo.getQuery());
jdbcProcessor.setProperty("queryTimeout", String.valueOf(vo.getQueryTimeout())); jdbcProcessor.setProperty("queryTimeout", String.valueOf(vo.getQueryTimeout()));
@ -1015,6 +1018,9 @@ public class ElementUtil {
} }
public static DataSourceElement jdbcDataSource(String sourceName, DatabaseConfig dataSource) { public static DataSourceElement jdbcDataSource(String sourceName, DatabaseConfig dataSource) {
if (dataSource == null) {
dataSource = new DatabaseConfig(MS_DEFAULT, MS_DEFAULT, 1, 1L, MS_DEFAULT, MS_DEFAULT, MS_DEFAULT, MS_DEFAULT);
}
DataSourceElement dataSourceElement = new DataSourceElement(); DataSourceElement dataSourceElement = new DataSourceElement();
dataSourceElement.setEnabled(true); dataSourceElement.setEnabled(true);
dataSourceElement.setName(sourceName + " JDBCDataSource"); dataSourceElement.setName(sourceName + " JDBCDataSource");
@ -1023,7 +1029,7 @@ public class ElementUtil {
dataSourceElement.setProperty("autocommit", true); dataSourceElement.setProperty("autocommit", true);
dataSourceElement.setProperty("keepAlive", true); dataSourceElement.setProperty("keepAlive", true);
dataSourceElement.setProperty("preinit", false); dataSourceElement.setProperty("preinit", false);
dataSourceElement.setProperty("dataSource", sourceName); dataSourceElement.setProperty("dataSource", StringUtils.defaultIfBlank(sourceName, MS_DEFAULT));
dataSourceElement.setProperty("dbUrl", dataSource.getDbUrl()); dataSourceElement.setProperty("dbUrl", dataSource.getDbUrl());
dataSourceElement.setProperty("driver", dataSource.getDriver()); dataSourceElement.setProperty("driver", dataSource.getDriver());
dataSourceElement.setProperty("username", dataSource.getUsername()); dataSourceElement.setProperty("username", dataSource.getUsername());
@ -1152,15 +1158,15 @@ public class ElementUtil {
return false; return false;
} }
public static DatabaseConfig selectDataSourceFromJDBCProcessor(String processorName, String environmentId, String dataSourceId, String projectId, ParameterConfig config) { public static DatabaseConfig getDataSource(String processorName, String environmentId, String dataSourceId, String projectId, ParameterConfig config) {
if (config == null) { if (config == null) {
return null; return null;
} }
DatabaseConfig dataSource = null; DatabaseConfig dataSource = null;
// 自选了数据源 // 自选了数据源
if (config.isEffective(projectId) && CollectionUtils.isNotEmpty(config.getConfig().get(projectId).getDatabaseConfigs()) if (config.isEffective(projectId) && CollectionUtils.isNotEmpty(config.get(projectId).getDatabaseConfigs())
&& isDataSource(dataSourceId, config.getConfig().get(projectId).getDatabaseConfigs())) { && isDataSource(dataSourceId, config.get(projectId).getDatabaseConfigs())) {
EnvironmentConfig environmentConfig = config.getConfig().get(projectId); EnvironmentConfig environmentConfig = config.get(projectId);
if (environmentConfig.getDatabaseConfigs() != null && StringUtils.isNotEmpty(environmentConfig.getEnvironmentId())) { if (environmentConfig.getDatabaseConfigs() != null && StringUtils.isNotEmpty(environmentConfig.getEnvironmentId())) {
environmentId = environmentConfig.getEnvironmentId(); environmentId = environmentConfig.getEnvironmentId();
} }
@ -1170,14 +1176,14 @@ public class ElementUtil {
} }
} else { } else {
// 取当前环境下默认的一个数据源 // 取当前环境下默认的一个数据源
if (config.isEffective(projectId) && CollectionUtils.isNotEmpty(config.getConfig().get(projectId).getDatabaseConfigs())) { if (config.isEffective(projectId) && CollectionUtils.isNotEmpty(config.get(projectId).getDatabaseConfigs())) {
LoggerUtil.info(processorName + ":开始获取当前环境下默认数据源"); LoggerUtil.info(processorName + ":开始获取当前环境下默认数据源");
DatabaseConfig dataSourceOrg = ElementUtil.dataSource(projectId, dataSourceId, config.getConfig().get(projectId)); DatabaseConfig dataSourceOrg = ElementUtil.dataSource(projectId, dataSourceId, config.get(projectId));
if (dataSourceOrg != null) { if (dataSourceOrg != null) {
dataSource = dataSourceOrg; dataSource = dataSourceOrg;
} else { } else {
LoggerUtil.info(processorName + ":获取当前环境下默认数据源结束!未查找到默认数据源"); LoggerUtil.info(processorName + ":获取当前环境下默认数据源结束!未查找到默认数据源");
dataSource = config.getConfig().get(projectId).getDatabaseConfigs().get(0); dataSource = config.get(projectId).getDatabaseConfigs().get(0);
} }
} }
} }

View File

@ -23,6 +23,7 @@ import io.metersphere.utils.LoggerUtil;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.config.Arguments; import org.apache.jmeter.config.Arguments;
@ -78,7 +79,7 @@ public class MsScenario extends MsTestElement {
// 设置共享cookie // 设置共享cookie
config.setEnableCookieShare(enableCookieShare); config.setEnableCookieShare(enableCookieShare);
Map<String, EnvironmentConfig> envConfig = new HashMap<>(16); Map<String, EnvironmentConfig> envConfig = new HashMap<>(16);
if (config.getConfig() == null) { if (MapUtils.isEmpty(config.getConfig())) {
// 兼容历史数据 // 兼容历史数据
if (this.environmentMap == null || this.environmentMap.isEmpty()) { if (this.environmentMap == null || this.environmentMap.isEmpty()) {
this.environmentMap = new HashMap<>(16); this.environmentMap = new HashMap<>(16);
@ -104,18 +105,18 @@ public class MsScenario extends MsTestElement {
} }
HashTree scenarioTree = tree; HashTree scenarioTree = tree;
// 取出自身场景环境 // 取出自身场景环境
ParameterConfig newConfig = new ParameterConfig(); ParameterConfig newConfig = new ParameterConfig(this.getProjectId(), false);
if (this.isEnvironmentEnable()) { if (this.isEnvironmentEnable()) {
this.setNewConfig(envConfig, newConfig); this.setNewConfig(envConfig, newConfig);
newConfig.setRetryNum(config.getRetryNum()); newConfig.setRetryNum(config.getRetryNum());
} }
if (config != null && StringUtils.equals(this.getId(), config.getScenarioId())) { if (StringUtils.equals(this.getId(), config.getScenarioId())) {
config.setTransferVariables(this.variables); config.setTransferVariables(this.variables);
if (CollectionUtils.isNotEmpty(this.headers)) { if (CollectionUtils.isNotEmpty(this.headers)) {
ElementUtil.setHeader(scenarioTree, this.headers, this.getName()); ElementUtil.setHeader(scenarioTree, this.headers, this.getName());
} }
} }
if (config != null && !config.getExcludeScenarioIds().contains(this.getId())) { if (!config.getExcludeScenarioIds().contains(this.getId())) {
scenarioTree = MsCriticalSectionController.createHashTree(tree, this.getName(), this.isEnable()); scenarioTree = MsCriticalSectionController.createHashTree(tree, this.getName(), this.isEnable());
} }
// 启用当前场景变量优先选择 // 启用当前场景变量优先选择
@ -130,7 +131,7 @@ public class MsScenario extends MsTestElement {
// 这里加入自定义变量解决ForEach循环控制器取值问题循环控制器无法从vars中取值 // 这里加入自定义变量解决ForEach循环控制器取值问题循环控制器无法从vars中取值
if (BooleanUtils.isTrue(this.variableEnable) || BooleanUtils.isTrue(this.mixEnable)) { if (BooleanUtils.isTrue(this.variableEnable) || BooleanUtils.isTrue(this.mixEnable)) {
scenarioTree.add(ElementUtil.argumentsToUserParameters(valueSupposeMock)); scenarioTree.add(ElementUtil.argumentsToUserParameters(valueSupposeMock));
} else if (config != null && (this.isAllEnable() || config.isApi())) { } else if (this.isAllEnable() || config.isApi()) {
valueSupposeMock.setProperty(ElementConstants.COVER, true); valueSupposeMock.setProperty(ElementConstants.COVER, true);
scenarioTree.add(valueSupposeMock); scenarioTree.add(valueSupposeMock);
} }
@ -188,7 +189,7 @@ public class MsScenario extends MsTestElement {
if (envProcessor != null) { if (envProcessor != null) {
BeanUtils.copyBean(processor, envProcessor); BeanUtils.copyBean(processor, envProcessor);
} }
if (processor != null && StringUtils.isNotEmpty(processor.getScript())) { if (StringUtils.isNotEmpty(processor.getScript())) {
processor.setType(ElementConstants.JSR223); processor.setType(ElementConstants.JSR223);
processor.setClazzName(MsJSR223Processor.class.getCanonicalName()); processor.setClazzName(MsJSR223Processor.class.getCanonicalName());
boolean isConnScenarioPre = false; boolean isConnScenarioPre = false;

View File

@ -22,6 +22,7 @@ import io.metersphere.service.definition.ApiTestCaseService;
import io.metersphere.service.plan.TestPlanApiCaseService; import io.metersphere.service.plan.TestPlanApiCaseService;
import lombok.Data; import lombok.Data;
import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.config.Arguments; import org.apache.jmeter.config.Arguments;
@ -30,15 +31,19 @@ import java.util.stream.Collectors;
@Data @Data
public class ParameterConfig extends MsParameter { public class ParameterConfig extends MsParameter {
public ParameterConfig(String currentProjectId, boolean isApi) {
this.currentProjectId = currentProjectId;
this.setApi(isApi);
if (MapUtils.isEmpty(this.config)) {
this.config = new HashMap<>();
}
}
/** /**
* 环境配置 * 环境配置
*/ */
private Map<String, EnvironmentConfig> config; private Map<String, EnvironmentConfig> config;
/**
* UI 指令全局配置
*/
private Object commandConfig;
/** /**
* 缓存同一批请求的认证信息 * 缓存同一批请求的认证信息
*/ */
@ -61,10 +66,6 @@ public class ParameterConfig extends MsParameter {
* 公共Cookie * 公共Cookie
*/ */
private boolean enableCookieShare; private boolean enableCookieShare;
/**
* 是否停止继续
*/
private Boolean onSampleError;
/** /**
* 是否是导入/导出操作 * 是否是导入/导出操作
@ -74,13 +75,12 @@ public class ParameterConfig extends MsParameter {
* 导入/导出操作时取样器的testName值 * 导入/导出操作时取样器的testName值
*/ */
private String operatingSampleTestName; private String operatingSampleTestName;
/**
* 项目ID支持单接口执行
*/
private String projectId;
private String scenarioId; private String scenarioId;
/**
* 当前项目id
*/
private String currentProjectId;
/** /**
* 报告 ID * 报告 ID
*/ */
@ -90,7 +90,6 @@ public class ParameterConfig extends MsParameter {
private boolean runLocal; private boolean runLocal;
private String browserLanguage;
private boolean isApi; private boolean isApi;
/** /**
* 失败重试次数 * 失败重试次数
@ -104,7 +103,8 @@ public class ParameterConfig extends MsParameter {
private List<String> csvFilePaths = new ArrayList<>(); private List<String> csvFilePaths = new ArrayList<>();
public boolean isEffective(String projectId) { public boolean isEffective(String projectId) {
if (this.config != null && this.config.get(projectId) != null) { if ((StringUtils.isNotBlank(projectId) && this.config != null && this.config.get(projectId) != null)
|| (StringUtils.isNotBlank(this.currentProjectId) && this.config != null && this.config.get(currentProjectId) != null)) {
return true; return true;
} }
return false; return false;
@ -119,8 +119,7 @@ public class ParameterConfig extends MsParameter {
} }
public HttpConfig matchConfig(MsHTTPSamplerProxy samplerProxy) { public HttpConfig matchConfig(MsHTTPSamplerProxy samplerProxy, HttpConfig httpConfig) {
HttpConfig httpConfig = this.getConfig().get(samplerProxy.getProjectId()).getHttpConfig();
boolean isNext = true; boolean isNext = true;
if (CollectionUtils.isNotEmpty(httpConfig.getConditions())) { if (CollectionUtils.isNotEmpty(httpConfig.getConditions())) {
for (HttpConfigCondition item : httpConfig.getConditions()) { for (HttpConfigCondition item : httpConfig.getConditions()) {
@ -254,4 +253,17 @@ public class ParameterConfig extends MsParameter {
}); });
} }
} }
/**
* 获取项目环境配置如果没有则返回当前项目环境配置
*
* @param projectId 项目ID
*/
public EnvironmentConfig get(String projectId) {
EnvironmentConfig envConfig = this.getConfig().get(projectId);
if (envConfig == null && StringUtils.isNotEmpty(this.getCurrentProjectId())) {
return this.config.get(this.getCurrentProjectId());
}
return envConfig;
}
} }

View File

@ -52,7 +52,7 @@ public class MsAuthManager extends MsTestElement {
} else { } else {
if (config != null && config.isEffective(this.getProjectId())) { if (config != null && config.isEffective(this.getProjectId())) {
if (config.isEffective(this.getProjectId())) { if (config.isEffective(this.getProjectId())) {
String url = config.getConfig().get(this.getProjectId()).getHttpConfig().getProtocol() + "://" + config.getConfig().get(this.getProjectId()).getHttpConfig().getSocket(); String url = config.get(this.getProjectId()).getHttpConfig().getProtocol() + "://" + config.get(this.getProjectId()).getHttpConfig().getSocket();
auth.setURL(url); auth.setURL(url);
} }
} }

View File

@ -6,7 +6,6 @@ import io.metersphere.api.dto.definition.request.ParameterConfig;
import io.metersphere.api.dto.scenario.DatabaseConfig; import io.metersphere.api.dto.scenario.DatabaseConfig;
import io.metersphere.api.dto.scenario.KeyValue; import io.metersphere.api.dto.scenario.KeyValue;
import io.metersphere.commons.constants.ElementConstants; import io.metersphere.commons.constants.ElementConstants;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.BeanUtils; import io.metersphere.commons.utils.BeanUtils;
import io.metersphere.commons.utils.JSONUtil; import io.metersphere.commons.utils.JSONUtil;
import io.metersphere.commons.vo.JDBCProcessorVO; import io.metersphere.commons.vo.JDBCProcessorVO;
@ -16,6 +15,7 @@ import io.metersphere.utils.LoggerUtil;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.protocol.jdbc.processor.JDBCPostProcessor; import org.apache.jmeter.protocol.jdbc.processor.JDBCPostProcessor;
import org.apache.jorphan.collections.HashTree; import org.apache.jorphan.collections.HashTree;
@ -57,22 +57,20 @@ public class MsJDBCPostProcessor extends MsTestElement {
if (StringUtils.isBlank(this.getProjectId()) && this.getParent() != null) { if (StringUtils.isBlank(this.getProjectId()) && this.getParent() != null) {
this.setProjectId(this.getParent().getProjectId()); this.setProjectId(this.getParent().getProjectId());
} }
if (config.getConfig() == null) { if (MapUtils.isEmpty(config.getConfig()) && config.isApi()) {
// 单独接口执行 // 单独接口执行
this.setProjectId(config.getProjectId()); this.setProjectId(config.getCurrentProjectId());
config.setConfig(ElementUtil.getEnvironmentConfig(StringUtils.isNotEmpty(useEnvironment) ? useEnvironment : environmentId, this.getProjectId())); config.setConfig(ElementUtil.getEnvironmentConfig(StringUtils.isNotEmpty(useEnvironment) ? useEnvironment : environmentId, this.getProjectId()));
} }
this.dataSource = ElementUtil.selectDataSourceFromJDBCProcessor(this.getName(), this.environmentId, this.dataSourceId, this.getProjectId(), config); this.dataSource = ElementUtil.getDataSource(this.getName(), this.environmentId, this.dataSourceId, this.getProjectId(), config);
if (this.dataSource == null) { if (this.dataSource == null) {
// 用自身的数据 // 用自身的数据
if (StringUtils.isNotEmpty(dataSourceId)) { if (StringUtils.isNotEmpty(dataSourceId)) {
this.dataSource = ElementUtil.initDataSource(this.environmentId, this.dataSourceId); this.dataSource = ElementUtil.initDataSource(this.environmentId, this.dataSourceId);
} }
if (this.dataSource == null) { if (this.dataSource == null) {
LoggerUtil.info(this.getName() + " 当前项目id", this.getProjectId() + " 当前环境配置信息", JSONUtil.toJSONString(config)); LoggerUtil.error(this.getName() + ",未找到数据源", JSONUtil.toJSONString(config));
String message = "数据源为空请选择数据源";
MSException.throwException(StringUtils.isNotEmpty(this.getName()) ? this.getName() + "" + message : message);
} }
} }
JDBCPostProcessor jdbcPostProcessor = jdbcPostProcessor(config); JDBCPostProcessor jdbcPostProcessor = jdbcPostProcessor(config);

View File

@ -17,6 +17,7 @@ import io.metersphere.utils.LoggerUtil;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.protocol.jdbc.processor.JDBCPreProcessor; import org.apache.jmeter.protocol.jdbc.processor.JDBCPreProcessor;
import org.apache.jorphan.collections.HashTree; import org.apache.jorphan.collections.HashTree;
@ -57,21 +58,19 @@ public class MsJDBCPreProcessor extends MsTestElement {
if (StringUtils.isBlank(this.getProjectId()) && this.getParent() != null) { if (StringUtils.isBlank(this.getProjectId()) && this.getParent() != null) {
this.setProjectId(this.getParent().getProjectId()); this.setProjectId(this.getParent().getProjectId());
} }
if (config.getConfig() == null) { if (MapUtils.isEmpty(config.getConfig()) && config.isApi()) {
// 单独接口执行 // 单独接口执行
this.setProjectId(config.getProjectId()); this.setProjectId(config.getCurrentProjectId());
config.setConfig(ElementUtil.getEnvironmentConfig(StringUtils.isNotEmpty(useEnvironment) ? useEnvironment : environmentId, this.getProjectId())); config.setConfig(ElementUtil.getEnvironmentConfig(StringUtils.isNotEmpty(useEnvironment) ? useEnvironment : environmentId, this.getProjectId()));
} }
this.dataSource = ElementUtil.selectDataSourceFromJDBCProcessor(this.getName(), this.environmentId, this.dataSourceId, this.getProjectId(), config); this.dataSource = ElementUtil.getDataSource(this.getName(), this.environmentId, this.dataSourceId, this.getProjectId(), config);
if (this.dataSource == null) { if (this.dataSource == null) {
// 用自身的数据 // 用自身的数据
if (StringUtils.isNotEmpty(dataSourceId)) { if (StringUtils.isNotEmpty(dataSourceId)) {
this.dataSource = ElementUtil.initDataSource(this.environmentId, this.dataSourceId); this.dataSource = ElementUtil.initDataSource(this.environmentId, this.dataSourceId);
} }
if (this.dataSource == null) { if (this.dataSource == null) {
LoggerUtil.info(this.getName() + " 当前项目id", this.getProjectId() + " 当前环境配置信息", JSONUtil.toJSONString(config)); LoggerUtil.error(this.getName() + ",未找到数据源", JSONUtil.toJSONString(config));
String message = "数据源为空请选择数据源";
MSException.throwException(StringUtils.isNotEmpty(this.getName()) ? this.getName() + "" + message : message);
} }
} }

View File

@ -90,7 +90,7 @@ public class MsDubboSampler extends MsTestElement {
//添加全局前后置脚本 //添加全局前后置脚本
EnvironmentConfig envConfig = null; EnvironmentConfig envConfig = null;
if (config.getConfig() != null) { if (config.getConfig() != null) {
envConfig = config.getConfig().get(this.getProjectId()); envConfig = config.get(this.getProjectId());
} }
//处理全局前后置脚本(步骤内) //处理全局前后置脚本(步骤内)
String environmentId = this.getEnvironmentId(); String environmentId = this.getEnvironmentId();

View File

@ -31,13 +31,16 @@ import io.metersphere.jmeter.utils.ScriptEngineUtils;
import io.metersphere.plugin.core.MsParameter; import io.metersphere.plugin.core.MsParameter;
import io.metersphere.plugin.core.MsTestElement; import io.metersphere.plugin.core.MsTestElement;
import io.metersphere.service.definition.ApiTestCaseService; import io.metersphere.service.definition.ApiTestCaseService;
import io.metersphere.utils.LoggerUtil;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.config.Arguments; import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.config.KeystoreConfig; import org.apache.jmeter.config.KeystoreConfig;
import org.apache.jmeter.protocol.http.sampler.HTTPSamplerBase;
import org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy; import org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy;
import org.apache.jmeter.protocol.http.util.HTTPArgument; import org.apache.jmeter.protocol.http.util.HTTPArgument;
import org.apache.jmeter.protocol.http.util.HTTPConstants; import org.apache.jmeter.protocol.http.util.HTTPConstants;
@ -134,10 +137,10 @@ public class MsHTTPSamplerProxy extends MsTestElement {
sampler.setImplementation(this.getImplementation()); sampler.setImplementation(this.getImplementation());
sampler.setUseKeepAlive(true); sampler.setUseKeepAlive(true);
sampler.setDoMultipart(this.isDoMultipartPost()); sampler.setDoMultipart(this.isDoMultipartPost());
if (config.getConfig() == null) { if (MapUtils.isEmpty(config.getConfig()) && config.isApi()) {
// 单独接口执行 // 单独接口执行
if (StringUtils.isNotEmpty(config.getProjectId())) { if (StringUtils.isNotEmpty(config.getCurrentProjectId())) {
this.setProjectId(config.getProjectId()); this.setProjectId(config.getCurrentProjectId());
} }
String projectId = this.getProjectId(); String projectId = this.getProjectId();
config.setConfig(ElementUtil.getEnvironmentConfig(this.useEnvironment, projectId)); config.setConfig(ElementUtil.getEnvironmentConfig(this.useEnvironment, projectId));
@ -166,7 +169,7 @@ public class MsHTTPSamplerProxy extends MsTestElement {
} }
if (CollectionUtils.isNotEmpty(bodyParams)) { if (CollectionUtils.isNotEmpty(bodyParams)) {
Arguments arguments = httpArguments(bodyParams); Arguments arguments = httpArguments(bodyParams);
if (arguments != null && !arguments.getArguments().isEmpty()) { if (!arguments.getArguments().isEmpty()) {
sampler.setArguments(arguments); sampler.setArguments(arguments);
} }
} }
@ -205,7 +208,7 @@ public class MsHTTPSamplerProxy extends MsTestElement {
} }
} }
// 场景头 // 场景头
if (config != null && CollectionUtils.isNotEmpty(config.getHeaders())) { if (CollectionUtils.isNotEmpty(config.getHeaders())) {
ElementUtil.setHeader(httpSamplerTree, config.getHeaders(), this.getName()); ElementUtil.setHeader(httpSamplerTree, config.getHeaders(), this.getName());
} }
// 环境通用请求头 // 环境通用请求头
@ -216,10 +219,10 @@ public class MsHTTPSamplerProxy extends MsTestElement {
//添加csv //添加csv
ElementUtil.addApiVariables(config, httpSamplerTree, this.getProjectId()); ElementUtil.addApiVariables(config, httpSamplerTree, this.getProjectId());
//判断是否要开启DNS //判断是否要开启DNS
if (config.isEffective(this.getProjectId()) && config.getConfig().get(this.getProjectId()).getCommonConfig() != null if (config.isEffective(this.getProjectId()) && config.get(this.getProjectId()).getCommonConfig() != null
&& config.getConfig().get(this.getProjectId()).getCommonConfig().isEnableHost()) { && config.get(this.getProjectId()).getCommonConfig().isEnableHost()) {
MsDNSCacheManager.addEnvironmentVariables(httpSamplerTree, this.getName(), config.getConfig().get(this.getProjectId())); MsDNSCacheManager.addEnvironmentVariables(httpSamplerTree, this.getName(), config.get(this.getProjectId()));
MsDNSCacheManager.addEnvironmentDNS(httpSamplerTree, this.getName(), config.getConfig().get(this.getProjectId()), httpConfig); MsDNSCacheManager.addEnvironmentDNS(httpSamplerTree, this.getName(), config.get(this.getProjectId()), httpConfig);
} }
if (this.authManager != null && MsAuthManager.mechanismMap.containsKey(this.authManager.getVerification())) { if (this.authManager != null && MsAuthManager.mechanismMap.containsKey(this.authManager.getVerification())) {
this.authManager.setAuth(httpSamplerTree, this.authManager, sampler); this.authManager.setAuth(httpSamplerTree, this.authManager, sampler);
@ -295,11 +298,11 @@ public class MsHTTPSamplerProxy extends MsTestElement {
private void initConnectAndResponseTimeout(ParameterConfig config) { private void initConnectAndResponseTimeout(ParameterConfig config) {
if (config.isEffective(this.getProjectId())) { if (config.isEffective(this.getProjectId())) {
String useEvnId = config.getConfig().get(this.getProjectId()).getEnvironmentId(); String useEvnId = config.get(this.getProjectId()).getEnvironmentId();
if (StringUtils.isNotEmpty(useEvnId) && !StringUtils.equals(useEvnId, this.getEnvironmentId())) { if (StringUtils.isNotEmpty(useEvnId) && !StringUtils.equals(useEvnId, this.getEnvironmentId())) {
this.setEnvironmentId(useEvnId); this.setEnvironmentId(useEvnId);
} }
CommonConfig commonConfig = config.getConfig().get(this.getProjectId()).getCommonConfig(); CommonConfig commonConfig = config.get(this.getProjectId()).getCommonConfig();
if (commonConfig != null) { if (commonConfig != null) {
if (this.getConnectTimeout() == null || StringUtils.equals(this.getConnectTimeout(), DEF_TIME_OUT)) { if (this.getConnectTimeout() == null || StringUtils.equals(this.getConnectTimeout(), DEF_TIME_OUT)) {
if (commonConfig.getRequestTimeout() != 0) { if (commonConfig.getRequestTimeout() != 0) {
@ -316,46 +319,47 @@ public class MsHTTPSamplerProxy extends MsTestElement {
} }
private EnvironmentConfig getEnvironmentConfig(ParameterConfig config) { private EnvironmentConfig getEnvironmentConfig(ParameterConfig config) {
return config.getConfig().get(this.getProjectId()); return config.get(this.getProjectId());
} }
private HttpConfig getHttpConfig(ParameterConfig config) { private HttpConfig getHttpConfig(ParameterConfig config) {
if (config.isEffective(this.getProjectId())) { if (config.isEffective(this.getProjectId()) || config.isEffective(config.getCurrentProjectId())) {
EnvironmentConfig environmentConfig = config.getConfig().get(this.getProjectId()); EnvironmentConfig envConfig = config.get(this.getProjectId());
if (environmentConfig != null) { if (envConfig != null) {
String useEvnId = environmentConfig.getEnvironmentId(); String useEnvId = envConfig.getEnvironmentId();
if (this.authManager == null && environmentConfig.getAuthManager() != null && environmentConfig.getAuthManager().getAuthManager() != null) { if (this.authManager == null && envConfig.getAuthManager() != null
&& envConfig.getAuthManager().getAuthManager() != null) {
MsAuthManager authManager = new MsAuthManager(); MsAuthManager authManager = new MsAuthManager();
BeanUtils.copyBean(authManager, environmentConfig.getAuthManager().getAuthManager()); BeanUtils.copyBean(authManager, envConfig.getAuthManager().getAuthManager());
this.authManager = authManager; this.authManager = authManager;
} }
if (StringUtils.isNotEmpty(useEvnId) && !StringUtils.equals(useEvnId, this.getEnvironmentId())) { if (StringUtils.isNotEmpty(useEnvId) && !StringUtils.equals(useEnvId, this.getEnvironmentId())) {
this.setEnvironmentId(useEvnId); this.setEnvironmentId(useEnvId);
} }
HttpConfig httpConfig = config.matchConfig(this); HttpConfig httpConfig = config.matchConfig(this, envConfig.getHttpConfig());
if (environmentConfig.getPreProcessor() != null) { if (envConfig.getPreProcessor() != null) {
MsJSR223PreProcessor msJSR223PreProcessor = new MsJSR223PreProcessor(); MsJSR223PreProcessor msJSR223PreProcessor = new MsJSR223PreProcessor();
if (environmentConfig.getPreProcessor() != null) { if (envConfig.getPreProcessor() != null) {
BeanUtils.copyBean(msJSR223PreProcessor, environmentConfig.getPreProcessor()); BeanUtils.copyBean(msJSR223PreProcessor, envConfig.getPreProcessor());
httpConfig.setPreProcessor(msJSR223PreProcessor); httpConfig.setPreProcessor(msJSR223PreProcessor);
} }
} }
if (environmentConfig.getPostProcessor() != null) { if (envConfig.getPostProcessor() != null) {
MsJSR223PostProcessor postProcessor = new MsJSR223PostProcessor(); MsJSR223PostProcessor postProcessor = new MsJSR223PostProcessor();
if (environmentConfig.getPostProcessor() != null) { if (envConfig.getPostProcessor() != null) {
BeanUtils.copyBean(postProcessor, environmentConfig.getPostProcessor()); BeanUtils.copyBean(postProcessor, envConfig.getPostProcessor());
httpConfig.setPostProcessor(postProcessor); httpConfig.setPostProcessor(postProcessor);
} }
} }
httpConfig.setGlobalScriptConfig(environmentConfig.getGlobalScriptConfig()); httpConfig.setGlobalScriptConfig(envConfig.getGlobalScriptConfig());
if (CollectionUtils.isNotEmpty(environmentConfig.getAssertions())) { if (CollectionUtils.isNotEmpty(envConfig.getAssertions())) {
httpConfig.setAssertions(ElementUtil.copyAssertion(environmentConfig.getAssertions())); httpConfig.setAssertions(ElementUtil.copyAssertion(envConfig.getAssertions()));
} }
if ((!this.isCustomizeReq() || this.isRefEnvironment) && environmentConfig.isUseErrorCode()) { if ((!this.isCustomizeReq() || this.isRefEnvironment) && envConfig.isUseErrorCode()) {
FakeError fakeError = new FakeError(); FakeError fakeError = new FakeError();
fakeError.setHigherThanError(environmentConfig.isHigherThanError()); fakeError.setHigherThanError(envConfig.isHigherThanError());
fakeError.setProjectId(this.getProjectId()); fakeError.setProjectId(this.getProjectId());
fakeError.setHigherThanSuccess(environmentConfig.isHigherThanSuccess()); fakeError.setHigherThanSuccess(envConfig.isHigherThanSuccess());
httpConfig.setFakeError(fakeError); httpConfig.setFakeError(fakeError);
} }
return httpConfig; return httpConfig;
@ -367,14 +371,8 @@ public class MsHTTPSamplerProxy extends MsTestElement {
private void setSamplerPath(ParameterConfig config, HttpConfig httpConfig, HTTPSamplerProxy sampler) { private void setSamplerPath(ParameterConfig config, HttpConfig httpConfig, HTTPSamplerProxy sampler) {
try { try {
if (config.isEffective(this.getProjectId())) { if (config.isEffective(this.getProjectId())) {
if (httpConfig == null && !ElementUtil.isURL(this.getUrl())) {
MSException.throwException("未匹配到环境,请检查环境配置");
}
if (StringUtils.isEmpty(httpConfig.getProtocol())) {
MSException.throwException(this.getName() + "接口,对应的环境无协议,请完善环境信息");
}
if (StringUtils.isEmpty(this.useEnvironment)) { if (StringUtils.isEmpty(this.useEnvironment)) {
this.useEnvironment = config.getConfig().get(this.getProjectId()).getEnvironmentId(); this.useEnvironment = config.get(this.getProjectId()).getEnvironmentId();
} }
String url = httpConfig.getProtocol() + "://" + httpConfig.getSocket(); String url = httpConfig.getProtocol() + "://" + httpConfig.getSocket();
if (isUrl()) { if (isUrl()) {
@ -386,7 +384,7 @@ public class MsHTTPSamplerProxy extends MsTestElement {
String envPath = sampler.getPath(); String envPath = sampler.getPath();
if (CollectionUtils.isNotEmpty(this.getRest()) && this.isRest()) { if (CollectionUtils.isNotEmpty(this.getRest()) && this.isRest()) {
envPath = getRestParameters(envPath); envPath = getRestParameters(envPath);
sampler.setProperty("HTTPSampler.path", envPath); sampler.setProperty(HTTPSamplerBase.PATH, envPath);
} }
if (CollectionUtils.isNotEmpty(this.getArguments())) { if (CollectionUtils.isNotEmpty(this.getArguments())) {
String path = postQueryParameters(envPath); String path = postQueryParameters(envPath);
@ -395,34 +393,30 @@ public class MsHTTPSamplerProxy extends MsTestElement {
path = "/" + path; path = "/" + path;
} }
} }
sampler.setProperty("HTTPSampler.path", path); sampler.setProperty(HTTPSamplerBase.PATH, path);
} }
} else { } else {
String url = this.getUrl(); String envPath;
if (StringUtils.isEmpty(url)) {
MSException.throwException("当前步骤:" + this.getName() + " 环境为空,请重新选择环境");
}
String envPath = "";
try { try {
URL urlObject = new URL(url); URL urlObject = new URL(url);
if (url.contains("${")) { if (url.contains("${")) {
envPath = url; envPath = url;
} else { } else {
sampler.setDomain(URLDecoder.decode(urlObject.getHost(), StandardCharsets.UTF_8.name())); sampler.setDomain(URLDecoder.decode(urlObject.getHost(), StandardCharsets.UTF_8));
envPath = urlObject.getPath(); envPath = urlObject.getPath();
sampler.setPort(urlObject.getPort()); sampler.setPort(urlObject.getPort());
} }
sampler.setProtocol(urlObject.getProtocol()); sampler.setProtocol(urlObject.getProtocol());
} catch (Exception e) { } catch (Exception e) {
envPath = url; envPath = url == null ? "" : url;
} }
sampler.setProperty("HTTPSampler.path", envPath, StandardCharsets.UTF_8.name()); sampler.setProperty(HTTPSamplerBase.PATH, envPath, StandardCharsets.UTF_8.name());
if (CollectionUtils.isNotEmpty(this.getRest()) && this.isRest()) { if (CollectionUtils.isNotEmpty(this.getRest()) && this.isRest()) {
envPath = getRestParameters(URLDecoder.decode(URLEncoder.encode(envPath, StandardCharsets.UTF_8.name()), StandardCharsets.UTF_8.name())); envPath = getRestParameters(URLDecoder.decode(URLEncoder.encode(envPath, StandardCharsets.UTF_8), StandardCharsets.UTF_8));
sampler.setProperty("HTTPSampler.path", envPath); sampler.setProperty(HTTPSamplerBase.PATH, envPath);
} }
if (CollectionUtils.isNotEmpty(this.getArguments())) { if (CollectionUtils.isNotEmpty(this.getArguments())) {
sampler.setProperty("HTTPSampler.path", postQueryParameters(URLDecoder.decode(URLEncoder.encode(envPath, StandardCharsets.UTF_8.name()), StandardCharsets.UTF_8.name()))); sampler.setProperty(HTTPSamplerBase.PATH, postQueryParameters(URLDecoder.decode(URLEncoder.encode(envPath, StandardCharsets.UTF_8), StandardCharsets.UTF_8)));
} }
} }
} catch (Exception e) { } catch (Exception e) {
@ -451,7 +445,7 @@ public class MsHTTPSamplerProxy extends MsTestElement {
} else { } else {
envPath = StringUtils.join(urlObject.getPath(), envPath); envPath = StringUtils.join(urlObject.getPath(), envPath);
} }
sampler.setDomain(URLDecoder.decode(urlObject.getHost(), StandardCharsets.UTF_8.name())); sampler.setDomain(URLDecoder.decode(urlObject.getHost(), StandardCharsets.UTF_8));
sampler.setProtocol(httpConfig.getProtocol()); sampler.setProtocol(httpConfig.getProtocol());
sampler.setPort(urlObject.getPort()); sampler.setPort(urlObject.getPort());
} else { } else {
@ -465,30 +459,30 @@ public class MsHTTPSamplerProxy extends MsTestElement {
} else { } else {
URL urlObject = new URL(this.path); URL urlObject = new URL(this.path);
envPath = StringUtils.equals(urlObject.getPath(), "/") ? "" : urlObject.getFile(); envPath = StringUtils.equals(urlObject.getPath(), "/") ? "" : urlObject.getFile();
sampler.setDomain(URLDecoder.decode(urlObject.getHost(), StandardCharsets.UTF_8.name())); sampler.setDomain(URLDecoder.decode(urlObject.getHost(), StandardCharsets.UTF_8));
sampler.setProtocol(urlObject.getProtocol()); sampler.setProtocol(urlObject.getProtocol());
} }
sampler.setProperty("HTTPSampler.path", URLDecoder.decode(URLEncoder.encode(envPath, StandardCharsets.UTF_8.name()), StandardCharsets.UTF_8.name())); sampler.setProperty(HTTPSamplerBase.PATH, URLDecoder.decode(URLEncoder.encode(envPath, StandardCharsets.UTF_8), StandardCharsets.UTF_8));
} }
} }
private void fullPath(HTTPSamplerProxy sampler, String url) { private void fullPath(HTTPSamplerProxy sampler, String url) {
try {
if (this.isCustomizeReq() && StringUtils.isNotEmpty(this.getUrl())) { if (this.isCustomizeReq() && StringUtils.isNotEmpty(this.getUrl())) {
url = this.getUrl(); url = this.getUrl();
sampler.setProperty("HTTPSampler.path", url); sampler.setProperty(HTTPSamplerBase.PATH, url);
} }
if (StringUtils.isNotEmpty(this.getPort()) && this.getPort().startsWith("${")) { if (StringUtils.isNotEmpty(this.getPort()) && this.getPort().startsWith("${")) {
url = url.replace(this.getPort(), "10990"); url = url.replace(this.getPort(), "10990");
} }
try {
URL urlObject = new URL(url); URL urlObject = new URL(url);
String envPath; String envPath;
if (url.contains("${")) { if (url.contains("${")) {
envPath = url; envPath = url;
} else { } else {
sampler.setDomain(URLDecoder.decode(urlObject.getHost(), StandardCharsets.UTF_8.name())); sampler.setDomain(URLDecoder.decode(urlObject.getHost(), StandardCharsets.UTF_8));
if (urlObject.getPort() > 0 && urlObject.getPort() == 10990 && StringUtils.isNotEmpty(this.getPort()) && this.getPort().startsWith("${")) { if (urlObject.getPort() == 10990 && StringUtils.isNotEmpty(this.getPort()) && this.getPort().startsWith("${")) {
sampler.setProperty("HTTPSampler.port", this.getPort()); sampler.setProperty(HTTPSamplerBase.PORT, this.getPort());
} else if (urlObject.getPort() != -1) { } else if (urlObject.getPort() != -1) {
sampler.setPort(urlObject.getPort()); sampler.setPort(urlObject.getPort());
} }
@ -496,19 +490,15 @@ public class MsHTTPSamplerProxy extends MsTestElement {
} }
sampler.setProtocol(urlObject.getProtocol()); sampler.setProtocol(urlObject.getProtocol());
sampler.setProperty("HTTPSampler.path", URLDecoder.decode(envPath, StandardCharsets.UTF_8.name()), StandardCharsets.UTF_8.name()); sampler.setProperty(HTTPSamplerBase.PATH, URLDecoder.decode(envPath, StandardCharsets.UTF_8), StandardCharsets.UTF_8.name());
} catch (Exception e) { } catch (Exception e) {
sampler.setProperty("HTTPSampler.path", url); sampler.setProperty(HTTPSamplerBase.PATH, url);
LogUtil.error(e.getMessage(), e); LogUtil.error(e.getMessage(), e);
} }
} }
/** /**
* 加载SSL认证 * 加载SSL认证
*
* @param config
* @param httpSamplerTree
* @return
*/ */
private void addCertificate(ParameterConfig config, HashTree httpSamplerTree) { private void addCertificate(ParameterConfig config, HashTree httpSamplerTree) {
if (config != null && config.isEffective(this.getProjectId()) && getEnvironmentConfig(config).getSslConfig() != null) { if (config != null && config.isEffective(this.getProjectId()) && getEnvironmentConfig(config).getSslConfig() != null) {
@ -561,24 +551,15 @@ public class MsHTTPSamplerProxy extends MsTestElement {
/** /**
* 自定义请求如果是完整url时不拼接mock信息 * 自定义请求如果是完整url时不拼接mock信息
*
* @param url
* @return
*/ */
private boolean isCustomizeReqCompleteUrl(String url) { private boolean isCustomizeReqCompleteUrl(String url) {
if (isCustomizeReq() && StringUtils.isNotEmpty(url) && (url.startsWith("http://") || url.startsWith("https://"))) { return isCustomizeReq() && StringUtils.isNotEmpty(url) && (url.startsWith("http://") || url.startsWith("https://"));
return true;
}
return false;
} }
private boolean isUrl() { private boolean isUrl() {
// 自定义字段没有引用环境则非url // 自定义字段没有引用环境则非url
if (this.isCustomizeReq()) { if (this.isCustomizeReq()) {
if (this.isRefEnvironment) { return !this.isRefEnvironment;
return false;
}
return true;
} }
return false; return false;
} }
@ -624,7 +605,7 @@ public class MsHTTPSamplerProxy extends MsTestElement {
} }
} }
} catch (Exception ex) { } catch (Exception ex) {
ex.printStackTrace(); LoggerUtil.error(this.getName(), ex);
} }
return path; return path;
} }
@ -632,7 +613,7 @@ public class MsHTTPSamplerProxy extends MsTestElement {
private String postQueryParameters(String path) { private String postQueryParameters(String path) {
StringBuffer stringBuffer = new StringBuffer(); StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append(path); stringBuffer.append(path);
if (path.indexOf("?") != -1) { if (path.contains("?")) {
stringBuffer.append("&"); stringBuffer.append("&");
} else { } else {
stringBuffer.append("?"); stringBuffer.append("?");

View File

@ -15,7 +15,6 @@ import io.metersphere.base.domain.ApiTestEnvironmentWithBLOBs;
import io.metersphere.commons.constants.CommonConstants; import io.metersphere.commons.constants.CommonConstants;
import io.metersphere.commons.constants.ElementConstants; import io.metersphere.commons.constants.ElementConstants;
import io.metersphere.commons.constants.MsTestElementConstants; import io.metersphere.commons.constants.MsTestElementConstants;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.*; import io.metersphere.commons.utils.*;
import io.metersphere.commons.vo.JDBCProcessorVO; import io.metersphere.commons.vo.JDBCProcessorVO;
import io.metersphere.environment.service.BaseEnvironmentService; import io.metersphere.environment.service.BaseEnvironmentService;
@ -76,9 +75,9 @@ public class MsJDBCSampler extends MsTestElement {
} }
hashTree = this.getHashTree(); hashTree = this.getHashTree();
} }
if (config != null && config.getConfig() == null) { if (MapUtils.isEmpty(config.getConfig()) && config.isApi()) {
// 单独接口执行 // 单独接口执行
this.setProjectId(config.getProjectId()); this.setProjectId(config.getCurrentProjectId());
config.setConfig(ElementUtil.getEnvironmentConfig(StringUtils.isNotEmpty(useEnvironment) ? useEnvironment : environmentId, this.getProjectId())); config.setConfig(ElementUtil.getEnvironmentConfig(StringUtils.isNotEmpty(useEnvironment) ? useEnvironment : environmentId, this.getProjectId()));
} }
@ -88,9 +87,9 @@ public class MsJDBCSampler extends MsTestElement {
this.dataSource = null; this.dataSource = null;
envConfig = this.initDataSource(); envConfig = this.initDataSource();
} else { } else {
if (config.isEffective(this.getProjectId()) && CollectionUtils.isNotEmpty(config.getConfig().get(this.getProjectId()).getDatabaseConfigs()) if (config.isEffective(this.getProjectId()) && CollectionUtils.isNotEmpty(config.get(this.getProjectId()).getDatabaseConfigs())
&& isDataSource(config.getConfig().get(this.getProjectId()).getDatabaseConfigs())) { && isDataSource(config.get(this.getProjectId()).getDatabaseConfigs())) {
EnvironmentConfig environmentConfig = config.getConfig().get(this.getProjectId()); EnvironmentConfig environmentConfig = config.get(this.getProjectId());
if (environmentConfig.getDatabaseConfigs() != null && StringUtils.isNotEmpty(environmentConfig.getEnvironmentId())) { if (environmentConfig.getDatabaseConfigs() != null && StringUtils.isNotEmpty(environmentConfig.getEnvironmentId())) {
this.environmentId = environmentConfig.getEnvironmentId(); this.environmentId = environmentConfig.getEnvironmentId();
} }
@ -102,8 +101,8 @@ public class MsJDBCSampler extends MsTestElement {
} else { } else {
// 取当前环境下默认的一个数据源 // 取当前环境下默认的一个数据源
if (config.isEffective(this.getProjectId())) { if (config.isEffective(this.getProjectId())) {
if (config.getConfig().get(this.getProjectId()) != null) { if (config.get(this.getProjectId()) != null) {
envConfig = config.getConfig().get(this.getProjectId()); envConfig = config.get(this.getProjectId());
if (CollectionUtils.isNotEmpty(envConfig.getDatabaseConfigs())) { if (CollectionUtils.isNotEmpty(envConfig.getDatabaseConfigs())) {
LoggerUtil.info(this.getName() + ":开始获取当前环境下默认数据源"); LoggerUtil.info(this.getName() + ":开始获取当前环境下默认数据源");
DatabaseConfig dataSourceOrg = ElementUtil.dataSource(getProjectId(), dataSourceId, envConfig); DatabaseConfig dataSourceOrg = ElementUtil.dataSource(getProjectId(), dataSourceId, envConfig);
@ -119,9 +118,7 @@ public class MsJDBCSampler extends MsTestElement {
} }
} }
if (this.dataSource == null) { if (this.dataSource == null) {
LoggerUtil.info(this.getName() + " 当前项目id", this.getProjectId() + " 当前环境配置信息", JSONUtil.toJSONString(config)); LoggerUtil.error(this.getName() + ",未找到数据源", JSONUtil.toJSONString(config));
String message = "数据源为空请选择数据源";
MSException.throwException(StringUtils.isNotEmpty(this.getName()) ? this.getName() + "" + message : message);
} }
JDBCSampler jdbcSampler = jdbcSampler(config); JDBCSampler jdbcSampler = jdbcSampler(config);
// 失败重试 // 失败重试

View File

@ -122,16 +122,16 @@ public class MsTCPSampler extends MsTestElement {
break; break;
} }
} }
if (config.getConfig() == null) { if (MapUtils.isEmpty(config.getConfig()) && config.isApi()) {
// 单独接口执行 // 单独接口执行
if (StringUtils.isNotEmpty(config.getProjectId())) { if (StringUtils.isNotEmpty(config.getCurrentProjectId())) {
this.setProjectId(config.getProjectId()); this.setProjectId(config.getCurrentProjectId());
} }
config.setConfig(ElementUtil.getEnvironmentConfig(StringUtils.isNotEmpty(this.getEnvironmentId()) ? this.getEnvironmentId() : useEnvironment, this.getProjectId())); config.setConfig(ElementUtil.getEnvironmentConfig(StringUtils.isNotEmpty(this.getEnvironmentId()) ? this.getEnvironmentId() : useEnvironment, this.getProjectId()));
} }
EnvironmentConfig envConfig = null; EnvironmentConfig envConfig = null;
if (config.getConfig() != null) { if (config.getConfig() != null) {
envConfig = config.getConfig().get(this.projectId); envConfig = config.get(this.projectId);
parseEnvironment(envConfig); parseEnvironment(envConfig);
} }
// 添加环境中的公共变量 // 添加环境中的公共变量
@ -303,17 +303,17 @@ public class MsTCPSampler extends MsTestElement {
List<StringProperty> threadValues = new ArrayList<>(); List<StringProperty> threadValues = new ArrayList<>();
if (CollectionUtils.isNotEmpty(this.parameters)) { if (CollectionUtils.isNotEmpty(this.parameters)) {
this.parameters.forEach(item -> { this.parameters.forEach(item -> {
names.add(new StringProperty(new Integer(new Random().nextInt(1000000)).toString(), item.getName())); names.add(new StringProperty(String.valueOf(new Random().nextInt(1000000)), item.getName()));
String value = item.getValue(); String value = item.getValue();
if (StringUtils.isNotEmpty(value)) { if (StringUtils.isNotEmpty(value)) {
value = this.formatMockValue(value); value = this.formatMockValue(value);
threadValues.add(new StringProperty(new Integer(new Random().nextInt(1000000)).toString(), value)); threadValues.add(new StringProperty(String.valueOf(new Random().nextInt(1000000)), value));
} }
}); });
} }
userParameters.setNames(new CollectionProperty(UserParameters.NAMES, names)); userParameters.setNames(new CollectionProperty(UserParameters.NAMES, names));
List<CollectionProperty> collectionPropertyList = new ArrayList<>(); List<CollectionProperty> collectionPropertyList = new ArrayList<>();
collectionPropertyList.add(new CollectionProperty(new Integer(new Random().nextInt(1000000)).toString(), threadValues)); collectionPropertyList.add(new CollectionProperty(String.valueOf(new Random().nextInt(1000000)), threadValues));
userParameters.setThreadLists(new CollectionProperty(UserParameters.THREAD_VALUES, collectionPropertyList)); userParameters.setThreadLists(new CollectionProperty(UserParameters.THREAD_VALUES, collectionPropertyList));
tree.add(userParameters); tree.add(userParameters);
} }

View File

@ -1,8 +1,12 @@
package io.metersphere.api.dto.scenario; package io.metersphere.api.dto.scenario;
import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor;
@Data @Data
@AllArgsConstructor
@NoArgsConstructor
public class DatabaseConfig { public class DatabaseConfig {
private String id; private String id;

View File

@ -75,8 +75,6 @@ public class ApiCaseExecuteService {
/** /**
* 测试计划case执行 * 测试计划case执行
* *
* @param request
* @return
*/ */
public List<MsExecResponseDTO> run(BatchRunDefinitionRequest request) { public List<MsExecResponseDTO> run(BatchRunDefinitionRequest request) {
List<MsExecResponseDTO> responseDTOS = new LinkedList<>(); List<MsExecResponseDTO> responseDTOS = new LinkedList<>();

View File

@ -136,7 +136,7 @@ public class ApiCaseSerialService {
parse(element, testId, envId, caseWithBLOBs.getProjectId()); parse(element, testId, envId, caseWithBLOBs.getProjectId());
group.getHashTree().add(JSONUtil.parseObject(element.toString(), MsTestElement.class)); group.getHashTree().add(JSONUtil.parseObject(element.toString(), MsTestElement.class));
testPlan.getHashTree().add(group); testPlan.getHashTree().add(group);
ParameterConfig config = new ParameterConfig(); ParameterConfig config = new ParameterConfig(projectId, true);
if (runRequest.isRetryEnable() && runRequest.getRetryNum() > 0) { if (runRequest.isRetryEnable() && runRequest.getRetryNum() > 0) {
config.setRetryNum(runRequest.getRetryNum()); config.setRetryNum(runRequest.getRetryNum());
} }

View File

@ -196,18 +196,17 @@ public class ApiExecuteService {
this.add(request.getProjectId()); this.add(request.getProjectId());
}}, new BooleanPool()).keySet().stream().toList()); }}, new BooleanPool()).keySet().stream().toList());
threadGroup.getHashTree().add(request); threadGroup.getHashTree().add(request);
ParameterConfig config = new ParameterConfig(); ParameterConfig config = new ParameterConfig(request.getProjectId(), true);
config.setProjectId(request.getProjectId()); config.setCurrentProjectId(request.getProjectId());
return testPlan.generateHashTree(config); return testPlan.generateHashTree(config);
} }
private JmeterRunRequestDTO initRunRequest(RunDefinitionRequest request, List<MultipartFile> bodyFiles) { private JmeterRunRequestDTO initRunRequest(RunDefinitionRequest request, List<MultipartFile> bodyFiles) {
ParameterConfig config = new ParameterConfig(); ParameterConfig config = new ParameterConfig(request.getProjectId(), true);
config.setProjectId(request.getProjectId());
config.setApi(true); config.setApi(true);
Map<String, EnvironmentConfig> envConfig = new HashMap<>(); Map<String, EnvironmentConfig> envConfig = new HashMap<>();
Map<String, String> map = request.getEnvironmentMap(); Map<String, String> map = request.getEnvironmentMap();
if (map != null && map.size() > 0) { if (MapUtils.isNotEmpty(map)) {
for (String key : map.keySet()) { for (String key : map.keySet()) {
ApiTestEnvironmentWithBLOBs environment = apiTestEnvironmentService.get(map.get(key)); ApiTestEnvironmentWithBLOBs environment = apiTestEnvironmentService.get(map.get(key));
if (environment != null) { if (environment != null) {
@ -317,8 +316,7 @@ public class ApiExecuteService {
BaseEnvironmentService apiTestEnvironmentService = CommonBeanFactory.getBean(BaseEnvironmentService.class); BaseEnvironmentService apiTestEnvironmentService = CommonBeanFactory.getBean(BaseEnvironmentService.class);
ApiTestEnvironmentWithBLOBs environment = apiTestEnvironmentService.get(request.getEnvironmentId()); ApiTestEnvironmentWithBLOBs environment = apiTestEnvironmentService.get(request.getEnvironmentId());
ParameterConfig parameterConfig = new ParameterConfig(); ParameterConfig parameterConfig = new ParameterConfig(projectId, true);
parameterConfig.setApi(true);
Map<String, EnvironmentConfig> envConfig = new HashMap<>(16); Map<String, EnvironmentConfig> envConfig = new HashMap<>(16);
if (environment != null && environment.getConfig() != null) { if (environment != null && environment.getConfig() != null) {
EnvironmentConfig environmentConfig = JSONUtil.parseObject(environment.getConfig(), EnvironmentConfig.class); EnvironmentConfig environmentConfig = JSONUtil.parseObject(environment.getConfig(), EnvironmentConfig.class);

View File

@ -3,7 +3,6 @@ package io.metersphere.api.exec.engine;
import io.fabric8.kubernetes.api.model.Pod; import io.fabric8.kubernetes.api.model.Pod;
import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.dsl.ExecListener; import io.fabric8.kubernetes.client.dsl.ExecListener;
import io.fabric8.kubernetes.client.dsl.ExecWatch;
import io.metersphere.commons.exception.MSException; import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.CommonBeanFactory; import io.metersphere.commons.utils.CommonBeanFactory;
import io.metersphere.commons.utils.JSON; import io.metersphere.commons.utils.JSON;
@ -32,9 +31,9 @@ public class KubernetesApiExec {
return nodePods.get(new Random().nextInt(nodePods.size())); return nodePods.get(new Random().nextInt(nodePods.size()));
} }
public static ExecWatch newExecWatch(KubernetesClient client, String namespace, String podName, String command, JmeterRunRequestDTO runRequest) { public static void newExecWatch(KubernetesClient client, String namespace, String podName, String command, JmeterRunRequestDTO runRequest) {
LoggerUtil.info("CURL 命令:【 " + command + ""); LoggerUtil.info("CURL 命令:【 " + command + "");
return client.pods().inNamespace(namespace).withName(podName) client.pods().inNamespace(namespace).withName(podName)
.readingInput(System.in) .readingInput(System.in)
.writingOutput(System.out) .writingOutput(System.out)
.writingError(System.err) .writingError(System.err)
@ -43,12 +42,7 @@ public class KubernetesApiExec {
.exec("sh", "-c", command); .exec("sh", "-c", command);
} }
private static class SimpleListener implements ExecListener { private record SimpleListener(JmeterRunRequestDTO runRequest) implements ExecListener {
private JmeterRunRequestDTO runRequest;
SimpleListener(JmeterRunRequestDTO runRequest) {
this.runRequest = runRequest;
}
@Override @Override
public void onOpen() { public void onOpen() {

View File

@ -1,21 +0,0 @@
package io.metersphere.api.exec.queue;
import io.metersphere.dto.JmeterRunRequestDTO;
import io.metersphere.utils.LoggerUtil;
public class ExecTask implements Runnable {
private JmeterRunRequestDTO request;
public ExecTask(JmeterRunRequestDTO request) {
this.request = request;
}
public JmeterRunRequestDTO getRequest() {
return this.request;
}
@Override
public void run() {
LoggerUtil.info("任务执行超时", request.getReportId());
}
}

View File

@ -1,183 +0,0 @@
package io.metersphere.api.exec.queue;
import io.metersphere.api.jmeter.ApiLocalRunner;
import io.metersphere.commons.utils.NamedThreadFactory;
import io.metersphere.dto.JmeterRunRequestDTO;
import io.metersphere.utils.LoggerUtil;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import java.util.Queue;
import java.util.concurrent.*;
@Service
public class ExecThreadPoolExecutor {
// 线程池维护线程的最少数量
private final static int CORE_POOL_SIZE = 10;
// 线程池维护线程的最大数量
private final static int MAX_POOL_SIZE = 10;
// 线程池维护线程所允许的空闲时间
private final static int KEEP_ALIVE_TIME = 1;
// 线程池所使用的缓冲队列大小
private final static int WORK_QUEUE_SIZE = 10000;
private MsRejectedExecutionHandler msRejectedExecutionHandler = new MsRejectedExecutionHandler();
/**
* 创建线程池
*/
private final ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
CORE_POOL_SIZE,
MAX_POOL_SIZE,
KEEP_ALIVE_TIME,
TimeUnit.SECONDS,
new ArrayBlockingQueue(WORK_QUEUE_SIZE),
new NamedThreadFactory("MS-JMETER-RUN-TASK"),
msRejectedExecutionHandler);
/**
* 缓冲区调度线程池
*/
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1, new NamedThreadFactory("MS-BUFFER-SCHEDULED"));
public void addTask(JmeterRunRequestDTO requestDTO) {
ExecTask task = new ExecTask(requestDTO);
threadPool.execute(task);
outApiThreadPoolExecutorLogger("报告:[" + requestDTO.getReportId() + "] 资源:[" + requestDTO.getTestId() + "] 加入执行队列");
}
/**
* 调度线程池检查缓冲区
*/
final ScheduledFuture scheduledFuture = scheduler.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
//判断缓冲队列是否存在记录
if (CollectionUtils.isNotEmpty(msRejectedExecutionHandler.getBufferQueue())) {
//当线程池的队列容量少于WORK_QUEUE_SIZE则开始把缓冲队列的任务 加入到 线程池
if (threadPool.getQueue().size() < WORK_QUEUE_SIZE) {
JmeterRunRequestDTO requestDTO = msRejectedExecutionHandler.getBufferQueue().poll();
ExecTask task = new ExecTask(requestDTO);
threadPool.submit(task);
LoggerUtil.info("把缓冲区任务重新添加到线程池报告ID" + requestDTO.getReportId());
}
}
}
}, 0, 2, TimeUnit.SECONDS);
/**
* 终止线程池和调度线程池
*/
public void shutdown() {
//true表示如果定时任务在执行立即中止false则等待任务结束后再停止
LoggerUtil.info("终止执行线程池和调度线程池:" + scheduledFuture.cancel(true));
scheduler.shutdown();
threadPool.shutdown();
}
/**
* 保留两位小数
*/
private String divide(int num1, int num2) {
return String.format("%1.2f%%", Double.parseDouble(num1 + StringUtils.EMPTY) / Double.parseDouble(num2 + StringUtils.EMPTY) * 100);
}
public void outApiThreadPoolExecutorLogger(String message) {
ArrayBlockingQueue queue = (ArrayBlockingQueue) threadPool.getQueue();
StringBuffer buffer = new StringBuffer(StringUtils.LF + message);
buffer.append(StringUtils.LF).append("线程池详情:").append(StringUtils.LF);
buffer.append(" 核心线程数:" + threadPool.getCorePoolSize()).append(StringUtils.LF);
buffer.append(" 活动线程数:" + threadPool.getActiveCount()).append(" (略有波动非精确数据)").append(StringUtils.LF);
buffer.append(" 最大线程数:" + threadPool.getMaximumPoolSize()).append(StringUtils.LF);
buffer.append(" 线程池活跃度:" + divide(threadPool.getActiveCount(), threadPool.getMaximumPoolSize())).append(StringUtils.LF);
buffer.append(" 最大队列数:" + (queue.size() + queue.remainingCapacity())).append(StringUtils.LF);
buffer.append(" 当前排队线程数:" + (msRejectedExecutionHandler.getBufferQueue().size() + queue.size())).append(StringUtils.LF);
buffer.append(" 执行中队列大小:" + PoolExecBlockingQueueUtil.queue.size()).append(StringUtils.LF);
buffer.append(" 队列使用度:" + divide(queue.size(), queue.size() + queue.remainingCapacity()));
LoggerUtil.info(buffer.toString());
if (queue.size() > 0 && LoggerUtil.getLogger().isDebugEnabled()) {
LoggerUtil.debug(this.getWorkerQueue());
}
}
public void setCorePoolSize(int maximumPoolSize) {
try {
if (maximumPoolSize != threadPool.getMaximumPoolSize()) {
threadPool.setMaximumPoolSize(maximumPoolSize);
int corePoolSize = maximumPoolSize > 500 ? 500 : maximumPoolSize;
if (corePoolSize > CORE_POOL_SIZE) {
threadPool.setCorePoolSize(corePoolSize);
}
threadPool.allowCoreThreadTimeOut(true);
LoggerUtil.info("AllCoreThreads: " + threadPool.prestartAllCoreThreads());
}
} catch (Exception e) {
LoggerUtil.error("设置线程参数异常:", e);
}
}
public void removeQueue(String reportId) {
ApiLocalRunner.stop(reportId);
// 检查缓冲区
Queue<JmeterRunRequestDTO> bufferQueue = msRejectedExecutionHandler.getBufferQueue();
if (CollectionUtils.isNotEmpty(bufferQueue)) {
bufferQueue.forEach(item -> {
if (item != null && StringUtils.equals(item.getReportId(), reportId)) {
bufferQueue.remove(item);
}
});
}
// 检查等待队列
BlockingQueue workerQueue = threadPool.getQueue();
workerQueue.forEach(item -> {
ExecTask task = (ExecTask) item;
if (task != null && task.getRequest() != null && StringUtils.equals(task.getRequest().getReportId(), reportId)) {
workerQueue.remove(item);
}
});
}
public void removeAllQueue() {
// 检查缓冲区
msRejectedExecutionHandler.getBufferQueue().clear();
// 检查等待队列
threadPool.getQueue().clear();
}
public boolean check(String reportId) {
// 检查缓冲区
Queue<JmeterRunRequestDTO> bufferQueue = msRejectedExecutionHandler.getBufferQueue();
if (CollectionUtils.isNotEmpty(bufferQueue)) {
return bufferQueue.stream().filter(task -> StringUtils.equals(task.getReportId(), reportId)).count() > 0;
}
// 检查等待队列
BlockingQueue workerQueue = threadPool.getQueue();
return workerQueue.stream().filter(task -> StringUtils.equals(((ExecTask) task).getRequest().getReportId(), reportId)).count() > 0;
}
public boolean checkPlanReport(String planReportId) {
// 检查缓冲区
Queue<JmeterRunRequestDTO> bufferQueue = msRejectedExecutionHandler.getBufferQueue();
if (CollectionUtils.isNotEmpty(bufferQueue)) {
return bufferQueue.stream().filter(task -> StringUtils.equals(task.getTestPlanReportId(), planReportId)).count() > 0;
}
// 检查等待队列
BlockingQueue workerQueue = threadPool.getQueue();
return workerQueue.stream().filter(task -> StringUtils.equals(((ExecTask) task).getRequest().getTestPlanReportId(), planReportId)).count() > 0;
}
public String getWorkerQueue() {
StringBuffer buffer = new StringBuffer();
BlockingQueue workerQueue = threadPool.getQueue();
workerQueue.forEach(item -> {
ExecTask task = (ExecTask) item;
if (task.getRequest() != null) {
buffer.append("等待队列报告:【 " + task.getRequest().getReportId() + "】资源:【 " + task.getRequest().getTestId() + "").append(StringUtils.LF);
}
});
return buffer.toString();
}
}

View File

@ -1,28 +0,0 @@
package io.metersphere.api.exec.queue;
import io.metersphere.dto.JmeterRunRequestDTO;
import io.metersphere.utils.LoggerUtil;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
public class MsRejectedExecutionHandler implements RejectedExecutionHandler {
/**
* 执行任务缓冲队列,当线程池满了则将任务存入到此缓冲队列
* 这里是否搞个redis/写到磁盘
*/
private Queue<JmeterRunRequestDTO> bufferQueue = new LinkedBlockingQueue<>();
public Queue<JmeterRunRequestDTO> getBufferQueue() {
return bufferQueue;
}
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
//任务加入到缓冲队列
bufferQueue.offer(((ExecTask) r).getRequest());
LoggerUtil.info("执行任务过多,任务加入缓冲区:" + ((ExecTask) r).getRequest().getReportId());
}
}

View File

@ -1,50 +0,0 @@
package io.metersphere.api.exec.queue;
import io.metersphere.commons.utils.LogUtil;
import org.apache.commons.lang3.StringUtils;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
public class PoolExecBlockingQueueUtil {
// 系统级队列控制整体并发数量
public static Map<String, BlockingQueue<Object>> queue = new ConcurrentHashMap<>();
private static final String END_SIGN = "RUN-END";
private static final int QUEUE_SIZE = 1;
public static void offer(String key) {
if (StringUtils.isNotEmpty(key) && queue.containsKey(key)) {
try {
queue.get(key).offer(END_SIGN);
} catch (Exception e) {
LogUtil.error(e);
} finally {
queue.remove(key);
}
}
}
public static Object take(String key) {
try {
if (StringUtils.isNotEmpty(key) && !queue.containsKey(key)) {
BlockingQueue<Object> blockingQueue = new ArrayBlockingQueue<>(QUEUE_SIZE);
queue.put(key, blockingQueue);
return blockingQueue.poll(10, TimeUnit.MINUTES);
}
} catch (Exception e) {
LogUtil.error("初始化队列失败:" + e.getMessage());
}
return null;
}
public static void remove(String key) {
if (StringUtils.isNotEmpty(key) && queue.containsKey(key)) {
queue.get(key).offer(END_SIGN);
queue.remove(key);
}
}
}

View File

@ -88,7 +88,7 @@ public class ApiEnvironmentRunningParamService {
itemObj.put(NAME, entry.getKey()); itemObj.put(NAME, entry.getKey());
itemObj.put(VALUE, entry.getValue()); itemObj.put(VALUE, entry.getValue());
itemObj.put(ENABLE, true); itemObj.put(ENABLE, true);
if (variables.length() > 0 && StringUtils.isEmpty(variables.optJSONObject(variables.length() - 1).optString(NAME))) { if (!variables.isEmpty() && StringUtils.isEmpty(variables.optJSONObject(variables.length() - 1).optString(NAME))) {
variables.put(variables.length() - 1, itemObj); variables.put(variables.length() - 1, itemObj);
} else { } else {
variables.put(itemObj); variables.put(itemObj);

View File

@ -1,8 +1,8 @@
package io.metersphere.api.exec.scenario; package io.metersphere.api.exec.scenario;
import io.metersphere.api.dto.EnvironmentCheckDTO;
import io.metersphere.api.dto.EnvironmentType; import io.metersphere.api.dto.EnvironmentType;
import io.metersphere.api.dto.RunModeConfigWithEnvironmentDTO; import io.metersphere.api.dto.RunModeConfigWithEnvironmentDTO;
import io.metersphere.api.dto.ScenarioEnv;
import io.metersphere.api.dto.automation.ApiScenarioDTO; import io.metersphere.api.dto.automation.ApiScenarioDTO;
import io.metersphere.api.dto.automation.RunScenarioRequest; import io.metersphere.api.dto.automation.RunScenarioRequest;
import io.metersphere.api.dto.definition.request.ElementUtil; import io.metersphere.api.dto.definition.request.ElementUtil;
@ -35,6 +35,7 @@ import io.metersphere.service.definition.ApiDefinitionService;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils; import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.json.JSONObject; import org.json.JSONObject;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -61,36 +62,32 @@ public class ApiScenarioEnvService {
@Resource @Resource
private BaseEnvironmentService apiTestEnvironmentService; private BaseEnvironmentService apiTestEnvironmentService;
public ScenarioEnv getApiScenarioEnv(String definition) { public EnvironmentCheckDTO getApiScenarioEnv(String definition) {
ScenarioEnv env = new ScenarioEnv(); EnvironmentCheckDTO env = new EnvironmentCheckDTO();
if (StringUtils.isEmpty(definition)) { if (StringUtils.isEmpty(definition)) {
return env; return env;
} }
List<MsTestElement> hashTree = GenerateHashTreeUtil.getScenarioHashTree(definition); List<MsTestElement> hashTree = GenerateHashTreeUtil.getScenarioHashTree(definition);
if (CollectionUtils.isNotEmpty(hashTree)) { List<Boolean> fullUrls = new ArrayList<>();
// 过滤掉禁用的步骤 getHashTree(hashTree, env, fullUrls);
hashTree = hashTree.stream().filter(item -> item.isEnable()).collect(Collectors.toList()); env.setFullUrl(!fullUrls.contains(false));
}
List<Boolean> hasFullUrlList = new ArrayList<>();
for (MsTestElement testElement : hashTree) {
this.formatElement(testElement, env, hasFullUrlList);
if (CollectionUtils.isNotEmpty(testElement.getHashTree())) {
getHashTree(testElement.getHashTree(), env, hasFullUrlList);
}
}
env.setFullUrl(!hasFullUrlList.contains(false));
return env; return env;
} }
private void getHashTree(List<MsTestElement> tree, ScenarioEnv env, List<Boolean> hasFullUrlList) { private void getHashTree(List<MsTestElement> tree, EnvironmentCheckDTO env, List<Boolean> fullUrls) {
try { try {
// 过滤掉禁用的步骤 // 过滤掉禁用的步骤
tree = tree.stream().filter(item -> item.isEnable()).collect(Collectors.toList()); tree = tree.stream().filter(MsTestElement::isEnable).collect(Collectors.toList());
for (MsTestElement element : tree) { for (MsTestElement element : tree) {
this.formatElement(element, env, hasFullUrlList); if (element instanceof MsScenario
&& BooleanUtils.isTrue(((MsScenario) element).isEnvironmentEnable())) {
fullUrls.add(true);
continue;
}
this.formatElement(element, env, fullUrls);
if (CollectionUtils.isNotEmpty(element.getHashTree())) { if (CollectionUtils.isNotEmpty(element.getHashTree())) {
getHashTree(element.getHashTree(), env, hasFullUrlList); getHashTree(element.getHashTree(), env, fullUrls);
} }
} }
} catch (Exception e) { } catch (Exception e) {
@ -98,7 +95,7 @@ public class ApiScenarioEnvService {
} }
} }
private void formatElement(MsTestElement testElement, ScenarioEnv env, List<Boolean> hasFullUrlList) { private void formatElement(MsTestElement testElement, EnvironmentCheckDTO env, List<Boolean> fullUrls) {
if (StringUtils.equals(MsTestElementConstants.REF.name(), testElement.getReferenced())) { if (StringUtils.equals(MsTestElementConstants.REF.name(), testElement.getReferenced())) {
if (StringUtils.equals(testElement.getType(), ElementConstants.HTTP_SAMPLER)) { if (StringUtils.equals(testElement.getType(), ElementConstants.HTTP_SAMPLER)) {
MsHTTPSamplerProxy http = (MsHTTPSamplerProxy) testElement; MsHTTPSamplerProxy http = (MsHTTPSamplerProxy) testElement;
@ -109,7 +106,7 @@ public class ApiScenarioEnvService {
if (!StringUtils.equalsIgnoreCase(http.getReferenced(), ElementConstants.STEP_CREATED) if (!StringUtils.equalsIgnoreCase(http.getReferenced(), ElementConstants.STEP_CREATED)
|| (http.getIsRefEnvironment() != null && http.getIsRefEnvironment())) { || (http.getIsRefEnvironment() != null && http.getIsRefEnvironment())) {
env.getProjectIds().add(http.getProjectId()); env.getProjectIds().add(http.getProjectId());
hasFullUrlList.add(false); fullUrls.add(false);
} }
} else if (StringUtils.equals(testElement.getType(), ElementConstants.JDBC_SAMPLER) } else if (StringUtils.equals(testElement.getType(), ElementConstants.JDBC_SAMPLER)
|| StringUtils.equals(testElement.getType(), ElementConstants.TCP_SAMPLER)) { || StringUtils.equals(testElement.getType(), ElementConstants.TCP_SAMPLER)) {
@ -118,18 +115,18 @@ public class ApiScenarioEnvService {
ApiTestCase testCase = apiTestCaseMapper.selectByPrimaryKey(testElement.getId()); ApiTestCase testCase = apiTestCaseMapper.selectByPrimaryKey(testElement.getId());
if (testCase != null) { if (testCase != null) {
env.getProjectIds().add(testCase.getProjectId()); env.getProjectIds().add(testCase.getProjectId());
hasFullUrlList.add(false); fullUrls.add(false);
} }
} else { } else {
ApiDefinition apiDefinition = apiDefinitionService.get(testElement.getId()); ApiDefinition apiDefinition = apiDefinitionService.get(testElement.getId());
if (apiDefinition != null) { if (apiDefinition != null) {
env.getProjectIds().add(apiDefinition.getProjectId()); env.getProjectIds().add(apiDefinition.getProjectId());
hasFullUrlList.add(false); fullUrls.add(false);
} }
} }
} else { } else {
env.getProjectIds().add(testElement.getProjectId()); env.getProjectIds().add(testElement.getProjectId());
hasFullUrlList.add(false); fullUrls.add(false);
} }
} else if (StringUtils.equals(testElement.getType(), ElementConstants.SCENARIO) && StringUtils.isEmpty(testElement.getProjectId())) { } else if (StringUtils.equals(testElement.getType(), ElementConstants.SCENARIO) && StringUtils.isEmpty(testElement.getProjectId())) {
ApiScenarioWithBLOBs apiScenario = apiScenarioMapper.selectByPrimaryKey(testElement.getId()); ApiScenarioWithBLOBs apiScenario = apiScenarioMapper.selectByPrimaryKey(testElement.getId());
@ -145,14 +142,14 @@ public class ApiScenarioEnvService {
if (StringUtils.equals(testElement.getType(), ElementConstants.HTTP_SAMPLER)) { if (StringUtils.equals(testElement.getType(), ElementConstants.HTTP_SAMPLER)) {
// 校验是否是全路径 // 校验是否是全路径
MsHTTPSamplerProxy httpSamplerProxy = (MsHTTPSamplerProxy) testElement; MsHTTPSamplerProxy httpSamplerProxy = (MsHTTPSamplerProxy) testElement;
checkCustomEnv(env, httpSamplerProxy.isCustomizeReq(), httpSamplerProxy.getProjectId(), httpSamplerProxy.getIsRefEnvironment(), httpSamplerProxy.getReferenced(), hasFullUrlList); checkCustomEnv(env, httpSamplerProxy.isCustomizeReq(), httpSamplerProxy.getProjectId(), httpSamplerProxy.getIsRefEnvironment(), httpSamplerProxy.getReferenced(), fullUrls);
} else if (StringUtils.equals(testElement.getType(), ElementConstants.TCP_SAMPLER)) { } else if (StringUtils.equals(testElement.getType(), ElementConstants.TCP_SAMPLER)) {
MsTCPSampler tcpSampler = (MsTCPSampler) testElement; MsTCPSampler tcpSampler = (MsTCPSampler) testElement;
checkCustomEnv(env, tcpSampler.isCustomizeReq(), tcpSampler.getProjectId(), tcpSampler.getIsRefEnvironment(), tcpSampler.getReferenced(), hasFullUrlList); checkCustomEnv(env, tcpSampler.isCustomizeReq(), tcpSampler.getProjectId(), tcpSampler.getIsRefEnvironment(), tcpSampler.getReferenced(), fullUrls);
} else if (StringUtils.equals(testElement.getType(), ElementConstants.JDBC_SAMPLER)) { } else if (StringUtils.equals(testElement.getType(), ElementConstants.JDBC_SAMPLER)) {
MsJDBCSampler jdbcSampler = (MsJDBCSampler) testElement; MsJDBCSampler jdbcSampler = (MsJDBCSampler) testElement;
checkCustomEnv(env, jdbcSampler.isCustomizeReq(), jdbcSampler.getProjectId(), jdbcSampler.getIsRefEnvironment(), jdbcSampler.getReferenced(), hasFullUrlList); checkCustomEnv(env, jdbcSampler.isCustomizeReq(), jdbcSampler.getProjectId(), jdbcSampler.getIsRefEnvironment(), jdbcSampler.getReferenced(), fullUrls);
} }
} }
if (StringUtils.equals(testElement.getType(), ElementConstants.SCENARIO) if (StringUtils.equals(testElement.getType(), ElementConstants.SCENARIO)
@ -161,10 +158,10 @@ public class ApiScenarioEnvService {
} }
} }
private void checkCustomEnv(ScenarioEnv env, boolean customizeReq, String projectId, Boolean isRefEnvironment, String referenced, List<Boolean> hasFullUrlList) { private void checkCustomEnv(EnvironmentCheckDTO env, boolean customizeReq, String projectId, Boolean isRefEnvironment, String referenced, List<Boolean> hasFullUrlList) {
if (customizeReq) { if (customizeReq) {
env.getProjectIds().add(projectId); env.getProjectIds().add(projectId);
hasFullUrlList.add(isRefEnvironment == null ? true : !isRefEnvironment); hasFullUrlList.add(isRefEnvironment == null || !isRefEnvironment);
} else if (!StringUtils.equalsIgnoreCase(referenced, ElementConstants.STEP_CREATED) } else if (!StringUtils.equalsIgnoreCase(referenced, ElementConstants.STEP_CREATED)
|| (isRefEnvironment != null && isRefEnvironment)) { || (isRefEnvironment != null && isRefEnvironment)) {
env.getProjectIds().add(projectId); env.getProjectIds().add(projectId);
@ -174,8 +171,6 @@ public class ApiScenarioEnvService {
/** /**
* 设置场景的运行环境 环境组/环境JSON * 设置场景的运行环境 环境组/环境JSON
*
* @param apiScenarioWithBLOBs
*/ */
public void setScenarioEnv(ApiScenarioWithBLOBs apiScenarioWithBLOBs, RunScenarioRequest request) { public void setScenarioEnv(ApiScenarioWithBLOBs apiScenarioWithBLOBs, RunScenarioRequest request) {
if (apiScenarioWithBLOBs == null) { if (apiScenarioWithBLOBs == null) {
@ -210,60 +205,22 @@ public class ApiScenarioEnvService {
String definition = apiScenarioWithBLOBs.getScenarioDefinition(); String definition = apiScenarioWithBLOBs.getScenarioDefinition();
MsScenarioEnv scenario = JSON.parseObject(definition, MsScenarioEnv.class); MsScenarioEnv scenario = JSON.parseObject(definition, MsScenarioEnv.class);
Map<String, String> envMap = scenario.getEnvironmentMap(); Map<String, String> envMap = scenario.getEnvironmentMap();
return this.check(definition, envMap, scenario.getEnvironmentId(), apiScenarioWithBLOBs.getProjectId()); return this.check(definition, envMap, apiScenarioWithBLOBs.getProjectId());
} }
return true; return true;
} }
private boolean check(String definition, Map<String, String> envMap, String envId, String projectId) { private boolean check(String definition, Map<String, String> envMap, String projectId) {
boolean isEnv = true; EnvironmentCheckDTO apiScenarioEnv = getApiScenarioEnv(definition);
ScenarioEnv apiScenarioEnv = getApiScenarioEnv(definition); // 当前项目是否选择了环境
// 所有请求非全路径检查环境 if (MapUtils.isNotEmpty(envMap) && envMap.containsKey(projectId)) {
return true;
}
// 所有请求是否都是自定义请求且没有引用环境
if (!apiScenarioEnv.getFullUrl()) { if (!apiScenarioEnv.getFullUrl()) {
try { return false;
if (envMap == null || envMap.isEmpty()) {
isEnv = false;
} else {
Set<String> projectIds = apiScenarioEnv.getProjectIds();
projectIds.remove(null);
if (CollectionUtils.isNotEmpty(envMap.keySet())) {
for (String id : projectIds) {
Project project = projectMapper.selectByPrimaryKey(id);
String s = envMap.get(id);
if (project == null) {
s = envMap.get(projectId);
} }
if (StringUtils.isBlank(s)) { return true;
isEnv = false;
break;
} else {
ApiTestEnvironmentWithBLOBs env = apiTestEnvironmentMapper.selectByPrimaryKey(s);
if (env == null) {
isEnv = false;
break;
}
}
}
} else {
isEnv = false;
}
}
} catch (Exception e) {
isEnv = false;
LogUtil.error(e.getMessage(), e);
}
}
// 1.8 之前环境是 environmentId
if (!isEnv) {
if (StringUtils.isNotBlank(envId)) {
ApiTestEnvironmentWithBLOBs env = apiTestEnvironmentMapper.selectByPrimaryKey(envId);
if (env != null) {
isEnv = true;
}
}
}
return isEnv;
} }
public boolean verifyPlanScenarioEnv(ApiScenarioWithBLOBs apiScenarioWithBLOBs, TestPlanApiScenarioInfoDTO testPlanApiScenarios) { public boolean verifyPlanScenarioEnv(ApiScenarioWithBLOBs apiScenarioWithBLOBs, TestPlanApiScenarioInfoDTO testPlanApiScenarios) {
@ -283,20 +240,15 @@ public class ApiScenarioEnvService {
envMap = new HashMap<>(); envMap = new HashMap<>();
} }
} }
return this.check(definition, envMap, scenario.getEnvironmentId(), apiScenarioWithBLOBs.getProjectId()); return this.check(definition, envMap, apiScenarioWithBLOBs.getProjectId());
} }
return true; return true;
} }
/** /**
* 检查是否存在运行环境若不存在则报错存在的话返回所存在的运行环境 * 检查是否存在运行环境若不存在则报错存在的话返回所存在的运行环境
*
* @param request
* @param apiScenarios
* @return <projectId,envIds>
*/ */
public Map<String, List<String>> checkEnv(RunScenarioRequest request, List<ApiScenarioWithBLOBs> apiScenarios) { public void checkEnv(RunScenarioRequest request, List<ApiScenarioWithBLOBs> apiScenarios) {
Map<String, List<String>> projectEnvMap = new HashMap<>();
if (StringUtils.equals(request.getRequestOriginator(), CommonConstants.TEST_PLAN)) { if (StringUtils.equals(request.getRequestOriginator(), CommonConstants.TEST_PLAN)) {
this.checkPlanScenarioEnv(request); this.checkPlanScenarioEnv(request);
} else if (StringUtils.isNotBlank(request.getRunMode()) && StringUtils.equalsAny(request.getRunMode(), ApiRunMode.SCENARIO.name(), ApiRunMode.SCENARIO_PLAN.name(), ApiRunMode.JENKINS_SCENARIO_PLAN.name())) { } else if (StringUtils.isNotBlank(request.getRunMode()) && StringUtils.equalsAny(request.getRunMode(), ApiRunMode.SCENARIO.name(), ApiRunMode.SCENARIO_PLAN.name(), ApiRunMode.JENKINS_SCENARIO_PLAN.name())) {
@ -312,7 +264,7 @@ public class ApiScenarioEnvService {
MSException.throwException("场景:" + apiScenarioWithBLOBs.getName() + ",步骤解析错误,检查是否包含插件步骤!"); MSException.throwException("场景:" + apiScenarioWithBLOBs.getName() + ",步骤解析错误,检查是否包含插件步骤!");
} }
} }
if (builder.length() > 0) { if (!builder.isEmpty()) {
MSException.throwException("场景:" + builder + "运行环境未配置,请检查!"); MSException.throwException("场景:" + builder + "运行环境未配置,请检查!");
} }
} else if (StringUtils.equals(request.getRunMode(), ApiRunMode.SCHEDULE_SCENARIO.name())) { } else if (StringUtils.equals(request.getRunMode(), ApiRunMode.SCHEDULE_SCENARIO.name())) {
@ -324,10 +276,11 @@ public class ApiScenarioEnvService {
} }
} }
} }
return projectEnvMap;
} }
//检查测试计划场景的环境没有环境的场景从执行队列中移除 /**
* 检查测试计划场景的环境没有环境的场景从执行队列中移除
*/
public void checkPlanScenarioEnv(RunScenarioRequest request) { public void checkPlanScenarioEnv(RunScenarioRequest request) {
if (request.getProcessVO() != null && if (request.getProcessVO() != null &&
MapUtils.isNotEmpty(request.getProcessVO().getTestPlanScenarioMap()) MapUtils.isNotEmpty(request.getProcessVO().getTestPlanScenarioMap())
@ -354,12 +307,12 @@ public class ApiScenarioEnvService {
public Map<String, List<String>> selectApiScenarioEnv(List<? extends ApiScenarioWithBLOBs> list) { public Map<String, List<String>> selectApiScenarioEnv(List<? extends ApiScenarioWithBLOBs> list) {
Map<String, List<String>> projectEnvMap = new LinkedHashMap<>(); Map<String, List<String>> projectEnvMap = new LinkedHashMap<>();
for (int i = 0; i < list.size(); i++) { for (ApiScenarioWithBLOBs apiScenarioWithBLOBs : list) {
try { try {
Map<String, String> map = new HashMap<>(); Map<String, String> map = new HashMap<>();
String environmentType = list.get(i).getEnvironmentType(); String environmentType = apiScenarioWithBLOBs.getEnvironmentType();
String environmentGroupId = list.get(i).getEnvironmentGroupId(); String environmentGroupId = apiScenarioWithBLOBs.getEnvironmentGroupId();
String env = list.get(i).getEnvironmentJson(); String env = apiScenarioWithBLOBs.getEnvironmentJson();
if (StringUtils.equals(environmentType, EnvironmentType.JSON.name())) { if (StringUtils.equals(environmentType, EnvironmentType.JSON.name())) {
// 环境属性为空 跳过 // 环境属性为空 跳过
if (StringUtils.isBlank(env)) { if (StringUtils.isBlank(env)) {
@ -394,7 +347,7 @@ public class ApiScenarioEnvService {
} }
} }
} catch (Exception e) { } catch (Exception e) {
LogUtil.error("api scenario environment map incorrect parsing. api scenario id:" + list.get(i).getId()); LogUtil.error("api scenario environment map incorrect parsing. api scenario id:" + apiScenarioWithBLOBs.getId());
} }
} }
return projectEnvMap; return projectEnvMap;
@ -594,9 +547,9 @@ public class ApiScenarioEnvService {
private boolean isProjectEnvMapNotContainsEnv(LinkedHashMap<String, List<String>> returnMap, String projectName, String envName) { private boolean isProjectEnvMapNotContainsEnv(LinkedHashMap<String, List<String>> returnMap, String projectName, String envName) {
if (MapUtils.isNotEmpty(returnMap)) { if (MapUtils.isNotEmpty(returnMap)) {
if (returnMap.containsKey(projectName) && CollectionUtils.isNotEmpty(returnMap.get(projectName)) && returnMap.get(projectName).contains(envName)) { return !returnMap.containsKey(projectName)
return false; || !CollectionUtils.isNotEmpty(returnMap.get(projectName))
} || !returnMap.get(projectName).contains(envName);
} }
return true; return true;
} }

View File

@ -122,9 +122,6 @@ public class ApiScenarioExecuteService {
} }
// 检查执行内容合规性 // 检查执行内容合规性
PerformInspectionUtil.scenarioInspection(apiScenarios); PerformInspectionUtil.scenarioInspection(apiScenarios);
// 环境检查
LoggerUtil.info("Scenario run-执行脚本装载-开始针对所有执行场景进行环境检查");
apiScenarioEnvService.checkEnv(request, apiScenarios);
// 集合报告设置 // 集合报告设置
if (!request.isRerun() && GenerateHashTreeUtil.isSetReport(request.getConfig())) { if (!request.isRerun() && GenerateHashTreeUtil.isSetReport(request.getConfig())) {
if (isSerial(request)) { if (isSerial(request)) {
@ -412,7 +409,7 @@ public class ApiScenarioExecuteService {
if (StringUtils.equals(request.getEnvironmentType(), EnvironmentType.GROUP.toString())) { if (StringUtils.equals(request.getEnvironmentType(), EnvironmentType.GROUP.toString())) {
request.setEnvironmentMap(environmentGroupProjectService.getEnvMap(request.getEnvironmentGroupId())); request.setEnvironmentMap(environmentGroupProjectService.getEnvMap(request.getEnvironmentGroupId()));
} }
ParameterConfig config = new ParameterConfig(); ParameterConfig config = new ParameterConfig(request.getProjectId(), false);
config.setScenarioId(request.getScenarioId()); config.setScenarioId(request.getScenarioId());
if (MapUtils.isNotEmpty(request.getEnvironmentMap())) { if (MapUtils.isNotEmpty(request.getEnvironmentMap())) {
apiScenarioEnvService.setEnvConfig(request.getEnvironmentMap(), config); apiScenarioEnvService.setEnvConfig(request.getEnvironmentMap(), config);

View File

@ -15,7 +15,7 @@ public class JMeterLoggerAppender extends UnsynchronizedAppenderBase<ILoggingEve
public void append(ILoggingEvent event) { public void append(ILoggingEvent event) {
try { try {
if (!event.getLevel().levelStr.equals(LogUtil.DEBUG) && StringUtils.isNotEmpty(event.getThreadName())) { if (!event.getLevel().levelStr.equals(LogUtil.DEBUG) && StringUtils.isNotEmpty(event.getThreadName())) {
StringBuffer message = new StringBuffer(); StringBuilder message = new StringBuilder();
String threadName = StringUtils.substringBeforeLast(event.getThreadName(), THREAD_SPLIT); String threadName = StringUtils.substringBeforeLast(event.getThreadName(), THREAD_SPLIT);
message.append(DateUtils.getTimeStr(event.getTimeStamp())).append(StringUtils.SPACE) message.append(DateUtils.getTimeStr(event.getTimeStamp())).append(StringUtils.SPACE)
.append(event.getLevel()).append(StringUtils.SPACE) .append(event.getLevel()).append(StringUtils.SPACE)
@ -31,8 +31,7 @@ public class JMeterLoggerAppender extends UnsynchronizedAppenderBase<ILoggingEve
} }
} }
} }
if (message != null && !message.toString().contains("java.net.UnknownHostException") if (!message.toString().contains("java.net.UnknownHostException") && FixedCapacityUtil.containsKey(threadName)) {
&& FixedCapacityUtil.containsKey(threadName)) {
FixedCapacityUtil.get(threadName).append(message); FixedCapacityUtil.get(threadName).append(message);
} }
} }

View File

@ -4,7 +4,6 @@ package io.metersphere.api.jmeter;
import io.metersphere.api.dto.definition.request.ElementUtil; import io.metersphere.api.dto.definition.request.ElementUtil;
import io.metersphere.api.dto.definition.request.MsTestPlan; import io.metersphere.api.dto.definition.request.MsTestPlan;
import io.metersphere.api.exec.engine.EngineFactory; import io.metersphere.api.exec.engine.EngineFactory;
import io.metersphere.api.exec.queue.ExecThreadPoolExecutor;
import io.metersphere.api.jmeter.utils.JmxFileUtil; import io.metersphere.api.jmeter.utils.JmxFileUtil;
import io.metersphere.api.jmeter.utils.ServerConfig; import io.metersphere.api.jmeter.utils.ServerConfig;
import io.metersphere.api.jmeter.utils.SmoothWeighted; import io.metersphere.api.jmeter.utils.SmoothWeighted;
@ -28,6 +27,7 @@ import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils; import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.util.JMeterUtils; import org.apache.jmeter.util.JMeterUtils;
import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.context.i18n.LocaleContextHolder;
@ -53,8 +53,6 @@ public class JMeterService {
@Resource @Resource
private RemakeReportService remakeReportService; private RemakeReportService remakeReportService;
@Resource @Resource
private ExecThreadPoolExecutor execThreadPoolExecutor;
@Resource
private ApiPoolDebugService apiPoolDebugService; private ApiPoolDebugService apiPoolDebugService;
@Resource @Resource
private PluginService pluginService; private PluginService pluginService;
@ -163,7 +161,7 @@ public class JMeterService {
request.setEnable(config.isEnable()); request.setEnable(config.isEnable());
LoggerUtil.info("开始发送请求【 " + request.getTestId() + " 】到 " + config.getUrl() + " 节点执行", request.getReportId()); LoggerUtil.info("开始发送请求【 " + request.getTestId() + " 】到 " + config.getUrl() + " 节点执行", request.getReportId());
ResponseEntity<String> result = restTemplate.postForEntity(config.getUrl(), request, String.class); ResponseEntity<String> result = restTemplate.postForEntity(config.getUrl(), request, String.class);
if (result == null || !StringUtils.equals("SUCCESS", result.getBody())) { if (!StringUtils.equals("SUCCESS", result.getBody())) {
LoggerUtil.error("发送请求[ " + request.getTestId() + " ] 到" + config.getUrl() + " 节点执行失败", request.getReportId()); LoggerUtil.error("发送请求[ " + request.getTestId() + " ] 到" + config.getUrl() + " 节点执行失败", request.getReportId());
} }
} catch (Exception e) { } catch (Exception e) {
@ -182,7 +180,6 @@ public class JMeterService {
ElementUtil.coverArguments(request.getHashTree()); ElementUtil.coverArguments(request.getHashTree());
//解析hashTree是否含有文件库文件 //解析hashTree是否含有文件库文件
HashTreeUtil.initRepositoryFiles(request); HashTreeUtil.initRepositoryFiles(request);
execThreadPoolExecutor.addTask(request);
} }
} }
@ -200,7 +197,7 @@ public class JMeterService {
Integer port = node.getPort(); Integer port = node.getPort();
String uri = String.format(BASE_URL + "/jmeter/get/running/queue/" + reportId, nodeIp, port); String uri = String.format(BASE_URL + "/jmeter/get/running/queue/" + reportId, nodeIp, port);
ResponseEntity<Boolean> result = restTemplate.getForEntity(uri, Boolean.class); ResponseEntity<Boolean> result = restTemplate.getForEntity(uri, Boolean.class);
if (result != null && result.getBody()) { if (BooleanUtils.isTrue(result.getBody())) {
isRunning = true; isRunning = true;
break; break;
} }

View File

@ -1,52 +0,0 @@
package io.metersphere.api.jmeter;
import io.metersphere.api.exec.queue.ExecThreadPoolExecutor;
import io.metersphere.api.exec.queue.PoolExecBlockingQueueUtil;
import io.metersphere.commons.utils.CommonBeanFactory;
import io.metersphere.commons.utils.LogUtil;
import org.apache.commons.lang3.StringUtils;
public class JMeterThreadUtils {
public static String stop(String name) {
ThreadGroup currentGroup = Thread.currentThread().getThreadGroup();
int noThreads = currentGroup.activeCount();
Thread[] lstThreads = new Thread[noThreads];
currentGroup.enumerate(lstThreads);
StringBuilder threadNames = new StringBuilder();
for (int i = 0; i < noThreads; i++) {
if (lstThreads[i] != null && StringUtils.isNotEmpty(lstThreads[i].getName()) && lstThreads[i].getName().startsWith(name)) {
LogUtil.error("异常强制处理线程编号:" + i + " = " + lstThreads[i].getName());
threadNames.append(lstThreads[i].getName()).append("");
lstThreads[i].interrupt();
}
}
return threadNames.toString();
}
public static boolean isRunning(String reportId, String testId) {
if (StringUtils.isEmpty(reportId)) {
return false;
}
if (PoolExecBlockingQueueUtil.queue.containsKey(reportId)) {
return true;
}
if (CommonBeanFactory.getBean(ExecThreadPoolExecutor.class).check(reportId)) {
return true;
}
ThreadGroup currentGroup = Thread.currentThread().getThreadGroup();
int noThreads = currentGroup.activeCount();
Thread[] lstThreads = new Thread[noThreads];
currentGroup.enumerate(lstThreads);
for (int i = 0; i < noThreads; i++) {
if (lstThreads[i] != null) {
if (StringUtils.isNotEmpty(reportId) && StringUtils.isNotEmpty(lstThreads[i].getName()) && lstThreads[i].getName().startsWith(reportId)) {
return true;
} else if (StringUtils.isNotEmpty(testId) && StringUtils.isNotEmpty(lstThreads[i].getName()) && lstThreads[i].getName().startsWith(testId)) {
return true;
}
}
}
return false;
}
}

View File

@ -65,14 +65,10 @@ public class KafkaListenerTask implements Runnable {
(MapUtils.isNotEmpty(dto.getArbitraryData()) && (MapUtils.isNotEmpty(dto.getArbitraryData()) &&
dto.getArbitraryData().containsKey(ENV))) { dto.getArbitraryData().containsKey(ENV))) {
String key = RUN_MODE_MAP.get(dto.getRunMode()); String key = RUN_MODE_MAP.get(dto.getRunMode());
if (assortMap.containsKey(key)) {
assortMap.get(key).add(dto);
} else {
assortMap.put(key, new LinkedList<>() {{ assortMap.put(key, new LinkedList<>() {{
this.add(dto); this.add(dto);
}}); }});
} }
}
if (MapUtils.isNotEmpty(assortMap)) { if (MapUtils.isNotEmpty(assortMap)) {
testResultService.batchSaveResults(assortMap); testResultService.batchSaveResults(assortMap);
} }

View File

@ -1,228 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to you under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.metersphere.api.jmeter;
import io.metersphere.api.dto.MsgDTO;
import io.metersphere.api.dto.RequestResultExpandDTO;
import io.metersphere.api.dto.RunningParamKeys;
import io.metersphere.api.exec.queue.PoolExecBlockingQueueUtil;
import io.metersphere.commons.constants.ApiRunMode;
import io.metersphere.commons.constants.CommonConstants;
import io.metersphere.commons.utils.*;
import io.metersphere.dto.MsRegexDTO;
import io.metersphere.dto.RequestResult;
import io.metersphere.jmeter.JMeterBase;
import io.metersphere.service.definition.ApiDefinitionEnvService;
import io.metersphere.utils.JMeterVars;
import io.metersphere.utils.LoggerUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.engine.util.NoThreadClone;
import org.apache.jmeter.reporters.AbstractListenerElement;
import org.apache.jmeter.samplers.*;
import org.apache.jmeter.testelement.TestStateListener;
import org.apache.jmeter.testelement.property.BooleanProperty;
import org.apache.jmeter.threads.JMeterVariables;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
/**
* 实时结果监听
*/
public class MsDebugListener extends AbstractListenerElement implements SampleListener, Clearable, Serializable,
TestStateListener, Remoteable, NoThreadClone {
private static final String ERROR_LOGGING = "MsResultCollector.error_logging"; // $NON-NLS-1$
private static final String SUCCESS_ONLY_LOGGING = "MsResultCollector.success_only_logging"; // $NON-NLS-1$
private static final String TEST_IS_LOCAL = "*local*"; // $NON-NLS-1$
public static final String TEST_END = "MS_TEST_END";
private String runMode;
private ApiDefinitionEnvService apiDefinitionEnvService;
private Map<String, List<MsRegexDTO>> fakeErrorMap;
@Override
public Object clone() {
MsDebugListener clone = (MsDebugListener) super.clone();
return clone;
}
public void setFakeErrorMap(Map<String, List<MsRegexDTO>> fakeErrorMap) {
this.fakeErrorMap = fakeErrorMap;
}
public boolean isErrorLogging() {
return getPropertyAsBoolean(ERROR_LOGGING);
}
public void setRunMode(String runMode) {
this.runMode = runMode;
}
public final void setSuccessOnlyLogging(boolean value) {
if (value) {
setProperty(new BooleanProperty(SUCCESS_ONLY_LOGGING, true));
} else {
removeProperty(SUCCESS_ONLY_LOGGING);
}
}
/**
* Get the state of successful only logging
*
* @return Flag whether only successful samples should be logged
*/
public boolean isSuccessOnlyLogging() {
return getPropertyAsBoolean(SUCCESS_ONLY_LOGGING, false);
}
public boolean isSampleWanted(boolean success, SampleResult result) {
boolean errorOnly = isErrorLogging();
boolean successOnly = isSuccessOnlyLogging();
return isSampleWanted(success, errorOnly, successOnly) && !StringUtils.containsIgnoreCase(result.getSampleLabel(), "MS_CLEAR_LOOPS_VAR_");
}
public static boolean isSampleWanted(boolean success, boolean errorOnly,
boolean successOnly) {
return (!errorOnly && !successOnly) ||
(success && successOnly) ||
(!success && errorOnly);
}
@Override
public void testEnded(String host) {
LoggerUtil.info("Debug TestEnded " + this.getName());
MsgDTO dto = new MsgDTO();
dto.setExecEnd(false);
dto.setContent(TEST_END);
dto.setReportId("send." + this.getName());
dto.setToReport(this.getName());
LoggerUtil.debug("send. " + this.getName());
WebSocketUtil.sendMessageSingle(dto);
PoolExecBlockingQueueUtil.offer(this.getName());
//删除可能出现的临时文件
FileUtils.deleteBodyTmpFiles(this.getName());
JvmUtil.memoryInfo();
ApiLocalRunner.clearCache(this.getName());
}
@Override
public void testStarted(String host) {
LogUtil.debug("TestStarted " + this.getName());
if (apiDefinitionEnvService == null) {
apiDefinitionEnvService = CommonBeanFactory.getBean(ApiDefinitionEnvService.class);
}
}
@Override
public void testEnded() {
testEnded(TEST_IS_LOCAL);
}
@Override
public void testStarted() {
testStarted(TEST_IS_LOCAL);
}
@Override
public void sampleStarted(SampleEvent e) {
try {
MsgDTO dto = new MsgDTO();
dto.setContent(e.getThreadGroup());
dto.setReportId("send." + this.getName());
dto.setToReport(this.getName());
LoggerUtil.debug("send. " + this.getName());
WebSocketUtil.sendMessageSingle(dto);
} catch (Exception ex) {
LoggerUtil.error("消息推送失败:", ex);
}
}
@Override
public void sampleStopped(SampleEvent e) {
}
@Override
public void sampleOccurred(SampleEvent event) {
SampleResult result = event.getResult();
this.setVars(result);
if (isSampleWanted(result.isSuccessful(), result) && !StringUtils.equals(result.getSampleLabel(), RunningParamKeys.RUNNING_DEBUG_SAMPLER_NAME)) {
RequestResult requestResult = JMeterBase.getRequestResult(result, fakeErrorMap);
if (requestResult != null && ResultParseUtil.isNotAutoGenerateSampler(requestResult)) {
MsgDTO dto = new MsgDTO();
dto.setExecEnd(false);
dto.setReportId("send." + this.getName());
dto.setToReport(this.getName());
dto.setRunMode(runMode);
String console = FixedCapacityUtil.getJmeterLogger(this.getName(), false);
if (StringUtils.isNotEmpty(requestResult.getName())
&& requestResult.getName().startsWith(CommonConstants.PRE_TRANSACTION)) {
requestResult.getSubRequestResults().forEach(transactionResult -> {
this.sendResult(transactionResult, console, dto);
});
} else {
this.sendResult(requestResult, console, dto);
}
LoggerUtil.debug("send. " + this.getName());
}
}
}
private void sendResult(RequestResult requestResult, String console, MsgDTO dto) {
requestResult.getResponseResult().setConsole(console);
//对响应内容进行进一步解析和处理
RequestResultExpandDTO expandDTO = ResponseUtil.parseByRequestResult(requestResult);
if (StringUtils.equalsAnyIgnoreCase(dto.getRunMode(), ApiRunMode.DEFINITION.name(), ApiRunMode.API_PLAN.name())) {
apiDefinitionEnvService.setEnvAndPoolName(requestResult, expandDTO);
}
dto.setContent("result_" + JSON.toJSONString(expandDTO));
WebSocketUtil.sendMessageSingle(dto);
}
private void setVars(SampleResult result) {
if (StringUtils.isNotEmpty(result.getSampleLabel())
&& result.getSampleLabel().startsWith(CommonConstants.PRE_TRANSACTION)) {
for (int i = 0; i < result.getSubResults().length; i++) {
SampleResult subResult = result.getSubResults()[i];
this.setVars(subResult);
}
}
JMeterVariables variables = JMeterVars.get(result.getResourceId());
if (variables != null && CollectionUtils.isNotEmpty(variables.entrySet())) {
StringBuilder builder = new StringBuilder();
for (Map.Entry<String, Object> entry : variables.entrySet()) {
builder.append(entry.getKey()).append("").append(entry.getValue()).append(StringUtils.LF);
}
if (StringUtils.isNotEmpty(builder)) {
result.setExtVars(builder.toString());
}
}
}
@Override
public void clearData() {
}
}

View File

@ -28,6 +28,7 @@ import java.util.concurrent.TimeUnit;
public class MsKafkaListener { public class MsKafkaListener {
public static final String CONSUME_ID = "ms-api-exec-consume"; public static final String CONSUME_ID = "ms-api-exec-consume";
public static final String DEBUG_CONSUME_ID = "ms-api-debug-consume"; public static final String DEBUG_CONSUME_ID = "ms-api-debug-consume";
private static final String PRE_RESULT = "result_";
@Resource @Resource
private ApiExecutionQueueService apiExecutionQueueService; private ApiExecutionQueueService apiExecutionQueueService;
@Resource @Resource
@ -52,7 +53,7 @@ public class MsKafkaListener {
MAX_POOL_SIZE, MAX_POOL_SIZE,
KEEP_ALIVE_TIME, KEEP_ALIVE_TIME,
TimeUnit.SECONDS, TimeUnit.SECONDS,
new ArrayBlockingQueue(WORK_QUEUE_SIZE), new ArrayBlockingQueue<>(WORK_QUEUE_SIZE),
new NamedThreadFactory("MS-KAFKA-LISTENER-TASK")); new NamedThreadFactory("MS-KAFKA-LISTENER-TASK"));
@KafkaListener(id = CONSUME_ID, topics = KafkaTopicConstants.API_REPORT_TOPIC, groupId = "${spring.kafka.consumer.group-id}", containerFactory = "batchFactory") @KafkaListener(id = CONSUME_ID, topics = KafkaTopicConstants.API_REPORT_TOPIC, groupId = "${spring.kafka.consumer.group-id}", containerFactory = "batchFactory")
@ -80,15 +81,18 @@ public class MsKafkaListener {
LoggerUtil.info("接收到执行结果:", record.key()); LoggerUtil.info("接收到执行结果:", record.key());
if (ObjectUtils.isNotEmpty(record.value()) && WebSocketUtil.has(record.key().toString())) { if (ObjectUtils.isNotEmpty(record.value()) && WebSocketUtil.has(record.key().toString())) {
MsgDTO dto = JSONUtil.parseObject(record.value(), MsgDTO.class); MsgDTO dto = JSONUtil.parseObject(record.value(), MsgDTO.class);
if (StringUtils.isNotBlank(dto.getContent()) && dto.getContent().startsWith("result_")) { if (StringUtils.isNotBlank(dto.getContent()) && dto.getContent().startsWith(PRE_RESULT)) {
String content = dto.getContent().substring(7); String content = dto.getContent().substring(7);
if (StringUtils.isNotBlank(content)) { if (StringUtils.isNotBlank(content)) {
RequestResult baseResult = JSONUtil.parseObject(content, RequestResult.class); RequestResult baseResult = JSONUtil.parseObject(content, RequestResult.class);
if (ObjectUtils.isNotEmpty(baseResult)) { if (ObjectUtils.isNotEmpty(baseResult)) {
//解析是否含有误报库信息 //解析是否含有误报库信息
RequestResultExpandDTO expandDTO = ResponseUtil.parseByRequestResult(baseResult); RequestResultExpandDTO expandDTO = ResponseUtil.parseByRequestResult(baseResult);
dto.setContent(StringUtils.join("result_", JSON.toJSONString(expandDTO))); dto.setContent(StringUtils.join(PRE_RESULT, JSON.toJSONString(expandDTO)));
if (StringUtils.equalsAnyIgnoreCase(dto.getRunMode(), ApiRunMode.DEFINITION.name(), ApiRunMode.API_PLAN.name()) && dto.getContent().startsWith("result_")) { if (StringUtils.equalsAnyIgnoreCase(dto.getRunMode(),
ApiRunMode.DEFINITION.name(),
ApiRunMode.API_PLAN.name())
&& dto.getContent().startsWith(PRE_RESULT)) {
apiDefinitionEnvService.setEnvAndPoolName(dto); apiDefinitionEnvService.setEnvAndPoolName(dto);
} }
} }

View File

@ -28,12 +28,11 @@ public class ResourcePoolCalculation {
example.createCriteria().andStatusEqualTo("VALID").andTypeEqualTo("NODE").andIdEqualTo(resourcePoolId); example.createCriteria().andStatusEqualTo("VALID").andTypeEqualTo("NODE").andIdEqualTo(resourcePoolId);
List<TestResourcePool> pools = testResourcePoolMapper.selectByExample(example); List<TestResourcePool> pools = testResourcePoolMapper.selectByExample(example);
if (CollectionUtils.isNotEmpty(pools)) { if (CollectionUtils.isNotEmpty(pools)) {
List<String> poolIds = pools.stream().map(pool -> pool.getId()).collect(Collectors.toList()); List<String> poolIds = pools.stream().map(TestResourcePool::getId).collect(Collectors.toList());
TestResourceExample resourceExample = new TestResourceExample(); TestResourceExample resourceExample = new TestResourceExample();
resourceExample.createCriteria().andTestResourcePoolIdIn(poolIds); resourceExample.createCriteria().andTestResourcePoolIdIn(poolIds);
resourceExample.setOrderByClause("create_time"); resourceExample.setOrderByClause("create_time");
List<TestResource> testResources = testResourceMapper.selectByExampleWithBLOBs(resourceExample); return testResourceMapper.selectByExampleWithBLOBs(resourceExample);
return testResources;
} }
return new ArrayList<>(); return new ArrayList<>();
} }

View File

@ -178,8 +178,7 @@ public class DataFormattingUtil {
public static JmxInfoDTO getJmxInfoDTO(RunDefinitionRequest runRequest, List<MultipartFile> bodyFiles) { public static JmxInfoDTO getJmxInfoDTO(RunDefinitionRequest runRequest, List<MultipartFile> bodyFiles) {
BaseEnvironmentService fileMetadataService = CommonBeanFactory.getBean(BaseEnvironmentService.class); BaseEnvironmentService fileMetadataService = CommonBeanFactory.getBean(BaseEnvironmentService.class);
ParameterConfig config = new ParameterConfig(); ParameterConfig config = new ParameterConfig(runRequest.getProjectId(), true);
config.setProjectId(runRequest.getProjectId());
config.setOperating(true); config.setOperating(true);
config.setOperatingSampleTestName(runRequest.getName()); config.setOperatingSampleTestName(runRequest.getName());

View File

@ -49,10 +49,10 @@ public class GenerateHashTreeUtil {
JSONObject element = JSONUtil.parseObject(scenarioDefinition); JSONObject element = JSONUtil.parseObject(scenarioDefinition);
ElementUtil.dataFormatting(element); ElementUtil.dataFormatting(element);
// 多态JSON普通转换会丢失内容需要通过 ObjectMapper 获取 // 多态JSON普通转换会丢失内容需要通过 ObjectMapper 获取
if (element != null && element.has(ElementConstants.HASH_TREE)) { if (element.has(ElementConstants.HASH_TREE)) {
scenario.setHashTree(JSONUtil.readValue(element.optJSONArray(ElementConstants.HASH_TREE).toString())); scenario.setHashTree(JSONUtil.readValue(element.optJSONArray(ElementConstants.HASH_TREE).toString()));
} }
if (element != null && StringUtils.isNotEmpty(element.optString("variables"))) { if (StringUtils.isNotEmpty(element.optString("variables"))) {
scenario.setVariables(JSONUtil.parseArray(element.optString("variables"), ScenarioVariable.class)); scenario.setVariables(JSONUtil.parseArray(element.optString("variables"), ScenarioVariable.class));
} }
} catch (Exception e) { } catch (Exception e) {
@ -62,7 +62,7 @@ public class GenerateHashTreeUtil {
public static LinkedList<MsTestElement> getScenarioHashTree(String definition) { public static LinkedList<MsTestElement> getScenarioHashTree(String definition) {
JSONObject element = JSONUtil.parseObject(definition); JSONObject element = JSONUtil.parseObject(definition);
if (element != null && element.has(ElementConstants.HASH_TREE)) { if (element.has(ElementConstants.HASH_TREE)) {
ElementUtil.dataFormatting(element); ElementUtil.dataFormatting(element);
return JSONUtil.readValue(element.optJSONArray(ElementConstants.HASH_TREE).toString()); return JSONUtil.readValue(element.optJSONArray(ElementConstants.HASH_TREE).toString());
} }
@ -153,7 +153,7 @@ public class GenerateHashTreeUtil {
group.setHashTree(scenarios); group.setHashTree(scenarios);
testPlan.getHashTree().add(group); testPlan.getHashTree().add(group);
ParameterConfig config = new ParameterConfig(); ParameterConfig config = new ParameterConfig(item.getProjectId(), false);
config.setScenarioId(item.getId()); config.setScenarioId(item.getId());
config.setReportType(runRequest.getReportType()); config.setReportType(runRequest.getReportType());
if (runRequest.isRetryEnable() && runRequest.getRetryNum() > 0) { if (runRequest.isRetryEnable() && runRequest.getRetryNum() > 0) {

View File

@ -1,7 +1,6 @@
package io.metersphere.controller; package io.metersphere.controller;
import io.metersphere.api.dto.BodyFileRequest; import io.metersphere.api.dto.BodyFileRequest;
import io.metersphere.api.jmeter.JMeterThreadUtils;
import io.metersphere.api.jmeter.utils.JmxFileUtil; import io.metersphere.api.jmeter.utils.JmxFileUtil;
import io.metersphere.dto.ProjectJarConfig; import io.metersphere.dto.ProjectJarConfig;
import io.metersphere.service.ApiJMeterFileService; import io.metersphere.service.ApiJMeterFileService;
@ -28,7 +27,7 @@ public class ApiJMeterFileController {
@GetMapping("stop/{name}") @GetMapping("stop/{name}")
public String stop(@PathVariable String name) { public String stop(@PathVariable String name) {
return JMeterThreadUtils.stop(name); return name;
} }
@PostMapping("download/jar") @PostMapping("download/jar")

View File

@ -1,21 +0,0 @@
package io.metersphere.controller;
import io.metersphere.api.exec.queue.ExecThreadPoolExecutor;
import io.metersphere.commons.utils.CommonBeanFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Objects;
@RestController
@RequestMapping("/exec/thread/pool")
public class ExecThreadPoolController {
@GetMapping("/set-core-size/{size}")
public void setExecThreadPoolCoreSize(@PathVariable int size) {
Objects.requireNonNull(CommonBeanFactory.getBean(ExecThreadPoolExecutor.class)).setCorePoolSize(size);
}
}

View File

@ -11,9 +11,7 @@ import io.metersphere.api.dto.definition.request.assertions.document.DocumentEle
import io.metersphere.api.dto.scenario.Body; import io.metersphere.api.dto.scenario.Body;
import io.metersphere.api.dto.swaggerurl.SwaggerTaskResult; import io.metersphere.api.dto.swaggerurl.SwaggerTaskResult;
import io.metersphere.api.dto.swaggerurl.SwaggerUrlRequest; import io.metersphere.api.dto.swaggerurl.SwaggerUrlRequest;
import io.metersphere.api.exec.api.ApiExecuteService;
import io.metersphere.api.exec.generator.JSONSchemaParser; import io.metersphere.api.exec.generator.JSONSchemaParser;
import io.metersphere.api.exec.queue.ExecThreadPoolExecutor;
import io.metersphere.api.parse.api.ApiDefinitionImport; import io.metersphere.api.parse.api.ApiDefinitionImport;
import io.metersphere.base.domain.ApiDefinition; import io.metersphere.base.domain.ApiDefinition;
import io.metersphere.base.domain.ApiTestEnvironmentWithBLOBs; import io.metersphere.base.domain.ApiTestEnvironmentWithBLOBs;
@ -53,10 +51,6 @@ public class ApiDefinitionController {
@Resource @Resource
private BaseEnvironmentService apiTestEnvironmentService; private BaseEnvironmentService apiTestEnvironmentService;
@Resource @Resource
private ExecThreadPoolExecutor execThreadPoolExecutor;
@Resource
private ApiExecuteService apiExecuteService;
@Resource
private FunctionRunService functionRunService; private FunctionRunService functionRunService;
@PostMapping("/list/{goPage}/{pageSize}") @PostMapping("/list/{goPage}/{pageSize}")
@ -376,11 +370,6 @@ public class ApiDefinitionController {
apiDefinitionService.deleteFollows(definitionIds); apiDefinitionService.deleteFollows(definitionIds);
} }
@GetMapping("/getWorkerQueue")
public String getWorkerQueue() {
return execThreadPoolExecutor.getWorkerQueue();
}
@GetMapping("versions/{definitionId}") @GetMapping("versions/{definitionId}")
public List<ApiDefinitionResult> getApiDefinitionVersions(@PathVariable String definitionId) { public List<ApiDefinitionResult> getApiDefinitionVersions(@PathVariable String definitionId) {
return apiDefinitionService.getApiDefinitionVersions(definitionId); return apiDefinitionService.getApiDefinitionVersions(definitionId);

View File

@ -196,13 +196,13 @@ public class ApiScenarioController {
return apiAutomationService.getNewApiScenario(id); return apiAutomationService.getNewApiScenario(id);
} }
@PostMapping("/scenario-env") @PostMapping("/project-valid")
public ScenarioEnv getScenarioDefinition(@RequestBody byte[] request) { public EnvironmentCheckDTO getScenarioDefinition(@RequestBody List<String> projectIds) {
return apiAutomationService.getApiScenarioEnv(request); return apiAutomationService.getApiScenarioEnv(projectIds);
} }
@GetMapping("/env-project-ids/{id}") @GetMapping("/env-project-ids/{id}")
public ScenarioEnv getApiScenarioProjectId(@PathVariable String id) { public EnvironmentCheckDTO getApiScenarioProjectId(@PathVariable String id) {
return apiAutomationService.getApiScenarioProjectId(id); return apiAutomationService.getApiScenarioProjectId(id);
} }

View File

@ -1,17 +1,14 @@
package io.metersphere.listener; package io.metersphere.listener;
import com.mchange.lang.IntegerUtils;
import io.metersphere.api.dto.shell.filter.ScriptFilter; import io.metersphere.api.dto.shell.filter.ScriptFilter;
import io.metersphere.api.exec.queue.ExecThreadPoolExecutor;
import io.metersphere.api.jmeter.JMeterService; import io.metersphere.api.jmeter.JMeterService;
import io.metersphere.commons.constants.ScheduleGroup; import io.metersphere.commons.constants.ScheduleGroup;
import io.metersphere.commons.utils.CommonBeanFactory;
import io.metersphere.commons.utils.LogUtil; import io.metersphere.commons.utils.LogUtil;
import io.metersphere.dto.BaseSystemConfigDTO;
import io.metersphere.jmeter.ProjectClassLoader; import io.metersphere.jmeter.ProjectClassLoader;
import io.metersphere.service.*; import io.metersphere.service.*;
import io.metersphere.service.definition.ApiModuleService; import io.metersphere.service.definition.ApiModuleService;
import io.metersphere.service.scenario.ApiScenarioModuleService; import io.metersphere.service.scenario.ApiScenarioModuleService;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.python.core.Options; import org.python.core.Options;
import org.python.util.PythonInterpreter; import org.python.util.PythonInterpreter;
@ -20,8 +17,6 @@ import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner; import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import jakarta.annotation.Resource;
@Component @Component
public class ApiAppStartListener implements ApplicationRunner { public class ApiAppStartListener implements ApplicationRunner {
@Resource @Resource
@ -74,12 +69,6 @@ public class ApiAppStartListener implements ApplicationRunner {
LogUtil.info("初始化默认项目场景模块"); LogUtil.info("初始化默认项目场景模块");
apiScenarioModuleService.initDefaultModule(); apiScenarioModuleService.initDefaultModule();
BaseSystemConfigDTO dto = systemParameterService.getBaseInfo();
LogUtil.info("设置并发队列核心数", dto.getConcurrency());
if (StringUtils.isNotEmpty(dto.getConcurrency())) {
CommonBeanFactory.getBean(ExecThreadPoolExecutor.class).setCorePoolSize(IntegerUtils.parseInt(dto.getConcurrency(), 10));
}
LogUtil.info("导入内置python包处理"); LogUtil.info("导入内置python包处理");
initPythonEnv(); initPythonEnv();

View File

@ -14,10 +14,12 @@ import io.metersphere.dto.RunModeConfigDTO;
import io.metersphere.service.ApiPoolDebugService; import io.metersphere.service.ApiPoolDebugService;
import io.metersphere.service.scenario.ApiScenarioService; import io.metersphere.service.scenario.ApiScenarioService;
import io.metersphere.utils.LoggerUtil; import io.metersphere.utils.LoggerUtil;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.quartz.*; import org.quartz.*;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
@ -40,6 +42,7 @@ public class ApiScenarioTestJob extends MsScheduleJob {
public ApiScenarioTestJob() { public ApiScenarioTestJob() {
apiAutomationService = CommonBeanFactory.getBean(ApiScenarioService.class); apiAutomationService = CommonBeanFactory.getBean(ApiScenarioService.class);
apiPoolDebugService = CommonBeanFactory.getBean(ApiPoolDebugService.class);
} }
@Override @Override
@ -77,6 +80,9 @@ public class ApiScenarioTestJob extends MsScheduleJob {
String config = jobDataMap.getString("config"); String config = jobDataMap.getString("config");
if (StringUtils.isNotBlank(config)) { if (StringUtils.isNotBlank(config)) {
RunModeConfigDTO runModeConfig = JSON.parseObject(config, RunModeConfigDTO.class); RunModeConfigDTO runModeConfig = JSON.parseObject(config, RunModeConfigDTO.class);
if (BooleanUtils.isTrue(runModeConfig.getDefaultEnv())) {
runModeConfig.setEnvMap(new HashMap<>());
}
request.setConfig(runModeConfig); request.setConfig(runModeConfig);
} else { } else {
RunModeConfigDTO runModeConfigDTO = new RunModeConfigDTO(); RunModeConfigDTO runModeConfigDTO = new RunModeConfigDTO();

View File

@ -5,7 +5,6 @@ import io.metersphere.api.exec.api.ApiCaseSerialService;
import io.metersphere.api.exec.queue.DBTestQueue; import io.metersphere.api.exec.queue.DBTestQueue;
import io.metersphere.api.exec.scenario.ApiScenarioSerialService; import io.metersphere.api.exec.scenario.ApiScenarioSerialService;
import io.metersphere.api.jmeter.JMeterService; import io.metersphere.api.jmeter.JMeterService;
import io.metersphere.api.jmeter.JMeterThreadUtils;
import io.metersphere.base.domain.*; import io.metersphere.base.domain.*;
import io.metersphere.base.mapper.*; import io.metersphere.base.mapper.*;
import io.metersphere.base.mapper.ext.BaseApiExecutionQueueMapper; import io.metersphere.base.mapper.ext.BaseApiExecutionQueueMapper;
@ -33,7 +32,6 @@ import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.services.FileServer;
import org.springframework.kafka.core.KafkaTemplate; import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Propagation;
@ -388,10 +386,6 @@ public class ApiExecutionQueueService {
if (StringUtils.isNotEmpty(queue.getPoolId()) && jMeterService.getRunningQueue(queue.getPoolId(), item.getReportId())) { if (StringUtils.isNotEmpty(queue.getPoolId()) && jMeterService.getRunningQueue(queue.getPoolId(), item.getReportId())) {
continue; continue;
} }
// 检查执行报告是否还在等待队列中或执行线程中
if (JMeterThreadUtils.isRunning(item.getReportId(), item.getTestId())) {
continue;
}
// 检查是否已经超时 // 检查是否已经超时
ResultDTO dto = new ResultDTO(); ResultDTO dto = new ResultDTO();
dto.setQueueId(item.getQueueId()); dto.setQueueId(item.getQueueId());
@ -446,9 +440,6 @@ public class ApiExecutionQueueService {
TestPlanReportStatus.RUNNING.name(), ApiReportStatus.PENDING.name()) && (report.getUpdateTime() < timeout)) { TestPlanReportStatus.RUNNING.name(), ApiReportStatus.PENDING.name()) && (report.getUpdateTime() < timeout)) {
report.setStatus(ApiReportStatus.ERROR.name()); report.setStatus(ApiReportStatus.ERROR.name());
apiScenarioReportMapper.updateByPrimaryKeySelective(report); apiScenarioReportMapper.updateByPrimaryKeySelective(report);
if (FileServer.getFileServer() != null) {
FileServer.getFileServer().closeCsv(item.getReportId());
}
} }
}); });
} }
@ -474,9 +465,6 @@ public class ApiExecutionQueueService {
} }
public void stop(String reportId) { public void stop(String reportId) {
if (FileServer.getFileServer() != null) {
FileServer.getFileServer().closeCsv(reportId);
}
ApiExecutionQueueDetailExample example = new ApiExecutionQueueDetailExample(); ApiExecutionQueueDetailExample example = new ApiExecutionQueueDetailExample();
example.createCriteria().andReportIdEqualTo(reportId); example.createCriteria().andReportIdEqualTo(reportId);
List<ApiExecutionQueueDetail> details = executionQueueDetailMapper.selectByExample(example); List<ApiExecutionQueueDetail> details = executionQueueDetailMapper.selectByExample(example);
@ -500,12 +488,6 @@ public class ApiExecutionQueueService {
if (CollectionUtils.isEmpty(reportIds)) { if (CollectionUtils.isEmpty(reportIds)) {
return; return;
} }
// 清理CSV
reportIds.forEach(item -> {
if (FileServer.getFileServer() != null) {
FileServer.getFileServer().closeCsv(item);
}
});
ApiExecutionQueueDetailExample example = new ApiExecutionQueueDetailExample(); ApiExecutionQueueDetailExample example = new ApiExecutionQueueDetailExample();
example.createCriteria().andReportIdIn(reportIds); example.createCriteria().andReportIdIn(reportIds);
List<ApiExecutionQueueDetail> details = executionQueueDetailMapper.selectByExample(example); List<ApiExecutionQueueDetail> details = executionQueueDetailMapper.selectByExample(example);

View File

@ -1,6 +1,5 @@
package io.metersphere.service; package io.metersphere.service;
import io.metersphere.api.exec.queue.PoolExecBlockingQueueUtil;
import io.metersphere.api.jmeter.ApiLocalRunner; import io.metersphere.api.jmeter.ApiLocalRunner;
import io.metersphere.base.domain.ApiDefinitionExecResultWithBLOBs; import io.metersphere.base.domain.ApiDefinitionExecResultWithBLOBs;
import io.metersphere.base.domain.ApiExecutionQueueDetail; import io.metersphere.base.domain.ApiExecutionQueueDetail;
@ -47,8 +46,6 @@ public class RemakeReportService {
dto.setTestId(request.getTestId()); dto.setTestId(request.getTestId());
dto.setErrorEnded(true); dto.setErrorEnded(true);
LoggerUtil.info("进入异常结果处理:" + dto.getRunMode() + " 整体处理完成", dto.getReportId()); LoggerUtil.info("进入异常结果处理:" + dto.getRunMode() + " 整体处理完成", dto.getReportId());
// 全局并发队列
PoolExecBlockingQueueUtil.offer(dto.getReportId());
LoggerUtil.error("执行异常处理:" + errorMsg, request.getReportId()); LoggerUtil.error("执行异常处理:" + errorMsg, request.getReportId());
if (StringUtils.isNotEmpty(dto.getQueueId())) { if (StringUtils.isNotEmpty(dto.getQueueId())) {
queueService.queueNext(dto); queueService.queueNext(dto);

View File

@ -1,9 +1,6 @@
package io.metersphere.service.ext; package io.metersphere.service.ext;
import io.metersphere.api.exec.queue.ExecThreadPoolExecutor;
import io.metersphere.api.exec.queue.PoolExecBlockingQueueUtil;
import io.metersphere.api.jmeter.JMeterService; import io.metersphere.api.jmeter.JMeterService;
import io.metersphere.api.jmeter.JMeterThreadUtils;
import io.metersphere.base.domain.*; import io.metersphere.base.domain.*;
import io.metersphere.base.mapper.ApiDefinitionExecResultMapper; import io.metersphere.base.mapper.ApiDefinitionExecResultMapper;
import io.metersphere.base.mapper.ApiScenarioReportMapper; import io.metersphere.base.mapper.ApiScenarioReportMapper;
@ -59,8 +56,6 @@ public class ExtApiTaskService extends TaskService {
@Resource @Resource
private ExtApiScenarioReportMapper extApiScenarioReportMapper; private ExtApiScenarioReportMapper extApiScenarioReportMapper;
@Resource @Resource
private ExecThreadPoolExecutor execThreadPoolExecutor;
@Resource
private ApiExecutionQueueService apiExecutionQueueService; private ApiExecutionQueueService apiExecutionQueueService;
public List<TaskCenterDTO> getCases(String id) { public List<TaskCenterDTO> getCases(String id) {
@ -98,7 +93,7 @@ public class ExtApiTaskService extends TaskService {
} }
} }
public String apiStop(List<TaskRequestDTO> taskRequests) { public void apiStop(List<TaskRequestDTO> taskRequests) {
if (CollectionUtils.isNotEmpty(taskRequests)) { if (CollectionUtils.isNotEmpty(taskRequests)) {
List<TaskRequestDTO> stopTasks = taskRequests.stream().filter(s -> StringUtils.isNotEmpty(s.getReportId())).collect(Collectors.toList()); List<TaskRequestDTO> stopTasks = taskRequests.stream().filter(s -> StringUtils.isNotEmpty(s.getReportId())).collect(Collectors.toList());
// 聚类同一批资源池的一批发送 // 聚类同一批资源池的一批发送
@ -107,9 +102,7 @@ public class ExtApiTaskService extends TaskService {
if (CollectionUtils.isNotEmpty(stopTasks) && stopTasks.size() == 1) { if (CollectionUtils.isNotEmpty(stopTasks) && stopTasks.size() == 1) {
// 从队列移除 // 从队列移除
TaskRequestDTO request = stopTasks.get(0); TaskRequestDTO request = stopTasks.get(0);
execThreadPoolExecutor.removeQueue(request.getReportId());
apiExecutionQueueService.stop(request.getReportId()); apiExecutionQueueService.stop(request.getReportId());
PoolExecBlockingQueueUtil.offer(request.getReportId());
if (StringUtils.equals(request.getType(), "API")) { if (StringUtils.equals(request.getType(), "API")) {
ApiDefinitionExecResultWithBLOBs result = apiDefinitionExecResultMapper.selectByPrimaryKey(request.getReportId()); ApiDefinitionExecResultWithBLOBs result = apiDefinitionExecResultMapper.selectByPrimaryKey(request.getReportId());
if (result != null) { if (result != null) {
@ -137,7 +130,6 @@ public class ExtApiTaskService extends TaskService {
thread.start(); thread.start();
} }
} }
return "SUCCESS";
} }
private void batchStop(List<TaskRequestDTO> taskRequests) { private void batchStop(List<TaskRequestDTO> taskRequests) {
@ -152,19 +144,12 @@ public class ExtApiTaskService extends TaskService {
// 结束掉未分发完成的任务 // 结束掉未分发完成的任务
LoggerUtil.info("结束正在进行中的计划任务队列"); LoggerUtil.info("结束正在进行中的计划任务队列");
JMeterThreadUtils.stop("PLAN-CASE");
JMeterThreadUtils.stop("API-CASE-RUN");
JMeterThreadUtils.stop("SCENARIO-PARALLEL-THREAD");
if (taskRequestMap.containsKey("API")) { if (taskRequestMap.containsKey("API")) {
List<TaskResultVO> results = extApiDefinitionExecResultMapper.findByProjectIds(taskCenterRequest); List<TaskResultVO> results = extApiDefinitionExecResultMapper.findByProjectIds(taskCenterRequest);
LoggerUtil.info("查询API进行中的报告" + results.size()); LoggerUtil.info("查询API进行中的报告" + results.size());
if (CollectionUtils.isNotEmpty(results)) { if (CollectionUtils.isNotEmpty(results)) {
for (TaskResultVO item : results) { for (TaskResultVO item : results) {
extracted(poolMap, item.getId(), item.getActuator()); extracted(poolMap, item.getId(), item.getActuator());
// 从队列移除
execThreadPoolExecutor.removeQueue(item.getId());
PoolExecBlockingQueueUtil.offer(item.getId());
} }
LoggerUtil.info("结束API进行中的报告"); LoggerUtil.info("结束API进行中的报告");
baseTaskMapper.stopApi(taskCenterRequest); baseTaskMapper.stopApi(taskCenterRequest);
@ -181,9 +166,6 @@ public class ExtApiTaskService extends TaskService {
for (TaskResultVO report : reports) { for (TaskResultVO report : reports) {
extracted(poolMap, report.getId(), report.getActuator()); extracted(poolMap, report.getId(), report.getActuator());
// 从队列移除
execThreadPoolExecutor.removeQueue(report.getId());
PoolExecBlockingQueueUtil.offer(report.getId());
} }
// 清理队列并停止测试计划报告 // 清理队列并停止测试计划报告
@ -213,8 +195,6 @@ public class ExtApiTaskService extends TaskService {
this.add(reportId); this.add(reportId);
}}); }});
} }
} else {
JMeterThreadUtils.stop(reportId);
} }
} }
@ -223,7 +203,7 @@ public class ExtApiTaskService extends TaskService {
example.createCriteria().andStatusEqualTo("VALID").andTypeEqualTo("NODE").andIdEqualTo(poolId); example.createCriteria().andStatusEqualTo("VALID").andTypeEqualTo("NODE").andIdEqualTo(poolId);
List<TestResourcePool> pools = testResourcePoolMapper.selectByExample(example); List<TestResourcePool> pools = testResourcePoolMapper.selectByExample(example);
if (CollectionUtils.isNotEmpty(pools)) { if (CollectionUtils.isNotEmpty(pools)) {
List<String> poolIds = pools.stream().map(pool -> pool.getId()).collect(Collectors.toList()); List<String> poolIds = pools.stream().map(TestResourcePool::getId).collect(Collectors.toList());
TestResourceExample resourceExample = new TestResourceExample(); TestResourceExample resourceExample = new TestResourceExample();
resourceExample.createCriteria().andTestResourcePoolIdIn(poolIds); resourceExample.createCriteria().andTestResourcePoolIdIn(poolIds);
resourceExample.setOrderByClause("create_time"); resourceExample.setOrderByClause("create_time");

View File

@ -5,7 +5,7 @@ import com.github.pagehelper.PageHelper;
import io.metersphere.api.dto.ApiCaseRelevanceRequest; import io.metersphere.api.dto.ApiCaseRelevanceRequest;
import io.metersphere.api.dto.EnvironmentType; import io.metersphere.api.dto.EnvironmentType;
import io.metersphere.api.dto.RelevanceScenarioRequest; import io.metersphere.api.dto.RelevanceScenarioRequest;
import io.metersphere.api.dto.ScenarioEnv; import io.metersphere.api.dto.EnvironmentCheckDTO;
import io.metersphere.api.dto.automation.*; import io.metersphere.api.dto.automation.*;
import io.metersphere.api.dto.plan.*; import io.metersphere.api.dto.plan.*;
import io.metersphere.api.exec.scenario.ApiScenarioEnvService; import io.metersphere.api.exec.scenario.ApiScenarioEnvService;
@ -237,7 +237,7 @@ public class TestPlanScenarioCaseService {
Map<String, String> newEnvMap = new HashMap<>(16); Map<String, String> newEnvMap = new HashMap<>(16);
List<String> list = mapping.get(id); List<String> list = mapping.get(id);
if (CollectionUtils.isEmpty(list)) { if (CollectionUtils.isEmpty(list)) {
ScenarioEnv scenarioEnv = apiAutomationService.getApiScenarioProjectId(id); EnvironmentCheckDTO scenarioEnv = apiAutomationService.getApiScenarioProjectId(id);
list = new ArrayList<>(scenarioEnv.getProjectIds()); list = new ArrayList<>(scenarioEnv.getProjectIds());
} }
list.forEach(l -> newEnvMap.put(l, envMap == null ? StringUtils.EMPTY : envMap.getOrDefault(l, StringUtils.EMPTY))); list.forEach(l -> newEnvMap.put(l, envMap == null ? StringUtils.EMPTY : envMap.getOrDefault(l, StringUtils.EMPTY)));

View File

@ -168,6 +168,8 @@ public class ApiScenarioService {
private ApiAutomationRelationshipEdgeService apiAutomationRelationshipEdgeService; private ApiAutomationRelationshipEdgeService apiAutomationRelationshipEdgeService;
@Resource @Resource
private ApiTestCaseService apiTestCaseService; private ApiTestCaseService apiTestCaseService;
@Resource
private BaseProjectService baseProjectService;
private ThreadLocal<Long> currentScenarioOrder = new ThreadLocal<>(); private ThreadLocal<Long> currentScenarioOrder = new ThreadLocal<>();
@ -840,8 +842,8 @@ public class ApiScenarioService {
public ParameterConfig getConfig(ApiScenarioDTO scenario) { public ParameterConfig getConfig(ApiScenarioDTO scenario) {
try { try {
ParameterConfig config = new ParameterConfig(); ParameterConfig config = new ParameterConfig(scenario.getProjectId(), false);
Map<String, String> environmentMap = new HashMap<>(); Map<String, String> environmentMap;
String environmentType = scenario.getEnvironmentType(); String environmentType = scenario.getEnvironmentType();
String environmentGroupId = scenario.getEnvironmentGroupId(); String environmentGroupId = scenario.getEnvironmentGroupId();
String environmentJson = scenario.getEnvironmentJson(); String environmentJson = scenario.getEnvironmentJson();
@ -881,12 +883,12 @@ public class ApiScenarioService {
String scenarioId = request.getId(); String scenarioId = request.getId();
ApiScenarioDTO scenario = getNewApiScenario(scenarioId); ApiScenarioDTO scenario = getNewApiScenario(scenarioId);
if (scenario != null) { if (scenario != null) {
String referenced = element.optString("referenced"); String referenced = element.optString(MsHashTreeConstants.REFERENCED);
if (StringUtils.equalsIgnoreCase("REF", referenced)) { if (StringUtils.equalsIgnoreCase(MsHashTreeConstants.REF, referenced)) {
JSONObject source = JSONUtil.parseObject(scenario.getScenarioDefinition()); JSONObject source = JSONUtil.parseObject(scenario.getScenarioDefinition());
element = jsonMerge(source, element); element = jsonMerge(source, element);
} }
element.put("referenced", referenced); element.put(MsHashTreeConstants.REFERENCED, referenced);
String environmentType = scenario.getEnvironmentType(); String environmentType = scenario.getEnvironmentType();
String environmentGroupId = scenario.getEnvironmentGroupId(); String environmentGroupId = scenario.getEnvironmentGroupId();
String environmentJson = scenario.getEnvironmentJson(); String environmentJson = scenario.getEnvironmentJson();
@ -897,8 +899,11 @@ public class ApiScenarioService {
} }
} }
} }
String projectId = StringUtils.isNotBlank(request.getProjectId())
? request.getProjectId()
: element.optString(PropertyConstant.PROJECT_ID);
ParameterConfig config = new ParameterConfig(); ParameterConfig config = new ParameterConfig(projectId, false);
apiScenarioEnvService.setEnvConfig(environmentMap, config); apiScenarioEnvService.setEnvConfig(environmentMap, config);
if (config.getConfig() != null && !config.getConfig().isEmpty()) { if (config.getConfig() != null && !config.getConfig().isEmpty()) {
ElementUtil.dataSetDomain(element.optJSONArray(ElementConstants.HASH_TREE), config); ElementUtil.dataSetDomain(element.optJSONArray(ElementConstants.HASH_TREE), config);
@ -933,6 +938,7 @@ public class ApiScenarioService {
} }
if (StringUtils.isNotBlank(dto.getEnvironmentJson())) { if (StringUtils.isNotBlank(dto.getEnvironmentJson())) {
ApiScenarioEnvRequest request = new ApiScenarioEnvRequest(); ApiScenarioEnvRequest request = new ApiScenarioEnvRequest();
request.setProjectId(dto.getProjectId());
request.setEnvironmentEnable(false); request.setEnvironmentEnable(false);
request.setDefinition(dto.getScenarioDefinition()); request.setDefinition(dto.getScenarioDefinition());
request.setEnvironmentMap(JSON.parseObject(dto.getEnvironmentJson(), Map.class)); request.setEnvironmentMap(JSON.parseObject(dto.getEnvironmentJson(), Map.class));
@ -975,7 +981,7 @@ public class ApiScenarioService {
projectIds.add(projectId); projectIds.add(projectId);
testPlan.setName(apiScenario.getName()); testPlan.setName(apiScenario.getName());
testPlan.setHashTree(new LinkedList<>()); testPlan.setHashTree(new LinkedList<>());
ParameterConfig config = new ParameterConfig(); ParameterConfig config = new ParameterConfig(apiScenario.getProjectId(), false);
config.setOperating(true); config.setOperating(true);
config.getExcludeScenarioIds().add(apiScenario.getId()); config.getExcludeScenarioIds().add(apiScenario.getId());
config.setScenarioId(apiScenario.getId()); config.setScenarioId(apiScenario.getId());
@ -1735,7 +1741,7 @@ public class ApiScenarioService {
public byte[] exportZip(ApiScenarioBatchRequest request) { public byte[] exportZip(ApiScenarioBatchRequest request) {
List<ApiScenarioWithBLOBs> scenarios = getExportResult(request); List<ApiScenarioWithBLOBs> scenarios = getExportResult(request);
//环境检查 //环境检查
checkExportEnv(scenarios); // checkExportEnv(scenarios);
// 生成jmx // 生成jmx
Map<String, byte[]> files = new LinkedHashMap<>(); Map<String, byte[]> files = new LinkedHashMap<>();
scenarios.forEach(item -> { scenarios.forEach(item -> {
@ -1901,9 +1907,9 @@ public class ApiScenarioService {
return apiIdList; return apiIdList;
} }
public ScenarioEnv getApiScenarioProjectId(String id) { public EnvironmentCheckDTO getApiScenarioProjectId(String id) {
ApiScenarioWithBLOBs scenario = apiScenarioMapper.selectByPrimaryKey(id); ApiScenarioWithBLOBs scenario = apiScenarioMapper.selectByPrimaryKey(id);
ScenarioEnv scenarioEnv = new ScenarioEnv(); EnvironmentCheckDTO scenarioEnv = new EnvironmentCheckDTO();
if (scenario == null) { if (scenario == null) {
return scenarioEnv; return scenarioEnv;
} }
@ -1931,7 +1937,7 @@ public class ApiScenarioService {
example.createCriteria().andIdIn(request.getIds()); example.createCriteria().andIdIn(request.getIds());
List<ApiScenarioWithBLOBs> scenarioList = apiScenarioMapper.selectByExampleWithBLOBs(example); List<ApiScenarioWithBLOBs> scenarioList = apiScenarioMapper.selectByExampleWithBLOBs(example);
for (ApiScenarioWithBLOBs scenario : scenarioList) { for (ApiScenarioWithBLOBs scenario : scenarioList) {
ScenarioEnv scenarioEnv = new ScenarioEnv(); EnvironmentCheckDTO scenarioEnv = new EnvironmentCheckDTO();
if (scenario == null) { if (scenario == null) {
continue; continue;
} }
@ -2141,9 +2147,11 @@ public class ApiScenarioService {
} }
public boolean verifyScenarioEnv(String scenarioId) { public boolean verifyScenarioEnv(String scenarioId) {
ApiScenarioWithBLOBs apiScenarioWithBLOBs = apiScenarioMapper.selectByPrimaryKey(scenarioId); return true;
// 暂时去除环境校验
/*ApiScenarioWithBLOBs apiScenarioWithBLOBs = apiScenarioMapper.selectByPrimaryKey(scenarioId);
apiScenarioEnvService.setScenarioEnv(apiScenarioWithBLOBs, null); apiScenarioEnvService.setScenarioEnv(apiScenarioWithBLOBs, null);
return apiScenarioEnvService.verifyScenarioEnv(apiScenarioWithBLOBs); return apiScenarioEnvService.verifyScenarioEnv(apiScenarioWithBLOBs);*/
} }
public List<String> getFollows(String scenarioId) { public List<String> getFollows(String scenarioId) {
@ -2157,9 +2165,12 @@ public class ApiScenarioService {
return follows.stream().map(ApiScenarioFollow::getFollowId).distinct().collect(Collectors.toList()); return follows.stream().map(ApiScenarioFollow::getFollowId).distinct().collect(Collectors.toList());
} }
public ScenarioEnv getApiScenarioEnv(byte[] request) { public EnvironmentCheckDTO getApiScenarioEnv(List<String> projectIds) {
String definition = new String(request, StandardCharsets.UTF_8); List<Project> projects = baseProjectService.getProjectByIds(projectIds);
return apiScenarioEnvService.getApiScenarioEnv(definition); projectIds.removeIf(id -> !projects.stream().map(Project::getId).collect(Collectors.toSet()).contains(id));
EnvironmentCheckDTO checkDTO = new EnvironmentCheckDTO();
checkDTO.setProjectIds(new HashSet<>(projectIds));
return checkDTO;
} }
public List<MsExecResponseDTO> run(RunScenarioRequest request) { public List<MsExecResponseDTO> run(RunScenarioRequest request) {
@ -2208,7 +2219,7 @@ public class ApiScenarioService {
List<String> strings = new LinkedList<>(); List<String> strings = new LinkedList<>();
apiScenarios.forEach(item -> { apiScenarios.forEach(item -> {
if (StringUtils.isNotEmpty(item.getScenarioDefinition())) { if (StringUtils.isNotEmpty(item.getScenarioDefinition())) {
ScenarioEnv env = apiScenarioEnvService.getApiScenarioEnv(item.getScenarioDefinition()); EnvironmentCheckDTO env = apiScenarioEnvService.getApiScenarioEnv(item.getScenarioDefinition());
if (!strings.contains(item.getProjectId())) { if (!strings.contains(item.getProjectId())) {
strings.add(item.getProjectId()); strings.add(item.getProjectId());
} }
@ -2436,7 +2447,7 @@ public class ApiScenarioService {
example.createCriteria().andIdIn(scenarioIdList); example.createCriteria().andIdIn(scenarioIdList);
List<ApiScenarioWithBLOBs> scenarioWithBLOBsList = apiScenarioMapper.selectByExampleWithBLOBs(example); List<ApiScenarioWithBLOBs> scenarioWithBLOBsList = apiScenarioMapper.selectByExampleWithBLOBs(example);
for (ApiScenarioWithBLOBs scenario : scenarioWithBLOBsList) { for (ApiScenarioWithBLOBs scenario : scenarioWithBLOBsList) {
ScenarioEnv scenarioEnv = apiScenarioEnvService.getApiScenarioEnv(scenario.getScenarioDefinition()); EnvironmentCheckDTO scenarioEnv = apiScenarioEnvService.getApiScenarioEnv(scenario.getScenarioDefinition());
if (CollectionUtils.isNotEmpty(scenarioEnv.getProjectIds())) { if (CollectionUtils.isNotEmpty(scenarioEnv.getProjectIds())) {
scenarioEnv.getProjectIds().forEach(projectId -> { scenarioEnv.getProjectIds().forEach(projectId -> {
if (!returnDTO.getProjectIdList().contains(projectId)) { if (!returnDTO.getProjectIdList().contains(projectId)) {

View File

@ -27,10 +27,6 @@ export function getScenarioByProjectId(projectId) {
return get('/api/automation/env-project-ids/' + projectId); return get('/api/automation/env-project-ids/' + projectId);
} }
export function checkScenarioEnv(scenarioId) {
return get('/api/automation/env-valid/' + scenarioId);
}
export function execStop(reportId) { export function execStop(reportId) {
return get('/api/automation/stop/' + reportId); return get('/api/automation/stop/' + reportId);
} }
@ -109,14 +105,13 @@ export function getUploadConfig(url, formData) {
url: url, url: url,
data: formData, data: formData,
headers: { headers: {
'Content-Type': "application/octet-stream", 'Content-Type': 'application/octet-stream',
}, },
}; };
} }
export function getApiScenarioEnv(params) { export function getApiScenarioEnv(params) {
let reqParams = getUploadConfig('/api/automation/scenario-env', params); return post('/api/automation/project-valid', params);
return request( reqParams);
} }
export function batchEditScenario(params) { export function batchEditScenario(params) {

View File

@ -1,154 +0,0 @@
<template>
<el-dialog
title="环境选择"
:visible.sync="dialogVisible"
width="30%"
:destroy-on-close="true"
:before-close="handleClose">
<div v-loading="result">
<div v-for="pe in data" :key="pe.id" style="margin-left: 20px">
{{ getProjectName(pe.id) }}
<el-select
v-model="pe['selectEnv']"
placeholder="请选择环境"
style="margin-left: 10px; margin-top: 10px"
size="small">
<el-option
v-for="(environment, index) in pe.envs"
:key="index"
:label="environment.name"
:value="environment.id" />
<el-button class="ms-scenario-button" size="mini" type="primary" @click="openEnvironmentConfig(pe.id)">
{{ $t('api_test.environment.environment_config') }}
</el-button>
<template v-slot:empty>
<div class="empty-environment">
<el-button class="ms-scenario-button" size="mini" type="primary" @click="openEnvironmentConfig(pe.id)">
{{ $t('api_test.environment.environment_config') }}
</el-button>
</div>
</template>
</el-select>
</div>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false" size="small"> </el-button>
<el-button type="primary" @click="handleConfirm" size="small"> </el-button>
</span>
<!-- 环境配置 -->
<api-environment-config ref="environmentConfig" @close="environmentConfigClose" />
</el-dialog>
</template>
<script>
import { parseEnvironment } from '@/business/environment/model/EnvironmentModel';
import ApiEnvironmentConfig from 'metersphere-frontend/src/components/environment/ApiEnvironmentConfig';
import { getEnvironmentByProjectId } from 'metersphere-frontend/src/api/environment';
export default {
name: 'ApiScenarioEnv',
components: { ApiEnvironmentConfig },
props: {
envMap: Map,
projectIds: Set,
projectList: Array,
},
data() {
return {
data: [],
result: false,
projects: [],
environmentId: '',
environments: [],
dialogVisible: false,
};
},
methods: {
handleClose() {
this.dialogVisible = false;
},
init() {
this.projectIds.forEach((id) => {
let item = { id: id, envs: [], selectEnv: '' };
this.data.push(item);
this.result = getEnvironmentByProjectId(id).then((res) => {
let envs = res.data;
envs.forEach((environment) => {
parseEnvironment(environment);
});
//
let temp = this.data.find((dt) => dt.id === id);
temp.envs = envs;
temp.selectEnv = this.envMap.get(id);
});
});
},
open() {
this.data = [];
this.dialogVisible = true;
if (this.projectIds.size > 0) {
this.init();
}
},
getProjectName(id) {
const project = this.projectList.find((p) => p.id === id);
return project ? project.name : '';
},
openEnvironmentConfig(projectId) {
if (!projectId) {
this.$error(this.$t('api_test.select_project'));
return;
}
this.$refs.environmentConfig.open(projectId);
},
handleConfirm() {
let map = new Map();
let sign = true;
this.data.forEach((dt) => {
if (!dt.selectEnv) {
sign = false;
return;
}
map.set(dt.id, dt.selectEnv);
});
if (!sign) {
this.$warning('请为当前场景选择一个运行环境!');
return;
}
this.$emit('setProjectEnvMap', map);
this.dialogVisible = false;
},
checkEnv() {
let sign = true;
if (this.data.length > 0) {
this.data.forEach((dt) => {
if (!dt.selectEnv) {
sign = false;
return false;
}
});
} else {
sign = false;
}
if (!sign) {
this.$warning('请为当前场景选择一个运行环境!');
return false;
}
return true;
},
environmentConfigClose() {
this.data = [];
this.init();
},
},
};
</script>
<style scoped>
.ms-scenario-button {
margin-left: 20px;
}
</style>

View File

@ -402,7 +402,6 @@ import {
batchEditScenario, batchEditScenario,
batchGenPerformanceTestJmx, batchGenPerformanceTestJmx,
checkBeforeDelete, checkBeforeDelete,
checkScenarioEnv,
delByScenarioId, delByScenarioId,
delByScenarioIdAndRefId, delByScenarioIdAndRefId,
deleteBatchByCondition, deleteBatchByCondition,
@ -1370,16 +1369,9 @@ export default {
} }
this.environmentType = this.currentScenario.environmentType; this.environmentType = this.currentScenario.environmentType;
this.envGroupId = this.currentScenario.environmentGroupId; this.envGroupId = this.currentScenario.environmentGroupId;
checkScenarioEnv(this.currentScenario.id).then((res) => {
let data = res.data;
if (!data) {
this.$warning(this.$t('workspace.env_group.please_select_env_for_current_scenario'));
return false;
}
this.reportId = getUUID().substring(0, 8); this.reportId = getUUID().substring(0, 8);
this.runVisible = true; this.runVisible = true;
this.$set(row, 'isStop', true); this.$set(row, 'isStop', true);
});
} }
}); });
}, },

View File

@ -424,9 +424,6 @@
:is-across-space="true" :is-across-space="true"
ref="scenarioRelevance" /> ref="scenarioRelevance" />
<!-- 环境 -->
<api-environment-config v-if="type !== 'detail'" ref="environmentConfig" @close="environmentConfigClose" />
<!--执行组件--> <!--执行组件-->
<ms-run <ms-run
:debug="true" :debug="true"
@ -564,7 +561,6 @@ import {
} from '@/api/scenario'; } from '@/api/scenario';
import { API_STATUS, PRIORITY } from '../../definition/model/JsonData'; import { API_STATUS, PRIORITY } from '../../definition/model/JsonData';
import { buttons, setComponent } from './menu/Menu'; import { buttons, setComponent } from './menu/Menu';
import { parseEnvironment } from '@/business/environment/model/EnvironmentModel';
import { ELEMENT_TYPE, STEP, TYPE_TO_C } from './Setting'; import { ELEMENT_TYPE, STEP, TYPE_TO_C } from './Setting';
import { KeyValue } from '@/business/definition/model/ApiTestModel'; import { KeyValue } from '@/business/definition/model/ApiTestModel';
import { getCurrentProjectID, getCurrentUser } from 'metersphere-frontend/src/utils/token'; import { getCurrentProjectID, getCurrentUser } from 'metersphere-frontend/src/utils/token';
@ -581,7 +577,6 @@ import {
import MsComponentConfig from './component/ComponentConfig'; import MsComponentConfig from './component/ComponentConfig';
import { ENV_TYPE } from 'metersphere-frontend/src/utils/constants'; import { ENV_TYPE } from 'metersphere-frontend/src/utils/constants';
import { mergeRequestDocumentData } from '@/business/definition/api-definition'; import { mergeRequestDocumentData } from '@/business/definition/api-definition';
import { getEnvironmentByProjectId } from 'metersphere-frontend/src/api/environment';
import { useApiStore } from '@/store'; import { useApiStore } from '@/store';
import { getDefaultVersion, setLatestVersionById } from 'metersphere-frontend/src/api/version'; import { getDefaultVersion, setLatestVersionById } from 'metersphere-frontend/src/api/version';
@ -722,7 +717,6 @@ export default {
projectEnvMap: new Map(), projectEnvMap: new Map(),
projectList: [], projectList: [],
drawer: false, drawer: false,
isFullUrl: true,
expandedStatus: false, expandedStatus: false,
stepEnable: true, stepEnable: true,
envResult: { envResult: {
@ -809,7 +803,6 @@ export default {
this.getWsProjects(); this.getWsProjects();
this.getMaintainerOptions(); this.getMaintainerOptions();
this.getApiScenario(); this.getApiScenario();
this.getEnvironments();
this.buttonData = buttons(this); this.buttonData = buttons(this);
this.getPlugins().then(() => { this.getPlugins().then(() => {
this.initPlugins(); this.initPlugins();
@ -1329,13 +1322,6 @@ export default {
this.debugLoading = true; this.debugLoading = true;
let definition = JSON.parse(JSON.stringify(this.currentScenario)); let definition = JSON.parse(JSON.stringify(this.currentScenario));
definition.hashTree = this.scenarioDefinition; definition.hashTree = this.scenarioDefinition;
await this.getEnv(JSON.stringify(definition));
await this.$refs.envPopover.initEnv();
const sign = await this.$refs.envPopover.checkEnv(this.isFullUrl);
if (!sign) {
this.debugLoading = false;
return;
}
this.initParameter(); this.initParameter();
this.debugData = { this.debugData = {
id: this.currentScenario.id, id: this.currentScenario.id,
@ -1799,15 +1785,6 @@ export default {
/*触发执行操作*/ /*触发执行操作*/
this.$refs.currentScenario.validate(async (valid) => { this.$refs.currentScenario.validate(async (valid) => {
if (valid) { if (valid) {
let definition = JSON.parse(JSON.stringify(this.currentScenario));
definition.hashTree = this.scenarioDefinition;
await this.getEnv(JSON.stringify(definition));
await this.$refs.envPopover.initEnv();
const sign = await this.$refs.envPopover.checkEnv(this.isFullUrl);
if (!sign) {
this.debugLoading = false;
return;
}
let scenario = undefined; let scenario = undefined;
if (runScenario && runScenario.type === 'scenario') { if (runScenario && runScenario.type === 'scenario') {
scenario = runScenario; scenario = runScenario;
@ -1847,16 +1824,6 @@ export default {
} }
}); });
}, },
getEnvironments() {
if (this.projectId) {
getEnvironmentByProjectId(this.projectId).then((response) => {
this.environments = response.data;
this.environments.forEach((environment) => {
parseEnvironment(environment);
});
});
}
},
checkDataIsCopy() { checkDataIsCopy() {
// //
if (this.currentScenario.copy) { if (this.currentScenario.copy) {
@ -1864,16 +1831,6 @@ export default {
} }
}, },
openEnvironmentConfig() {
if (!this.projectId) {
this.$error(this.$t('api_test.select_project'));
return;
}
this.$refs.environmentConfig.open(this.projectId);
},
environmentConfigClose() {
this.getEnvironments();
},
allowDrag(node) { allowDrag(node) {
if (node.data && node.data.disabled && node.parent.data && node.parent.data.disabled) { if (node.data && node.data.disabled && node.parent.data && node.parent.data.disabled) {
return false; return false;
@ -1999,16 +1956,21 @@ export default {
}, },
getEnv(definition) { getEnv(definition) {
return new Promise((resolve) => { return new Promise((resolve) => {
const encoder = new TextEncoder(); this.projectIds = new Set();
const bytes = encoder.encode(definition, 'utf-8'); const regex = /"projectId"\s*:\s*"([^"]+)"/g;
getApiScenarioEnv(bytes).then((res) => { let match;
if (res.data && res.data.data) { while ((match = regex.exec(definition)) !== null) {
this.projectIds = new Set(res.data.data.projectIds); this.projectIds.add(match[1]);
this.projectIds.add(this.projectId);
this.isFullUrl = res.data.data.fullUrl;
} }
this.projectIds.add(this.projectId);
if (this.projectIds.size > 1) {
getApiScenarioEnv(Array.from(this.projectIds)).then((res) => {
this.projectIds = new Set(res.data.projectIds);
resolve(); resolve();
}); });
} else {
resolve();
}
}); });
}, },
getApiScenario(isRefresh) { getApiScenario(isRefresh) {

View File

@ -1,11 +1,69 @@
<template> <template>
<div v-loading="result.loading"> <div v-loading="result.loading">
<div v-for="pe in data" :key="pe.id" style="margin-left: 20px"> <div v-for="pe in currentProjectEnv" :key="pe.id" style="margin-left: 20px; margin-bottom: 20px">
<span :v-show="pe.id === currentProjectID">
<span :title="getProjectName(pe.id)" style="margin-left: 30px; margin-right: 10px">
{{ getProjectName(pe.id) }}
</span>
<el-select <el-select
v-model="pe['selectEnv']" v-model="pe['selectEnv']"
filterable filterable
:placeholder="$t('workspace.env_group.please_select_env')" :placeholder="$t('workspace.env_group.please_select_env')"
style="margin-top: 8px; width: 200px" style="margin-top: 8px; width: 250px"
size="small">
<el-option
v-for="(environment, index) in pe.envs"
:key="index"
:label="environment.name"
:value="environment.id" />
<el-button
class="ms-scenario-button"
v-if="isShowConfirmButton(pe.id)"
size="mini"
type="primary"
@click="openEnvironmentConfig(pe.id, pe['selectEnv'])">
{{ $t('api_test.environment.environment_config') }}
</el-button>
<template v-slot:empty>
<div v-if="isShowConfirmButton(pe.id) && pe.envs.length === 0" class="empty-environment">
<el-button
class="ms-scenario-button"
size="mini"
type="primary"
@click="openEnvironmentConfig(pe.id, pe['selectEnv'])">
{{ $t('api_test.environment.environment_config') }}
</el-button>
</div>
</template>
</el-select>
</span>
</div>
<div v-show="data.length > 0">
<span @click="active" @click.stop>
<el-tooltip>
<i class="header-icon el-icon-info" />
<div slot="content">
<div style="width: 560px">{{ $t('automation.project_env_info_tips') }}</div>
</div>
</el-tooltip>
{{ $t('automation.project_env_info') }}
<i class="icon el-icon-arrow-right" :class="{ 'is-active': isActive }" @click="active" @click.stop />
</span>
<el-collapse-transition>
<div v-if="isActive">
<div v-for="pe in data" :key="pe.id" style="margin-left: 20px">
<span :v-if="pe.id !== currentProjectID">
<span class="project-name" :title="getProjectName(pe.id)">
{{ getProjectName(pe.id) }}
<span style="color: red" :v-show="pe.id === currentProjectID">* </span>
</span>
<el-select
v-model="pe['selectEnv']"
filterable
:placeholder="$t('workspace.env_group.please_select_env')"
style="margin-top: 8px; width: 250px"
clearable
size="small"> size="small">
<el-option <el-option
v-for="(environment, index) in pe.envs" v-for="(environment, index) in pe.envs"
@ -33,10 +91,11 @@
</div> </div>
</template> </template>
</el-select> </el-select>
<span class="project-name" :title="getProjectName(pe.id)">
{{ getProjectName(pe.id) }}
</span> </span>
</div> </div>
</div>
</el-collapse-transition>
</div>
<el-button type="primary" @click="handleConfirm" size="small" :style="btnStyle" class="env-confirm"> <el-button type="primary" @click="handleConfirm" size="small" :style="btnStyle" class="env-confirm">
{{ $t('workspace.env_group.confirm') }} {{ $t('workspace.env_group.confirm') }}
@ -50,8 +109,9 @@
<script> <script>
import { parseEnvironment } from '@/business/environment/model/EnvironmentModel'; import { parseEnvironment } from '@/business/environment/model/EnvironmentModel';
import ApiEnvironmentConfig from 'metersphere-frontend/src/components/environment/ApiEnvironmentConfig'; import ApiEnvironmentConfig from 'metersphere-frontend/src/components/environment/ApiEnvironmentConfig';
import { getEnvironmentByProjectId } from 'metersphere-frontend/src/api/environment'; import { getEnvironmentByProjectId, getEnvironmentByProjectIds } from 'metersphere-frontend/src/api/environment';
import { getOwnerProjectIds } from '@/api/project'; import { getOwnerProjectIds } from '@/api/project';
import { getCurrentProjectID } from 'metersphere-frontend/src/utils/token';
export default { export default {
name: 'EnvironmentSelect', name: 'EnvironmentSelect',
@ -87,9 +147,17 @@ export default {
permissionProjectIds: [], permissionProjectIds: [],
dialogVisible: false, dialogVisible: false,
isFullUrl: true, isFullUrl: true,
currentProjectEnv: [],
isActive: false,
}; };
}, },
methods: { methods: {
active() {
this.isActive = !this.isActive;
},
currentProjectID() {
return getCurrentProjectID();
},
isShowConfirmButton(projectId) { isShowConfirmButton(projectId) {
if (this.showConfigButtonWithOutPermission === true) { if (this.showConfigButtonWithOutPermission === true) {
return true; return true;
@ -112,32 +180,44 @@ export default {
} }
let arr = []; let arr = [];
this.currentProjectEnv = [];
this.projectIds.forEach((id) => { this.projectIds.forEach((id) => {
const project = this.projectList.find((p) => p.id === id); const project = this.projectList.find((p) => p.id === id);
if (project) { if (project) {
let item = { id: id, envs: [], selectEnv: '' }; let item = { id: id, envs: [], selectEnv: '' };
if (this.currentProjectID() === project.id) {
this.currentProjectEnv.push(item);
} else {
this.data.push(item); this.data.push(item);
let p = new Promise((resolve) => { }
getEnvironmentByProjectId(id).then((res) => { }
let envs = res.data;
envs.forEach((environment) => {
parseEnvironment(environment);
}); });
let p = new Promise((resolve) => {
getEnvironmentByProjectIds(Array.from(this.projectIds)).then((res) => {
let envMap = new Map();
res.data.forEach((environment) => {
envMap.has(environment.projectId)
? envMap.set(environment.projectId, envMap.get(environment.projectId).concat(environment))
: envMap.set(environment.projectId, [environment]);
});
envMap.forEach((value, key) => {
// //
let temp = this.data.find((dt) => dt.id === id); let temp =
temp.envs = envs; this.currentProjectID() === key
? this.currentProjectEnv.find((dt) => dt.id === key)
: this.data.find((dt) => dt.id === key);
temp.envs = envMap.get(key);
let envId = undefined; let envId = undefined;
if (this.envMap) { if (this.envMap) {
envId = this.envMap.get(id); envId = this.envMap.get(key);
} }
// //
temp.selectEnv = envs.filter((e) => e.id === envId).length === 0 ? null : envId; temp.selectEnv = envMap.get(key).filter((e) => e.id === envId).length === 0 ? null : envId;
});
resolve(); resolve();
}); });
}); });
arr.push(p); arr.push(p);
}
});
return arr; return arr;
}, },
getUserPermissionProjectIds() { getUserPermissionProjectIds() {
@ -156,6 +236,9 @@ export default {
return Promise.all(this.init()); return Promise.all(this.init());
}, },
getProjectName(id) { getProjectName(id) {
if (id === getCurrentProjectID()) {
return this.$t('commons.current_project');
}
const project = this.projectList.find((p) => p.id === id); const project = this.projectList.find((p) => p.id === id);
return project ? project.name : ''; return project ? project.name : '';
}, },
@ -168,31 +251,30 @@ export default {
}, },
handleConfirm() { handleConfirm() {
let map = new Map(); let map = new Map();
let sign = true; this.currentProjectEnv.forEach((dt) => {
this.data.forEach((dt) => {
if (!dt.selectEnv) { if (!dt.selectEnv) {
sign = false;
return; return;
} }
map.set(dt.id, dt.selectEnv); map.set(dt.id, dt.selectEnv);
}); });
if (!sign) { //
this.$warning(this.$t('workspace.env_group.please_select_env_for_current_scenario')); this.data.forEach((dt) => {
if (!dt.selectEnv) {
return; return;
} }
map.set(dt.id, dt.selectEnv);
});
this.$emit('setProjectEnvMap', map); this.$emit('setProjectEnvMap', map);
this.$emit('close'); this.$emit('close');
}, },
checkEnv(data) { checkEnv(data) {
let sign = true;
this.isFullUrl = true; this.isFullUrl = true;
if (data) { if (data) {
return true; return true;
} }
if (this.data.length > 0) { if (this.currentProjectEnv.length > 0) {
this.data.forEach((dt) => { this.currentProjectEnv.forEach((dt) => {
if (!dt.selectEnv) { if (!dt.selectEnv) {
sign = false;
return false; return false;
} }
}); });
@ -201,17 +283,11 @@ export default {
if (this.envMap && this.envMap.size > 0) { if (this.envMap && this.envMap.size > 0) {
this.projectIds.forEach((id) => { this.projectIds.forEach((id) => {
if (!this.envMap.get(id)) { if (!this.envMap.get(id)) {
sign = false;
return false; return false;
} }
}); });
} }
} }
if (!sign) {
this.$warning(this.$t('workspace.env_group.please_select_env_for_current_scenario'));
return false;
}
return true; return true;
}, },
environmentConfigClose() { environmentConfigClose() {
@ -232,13 +308,24 @@ export default {
margin-top: 10px; margin-top: 10px;
} }
:deep(.el-collapse-item__arrow) {
margin: 0 0px 0px 8px;
}
.project-name { .project-name {
display: inline-block; display: inline-block;
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
width: 150px; width: 100px;
margin-left: 8px;
vertical-align: middle; vertical-align: middle;
} }
.icon.is-active {
transform: rotate(90deg);
}
.el-icon-arrow-right {
margin-right: 3px;
}
</style> </style>

View File

@ -210,7 +210,6 @@ export default {
this.basisData.method = this.basisData.protocol; this.basisData.method = this.basisData.protocol;
this.$emit('saveApi', this.basisData); this.$emit('saveApi', this.basisData);
}, },
runTest() {},
itselfEnvironment(environmentId) { itselfEnvironment(environmentId) {
let id = this.request.projectId ? this.request.projectId : this.projectId; let id = this.request.projectId ? this.request.projectId : this.projectId;
this.result = getEnvironmentByProjectId(id).then((response) => { this.result = getEnvironmentByProjectId(id).then((response) => {
@ -240,9 +239,22 @@ export default {
this.initDataSource(undefined, undefined, targetDataSourceName); this.initDataSource(undefined, undefined, targetDataSourceName);
}); });
}, },
getEnvironments(environmentId, isCreated) { //
let envId = ''; async selectProjectId(environmentId) {
let id = this.request.projectId ? this.request.projectId : this.projectId; let id = this.request.projectId ? this.request.projectId : this.projectId;
//
if (environmentId) {
return id;
}
let scenarioEnvId = this.scenarioId !== '' ? this.scenarioId + '_' + id : id;
if (store.scenarioEnvMap && store.scenarioEnvMap instanceof Map && store.scenarioEnvMap.has(scenarioEnvId)) {
return id;
}
return this.projectId;
},
async getEnvironments(environmentId, isCreated) {
let envId = '';
let id = await this.selectProjectId(environmentId);
let scenarioEnvId = this.scenarioId !== '' ? this.scenarioId + '_' + id : id; let scenarioEnvId = this.scenarioId !== '' ? this.scenarioId + '_' + id : id;
if (store.scenarioEnvMap && store.scenarioEnvMap instanceof Map && store.scenarioEnvMap.has(scenarioEnvId)) { if (store.scenarioEnvMap && store.scenarioEnvMap instanceof Map && store.scenarioEnvMap.has(scenarioEnvId)) {
envId = store.scenarioEnvMap.get(scenarioEnvId); envId = store.scenarioEnvMap.get(scenarioEnvId);
@ -298,7 +310,7 @@ export default {
} }
}); });
if (!hasEnvironment) { if (!hasEnvironment) {
this.request.environmentId = ""; this.request.environmentId = '';
} }
this.initDataSource(envId, currentEnvironment, targetDataSourceName); this.initDataSource(envId, currentEnvironment, targetDataSourceName);
}); });
@ -402,14 +414,6 @@ export default {
margin-left: 60px; margin-left: 60px;
} }
.ms-left-cell {
margin-top: 40px;
}
.ms-left-buttion {
margin: 6px 0px 8px 30px;
}
.environment-button { .environment-button {
margin-left: 20px; margin-left: 20px;
padding: 7px; padding: 7px;

View File

@ -112,7 +112,7 @@ import { getCurrentProjectID, getCurrentWorkspaceId } from 'metersphere-frontend
import { getUUID, strMapToObj } from 'metersphere-frontend/src/utils'; import { getUUID, strMapToObj } from 'metersphere-frontend/src/utils';
import { STEP } from '@/business/automation/scenario/Setting'; import { STEP } from '@/business/automation/scenario/Setting';
import { getOwnerProjectIds, getProject } from '@/api/project'; import { getOwnerProjectIds, getProject } from '@/api/project';
import { checkScenarioEnv, getScenarioById, setScenarioDomain } from '@/api/scenario'; import { getScenarioById, setScenarioDomain } from '@/api/scenario';
export default { export default {
name: 'ApiScenarioComponent', name: 'ApiScenarioComponent',
@ -228,14 +228,7 @@ export default {
this.reload(); this.reload();
}, },
checkEnv(val) { checkEnv(val) {
checkScenarioEnv(this.scenario.id).then((res) => {
if (this.scenario.environmentEnable && !res.data) {
this.scenario.environmentEnable = false;
this.$warning(this.$t('commons.scenario_warning'));
return;
}
this.setDomain(val); this.setDomain(val);
});
}, },
setDomain(val) { setDomain(val) {
let param = { let param = {

View File

@ -70,7 +70,7 @@ import MsAddBasisApi from '../api/AddBasisApi';
import MsAddApiCase from '../api/AddApiCase'; import MsAddApiCase from '../api/AddApiCase';
import { getUUID, strMapToObj } from 'metersphere-frontend/src/utils'; import { getUUID, strMapToObj } from 'metersphere-frontend/src/utils';
import { getCurrentProjectID } from 'metersphere-frontend/src/utils/token'; import { getCurrentProjectID } from 'metersphere-frontend/src/utils/token';
import { checkScenarioEnv, getScenarioWithBLOBsById, setScenarioDomain } from '@/api/scenario'; import { getScenarioWithBLOBsById, setScenarioDomain } from '@/api/scenario';
import { hasPermission } from 'metersphere-frontend/src/utils/permission'; import { hasPermission } from 'metersphere-frontend/src/utils/permission';
export default { export default {
@ -176,14 +176,7 @@ export default {
}); });
}, },
checkEnv(val) { checkEnv(val) {
checkScenarioEnv(this.data.id).then((res) => {
if (this.data.environmentEnable && !res.data) {
this.data.environmentEnable = false;
this.$warning(this.$t('commons.scenario_warning'));
return;
}
this.setDomain(val); this.setDomain(val);
});
}, },
setDomain(val) { setDomain(val) {
let param = { let param = {

View File

@ -46,7 +46,9 @@
</div> </div>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<el-checkbox v-model="cookieShare" @change="setCookieShare">{{ $t('api_test.scenario.share_cookie') }}</el-checkbox> <el-checkbox v-model="cookieShare" @change="setCookieShare">{{
$t('api_test.scenario.share_cookie')
}}</el-checkbox>
<el-checkbox v-model="sampleError" @change="setOnSampleError" style="margin-right: 10px"> <el-checkbox v-model="sampleError" @change="setOnSampleError" style="margin-right: 10px">
{{ $t('commons.failure_continues') }} {{ $t('commons.failure_continues') }}
</el-checkbox> </el-checkbox>
@ -89,7 +91,12 @@
<!-- 场景步骤--> <!-- 场景步骤-->
<ms-container :class="{ 'maximize-container': !asideHidden }"> <ms-container :class="{ 'maximize-container': !asideHidden }">
<ms-aside-container :draggable="false" @setAsideHidden="setAsideHidden" style="padding: 0px; overflow: hidden" width="50%" @click.native="handleMainClick"> <ms-aside-container
:draggable="false"
@setAsideHidden="setAsideHidden"
style="padding: 0px; overflow: hidden"
width="35%"
@click.native="handleMainClick">
<div class="ms-debug-result" v-if="reqTotal > 0"> <div class="ms-debug-result" v-if="reqTotal > 0">
<span style="float: right"> <span style="float: right">
<span class="ms-message-right"> {{ reqTotalTime }} ms </span> <span class="ms-message-right"> {{ reqTotalTime }} ms </span>
@ -116,8 +123,7 @@
@node-drag-end="allowDrag" @node-drag-end="allowDrag"
@node-click="nodeClick" @node-click="nodeClick"
class="ms-max-tree" class="ms-max-tree"
ref="maxStepTree" ref="maxStepTree">
>
<el-row <el-row
class="custom-tree-node" class="custom-tree-node"
:gutter="18" :gutter="18"
@ -224,8 +230,7 @@
@openScenario="openScenario" @openScenario="openScenario"
@runScenario="runScenario" @runScenario="runScenario"
@stopScenario="stopScenario" @stopScenario="stopScenario"
v-if="selectedTreeNode && selectedNode" v-if="selectedTreeNode && selectedNode" />
/>
<!-- 请求下还有的子步骤--> <!-- 请求下还有的子步骤-->
<div v-if="selectedTreeNode && selectedTreeNode.hashTree && showNode(selectedTreeNode)"> <div v-if="selectedTreeNode && selectedTreeNode.hashTree && showNode(selectedTreeNode)">
<div v-for="item in selectedTreeNode.hashTree" :key="item.id" class="ms-col-one"> <div v-for="item in selectedTreeNode.hashTree" :key="item.id" class="ms-col-one">
@ -274,9 +279,6 @@
<!--场景导入 --> <!--场景导入 -->
<scenario-relevance v-if="type !== 'detail'" @save="addScenario" ref="scenarioRelevance" /> <scenario-relevance v-if="type !== 'detail'" @save="addScenario" ref="scenarioRelevance" />
<!-- 环境 -->
<api-environment-config v-if="type !== 'detail'" ref="environmentConfig" @close="environmentConfigClose" />
<!--执行组件--> <!--执行组件-->
<ms-run <ms-run
:debug="true" :debug="true"
@ -318,21 +320,16 @@
<script> <script>
import { getApiScenarioEnv } from '@/api/scenario'; import { getApiScenarioEnv } from '@/api/scenario';
import { API_STATUS, PRIORITY } from '../../../definition/model/JsonData'; import { API_STATUS, PRIORITY } from '../../../definition/model/JsonData';
import { parseEnvironment } from '@/business/environment/model/EnvironmentModel';
import { STEP } from '../Setting'; import { STEP } from '../Setting';
import { getUUID, strMapToObj } from 'metersphere-frontend/src/utils'; import { getUUID, strMapToObj } from 'metersphere-frontend/src/utils';
import { getCurrentProjectID } from 'metersphere-frontend/src/utils/token'; import { getCurrentProjectID } from 'metersphere-frontend/src/utils/token';
import { hasLicense } from 'metersphere-frontend/src/utils/permission';
import OutsideClick from '../common/outside-click'; import OutsideClick from '../common/outside-click';
import { copyScenarioRow, saveScenario, scenarioSort, handleCtrlSEvent } from '@/business/automation/api-automation'; import { copyScenarioRow, saveScenario, scenarioSort, handleCtrlSEvent } from '@/business/automation/api-automation';
import { buttons, setComponent } from '../menu/Menu'; import { buttons, setComponent } from '../menu/Menu';
import MsContainer from 'metersphere-frontend/src/components/MsContainer'; import MsContainer from 'metersphere-frontend/src/components/MsContainer';
import MsMainContainer from 'metersphere-frontend/src/components/MsMainContainer'; import MsMainContainer from 'metersphere-frontend/src/components/MsMainContainer';
import MsAsideContainer from 'metersphere-frontend/src/components/MsAsideContainer'; import MsAsideContainer from 'metersphere-frontend/src/components/MsAsideContainer';
// import html2canvas from 'html2canvas';
import { getEnvironmentByProjectId } from 'metersphere-frontend/src/api/environment';
import { useApiStore } from '@/store'; import { useApiStore } from '@/store';
import { getPluginList } from '@/api/plugin';
const store = useApiStore(); const store = useApiStore();
let jsonPath = require('jsonpath'); let jsonPath = require('jsonpath');
@ -537,11 +534,11 @@ export default {
methods: { methods: {
handleHeaderClick(e) { handleHeaderClick(e) {
if (e.target.tagName === 'DIV') { if (e.target.tagName === 'DIV') {
this.outsideClick(e) this.outsideClick(e);
} }
}, },
handleMainClick(e) { handleMainClick(e) {
this.outsideClick(e) this.outsideClick(e);
}, },
handleComponentClick(e) { handleComponentClick(e) {
e.stopPropagation(); e.stopPropagation();
@ -819,50 +816,6 @@ export default {
this.debugLoading = true; this.debugLoading = true;
this.$emit('runDebug'); this.$emit('runDebug');
}, },
runDebug() {
/*触发执行操作*/
let sign = this.$refs.envPopover.checkEnv();
if (!sign) {
this.errorRefresh();
return;
}
if (this.$refs['currentScenario']) {
this.$refs['currentScenario'].validate((valid) => {
if (valid) {
Promise.all([this.editScenario()]).then((val) => {
if (val) {
this.debugData = {
id: this.currentScenario.id,
name: this.currentScenario.name,
type: 'scenario',
variables: this.currentScenario.variables,
referenced: 'Created',
enableCookieShare: this.enableCookieShare,
headers: this.currentScenario.headers,
environmentMap: this.projectEnvMap,
hashTree: this.scenarioDefinition,
};
this.reportId = getUUID().substring(0, 8);
}
});
} else {
this.errorRefresh();
}
});
}
},
getEnvironments() {
if (this.projectId) {
getEnvironmentByProjectId(this.projectId).then((response) => {
this.environments = response.data;
this.environments.forEach((environment) => {
parseEnvironment(environment);
});
//
this.checkDataIsCopy();
});
}
},
checkDataIsCopy() { checkDataIsCopy() {
// //
@ -871,16 +824,6 @@ export default {
} }
}, },
openEnvironmentConfig() {
if (!this.projectId) {
this.$error(this.$t('api_test.select_project'));
return;
}
this.$refs.environmentConfig.open(this.projectId);
},
environmentConfigClose() {
this.getEnvironments();
},
allowDrop(draggingNode, dropNode, dropType) { allowDrop(draggingNode, dropNode, dropType) {
if (draggingNode.data.type === 'Assertions' || dropNode.data.type === 'Assertions') { if (draggingNode.data.type === 'Assertions' || dropNode.data.type === 'Assertions') {
return false; return false;
@ -1324,16 +1267,21 @@ export default {
}, },
getEnv(definition) { getEnv(definition) {
return new Promise((resolve) => { return new Promise((resolve) => {
const encoder = new TextEncoder(); this.projectIds = new Set();
const bytes = encoder.encode(definition, 'utf-8'); const regex = /"projectId"\s*:\s*"([^"]+)"/g;
getApiScenarioEnv(bytes).then((res) => { let match;
if (res.data && res.data.data) { while ((match = regex.exec(definition)) !== null) {
res.data.data.projectIds.push(this.projectId); this.projectIds.add(match[1]);
this.$emit('update:projectIds', new Set(res.data.data.projectIds));
this.$emit('update:isFullUrl', res.data.data.fullUrl);
} }
this.projectIds.add(this.projectId);
if (this.projectIds.size > 1) {
getApiScenarioEnv(Array.from(this.projectIds)).then((res) => {
this.$emit('update:projectIds', new Set(res.data.projectIds));
resolve(); resolve();
}); });
} else {
resolve();
}
}); });
}, },
}, },
@ -1516,5 +1464,4 @@ export default {
white-space: nowrap; white-space: nowrap;
width: 120px; width: 120px;
} }
</style> </style>

View File

@ -52,6 +52,10 @@
<span>{{ $t('load_test.runtime_config') }}</span> <span>{{ $t('load_test.runtime_config') }}</span>
<div style="padding-top: 10px"> <div style="padding-top: 10px">
<span class="ms-mode-span">{{ $t('commons.environment') }}</span> <span class="ms-mode-span">{{ $t('commons.environment') }}</span>
<el-radio-group v-model="runConfig.defaultEnv" style="margin-right: 20px">
<el-radio :label="true">{{ $t('automation.default_environment') }}</el-radio>
<el-radio :label="false">{{ $t('automation.select_new_environment') }}</el-radio>
</el-radio-group>
<env-popover <env-popover
:project-ids="projectIds" :project-ids="projectIds"
:placement="'bottom-start'" :placement="'bottom-start'"
@ -64,15 +68,13 @@
@setProjectEnvMap="setProjectEnvMap" @setProjectEnvMap="setProjectEnvMap"
@showPopover="showPopover" @showPopover="showPopover"
ref="envPopover" ref="envPopover"
class="env-popover" /> class="env-popover"
v-show="this.runConfig.defaultEnv === false" />
</div> </div>
<div class="ms-mode-div"> <div class="ms-mode-div">
<span class="ms-mode-span">{{ $t('run_mode.other_config') }}</span> <span class="ms-mode-span">{{ $t('run_mode.other_config') }}</span>
<span>{{ $t('run_mode.run_with_resource_pool') }}:</span> <span>{{ $t('run_mode.run_with_resource_pool') }}:</span>
<el-select <el-select style="margin-left: 10px" v-model="runConfig.resourcePoolId" size="mini">
style="margin-left: 10px"
v-model="runConfig.resourcePoolId"
size="mini">
<el-option <el-option
v-for="item in resourcePools" v-for="item in resourcePools"
:key="item.id" :key="item.id"
@ -87,8 +89,7 @@
<crontab @hide="showCron = false" @fill="crontabFill" :expression="schedule.value" ref="crontab" /> <crontab @hide="showCron = false" @fill="crontabFill" :expression="schedule.value" ref="crontab" />
</el-dialog> </el-dialog>
</el-tab-pane> </el-tab-pane>
<el-tab-pane :label="$t('schedule.task_notification')" name="second" <el-tab-pane :label="$t('schedule.task_notification')" name="second" v-permission="['PROJECT_MESSAGE:READ']">
v-permission="['PROJECT_MESSAGE:READ']">
<ms-schedule-notification :test-id="testId" :schedule-receiver-options="scheduleReceiverOptions" /> <ms-schedule-notification :test-id="testId" :schedule-receiver-options="scheduleReceiverOptions" />
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
@ -188,6 +189,7 @@ export default {
envMap: {}, envMap: {},
environmentGroupId: '', environmentGroupId: '',
environmentType: ENV_TYPE.JSON, environmentType: ENV_TYPE.JSON,
defaultEnv: true,
}, },
projectList: [], projectList: [],
projectIds: new Set(), projectIds: new Set(),
@ -197,7 +199,7 @@ export default {
methods: { methods: {
async checkPool() { async checkPool() {
let hasPool = false; let hasPool = false;
this.resourcePools.forEach(item => { this.resourcePools.forEach((item) => {
if (item.id === this.runConfig.resourcePoolId) { if (item.id === this.runConfig.resourcePoolId) {
hasPool = true; hasPool = true;
} }
@ -208,7 +210,7 @@ export default {
let hasPool = await this.checkPool(); let hasPool = await this.checkPool();
if (!hasPool) { if (!hasPool) {
this.runConfig.resourcePoolId = null; this.runConfig.resourcePoolId = null;
getProjectConfig(getCurrentProjectID(), "").then(async (res) => { getProjectConfig(getCurrentProjectID(), '').then(async (res) => {
if (res.data && res.data.poolEnable && res.data.resourcePoolId) { if (res.data && res.data.poolEnable && res.data.resourcePoolId) {
this.runConfig.resourcePoolId = res.data.resourcePoolId; this.runConfig.resourcePoolId = res.data.resourcePoolId;
} }
@ -219,9 +221,6 @@ export default {
}); });
} }
}, },
currentUser: () => {
return getCurrentUser();
},
intervalValidate() { intervalValidate() {
if (this.getIntervalTime() < 1 * 60 * 1000) { if (this.getIntervalTime() < 1 * 60 * 1000) {
return false; return false;
@ -322,6 +321,10 @@ export default {
this.schedule = response.data; this.schedule = response.data;
if (response.data.config) { if (response.data.config) {
this.runConfig = JSON.parse(response.data.config); this.runConfig = JSON.parse(response.data.config);
//
if (this.runConfig.defaultEnv === null) {
this.runConfig.defaultEnv = false;
}
if (this.runConfig.envMap) { if (this.runConfig.envMap) {
this.projectEnvListMap = objToStrMap(this.runConfig.envMap); this.projectEnvListMap = objToStrMap(this.runConfig.envMap);
} else { } else {
@ -384,12 +387,11 @@ export default {
} }
if (this.schedule.enable) { if (this.schedule.enable) {
if ( if (
(this.runConfig.environmentType === 'JSON' && Object.keys(this.runConfig.envMap).length === 0) || (!this.runConfig.defaultEnv &&
this.runConfig.environmentType === 'JSON' &&
Object.keys(this.runConfig.envMap).length === 0) ||
(this.runConfig.environmentType === 'GROUP' && !this.runConfig.environmentGroupId) (this.runConfig.environmentType === 'GROUP' && !this.runConfig.environmentGroupId)
) { )
this.$warning(this.$t('workspace.env_group.please_select_env_for_current_scenario'));
return;
}
if (this.runConfig.resourcePoolId == null) { if (this.runConfig.resourcePoolId == null) {
this.$warning(this.$t('workspace.env_group.please_select_run_within_resource_pool')); this.$warning(this.$t('workspace.env_group.please_select_run_within_resource_pool'));
return; return;

View File

@ -132,13 +132,19 @@ const message = {
automation: { automation: {
project_no_permission: 'The current person does not have the operation permission for this step', project_no_permission: 'The current person does not have the operation permission for this step',
document_validity_msg: 'The file has been modified, please re-upload', document_validity_msg: 'The file has been modified, please re-upload',
scenario_step_ref_message: scenario_step_ref_message: 'The current may cause page loading exceptions, whether to continue?',
'The current may cause page loading exceptions, whether to continue?',
case_message: 'Please select a case', case_message: 'Please select a case',
scenario_message: 'Please select a scene', scenario_message: 'Please select a scene',
scenario_plugin_debug_warning: 'The scenario contains plugin steps, and the corresponding scenario has been deleted and cannot be debugged ', scenario_plugin_debug_warning:
scenario_plugin_save_warning: 'The scene contains plugin steps, and the corresponding scene has been deleted and cannot be edited ', 'The scenario contains plugin steps, and the corresponding scenario has been deleted and cannot be debugged ',
scenario_plugin_run_warning: 'The scenario contains plugin steps, and the corresponding scenario has been deleted and cannot be executed', scenario_plugin_save_warning:
'The scene contains plugin steps, and the corresponding scene has been deleted and cannot be edited ',
scenario_plugin_run_warning:
'The scenario contains plugin steps, and the corresponding scenario has been deleted and cannot be executed',
project_env_info: 'More project environment',
project_env_info_tips:
'1. Steps involving cross-projects in the current scenario can be executed in the environment of the project to which they belong. If not specified, the current project environment will be used by default.\n' +
' 2. If the cross-project steps are configured to use the original scene environment in the scene settings, the original scene environment will be used by default to execute.',
}, },
}; };
export default { export default {

View File

@ -134,6 +134,11 @@ const message = {
scenario_plugin_debug_warning: '场景包含插件步骤,对应场景已经删除不能调试!', scenario_plugin_debug_warning: '场景包含插件步骤,对应场景已经删除不能调试!',
scenario_plugin_save_warning: '场景包含插件步骤,对应场景已经删除不能编辑!', scenario_plugin_save_warning: '场景包含插件步骤,对应场景已经删除不能编辑!',
scenario_plugin_run_warning: '场景包含插件步骤,对应场景已经删除不能执行!', scenario_plugin_run_warning: '场景包含插件步骤,对应场景已经删除不能执行!',
project_env_info: '更多项目环境',
project_env_info_tips:
'1.当前场景涉及到跨项目的步骤可以指定其所属项目的环境执行,不指定则默认使用当前项目环境执行。\n 2.如跨项目的步骤在场景设置中配置了使用原场景环境,则默认使用原场景环境执行。',
default_environment: '默认环境',
select_new_environment: '指定新环境',
}, },
}; };

View File

@ -133,7 +133,10 @@ const message = {
scenario_message: '請選擇場景', scenario_message: '請選擇場景',
scenario_plugin_debug_warning: '場景包含挿件步驟,對應場景已經刪除不能調試! ', scenario_plugin_debug_warning: '場景包含挿件步驟,對應場景已經刪除不能調試! ',
scenario_plugin_save_warning: '場景包含挿件步驟,對應場景已經刪除不能編輯! ', scenario_plugin_save_warning: '場景包含挿件步驟,對應場景已經刪除不能編輯! ',
scenario_plugin_run_warning: '場景包含挿件步驟,對應場景已經刪除不能運行! ' scenario_plugin_run_warning: '場景包含挿件步驟,對應場景已經刪除不能運行! ',
project_env_info: '更多項目環境',
project_env_info_tips:
'1.當前場景涉及到跨項目的步驟可以指定其所屬項目的環境執行,不指定則默認使用當前項目環境執行。 \n 2.如跨項目的步驟在場景設置中配置了使用原場景環境,則默認使用原場景環境執行。 ',
}, },
}; };

View File

@ -1,6 +1,6 @@
import {get, post, request} from "../plugins/request" import { get, post, request } from '../plugins/request';
// 获取使用当前js模块的package.json不要修改引入路径 // 获取使用当前js模块的package.json不要修改引入路径
import packageInfo from '@/../package.json' import packageInfo from '@/../package.json';
const currentModuleName = packageInfo.name; const currentModuleName = packageInfo.name;
export function getEnvironmentMapByGroupId(id) { export function getEnvironmentMapByGroupId(id) {
@ -10,6 +10,10 @@ export function getEnvironmentMapByGroupId(id) {
export function getEnvironmentByProjectId(projectId) { export function getEnvironmentByProjectId(projectId) {
return get('/environment/list/' + projectId); return get('/environment/list/' + projectId);
} }
// 不含环境的blob数据
export function getEnvironmentByProjectIds(projectIds) {
return post('/environment/project-env', projectIds);
}
export function getEnvironmentById(environmentId) { export function getEnvironmentById(environmentId) {
return get('/environment/get/' + environmentId); return get('/environment/get/' + environmentId);
@ -84,23 +88,26 @@ export function databaseValidate(params) {
export function getUploadConfig(url, formData) { export function getUploadConfig(url, formData) {
return { return {
method: 'POST', url: url, data: formData, headers: { method: 'POST',
'Content-Type': undefined url: url,
} data: formData,
headers: {
'Content-Type': undefined,
},
}; };
} }
export function fileUpload(url, file, files, param) { export function fileUpload(url, file, files, param) {
let formData = new FormData(); let formData = new FormData();
if (file) { if (file) {
formData.append("file", file); formData.append('file', file);
} }
if (files) { if (files) {
files.forEach(f => { files.forEach((f) => {
formData.append("files", f); formData.append('files', f);
}); });
} }
formData.append('request', new Blob([JSON.stringify(param)], {type: "application/json"})); formData.append('request', new Blob([JSON.stringify(param)], { type: 'application/json' }));
let config = getUploadConfig(url, formData); let config = getUploadConfig(url, formData);
return request(config); return request(config);
} }
@ -150,7 +157,7 @@ export function getModuleByUrl(url) {
} }
export function getCaseRelateModuleByCondition(url, params) { export function getCaseRelateModuleByCondition(url, params) {
return post('/environment/relate' + url, params) return post('/environment/relate' + url, params);
} }
export function getCodeSnippetPages(goPage, pageSize, params) { export function getCodeSnippetPages(goPage, pageSize, params) {

View File

@ -14,6 +14,7 @@ public class RunModeConfigDTO {
private String testId; private String testId;
private String amassReport; private String amassReport;
private boolean onSampleError; private boolean onSampleError;
private Boolean defaultEnv;
// 失败重试 // 失败重试
private boolean retryEnable; private boolean retryEnable;
// 失败重试次数 // 失败重试次数

View File

@ -1,353 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to you under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jmeter.config;
import io.metersphere.utils.LoggerUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.engine.event.LoopIterationEvent;
import org.apache.jmeter.engine.event.LoopIterationListener;
import org.apache.jmeter.engine.util.NoConfigMerge;
import org.apache.jmeter.gui.GUIMenuSortOrder;
import org.apache.jmeter.gui.TestElementMetadata;
import org.apache.jmeter.save.CSVSaveService;
import org.apache.jmeter.services.FileServer;
import org.apache.jmeter.testbeans.TestBean;
import org.apache.jmeter.testbeans.gui.GenericTestBeanCustomizer;
import org.apache.jmeter.testelement.property.JMeterProperty;
import org.apache.jmeter.testelement.property.StringProperty;
import org.apache.jmeter.threads.JMeterContext;
import org.apache.jmeter.threads.JMeterVariables;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jorphan.util.JMeterStopThreadException;
import org.apache.jorphan.util.JOrphanUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.io.IOException;
import java.util.ResourceBundle;
/**
* Read lines from a file and split int variables.
*
* The iterationStart() method is used to set up each set of values.
*
* By default, the same file is shared between all threads
* (and other thread groups, if they use the same file name).
*
* The shareMode can be set to:
* <ul>
* <li>All threads - default, as described above</li>
* <li>Current thread group</li>
* <li>Current thread</li>
* <li>Identifier - all threads sharing the same identifier</li>
* </ul>
*
* The class uses the FileServer alias mechanism to provide the different share modes.
* For all threads, the file alias is set to the file name.
* Otherwise, a suffix is appended to the filename to make it unique within the required context.
* For current thread group, the thread group identityHashcode is used;
* for individual threads, the thread hashcode is used as the suffix.
* Or the user can provide their own suffix, in which case the file is shared between all
* threads with the same suffix.
*
*/
@GUIMenuSortOrder(1)
@TestElementMetadata(labelResource = "displayName")
public class CSVDataSet extends ConfigTestElement
implements TestBean, LoopIterationListener, NoConfigMerge {
private static final Logger log = LoggerFactory.getLogger(CSVDataSet.class);
private static final long serialVersionUID = 233L;
private static final String EOFVALUE = // value to return at EOF
JMeterUtils.getPropDefault("csvdataset.eofstring", "<EOF>"); //$NON-NLS-1$ //$NON-NLS-2$
private transient String filename;
private transient String fileEncoding;
private transient String variableNames;
private transient String delimiter;
private transient boolean quoted;
private transient boolean recycle = true;
private transient boolean stopThread;
private transient String[] vars;
private transient String alias;
private transient String shareMode;
private boolean firstLineIsNames = false;
private boolean ignoreFirstLine = false;
private final static String THREAD_SPLIT = " ";
private Object readResolve() {
recycle = true;
return this;
}
/**
* Override the setProperty method in order to convert
* the original String shareMode property.
* This used the locale-dependent display value, so caused
* problems when the language was changed.
* If the "shareMode" value matches a resource value then it is converted
* into the resource key.
* To reduce the need to look up resources, we only attempt to
* convert values with spaces in them, as these are almost certainly
* not variables (and they are definitely not resource keys).
*/
@Override
public void setProperty(JMeterProperty property) {
if (!(property instanceof StringProperty)) {
super.setProperty(property);
return;
}
final String propName = property.getName();
if (!"shareMode".equals(propName)) {
super.setProperty(property);
return;
}
final String propValue = property.getStringValue();
if (propValue.contains(" ")) { // variables are unlikely to contain spaces, so most likely a translation
try {
final BeanInfo beanInfo = Introspector.getBeanInfo(this.getClass());
final ResourceBundle rb = (ResourceBundle) beanInfo.getBeanDescriptor().getValue(GenericTestBeanCustomizer.RESOURCE_BUNDLE);
for (String resKey : CSVDataSetBeanInfo.getShareTags()) {
if (propValue.equals(rb.getString(resKey))) {
if (log.isDebugEnabled()) {
log.debug("Converted {}={} to {} using Locale: {}", propName, propValue, resKey, rb.getLocale());
}
((StringProperty) property).setValue(resKey); // reset the value
super.setProperty(property);
return;
}
}
// This could perhaps be a variable name
log.warn("Could not translate {}={} using Locale: {}", propName, propValue, rb.getLocale());
} catch (IntrospectionException e) {
LoggerUtil.error("Could not find BeanInfo; cannot translate shareMode entries", e);
}
}
super.setProperty(property);
}
@Override
public void iterationStart(LoopIterationEvent iterEvent) {
FileServer server = FileServer.getFileServer();
final JMeterContext context = getThreadContext();
String delim = getDelimiter();
if ("\\t".equals(delim)) { // $NON-NLS-1$
delim = "\t";// Make it easier to enter a Tab // $NON-NLS-1$
} else if (delim.isEmpty()) {
log.debug("Empty delimiter, will use ','");
delim = ",";
}
if (vars == null) {
initVars(server, context, delim);
}
// TODO: fetch this once as per vars above?
JMeterVariables threadVars = context.getVariables();
String[] lineValues = {};
try {
if (getQuotedData()) {
lineValues = server.getParsedLine(alias, recycle,
firstLineIsNames || ignoreFirstLine, delim.charAt(0));
} else {
String line = server.readLine(alias, recycle,
firstLineIsNames || ignoreFirstLine);
lineValues = JOrphanUtils.split(line, delim, false);
}
for (int a = 0; a < vars.length && a < lineValues.length; a++) {
threadVars.put(vars[a], lineValues[a]);
}
} catch (IOException e) { // treat the same as EOF
LoggerUtil.error(e.toString());
}
if (lineValues.length == 0) {// i.e. EOF
if (getStopThread()) {
throw new JMeterStopThreadException("End of file:" + getFilename() + " detected for CSV DataSet:"
+ getName() + " configured with stopThread:" + getStopThread() + ", recycle:" + getRecycle());
}
for (String var : vars) {
threadVars.put(var, EOFVALUE);
}
}
}
private void initVars(FileServer server, final JMeterContext context, String delim) {
String fileName = getFilename().trim();
LoggerUtil.info("初始化csv内容" + fileName);
setAlias(context, fileName);
final String names = getVariableNames();
if (StringUtils.isEmpty(names)) {
String header = server.reserveFile(fileName, getFileEncoding(), alias, true);
try {
vars = CSVSaveService.csvSplitString(header, delim.charAt(0));
firstLineIsNames = true;
} catch (IOException e) {
throw new IllegalArgumentException("Could not split CSV header line from file:" + fileName, e);
}
} else {
server.reserveFile(fileName, getFileEncoding(), alias, ignoreFirstLine);
vars = JOrphanUtils.split(names, ","); // $NON-NLS-1$
}
trimVarNames(vars);
}
private void setAlias(final JMeterContext context, String alias) {
String mode = getShareMode();
int modeInt = CSVDataSetBeanInfo.getShareModeAsInt(mode);
String threadName = StringUtils.substringBeforeLast(context.getThread().getThreadName(), THREAD_SPLIT);
switch (modeInt) {
case CSVDataSetBeanInfo.SHARE_ALL:
this.alias = alias;
break;
case CSVDataSetBeanInfo.SHARE_GROUP:
this.alias = alias + "@" + "GROUP_" + threadName + "@" + System.identityHashCode(context.getThreadGroup());
break;
case CSVDataSetBeanInfo.SHARE_THREAD:
this.alias = alias + "@" + threadName + "@" + System.identityHashCode(context.getThread());
break;
default:
this.alias = alias + "@" + mode; // user-specified key
break;
}
}
/**
* trim content of array varNames
* @param varsNames
*/
private void trimVarNames(String[] varsNames) {
for (int i = 0; i < varsNames.length; i++) {
varsNames[i] = varsNames[i].trim();
}
}
/**
* @return Returns the filename.
*/
public String getFilename() {
return filename;
}
/**
* @param filename The filename to set.
*/
public void setFilename(String filename) {
this.filename = filename;
}
/**
* @return Returns the file encoding.
*/
public String getFileEncoding() {
return fileEncoding;
}
/**
* @param fileEncoding
* The fileEncoding to set.
*/
public void setFileEncoding(String fileEncoding) {
this.fileEncoding = fileEncoding;
}
/**
* @return Returns the variableNames.
*/
public String getVariableNames() {
return variableNames;
}
/**
* @param variableNames
* The variableNames to set.
*/
public void setVariableNames(String variableNames) {
this.variableNames = variableNames;
}
public String getDelimiter() {
return delimiter;
}
public void setDelimiter(String delimiter) {
this.delimiter = delimiter;
}
public boolean getQuotedData() {
return quoted;
}
public void setQuotedData(boolean quoted) {
this.quoted = quoted;
}
public boolean getRecycle() {
return recycle;
}
public void setRecycle(boolean recycle) {
this.recycle = recycle;
}
public boolean getStopThread() {
return stopThread;
}
public void setStopThread(boolean value) {
this.stopThread = value;
}
public String getShareMode() {
return shareMode;
}
public void setShareMode(String value) {
this.shareMode = value;
}
/**
* @return the ignoreFirstLine
*/
public boolean isIgnoreFirstLine() {
return ignoreFirstLine;
}
/**
* @param ignoreFirstLine the ignoreFirstLine to set
*/
public void setIgnoreFirstLine(boolean ignoreFirstLine) {
this.ignoreFirstLine = ignoreFirstLine;
}
}

View File

@ -18,11 +18,11 @@
package org.apache.jmeter.services; package org.apache.jmeter.services;
import io.metersphere.utils.LoggerUtil; import io.metersphere.utils.LoggerUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.io.input.BOMInputStream; import org.apache.commons.io.input.BOMInputStream;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.gui.JMeterFileFilter; import org.apache.jmeter.gui.JMeterFileFilter;
import org.apache.jmeter.save.CSVSaveService; import org.apache.jmeter.save.CSVSaveService;
import org.apache.jmeter.threads.JMeterContextService;
import org.apache.jmeter.util.JMeterUtils; import org.apache.jmeter.util.JMeterUtils;
import org.apache.jorphan.util.JOrphanUtils; import org.apache.jorphan.util.JOrphanUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -30,7 +30,10 @@ import org.slf4j.LoggerFactory;
import java.io.*; import java.io.*;
import java.nio.file.Files; import java.nio.file.Files;
import java.util.*; import java.util.ArrayDeque;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.ThreadLocalRandom;
/** /**
@ -251,6 +254,16 @@ public class FileServer {
if (alias == null) { if (alias == null) {
throw new IllegalArgumentException("Alias must not be null"); throw new IllegalArgumentException("Alias must not be null");
} }
// todo 这里更改了原始代码
if(!new File(filename).exists()){
log.error("file does not exist [ "+ filename+" ]");
return "";
}
String threadName = JMeterContextService.getContext().getThread().getThreadName();
if (!StringUtils.contains(alias, threadName)) {
alias = StringUtils.join(threadName, alias);
}
FileEntry fileEntry = files.get(alias); FileEntry fileEntry = files.get(alias);
if (fileEntry == null) { if (fileEntry == null) {
fileEntry = new FileEntry(resolveFileFromPath(filename), null, charsetName); fileEntry = new FileEntry(resolveFileFromPath(filename), null, charsetName);
@ -282,6 +295,7 @@ public class FileServer {
* Resolves file name into {@link File} instance. * Resolves file name into {@link File} instance.
* When filename is not absolute and not found from current working dir, * When filename is not absolute and not found from current working dir,
* it tries to find it under current base directory * it tries to find it under current base directory
*
* @param filename original file name * @param filename original file name
* @return {@link File} instance * @return {@link File} instance
*/ */
@ -328,6 +342,11 @@ public class FileServer {
*/ */
public synchronized String readLine(String filename, boolean recycle, public synchronized String readLine(String filename, boolean recycle,
boolean ignoreFirstLine) throws IOException { boolean ignoreFirstLine) throws IOException {
String threadName = JMeterContextService.getContext().getThread().getThreadName();
if (!StringUtils.contains(filename, threadName)) {
filename = StringUtils.join(threadName, filename);
}
FileEntry fileEntry = files.get(filename); FileEntry fileEntry = files.get(filename);
if (fileEntry != null) { if (fileEntry != null) {
if (fileEntry.inputOutputObject == null) { if (fileEntry.inputOutputObject == null) {
@ -350,11 +369,13 @@ public class FileServer {
log.debug("Read:{}", line); log.debug("Read:{}", line);
return line; return line;
} }
throw new IOException("File never reserved: "+filename); // todo 这里更改了原始代码
//throw new IOException("File never reserved: " + filename);
log.error("File never reserved: " + filename);
return "";
} }
/** /**
*
* @param alias the file name or alias * @param alias the file name or alias
* @param recycle whether the file should be re-started on EOF * @param recycle whether the file should be re-started on EOF
* @param ignoreFirstLine whether the file contains a file header which will be ignored * @param ignoreFirstLine whether the file contains a file header which will be ignored
@ -377,6 +398,11 @@ public class FileServer {
* @return {@link BufferedReader} * @return {@link BufferedReader}
*/ */
private BufferedReader getReader(String alias, boolean recycle, boolean ignoreFirstLine) throws IOException { private BufferedReader getReader(String alias, boolean recycle, boolean ignoreFirstLine) throws IOException {
String threadName = JMeterContextService.getContext().getThread().getThreadName();
if (!StringUtils.contains(alias, threadName)) {
alias = StringUtils.join(threadName, alias);
}
FileEntry fileEntry = files.get(alias); FileEntry fileEntry = files.get(alias);
if (fileEntry != null) { if (fileEntry != null) {
BufferedReader reader; BufferedReader reader;
@ -433,6 +459,10 @@ public class FileServer {
} }
public synchronized void write(String filename, String value) throws IOException { public synchronized void write(String filename, String value) throws IOException {
String threadName = JMeterContextService.getContext().getThread().getThreadName();
if (!StringUtils.contains(filename, threadName)) {
filename = StringUtils.join(threadName, filename);
}
FileEntry fileEntry = files.get(filename); FileEntry fileEntry = files.get(filename);
if (fileEntry != null) { if (fileEntry != null) {
if (fileEntry.inputOutputObject == null) { if (fileEntry.inputOutputObject == null) {
@ -469,44 +499,31 @@ public class FileServer {
files.clear(); files.clear();
} }
/** public synchronized void closeFiles(String threadName) {
* 根据线程名字清理掉当前线程缓存的csv
*
* @param name 线程组名称
*/
public synchronized void closeCsv(String name) {
try { try {
if (StringUtils.isNotEmpty(name)) { for (Iterator<Map.Entry<String, FileEntry>> it = files.entrySet().iterator(); it.hasNext(); ) {
List<String> list = new ArrayList<>(); Map.Entry<String, FileEntry> entry = it.next();
for (Iterator<String> iterator = files.keySet().iterator(); iterator.hasNext(); ) { if (entry.getKey().startsWith(threadName)) {
String key = iterator.next(); closeFile(entry.getKey(), entry.getValue());
if (key.contains(name)) { it.remove();
FileEntry fileEntry = files.get(key);
closeFile(name, fileEntry);
list.add(key);
} }
} }
if (CollectionUtils.isNotEmpty(list)) { LoggerUtil.info("清除当前线程组占用文件后剩余文件数:" + files.size() + "", threadName);
for (String key : list) {
files.remove(key);
}
}
}
LoggerUtil.info("在处理中的CSV数量" + files.size());
} catch (Exception e) { } catch (Exception e) {
LoggerUtil.error("关闭CSV异常" + name, e); log.error("close files exception", e);
} }
} }
public int fileSize() {
return files.size();
}
/** /**
* @param name the name or alias of the file to be closed * @param name the name or alias of the file to be closed
* @throws IOException when closing of the aliased file fails * @throws IOException when closing of the aliased file fails
*/ */
public synchronized void closeFile(String name) throws IOException { public synchronized void closeFile(String name) throws IOException {
String threadName = JMeterContextService.getContext().getThread().getThreadName();
if (!StringUtils.contains(name, threadName)) {
name = StringUtils.join(threadName, name);
}
FileEntry fileEntry = files.get(name); FileEntry fileEntry = files.get(name);
closeFile(name, fileEntry); closeFile(name, fileEntry);
} }
@ -582,8 +599,7 @@ public class FileServer {
* "jmeter.save.saveservice.base_prefix" - default "~/" - then the name is * "jmeter.save.saveservice.base_prefix" - default "~/" - then the name is
* assumed to be relative to the basename. * assumed to be relative to the basename.
* *
* @param relativeName * @param relativeName filename that should be checked for
* filename that should be checked for
* <code>jmeter.save.saveservice.base_prefix</code> * <code>jmeter.save.saveservice.base_prefix</code>
* @return the updated filename * @return the updated filename
*/ */

View File

@ -32,6 +32,7 @@ import org.apache.jmeter.gui.GuiPackage;
import org.apache.jmeter.processor.PostProcessor; import org.apache.jmeter.processor.PostProcessor;
import org.apache.jmeter.processor.PreProcessor; import org.apache.jmeter.processor.PreProcessor;
import org.apache.jmeter.samplers.*; import org.apache.jmeter.samplers.*;
import org.apache.jmeter.services.FileServer;
import org.apache.jmeter.testbeans.TestBeanHelper; import org.apache.jmeter.testbeans.TestBeanHelper;
import org.apache.jmeter.testelement.*; import org.apache.jmeter.testelement.*;
import org.apache.jmeter.threads.JMeterContext.TestLogicalAction; import org.apache.jmeter.threads.JMeterContext.TestLogicalAction;
@ -334,6 +335,7 @@ public class JMeterThread implements Runnable, Interruptible {
threadFinished(iterationListener); threadFinished(iterationListener);
monitor.threadFinished(this); // Tell the monitor we are done monitor.threadFinished(this); // Tell the monitor we are done
JMeterContextService.removeContext(); // Remove the ThreadLocal entry JMeterContextService.removeContext(); // Remove the ThreadLocal entry
FileServer.getFileServer().closeFiles(threadName);
} finally { } finally {
interruptLock.unlock(); // Allow any pending interrupt to complete (OK because currentSampler == null) interruptLock.unlock(); // Allow any pending interrupt to complete (OK because currentSampler == null)
} }

View File

@ -2,6 +2,7 @@ package io.metersphere.environment.controller;
import com.github.pagehelper.Page; import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageHelper;
import io.metersphere.base.domain.ApiTestEnvironment;
import io.metersphere.base.domain.ApiTestEnvironmentWithBLOBs; import io.metersphere.base.domain.ApiTestEnvironmentWithBLOBs;
import io.metersphere.base.domain.EnvironmentGroup; import io.metersphere.base.domain.EnvironmentGroup;
import io.metersphere.commons.constants.OperLogConstants; import io.metersphere.commons.constants.OperLogConstants;
@ -45,6 +46,11 @@ public class TestEnvironmentController {
return baseEnvironmentService.list(projectId); return baseEnvironmentService.list(projectId);
} }
@PostMapping("/project-env")
public List<ApiTestEnvironment> projectEnv(@RequestBody List<String> projectIds) {
return baseEnvironmentService.selectList(projectIds);
}
/** /**
* 查询指定项目和指定名称的环境 * 查询指定项目和指定名称的环境
* *

View File

@ -370,6 +370,16 @@ public class BaseEnvironmentService extends NodeTreeService<ApiModuleDTO> {
return apiTestEnvironmentMapper.selectByExampleWithBLOBs(example); return apiTestEnvironmentMapper.selectByExampleWithBLOBs(example);
} }
public List<ApiTestEnvironment> selectList(List<String> projectIds) {
if(CollectionUtils.isEmpty(projectIds)){
return new ArrayList<>();
}
ApiTestEnvironmentExample example = new ApiTestEnvironmentExample();
example.createCriteria().andProjectIdIn(projectIds);
return apiTestEnvironmentMapper.selectByExample(example);
}
public ApiTestEnvironmentWithBLOBs get(String id) { public ApiTestEnvironmentWithBLOBs get(String id) {
return apiTestEnvironmentMapper.selectByPrimaryKey(id); return apiTestEnvironmentMapper.selectByPrimaryKey(id);
} }

View File

@ -1,21 +1,41 @@
<template> <template>
<div v-loading="result.loading"> <div v-loading="result.loading">
<div v-for="pe in data" :key="pe.id" style="margin-left: 20px;"> <div v-for="pe in data" :key="pe.id" style="margin-left: 20px">
<el-select v-model="pe['selectEnv']" filterable :placeholder="$t('workspace.env_group.please_select_env')" <el-select
style="margin-top: 8px;width: 200px;" size="small"> v-model="pe['selectEnv']"
<el-option v-for="(environment, index) in pe.envs" :key="index" filterable
:placeholder="$t('workspace.env_group.please_select_env')"
style="margin-top: 8px; width: 200px"
size="small"
>
<el-option
v-for="(environment, index) in pe.envs"
:key="index"
:label="environment.name" :label="environment.name"
:value="environment.id"/> :value="environment.id"
<el-button class="ms-scenario-button" v-if="isShowConfirmButton(pe.id)" size="mini" type="primary" />
@click="openEnvironmentConfig(pe.id, pe['selectEnv'])"> <el-button
{{ $t('api_test.environment.environment_config') }} class="ms-scenario-button"
v-if="isShowConfirmButton(pe.id)"
size="mini"
type="primary"
@click="openEnvironmentConfig(pe.id, pe['selectEnv'])"
>
{{ $t("api_test.environment.environment_config") }}
</el-button> </el-button>
<template v-slot:empty> <template v-slot:empty>
<!--这里只做没有可搜索内容时使用否则如果没有符合搜索条件的也会显示该项与上面的btn重复显示 --> <!--这里只做没有可搜索内容时使用否则如果没有符合搜索条件的也会显示该项与上面的btn重复显示 -->
<div v-if="isShowConfirmButton(pe.id) && pe.envs.length===0" class="empty-environment"> <div
<el-button class="ms-scenario-button" size="mini" type="primary" v-if="isShowConfirmButton(pe.id) && pe.envs.length === 0"
@click="openEnvironmentConfig(pe.id, pe['selectEnv'])"> class="empty-environment"
{{ $t('api_test.environment.environment_config') }} >
<el-button
class="ms-scenario-button"
size="mini"
type="primary"
@click="openEnvironmentConfig(pe.id, pe['selectEnv'])"
>
{{ $t("api_test.environment.environment_config") }}
</el-button> </el-button>
</div> </div>
</template> </template>
@ -25,10 +45,19 @@
</span> </span>
</div> </div>
<el-button type="primary" @click="handleConfirm" size="small" class="env-confirm">{{$t('workspace.env_group.confirm')}}</el-button> <el-button
type="primary"
@click="handleConfirm"
size="small"
class="env-confirm"
>{{ $t("workspace.env_group.confirm") }}</el-button
>
<!-- 环境配置 --> <!-- 环境配置 -->
<api-environment-config ref="environmentConfig" @close="environmentConfigClose"/> <api-environment-config
ref="environmentConfig"
@close="environmentConfigClose"
/>
</div> </div>
</template> </template>
@ -49,14 +78,14 @@ export default {
type: Boolean, type: Boolean,
default() { default() {
return true; return true;
} },
}, },
result: { result: {
type: Object, type: Object,
default() { default() {
return {loading: false} return { loading: false };
} },
} },
}, },
data() { data() {
return { return {
@ -66,7 +95,7 @@ export default {
permissionProjectIds: [], permissionProjectIds: [],
dialogVisible: false, dialogVisible: false,
isFullUrl: true, isFullUrl: true,
} };
}, },
methods: { methods: {
isShowConfirmButton(projectId) { isShowConfirmButton(projectId) {
@ -91,38 +120,39 @@ export default {
} }
let arr = []; let arr = [];
this.projectIds.forEach(id => { this.projectIds.forEach((id) => {
const project = this.projectList.find(p => p.id === id); const project = this.projectList.find((p) => p.id === id);
if (project) { if (project) {
let item = { id: id, envs: [], selectEnv: "" }; let item = { id: id, envs: [], selectEnv: "" };
this.data.push(item); this.data.push(item);
let p = new Promise(resolve => { let p = new Promise((resolve) => {
getEnvironmentByProjectId(id).then(res => { getEnvironmentByProjectId(id).then((res) => {
let envs = res.data; let envs = res.data;
envs.forEach(environment => { envs.forEach((environment) => {
parseEnvironment(environment); parseEnvironment(environment);
}); });
// //
let temp = this.data.find(dt => dt.id === id); let temp = this.data.find((dt) => dt.id === id);
temp.envs = envs; temp.envs = envs;
let envId = undefined; let envId = undefined;
if (this.envMap) { if (this.envMap) {
envId = this.envMap.get(id); envId = this.envMap.get(id);
} }
// //
temp.selectEnv = envs.filter(e => e.id === envId).length === 0 ? null : envId; temp.selectEnv =
envs.filter((e) => e.id === envId).length === 0 ? null : envId;
resolve(); resolve();
}) });
}) });
arr.push(p); arr.push(p);
} }
}) });
return arr; return arr;
}, },
getUserPermissionProjectIds() { getUserPermissionProjectIds() {
getOwnerProjectIds().then(res => { getOwnerProjectIds().then((res) => {
this.permissionProjectIds = res.data; this.permissionProjectIds = res.data;
}) });
}, },
open() { open() {
this.data = []; this.data = [];
@ -135,12 +165,12 @@ export default {
return Promise.all(this.init()); return Promise.all(this.init());
}, },
getProjectName(id) { getProjectName(id) {
const project = this.projectList.find(p => p.id === id); const project = this.projectList.find((p) => p.id === id);
return project ? project.name : ""; return project ? project.name : "";
}, },
openEnvironmentConfig(projectId, envId) { openEnvironmentConfig(projectId, envId) {
if (!projectId) { if (!projectId) {
this.$error(this.$t('api_test.select_project')); this.$error(this.$t("api_test.select_project"));
return; return;
} }
this.$refs.environmentConfig.open(projectId, envId); this.$refs.environmentConfig.open(projectId, envId);
@ -148,19 +178,15 @@ export default {
handleConfirm() { handleConfirm() {
let map = new Map(); let map = new Map();
let sign = true; let sign = true;
this.data.forEach(dt => { this.data.forEach((dt) => {
if (!dt.selectEnv) { if (!dt.selectEnv) {
sign = false; sign = false;
return; return;
} }
map.set(dt.id, dt.selectEnv); map.set(dt.id, dt.selectEnv);
}) });
if (!sign) { this.$emit("setProjectEnvMap", map);
this.$warning(this.$t('workspace.env_group.please_select_env_for_current_scenario')); this.$emit("close");
return;
}
this.$emit('setProjectEnvMap', map);
this.$emit('close');
}, },
checkEnv(data) { checkEnv(data) {
let sign = true; let sign = true;
@ -169,35 +195,30 @@ export default {
return true; return true;
} }
if (this.data.length > 0) { if (this.data.length > 0) {
this.data.forEach(dt => { this.data.forEach((dt) => {
if (!dt.selectEnv) { if (!dt.selectEnv) {
sign = false; sign = false;
return false; return false;
} }
}) });
} else { } else {
// //
if (this.envMap && this.envMap.size > 0) { if (this.envMap && this.envMap.size > 0) {
this.projectIds.forEach(id => { this.projectIds.forEach((id) => {
if (!this.envMap.get(id)) { if (!this.envMap.get(id)) {
sign = false; sign = false;
return false; return false;
} }
}) });
} }
} }
if (!sign) {
this.$warning(this.$t('workspace.env_group.please_select_env_for_current_scenario'));
return false;
}
return true; return true;
}, },
environmentConfigClose() { environmentConfigClose() {
// todo // todo
} },
} },
} };
</script> </script>
<style scoped> <style scoped>