refactor(接口测试): 支持跨项目场景可以走当前项目环境执行
Signed-off-by: fit2-zhao <yong.zhao@fit2cloud.com>
This commit is contained in:
parent
9cb5163212
commit
a85a59475f
|
@ -8,7 +8,7 @@ import java.util.Map;
|
|||
@Getter
|
||||
@Setter
|
||||
public class ApiScenarioEnvRequest {
|
||||
|
||||
private String projectId;
|
||||
private String definition;
|
||||
private String environmentType;
|
||||
private String environmentGroupId;
|
||||
|
|
|
@ -8,7 +8,7 @@ import java.util.Set;
|
|||
|
||||
@Getter
|
||||
@Setter
|
||||
public class ScenarioEnv {
|
||||
public class EnvironmentCheckDTO {
|
||||
|
||||
private Set<String> projectIds = new HashSet<>();
|
||||
private Boolean fullUrl = true;
|
|
@ -49,6 +49,7 @@ import io.metersphere.utils.LoggerUtil;
|
|||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.jmeter.config.Arguments;
|
||||
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 TEST_BEAN_GUI = "TestBeanGUI";
|
||||
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>() {{
|
||||
this.add(ElementConstants.JSR223);
|
||||
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 {
|
||||
ApiScenarioMapper apiScenarioMapper = CommonBeanFactory.getBean(ApiScenarioMapper.class);
|
||||
BaseEnvGroupProjectService environmentGroupProjectService = CommonBeanFactory.getBean(BaseEnvGroupProjectService.class);
|
||||
|
@ -358,7 +359,7 @@ public class ElementUtil {
|
|||
for (int i = 0; i < hashTree.length(); i++) {
|
||||
JSONObject element = hashTree.optJSONObject(i);
|
||||
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)) {
|
||||
MsScenario scenario = JSON.parseObject(element.toString(), MsScenario.class);
|
||||
if (scenario.isEnvironmentEnable()) {
|
||||
|
@ -829,10 +830,10 @@ public class ElementUtil {
|
|||
}
|
||||
// 环境通用变量
|
||||
if (config.isEffective(projectId)
|
||||
&& config.getConfig().get(projectId).getCommonConfig() != null
|
||||
&& CollectionUtils.isNotEmpty(config.getConfig().get(projectId).getCommonConfig().getVariables())) {
|
||||
&& config.get(projectId).getCommonConfig() != null
|
||||
&& 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::isEnable)
|
||||
.collect(Collectors.toList());
|
||||
|
@ -844,7 +845,7 @@ public class ElementUtil {
|
|||
? keyValue.getValue().replaceAll("[\r\n]", "")
|
||||
: keyValue.getValue()), "="));
|
||||
// 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::isEnable)
|
||||
.collect(Collectors.toList());
|
||||
|
@ -855,8 +856,8 @@ public class ElementUtil {
|
|||
}
|
||||
});
|
||||
// 清空变量,防止重复添加
|
||||
config.getConfig().get(projectId).getCommonConfig().getVariables().removeAll(constants);
|
||||
config.getConfig().get(projectId).getCommonConfig().getVariables().removeAll(variableList);
|
||||
config.get(projectId).getCommonConfig().getVariables().removeAll(constants);
|
||||
config.get(projectId).getCommonConfig().getVariables().removeAll(variableList);
|
||||
}
|
||||
|
||||
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) {
|
||||
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,
|
||||
config.getConfig().get(projectId).getCommonConfig().getVariables(),
|
||||
config.get(projectId).getCommonConfig().getVariables(),
|
||||
config, "shareMode.group");
|
||||
ElementUtil.addCounter(httpSamplerTree,
|
||||
config.getConfig().get(projectId).getCommonConfig().getVariables());
|
||||
config.get(projectId).getCommonConfig().getVariables());
|
||||
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 (config.getConfig() != null) {
|
||||
if (StringUtils.isNotBlank(projectId) && config.getConfig().containsKey(projectId)) {
|
||||
return config.getConfig().get(projectId).getEnvironmentId();
|
||||
return config.get(projectId).getEnvironmentId();
|
||||
} else {
|
||||
if (CollectionUtils.isNotEmpty(config.getConfig().values())) {
|
||||
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));
|
||||
|
||||
ElementUtil.setBaseParams(jdbcProcessor, vo.getParent(), config, vo.getId(), vo.getIndex());
|
||||
if (ObjectUtils.isNotEmpty(vo.getDataSource())) {
|
||||
jdbcProcessor.setDataSource(ElementUtil.getDataSourceName(vo.getDataSource().getName()));
|
||||
}
|
||||
jdbcProcessor.setProperty("dataSource", jdbcProcessor.getDataSource());
|
||||
jdbcProcessor.setProperty("query", vo.getQuery());
|
||||
jdbcProcessor.setProperty("queryTimeout", String.valueOf(vo.getQueryTimeout()));
|
||||
|
@ -1015,6 +1018,9 @@ public class ElementUtil {
|
|||
}
|
||||
|
||||
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.setEnabled(true);
|
||||
dataSourceElement.setName(sourceName + " JDBCDataSource");
|
||||
|
@ -1023,7 +1029,7 @@ public class ElementUtil {
|
|||
dataSourceElement.setProperty("autocommit", true);
|
||||
dataSourceElement.setProperty("keepAlive", true);
|
||||
dataSourceElement.setProperty("preinit", false);
|
||||
dataSourceElement.setProperty("dataSource", sourceName);
|
||||
dataSourceElement.setProperty("dataSource", StringUtils.defaultIfBlank(sourceName, MS_DEFAULT));
|
||||
dataSourceElement.setProperty("dbUrl", dataSource.getDbUrl());
|
||||
dataSourceElement.setProperty("driver", dataSource.getDriver());
|
||||
dataSourceElement.setProperty("username", dataSource.getUsername());
|
||||
|
@ -1152,15 +1158,15 @@ public class ElementUtil {
|
|||
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) {
|
||||
return null;
|
||||
}
|
||||
DatabaseConfig dataSource = null;
|
||||
// 自选了数据源
|
||||
if (config.isEffective(projectId) && CollectionUtils.isNotEmpty(config.getConfig().get(projectId).getDatabaseConfigs())
|
||||
&& isDataSource(dataSourceId, config.getConfig().get(projectId).getDatabaseConfigs())) {
|
||||
EnvironmentConfig environmentConfig = config.getConfig().get(projectId);
|
||||
if (config.isEffective(projectId) && CollectionUtils.isNotEmpty(config.get(projectId).getDatabaseConfigs())
|
||||
&& isDataSource(dataSourceId, config.get(projectId).getDatabaseConfigs())) {
|
||||
EnvironmentConfig environmentConfig = config.get(projectId);
|
||||
if (environmentConfig.getDatabaseConfigs() != null && StringUtils.isNotEmpty(environmentConfig.getEnvironmentId())) {
|
||||
environmentId = environmentConfig.getEnvironmentId();
|
||||
}
|
||||
|
@ -1170,14 +1176,14 @@ public class ElementUtil {
|
|||
}
|
||||
} 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 + ":开始获取当前环境下默认数据源");
|
||||
DatabaseConfig dataSourceOrg = ElementUtil.dataSource(projectId, dataSourceId, config.getConfig().get(projectId));
|
||||
DatabaseConfig dataSourceOrg = ElementUtil.dataSource(projectId, dataSourceId, config.get(projectId));
|
||||
if (dataSourceOrg != null) {
|
||||
dataSource = dataSourceOrg;
|
||||
} else {
|
||||
LoggerUtil.info(processorName + ":获取当前环境下默认数据源结束!未查找到默认数据源");
|
||||
dataSource = config.getConfig().get(projectId).getDatabaseConfigs().get(0);
|
||||
dataSource = config.get(projectId).getDatabaseConfigs().get(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import io.metersphere.utils.LoggerUtil;
|
|||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.jmeter.config.Arguments;
|
||||
|
@ -78,7 +79,7 @@ public class MsScenario extends MsTestElement {
|
|||
// 设置共享cookie
|
||||
config.setEnableCookieShare(enableCookieShare);
|
||||
Map<String, EnvironmentConfig> envConfig = new HashMap<>(16);
|
||||
if (config.getConfig() == null) {
|
||||
if (MapUtils.isEmpty(config.getConfig())) {
|
||||
// 兼容历史数据
|
||||
if (this.environmentMap == null || this.environmentMap.isEmpty()) {
|
||||
this.environmentMap = new HashMap<>(16);
|
||||
|
@ -104,18 +105,18 @@ public class MsScenario extends MsTestElement {
|
|||
}
|
||||
HashTree scenarioTree = tree;
|
||||
// 取出自身场景环境
|
||||
ParameterConfig newConfig = new ParameterConfig();
|
||||
ParameterConfig newConfig = new ParameterConfig(this.getProjectId(), false);
|
||||
if (this.isEnvironmentEnable()) {
|
||||
this.setNewConfig(envConfig, newConfig);
|
||||
newConfig.setRetryNum(config.getRetryNum());
|
||||
}
|
||||
if (config != null && StringUtils.equals(this.getId(), config.getScenarioId())) {
|
||||
if (StringUtils.equals(this.getId(), config.getScenarioId())) {
|
||||
config.setTransferVariables(this.variables);
|
||||
if (CollectionUtils.isNotEmpty(this.headers)) {
|
||||
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());
|
||||
}
|
||||
// 启用当前场景变量优先选择
|
||||
|
@ -130,7 +131,7 @@ public class MsScenario extends MsTestElement {
|
|||
// 这里加入自定义变量解决ForEach循环控制器取值问题,循环控制器无法从vars中取值
|
||||
if (BooleanUtils.isTrue(this.variableEnable) || BooleanUtils.isTrue(this.mixEnable)) {
|
||||
scenarioTree.add(ElementUtil.argumentsToUserParameters(valueSupposeMock));
|
||||
} else if (config != null && (this.isAllEnable() || config.isApi())) {
|
||||
} else if (this.isAllEnable() || config.isApi()) {
|
||||
valueSupposeMock.setProperty(ElementConstants.COVER, true);
|
||||
scenarioTree.add(valueSupposeMock);
|
||||
}
|
||||
|
@ -188,7 +189,7 @@ public class MsScenario extends MsTestElement {
|
|||
if (envProcessor != null) {
|
||||
BeanUtils.copyBean(processor, envProcessor);
|
||||
}
|
||||
if (processor != null && StringUtils.isNotEmpty(processor.getScript())) {
|
||||
if (StringUtils.isNotEmpty(processor.getScript())) {
|
||||
processor.setType(ElementConstants.JSR223);
|
||||
processor.setClazzName(MsJSR223Processor.class.getCanonicalName());
|
||||
boolean isConnScenarioPre = false;
|
||||
|
|
|
@ -22,6 +22,7 @@ import io.metersphere.service.definition.ApiTestCaseService;
|
|||
import io.metersphere.service.plan.TestPlanApiCaseService;
|
||||
import lombok.Data;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.jmeter.config.Arguments;
|
||||
|
||||
|
@ -30,15 +31,19 @@ import java.util.stream.Collectors;
|
|||
|
||||
@Data
|
||||
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;
|
||||
|
||||
/**
|
||||
* UI 指令全局配置
|
||||
*/
|
||||
private Object commandConfig;
|
||||
/**
|
||||
* 缓存同一批请求的认证信息
|
||||
*/
|
||||
|
@ -61,10 +66,6 @@ public class ParameterConfig extends MsParameter {
|
|||
* 公共Cookie
|
||||
*/
|
||||
private boolean enableCookieShare;
|
||||
/**
|
||||
* 是否停止继续
|
||||
*/
|
||||
private Boolean onSampleError;
|
||||
|
||||
/**
|
||||
* 是否是导入/导出操作
|
||||
|
@ -74,13 +75,12 @@ public class ParameterConfig extends MsParameter {
|
|||
* 导入/导出操作时取样器的testName值
|
||||
*/
|
||||
private String operatingSampleTestName;
|
||||
/**
|
||||
* 项目ID,支持单接口执行
|
||||
*/
|
||||
private String projectId;
|
||||
|
||||
private String scenarioId;
|
||||
|
||||
/**
|
||||
* 当前项目id
|
||||
*/
|
||||
private String currentProjectId;
|
||||
/**
|
||||
* 报告 ID
|
||||
*/
|
||||
|
@ -90,7 +90,6 @@ public class ParameterConfig extends MsParameter {
|
|||
|
||||
private boolean runLocal;
|
||||
|
||||
private String browserLanguage;
|
||||
private boolean isApi;
|
||||
/**
|
||||
* 失败重试次数
|
||||
|
@ -104,7 +103,8 @@ public class ParameterConfig extends MsParameter {
|
|||
private List<String> csvFilePaths = new ArrayList<>();
|
||||
|
||||
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 false;
|
||||
|
@ -119,8 +119,7 @@ public class ParameterConfig extends MsParameter {
|
|||
}
|
||||
|
||||
|
||||
public HttpConfig matchConfig(MsHTTPSamplerProxy samplerProxy) {
|
||||
HttpConfig httpConfig = this.getConfig().get(samplerProxy.getProjectId()).getHttpConfig();
|
||||
public HttpConfig matchConfig(MsHTTPSamplerProxy samplerProxy, HttpConfig httpConfig) {
|
||||
boolean isNext = true;
|
||||
if (CollectionUtils.isNotEmpty(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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ public class MsAuthManager extends MsTestElement {
|
|||
} else {
|
||||
if (config != null && 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.KeyValue;
|
||||
import io.metersphere.commons.constants.ElementConstants;
|
||||
import io.metersphere.commons.exception.MSException;
|
||||
import io.metersphere.commons.utils.BeanUtils;
|
||||
import io.metersphere.commons.utils.JSONUtil;
|
||||
import io.metersphere.commons.vo.JDBCProcessorVO;
|
||||
|
@ -16,6 +15,7 @@ import io.metersphere.utils.LoggerUtil;
|
|||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.jmeter.protocol.jdbc.processor.JDBCPostProcessor;
|
||||
import org.apache.jorphan.collections.HashTree;
|
||||
|
@ -57,22 +57,20 @@ public class MsJDBCPostProcessor extends MsTestElement {
|
|||
if (StringUtils.isBlank(this.getProjectId()) && this.getParent() != null) {
|
||||
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()));
|
||||
}
|
||||
|
||||
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 (StringUtils.isNotEmpty(dataSourceId)) {
|
||||
this.dataSource = ElementUtil.initDataSource(this.environmentId, this.dataSourceId);
|
||||
}
|
||||
if (this.dataSource == null) {
|
||||
LoggerUtil.info(this.getName() + ": 当前项目id", this.getProjectId() + " 当前环境配置信息", JSONUtil.toJSONString(config));
|
||||
String message = "数据源为空请选择数据源";
|
||||
MSException.throwException(StringUtils.isNotEmpty(this.getName()) ? this.getName() + ":" + message : message);
|
||||
LoggerUtil.error(this.getName() + ",未找到数据源", JSONUtil.toJSONString(config));
|
||||
}
|
||||
}
|
||||
JDBCPostProcessor jdbcPostProcessor = jdbcPostProcessor(config);
|
||||
|
|
|
@ -17,6 +17,7 @@ import io.metersphere.utils.LoggerUtil;
|
|||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.jmeter.protocol.jdbc.processor.JDBCPreProcessor;
|
||||
import org.apache.jorphan.collections.HashTree;
|
||||
|
@ -57,21 +58,19 @@ public class MsJDBCPreProcessor extends MsTestElement {
|
|||
if (StringUtils.isBlank(this.getProjectId()) && this.getParent() != null) {
|
||||
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()));
|
||||
}
|
||||
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 (StringUtils.isNotEmpty(dataSourceId)) {
|
||||
this.dataSource = ElementUtil.initDataSource(this.environmentId, this.dataSourceId);
|
||||
}
|
||||
if (this.dataSource == null) {
|
||||
LoggerUtil.info(this.getName() + " 当前项目id", this.getProjectId() + " 当前环境配置信息", JSONUtil.toJSONString(config));
|
||||
String message = "数据源为空请选择数据源";
|
||||
MSException.throwException(StringUtils.isNotEmpty(this.getName()) ? this.getName() + ":" + message : message);
|
||||
LoggerUtil.error(this.getName() + ",未找到数据源", JSONUtil.toJSONString(config));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -90,7 +90,7 @@ public class MsDubboSampler extends MsTestElement {
|
|||
//添加全局前后置脚本
|
||||
EnvironmentConfig envConfig = null;
|
||||
if (config.getConfig() != null) {
|
||||
envConfig = config.getConfig().get(this.getProjectId());
|
||||
envConfig = config.get(this.getProjectId());
|
||||
}
|
||||
//处理全局前后置脚本(步骤内)
|
||||
String environmentId = this.getEnvironmentId();
|
||||
|
|
|
@ -31,13 +31,16 @@ import io.metersphere.jmeter.utils.ScriptEngineUtils;
|
|||
import io.metersphere.plugin.core.MsParameter;
|
||||
import io.metersphere.plugin.core.MsTestElement;
|
||||
import io.metersphere.service.definition.ApiTestCaseService;
|
||||
import io.metersphere.utils.LoggerUtil;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.jmeter.config.Arguments;
|
||||
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.util.HTTPArgument;
|
||||
import org.apache.jmeter.protocol.http.util.HTTPConstants;
|
||||
|
@ -134,10 +137,10 @@ public class MsHTTPSamplerProxy extends MsTestElement {
|
|||
sampler.setImplementation(this.getImplementation());
|
||||
sampler.setUseKeepAlive(true);
|
||||
sampler.setDoMultipart(this.isDoMultipartPost());
|
||||
if (config.getConfig() == null) {
|
||||
if (MapUtils.isEmpty(config.getConfig()) && config.isApi()) {
|
||||
// 单独接口执行
|
||||
if (StringUtils.isNotEmpty(config.getProjectId())) {
|
||||
this.setProjectId(config.getProjectId());
|
||||
if (StringUtils.isNotEmpty(config.getCurrentProjectId())) {
|
||||
this.setProjectId(config.getCurrentProjectId());
|
||||
}
|
||||
String projectId = this.getProjectId();
|
||||
config.setConfig(ElementUtil.getEnvironmentConfig(this.useEnvironment, projectId));
|
||||
|
@ -166,7 +169,7 @@ public class MsHTTPSamplerProxy extends MsTestElement {
|
|||
}
|
||||
if (CollectionUtils.isNotEmpty(bodyParams)) {
|
||||
Arguments arguments = httpArguments(bodyParams);
|
||||
if (arguments != null && !arguments.getArguments().isEmpty()) {
|
||||
if (!arguments.getArguments().isEmpty()) {
|
||||
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());
|
||||
}
|
||||
// 环境通用请求头
|
||||
|
@ -216,10 +219,10 @@ public class MsHTTPSamplerProxy extends MsTestElement {
|
|||
//添加csv
|
||||
ElementUtil.addApiVariables(config, httpSamplerTree, this.getProjectId());
|
||||
//判断是否要开启DNS
|
||||
if (config.isEffective(this.getProjectId()) && config.getConfig().get(this.getProjectId()).getCommonConfig() != null
|
||||
&& config.getConfig().get(this.getProjectId()).getCommonConfig().isEnableHost()) {
|
||||
MsDNSCacheManager.addEnvironmentVariables(httpSamplerTree, this.getName(), config.getConfig().get(this.getProjectId()));
|
||||
MsDNSCacheManager.addEnvironmentDNS(httpSamplerTree, this.getName(), config.getConfig().get(this.getProjectId()), httpConfig);
|
||||
if (config.isEffective(this.getProjectId()) && config.get(this.getProjectId()).getCommonConfig() != null
|
||||
&& config.get(this.getProjectId()).getCommonConfig().isEnableHost()) {
|
||||
MsDNSCacheManager.addEnvironmentVariables(httpSamplerTree, this.getName(), config.get(this.getProjectId()));
|
||||
MsDNSCacheManager.addEnvironmentDNS(httpSamplerTree, this.getName(), config.get(this.getProjectId()), httpConfig);
|
||||
}
|
||||
if (this.authManager != null && MsAuthManager.mechanismMap.containsKey(this.authManager.getVerification())) {
|
||||
this.authManager.setAuth(httpSamplerTree, this.authManager, sampler);
|
||||
|
@ -295,11 +298,11 @@ public class MsHTTPSamplerProxy extends MsTestElement {
|
|||
|
||||
private void initConnectAndResponseTimeout(ParameterConfig config) {
|
||||
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())) {
|
||||
this.setEnvironmentId(useEvnId);
|
||||
}
|
||||
CommonConfig commonConfig = config.getConfig().get(this.getProjectId()).getCommonConfig();
|
||||
CommonConfig commonConfig = config.get(this.getProjectId()).getCommonConfig();
|
||||
if (commonConfig != null) {
|
||||
if (this.getConnectTimeout() == null || StringUtils.equals(this.getConnectTimeout(), DEF_TIME_OUT)) {
|
||||
if (commonConfig.getRequestTimeout() != 0) {
|
||||
|
@ -316,46 +319,47 @@ public class MsHTTPSamplerProxy extends MsTestElement {
|
|||
}
|
||||
|
||||
private EnvironmentConfig getEnvironmentConfig(ParameterConfig config) {
|
||||
return config.getConfig().get(this.getProjectId());
|
||||
return config.get(this.getProjectId());
|
||||
}
|
||||
|
||||
private HttpConfig getHttpConfig(ParameterConfig config) {
|
||||
if (config.isEffective(this.getProjectId())) {
|
||||
EnvironmentConfig environmentConfig = config.getConfig().get(this.getProjectId());
|
||||
if (environmentConfig != null) {
|
||||
String useEvnId = environmentConfig.getEnvironmentId();
|
||||
if (this.authManager == null && environmentConfig.getAuthManager() != null && environmentConfig.getAuthManager().getAuthManager() != null) {
|
||||
if (config.isEffective(this.getProjectId()) || config.isEffective(config.getCurrentProjectId())) {
|
||||
EnvironmentConfig envConfig = config.get(this.getProjectId());
|
||||
if (envConfig != null) {
|
||||
String useEnvId = envConfig.getEnvironmentId();
|
||||
if (this.authManager == null && envConfig.getAuthManager() != null
|
||||
&& envConfig.getAuthManager().getAuthManager() != null) {
|
||||
MsAuthManager authManager = new MsAuthManager();
|
||||
BeanUtils.copyBean(authManager, environmentConfig.getAuthManager().getAuthManager());
|
||||
BeanUtils.copyBean(authManager, envConfig.getAuthManager().getAuthManager());
|
||||
this.authManager = authManager;
|
||||
}
|
||||
if (StringUtils.isNotEmpty(useEvnId) && !StringUtils.equals(useEvnId, this.getEnvironmentId())) {
|
||||
this.setEnvironmentId(useEvnId);
|
||||
if (StringUtils.isNotEmpty(useEnvId) && !StringUtils.equals(useEnvId, this.getEnvironmentId())) {
|
||||
this.setEnvironmentId(useEnvId);
|
||||
}
|
||||
HttpConfig httpConfig = config.matchConfig(this);
|
||||
if (environmentConfig.getPreProcessor() != null) {
|
||||
HttpConfig httpConfig = config.matchConfig(this, envConfig.getHttpConfig());
|
||||
if (envConfig.getPreProcessor() != null) {
|
||||
MsJSR223PreProcessor msJSR223PreProcessor = new MsJSR223PreProcessor();
|
||||
if (environmentConfig.getPreProcessor() != null) {
|
||||
BeanUtils.copyBean(msJSR223PreProcessor, environmentConfig.getPreProcessor());
|
||||
if (envConfig.getPreProcessor() != null) {
|
||||
BeanUtils.copyBean(msJSR223PreProcessor, envConfig.getPreProcessor());
|
||||
httpConfig.setPreProcessor(msJSR223PreProcessor);
|
||||
}
|
||||
}
|
||||
if (environmentConfig.getPostProcessor() != null) {
|
||||
if (envConfig.getPostProcessor() != null) {
|
||||
MsJSR223PostProcessor postProcessor = new MsJSR223PostProcessor();
|
||||
if (environmentConfig.getPostProcessor() != null) {
|
||||
BeanUtils.copyBean(postProcessor, environmentConfig.getPostProcessor());
|
||||
if (envConfig.getPostProcessor() != null) {
|
||||
BeanUtils.copyBean(postProcessor, envConfig.getPostProcessor());
|
||||
httpConfig.setPostProcessor(postProcessor);
|
||||
}
|
||||
}
|
||||
httpConfig.setGlobalScriptConfig(environmentConfig.getGlobalScriptConfig());
|
||||
if (CollectionUtils.isNotEmpty(environmentConfig.getAssertions())) {
|
||||
httpConfig.setAssertions(ElementUtil.copyAssertion(environmentConfig.getAssertions()));
|
||||
httpConfig.setGlobalScriptConfig(envConfig.getGlobalScriptConfig());
|
||||
if (CollectionUtils.isNotEmpty(envConfig.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.setHigherThanError(environmentConfig.isHigherThanError());
|
||||
fakeError.setHigherThanError(envConfig.isHigherThanError());
|
||||
fakeError.setProjectId(this.getProjectId());
|
||||
fakeError.setHigherThanSuccess(environmentConfig.isHigherThanSuccess());
|
||||
fakeError.setHigherThanSuccess(envConfig.isHigherThanSuccess());
|
||||
httpConfig.setFakeError(fakeError);
|
||||
}
|
||||
return httpConfig;
|
||||
|
@ -367,14 +371,8 @@ public class MsHTTPSamplerProxy extends MsTestElement {
|
|||
private void setSamplerPath(ParameterConfig config, HttpConfig httpConfig, HTTPSamplerProxy sampler) {
|
||||
try {
|
||||
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)) {
|
||||
this.useEnvironment = config.getConfig().get(this.getProjectId()).getEnvironmentId();
|
||||
this.useEnvironment = config.get(this.getProjectId()).getEnvironmentId();
|
||||
}
|
||||
String url = httpConfig.getProtocol() + "://" + httpConfig.getSocket();
|
||||
if (isUrl()) {
|
||||
|
@ -386,7 +384,7 @@ public class MsHTTPSamplerProxy extends MsTestElement {
|
|||
String envPath = sampler.getPath();
|
||||
if (CollectionUtils.isNotEmpty(this.getRest()) && this.isRest()) {
|
||||
envPath = getRestParameters(envPath);
|
||||
sampler.setProperty("HTTPSampler.path", envPath);
|
||||
sampler.setProperty(HTTPSamplerBase.PATH, envPath);
|
||||
}
|
||||
if (CollectionUtils.isNotEmpty(this.getArguments())) {
|
||||
String path = postQueryParameters(envPath);
|
||||
|
@ -395,34 +393,30 @@ public class MsHTTPSamplerProxy extends MsTestElement {
|
|||
path = "/" + path;
|
||||
}
|
||||
}
|
||||
sampler.setProperty("HTTPSampler.path", path);
|
||||
sampler.setProperty(HTTPSamplerBase.PATH, path);
|
||||
}
|
||||
} else {
|
||||
String url = this.getUrl();
|
||||
if (StringUtils.isEmpty(url)) {
|
||||
MSException.throwException("当前步骤:" + this.getName() + " 环境为空,请重新选择环境");
|
||||
}
|
||||
String envPath = "";
|
||||
String envPath;
|
||||
try {
|
||||
URL urlObject = new URL(url);
|
||||
if (url.contains("${")) {
|
||||
envPath = url;
|
||||
} else {
|
||||
sampler.setDomain(URLDecoder.decode(urlObject.getHost(), StandardCharsets.UTF_8.name()));
|
||||
sampler.setDomain(URLDecoder.decode(urlObject.getHost(), StandardCharsets.UTF_8));
|
||||
envPath = urlObject.getPath();
|
||||
sampler.setPort(urlObject.getPort());
|
||||
}
|
||||
sampler.setProtocol(urlObject.getProtocol());
|
||||
} 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()) {
|
||||
envPath = getRestParameters(URLDecoder.decode(URLEncoder.encode(envPath, StandardCharsets.UTF_8.name()), StandardCharsets.UTF_8.name()));
|
||||
sampler.setProperty("HTTPSampler.path", envPath);
|
||||
envPath = getRestParameters(URLDecoder.decode(URLEncoder.encode(envPath, StandardCharsets.UTF_8), StandardCharsets.UTF_8));
|
||||
sampler.setProperty(HTTPSamplerBase.PATH, envPath);
|
||||
}
|
||||
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) {
|
||||
|
@ -451,7 +445,7 @@ public class MsHTTPSamplerProxy extends MsTestElement {
|
|||
} else {
|
||||
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.setPort(urlObject.getPort());
|
||||
} else {
|
||||
|
@ -465,30 +459,30 @@ public class MsHTTPSamplerProxy extends MsTestElement {
|
|||
} else {
|
||||
URL urlObject = new URL(this.path);
|
||||
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.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) {
|
||||
try {
|
||||
if (this.isCustomizeReq() && StringUtils.isNotEmpty(this.getUrl())) {
|
||||
url = this.getUrl();
|
||||
sampler.setProperty("HTTPSampler.path", url);
|
||||
sampler.setProperty(HTTPSamplerBase.PATH, url);
|
||||
}
|
||||
if (StringUtils.isNotEmpty(this.getPort()) && this.getPort().startsWith("${")) {
|
||||
url = url.replace(this.getPort(), "10990");
|
||||
}
|
||||
try {
|
||||
URL urlObject = new URL(url);
|
||||
String envPath;
|
||||
if (url.contains("${")) {
|
||||
envPath = url;
|
||||
} else {
|
||||
sampler.setDomain(URLDecoder.decode(urlObject.getHost(), StandardCharsets.UTF_8.name()));
|
||||
if (urlObject.getPort() > 0 && urlObject.getPort() == 10990 && StringUtils.isNotEmpty(this.getPort()) && this.getPort().startsWith("${")) {
|
||||
sampler.setProperty("HTTPSampler.port", this.getPort());
|
||||
sampler.setDomain(URLDecoder.decode(urlObject.getHost(), StandardCharsets.UTF_8));
|
||||
if (urlObject.getPort() == 10990 && StringUtils.isNotEmpty(this.getPort()) && this.getPort().startsWith("${")) {
|
||||
sampler.setProperty(HTTPSamplerBase.PORT, this.getPort());
|
||||
} else if (urlObject.getPort() != -1) {
|
||||
sampler.setPort(urlObject.getPort());
|
||||
}
|
||||
|
@ -496,19 +490,15 @@ public class MsHTTPSamplerProxy extends MsTestElement {
|
|||
}
|
||||
|
||||
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) {
|
||||
sampler.setProperty("HTTPSampler.path", url);
|
||||
sampler.setProperty(HTTPSamplerBase.PATH, url);
|
||||
LogUtil.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载SSL认证
|
||||
*
|
||||
* @param config
|
||||
* @param httpSamplerTree
|
||||
* @return
|
||||
*/
|
||||
private void addCertificate(ParameterConfig config, HashTree httpSamplerTree) {
|
||||
if (config != null && config.isEffective(this.getProjectId()) && getEnvironmentConfig(config).getSslConfig() != null) {
|
||||
|
@ -561,24 +551,15 @@ public class MsHTTPSamplerProxy extends MsTestElement {
|
|||
|
||||
/**
|
||||
* 自定义请求如果是完整url时不拼接mock信息
|
||||
*
|
||||
* @param url
|
||||
* @return
|
||||
*/
|
||||
private boolean isCustomizeReqCompleteUrl(String url) {
|
||||
if (isCustomizeReq() && StringUtils.isNotEmpty(url) && (url.startsWith("http://") || url.startsWith("https://"))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return isCustomizeReq() && StringUtils.isNotEmpty(url) && (url.startsWith("http://") || url.startsWith("https://"));
|
||||
}
|
||||
|
||||
private boolean isUrl() {
|
||||
// 自定义字段没有引用环境则非url
|
||||
if (this.isCustomizeReq()) {
|
||||
if (this.isRefEnvironment) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return !this.isRefEnvironment;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -624,7 +605,7 @@ public class MsHTTPSamplerProxy extends MsTestElement {
|
|||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
LoggerUtil.error(this.getName(), ex);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
@ -632,7 +613,7 @@ public class MsHTTPSamplerProxy extends MsTestElement {
|
|||
private String postQueryParameters(String path) {
|
||||
StringBuffer stringBuffer = new StringBuffer();
|
||||
stringBuffer.append(path);
|
||||
if (path.indexOf("?") != -1) {
|
||||
if (path.contains("?")) {
|
||||
stringBuffer.append("&");
|
||||
} else {
|
||||
stringBuffer.append("?");
|
||||
|
|
|
@ -15,7 +15,6 @@ import io.metersphere.base.domain.ApiTestEnvironmentWithBLOBs;
|
|||
import io.metersphere.commons.constants.CommonConstants;
|
||||
import io.metersphere.commons.constants.ElementConstants;
|
||||
import io.metersphere.commons.constants.MsTestElementConstants;
|
||||
import io.metersphere.commons.exception.MSException;
|
||||
import io.metersphere.commons.utils.*;
|
||||
import io.metersphere.commons.vo.JDBCProcessorVO;
|
||||
import io.metersphere.environment.service.BaseEnvironmentService;
|
||||
|
@ -76,9 +75,9 @@ public class MsJDBCSampler extends MsTestElement {
|
|||
}
|
||||
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()));
|
||||
}
|
||||
|
||||
|
@ -88,9 +87,9 @@ public class MsJDBCSampler extends MsTestElement {
|
|||
this.dataSource = null;
|
||||
envConfig = this.initDataSource();
|
||||
} else {
|
||||
if (config.isEffective(this.getProjectId()) && CollectionUtils.isNotEmpty(config.getConfig().get(this.getProjectId()).getDatabaseConfigs())
|
||||
&& isDataSource(config.getConfig().get(this.getProjectId()).getDatabaseConfigs())) {
|
||||
EnvironmentConfig environmentConfig = config.getConfig().get(this.getProjectId());
|
||||
if (config.isEffective(this.getProjectId()) && CollectionUtils.isNotEmpty(config.get(this.getProjectId()).getDatabaseConfigs())
|
||||
&& isDataSource(config.get(this.getProjectId()).getDatabaseConfigs())) {
|
||||
EnvironmentConfig environmentConfig = config.get(this.getProjectId());
|
||||
if (environmentConfig.getDatabaseConfigs() != null && StringUtils.isNotEmpty(environmentConfig.getEnvironmentId())) {
|
||||
this.environmentId = environmentConfig.getEnvironmentId();
|
||||
}
|
||||
|
@ -102,8 +101,8 @@ public class MsJDBCSampler extends MsTestElement {
|
|||
} else {
|
||||
// 取当前环境下默认的一个数据源
|
||||
if (config.isEffective(this.getProjectId())) {
|
||||
if (config.getConfig().get(this.getProjectId()) != null) {
|
||||
envConfig = config.getConfig().get(this.getProjectId());
|
||||
if (config.get(this.getProjectId()) != null) {
|
||||
envConfig = config.get(this.getProjectId());
|
||||
if (CollectionUtils.isNotEmpty(envConfig.getDatabaseConfigs())) {
|
||||
LoggerUtil.info(this.getName() + ":开始获取当前环境下默认数据源");
|
||||
DatabaseConfig dataSourceOrg = ElementUtil.dataSource(getProjectId(), dataSourceId, envConfig);
|
||||
|
@ -119,9 +118,7 @@ public class MsJDBCSampler extends MsTestElement {
|
|||
}
|
||||
}
|
||||
if (this.dataSource == null) {
|
||||
LoggerUtil.info(this.getName() + " 当前项目id", this.getProjectId() + " 当前环境配置信息", JSONUtil.toJSONString(config));
|
||||
String message = "数据源为空请选择数据源";
|
||||
MSException.throwException(StringUtils.isNotEmpty(this.getName()) ? this.getName() + ":" + message : message);
|
||||
LoggerUtil.error(this.getName() + ",未找到数据源", JSONUtil.toJSONString(config));
|
||||
}
|
||||
JDBCSampler jdbcSampler = jdbcSampler(config);
|
||||
// 失败重试
|
||||
|
|
|
@ -122,16 +122,16 @@ public class MsTCPSampler extends MsTestElement {
|
|||
break;
|
||||
}
|
||||
}
|
||||
if (config.getConfig() == null) {
|
||||
if (MapUtils.isEmpty(config.getConfig()) && config.isApi()) {
|
||||
// 单独接口执行
|
||||
if (StringUtils.isNotEmpty(config.getProjectId())) {
|
||||
this.setProjectId(config.getProjectId());
|
||||
if (StringUtils.isNotEmpty(config.getCurrentProjectId())) {
|
||||
this.setProjectId(config.getCurrentProjectId());
|
||||
}
|
||||
config.setConfig(ElementUtil.getEnvironmentConfig(StringUtils.isNotEmpty(this.getEnvironmentId()) ? this.getEnvironmentId() : useEnvironment, this.getProjectId()));
|
||||
}
|
||||
EnvironmentConfig envConfig = null;
|
||||
if (config.getConfig() != null) {
|
||||
envConfig = config.getConfig().get(this.projectId);
|
||||
envConfig = config.get(this.projectId);
|
||||
parseEnvironment(envConfig);
|
||||
}
|
||||
// 添加环境中的公共变量
|
||||
|
@ -303,17 +303,17 @@ public class MsTCPSampler extends MsTestElement {
|
|||
List<StringProperty> threadValues = new ArrayList<>();
|
||||
if (CollectionUtils.isNotEmpty(this.parameters)) {
|
||||
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();
|
||||
if (StringUtils.isNotEmpty(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));
|
||||
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));
|
||||
tree.add(userParameters);
|
||||
}
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
package io.metersphere.api.dto.scenario;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class DatabaseConfig {
|
||||
|
||||
private String id;
|
||||
|
|
|
@ -75,8 +75,6 @@ public class ApiCaseExecuteService {
|
|||
/**
|
||||
* 测试计划case执行
|
||||
*
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
public List<MsExecResponseDTO> run(BatchRunDefinitionRequest request) {
|
||||
List<MsExecResponseDTO> responseDTOS = new LinkedList<>();
|
||||
|
|
|
@ -136,7 +136,7 @@ public class ApiCaseSerialService {
|
|||
parse(element, testId, envId, caseWithBLOBs.getProjectId());
|
||||
group.getHashTree().add(JSONUtil.parseObject(element.toString(), MsTestElement.class));
|
||||
testPlan.getHashTree().add(group);
|
||||
ParameterConfig config = new ParameterConfig();
|
||||
ParameterConfig config = new ParameterConfig(projectId, true);
|
||||
if (runRequest.isRetryEnable() && runRequest.getRetryNum() > 0) {
|
||||
config.setRetryNum(runRequest.getRetryNum());
|
||||
}
|
||||
|
|
|
@ -196,18 +196,17 @@ public class ApiExecuteService {
|
|||
this.add(request.getProjectId());
|
||||
}}, new BooleanPool()).keySet().stream().toList());
|
||||
threadGroup.getHashTree().add(request);
|
||||
ParameterConfig config = new ParameterConfig();
|
||||
config.setProjectId(request.getProjectId());
|
||||
ParameterConfig config = new ParameterConfig(request.getProjectId(), true);
|
||||
config.setCurrentProjectId(request.getProjectId());
|
||||
return testPlan.generateHashTree(config);
|
||||
}
|
||||
|
||||
private JmeterRunRequestDTO initRunRequest(RunDefinitionRequest request, List<MultipartFile> bodyFiles) {
|
||||
ParameterConfig config = new ParameterConfig();
|
||||
config.setProjectId(request.getProjectId());
|
||||
ParameterConfig config = new ParameterConfig(request.getProjectId(), true);
|
||||
config.setApi(true);
|
||||
Map<String, EnvironmentConfig> envConfig = new HashMap<>();
|
||||
Map<String, String> map = request.getEnvironmentMap();
|
||||
if (map != null && map.size() > 0) {
|
||||
if (MapUtils.isNotEmpty(map)) {
|
||||
for (String key : map.keySet()) {
|
||||
ApiTestEnvironmentWithBLOBs environment = apiTestEnvironmentService.get(map.get(key));
|
||||
if (environment != null) {
|
||||
|
@ -317,8 +316,7 @@ public class ApiExecuteService {
|
|||
|
||||
BaseEnvironmentService apiTestEnvironmentService = CommonBeanFactory.getBean(BaseEnvironmentService.class);
|
||||
ApiTestEnvironmentWithBLOBs environment = apiTestEnvironmentService.get(request.getEnvironmentId());
|
||||
ParameterConfig parameterConfig = new ParameterConfig();
|
||||
parameterConfig.setApi(true);
|
||||
ParameterConfig parameterConfig = new ParameterConfig(projectId, true);
|
||||
Map<String, EnvironmentConfig> envConfig = new HashMap<>(16);
|
||||
if (environment != null && environment.getConfig() != null) {
|
||||
EnvironmentConfig environmentConfig = JSONUtil.parseObject(environment.getConfig(), EnvironmentConfig.class);
|
||||
|
|
|
@ -3,7 +3,6 @@ package io.metersphere.api.exec.engine;
|
|||
import io.fabric8.kubernetes.api.model.Pod;
|
||||
import io.fabric8.kubernetes.client.KubernetesClient;
|
||||
import io.fabric8.kubernetes.client.dsl.ExecListener;
|
||||
import io.fabric8.kubernetes.client.dsl.ExecWatch;
|
||||
import io.metersphere.commons.exception.MSException;
|
||||
import io.metersphere.commons.utils.CommonBeanFactory;
|
||||
import io.metersphere.commons.utils.JSON;
|
||||
|
@ -32,9 +31,9 @@ public class KubernetesApiExec {
|
|||
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 + " 】");
|
||||
return client.pods().inNamespace(namespace).withName(podName)
|
||||
client.pods().inNamespace(namespace).withName(podName)
|
||||
.readingInput(System.in)
|
||||
.writingOutput(System.out)
|
||||
.writingError(System.err)
|
||||
|
@ -43,12 +42,7 @@ public class KubernetesApiExec {
|
|||
.exec("sh", "-c", command);
|
||||
}
|
||||
|
||||
private static class SimpleListener implements ExecListener {
|
||||
private JmeterRunRequestDTO runRequest;
|
||||
|
||||
SimpleListener(JmeterRunRequestDTO runRequest) {
|
||||
this.runRequest = runRequest;
|
||||
}
|
||||
private record SimpleListener(JmeterRunRequestDTO runRequest) implements ExecListener {
|
||||
|
||||
@Override
|
||||
public void onOpen() {
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -88,7 +88,7 @@ public class ApiEnvironmentRunningParamService {
|
|||
itemObj.put(NAME, entry.getKey());
|
||||
itemObj.put(VALUE, entry.getValue());
|
||||
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);
|
||||
} else {
|
||||
variables.put(itemObj);
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package io.metersphere.api.exec.scenario;
|
||||
|
||||
import io.metersphere.api.dto.EnvironmentCheckDTO;
|
||||
import io.metersphere.api.dto.EnvironmentType;
|
||||
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.RunScenarioRequest;
|
||||
import io.metersphere.api.dto.definition.request.ElementUtil;
|
||||
|
@ -35,6 +35,7 @@ import io.metersphere.service.definition.ApiDefinitionService;
|
|||
import jakarta.annotation.Resource;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.json.JSONObject;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
@ -61,36 +62,32 @@ public class ApiScenarioEnvService {
|
|||
@Resource
|
||||
private BaseEnvironmentService apiTestEnvironmentService;
|
||||
|
||||
public ScenarioEnv getApiScenarioEnv(String definition) {
|
||||
ScenarioEnv env = new ScenarioEnv();
|
||||
public EnvironmentCheckDTO getApiScenarioEnv(String definition) {
|
||||
EnvironmentCheckDTO env = new EnvironmentCheckDTO();
|
||||
if (StringUtils.isEmpty(definition)) {
|
||||
return env;
|
||||
}
|
||||
List<MsTestElement> hashTree = GenerateHashTreeUtil.getScenarioHashTree(definition);
|
||||
if (CollectionUtils.isNotEmpty(hashTree)) {
|
||||
// 过滤掉禁用的步骤
|
||||
hashTree = hashTree.stream().filter(item -> item.isEnable()).collect(Collectors.toList());
|
||||
}
|
||||
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));
|
||||
List<Boolean> fullUrls = new ArrayList<>();
|
||||
getHashTree(hashTree, env, fullUrls);
|
||||
env.setFullUrl(!fullUrls.contains(false));
|
||||
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 {
|
||||
// 过滤掉禁用的步骤
|
||||
tree = tree.stream().filter(item -> item.isEnable()).collect(Collectors.toList());
|
||||
tree = tree.stream().filter(MsTestElement::isEnable).collect(Collectors.toList());
|
||||
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())) {
|
||||
getHashTree(element.getHashTree(), env, hasFullUrlList);
|
||||
getHashTree(element.getHashTree(), env, fullUrls);
|
||||
}
|
||||
}
|
||||
} 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(testElement.getType(), ElementConstants.HTTP_SAMPLER)) {
|
||||
MsHTTPSamplerProxy http = (MsHTTPSamplerProxy) testElement;
|
||||
|
@ -109,7 +106,7 @@ public class ApiScenarioEnvService {
|
|||
if (!StringUtils.equalsIgnoreCase(http.getReferenced(), ElementConstants.STEP_CREATED)
|
||||
|| (http.getIsRefEnvironment() != null && http.getIsRefEnvironment())) {
|
||||
env.getProjectIds().add(http.getProjectId());
|
||||
hasFullUrlList.add(false);
|
||||
fullUrls.add(false);
|
||||
}
|
||||
} else if (StringUtils.equals(testElement.getType(), ElementConstants.JDBC_SAMPLER)
|
||||
|| StringUtils.equals(testElement.getType(), ElementConstants.TCP_SAMPLER)) {
|
||||
|
@ -118,18 +115,18 @@ public class ApiScenarioEnvService {
|
|||
ApiTestCase testCase = apiTestCaseMapper.selectByPrimaryKey(testElement.getId());
|
||||
if (testCase != null) {
|
||||
env.getProjectIds().add(testCase.getProjectId());
|
||||
hasFullUrlList.add(false);
|
||||
fullUrls.add(false);
|
||||
}
|
||||
} else {
|
||||
ApiDefinition apiDefinition = apiDefinitionService.get(testElement.getId());
|
||||
if (apiDefinition != null) {
|
||||
env.getProjectIds().add(apiDefinition.getProjectId());
|
||||
hasFullUrlList.add(false);
|
||||
fullUrls.add(false);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
env.getProjectIds().add(testElement.getProjectId());
|
||||
hasFullUrlList.add(false);
|
||||
fullUrls.add(false);
|
||||
}
|
||||
} else if (StringUtils.equals(testElement.getType(), ElementConstants.SCENARIO) && StringUtils.isEmpty(testElement.getProjectId())) {
|
||||
ApiScenarioWithBLOBs apiScenario = apiScenarioMapper.selectByPrimaryKey(testElement.getId());
|
||||
|
@ -145,14 +142,14 @@ public class ApiScenarioEnvService {
|
|||
if (StringUtils.equals(testElement.getType(), ElementConstants.HTTP_SAMPLER)) {
|
||||
// 校验是否是全路径
|
||||
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)) {
|
||||
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)) {
|
||||
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)
|
||||
|
@ -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) {
|
||||
env.getProjectIds().add(projectId);
|
||||
hasFullUrlList.add(isRefEnvironment == null ? true : !isRefEnvironment);
|
||||
hasFullUrlList.add(isRefEnvironment == null || !isRefEnvironment);
|
||||
} else if (!StringUtils.equalsIgnoreCase(referenced, ElementConstants.STEP_CREATED)
|
||||
|| (isRefEnvironment != null && isRefEnvironment)) {
|
||||
env.getProjectIds().add(projectId);
|
||||
|
@ -174,8 +171,6 @@ public class ApiScenarioEnvService {
|
|||
|
||||
/**
|
||||
* 设置场景的运行环境 环境组/环境JSON
|
||||
*
|
||||
* @param apiScenarioWithBLOBs
|
||||
*/
|
||||
public void setScenarioEnv(ApiScenarioWithBLOBs apiScenarioWithBLOBs, RunScenarioRequest request) {
|
||||
if (apiScenarioWithBLOBs == null) {
|
||||
|
@ -210,60 +205,22 @@ public class ApiScenarioEnvService {
|
|||
String definition = apiScenarioWithBLOBs.getScenarioDefinition();
|
||||
MsScenarioEnv scenario = JSON.parseObject(definition, MsScenarioEnv.class);
|
||||
Map<String, String> envMap = scenario.getEnvironmentMap();
|
||||
return this.check(definition, envMap, scenario.getEnvironmentId(), apiScenarioWithBLOBs.getProjectId());
|
||||
return this.check(definition, envMap, apiScenarioWithBLOBs.getProjectId());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean check(String definition, Map<String, String> envMap, String envId, String projectId) {
|
||||
boolean isEnv = true;
|
||||
ScenarioEnv apiScenarioEnv = getApiScenarioEnv(definition);
|
||||
// 所有请求非全路径检查环境
|
||||
private boolean check(String definition, Map<String, String> envMap, String projectId) {
|
||||
EnvironmentCheckDTO apiScenarioEnv = getApiScenarioEnv(definition);
|
||||
// 当前项目是否选择了环境
|
||||
if (MapUtils.isNotEmpty(envMap) && envMap.containsKey(projectId)) {
|
||||
return true;
|
||||
}
|
||||
// 所有请求是否都是自定义请求且没有引用环境
|
||||
if (!apiScenarioEnv.getFullUrl()) {
|
||||
try {
|
||||
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);
|
||||
return false;
|
||||
}
|
||||
if (StringUtils.isBlank(s)) {
|
||||
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;
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean verifyPlanScenarioEnv(ApiScenarioWithBLOBs apiScenarioWithBLOBs, TestPlanApiScenarioInfoDTO testPlanApiScenarios) {
|
||||
|
@ -283,20 +240,15 @@ public class ApiScenarioEnvService {
|
|||
envMap = new HashMap<>();
|
||||
}
|
||||
}
|
||||
return this.check(definition, envMap, scenario.getEnvironmentId(), apiScenarioWithBLOBs.getProjectId());
|
||||
return this.check(definition, envMap, apiScenarioWithBLOBs.getProjectId());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否存在运行环境。若不存在则报错,存在的话返回所存在的运行环境
|
||||
*
|
||||
* @param request
|
||||
* @param apiScenarios
|
||||
* @return <projectId,envIds>
|
||||
*/
|
||||
public Map<String, List<String>> checkEnv(RunScenarioRequest request, List<ApiScenarioWithBLOBs> apiScenarios) {
|
||||
Map<String, List<String>> projectEnvMap = new HashMap<>();
|
||||
public void checkEnv(RunScenarioRequest request, List<ApiScenarioWithBLOBs> apiScenarios) {
|
||||
if (StringUtils.equals(request.getRequestOriginator(), CommonConstants.TEST_PLAN)) {
|
||||
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())) {
|
||||
|
@ -312,7 +264,7 @@ public class ApiScenarioEnvService {
|
|||
MSException.throwException("场景:" + apiScenarioWithBLOBs.getName() + ",步骤解析错误,检查是否包含插件步骤!");
|
||||
}
|
||||
}
|
||||
if (builder.length() > 0) {
|
||||
if (!builder.isEmpty()) {
|
||||
MSException.throwException("场景:" + builder + "运行环境未配置,请检查!");
|
||||
}
|
||||
} else if (StringUtils.equals(request.getRunMode(), ApiRunMode.SCHEDULE_SCENARIO.name())) {
|
||||
|
@ -324,10 +276,11 @@ public class ApiScenarioEnvService {
|
|||
}
|
||||
}
|
||||
}
|
||||
return projectEnvMap;
|
||||
}
|
||||
|
||||
//检查测试计划场景的环境,没有环境的场景从执行队列中移除
|
||||
/**
|
||||
* 检查测试计划场景的环境,没有环境的场景从执行队列中移除
|
||||
*/
|
||||
public void checkPlanScenarioEnv(RunScenarioRequest request) {
|
||||
if (request.getProcessVO() != null &&
|
||||
MapUtils.isNotEmpty(request.getProcessVO().getTestPlanScenarioMap())
|
||||
|
@ -354,12 +307,12 @@ public class ApiScenarioEnvService {
|
|||
|
||||
public Map<String, List<String>> selectApiScenarioEnv(List<? extends ApiScenarioWithBLOBs> list) {
|
||||
Map<String, List<String>> projectEnvMap = new LinkedHashMap<>();
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
for (ApiScenarioWithBLOBs apiScenarioWithBLOBs : list) {
|
||||
try {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
String environmentType = list.get(i).getEnvironmentType();
|
||||
String environmentGroupId = list.get(i).getEnvironmentGroupId();
|
||||
String env = list.get(i).getEnvironmentJson();
|
||||
String environmentType = apiScenarioWithBLOBs.getEnvironmentType();
|
||||
String environmentGroupId = apiScenarioWithBLOBs.getEnvironmentGroupId();
|
||||
String env = apiScenarioWithBLOBs.getEnvironmentJson();
|
||||
if (StringUtils.equals(environmentType, EnvironmentType.JSON.name())) {
|
||||
// 环境属性为空 跳过
|
||||
if (StringUtils.isBlank(env)) {
|
||||
|
@ -394,7 +347,7 @@ public class ApiScenarioEnvService {
|
|||
}
|
||||
}
|
||||
} 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;
|
||||
|
@ -594,9 +547,9 @@ public class ApiScenarioEnvService {
|
|||
|
||||
private boolean isProjectEnvMapNotContainsEnv(LinkedHashMap<String, List<String>> returnMap, String projectName, String envName) {
|
||||
if (MapUtils.isNotEmpty(returnMap)) {
|
||||
if (returnMap.containsKey(projectName) && CollectionUtils.isNotEmpty(returnMap.get(projectName)) && returnMap.get(projectName).contains(envName)) {
|
||||
return false;
|
||||
}
|
||||
return !returnMap.containsKey(projectName)
|
||||
|| !CollectionUtils.isNotEmpty(returnMap.get(projectName))
|
||||
|| !returnMap.get(projectName).contains(envName);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -122,9 +122,6 @@ public class ApiScenarioExecuteService {
|
|||
}
|
||||
// 检查执行内容合规性
|
||||
PerformInspectionUtil.scenarioInspection(apiScenarios);
|
||||
// 环境检查
|
||||
LoggerUtil.info("Scenario run-执行脚本装载-开始针对所有执行场景进行环境检查");
|
||||
apiScenarioEnvService.checkEnv(request, apiScenarios);
|
||||
// 集合报告设置
|
||||
if (!request.isRerun() && GenerateHashTreeUtil.isSetReport(request.getConfig())) {
|
||||
if (isSerial(request)) {
|
||||
|
@ -412,7 +409,7 @@ public class ApiScenarioExecuteService {
|
|||
if (StringUtils.equals(request.getEnvironmentType(), EnvironmentType.GROUP.toString())) {
|
||||
request.setEnvironmentMap(environmentGroupProjectService.getEnvMap(request.getEnvironmentGroupId()));
|
||||
}
|
||||
ParameterConfig config = new ParameterConfig();
|
||||
ParameterConfig config = new ParameterConfig(request.getProjectId(), false);
|
||||
config.setScenarioId(request.getScenarioId());
|
||||
if (MapUtils.isNotEmpty(request.getEnvironmentMap())) {
|
||||
apiScenarioEnvService.setEnvConfig(request.getEnvironmentMap(), config);
|
||||
|
@ -445,7 +442,7 @@ public class ApiScenarioExecuteService {
|
|||
if (testElement != null) {
|
||||
apiScenario.setName(testElement.getName());
|
||||
apiScenario.setScenarioDefinition(JSON.toJSONString(testElement));
|
||||
List<String> projectIdLists =ElementUtil. getProjectIds(apiScenario.getScenarioDefinition());
|
||||
List<String> projectIdLists = ElementUtil.getProjectIds(apiScenario.getScenarioDefinition());
|
||||
Map<String, String> envMap = ElementUtil.getProjectEnvMap(projectIdLists, request.getEnvironmentMap());
|
||||
request.getConfig().setEnvMap(envMap);
|
||||
report.setEnvConfig(JSON.toJSONString(request.getConfig()));
|
||||
|
|
|
@ -15,7 +15,7 @@ public class JMeterLoggerAppender extends UnsynchronizedAppenderBase<ILoggingEve
|
|||
public void append(ILoggingEvent event) {
|
||||
try {
|
||||
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);
|
||||
message.append(DateUtils.getTimeStr(event.getTimeStamp())).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")
|
||||
&& FixedCapacityUtil.containsKey(threadName)) {
|
||||
if (!message.toString().contains("java.net.UnknownHostException") && FixedCapacityUtil.containsKey(threadName)) {
|
||||
FixedCapacityUtil.get(threadName).append(message);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ package io.metersphere.api.jmeter;
|
|||
import io.metersphere.api.dto.definition.request.ElementUtil;
|
||||
import io.metersphere.api.dto.definition.request.MsTestPlan;
|
||||
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.ServerConfig;
|
||||
import io.metersphere.api.jmeter.utils.SmoothWeighted;
|
||||
|
@ -28,6 +27,7 @@ import jakarta.annotation.PostConstruct;
|
|||
import jakarta.annotation.Resource;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.jmeter.util.JMeterUtils;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
|
@ -53,8 +53,6 @@ public class JMeterService {
|
|||
@Resource
|
||||
private RemakeReportService remakeReportService;
|
||||
@Resource
|
||||
private ExecThreadPoolExecutor execThreadPoolExecutor;
|
||||
@Resource
|
||||
private ApiPoolDebugService apiPoolDebugService;
|
||||
@Resource
|
||||
private PluginService pluginService;
|
||||
|
@ -163,7 +161,7 @@ public class JMeterService {
|
|||
request.setEnable(config.isEnable());
|
||||
LoggerUtil.info("开始发送请求【 " + request.getTestId() + " 】到 " + config.getUrl() + " 节点执行", request.getReportId());
|
||||
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());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
@ -182,7 +180,6 @@ public class JMeterService {
|
|||
ElementUtil.coverArguments(request.getHashTree());
|
||||
//解析hashTree,是否含有文件库文件
|
||||
HashTreeUtil.initRepositoryFiles(request);
|
||||
execThreadPoolExecutor.addTask(request);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -200,7 +197,7 @@ public class JMeterService {
|
|||
Integer port = node.getPort();
|
||||
String uri = String.format(BASE_URL + "/jmeter/get/running/queue/" + reportId, nodeIp, port);
|
||||
ResponseEntity<Boolean> result = restTemplate.getForEntity(uri, Boolean.class);
|
||||
if (result != null && result.getBody()) {
|
||||
if (BooleanUtils.isTrue(result.getBody())) {
|
||||
isRunning = true;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -65,14 +65,10 @@ public class KafkaListenerTask implements Runnable {
|
|||
(MapUtils.isNotEmpty(dto.getArbitraryData()) &&
|
||||
dto.getArbitraryData().containsKey(ENV))) {
|
||||
String key = RUN_MODE_MAP.get(dto.getRunMode());
|
||||
if (assortMap.containsKey(key)) {
|
||||
assortMap.get(key).add(dto);
|
||||
} else {
|
||||
assortMap.put(key, new LinkedList<>() {{
|
||||
this.add(dto);
|
||||
}});
|
||||
}
|
||||
}
|
||||
if (MapUtils.isNotEmpty(assortMap)) {
|
||||
testResultService.batchSaveResults(assortMap);
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
}
|
||||
}
|
|
@ -28,6 +28,7 @@ import java.util.concurrent.TimeUnit;
|
|||
public class MsKafkaListener {
|
||||
public static final String CONSUME_ID = "ms-api-exec-consume";
|
||||
public static final String DEBUG_CONSUME_ID = "ms-api-debug-consume";
|
||||
private static final String PRE_RESULT = "result_";
|
||||
@Resource
|
||||
private ApiExecutionQueueService apiExecutionQueueService;
|
||||
@Resource
|
||||
|
@ -52,7 +53,7 @@ public class MsKafkaListener {
|
|||
MAX_POOL_SIZE,
|
||||
KEEP_ALIVE_TIME,
|
||||
TimeUnit.SECONDS,
|
||||
new ArrayBlockingQueue(WORK_QUEUE_SIZE),
|
||||
new ArrayBlockingQueue<>(WORK_QUEUE_SIZE),
|
||||
new NamedThreadFactory("MS-KAFKA-LISTENER-TASK"));
|
||||
|
||||
@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());
|
||||
if (ObjectUtils.isNotEmpty(record.value()) && WebSocketUtil.has(record.key().toString())) {
|
||||
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);
|
||||
if (StringUtils.isNotBlank(content)) {
|
||||
RequestResult baseResult = JSONUtil.parseObject(content, RequestResult.class);
|
||||
if (ObjectUtils.isNotEmpty(baseResult)) {
|
||||
//解析是否含有误报库信息
|
||||
RequestResultExpandDTO expandDTO = ResponseUtil.parseByRequestResult(baseResult);
|
||||
dto.setContent(StringUtils.join("result_", JSON.toJSONString(expandDTO)));
|
||||
if (StringUtils.equalsAnyIgnoreCase(dto.getRunMode(), ApiRunMode.DEFINITION.name(), ApiRunMode.API_PLAN.name()) && dto.getContent().startsWith("result_")) {
|
||||
dto.setContent(StringUtils.join(PRE_RESULT, JSON.toJSONString(expandDTO)));
|
||||
if (StringUtils.equalsAnyIgnoreCase(dto.getRunMode(),
|
||||
ApiRunMode.DEFINITION.name(),
|
||||
ApiRunMode.API_PLAN.name())
|
||||
&& dto.getContent().startsWith(PRE_RESULT)) {
|
||||
apiDefinitionEnvService.setEnvAndPoolName(dto);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,12 +28,11 @@ public class ResourcePoolCalculation {
|
|||
example.createCriteria().andStatusEqualTo("VALID").andTypeEqualTo("NODE").andIdEqualTo(resourcePoolId);
|
||||
List<TestResourcePool> pools = testResourcePoolMapper.selectByExample(example);
|
||||
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();
|
||||
resourceExample.createCriteria().andTestResourcePoolIdIn(poolIds);
|
||||
resourceExample.setOrderByClause("create_time");
|
||||
List<TestResource> testResources = testResourceMapper.selectByExampleWithBLOBs(resourceExample);
|
||||
return testResources;
|
||||
return testResourceMapper.selectByExampleWithBLOBs(resourceExample);
|
||||
}
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
|
|
@ -178,8 +178,7 @@ public class DataFormattingUtil {
|
|||
|
||||
public static JmxInfoDTO getJmxInfoDTO(RunDefinitionRequest runRequest, List<MultipartFile> bodyFiles) {
|
||||
BaseEnvironmentService fileMetadataService = CommonBeanFactory.getBean(BaseEnvironmentService.class);
|
||||
ParameterConfig config = new ParameterConfig();
|
||||
config.setProjectId(runRequest.getProjectId());
|
||||
ParameterConfig config = new ParameterConfig(runRequest.getProjectId(), true);
|
||||
config.setOperating(true);
|
||||
config.setOperatingSampleTestName(runRequest.getName());
|
||||
|
||||
|
|
|
@ -49,10 +49,10 @@ public class GenerateHashTreeUtil {
|
|||
JSONObject element = JSONUtil.parseObject(scenarioDefinition);
|
||||
ElementUtil.dataFormatting(element);
|
||||
// 多态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()));
|
||||
}
|
||||
if (element != null && StringUtils.isNotEmpty(element.optString("variables"))) {
|
||||
if (StringUtils.isNotEmpty(element.optString("variables"))) {
|
||||
scenario.setVariables(JSONUtil.parseArray(element.optString("variables"), ScenarioVariable.class));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
@ -62,7 +62,7 @@ public class GenerateHashTreeUtil {
|
|||
|
||||
public static LinkedList<MsTestElement> getScenarioHashTree(String definition) {
|
||||
JSONObject element = JSONUtil.parseObject(definition);
|
||||
if (element != null && element.has(ElementConstants.HASH_TREE)) {
|
||||
if (element.has(ElementConstants.HASH_TREE)) {
|
||||
ElementUtil.dataFormatting(element);
|
||||
return JSONUtil.readValue(element.optJSONArray(ElementConstants.HASH_TREE).toString());
|
||||
}
|
||||
|
@ -153,7 +153,7 @@ public class GenerateHashTreeUtil {
|
|||
group.setHashTree(scenarios);
|
||||
testPlan.getHashTree().add(group);
|
||||
|
||||
ParameterConfig config = new ParameterConfig();
|
||||
ParameterConfig config = new ParameterConfig(item.getProjectId(), false);
|
||||
config.setScenarioId(item.getId());
|
||||
config.setReportType(runRequest.getReportType());
|
||||
if (runRequest.isRetryEnable() && runRequest.getRetryNum() > 0) {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package io.metersphere.controller;
|
||||
|
||||
import io.metersphere.api.dto.BodyFileRequest;
|
||||
import io.metersphere.api.jmeter.JMeterThreadUtils;
|
||||
import io.metersphere.api.jmeter.utils.JmxFileUtil;
|
||||
import io.metersphere.dto.ProjectJarConfig;
|
||||
import io.metersphere.service.ApiJMeterFileService;
|
||||
|
@ -28,7 +27,7 @@ public class ApiJMeterFileController {
|
|||
|
||||
@GetMapping("stop/{name}")
|
||||
public String stop(@PathVariable String name) {
|
||||
return JMeterThreadUtils.stop(name);
|
||||
return name;
|
||||
}
|
||||
|
||||
@PostMapping("download/jar")
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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.swaggerurl.SwaggerTaskResult;
|
||||
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.queue.ExecThreadPoolExecutor;
|
||||
import io.metersphere.api.parse.api.ApiDefinitionImport;
|
||||
import io.metersphere.base.domain.ApiDefinition;
|
||||
import io.metersphere.base.domain.ApiTestEnvironmentWithBLOBs;
|
||||
|
@ -53,10 +51,6 @@ public class ApiDefinitionController {
|
|||
@Resource
|
||||
private BaseEnvironmentService apiTestEnvironmentService;
|
||||
@Resource
|
||||
private ExecThreadPoolExecutor execThreadPoolExecutor;
|
||||
@Resource
|
||||
private ApiExecuteService apiExecuteService;
|
||||
@Resource
|
||||
private FunctionRunService functionRunService;
|
||||
|
||||
@PostMapping("/list/{goPage}/{pageSize}")
|
||||
|
@ -376,11 +370,6 @@ public class ApiDefinitionController {
|
|||
apiDefinitionService.deleteFollows(definitionIds);
|
||||
}
|
||||
|
||||
@GetMapping("/getWorkerQueue")
|
||||
public String getWorkerQueue() {
|
||||
return execThreadPoolExecutor.getWorkerQueue();
|
||||
}
|
||||
|
||||
@GetMapping("versions/{definitionId}")
|
||||
public List<ApiDefinitionResult> getApiDefinitionVersions(@PathVariable String definitionId) {
|
||||
return apiDefinitionService.getApiDefinitionVersions(definitionId);
|
||||
|
|
|
@ -196,13 +196,13 @@ public class ApiScenarioController {
|
|||
return apiAutomationService.getNewApiScenario(id);
|
||||
}
|
||||
|
||||
@PostMapping("/scenario-env")
|
||||
public ScenarioEnv getScenarioDefinition(@RequestBody byte[] request) {
|
||||
return apiAutomationService.getApiScenarioEnv(request);
|
||||
@PostMapping("/project-valid")
|
||||
public EnvironmentCheckDTO getScenarioDefinition(@RequestBody List<String> projectIds) {
|
||||
return apiAutomationService.getApiScenarioEnv(projectIds);
|
||||
}
|
||||
|
||||
@GetMapping("/env-project-ids/{id}")
|
||||
public ScenarioEnv getApiScenarioProjectId(@PathVariable String id) {
|
||||
public EnvironmentCheckDTO getApiScenarioProjectId(@PathVariable String id) {
|
||||
return apiAutomationService.getApiScenarioProjectId(id);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,17 +1,14 @@
|
|||
package io.metersphere.listener;
|
||||
|
||||
import com.mchange.lang.IntegerUtils;
|
||||
import io.metersphere.api.dto.shell.filter.ScriptFilter;
|
||||
import io.metersphere.api.exec.queue.ExecThreadPoolExecutor;
|
||||
import io.metersphere.api.jmeter.JMeterService;
|
||||
import io.metersphere.commons.constants.ScheduleGroup;
|
||||
import io.metersphere.commons.utils.CommonBeanFactory;
|
||||
import io.metersphere.commons.utils.LogUtil;
|
||||
import io.metersphere.dto.BaseSystemConfigDTO;
|
||||
import io.metersphere.jmeter.ProjectClassLoader;
|
||||
import io.metersphere.service.*;
|
||||
import io.metersphere.service.definition.ApiModuleService;
|
||||
import io.metersphere.service.scenario.ApiScenarioModuleService;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.python.core.Options;
|
||||
import org.python.util.PythonInterpreter;
|
||||
|
@ -20,8 +17,6 @@ import org.springframework.boot.ApplicationArguments;
|
|||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
@Component
|
||||
public class ApiAppStartListener implements ApplicationRunner {
|
||||
@Resource
|
||||
|
@ -74,12 +69,6 @@ public class ApiAppStartListener implements ApplicationRunner {
|
|||
LogUtil.info("初始化默认项目场景模块");
|
||||
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包处理");
|
||||
initPythonEnv();
|
||||
|
||||
|
|
|
@ -14,10 +14,12 @@ import io.metersphere.dto.RunModeConfigDTO;
|
|||
import io.metersphere.service.ApiPoolDebugService;
|
||||
import io.metersphere.service.scenario.ApiScenarioService;
|
||||
import io.metersphere.utils.LoggerUtil;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.quartz.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
|
@ -40,6 +42,7 @@ public class ApiScenarioTestJob extends MsScheduleJob {
|
|||
|
||||
public ApiScenarioTestJob() {
|
||||
apiAutomationService = CommonBeanFactory.getBean(ApiScenarioService.class);
|
||||
apiPoolDebugService = CommonBeanFactory.getBean(ApiPoolDebugService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -77,6 +80,9 @@ public class ApiScenarioTestJob extends MsScheduleJob {
|
|||
String config = jobDataMap.getString("config");
|
||||
if (StringUtils.isNotBlank(config)) {
|
||||
RunModeConfigDTO runModeConfig = JSON.parseObject(config, RunModeConfigDTO.class);
|
||||
if (BooleanUtils.isTrue(runModeConfig.getDefaultEnv())) {
|
||||
runModeConfig.setEnvMap(new HashMap<>());
|
||||
}
|
||||
request.setConfig(runModeConfig);
|
||||
} else {
|
||||
RunModeConfigDTO runModeConfigDTO = new RunModeConfigDTO();
|
||||
|
|
|
@ -5,7 +5,6 @@ import io.metersphere.api.exec.api.ApiCaseSerialService;
|
|||
import io.metersphere.api.exec.queue.DBTestQueue;
|
||||
import io.metersphere.api.exec.scenario.ApiScenarioSerialService;
|
||||
import io.metersphere.api.jmeter.JMeterService;
|
||||
import io.metersphere.api.jmeter.JMeterThreadUtils;
|
||||
import io.metersphere.base.domain.*;
|
||||
import io.metersphere.base.mapper.*;
|
||||
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.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.jmeter.services.FileServer;
|
||||
import org.springframework.kafka.core.KafkaTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
|
@ -388,10 +386,6 @@ public class ApiExecutionQueueService {
|
|||
if (StringUtils.isNotEmpty(queue.getPoolId()) && jMeterService.getRunningQueue(queue.getPoolId(), item.getReportId())) {
|
||||
continue;
|
||||
}
|
||||
// 检查执行报告是否还在等待队列中或执行线程中
|
||||
if (JMeterThreadUtils.isRunning(item.getReportId(), item.getTestId())) {
|
||||
continue;
|
||||
}
|
||||
// 检查是否已经超时
|
||||
ResultDTO dto = new ResultDTO();
|
||||
dto.setQueueId(item.getQueueId());
|
||||
|
@ -446,9 +440,6 @@ public class ApiExecutionQueueService {
|
|||
TestPlanReportStatus.RUNNING.name(), ApiReportStatus.PENDING.name()) && (report.getUpdateTime() < timeout)) {
|
||||
report.setStatus(ApiReportStatus.ERROR.name());
|
||||
apiScenarioReportMapper.updateByPrimaryKeySelective(report);
|
||||
if (FileServer.getFileServer() != null) {
|
||||
FileServer.getFileServer().closeCsv(item.getReportId());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -474,9 +465,6 @@ public class ApiExecutionQueueService {
|
|||
}
|
||||
|
||||
public void stop(String reportId) {
|
||||
if (FileServer.getFileServer() != null) {
|
||||
FileServer.getFileServer().closeCsv(reportId);
|
||||
}
|
||||
ApiExecutionQueueDetailExample example = new ApiExecutionQueueDetailExample();
|
||||
example.createCriteria().andReportIdEqualTo(reportId);
|
||||
List<ApiExecutionQueueDetail> details = executionQueueDetailMapper.selectByExample(example);
|
||||
|
@ -500,12 +488,6 @@ public class ApiExecutionQueueService {
|
|||
if (CollectionUtils.isEmpty(reportIds)) {
|
||||
return;
|
||||
}
|
||||
// 清理CSV
|
||||
reportIds.forEach(item -> {
|
||||
if (FileServer.getFileServer() != null) {
|
||||
FileServer.getFileServer().closeCsv(item);
|
||||
}
|
||||
});
|
||||
ApiExecutionQueueDetailExample example = new ApiExecutionQueueDetailExample();
|
||||
example.createCriteria().andReportIdIn(reportIds);
|
||||
List<ApiExecutionQueueDetail> details = executionQueueDetailMapper.selectByExample(example);
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package io.metersphere.service;
|
||||
|
||||
import io.metersphere.api.exec.queue.PoolExecBlockingQueueUtil;
|
||||
import io.metersphere.api.jmeter.ApiLocalRunner;
|
||||
import io.metersphere.base.domain.ApiDefinitionExecResultWithBLOBs;
|
||||
import io.metersphere.base.domain.ApiExecutionQueueDetail;
|
||||
|
@ -47,8 +46,6 @@ public class RemakeReportService {
|
|||
dto.setTestId(request.getTestId());
|
||||
dto.setErrorEnded(true);
|
||||
LoggerUtil.info("进入异常结果处理:" + dto.getRunMode() + " 整体处理完成", dto.getReportId());
|
||||
// 全局并发队列
|
||||
PoolExecBlockingQueueUtil.offer(dto.getReportId());
|
||||
LoggerUtil.error("执行异常处理:" + errorMsg, request.getReportId());
|
||||
if (StringUtils.isNotEmpty(dto.getQueueId())) {
|
||||
queueService.queueNext(dto);
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
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.JMeterThreadUtils;
|
||||
import io.metersphere.base.domain.*;
|
||||
import io.metersphere.base.mapper.ApiDefinitionExecResultMapper;
|
||||
import io.metersphere.base.mapper.ApiScenarioReportMapper;
|
||||
|
@ -59,8 +56,6 @@ public class ExtApiTaskService extends TaskService {
|
|||
@Resource
|
||||
private ExtApiScenarioReportMapper extApiScenarioReportMapper;
|
||||
@Resource
|
||||
private ExecThreadPoolExecutor execThreadPoolExecutor;
|
||||
@Resource
|
||||
private ApiExecutionQueueService apiExecutionQueueService;
|
||||
|
||||
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)) {
|
||||
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) {
|
||||
// 从队列移除
|
||||
TaskRequestDTO request = stopTasks.get(0);
|
||||
execThreadPoolExecutor.removeQueue(request.getReportId());
|
||||
apiExecutionQueueService.stop(request.getReportId());
|
||||
PoolExecBlockingQueueUtil.offer(request.getReportId());
|
||||
if (StringUtils.equals(request.getType(), "API")) {
|
||||
ApiDefinitionExecResultWithBLOBs result = apiDefinitionExecResultMapper.selectByPrimaryKey(request.getReportId());
|
||||
if (result != null) {
|
||||
|
@ -137,7 +130,6 @@ public class ExtApiTaskService extends TaskService {
|
|||
thread.start();
|
||||
}
|
||||
}
|
||||
return "SUCCESS";
|
||||
}
|
||||
|
||||
private void batchStop(List<TaskRequestDTO> taskRequests) {
|
||||
|
@ -152,19 +144,12 @@ public class ExtApiTaskService extends TaskService {
|
|||
|
||||
// 结束掉未分发完成的任务
|
||||
LoggerUtil.info("结束正在进行中的计划任务队列");
|
||||
JMeterThreadUtils.stop("PLAN-CASE");
|
||||
JMeterThreadUtils.stop("API-CASE-RUN");
|
||||
JMeterThreadUtils.stop("SCENARIO-PARALLEL-THREAD");
|
||||
|
||||
if (taskRequestMap.containsKey("API")) {
|
||||
List<TaskResultVO> results = extApiDefinitionExecResultMapper.findByProjectIds(taskCenterRequest);
|
||||
LoggerUtil.info("查询API进行中的报告:" + results.size());
|
||||
if (CollectionUtils.isNotEmpty(results)) {
|
||||
for (TaskResultVO item : results) {
|
||||
extracted(poolMap, item.getId(), item.getActuator());
|
||||
// 从队列移除
|
||||
execThreadPoolExecutor.removeQueue(item.getId());
|
||||
PoolExecBlockingQueueUtil.offer(item.getId());
|
||||
}
|
||||
LoggerUtil.info("结束API进行中的报告");
|
||||
baseTaskMapper.stopApi(taskCenterRequest);
|
||||
|
@ -181,9 +166,6 @@ public class ExtApiTaskService extends TaskService {
|
|||
for (TaskResultVO report : reports) {
|
||||
|
||||
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);
|
||||
}});
|
||||
}
|
||||
} else {
|
||||
JMeterThreadUtils.stop(reportId);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -223,7 +203,7 @@ public class ExtApiTaskService extends TaskService {
|
|||
example.createCriteria().andStatusEqualTo("VALID").andTypeEqualTo("NODE").andIdEqualTo(poolId);
|
||||
List<TestResourcePool> pools = testResourcePoolMapper.selectByExample(example);
|
||||
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();
|
||||
resourceExample.createCriteria().andTestResourcePoolIdIn(poolIds);
|
||||
resourceExample.setOrderByClause("create_time");
|
||||
|
|
|
@ -5,7 +5,7 @@ import com.github.pagehelper.PageHelper;
|
|||
import io.metersphere.api.dto.ApiCaseRelevanceRequest;
|
||||
import io.metersphere.api.dto.EnvironmentType;
|
||||
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.plan.*;
|
||||
import io.metersphere.api.exec.scenario.ApiScenarioEnvService;
|
||||
|
@ -237,7 +237,7 @@ public class TestPlanScenarioCaseService {
|
|||
Map<String, String> newEnvMap = new HashMap<>(16);
|
||||
List<String> list = mapping.get(id);
|
||||
if (CollectionUtils.isEmpty(list)) {
|
||||
ScenarioEnv scenarioEnv = apiAutomationService.getApiScenarioProjectId(id);
|
||||
EnvironmentCheckDTO scenarioEnv = apiAutomationService.getApiScenarioProjectId(id);
|
||||
list = new ArrayList<>(scenarioEnv.getProjectIds());
|
||||
}
|
||||
list.forEach(l -> newEnvMap.put(l, envMap == null ? StringUtils.EMPTY : envMap.getOrDefault(l, StringUtils.EMPTY)));
|
||||
|
|
|
@ -168,6 +168,8 @@ public class ApiScenarioService {
|
|||
private ApiAutomationRelationshipEdgeService apiAutomationRelationshipEdgeService;
|
||||
@Resource
|
||||
private ApiTestCaseService apiTestCaseService;
|
||||
@Resource
|
||||
private BaseProjectService baseProjectService;
|
||||
|
||||
private ThreadLocal<Long> currentScenarioOrder = new ThreadLocal<>();
|
||||
|
||||
|
@ -492,7 +494,7 @@ public class ApiScenarioService {
|
|||
.flatMap(nv -> Optional.ofNullable(nv.getFiles()).orElse(List.of()).stream())
|
||||
.map(BodyFile::getId)
|
||||
.toList();
|
||||
oldVariables.forEach(item ->{
|
||||
oldVariables.forEach(item -> {
|
||||
if (CollectionUtils.isNotEmpty(item.getFiles()) &&
|
||||
StringUtils.isNotBlank(item.getFiles().get(0).getId()) &&
|
||||
!ids.contains(item.getFiles().get(0).getId())) {
|
||||
|
@ -501,7 +503,7 @@ public class ApiScenarioService {
|
|||
});
|
||||
}
|
||||
if (CollectionUtils.isEmpty(newVariables) && CollectionUtils.isNotEmpty(oldVariables)) {
|
||||
oldVariables.forEach(item ->{
|
||||
oldVariables.forEach(item -> {
|
||||
if (CollectionUtils.isNotEmpty(item.getFiles()) &&
|
||||
StringUtils.isNotBlank(item.getFiles().get(0).getId())) {
|
||||
ApiFileUtil.deleteBodyFiles(item.getFiles().get(0).getId() + "_" + item.getFiles().get(0).getName());
|
||||
|
@ -840,8 +842,8 @@ public class ApiScenarioService {
|
|||
|
||||
public ParameterConfig getConfig(ApiScenarioDTO scenario) {
|
||||
try {
|
||||
ParameterConfig config = new ParameterConfig();
|
||||
Map<String, String> environmentMap = new HashMap<>();
|
||||
ParameterConfig config = new ParameterConfig(scenario.getProjectId(), false);
|
||||
Map<String, String> environmentMap;
|
||||
String environmentType = scenario.getEnvironmentType();
|
||||
String environmentGroupId = scenario.getEnvironmentGroupId();
|
||||
String environmentJson = scenario.getEnvironmentJson();
|
||||
|
@ -881,12 +883,12 @@ public class ApiScenarioService {
|
|||
String scenarioId = request.getId();
|
||||
ApiScenarioDTO scenario = getNewApiScenario(scenarioId);
|
||||
if (scenario != null) {
|
||||
String referenced = element.optString("referenced");
|
||||
if (StringUtils.equalsIgnoreCase("REF", referenced)) {
|
||||
String referenced = element.optString(MsHashTreeConstants.REFERENCED);
|
||||
if (StringUtils.equalsIgnoreCase(MsHashTreeConstants.REF, referenced)) {
|
||||
JSONObject source = JSONUtil.parseObject(scenario.getScenarioDefinition());
|
||||
element = jsonMerge(source, element);
|
||||
}
|
||||
element.put("referenced", referenced);
|
||||
element.put(MsHashTreeConstants.REFERENCED, referenced);
|
||||
String environmentType = scenario.getEnvironmentType();
|
||||
String environmentGroupId = scenario.getEnvironmentGroupId();
|
||||
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);
|
||||
if (config.getConfig() != null && !config.getConfig().isEmpty()) {
|
||||
ElementUtil.dataSetDomain(element.optJSONArray(ElementConstants.HASH_TREE), config);
|
||||
|
@ -933,6 +938,7 @@ public class ApiScenarioService {
|
|||
}
|
||||
if (StringUtils.isNotBlank(dto.getEnvironmentJson())) {
|
||||
ApiScenarioEnvRequest request = new ApiScenarioEnvRequest();
|
||||
request.setProjectId(dto.getProjectId());
|
||||
request.setEnvironmentEnable(false);
|
||||
request.setDefinition(dto.getScenarioDefinition());
|
||||
request.setEnvironmentMap(JSON.parseObject(dto.getEnvironmentJson(), Map.class));
|
||||
|
@ -975,7 +981,7 @@ public class ApiScenarioService {
|
|||
projectIds.add(projectId);
|
||||
testPlan.setName(apiScenario.getName());
|
||||
testPlan.setHashTree(new LinkedList<>());
|
||||
ParameterConfig config = new ParameterConfig();
|
||||
ParameterConfig config = new ParameterConfig(apiScenario.getProjectId(), false);
|
||||
config.setOperating(true);
|
||||
config.getExcludeScenarioIds().add(apiScenario.getId());
|
||||
config.setScenarioId(apiScenario.getId());
|
||||
|
@ -1735,7 +1741,7 @@ public class ApiScenarioService {
|
|||
public byte[] exportZip(ApiScenarioBatchRequest request) {
|
||||
List<ApiScenarioWithBLOBs> scenarios = getExportResult(request);
|
||||
//环境检查
|
||||
checkExportEnv(scenarios);
|
||||
// checkExportEnv(scenarios);
|
||||
// 生成jmx
|
||||
Map<String, byte[]> files = new LinkedHashMap<>();
|
||||
scenarios.forEach(item -> {
|
||||
|
@ -1901,9 +1907,9 @@ public class ApiScenarioService {
|
|||
return apiIdList;
|
||||
}
|
||||
|
||||
public ScenarioEnv getApiScenarioProjectId(String id) {
|
||||
public EnvironmentCheckDTO getApiScenarioProjectId(String id) {
|
||||
ApiScenarioWithBLOBs scenario = apiScenarioMapper.selectByPrimaryKey(id);
|
||||
ScenarioEnv scenarioEnv = new ScenarioEnv();
|
||||
EnvironmentCheckDTO scenarioEnv = new EnvironmentCheckDTO();
|
||||
if (scenario == null) {
|
||||
return scenarioEnv;
|
||||
}
|
||||
|
@ -1931,7 +1937,7 @@ public class ApiScenarioService {
|
|||
example.createCriteria().andIdIn(request.getIds());
|
||||
List<ApiScenarioWithBLOBs> scenarioList = apiScenarioMapper.selectByExampleWithBLOBs(example);
|
||||
for (ApiScenarioWithBLOBs scenario : scenarioList) {
|
||||
ScenarioEnv scenarioEnv = new ScenarioEnv();
|
||||
EnvironmentCheckDTO scenarioEnv = new EnvironmentCheckDTO();
|
||||
if (scenario == null) {
|
||||
continue;
|
||||
}
|
||||
|
@ -2141,9 +2147,11 @@ public class ApiScenarioService {
|
|||
}
|
||||
|
||||
public boolean verifyScenarioEnv(String scenarioId) {
|
||||
ApiScenarioWithBLOBs apiScenarioWithBLOBs = apiScenarioMapper.selectByPrimaryKey(scenarioId);
|
||||
return true;
|
||||
// 暂时去除环境校验
|
||||
/*ApiScenarioWithBLOBs apiScenarioWithBLOBs = apiScenarioMapper.selectByPrimaryKey(scenarioId);
|
||||
apiScenarioEnvService.setScenarioEnv(apiScenarioWithBLOBs, null);
|
||||
return apiScenarioEnvService.verifyScenarioEnv(apiScenarioWithBLOBs);
|
||||
return apiScenarioEnvService.verifyScenarioEnv(apiScenarioWithBLOBs);*/
|
||||
}
|
||||
|
||||
public List<String> getFollows(String scenarioId) {
|
||||
|
@ -2157,9 +2165,12 @@ public class ApiScenarioService {
|
|||
return follows.stream().map(ApiScenarioFollow::getFollowId).distinct().collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public ScenarioEnv getApiScenarioEnv(byte[] request) {
|
||||
String definition = new String(request, StandardCharsets.UTF_8);
|
||||
return apiScenarioEnvService.getApiScenarioEnv(definition);
|
||||
public EnvironmentCheckDTO getApiScenarioEnv(List<String> projectIds) {
|
||||
List<Project> projects = baseProjectService.getProjectByIds(projectIds);
|
||||
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) {
|
||||
|
@ -2208,7 +2219,7 @@ public class ApiScenarioService {
|
|||
List<String> strings = new LinkedList<>();
|
||||
apiScenarios.forEach(item -> {
|
||||
if (StringUtils.isNotEmpty(item.getScenarioDefinition())) {
|
||||
ScenarioEnv env = apiScenarioEnvService.getApiScenarioEnv(item.getScenarioDefinition());
|
||||
EnvironmentCheckDTO env = apiScenarioEnvService.getApiScenarioEnv(item.getScenarioDefinition());
|
||||
if (!strings.contains(item.getProjectId())) {
|
||||
strings.add(item.getProjectId());
|
||||
}
|
||||
|
@ -2436,7 +2447,7 @@ public class ApiScenarioService {
|
|||
example.createCriteria().andIdIn(scenarioIdList);
|
||||
List<ApiScenarioWithBLOBs> scenarioWithBLOBsList = apiScenarioMapper.selectByExampleWithBLOBs(example);
|
||||
for (ApiScenarioWithBLOBs scenario : scenarioWithBLOBsList) {
|
||||
ScenarioEnv scenarioEnv = apiScenarioEnvService.getApiScenarioEnv(scenario.getScenarioDefinition());
|
||||
EnvironmentCheckDTO scenarioEnv = apiScenarioEnvService.getApiScenarioEnv(scenario.getScenarioDefinition());
|
||||
if (CollectionUtils.isNotEmpty(scenarioEnv.getProjectIds())) {
|
||||
scenarioEnv.getProjectIds().forEach(projectId -> {
|
||||
if (!returnDTO.getProjectIdList().contains(projectId)) {
|
||||
|
|
|
@ -27,10 +27,6 @@ export function getScenarioByProjectId(projectId) {
|
|||
return get('/api/automation/env-project-ids/' + projectId);
|
||||
}
|
||||
|
||||
export function checkScenarioEnv(scenarioId) {
|
||||
return get('/api/automation/env-valid/' + scenarioId);
|
||||
}
|
||||
|
||||
export function execStop(reportId) {
|
||||
return get('/api/automation/stop/' + reportId);
|
||||
}
|
||||
|
@ -109,14 +105,13 @@ export function getUploadConfig(url, formData) {
|
|||
url: url,
|
||||
data: formData,
|
||||
headers: {
|
||||
'Content-Type': "application/octet-stream",
|
||||
'Content-Type': 'application/octet-stream',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function getApiScenarioEnv(params) {
|
||||
let reqParams = getUploadConfig('/api/automation/scenario-env', params);
|
||||
return request( reqParams);
|
||||
return post('/api/automation/project-valid', params);
|
||||
}
|
||||
|
||||
export function batchEditScenario(params) {
|
||||
|
|
|
@ -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>
|
|
@ -402,7 +402,6 @@ import {
|
|||
batchEditScenario,
|
||||
batchGenPerformanceTestJmx,
|
||||
checkBeforeDelete,
|
||||
checkScenarioEnv,
|
||||
delByScenarioId,
|
||||
delByScenarioIdAndRefId,
|
||||
deleteBatchByCondition,
|
||||
|
@ -1370,16 +1369,9 @@ export default {
|
|||
}
|
||||
this.environmentType = this.currentScenario.environmentType;
|
||||
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.runVisible = true;
|
||||
this.$set(row, 'isStop', true);
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
|
|
@ -424,9 +424,6 @@
|
|||
:is-across-space="true"
|
||||
ref="scenarioRelevance" />
|
||||
|
||||
<!-- 环境 -->
|
||||
<api-environment-config v-if="type !== 'detail'" ref="environmentConfig" @close="environmentConfigClose" />
|
||||
|
||||
<!--执行组件-->
|
||||
<ms-run
|
||||
:debug="true"
|
||||
|
@ -564,7 +561,6 @@ import {
|
|||
} from '@/api/scenario';
|
||||
import { API_STATUS, PRIORITY } from '../../definition/model/JsonData';
|
||||
import { buttons, setComponent } from './menu/Menu';
|
||||
import { parseEnvironment } from '@/business/environment/model/EnvironmentModel';
|
||||
import { ELEMENT_TYPE, STEP, TYPE_TO_C } from './Setting';
|
||||
import { KeyValue } from '@/business/definition/model/ApiTestModel';
|
||||
import { getCurrentProjectID, getCurrentUser } from 'metersphere-frontend/src/utils/token';
|
||||
|
@ -581,7 +577,6 @@ import {
|
|||
import MsComponentConfig from './component/ComponentConfig';
|
||||
import { ENV_TYPE } from 'metersphere-frontend/src/utils/constants';
|
||||
import { mergeRequestDocumentData } from '@/business/definition/api-definition';
|
||||
import { getEnvironmentByProjectId } from 'metersphere-frontend/src/api/environment';
|
||||
import { useApiStore } from '@/store';
|
||||
import { getDefaultVersion, setLatestVersionById } from 'metersphere-frontend/src/api/version';
|
||||
|
||||
|
@ -722,7 +717,6 @@ export default {
|
|||
projectEnvMap: new Map(),
|
||||
projectList: [],
|
||||
drawer: false,
|
||||
isFullUrl: true,
|
||||
expandedStatus: false,
|
||||
stepEnable: true,
|
||||
envResult: {
|
||||
|
@ -809,7 +803,6 @@ export default {
|
|||
this.getWsProjects();
|
||||
this.getMaintainerOptions();
|
||||
this.getApiScenario();
|
||||
this.getEnvironments();
|
||||
this.buttonData = buttons(this);
|
||||
this.getPlugins().then(() => {
|
||||
this.initPlugins();
|
||||
|
@ -1329,13 +1322,6 @@ export default {
|
|||
this.debugLoading = true;
|
||||
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;
|
||||
}
|
||||
this.initParameter();
|
||||
this.debugData = {
|
||||
id: this.currentScenario.id,
|
||||
|
@ -1799,15 +1785,6 @@ export default {
|
|||
/*触发执行操作*/
|
||||
this.$refs.currentScenario.validate(async (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;
|
||||
if (runScenario && runScenario.type === 'scenario') {
|
||||
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() {
|
||||
// 如果是复制按钮创建的场景,直接进行保存
|
||||
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) {
|
||||
if (node.data && node.data.disabled && node.parent.data && node.parent.data.disabled) {
|
||||
return false;
|
||||
|
@ -1999,16 +1956,21 @@ export default {
|
|||
},
|
||||
getEnv(definition) {
|
||||
return new Promise((resolve) => {
|
||||
const encoder = new TextEncoder();
|
||||
const bytes = encoder.encode(definition, 'utf-8');
|
||||
getApiScenarioEnv(bytes).then((res) => {
|
||||
if (res.data && res.data.data) {
|
||||
this.projectIds = new Set(res.data.data.projectIds);
|
||||
this.projectIds.add(this.projectId);
|
||||
this.isFullUrl = res.data.data.fullUrl;
|
||||
this.projectIds = new Set();
|
||||
const regex = /"projectId"\s*:\s*"([^"]+)"/g;
|
||||
let match;
|
||||
while ((match = regex.exec(definition)) !== null) {
|
||||
this.projectIds.add(match[1]);
|
||||
}
|
||||
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();
|
||||
});
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
},
|
||||
getApiScenario(isRefresh) {
|
||||
|
|
|
@ -1,11 +1,69 @@
|
|||
<template>
|
||||
<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
|
||||
v-model="pe['selectEnv']"
|
||||
filterable
|
||||
: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">
|
||||
<el-option
|
||||
v-for="(environment, index) in pe.envs"
|
||||
|
@ -33,10 +91,11 @@
|
|||
</div>
|
||||
</template>
|
||||
</el-select>
|
||||
<span class="project-name" :title="getProjectName(pe.id)">
|
||||
{{ getProjectName(pe.id) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-collapse-transition>
|
||||
</div>
|
||||
|
||||
<el-button type="primary" @click="handleConfirm" size="small" :style="btnStyle" class="env-confirm">
|
||||
{{ $t('workspace.env_group.confirm') }}
|
||||
|
@ -50,8 +109,9 @@
|
|||
<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';
|
||||
import { getEnvironmentByProjectId, getEnvironmentByProjectIds } from 'metersphere-frontend/src/api/environment';
|
||||
import { getOwnerProjectIds } from '@/api/project';
|
||||
import { getCurrentProjectID } from 'metersphere-frontend/src/utils/token';
|
||||
|
||||
export default {
|
||||
name: 'EnvironmentSelect',
|
||||
|
@ -87,9 +147,17 @@ export default {
|
|||
permissionProjectIds: [],
|
||||
dialogVisible: false,
|
||||
isFullUrl: true,
|
||||
currentProjectEnv: [],
|
||||
isActive: false,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
active() {
|
||||
this.isActive = !this.isActive;
|
||||
},
|
||||
currentProjectID() {
|
||||
return getCurrentProjectID();
|
||||
},
|
||||
isShowConfirmButton(projectId) {
|
||||
if (this.showConfigButtonWithOutPermission === true) {
|
||||
return true;
|
||||
|
@ -112,32 +180,44 @@ export default {
|
|||
}
|
||||
|
||||
let arr = [];
|
||||
this.currentProjectEnv = [];
|
||||
this.projectIds.forEach((id) => {
|
||||
const project = this.projectList.find((p) => p.id === id);
|
||||
if (project) {
|
||||
let item = { id: id, envs: [], selectEnv: '' };
|
||||
if (this.currentProjectID() === project.id) {
|
||||
this.currentProjectEnv.push(item);
|
||||
} else {
|
||||
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);
|
||||
temp.envs = envs;
|
||||
let temp =
|
||||
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;
|
||||
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();
|
||||
});
|
||||
});
|
||||
arr.push(p);
|
||||
}
|
||||
});
|
||||
return arr;
|
||||
},
|
||||
getUserPermissionProjectIds() {
|
||||
|
@ -156,6 +236,9 @@ export default {
|
|||
return Promise.all(this.init());
|
||||
},
|
||||
getProjectName(id) {
|
||||
if (id === getCurrentProjectID()) {
|
||||
return this.$t('commons.current_project');
|
||||
}
|
||||
const project = this.projectList.find((p) => p.id === id);
|
||||
return project ? project.name : '';
|
||||
},
|
||||
|
@ -168,31 +251,30 @@ export default {
|
|||
},
|
||||
handleConfirm() {
|
||||
let map = new Map();
|
||||
let sign = true;
|
||||
this.data.forEach((dt) => {
|
||||
this.currentProjectEnv.forEach((dt) => {
|
||||
if (!dt.selectEnv) {
|
||||
sign = false;
|
||||
return;
|
||||
}
|
||||
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;
|
||||
}
|
||||
map.set(dt.id, dt.selectEnv);
|
||||
});
|
||||
this.$emit('setProjectEnvMap', map);
|
||||
this.$emit('close');
|
||||
},
|
||||
checkEnv(data) {
|
||||
let sign = true;
|
||||
this.isFullUrl = true;
|
||||
if (data) {
|
||||
return true;
|
||||
}
|
||||
if (this.data.length > 0) {
|
||||
this.data.forEach((dt) => {
|
||||
if (this.currentProjectEnv.length > 0) {
|
||||
this.currentProjectEnv.forEach((dt) => {
|
||||
if (!dt.selectEnv) {
|
||||
sign = false;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
@ -201,17 +283,11 @@ export default {
|
|||
if (this.envMap && this.envMap.size > 0) {
|
||||
this.projectIds.forEach((id) => {
|
||||
if (!this.envMap.get(id)) {
|
||||
sign = false;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (!sign) {
|
||||
this.$warning(this.$t('workspace.env_group.please_select_env_for_current_scenario'));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
environmentConfigClose() {
|
||||
|
@ -232,13 +308,24 @@ export default {
|
|||
margin-top: 10px;
|
||||
}
|
||||
|
||||
:deep(.el-collapse-item__arrow) {
|
||||
margin: 0 0px 0px 8px;
|
||||
}
|
||||
|
||||
.project-name {
|
||||
display: inline-block;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
width: 150px;
|
||||
margin-left: 8px;
|
||||
width: 100px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.icon.is-active {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.el-icon-arrow-right {
|
||||
margin-right: 3px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -210,7 +210,6 @@ export default {
|
|||
this.basisData.method = this.basisData.protocol;
|
||||
this.$emit('saveApi', this.basisData);
|
||||
},
|
||||
runTest() {},
|
||||
itselfEnvironment(environmentId) {
|
||||
let id = this.request.projectId ? this.request.projectId : this.projectId;
|
||||
this.result = getEnvironmentByProjectId(id).then((response) => {
|
||||
|
@ -240,9 +239,22 @@ export default {
|
|||
this.initDataSource(undefined, undefined, targetDataSourceName);
|
||||
});
|
||||
},
|
||||
getEnvironments(environmentId, isCreated) {
|
||||
let envId = '';
|
||||
// 跨项目步骤如果没有环境则走当前场景环境
|
||||
async selectProjectId(environmentId) {
|
||||
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;
|
||||
if (store.scenarioEnvMap && store.scenarioEnvMap instanceof Map && store.scenarioEnvMap.has(scenarioEnvId)) {
|
||||
envId = store.scenarioEnvMap.get(scenarioEnvId);
|
||||
|
@ -298,7 +310,7 @@ export default {
|
|||
}
|
||||
});
|
||||
if (!hasEnvironment) {
|
||||
this.request.environmentId = "";
|
||||
this.request.environmentId = '';
|
||||
}
|
||||
this.initDataSource(envId, currentEnvironment, targetDataSourceName);
|
||||
});
|
||||
|
@ -402,14 +414,6 @@ export default {
|
|||
margin-left: 60px;
|
||||
}
|
||||
|
||||
.ms-left-cell {
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
.ms-left-buttion {
|
||||
margin: 6px 0px 8px 30px;
|
||||
}
|
||||
|
||||
.environment-button {
|
||||
margin-left: 20px;
|
||||
padding: 7px;
|
||||
|
|
|
@ -112,7 +112,7 @@ import { getCurrentProjectID, getCurrentWorkspaceId } from 'metersphere-frontend
|
|||
import { getUUID, strMapToObj } from 'metersphere-frontend/src/utils';
|
||||
import { STEP } from '@/business/automation/scenario/Setting';
|
||||
import { getOwnerProjectIds, getProject } from '@/api/project';
|
||||
import { checkScenarioEnv, getScenarioById, setScenarioDomain } from '@/api/scenario';
|
||||
import { getScenarioById, setScenarioDomain } from '@/api/scenario';
|
||||
|
||||
export default {
|
||||
name: 'ApiScenarioComponent',
|
||||
|
@ -228,14 +228,7 @@ export default {
|
|||
this.reload();
|
||||
},
|
||||
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);
|
||||
});
|
||||
},
|
||||
setDomain(val) {
|
||||
let param = {
|
||||
|
|
|
@ -70,7 +70,7 @@ import MsAddBasisApi from '../api/AddBasisApi';
|
|||
import MsAddApiCase from '../api/AddApiCase';
|
||||
import { getUUID, strMapToObj } from 'metersphere-frontend/src/utils';
|
||||
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';
|
||||
|
||||
export default {
|
||||
|
@ -176,14 +176,7 @@ export default {
|
|||
});
|
||||
},
|
||||
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);
|
||||
});
|
||||
},
|
||||
setDomain(val) {
|
||||
let param = {
|
||||
|
|
|
@ -46,7 +46,9 @@
|
|||
</div>
|
||||
</el-col>
|
||||
<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">
|
||||
{{ $t('commons.failure_continues') }}
|
||||
</el-checkbox>
|
||||
|
@ -89,7 +91,12 @@
|
|||
|
||||
<!-- 场景步骤-->
|
||||
<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">
|
||||
<span style="float: right">
|
||||
<span class="ms-message-right"> {{ reqTotalTime }} ms </span>
|
||||
|
@ -116,8 +123,7 @@
|
|||
@node-drag-end="allowDrag"
|
||||
@node-click="nodeClick"
|
||||
class="ms-max-tree"
|
||||
ref="maxStepTree"
|
||||
>
|
||||
ref="maxStepTree">
|
||||
<el-row
|
||||
class="custom-tree-node"
|
||||
:gutter="18"
|
||||
|
@ -224,8 +230,7 @@
|
|||
@openScenario="openScenario"
|
||||
@runScenario="runScenario"
|
||||
@stopScenario="stopScenario"
|
||||
v-if="selectedTreeNode && selectedNode"
|
||||
/>
|
||||
v-if="selectedTreeNode && selectedNode" />
|
||||
<!-- 请求下还有的子步骤-->
|
||||
<div v-if="selectedTreeNode && selectedTreeNode.hashTree && showNode(selectedTreeNode)">
|
||||
<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" />
|
||||
|
||||
<!-- 环境 -->
|
||||
<api-environment-config v-if="type !== 'detail'" ref="environmentConfig" @close="environmentConfigClose" />
|
||||
|
||||
<!--执行组件-->
|
||||
<ms-run
|
||||
:debug="true"
|
||||
|
@ -318,21 +320,16 @@
|
|||
<script>
|
||||
import { getApiScenarioEnv } from '@/api/scenario';
|
||||
import { API_STATUS, PRIORITY } from '../../../definition/model/JsonData';
|
||||
import { parseEnvironment } from '@/business/environment/model/EnvironmentModel';
|
||||
import { STEP } from '../Setting';
|
||||
import { getUUID, strMapToObj } from 'metersphere-frontend/src/utils';
|
||||
import { getCurrentProjectID } from 'metersphere-frontend/src/utils/token';
|
||||
import { hasLicense } from 'metersphere-frontend/src/utils/permission';
|
||||
import OutsideClick from '../common/outside-click';
|
||||
import { copyScenarioRow, saveScenario, scenarioSort, handleCtrlSEvent } from '@/business/automation/api-automation';
|
||||
import { buttons, setComponent } from '../menu/Menu';
|
||||
import MsContainer from 'metersphere-frontend/src/components/MsContainer';
|
||||
import MsMainContainer from 'metersphere-frontend/src/components/MsMainContainer';
|
||||
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 { getPluginList } from '@/api/plugin';
|
||||
|
||||
const store = useApiStore();
|
||||
let jsonPath = require('jsonpath');
|
||||
|
@ -535,19 +532,19 @@ export default {
|
|||
},
|
||||
},
|
||||
methods: {
|
||||
handleHeaderClick(e){
|
||||
if(e.target.tagName === 'DIV'){
|
||||
this.outsideClick(e)
|
||||
handleHeaderClick(e) {
|
||||
if (e.target.tagName === 'DIV') {
|
||||
this.outsideClick(e);
|
||||
}
|
||||
},
|
||||
handleMainClick(e) {
|
||||
this.outsideClick(e)
|
||||
this.outsideClick(e);
|
||||
},
|
||||
handleComponentClick(e) {
|
||||
e.stopPropagation();
|
||||
},
|
||||
initPlugins() {
|
||||
if(this.pluginList && this.pluginList.length > 0){
|
||||
if (this.pluginList && this.pluginList.length > 0) {
|
||||
this.pluginList.forEach((item) => {
|
||||
let plugin = {
|
||||
title: item.name,
|
||||
|
@ -813,56 +810,12 @@ export default {
|
|||
});
|
||||
},
|
||||
debugScenario() {
|
||||
if(this.scenarioDefinition.length < 1){
|
||||
if (this.scenarioDefinition.length < 1) {
|
||||
return;
|
||||
}
|
||||
this.debugLoading = true;
|
||||
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() {
|
||||
// 如果是复制按钮创建的场景,直接进行保存
|
||||
|
@ -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) {
|
||||
if (draggingNode.data.type === 'Assertions' || dropNode.data.type === 'Assertions') {
|
||||
return false;
|
||||
|
@ -1324,16 +1267,21 @@ export default {
|
|||
},
|
||||
getEnv(definition) {
|
||||
return new Promise((resolve) => {
|
||||
const encoder = new TextEncoder();
|
||||
const bytes = encoder.encode(definition, 'utf-8');
|
||||
getApiScenarioEnv(bytes).then((res) => {
|
||||
if (res.data && res.data.data) {
|
||||
res.data.data.projectIds.push(this.projectId);
|
||||
this.$emit('update:projectIds', new Set(res.data.data.projectIds));
|
||||
this.$emit('update:isFullUrl', res.data.data.fullUrl);
|
||||
this.projectIds = new Set();
|
||||
const regex = /"projectId"\s*:\s*"([^"]+)"/g;
|
||||
let match;
|
||||
while ((match = regex.exec(definition)) !== null) {
|
||||
this.projectIds.add(match[1]);
|
||||
}
|
||||
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();
|
||||
});
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
|
@ -1516,5 +1464,4 @@ export default {
|
|||
white-space: nowrap;
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
|
@ -52,6 +52,10 @@
|
|||
<span>{{ $t('load_test.runtime_config') }}</span>
|
||||
<div style="padding-top: 10px">
|
||||
<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
|
||||
:project-ids="projectIds"
|
||||
:placement="'bottom-start'"
|
||||
|
@ -64,15 +68,13 @@
|
|||
@setProjectEnvMap="setProjectEnvMap"
|
||||
@showPopover="showPopover"
|
||||
ref="envPopover"
|
||||
class="env-popover" />
|
||||
class="env-popover"
|
||||
v-show="this.runConfig.defaultEnv === false" />
|
||||
</div>
|
||||
<div class="ms-mode-div">
|
||||
<span class="ms-mode-span">{{ $t('run_mode.other_config') }}:</span>
|
||||
<span>{{ $t('run_mode.run_with_resource_pool') }}:</span>
|
||||
<el-select
|
||||
style="margin-left: 10px"
|
||||
v-model="runConfig.resourcePoolId"
|
||||
size="mini">
|
||||
<el-select style="margin-left: 10px" v-model="runConfig.resourcePoolId" size="mini">
|
||||
<el-option
|
||||
v-for="item in resourcePools"
|
||||
:key="item.id"
|
||||
|
@ -87,9 +89,8 @@
|
|||
<crontab @hide="showCron = false" @fill="crontabFill" :expression="schedule.value" ref="crontab" />
|
||||
</el-dialog>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('schedule.task_notification')" name="second"
|
||||
v-permission="['PROJECT_MESSAGE:READ']">
|
||||
<ms-schedule-notification :test-id="testId" :schedule-receiver-options="scheduleReceiverOptions"/>
|
||||
<el-tab-pane :label="$t('schedule.task_notification')" name="second" v-permission="['PROJECT_MESSAGE:READ']">
|
||||
<ms-schedule-notification :test-id="testId" :schedule-receiver-options="scheduleReceiverOptions" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
|
@ -188,6 +189,7 @@ export default {
|
|||
envMap: {},
|
||||
environmentGroupId: '',
|
||||
environmentType: ENV_TYPE.JSON,
|
||||
defaultEnv: true,
|
||||
},
|
||||
projectList: [],
|
||||
projectIds: new Set(),
|
||||
|
@ -195,9 +197,9 @@ export default {
|
|||
};
|
||||
},
|
||||
methods: {
|
||||
async checkPool(){
|
||||
async checkPool() {
|
||||
let hasPool = false;
|
||||
this.resourcePools.forEach(item => {
|
||||
this.resourcePools.forEach((item) => {
|
||||
if (item.id === this.runConfig.resourcePoolId) {
|
||||
hasPool = true;
|
||||
}
|
||||
|
@ -208,7 +210,7 @@ export default {
|
|||
let hasPool = await this.checkPool();
|
||||
if (!hasPool) {
|
||||
this.runConfig.resourcePoolId = null;
|
||||
getProjectConfig(getCurrentProjectID(), "").then(async (res) => {
|
||||
getProjectConfig(getCurrentProjectID(), '').then(async (res) => {
|
||||
if (res.data && res.data.poolEnable && res.data.resourcePoolId) {
|
||||
this.runConfig.resourcePoolId = res.data.resourcePoolId;
|
||||
}
|
||||
|
@ -219,9 +221,6 @@ export default {
|
|||
});
|
||||
}
|
||||
},
|
||||
currentUser: () => {
|
||||
return getCurrentUser();
|
||||
},
|
||||
intervalValidate() {
|
||||
if (this.getIntervalTime() < 1 * 60 * 1000) {
|
||||
return false;
|
||||
|
@ -322,6 +321,10 @@ export default {
|
|||
this.schedule = response.data;
|
||||
if (response.data.config) {
|
||||
this.runConfig = JSON.parse(response.data.config);
|
||||
// 兼容历史数据
|
||||
if (this.runConfig.defaultEnv === null) {
|
||||
this.runConfig.defaultEnv = false;
|
||||
}
|
||||
if (this.runConfig.envMap) {
|
||||
this.projectEnvListMap = objToStrMap(this.runConfig.envMap);
|
||||
} else {
|
||||
|
@ -384,12 +387,11 @@ export default {
|
|||
}
|
||||
if (this.schedule.enable) {
|
||||
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.$warning(this.$t('workspace.env_group.please_select_env_for_current_scenario'));
|
||||
return;
|
||||
}
|
||||
)
|
||||
if (this.runConfig.resourcePoolId == null) {
|
||||
this.$warning(this.$t('workspace.env_group.please_select_run_within_resource_pool'));
|
||||
return;
|
||||
|
|
|
@ -132,13 +132,19 @@ const message = {
|
|||
automation: {
|
||||
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',
|
||||
scenario_step_ref_message:
|
||||
'The current may cause page loading exceptions, whether to continue?',
|
||||
scenario_step_ref_message: 'The current may cause page loading exceptions, whether to continue?',
|
||||
case_message: 'Please select a case',
|
||||
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_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!',
|
||||
scenario_plugin_debug_warning:
|
||||
'The scenario contains plugin steps, and the corresponding scenario has been deleted and cannot be debugged! ',
|
||||
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 {
|
||||
|
|
|
@ -134,6 +134,11 @@ const message = {
|
|||
scenario_plugin_debug_warning: '场景包含插件步骤,对应场景已经删除不能调试!',
|
||||
scenario_plugin_save_warning: '场景包含插件步骤,对应场景已经删除不能编辑!',
|
||||
scenario_plugin_run_warning: '场景包含插件步骤,对应场景已经删除不能执行!',
|
||||
project_env_info: '更多项目环境',
|
||||
project_env_info_tips:
|
||||
'1.当前场景涉及到跨项目的步骤可以指定其所属项目的环境执行,不指定则默认使用当前项目环境执行。\n 2.如跨项目的步骤在场景设置中配置了使用原场景环境,则默认使用原场景环境执行。',
|
||||
default_environment: '默认环境',
|
||||
select_new_environment: '指定新环境',
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -131,9 +131,12 @@ const message = {
|
|||
scenario_step_ref_message: '當前操作可能導致頁面加載異常,是否繼續',
|
||||
case_message: '請選擇案例',
|
||||
scenario_message: '請選擇場景',
|
||||
scenario_plugin_debug_warning:'場景包含挿件步驟,對應場景已經刪除不能調試! ',
|
||||
scenario_plugin_debug_warning: '場景包含挿件步驟,對應場景已經刪除不能調試! ',
|
||||
scenario_plugin_save_warning: '場景包含挿件步驟,對應場景已經刪除不能編輯! ',
|
||||
scenario_plugin_run_warning: '場景包含挿件步驟,對應場景已經刪除不能運行! '
|
||||
scenario_plugin_run_warning: '場景包含挿件步驟,對應場景已經刪除不能運行! ',
|
||||
project_env_info: '更多項目環境',
|
||||
project_env_info_tips:
|
||||
'1.當前場景涉及到跨項目的步驟可以指定其所屬項目的環境執行,不指定則默認使用當前項目環境執行。 \n 2.如跨項目的步驟在場景設置中配置了使用原場景環境,則默認使用原場景環境執行。 ',
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import {get, post, request} from "../plugins/request"
|
||||
import { get, post, request } from '../plugins/request';
|
||||
// 获取使用当前js模块的package.json,不要修改引入路径
|
||||
import packageInfo from '@/../package.json'
|
||||
import packageInfo from '@/../package.json';
|
||||
const currentModuleName = packageInfo.name;
|
||||
|
||||
export function getEnvironmentMapByGroupId(id) {
|
||||
|
@ -10,6 +10,10 @@ export function getEnvironmentMapByGroupId(id) {
|
|||
export function getEnvironmentByProjectId(projectId) {
|
||||
return get('/environment/list/' + projectId);
|
||||
}
|
||||
// 不含环境的blob数据
|
||||
export function getEnvironmentByProjectIds(projectIds) {
|
||||
return post('/environment/project-env', projectIds);
|
||||
}
|
||||
|
||||
export function getEnvironmentById(environmentId) {
|
||||
return get('/environment/get/' + environmentId);
|
||||
|
@ -84,23 +88,26 @@ export function databaseValidate(params) {
|
|||
|
||||
export function getUploadConfig(url, formData) {
|
||||
return {
|
||||
method: 'POST', url: url, data: formData, headers: {
|
||||
'Content-Type': undefined
|
||||
}
|
||||
method: 'POST',
|
||||
url: url,
|
||||
data: formData,
|
||||
headers: {
|
||||
'Content-Type': undefined,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function fileUpload(url, file, files, param) {
|
||||
let formData = new FormData();
|
||||
if (file) {
|
||||
formData.append("file", file);
|
||||
formData.append('file', file);
|
||||
}
|
||||
if (files) {
|
||||
files.forEach(f => {
|
||||
formData.append("files", f);
|
||||
files.forEach((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);
|
||||
return request(config);
|
||||
}
|
||||
|
@ -150,7 +157,7 @@ export function getModuleByUrl(url) {
|
|||
}
|
||||
|
||||
export function getCaseRelateModuleByCondition(url, params) {
|
||||
return post('/environment/relate' + url, params)
|
||||
return post('/environment/relate' + url, params);
|
||||
}
|
||||
|
||||
export function getCodeSnippetPages(goPage, pageSize, params) {
|
||||
|
|
|
@ -14,6 +14,7 @@ public class RunModeConfigDTO {
|
|||
private String testId;
|
||||
private String amassReport;
|
||||
private boolean onSampleError;
|
||||
private Boolean defaultEnv;
|
||||
// 失败重试
|
||||
private boolean retryEnable;
|
||||
// 失败重试次数
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -18,11 +18,11 @@
|
|||
package org.apache.jmeter.services;
|
||||
|
||||
import io.metersphere.utils.LoggerUtil;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.io.input.BOMInputStream;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.jmeter.gui.JMeterFileFilter;
|
||||
import org.apache.jmeter.save.CSVSaveService;
|
||||
import org.apache.jmeter.threads.JMeterContextService;
|
||||
import org.apache.jmeter.util.JMeterUtils;
|
||||
import org.apache.jorphan.util.JOrphanUtils;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -30,7 +30,10 @@ import org.slf4j.LoggerFactory;
|
|||
|
||||
import java.io.*;
|
||||
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;
|
||||
|
||||
/**
|
||||
|
@ -207,7 +210,7 @@ public class FileServer {
|
|||
* @param filename - relative (to base) or absolute file name (must not be null)
|
||||
*/
|
||||
public void reserveFile(String filename) {
|
||||
reserveFile(filename,null);
|
||||
reserveFile(filename, null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -251,6 +254,16 @@ public class FileServer {
|
|||
if (alias == 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);
|
||||
if (fileEntry == null) {
|
||||
fileEntry = new FileEntry(resolveFileFromPath(filename), null, charsetName);
|
||||
|
@ -282,6 +295,7 @@ public class FileServer {
|
|||
* Resolves file name into {@link File} instance.
|
||||
* When filename is not absolute and not found from current working dir,
|
||||
* it tries to find it under current base directory
|
||||
*
|
||||
* @param filename original file name
|
||||
* @return {@link File} instance
|
||||
*/
|
||||
|
@ -328,6 +342,11 @@ public class FileServer {
|
|||
*/
|
||||
public synchronized String readLine(String filename, boolean recycle,
|
||||
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);
|
||||
if (fileEntry != null) {
|
||||
if (fileEntry.inputOutputObject == null) {
|
||||
|
@ -350,11 +369,13 @@ public class FileServer {
|
|||
log.debug("Read:{}", 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 recycle whether the file should be re-started on EOF
|
||||
* @param ignoreFirstLine whether the file contains a file header which will be ignored
|
||||
|
@ -377,6 +398,11 @@ public class FileServer {
|
|||
* @return {@link BufferedReader}
|
||||
*/
|
||||
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);
|
||||
if (fileEntry != null) {
|
||||
BufferedReader reader;
|
||||
|
@ -409,7 +435,7 @@ public class FileServer {
|
|||
}
|
||||
return reader;
|
||||
} else {
|
||||
throw new IOException("File never reserved: "+alias);
|
||||
throw new IOException("File never reserved: " + alias);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -433,6 +459,10 @@ public class FileServer {
|
|||
}
|
||||
|
||||
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);
|
||||
if (fileEntry != null) {
|
||||
if (fileEntry.inputOutputObject == null) {
|
||||
|
@ -444,7 +474,7 @@ public class FileServer {
|
|||
log.debug("Write:{}", value);
|
||||
writer.write(value);
|
||||
} else {
|
||||
throw new IOException("File never reserved: "+filename);
|
||||
throw new IOException("File never reserved: " + filename);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -464,49 +494,36 @@ public class FileServer {
|
|||
|
||||
public synchronized void closeFiles() throws IOException {
|
||||
for (Map.Entry<String, FileEntry> me : files.entrySet()) {
|
||||
closeFile(me.getKey(), me.getValue() );
|
||||
closeFile(me.getKey(), me.getValue());
|
||||
}
|
||||
files.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据线程名字清理掉当前线程缓存的csv
|
||||
*
|
||||
* @param name 线程组名称
|
||||
*/
|
||||
public synchronized void closeCsv(String name) {
|
||||
public synchronized void closeFiles(String threadName) {
|
||||
try {
|
||||
if (StringUtils.isNotEmpty(name)) {
|
||||
List<String> list = new ArrayList<>();
|
||||
for (Iterator<String> iterator = files.keySet().iterator(); iterator.hasNext(); ) {
|
||||
String key = iterator.next();
|
||||
if (key.contains(name)) {
|
||||
FileEntry fileEntry = files.get(key);
|
||||
closeFile(name, fileEntry);
|
||||
list.add(key);
|
||||
for (Iterator<Map.Entry<String, FileEntry>> it = files.entrySet().iterator(); it.hasNext(); ) {
|
||||
Map.Entry<String, FileEntry> entry = it.next();
|
||||
if (entry.getKey().startsWith(threadName)) {
|
||||
closeFile(entry.getKey(), entry.getValue());
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
if (CollectionUtils.isNotEmpty(list)) {
|
||||
for (String key : list) {
|
||||
files.remove(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
LoggerUtil.info("在处理中的CSV数量:" + files.size());
|
||||
LoggerUtil.info("清除当前线程组占用文件后剩余文件数:" + files.size() + " 个", threadName);
|
||||
} 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
|
||||
* @throws IOException when closing of the aliased file fails
|
||||
*/
|
||||
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);
|
||||
closeFile(name, fileEntry);
|
||||
}
|
||||
|
@ -582,8 +599,7 @@ public class FileServer {
|
|||
* "jmeter.save.saveservice.base_prefix" - default "~/" - then the name is
|
||||
* assumed to be relative to the basename.
|
||||
*
|
||||
* @param relativeName
|
||||
* filename that should be checked for
|
||||
* @param relativeName filename that should be checked for
|
||||
* <code>jmeter.save.saveservice.base_prefix</code>
|
||||
* @return the updated filename
|
||||
*/
|
||||
|
|
|
@ -32,6 +32,7 @@ import org.apache.jmeter.gui.GuiPackage;
|
|||
import org.apache.jmeter.processor.PostProcessor;
|
||||
import org.apache.jmeter.processor.PreProcessor;
|
||||
import org.apache.jmeter.samplers.*;
|
||||
import org.apache.jmeter.services.FileServer;
|
||||
import org.apache.jmeter.testbeans.TestBeanHelper;
|
||||
import org.apache.jmeter.testelement.*;
|
||||
import org.apache.jmeter.threads.JMeterContext.TestLogicalAction;
|
||||
|
@ -334,6 +335,7 @@ public class JMeterThread implements Runnable, Interruptible {
|
|||
threadFinished(iterationListener);
|
||||
monitor.threadFinished(this); // Tell the monitor we are done
|
||||
JMeterContextService.removeContext(); // Remove the ThreadLocal entry
|
||||
FileServer.getFileServer().closeFiles(threadName);
|
||||
} finally {
|
||||
interruptLock.unlock(); // Allow any pending interrupt to complete (OK because currentSampler == null)
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package io.metersphere.environment.controller;
|
|||
|
||||
import com.github.pagehelper.Page;
|
||||
import com.github.pagehelper.PageHelper;
|
||||
import io.metersphere.base.domain.ApiTestEnvironment;
|
||||
import io.metersphere.base.domain.ApiTestEnvironmentWithBLOBs;
|
||||
import io.metersphere.base.domain.EnvironmentGroup;
|
||||
import io.metersphere.commons.constants.OperLogConstants;
|
||||
|
@ -45,6 +46,11 @@ public class TestEnvironmentController {
|
|||
return baseEnvironmentService.list(projectId);
|
||||
}
|
||||
|
||||
@PostMapping("/project-env")
|
||||
public List<ApiTestEnvironment> projectEnv(@RequestBody List<String> projectIds) {
|
||||
return baseEnvironmentService.selectList(projectIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询指定项目和指定名称的环境
|
||||
*
|
||||
|
|
|
@ -370,6 +370,16 @@ public class BaseEnvironmentService extends NodeTreeService<ApiModuleDTO> {
|
|||
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) {
|
||||
return apiTestEnvironmentMapper.selectByPrimaryKey(id);
|
||||
}
|
||||
|
|
|
@ -1,21 +1,41 @@
|
|||
<template>
|
||||
<div v-loading="result.loading">
|
||||
<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')"
|
||||
style="margin-top: 8px;width: 200px;" size="small">
|
||||
<el-option v-for="(environment, index) in pe.envs" :key="index"
|
||||
<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')"
|
||||
style="margin-top: 8px; width: 200px"
|
||||
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') }}
|
||||
: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>
|
||||
<!--这里只做没有可搜索内容时使用,否则如果没有符合搜索条件的,也会显示该项,与上面的btn重复显示 -->
|
||||
<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') }}
|
||||
<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>
|
||||
|
@ -25,104 +45,114 @@
|
|||
</span>
|
||||
</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>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {parseEnvironment} from "metersphere-frontend/src/model/EnvironmentModel";
|
||||
import { parseEnvironment } from "metersphere-frontend/src/model/EnvironmentModel";
|
||||
import ApiEnvironmentConfig from "@/business/module/api/ApiEnvironmentConfig";
|
||||
import {getEnvironmentByProjectId} from "metersphere-frontend/src/api/environment";
|
||||
import {getOwnerProjectIds} from "@/api/project";
|
||||
import { getEnvironmentByProjectId } from "metersphere-frontend/src/api/environment";
|
||||
import { getOwnerProjectIds } from "@/api/project";
|
||||
|
||||
export default {
|
||||
name: "EnvironmentSelect",
|
||||
components: {ApiEnvironmentConfig},
|
||||
components: { ApiEnvironmentConfig },
|
||||
props: {
|
||||
envMap: Map,
|
||||
projectIds: Set,
|
||||
projectList: Array,
|
||||
showConfigButtonWithOutPermission:{
|
||||
showConfigButtonWithOutPermission: {
|
||||
type: Boolean,
|
||||
default() {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
},
|
||||
result: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {loading: false}
|
||||
}
|
||||
}
|
||||
return { loading: false };
|
||||
},
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
data: [],
|
||||
projects: [],
|
||||
environments: [],
|
||||
permissionProjectIds:[],
|
||||
permissionProjectIds: [],
|
||||
dialogVisible: false,
|
||||
isFullUrl: true,
|
||||
}
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
isShowConfirmButton(projectId){
|
||||
if(this.showConfigButtonWithOutPermission === true){
|
||||
isShowConfirmButton(projectId) {
|
||||
if (this.showConfigButtonWithOutPermission === true) {
|
||||
return true;
|
||||
}else{
|
||||
if(this.permissionProjectIds){
|
||||
if(this.permissionProjectIds.indexOf(projectId)<0){
|
||||
} else {
|
||||
if (this.permissionProjectIds) {
|
||||
if (this.permissionProjectIds.indexOf(projectId) < 0) {
|
||||
return false;
|
||||
}else {
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}else{
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
},
|
||||
init() {
|
||||
//获取当前用户有权限的ID
|
||||
if(this.permissionProjectIds.length === 0){
|
||||
if (this.permissionProjectIds.length === 0) {
|
||||
this.getUserPermissionProjectIds();
|
||||
}
|
||||
|
||||
let arr = [];
|
||||
this.projectIds.forEach(id => {
|
||||
const project = this.projectList.find(p => p.id === id);
|
||||
this.projectIds.forEach((id) => {
|
||||
const project = this.projectList.find((p) => p.id === id);
|
||||
if (project) {
|
||||
let item = {id: id, envs: [], selectEnv: ""};
|
||||
let item = { id: id, envs: [], selectEnv: "" };
|
||||
this.data.push(item);
|
||||
let p = new Promise(resolve => {
|
||||
getEnvironmentByProjectId(id).then(res => {
|
||||
let p = new Promise((resolve) => {
|
||||
getEnvironmentByProjectId(id).then((res) => {
|
||||
let envs = res.data;
|
||||
envs.forEach(environment => {
|
||||
envs.forEach((environment) => {
|
||||
parseEnvironment(environment);
|
||||
});
|
||||
// 固定环境列表渲染顺序
|
||||
let temp = this.data.find(dt => dt.id === id);
|
||||
let temp = this.data.find((dt) => dt.id === id);
|
||||
temp.envs = envs;
|
||||
let envId = undefined;
|
||||
if (this.envMap) {
|
||||
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();
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
arr.push(p);
|
||||
}
|
||||
})
|
||||
});
|
||||
return arr;
|
||||
},
|
||||
getUserPermissionProjectIds(){
|
||||
getOwnerProjectIds().then(res => {
|
||||
getUserPermissionProjectIds() {
|
||||
getOwnerProjectIds().then((res) => {
|
||||
this.permissionProjectIds = res.data;
|
||||
})
|
||||
});
|
||||
},
|
||||
open() {
|
||||
this.data = [];
|
||||
|
@ -135,12 +165,12 @@ export default {
|
|||
return Promise.all(this.init());
|
||||
},
|
||||
getProjectName(id) {
|
||||
const project = this.projectList.find(p => p.id === id);
|
||||
const project = this.projectList.find((p) => p.id === id);
|
||||
return project ? project.name : "";
|
||||
},
|
||||
openEnvironmentConfig(projectId, envId) {
|
||||
if (!projectId) {
|
||||
this.$error(this.$t('api_test.select_project'));
|
||||
this.$error(this.$t("api_test.select_project"));
|
||||
return;
|
||||
}
|
||||
this.$refs.environmentConfig.open(projectId, envId);
|
||||
|
@ -148,56 +178,47 @@ export default {
|
|||
handleConfirm() {
|
||||
let map = new Map();
|
||||
let sign = true;
|
||||
this.data.forEach(dt => {
|
||||
this.data.forEach((dt) => {
|
||||
if (!dt.selectEnv) {
|
||||
sign = false;
|
||||
return;
|
||||
}
|
||||
map.set(dt.id, dt.selectEnv);
|
||||
})
|
||||
if (!sign) {
|
||||
this.$warning(this.$t('workspace.env_group.please_select_env_for_current_scenario'));
|
||||
return;
|
||||
}
|
||||
this.$emit('setProjectEnvMap', map);
|
||||
this.$emit('close');
|
||||
});
|
||||
this.$emit("setProjectEnvMap", map);
|
||||
this.$emit("close");
|
||||
},
|
||||
checkEnv(data) {
|
||||
let sign = true;
|
||||
this.isFullUrl = true;
|
||||
if(data){
|
||||
if (data) {
|
||||
return true;
|
||||
}
|
||||
if (this.data.length > 0) {
|
||||
this.data.forEach(dt => {
|
||||
this.data.forEach((dt) => {
|
||||
if (!dt.selectEnv) {
|
||||
sign = false;
|
||||
return false;
|
||||
}
|
||||
})
|
||||
});
|
||||
} else {
|
||||
// 如果有环境,检查环境
|
||||
if (this.envMap && this.envMap.size > 0) {
|
||||
this.projectIds.forEach(id => {
|
||||
this.projectIds.forEach((id) => {
|
||||
if (!this.envMap.get(id)) {
|
||||
sign = false;
|
||||
return false;
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (!sign) {
|
||||
this.$warning(this.$t('workspace.env_group.please_select_env_for_current_scenario'));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
environmentConfigClose() {
|
||||
// todo 关闭处理
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
Loading…
Reference in New Issue