fix(接口测试): 修复多层嵌套引用场景禁用状态不生效问题

【[接口测试]github24932接口自动化,场景内引用多层级场景时,设置场景步骤为“禁用”状态会失效】https://www.tapd.cn/55049933/bugtrace/bugs/view?bug_id=1155049933001026993

Signed-off-by: fit2-zhao <yong.zhao@fit2cloud.com>
This commit is contained in:
fit2-zhao 2023-06-20 20:44:19 +08:00 committed by fit2-zhao
parent aeb7688084
commit 6ec19ba734
18 changed files with 208 additions and 137 deletions

View File

@ -28,6 +28,7 @@ import io.metersphere.base.domain.ApiTestEnvironmentWithBLOBs;
import io.metersphere.base.domain.FileMetadata; import io.metersphere.base.domain.FileMetadata;
import io.metersphere.base.mapper.ApiScenarioMapper; import io.metersphere.base.mapper.ApiScenarioMapper;
import io.metersphere.commons.constants.ElementConstants; import io.metersphere.commons.constants.ElementConstants;
import io.metersphere.commons.constants.MsHashTreeConstants;
import io.metersphere.commons.constants.PropertyConstant; import io.metersphere.commons.constants.PropertyConstant;
import io.metersphere.commons.constants.StorageConstants; import io.metersphere.commons.constants.StorageConstants;
import io.metersphere.commons.exception.MSException; import io.metersphere.commons.exception.MSException;
@ -68,7 +69,6 @@ import org.apache.jorphan.collections.HashTree;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONObject; import org.json.JSONObject;
import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
import java.net.URL; import java.net.URL;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
@ -82,6 +82,8 @@ public class ElementUtil {
private static final String ASSERTIONS = ElementConstants.ASSERTIONS; private static final String ASSERTIONS = ElementConstants.ASSERTIONS;
private static final String BODY_FILE_DIR = FileUtils.BODY_FILE_DIR; private static final String BODY_FILE_DIR = FileUtils.BODY_FILE_DIR;
private static final String TEST_BEAN_GUI = "TestBeanGUI"; private static final String TEST_BEAN_GUI = "TestBeanGUI";
private final static String SCENARIO_REF = "SCENARIO-REF-STEP";
public final static List<String> scriptList = new ArrayList<String>() {{ public final static List<String> scriptList = new ArrayList<String>() {{
this.add(ElementConstants.JSR223); this.add(ElementConstants.JSR223);
this.add(ElementConstants.JSR223_PRE); this.add(ElementConstants.JSR223_PRE);
@ -91,7 +93,6 @@ public class ElementUtil {
public static final String CLAZZ = "clazzName"; public static final String CLAZZ = "clazzName";
public static Map<String, EnvironmentConfig> getEnvironmentConfig(String environmentId, String projectId) { public static Map<String, EnvironmentConfig> getEnvironmentConfig(String environmentId, String projectId) {
BaseEnvironmentService apiTestEnvironmentService = CommonBeanFactory.getBean(BaseEnvironmentService.class); BaseEnvironmentService apiTestEnvironmentService = CommonBeanFactory.getBean(BaseEnvironmentService.class);
ApiTestEnvironmentWithBLOBs environment = apiTestEnvironmentService.get(environmentId); ApiTestEnvironmentWithBLOBs environment = apiTestEnvironmentService.get(environmentId);
@ -463,73 +464,74 @@ public class ElementUtil {
} }
} }
public static List<JSONObject> mergeHashTree(List<JSONObject> sourceHashTree, List<JSONObject> targetHashTree) { public static List<JSONObject> mergeHashTree(List<JSONObject> sources, List<JSONObject> targets) {
try { try {
List<String> sourceIds = new ArrayList<>(); List<String> sourceIds = new ArrayList<>();
List<String> delIds = new ArrayList<>(); List<String> delIds = new ArrayList<>();
Map<String, JSONObject> updateMap = new HashMap<>(); Map<String, JSONObject> updateMap = new HashMap<>();
if (CollectionUtils.isNotEmpty(targets)) {
if (CollectionUtils.isNotEmpty(targetHashTree)) { for (int i = 0; i < targets.size(); i++) {
for (int i = 0; i < targetHashTree.size(); i++) { JSONObject item = targets.get(i);
JSONObject item = targetHashTree.get(i); item.put(MsHashTreeConstants.DISABLED, true);
item.put("disabled", true); item.put(ElementConstants.REF_ENABLE, true);
if (StringUtils.isNotEmpty(item.optString("id"))) { if (StringUtils.isNotEmpty(item.optString(ElementConstants.ID))) {
updateMap.put(item.optString("id"), item); updateMap.put(item.optString(ElementConstants.ID), item);
} }
} }
} }
// 找出待更新内容和源已经被删除的内容 // 找出待更新内容和源已经被删除的内容
if (CollectionUtils.isNotEmpty(sourceHashTree)) { if (CollectionUtils.isNotEmpty(sources)) {
for (int i = 0; i < sourceHashTree.size(); i++) { for (int i = 0; i < sources.size(); i++) {
JSONObject source = sourceHashTree.get(i); JSONObject object = sources.get(i);
if (source != null) { if (object == null) {
sourceIds.add(source.optString("id")); continue;
if (!StringUtils.equals(source.optString("label"), "SCENARIO-REF-STEP") && StringUtils.isNotEmpty(source.optString("id"))) { }
if (updateMap.containsKey(source.optString("id"))) { sourceIds.add(object.optString(ElementConstants.ID));
sourceHashTree.set(i, updateMap.get(source.optString("id"))); if (!StringUtils.equals(object.optString("label"), SCENARIO_REF)
&& StringUtils.isNotEmpty(object.optString(ElementConstants.ID))) {
if (updateMap.containsKey(object.optString(ElementConstants.ID))) {
sources.set(i, updateMap.get(object.optString(ElementConstants.ID)));
updateMap.remove(object.optString(ElementConstants.ID));
} else { } else {
delIds.add(source.optString("id")); delIds.add(object.optString(ElementConstants.ID));
} }
} }
// 历史数据兼容 // 历史数据兼容
if (!source.has("id") && !StringUtils.equals(source.optString("label"), "SCENARIO-REF-STEP") && i < targetHashTree.size()) { if (!object.has(ElementConstants.ID)
sourceHashTree.set(i, targetHashTree.get(i)); && !StringUtils.equals(object.optString("label"), SCENARIO_REF)
&& i < targets.size()) {
sources.set(i, targets.get(i));
} }
} }
} }
// 添加少的步骤
if (MapUtils.isNotEmpty(updateMap)) {
updateMap.forEach((k, v) -> {
sources.add(v);
});
} }
// 删除多余的步骤 // 删除多余的步骤
for (int i = 0; i < sourceHashTree.size(); i++) { for (int i = 0; i < sources.size(); i++) {
JSONObject source = sourceHashTree.get(i); JSONObject source = sources.get(i);
if (delIds.contains(source.optString("id"))) { if (delIds.contains(source.optString(ElementConstants.ID))) {
sourceHashTree.remove(i); sources.remove(i);
} }
} }
// 补充新增的源引用步骤 // 补充新增的源引用步骤
if (CollectionUtils.isNotEmpty(targetHashTree)) { if (CollectionUtils.isNotEmpty(targets)) {
for (int i = 0; i < targetHashTree.size(); i++) { for (int i = 0; i < targets.size(); i++) {
JSONObject item = sourceHashTree.get(i); JSONObject item = sources.get(i);
if (!sourceIds.contains(item.optString("id"))) { if (!sourceIds.contains(item.optString(ElementConstants.ID))) {
sourceHashTree.add(item); sources.add(item);
} }
} }
} }
} catch (Exception e) { } catch (Exception e) {
return targetHashTree; return targets;
} }
return sourceHashTree; return sources;
}
public static String hashTreeToString(HashTree hashTree) {
try (ByteArrayOutputStream bas = new ByteArrayOutputStream()) {
SaveService.saveTree(hashTree, bas);
return bas.toString();
} catch (Exception e) {
e.printStackTrace();
io.metersphere.plugin.core.utils.LogUtil.warn("HashTree error, can't log jmx scenarioDefinition");
}
return null;
} }
public static String getResourceId(String resourceId, ParameterConfig config, MsTestElement parent, String indexPath) { public static String getResourceId(String resourceId, ParameterConfig config, MsTestElement parent, String indexPath) {
@ -1170,4 +1172,15 @@ public class ElementUtil {
return false; return false;
} }
public static boolean isEnable(MsTestElement element, ParameterConfig config) {
String path = ElementUtil.getFullIndexPath(element, "");
if (StringUtils.isNotBlank(path) && path.endsWith("_")) {
path = path.substring(0, path.length() - 1);
}
String key = StringUtils.join(element.getId(), "_", path);
if (config.getKeyMap().containsKey(key)) {
return config.getKeyMap().get(key);
}
return true;
}
} }

View File

@ -20,6 +20,7 @@ import io.metersphere.environment.service.BaseEnvironmentService;
import io.metersphere.plugin.core.MsParameter; import io.metersphere.plugin.core.MsParameter;
import io.metersphere.plugin.core.MsTestElement; import io.metersphere.plugin.core.MsTestElement;
import io.metersphere.service.MsHashTreeService; import io.metersphere.service.MsHashTreeService;
import io.metersphere.utils.LoggerUtil;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.CollectionUtils;
@ -27,7 +28,6 @@ import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.config.Arguments; import org.apache.jmeter.config.Arguments;
import org.apache.jorphan.collections.HashTree; import org.apache.jorphan.collections.HashTree;
import org.json.JSONArray;
import org.json.JSONObject; import org.json.JSONObject;
import java.util.HashMap; import java.util.HashMap;
@ -72,7 +72,7 @@ public class MsScenario extends MsTestElement {
if (this.getReferenced() != null && this.getReferenced().equals(MsTestElementConstants.Deleted.name())) { if (this.getReferenced() != null && this.getReferenced().equals(MsTestElementConstants.Deleted.name())) {
return; return;
} else if (this.getReferenced() != null && MsTestElementConstants.REF.name().equals(this.getReferenced()) } else if (this.getReferenced() != null && MsTestElementConstants.REF.name().equals(this.getReferenced())
&& !this.setRefScenario(hashTree)) { && !this.setRefScenario(hashTree, config)) {
return; return;
} }
// 设置共享cookie // 设置共享cookie
@ -137,6 +137,7 @@ public class MsScenario extends MsTestElement {
} }
if ((this.variableEnable == null && this.mixEnable == null) if ((this.variableEnable == null && this.mixEnable == null)
|| BooleanUtils.isTrue(this.variableEnable) || BooleanUtils.isTrue(this.mixEnable)) { || BooleanUtils.isTrue(this.variableEnable) || BooleanUtils.isTrue(this.mixEnable)) {
newConfig.setKeyMap(config.getKeyMap());
ElementUtil.addCsvDataSet(scenarioTree, variables, this.isEnvironmentEnable() ? newConfig : config, "shareMode.group"); ElementUtil.addCsvDataSet(scenarioTree, variables, this.isEnvironmentEnable() ? newConfig : config, "shareMode.group");
ElementUtil.addCounter(scenarioTree, variables); ElementUtil.addCounter(scenarioTree, variables);
ElementUtil.addRandom(scenarioTree, variables); ElementUtil.addRandom(scenarioTree, variables);
@ -205,13 +206,21 @@ public class MsScenario extends MsTestElement {
} }
} }
private boolean setRefScenario(List<MsTestElement> hashTree) { private boolean setRefScenario(List<MsTestElement> hashTree, ParameterConfig config) {
try { try {
ApiScenarioMapper apiAutomationService = CommonBeanFactory.getBean(ApiScenarioMapper.class); ApiScenarioMapper apiAutomationService = CommonBeanFactory.getBean(ApiScenarioMapper.class);
ApiScenarioWithBLOBs scenario = apiAutomationService.selectByPrimaryKey(this.getId()); ApiScenarioWithBLOBs scenario = apiAutomationService.selectByPrimaryKey(this.getId());
if (scenario != null && StringUtils.isNotEmpty(scenario.getScenarioDefinition())) { if (scenario != null && StringUtils.isNotEmpty(scenario.getScenarioDefinition())) {
JSONObject elementOrg = JSONUtil.parseObject(scenario.getScenarioDefinition()); JSONObject element = JSONUtil.parseObject(scenario.getScenarioDefinition());
JSONObject element = setRefEnable(this, elementOrg); String path = ElementUtil.getFullIndexPath(this, "");
if (path.endsWith("_")) {
path = path.substring(0, path.length() - 1);
}
element.put(MsHashTreeService.INDEX, path);
boolean enable = config.getKeyMap().get(this.getId() + "_" + path);
if (!enable) {
return false;
}
// 历史数据处理 // 历史数据处理
ElementUtil.dataFormatting(element.optJSONArray(ElementConstants.HASH_TREE)); ElementUtil.dataFormatting(element.optJSONArray(ElementConstants.HASH_TREE));
this.setName(scenario.getName()); this.setName(scenario.getName());
@ -231,43 +240,11 @@ public class MsScenario extends MsTestElement {
return true; return true;
} }
} catch (Exception ex) { } catch (Exception ex) {
ex.printStackTrace(); LoggerUtil.error(ex);
} }
return false; return false;
} }
public static JSONObject setRefEnable(MsTestElement targetElement, JSONObject orgElement) {
if (JSONObject.NULL.equals(orgElement) || targetElement == null) {
return orgElement;
}
if (!orgElement.optBoolean(MsHashTreeService.ENABLE)) {
orgElement.put(MsHashTreeService.ENABLE, false);
} else {
orgElement.put(MsHashTreeService.ENABLE, targetElement.isEnable());
}
try {
if (orgElement.has(MsHashTreeService.HASH_TREE)) {
JSONArray orgJSONArray = orgElement.optJSONArray(MsHashTreeService.HASH_TREE);
LinkedList<MsTestElement> hashTree = targetElement.getHashTree();
if (orgJSONArray != null && CollectionUtils.isNotEmpty(hashTree)) {
orgJSONArray.forEach(obj -> {
JSONObject orgJsonObject = (JSONObject) obj;
hashTree.forEach(targetObj -> {
if (StringUtils.equals(orgJsonObject.optString(MsHashTreeService.ID), targetObj.getId())
&& StringUtils.equals(orgJsonObject.optString(MsHashTreeService.INDEX), targetObj.getIndex())) {
setRefEnable(targetObj, orgJsonObject);
}
});
});
}
}
} catch (Exception ex) {
LogUtil.error(ex, ex.getMessage());
return orgElement;
}
return orgElement;
}
private void setNewConfig(Map<String, EnvironmentConfig> envConfig, ParameterConfig newConfig) { private void setNewConfig(Map<String, EnvironmentConfig> envConfig, ParameterConfig newConfig) {
if (this.isEnvironmentEnable()) { if (this.isEnvironmentEnable()) {
ApiScenarioMapper apiScenarioMapper = CommonBeanFactory.getBean(ApiScenarioMapper.class); ApiScenarioMapper apiScenarioMapper = CommonBeanFactory.getBean(ApiScenarioMapper.class);

View File

@ -102,6 +102,10 @@ public class ParameterConfig extends MsParameter {
private List<String> excludeScenarioIds = new ArrayList<>(); private List<String> excludeScenarioIds = new ArrayList<>();
private List<String> csvFilePaths = new ArrayList<>(); private List<String> csvFilePaths = new ArrayList<>();
/**
* 启用或禁用记录
*/
private Map<String, Boolean> keyMap = new HashMap<>();
public boolean isEffective(String projectId) { public boolean isEffective(String projectId) {

View File

@ -1,5 +1,6 @@
package io.metersphere.api.dto.definition.request.sampler; package io.metersphere.api.dto.definition.request.sampler;
import io.metersphere.api.dto.definition.request.ElementUtil;
import io.metersphere.api.dto.definition.request.ParameterConfig; import io.metersphere.api.dto.definition.request.ParameterConfig;
import io.metersphere.commons.constants.ElementConstants; import io.metersphere.commons.constants.ElementConstants;
import io.metersphere.plugin.core.MsParameter; import io.metersphere.plugin.core.MsParameter;
@ -37,6 +38,9 @@ public class MsDebugSampler extends MsTestElement {
if (!config.isOperating() && !this.isEnable()) { if (!config.isOperating() && !this.isEnable()) {
return; return;
} }
if (!ElementUtil.isEnable(this, config)) {
return;
}
final HashTree groupTree = tree.add(debugSampler()); final HashTree groupTree = tree.add(debugSampler());
if (CollectionUtils.isNotEmpty(hashTree)) { if (CollectionUtils.isNotEmpty(hashTree)) {
hashTree.forEach(el -> { hashTree.forEach(el -> {

View File

@ -71,6 +71,9 @@ public class MsDubboSampler extends MsTestElement {
if (this.getReferenced() != null && "Deleted".equals(this.getReferenced())) { if (this.getReferenced() != null && "Deleted".equals(this.getReferenced())) {
return; return;
} }
if (!ElementUtil.isEnable(this, config)) {
return;
}
if (this.getReferenced() != null && MsTestElementConstants.REF.name().equals(this.getReferenced())) { if (this.getReferenced() != null && MsTestElementConstants.REF.name().equals(this.getReferenced())) {
boolean ref = this.setRefElement(); boolean ref = this.setRefElement();
if (!ref) { if (!ref) {

View File

@ -96,12 +96,16 @@ public class MsHTTPSamplerProxy extends MsTestElement {
if (StringUtils.isEmpty(this.getEnvironmentId())) { if (StringUtils.isEmpty(this.getEnvironmentId())) {
this.setEnvironmentId(this.useEnvironment); this.setEnvironmentId(this.useEnvironment);
} }
// 非导出操作且不是启用状态则跳过执行Ms // 非导出操作且不是启用状态则跳过执行Ms
if (!config.isOperating() && !this.isEnable()) { if (!config.isOperating() && !this.isEnable()) {
return; return;
} else if (config.isOperating() && StringUtils.isNotEmpty(config.getOperatingSampleTestName())) { } else if (config.isOperating() && StringUtils.isNotEmpty(config.getOperatingSampleTestName())) {
this.setName(config.getOperatingSampleTestName()); this.setName(config.getOperatingSampleTestName());
} }
if (!ElementUtil.isEnable(this, config)) {
return;
}
if (this.getReferenced() != null && MsTestElementConstants.REF.name().equals(this.getReferenced())) { if (this.getReferenced() != null && MsTestElementConstants.REF.name().equals(this.getReferenced())) {
boolean ref = this.setRefElement(); boolean ref = this.setRefElement();
if (!ref) { if (!ref) {

View File

@ -66,6 +66,9 @@ public class MsJDBCSampler extends MsTestElement {
} else if (config.isOperating() && StringUtils.isNotEmpty(config.getOperatingSampleTestName())) { } else if (config.isOperating() && StringUtils.isNotEmpty(config.getOperatingSampleTestName())) {
this.setName(config.getOperatingSampleTestName()); this.setName(config.getOperatingSampleTestName());
} }
if (!ElementUtil.isEnable(this, config)) {
return;
}
if (this.getReferenced() != null && MsTestElementConstants.REF.name().equals(this.getReferenced())) { if (this.getReferenced() != null && MsTestElementConstants.REF.name().equals(this.getReferenced())) {
boolean ref = this.setRefElement(); boolean ref = this.setRefElement();
if (!ref) { if (!ref) {

View File

@ -90,6 +90,9 @@ public class MsTCPSampler extends MsTestElement {
} else if (config.isOperating() && StringUtils.isNotEmpty(config.getOperatingSampleTestName())) { } else if (config.isOperating() && StringUtils.isNotEmpty(config.getOperatingSampleTestName())) {
this.setName(config.getOperatingSampleTestName()); this.setName(config.getOperatingSampleTestName());
} }
if (!ElementUtil.isEnable(this, config)) {
return;
}
if (this.getReferenced() != null && MsTestElementConstants.REF.name().equals(this.getReferenced())) { if (this.getReferenced() != null && MsTestElementConstants.REF.name().equals(this.getReferenced())) {
boolean ref = this.setRefElement(); boolean ref = this.setRefElement();
if (!ref) { if (!ref) {

View File

@ -34,10 +34,7 @@ import io.metersphere.dto.*;
import io.metersphere.environment.service.BaseEnvGroupProjectService; import io.metersphere.environment.service.BaseEnvGroupProjectService;
import io.metersphere.i18n.Translator; import io.metersphere.i18n.Translator;
import io.metersphere.plugin.core.MsTestElement; import io.metersphere.plugin.core.MsTestElement;
import io.metersphere.service.ApiExecutionQueueService; import io.metersphere.service.*;
import io.metersphere.service.RedisTemplateService;
import io.metersphere.service.ServiceUtils;
import io.metersphere.service.SystemParameterService;
import io.metersphere.service.definition.TcpApiParamService; import io.metersphere.service.definition.TcpApiParamService;
import io.metersphere.service.scenario.ApiScenarioReportService; import io.metersphere.service.scenario.ApiScenarioReportService;
import io.metersphere.service.scenario.ApiScenarioReportStructureService; import io.metersphere.service.scenario.ApiScenarioReportStructureService;
@ -451,6 +448,9 @@ public class ApiScenarioExecuteService {
uploadBodyFiles(request.getBodyFileRequestIds(), bodyFiles); uploadBodyFiles(request.getBodyFileRequestIds(), bodyFiles);
FileUtils.createBodyFiles(request.getScenarioFileIds(), scenarioFiles); FileUtils.createBodyFiles(request.getScenarioFileIds(), scenarioFiles);
this.testElement(request); this.testElement(request);
Map<String, Boolean> keyMap = MsHashTreeService.getIndexKeyMap(request.getTestElement(), "");
config.setKeyMap(keyMap);
HashTree hashTree = request.getTestElement().generateHashTree(config); HashTree hashTree = request.getTestElement().generateHashTree(config);
String runMode = StringUtils.isEmpty(request.getRunMode()) ? ApiRunMode.SCENARIO.name() : request.getRunMode(); String runMode = StringUtils.isEmpty(request.getRunMode()) ? ApiRunMode.SCENARIO.name() : request.getRunMode();
JmeterRunRequestDTO runRequest = new JmeterRunRequestDTO(request.getId(), request.getId(), runMode, hashTree); JmeterRunRequestDTO runRequest = new JmeterRunRequestDTO(request.getId(), request.getId(), runMode, hashTree);

View File

@ -31,6 +31,10 @@ public class ElementConstants {
public static final String ASSERTIONS = "Assertions"; public static final String ASSERTIONS = "Assertions";
public static final String EXTRACT = "Extract"; public static final String EXTRACT = "Extract";
public static final String STEP_CREATED = "Created"; public static final String STEP_CREATED = "Created";
public static final String INDEX = "index";
public static final String ID = "id";
public static final String REF_ENABLE = "refEnable";
public final static List<String> REQUESTS = new ArrayList<String>() {{ public final static List<String> REQUESTS = new ArrayList<String>() {{
this.add(ElementConstants.HTTP_SAMPLER); this.add(ElementConstants.HTTP_SAMPLER);
this.add(ElementConstants.DUBBO_SAMPLER); this.add(ElementConstants.DUBBO_SAMPLER);

View File

@ -20,6 +20,7 @@ import io.metersphere.dto.ProjectJarConfig;
import io.metersphere.dto.RunModeConfigDTO; import io.metersphere.dto.RunModeConfigDTO;
import io.metersphere.environment.service.BaseEnvGroupProjectService; import io.metersphere.environment.service.BaseEnvGroupProjectService;
import io.metersphere.plugin.core.MsTestElement; import io.metersphere.plugin.core.MsTestElement;
import io.metersphere.service.MsHashTreeService;
import io.metersphere.utils.LoggerUtil; import io.metersphere.utils.LoggerUtil;
import io.metersphere.vo.BooleanPool; import io.metersphere.vo.BooleanPool;
import org.apache.commons.collections.MapUtils; import org.apache.commons.collections.MapUtils;
@ -159,6 +160,9 @@ public class GenerateHashTreeUtil {
if (runRequest.isRetryEnable() && runRequest.getRetryNum() > 0) { if (runRequest.isRetryEnable() && runRequest.getRetryNum() > 0) {
config.setRetryNum(runRequest.getRetryNum()); config.setRetryNum(runRequest.getRetryNum());
} }
Map<String, Boolean> keyMap = MsHashTreeService.getIndexKeyMap(element, "");
config.setKeyMap(keyMap);
testPlan.toHashTree(jmeterHashTree, testPlan.getHashTree(), config); testPlan.toHashTree(jmeterHashTree, testPlan.getHashTree(), config);
LoggerUtil.info("场景资源:" + item.getName() + ", 生成执行脚本JMX成功", runRequest.getReportId()); LoggerUtil.info("场景资源:" + item.getName() + ", 生成执行脚本JMX成功", runRequest.getReportId());

View File

@ -19,15 +19,18 @@ import io.metersphere.commons.utils.JSON;
import io.metersphere.commons.utils.JSONUtil; import io.metersphere.commons.utils.JSONUtil;
import io.metersphere.commons.utils.LogUtil; import io.metersphere.commons.utils.LogUtil;
import io.metersphere.dto.ProjectConfig; import io.metersphere.dto.ProjectConfig;
import io.metersphere.plugin.core.MsTestElement;
import io.metersphere.service.definition.ApiDefinitionService; import io.metersphere.service.definition.ApiDefinitionService;
import io.metersphere.service.definition.ApiTestCaseService; import io.metersphere.service.definition.ApiTestCaseService;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONObject; import org.json.JSONObject;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -97,6 +100,8 @@ public class MsHashTreeService {
private static final String DATASOURCEID = "dataSourceId"; private static final String DATASOURCEID = "dataSourceId";
private static final String RESULT_VARIABLE = "resultVariable"; private static final String RESULT_VARIABLE = "resultVariable";
private static final String ENV_Id = "environmentId"; private static final String ENV_Id = "environmentId";
private static final String PARENT_INDEX = "parentIndex";
private final static String JSON_PATH="jsonPath"; private final static String JSON_PATH="jsonPath";
private final static String JSR223="jsr223"; private final static String JSR223="jsr223";
@ -310,7 +315,7 @@ public class MsHashTreeService {
} }
} }
private JSONObject setRefScenario(JSONObject element) { private JSONObject setRefScenario(JSONObject element, Map<String, Boolean> keyMap) {
boolean enable = element.has(ENABLE) ? element.optBoolean(ENABLE) : true; boolean enable = element.has(ENABLE) ? element.optBoolean(ENABLE) : true;
if (!element.has(MIX_ENABLE)) { if (!element.has(MIX_ENABLE)) {
element.put(MIX_ENABLE, false); element.put(MIX_ENABLE, false);
@ -327,7 +332,21 @@ public class MsHashTreeService {
element.put(ENV_MAP, JSON.parseObject(scenarioWithBLOBs.getEnvironmentJson(), Map.class)); element.put(ENV_MAP, JSON.parseObject(scenarioWithBLOBs.getEnvironmentJson(), Map.class));
} }
if (StringUtils.equalsIgnoreCase(element.optString(REFERENCED), REF)) { if (StringUtils.equalsIgnoreCase(element.optString(REFERENCED), REF)) {
element = setRefEnable(element, JSONUtil.parseObject(scenarioWithBLOBs.getScenarioDefinition())); JSONObject object = JSONUtil.parseObject(scenarioWithBLOBs.getScenarioDefinition());
object.put(PARENT_INDEX, element.optString(PARENT_INDEX));
object.put(INDEX, element.optString(INDEX));
if (object.has(ENABLE) && BooleanUtils.isFalse(object.optBoolean(ENABLE))) {
enable = false;
object.put(REF_ENABLE, true);
} else {
String indexStr = object.has(PARENT_INDEX) && StringUtils.isNotBlank(object.optString(PARENT_INDEX)) ?
StringUtils.join(object.optString(PARENT_INDEX), "_", object.optString(INDEX)) : object.optString(INDEX);
if (keyMap.containsKey(element.optString(ID) + indexStr)) {
enable = keyMap.get(element.optString(ID) + indexStr);
object.put(ENABLE, enable);
}
}
element = object;
element.put(REFERENCED, REF); element.put(REFERENCED, REF);
element.put(NAME, scenarioWithBLOBs.getName()); element.put(NAME, scenarioWithBLOBs.getName());
} }
@ -354,69 +373,76 @@ public class MsHashTreeService {
return element; return element;
} }
public static JSONObject setRefEnable(JSONObject targetElement, JSONObject orgElement) { public static Map<String, Boolean> getIndexKeyMap(JSONObject element, String parentIndex) {
if (orgElement == null || targetElement == null) { Map<String, Boolean> indexKeyMap = new HashMap<>();
return orgElement; StringBuilder builder = new StringBuilder(parentIndex);
if (element.has(ENABLE) && element.has(ID) && element.has(INDEX)) {
builder.append("_").append(element.getString(INDEX));
String key = StringUtils.join(element.optString(ID), builder.toString());
indexKeyMap.put(key, element.optBoolean(ENABLE));
} }
if (!orgElement.optBoolean(ENABLE)) { if (element.has(HASH_TREE)) {
orgElement.put(ENABLE, false); element.getJSONArray(HASH_TREE).forEach(item -> {
orgElement.put(REF_ENABLE, true); JSONObject obj = (JSONObject) item;
} else { indexKeyMap.putAll(getIndexKeyMap(obj, builder.toString()));
orgElement.put(ENABLE, targetElement.optBoolean(ENABLE));
}
if (targetElement.optBoolean(REF_ENABLE)) {
orgElement.put(REF_ENABLE, targetElement.optBoolean(REF_ENABLE));
}
try {
if (orgElement.has(HASH_TREE)) {
JSONArray org = orgElement.optJSONArray(HASH_TREE);
JSONArray target = targetElement.optJSONArray(HASH_TREE);
if (org != null && target != null) {
org.forEach(obj -> {
JSONObject childOrg = (JSONObject) obj;
target.forEach(targetObj -> {
JSONObject childTarget = (JSONObject) targetObj;
if (StringUtils.equals(childOrg.optString(ID), childTarget.optString(ID))
&& StringUtils.equals(childOrg.optString(INDEX), childTarget.optString(INDEX))) {
setRefEnable(childTarget, childOrg);
}
});
}); });
} }
} return indexKeyMap;
} catch (Exception e) {
LogUtil.error(e, e.getMessage());
return orgElement;
}
return orgElement;
} }
public void dataFormatting(JSONArray hashTree, List<String> caseIds) { public void dataFormatting(JSONArray hashTree, List<String> caseIds, Map<String, Boolean> keyMap, String parentIndex) {
for (int i = 0; i < hashTree.length(); i++) { for (int i = 0; i < hashTree.length(); i++) {
JSONObject element = hashTree.optJSONObject(i); JSONObject element = hashTree.optJSONObject(i);
// 设置父级索引
element.put(PARENT_INDEX, parentIndex);
if (element != null && StringUtils.equalsIgnoreCase(element.optString(TYPE), SCENARIO)) { if (element != null && StringUtils.equalsIgnoreCase(element.optString(TYPE), SCENARIO)) {
element = this.setRefScenario(element); element = this.setRefScenario(element, keyMap);
hashTree.put(i, element); hashTree.put(i, element);
} else if (element != null && ElementConstants.REQUESTS.contains(element.optString(TYPE))) { } else if (element != null && ElementConstants.REQUESTS.contains(element.optString(TYPE))) {
setCaseEnable(element, keyMap, parentIndex);
this.getCaseIds(element, caseIds); this.getCaseIds(element, caseIds);
hashTree.put(i, element); hashTree.put(i, element);
} }
if (element.has(HASH_TREE)) { if (element.has(HASH_TREE)) {
JSONArray elementJSONArray = element.optJSONArray(HASH_TREE); JSONArray elementJSONArray = element.optJSONArray(HASH_TREE);
dataFormatting(elementJSONArray, caseIds); String indexStr = StringUtils.join(parentIndex, "_", element.optString(INDEX));
dataFormatting(elementJSONArray, caseIds, keyMap, indexStr);
} }
} }
} }
public void dataFormatting(JSONObject element, List<String> caseIds) { public void dataFormatting(JSONObject element, List<String> caseIds, Map<String, Boolean> keyMap) {
if (element != null && StringUtils.equalsIgnoreCase(element.optString(TYPE), SCENARIO)) { if (element == null) {
element = this.setRefScenario(element); return;
} else if (element != null && ElementConstants.REQUESTS.contains(element.optString(TYPE))) { }
// 设置父级索引
String parentIndex = element.has(PARENT_INDEX) ?
StringUtils.join(element.optString(PARENT_INDEX), "_", element.optString(INDEX))
: element.optString(INDEX);
element.put(PARENT_INDEX, parentIndex);
if (StringUtils.equalsIgnoreCase(element.optString(TYPE), SCENARIO)) {
element = this.setRefScenario(element, keyMap);
} else if (ElementConstants.REQUESTS.contains(element.optString(TYPE))) {
setCaseEnable(element, keyMap, parentIndex);
this.getCaseIds(element, caseIds); this.getCaseIds(element, caseIds);
} }
if (element != null && element.has(HASH_TREE)) { if (element.has(HASH_TREE)) {
JSONArray elementJSONArray = element.optJSONArray(HASH_TREE); JSONArray elementJSONArray = element.optJSONArray(HASH_TREE);
dataFormatting(elementJSONArray, caseIds); dataFormatting(elementJSONArray, caseIds, keyMap, parentIndex);
}
}
private void setCaseEnable(JSONObject element, Map<String, Boolean> keyMap, String parentIndex) {
if (element.has(ENABLE) && BooleanUtils.isFalse(element.optBoolean(ENABLE))) {
element.put(ENABLE, false);
element.put(REF_ENABLE, true);
} else {
String indexStr = StringUtils.join(element.optString(ID), parentIndex, "_", element.optString(INDEX));
if (keyMap.containsKey(indexStr)) {
element.put(ENABLE, keyMap.get(indexStr));
}
} }
} }
@ -447,4 +473,19 @@ public class MsHashTreeService {
} }
} }
public static Map<String, Boolean> getIndexKeyMap(MsTestElement element, String parentIndex) {
Map<String, Boolean> indexKeyMap = new HashMap<>();
StringBuilder builder = new StringBuilder(parentIndex);
if (StringUtils.isNotBlank(element.getId()) && StringUtils.isNotBlank(element.getIndex())) {
builder.append("_").append(element.getIndex());
String key = StringUtils.join(element.getId(), builder.toString());
indexKeyMap.put(key, element.isEnable());
}
if (CollectionUtils.isNotEmpty(element.getHashTree())) {
element.getHashTree().forEach(item -> {
indexKeyMap.putAll(getIndexKeyMap(item, builder.toString()));
});
}
return indexKeyMap;
}
} }

View File

@ -168,7 +168,9 @@ public class ApiScenarioReportStructureService {
MsHashTreeService hashTreeService = CommonBeanFactory.getBean(MsHashTreeService.class); MsHashTreeService hashTreeService = CommonBeanFactory.getBean(MsHashTreeService.class);
assert hashTreeService != null; assert hashTreeService != null;
List<String> caseIds = new ArrayList<>(); List<String> caseIds = new ArrayList<>();
hashTreeService.dataFormatting(element, caseIds); Map<String, Boolean> keyMap = MsHashTreeService.getIndexKeyMap(element,element.optString(ElementConstants.INDEX));
hashTreeService.dataFormatting(element, caseIds, keyMap);
// 处理用例 // 处理用例
hashTreeService.caseFormatting(element, caseIds, null); hashTreeService.caseFormatting(element, caseIds, null);

View File

@ -757,7 +757,8 @@ public class ApiScenarioService {
if (StringUtils.isNotEmpty(scenarioWithBLOBs.getScenarioDefinition())) { if (StringUtils.isNotEmpty(scenarioWithBLOBs.getScenarioDefinition())) {
JSONObject element = JSONUtil.parseObject(scenarioWithBLOBs.getScenarioDefinition()); JSONObject element = JSONUtil.parseObject(scenarioWithBLOBs.getScenarioDefinition());
List<String> caseIds = new ArrayList<>(); List<String> caseIds = new ArrayList<>();
hashTreeService.dataFormatting(element, caseIds); Map<String, Boolean> keyMap = MsHashTreeService.getIndexKeyMap(element, element.optString(ElementConstants.INDEX));
hashTreeService.dataFormatting(element, caseIds, keyMap);
// 处理用例 // 处理用例
hashTreeService.caseFormatting(element, caseIds, getConfig(scenarioWithBLOBs)); hashTreeService.caseFormatting(element, caseIds, getConfig(scenarioWithBLOBs));
ElementUtil.dataFormatting(element); ElementUtil.dataFormatting(element);
@ -885,7 +886,8 @@ public class ApiScenarioService {
JSONObject element = JSONUtil.parseObject(dto.getScenarioDefinition()); JSONObject element = JSONUtil.parseObject(dto.getScenarioDefinition());
// 获取所有case // 获取所有case
List<String> caseIds = new ArrayList<>(); List<String> caseIds = new ArrayList<>();
hashTreeService.dataFormatting(element, caseIds); Map<String, Boolean> keyMap = MsHashTreeService.getIndexKeyMap(element, element.optString(ElementConstants.INDEX));
hashTreeService.dataFormatting(element, caseIds, keyMap);
// 处理用例 // 处理用例
hashTreeService.caseFormatting(element, caseIds, null); hashTreeService.caseFormatting(element, caseIds, null);

View File

@ -81,7 +81,7 @@
v-model="data.enable" v-model="data.enable"
class="enable-switch" class="enable-switch"
size="mini" size="mini"
:disabled="data.refEnable || !showVersion || isDeleted" /> :disabled="data.refEnable || !showVersion || isDeleted || isEnabled()" />
</el-tooltip> </el-tooltip>
<el-button <el-button
@ -302,6 +302,9 @@ export default {
}, },
}, },
methods: { methods: {
isEnabled() {
return this.stepFilter.get("ALlSamplerStep").indexOf(this.data.type) !== -1 && this.data.caseEnable;
},
active() { active() {
this.$emit('active'); this.$emit('active');
}, },

View File

@ -308,6 +308,7 @@ export default {
arr[i].disabled = disabled; arr[i].disabled = disabled;
arr[i].isCopy = false; arr[i].isCopy = false;
arr[i].projectId = this.calcProjectId(arr[i].projectId, id); arr[i].projectId = this.calcProjectId(arr[i].projectId, id);
arr[i].caseEnable = disabled;
// //
let typeArray = ['JDBCPostProcessor', 'JDBCSampler', 'JDBCPreProcessor']; let typeArray = ['JDBCPostProcessor', 'JDBCSampler', 'JDBCPreProcessor'];
if (typeArray.indexOf(arr[i].type) !== -1) { if (typeArray.indexOf(arr[i].type) !== -1) {

View File

@ -6,6 +6,7 @@
<api-json-path-suggest-button <api-json-path-suggest-button
:open-tip="$t('api_test.request.assertions.json_path_suggest')" :open-tip="$t('api_test.request.assertions.json_path_suggest')"
:clear-tip="$t('api_test.request.assertions.json_path_clear')" :clear-tip="$t('api_test.request.assertions.json_path_clear')"
:is-read-only="request.caseEnable"
@open="suggestJsonOpen" @open="suggestJsonOpen"
@clear="clearJson" /> @clear="clearJson" />
</span> </span>
@ -16,6 +17,7 @@
<el-select <el-select
class="assertion-item" class="assertion-item"
v-model="type" v-model="type"
:disabled="request.caseEnable"
:placeholder="$t('api_test.request.assertions.select_type')" :placeholder="$t('api_test.request.assertions.select_type')"
size="small"> size="small">
<el-option :label="$t('api_test.request.assertions.text')" :value="options.TEXT" /> <el-option :label="$t('api_test.request.assertions.text')" :value="options.TEXT" />

View File

@ -10,6 +10,7 @@
</el-select> </el-select>
<el-button <el-button
size="mini" size="mini"
:disabled="request.caseEnable"
@click="add" @click="add"
type="primary" type="primary"
v-if="tabType !== 'assertionsRule'" v-if="tabType !== 'assertionsRule'"