From 92cbcc1939ed1c3b7fa424753f50fb35d1c6b498 Mon Sep 17 00:00:00 2001 From: AgAngle <1323481023@qq.com> Date: Tue, 20 Feb 2024 09:45:36 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E6=8E=A5=E5=8F=A3=E8=AE=BE=E7=BD=AE):=20?= =?UTF-8?q?=E5=A4=84=E7=90=86http=E7=8E=AF=E5=A2=83=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E8=A7=A3=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../metersphere/api/dto/ApiParamConfig.java | 9 + .../api/dto/ApiResourceModuleInfo.java | 19 ++ .../api/dto/debug/ApiDebugRunRequest.java | 2 - .../api/dto/request/MsScenario.java | 1 + .../api/dto/request/http/MsHTTPElement.java | 5 + .../api/dto/scenario/ScenarioOtherConfig.java | 2 +- .../api/mapper/ExtApiDefinitionMapper.java | 3 + .../api/mapper/ExtApiDefinitionMapper.xml | 8 + .../mapper/ExtApiDefinitionModuleMapper.java | 2 + .../mapper/ExtApiDefinitionModuleMapper.xml | 8 + .../api/mapper/ExtApiTestCaseMapper.java | 4 + .../api/mapper/ExtApiTestCaseMapper.xml | 8 + .../parser/jmeter/MsHTTPElementConverter.java | 216 +++++++++++++++++- .../parser/jmeter/MsScenarioConverter.java | 51 ++++- .../parser/jmeter/constants/JmeterAlias.java | 2 + .../api/service/ApiExecuteService.java | 19 +- .../api/service/debug/ApiDebugService.java | 10 - .../ApiDefinitionModuleService.java | 8 +- .../definition/ApiDefinitionService.java | 5 + .../definition/ApiTestCaseService.java | 17 ++ .../service/scenario/ApiScenarioService.java | 93 +++++++- .../ApiScenarioControllerTests.java | 86 ++++++- .../dto/environment/http/HttpConfig.java | 43 +++- .../http/HttpConfigPathMatchRule.java | 16 +- .../system/service/PluginLoadService.java | 2 +- .../system/base/BasePluginTestService.java | 6 +- 26 files changed, 591 insertions(+), 54 deletions(-) create mode 100644 backend/services/api-test/src/main/java/io/metersphere/api/dto/ApiResourceModuleInfo.java diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/dto/ApiParamConfig.java b/backend/services/api-test/src/main/java/io/metersphere/api/dto/ApiParamConfig.java index 156fc0f8fb..630881f8f6 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/dto/ApiParamConfig.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/dto/ApiParamConfig.java @@ -3,6 +3,7 @@ package io.metersphere.api.dto; import io.metersphere.plugin.api.dto.ParameterConfig; import io.metersphere.plugin.api.spi.AbstractMsTestElement; import io.metersphere.project.dto.environment.EnvironmentInfoDTO; +import io.metersphere.project.dto.environment.GlobalParams; import lombok.Data; import java.util.HashMap; @@ -18,10 +19,18 @@ public class ApiParamConfig extends ParameterConfig { * 报告ID */ private String reportId; + /** + * 使用全局cookie + */ + private Boolean enableGlobalCookie = true; /** * 环境配置信息 */ private EnvironmentInfoDTO envConfig; + /** + * 全局参数 + */ + private GlobalParams globalParams; /** * AbstractMsTestElement 实现类与插件 ID 的映射 * key 为 AbstractMsTestElement 实现类对象 diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/dto/ApiResourceModuleInfo.java b/backend/services/api-test/src/main/java/io/metersphere/api/dto/ApiResourceModuleInfo.java new file mode 100644 index 0000000000..f75b76c4f8 --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/dto/ApiResourceModuleInfo.java @@ -0,0 +1,19 @@ +package io.metersphere.api.dto; + +import lombok.Data; + +/** + * @Author: jianxing + * @CreateTime: 2024-02-17 18:46 + */ +@Data +public class ApiResourceModuleInfo { + /** + * 模块Id + */ + private String moduleId; + /** + * 资源id,接口定义,接口用例等 + */ + private String resourceId; +} diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/dto/debug/ApiDebugRunRequest.java b/backend/services/api-test/src/main/java/io/metersphere/api/dto/debug/ApiDebugRunRequest.java index effbccf8d9..263c7d5a23 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/dto/debug/ApiDebugRunRequest.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/dto/debug/ApiDebugRunRequest.java @@ -15,8 +15,6 @@ public class ApiDebugRunRequest { @Schema(description = "报告ID") @NotNull private String reportId; - @Schema(description = "环境ID") - private String environmentId; @Schema(description = "点击调试时尚未保存的文件ID列表") private List tempFileIds; @NotNull diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/dto/request/MsScenario.java b/backend/services/api-test/src/main/java/io/metersphere/api/dto/request/MsScenario.java index 0ce92895c3..b33b543058 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/dto/request/MsScenario.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/dto/request/MsScenario.java @@ -43,6 +43,7 @@ public class MsScenario extends AbstractMsTestElement { private Boolean grouped; /** * {@link io.metersphere.api.constants.ApiScenarioStepRefType} + * DIRECT 表示当前根场景 */ private String refType; } diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/dto/request/http/MsHTTPElement.java b/backend/services/api-test/src/main/java/io/metersphere/api/dto/request/http/MsHTTPElement.java index afa7ed6ffc..6cc3331c80 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/dto/request/http/MsHTTPElement.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/dto/request/http/MsHTTPElement.java @@ -72,4 +72,9 @@ public class MsHTTPElement extends AbstractMsTestElement { */ @Valid private HTTPAuth authConfig; + /** + * 模块ID + * 运行时参数,接口无需设置 + */ + private String moduleId; } \ No newline at end of file diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/dto/scenario/ScenarioOtherConfig.java b/backend/services/api-test/src/main/java/io/metersphere/api/dto/scenario/ScenarioOtherConfig.java index 56cb894ca5..037d1b7993 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/dto/scenario/ScenarioOtherConfig.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/dto/scenario/ScenarioOtherConfig.java @@ -15,7 +15,7 @@ public class ScenarioOtherConfig { /** * 是否共享cookie */ - private Boolean enableCookieShare; + private Boolean enableCookieShare = false; /** * 场景步骤等待时间 * 每一个步骤执行后都会等待相应的时间 diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiDefinitionMapper.java b/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiDefinitionMapper.java index 0066d1f2a5..44fdbba4ec 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiDefinitionMapper.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiDefinitionMapper.java @@ -2,6 +2,7 @@ package io.metersphere.api.mapper; import io.metersphere.api.domain.ApiDefinition; import io.metersphere.api.domain.ApiDefinitionCustomField; +import io.metersphere.api.dto.ApiResourceModuleInfo; import io.metersphere.api.dto.converter.ApiDefinitionImportDetail; import io.metersphere.api.dto.definition.*; import io.metersphere.api.dto.scenario.ScenarioSystemRequest; @@ -61,4 +62,6 @@ public interface ExtApiDefinitionMapper { Long getPrePos(@Param("projectId") String projectId, @Param("basePos") Long basePos); Long getLastPos(@Param("projectId") String projectId, @Param("basePos") Long basePos); + + List getModuleInfoByIds(@Param("ids") List ids); } diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiDefinitionMapper.xml b/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiDefinitionMapper.xml index 4c92a313ae..ab7ea2497b 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiDefinitionMapper.xml +++ b/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiDefinitionMapper.xml @@ -541,4 +541,12 @@ order by `pos` desc limit 1; + diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiDefinitionModuleMapper.java b/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiDefinitionModuleMapper.java index b2b06b7bc4..ff5a2a5e41 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiDefinitionModuleMapper.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiDefinitionModuleMapper.java @@ -34,4 +34,6 @@ public interface ExtApiDefinitionModuleMapper { List selectNodeByIds(@Param("ids") List ids); List selectBaseByIds(@Param("ids") List ids); + + List getModuleIdsByParentIds(@Param("parentIds") List parentIds); } diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiDefinitionModuleMapper.xml b/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiDefinitionModuleMapper.xml index d683ab83ea..a2a3da07a6 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiDefinitionModuleMapper.xml +++ b/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiDefinitionModuleMapper.xml @@ -115,6 +115,14 @@ ORDER BY pos + diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiTestCaseMapper.java b/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiTestCaseMapper.java index e735ddcf2c..0bf4bac2ef 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiTestCaseMapper.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiTestCaseMapper.java @@ -10,10 +10,12 @@ import io.metersphere.request.AssociateOtherCaseRequest; import io.metersphere.request.TestCasePageProviderRequest; import io.metersphere.system.dto.sdk.BaseTreeNode; import io.metersphere.system.dto.sdk.OptionDTO; +import org.apache.ibatis.annotations.MapKey; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import java.util.List; +import java.util.Map; /** * @author jianxing @@ -62,4 +64,6 @@ public interface ExtApiTestCaseMapper { List selectVersionOptionByIds(@Param("ids") List ids); List getIdsByModules(@Param("request") ScenarioSystemRequest caseRequest); + + List getApiCaseDefinitionInfo(@Param("ids") List ids); } \ No newline at end of file diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiTestCaseMapper.xml b/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiTestCaseMapper.xml index 433a204e5a..0520187a98 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiTestCaseMapper.xml +++ b/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiTestCaseMapper.xml @@ -274,6 +274,14 @@ and api_test_case.version_id = #{request.versionId} + diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/MsHTTPElementConverter.java b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/MsHTTPElementConverter.java index 793a7c1308..4753ddf9e1 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/MsHTTPElementConverter.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/MsHTTPElementConverter.java @@ -1,24 +1,42 @@ package io.metersphere.api.parser.jmeter; +import io.metersphere.api.dto.ApiParamConfig; import io.metersphere.api.dto.request.http.MsHTTPElement; +import io.metersphere.api.dto.request.http.QueryParam; import io.metersphere.api.dto.request.http.body.Body; import io.metersphere.api.parser.jmeter.body.MsBodyConverter; import io.metersphere.api.parser.jmeter.body.MsBodyConverterFactory; import io.metersphere.api.parser.jmeter.body.MsFormDataBodyConverter; import io.metersphere.api.parser.jmeter.body.MsWWWFormBodyConverter; +import io.metersphere.jmeter.mock.Mock; import io.metersphere.plugin.api.constants.ElementProperty; import io.metersphere.plugin.api.dto.ParameterConfig; import io.metersphere.plugin.api.spi.AbstractJmeterElementConverter; +import io.metersphere.project.api.KeyValueEnableParam; +import io.metersphere.project.api.KeyValueParam; +import io.metersphere.project.dto.environment.EnvironmentInfoDTO; +import io.metersphere.project.dto.environment.GlobalParams; +import io.metersphere.project.dto.environment.http.HttpConfig; +import io.metersphere.project.dto.environment.http.HttpConfigPathMatchRule; +import io.metersphere.project.dto.environment.http.SelectModule; +import io.metersphere.sdk.util.EnumValidator; import io.metersphere.sdk.util.LogUtils; +import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; +import org.apache.jmeter.protocol.http.control.Header; +import org.apache.jmeter.protocol.http.control.HeaderManager; import org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy; import org.apache.jmeter.save.SaveService; import org.apache.jmeter.testelement.TestElement; import org.apache.jorphan.collections.HashTree; import org.springframework.http.HttpMethod; +import java.util.*; +import java.util.stream.Collectors; + +import static io.metersphere.api.parser.jmeter.constants.JmeterAlias.HEADER_PANEL; import static io.metersphere.api.parser.jmeter.constants.JmeterAlias.HTTP_TEST_SAMPLE_GUI; /** @@ -29,7 +47,8 @@ import static io.metersphere.api.parser.jmeter.constants.JmeterAlias.HTTP_TEST_S */ public class MsHTTPElementConverter extends AbstractJmeterElementConverter { - private ParameterConfig config; + public static final String URL_ENCODE = "${__urlencode(%s)}"; + public static final String COOKIE = "Cookie"; @Override public void toHashTree(HashTree tree, MsHTTPElement msHTTPElement, ParameterConfig config) { @@ -37,26 +56,161 @@ public class MsHTTPElementConverter extends AbstractJmeterElementConverter headerMap = new HashMap<>(); + + // 获取全局参数中的请求头 + GlobalParams globalParams = apiParamConfig.getGlobalParams(); + if (globalParams != null) { + setHeaderMap(headerMap, globalParams.getHeaders()); + } + + // 获取环境中的请求头 + if (httpConfig != null && CollectionUtils.isNotEmpty(httpConfig.getHeaders())) { + Boolean enableGlobalCookie = apiParamConfig.getEnableGlobalCookie(); + List envHeaders = httpConfig.getHeaders(); + if (BooleanUtils.isFalse(enableGlobalCookie)) { + // 如果不启用全局 cookie,则过滤 cookie + envHeaders = envHeaders.stream() + .filter(header -> !StringUtils.equalsIgnoreCase(header.getKey(), COOKIE)) + .toList(); + } + setHeaderMap(headerMap, envHeaders); + } + + // 获取请求中的请求头 + if (CollectionUtils.isNotEmpty(msHTTPElement.getHeaders())) { + setHeaderMap(headerMap, msHTTPElement.getHeaders()); + } + + if (headerMap.isEmpty()) { + return null; + } + + HeaderManager headerManager = new HeaderManager(); + headerManager.setEnabled(true); + headerManager.setName(StringUtils.isNotEmpty(msHTTPElement.getName()) ? msHTTPElement.getName() + "_HeaderManager" : "HeaderManager"); + headerManager.setProperty(TestElement.TEST_CLASS, HeaderManager.class.getName()); + headerManager.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass(HEADER_PANEL)); + headerMap.forEach((k, v) -> headerManager.add(new Header(k, Mock.buildFunctionCallString(v)))); + return headerManager; + } + + private void setHeaderMap(Map headerMap, List headers) { + if (CollectionUtils.isEmpty(headers)) { + return; + } + headers.stream() + .filter(KeyValueEnableParam::getEnable) + .filter(KeyValueParam::isValid) + .forEach(header -> { + if (StringUtils.equalsIgnoreCase(header.getKey(), COOKIE)) { + String cookieValue = header.getValue(); + if (headerMap.get(COOKIE) != null && header.getValue() != null) { + // 合并 cookie + cookieValue = headerMap.get(COOKIE) + ";" + header.getValue(); + } + headerMap.put(COOKIE, cookieValue); + } else { + headerMap.put(header.getKey(), header.getValue()); + } + }); + } + + /** + * 获取环境 http 配置 + * + * @param msHTTPElement + * @param config + * @return + */ + private HttpConfig getHttpConfig(MsHTTPElement msHTTPElement, ApiParamConfig config) { + ApiParamConfig apiParamConfig = config; + EnvironmentInfoDTO envConfig = apiParamConfig.getEnvConfig(); + if (envConfig == null) { + return null; + } + // http配置按优先级排序 + List httpConfigs = envConfig.getConfig().getHttpConfig() + .stream() + .sorted(Comparator.comparing(HttpConfig::getModuleMatchRuleOrder)) + .toList(); + for (HttpConfig httpConfig : httpConfigs) { + boolean match; + if (httpConfig.isPathMatchRule()) { + // 匹配路径 + HttpConfigPathMatchRule pathMatchRule = httpConfig.getPathMatchRule(); + HttpConfigPathMatchRule.MatchRuleCondition matchRuleCondition = + EnumValidator.validateEnum(HttpConfigPathMatchRule.MatchRuleCondition.class, pathMatchRule.getCondition()); + match = matchRuleCondition.match(pathMatchRule.getPath(), msHTTPElement.getPath()); + } else if (httpConfig.isModuleMatchRule()) { + // 匹配模块 + Set moduleIds = httpConfig.getModuleMatchRule().getModules() + .stream() + .map(SelectModule::getModuleId) + .collect(Collectors.toSet()); + match = moduleIds.contains(msHTTPElement.getModuleId()); + } else { + // 无条件匹配 + match = true; + } + if (match) { + return httpConfig; + } + } + return null; } /** @@ -65,7 +219,7 @@ public class MsHTTPElementConverter extends AbstractJmeterElementConverter query) { + if (CollectionUtils.isEmpty(query)) { + return path; + } + StringBuffer stringBuffer = new StringBuffer(); + stringBuffer.append(path); + if (path.contains("?")) { + stringBuffer.append("&"); + } else { + stringBuffer.append("?"); + } + query.stream() + .filter(KeyValueEnableParam::getEnable) + .filter(KeyValueParam::isValid) + .forEach(queryParam -> { + stringBuffer.append(queryParam.getEncode() ? String.format(URL_ENCODE, queryParam.getKey()) : queryParam.getKey()); + if (queryParam.getValue() != null) { + try { + String value = queryParam.getValue().startsWith("@") ? Mock.buildFunctionCallString(queryParam.getValue()) : queryParam.getValue(); + value = queryParam.getEncode() ? String.format(URL_ENCODE, value.replace(",", "\\,")) : value; + if (StringUtils.isNotEmpty(value) && value.contains(StringUtils.CR)) { + value = value.replaceAll(StringUtils.CR, StringUtils.EMPTY); + } + stringBuffer.append("=").append(value); + } catch (Exception e) { + LogUtils.error(e); + } + } + stringBuffer.append("&"); + }); + return stringBuffer.substring(0, stringBuffer.length() - 1); + } } diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/MsScenarioConverter.java b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/MsScenarioConverter.java index 42c07b1e5e..f1bfd0e600 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/MsScenarioConverter.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/MsScenarioConverter.java @@ -21,12 +21,17 @@ import io.metersphere.sdk.util.BeanUtils; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; +import org.apache.jmeter.protocol.http.control.CookieManager; +import org.apache.jmeter.save.SaveService; +import org.apache.jmeter.testelement.TestElement; import org.apache.jorphan.collections.HashTree; import java.util.List; import java.util.Map; import java.util.function.Function; +import static io.metersphere.api.parser.jmeter.constants.JmeterAlias.COOKIE_PANEL; + /** * @Author: jianxing * @CreateTime: 2023-10-27 10:07 @@ -40,11 +45,17 @@ public class MsScenarioConverter extends AbstractJmeterElementConverter projectEnvMap = msScenario.getProjectEnvMap(); - chileConfig.setProjectEnvMap(projectEnvMap); + childConfig.setProjectEnvMap(projectEnvMap); } else { // 设置环境信息 EnvironmentInfoDTO environmentInfo = msScenario.getEnvironmentInfo(); - chileConfig.setEnvConfig(environmentInfo); + childConfig.setEnvConfig(environmentInfo); } - return chileConfig; } - return config; + + ScenarioConfig scenarioConfig = msScenario.getScenarioConfig(); + if (scenarioConfig != null) { + // 设置是否使用全局cookie + childConfig.setEnableGlobalCookie(scenarioConfig.getOtherConfig().getEnableCookieShare()); + } + + return childConfig; + } + + private CookieManager getCookieManager() { + CookieManager cookieManager = new CookieManager(); + cookieManager.setProperty(TestElement.TEST_CLASS, CookieManager.class.getName()); + cookieManager.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass(COOKIE_PANEL)); + cookieManager.setEnabled(true); + cookieManager.setName("CookieManager"); + cookieManager.setClearEachIteration(false); + cookieManager.setControlledByThread(false); + return cookieManager; } } diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/constants/JmeterAlias.java b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/constants/JmeterAlias.java index efa1357bb8..6b71a695d8 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/constants/JmeterAlias.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/constants/JmeterAlias.java @@ -17,4 +17,6 @@ public class JmeterAlias { public static final String XPATH_ASSERTION_GUI = "XPathAssertionGui"; public static final String X_PATH_2_ASSERTION_GUI = "XPath2AssertionGui"; public static final String USER_PARAMETERS_GUI = "UserParametersGui"; + public static final String COOKIE_PANEL = "CookiePanel"; + public static final String HEADER_PANEL = "HeaderPanel"; } diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiExecuteService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiExecuteService.java index a8e0eb3485..eb1bec6e05 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiExecuteService.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiExecuteService.java @@ -13,10 +13,9 @@ import io.metersphere.plugin.api.spi.AbstractMsTestElement; import io.metersphere.project.domain.FileMetadata; import io.metersphere.project.domain.ProjectApplication; import io.metersphere.project.dto.customfunction.request.CustomFunctionRunRequest; -import io.metersphere.project.service.FileAssociationService; -import io.metersphere.project.service.FileManagementService; -import io.metersphere.project.service.FileMetadataService; -import io.metersphere.project.service.ProjectApplicationService; +import io.metersphere.project.dto.environment.GlobalParams; +import io.metersphere.project.dto.environment.GlobalParamsDTO; +import io.metersphere.project.service.*; import io.metersphere.sdk.constants.ApiExecuteResourceType; import io.metersphere.sdk.constants.ApiExecuteRunMode; import io.metersphere.sdk.constants.ProjectApplicationType; @@ -82,6 +81,8 @@ public class ApiExecuteService { private FileManagementService fileManagementService; @Resource private ApiPluginService apiPluginService; + @Resource + private GlobalParamsService globalParamsService; @PostConstruct private void init() { @@ -128,6 +129,8 @@ public class ApiExecuteService { taskRequest.setMsRegexList(projectApplicationService.get(Collections.singletonList(request.getProjectId()))); } + parameterConfig.setGlobalParams(getGlobalParam(request)); + // todo 获取接口插件和jar包 // todo 处理公共脚本 // todo 接口用例 method 获取定义中的数据库字段 @@ -136,6 +139,14 @@ public class ApiExecuteService { doDebug(reportId, testId, taskRequest, executeScript, request.getProjectId()); } + private GlobalParams getGlobalParam(ApiResourceRunRequest request) { + GlobalParamsDTO globalParamsDTO = globalParamsService.get(request.getProjectId()); + if (globalParamsDTO != null) { + return globalParamsDTO.getGlobalParams(); + } + return null; + } + /** * 发送执行任务 * diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/debug/ApiDebugService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/debug/ApiDebugService.java index 32901434d9..529e8bc12c 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/service/debug/ApiDebugService.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/debug/ApiDebugService.java @@ -13,8 +13,6 @@ import io.metersphere.api.service.ApiExecuteService; import io.metersphere.api.service.ApiFileResourceService; import io.metersphere.api.utils.ApiDataUtils; import io.metersphere.plugin.api.spi.AbstractMsTestElement; -import io.metersphere.project.dto.environment.EnvironmentInfoDTO; -import io.metersphere.project.service.EnvironmentService; import io.metersphere.project.service.ProjectService; import io.metersphere.sdk.constants.DefaultRepositoryDir; import io.metersphere.sdk.exception.MSException; @@ -55,8 +53,6 @@ public class ApiDebugService { private ApiExecuteService apiExecuteService; @Resource private ApiPluginService apiPluginService; - @Resource - private EnvironmentService environmentService; public static final Long ORDER_STEP = 5000L; @@ -203,19 +199,13 @@ public class ApiDebugService { runRequest.setTestId(id); runRequest.setReportId(reportId); runRequest.setResourceType(ApiResourceType.API_DEBUG.name()); - runRequest.setEnvironmentId(request.getEnvironmentId()); runRequest.setTestElement(ApiDataUtils.parseObject(JSON.toJSONString(request.getRequest()), AbstractMsTestElement.class)); ApiParamConfig paramConfig = new ApiParamConfig(); paramConfig.setTestElementClassPluginIdMap(apiPluginService.getTestElementPluginMap()); paramConfig.setTestElementClassProtocalMap(apiPluginService.getTestElementProtocolMap()); paramConfig.setReportId(reportId); - EnvironmentInfoDTO environmentInfoDTO = environmentService.get(request.getEnvironmentId()); - // 设置环境 - paramConfig.setEnvConfig(environmentInfoDTO); - apiExecuteService.debug(runRequest, paramConfig); - return runRequest.getReportId(); } diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiDefinitionModuleService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiDefinitionModuleService.java index d3807850e6..8563639ab1 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiDefinitionModuleService.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiDefinitionModuleService.java @@ -32,6 +32,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -346,5 +347,10 @@ public class ApiDefinitionModuleService extends ModuleTreeService { }); } - + public List getModuleIdsByParentIds(List parentIds) { + if (CollectionUtils.isEmpty(parentIds)) { + return Collections.emptyList(); + } + return extApiDefinitionModuleMapper.getModuleIdsByParentIds(parentIds); + } } diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiDefinitionService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiDefinitionService.java index 75b3197234..dcd4cc97e3 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiDefinitionService.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiDefinitionService.java @@ -4,6 +4,7 @@ import io.metersphere.api.constants.ApiDefinitionDocType; import io.metersphere.api.constants.ApiResourceType; import io.metersphere.api.controller.result.ApiResultCode; import io.metersphere.api.domain.*; +import io.metersphere.api.dto.ApiResourceModuleInfo; import io.metersphere.api.dto.converter.ApiDefinitionImport; import io.metersphere.api.dto.debug.ApiFileResourceUpdateRequest; import io.metersphere.api.dto.definition.*; @@ -1024,4 +1025,8 @@ public class ApiDefinitionService { } } } + + public List getModuleInfoByIds(List apiIds) { + return extApiDefinitionMapper.getModuleInfoByIds(apiIds); + } } diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiTestCaseService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiTestCaseService.java index 9bf5946f97..4ce9c304d9 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiTestCaseService.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiTestCaseService.java @@ -2,6 +2,7 @@ package io.metersphere.api.service.definition; import io.metersphere.api.constants.ApiResourceType; import io.metersphere.api.domain.*; +import io.metersphere.api.dto.ApiResourceModuleInfo; import io.metersphere.api.dto.debug.ApiFileResourceUpdateRequest; import io.metersphere.api.dto.definition.*; import io.metersphere.api.dto.request.http.MsHTTPElement; @@ -81,6 +82,8 @@ public class ApiTestCaseService { private ApiDefinitionModuleMapper apiDefinitionModuleMapper; @Resource private OperationHistoryService operationHistoryService; + @Resource + private ExtApiDefinitionMapper extApiDefinitionMapper; private static final String CASE_TABLE = "api_test_case"; private void checkProjectExist(String projectId) { @@ -581,4 +584,18 @@ public class ApiTestCaseService { update.setUpdateTime(System.currentTimeMillis()); apiTestCaseMapper.updateByPrimaryKeySelective(update); } + + public List getModuleInfoByIds(List ids) { + // 获取接口定义ID和用例ID的映射 + Map apiCaseDefinitionMap = extApiTestCaseMapper.getApiCaseDefinitionInfo(ids) + .stream() + .collect(Collectors.toMap(ApiTestCase::getApiDefinitionId, ApiTestCase::getId)); + + List definitionIds = apiCaseDefinitionMap.keySet().stream().collect(Collectors.toList()); + List moduleInfos = extApiDefinitionMapper.getModuleInfoByIds(definitionIds); + // 将 resourceId 从定义ID替换成用例ID + moduleInfos.forEach(moduleInfo -> + moduleInfo.setResourceId(apiCaseDefinitionMap.get(moduleInfo.getResourceId()))); + return moduleInfos; + } } diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/scenario/ApiScenarioService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/scenario/ApiScenarioService.java index 2b069d9123..315101f3ac 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/service/scenario/ApiScenarioService.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/scenario/ApiScenarioService.java @@ -4,12 +4,14 @@ import io.metersphere.api.constants.ApiResourceType; import io.metersphere.api.constants.ApiScenarioStepRefType; import io.metersphere.api.constants.ApiScenarioStepType; import io.metersphere.api.domain.*; +import io.metersphere.api.dto.ApiResourceModuleInfo; import io.metersphere.api.dto.ApiScenarioParamConfig; import io.metersphere.api.dto.ApiScenarioParseEnvInfo; import io.metersphere.api.dto.EnvironmentModeDTO; import io.metersphere.api.dto.debug.ApiFileResourceUpdateRequest; import io.metersphere.api.dto.debug.ApiResourceRunRequest; import io.metersphere.api.dto.request.MsScenario; +import io.metersphere.api.dto.request.http.MsHTTPElement; import io.metersphere.api.dto.response.ApiScenarioBatchOperationResponse; import io.metersphere.api.dto.scenario.*; import io.metersphere.api.job.ApiScenarioScheduleJob; @@ -18,6 +20,7 @@ import io.metersphere.api.parser.step.StepParser; import io.metersphere.api.parser.step.StepParserFactory; import io.metersphere.api.service.ApiExecuteService; import io.metersphere.api.service.ApiFileResourceService; +import io.metersphere.api.service.definition.ApiDefinitionModuleService; import io.metersphere.api.service.definition.ApiDefinitionService; import io.metersphere.api.service.definition.ApiTestCaseService; import io.metersphere.api.utils.ApiScenarioBatchOperationUtils; @@ -26,10 +29,16 @@ import io.metersphere.project.domain.FileMetadata; import io.metersphere.project.domain.Project; import io.metersphere.project.domain.ProjectExample; import io.metersphere.project.dto.environment.EnvironmentInfoDTO; +import io.metersphere.project.dto.environment.http.HttpConfig; +import io.metersphere.project.dto.environment.http.HttpConfigModuleMatchRule; +import io.metersphere.project.dto.environment.http.SelectModule; import io.metersphere.project.mapper.ExtBaseProjectVersionMapper; import io.metersphere.project.mapper.ProjectMapper; import io.metersphere.project.service.*; -import io.metersphere.sdk.constants.*; +import io.metersphere.sdk.constants.ApplicationNumScope; +import io.metersphere.sdk.constants.DefaultRepositoryDir; +import io.metersphere.sdk.constants.ModuleConstants; +import io.metersphere.sdk.constants.ScheduleResourceType; import io.metersphere.sdk.domain.Environment; import io.metersphere.sdk.domain.EnvironmentExample; import io.metersphere.sdk.domain.EnvironmentGroup; @@ -157,6 +166,8 @@ public class ApiScenarioService { private ApiTestCaseMapper apiTestCaseMapper; @Resource private ExtApiTestCaseMapper extApiTestCaseMapper; + @Resource + private ApiDefinitionModuleService apiDefinitionModuleService; public static final String PRIORITY = "Priority"; public static final String STATUS = "Status"; @@ -1046,7 +1057,10 @@ public class ApiScenarioService { // 获取场景环境相关配置 ApiScenarioParseEnvInfo scenarioParseEnvInfo = getScenarioParseEnvInfo(refResourceMap, request.getEnvironmentId(), request.getGrouped()); - parseStep2MsElement(msScenario, steps, resourceBlobMap, detailMap, scenarioParseEnvInfo); + Map> stepTypeHttpElementMap = new HashMap<>(); + parseStep2MsElement(msScenario, steps, resourceBlobMap, detailMap, stepTypeHttpElementMap, scenarioParseEnvInfo); + // 设置 HttpElement 的模块信息 + setHttpElementModuleId(stepTypeHttpElementMap); ApiResourceRunRequest runRequest = BeanUtils.copyBean(new ApiResourceRunRequest(), request); runRequest.setProjectId(request.getProjectId()); @@ -1063,7 +1077,6 @@ public class ApiScenarioService { parseConfig.setTestElementClassProtocalMap(apiPluginService.getTestElementProtocolMap()); parseConfig.setGrouped(request.getGrouped()); parseConfig.setReportId(request.getReportId()); - scenarioParseEnvInfo.getPluginClassEnvConfigMap(); if (BooleanUtils.isTrue(request.getGrouped())) { // 设置环境组 map parseConfig.setProjectEnvMap(getProjectEnvMap(scenarioParseEnvInfo, request.getEnvironmentId())); @@ -1073,10 +1086,33 @@ public class ApiScenarioService { } apiExecuteService.debug(runRequest, parseConfig); - return request.getReportId(); } + /** + * 设置 HttpElement 的模块信息 + * 用户环境中的模块过滤 + * @param stepTypeHttpElementMap + */ + private void setHttpElementModuleId(Map> stepTypeHttpElementMap) { + setHttpElementModuleId(stepTypeHttpElementMap.get(ApiScenarioStepType.API.name()), apiDefinitionService::getModuleInfoByIds); + setHttpElementModuleId(stepTypeHttpElementMap.get(ApiScenarioStepType.API_CASE.name()), apiTestCaseService::getModuleInfoByIds); + } + + private void setHttpElementModuleId(List httpElements, Function, List> getModuleInfoFunc) { + if (CollectionUtils.isNotEmpty(httpElements)) { + List apiIds = httpElements.stream().map(MsHTTPElement::getResourceId).collect(Collectors.toList()); + // 获取接口模块信息 + Map resourceModuleMap = getModuleInfoFunc.apply(apiIds) + .stream() + .collect(Collectors.toMap(ApiResourceModuleInfo::getResourceId, ApiResourceModuleInfo::getModuleId)); + httpElements.forEach(httpElement -> { + // httpElement 设置模块信息 + httpElement.setModuleId(resourceModuleMap.get(httpElement.getResourceId())); + }); + } + } + /** * 设置脚本解析-环境相关参数 */ @@ -1128,9 +1164,48 @@ public class ApiScenarioService { envInfo.setEnvGroupMap(envGroupMap); envInfo.setEnvMap(envMap); + + envMap.forEach((envId, envInfoDTO) -> handleHttpModuleMatchRule(envInfoDTO)); + return envInfo; } + /** + * 处理环境的 HTTP 配置模块匹配规则 + * 查询新增子模块 + * @param envInfoDTO + */ + private void handleHttpModuleMatchRule(EnvironmentInfoDTO envInfoDTO) { + List httpConfigs = envInfoDTO.getConfig().getHttpConfig(); + for (HttpConfig httpConfig : httpConfigs) { + if (!httpConfig.isModuleMatchRule()) { + continue; + } + // 获取勾选了包含子模块的模块ID + HttpConfigModuleMatchRule moduleMatchRule = httpConfig.getModuleMatchRule(); + List selectModules = moduleMatchRule.getModules(); + List containChildModuleIds = selectModules.stream() + .filter(SelectModule::getContainChildModule) + .map(SelectModule::getModuleId) + .toList(); + + // 查询子模块ID, 并去重 + Set moduleIds = apiDefinitionModuleService.getModuleIdsByParentIds(containChildModuleIds) + .stream() + .collect(Collectors.toSet()); + selectModules.forEach(selectModule -> moduleIds.add(selectModule.getModuleId())); + + // 重新设置选中的模块ID + moduleMatchRule.setModules(null); + List allSelectModules = moduleIds.stream().map(moduleId -> { + SelectModule module = new SelectModule(); + module.setModuleId(moduleId); + return module; + }).collect(Collectors.toList()); + moduleMatchRule.setModules(allSelectModules); + } + } + private List getApiScenarioByIds(List apiScenarioIds) { ApiScenarioExample example = new ApiScenarioExample(); example.createCriteria().andIdIn(apiScenarioIds); @@ -1144,6 +1219,7 @@ public class ApiScenarioService { List steps, Map resourceBlobMap, Map stepDetailMap, + Map> stepTypeHttpElementMap, ApiScenarioParseEnvInfo scenarioParseEnvInfo) { if (CollectionUtils.isNotEmpty(steps)) { parentElement.setChildren(new LinkedList<>()); @@ -1158,12 +1234,19 @@ public class ApiScenarioService { // 将步骤详情解析生成对应的MsTestElement AbstractMsTestElement msTestElement = stepParser.parseTestElement(step, resourceBlobMap.get(step.getResourceId()), stepDetailMap.get(step.getId())); if (msTestElement != null) { + if (msTestElement instanceof MsHTTPElement msHTTPElement) { + // 暂存http类型的步骤 + stepTypeHttpElementMap.putIfAbsent(step.getStepType(), new LinkedList<>()); + stepTypeHttpElementMap.get(step.getStepType()).add(msHTTPElement); + } msTestElement.setProjectId(step.getProjectId()); + msTestElement.setResourceId(step.getResourceId()); setMsScenarioParam(scenarioParseEnvInfo, step, msTestElement); parentElement.getChildren().add(msTestElement); } if (CollectionUtils.isNotEmpty(step.getChildren())) { - parseStep2MsElement(msTestElement, step.getChildren(), resourceBlobMap, stepDetailMap, scenarioParseEnvInfo); + parseStep2MsElement(msTestElement, step.getChildren(), resourceBlobMap, + stepDetailMap, stepTypeHttpElementMap, scenarioParseEnvInfo); } } } diff --git a/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiScenarioControllerTests.java b/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiScenarioControllerTests.java index 32aeacd376..857a0adc73 100644 --- a/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiScenarioControllerTests.java +++ b/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiScenarioControllerTests.java @@ -3,9 +3,12 @@ package io.metersphere.api.controller; import io.metersphere.api.constants.*; import io.metersphere.api.domain.*; import io.metersphere.api.dto.assertion.MsAssertionConfig; +import io.metersphere.api.dto.debug.ModuleCreateRequest; import io.metersphere.api.dto.definition.ApiDefinitionAddRequest; import io.metersphere.api.dto.definition.ApiTestCaseAddRequest; +import io.metersphere.api.dto.request.http.Header; import io.metersphere.api.dto.request.http.MsHTTPElement; +import io.metersphere.api.dto.request.http.QueryParam; import io.metersphere.api.dto.response.ApiScenarioBatchOperationResponse; import io.metersphere.api.dto.response.OperationDataInfo; import io.metersphere.api.dto.scenario.*; @@ -13,10 +16,12 @@ import io.metersphere.api.job.ApiScenarioScheduleJob; import io.metersphere.api.mapper.*; import io.metersphere.api.service.ApiScenarioBatchOperationTestService; import io.metersphere.api.service.BaseResourcePoolTestService; +import io.metersphere.api.service.definition.ApiDefinitionModuleService; import io.metersphere.api.service.definition.ApiDefinitionService; import io.metersphere.api.service.definition.ApiTestCaseService; import io.metersphere.api.service.scenario.ApiScenarioService; import io.metersphere.api.utils.ApiDataUtils; +import io.metersphere.project.api.KeyValueEnableParam; import io.metersphere.project.api.assertion.MsResponseCodeAssertion; import io.metersphere.project.api.assertion.MsScriptAssertion; import io.metersphere.project.api.processor.MsProcessor; @@ -25,6 +30,9 @@ import io.metersphere.project.dto.environment.EnvironmentConfig; import io.metersphere.project.dto.environment.EnvironmentGroupProjectDTO; import io.metersphere.project.dto.environment.EnvironmentGroupRequest; import io.metersphere.project.dto.environment.EnvironmentRequest; +import io.metersphere.project.dto.environment.http.HttpConfig; +import io.metersphere.project.dto.environment.http.HttpConfigPathMatchRule; +import io.metersphere.project.dto.environment.http.SelectModule; import io.metersphere.project.dto.environment.processors.EnvProcessorConfig; import io.metersphere.project.dto.environment.processors.EnvRequestScriptProcessor; import io.metersphere.project.dto.environment.processors.EnvScenarioScriptProcessor; @@ -49,10 +57,10 @@ import io.metersphere.system.controller.handler.ResultHolder; import io.metersphere.system.domain.Plugin; import io.metersphere.system.domain.Schedule; import io.metersphere.system.dto.request.PluginUpdateRequest; +import io.metersphere.system.dto.sdk.BaseTreeNode; import io.metersphere.system.dto.sdk.request.PosRequest; import io.metersphere.system.log.constants.OperationLogType; import io.metersphere.system.mapper.ScheduleMapper; -import io.metersphere.system.service.PluginLoadService; import io.metersphere.system.service.PluginService; import io.metersphere.system.uid.IDGenerator; import io.metersphere.system.uid.NumGenerator; @@ -144,11 +152,11 @@ public class ApiScenarioControllerTests extends BaseTest { @Resource private PluginService pluginService; @Resource - private PluginLoadService pluginLoadService; - @Resource private ApiDefinitionMapper apiDefinitionMapper; @Resource private ApiTestCaseMapper apiTestCaseMapper; + @Resource + private ApiDefinitionModuleService apiDefinitionModuleService; private static String fileMetadataId; private static String localFileId; private static ApiScenario addApiScenario; @@ -159,6 +167,7 @@ public class ApiScenarioControllerTests extends BaseTest { private static ApiTestCase apiTestCase; private static String envId; private static String envGroupId; + private static String moduleId; private static final List LOG_CHECK_LIST = new ArrayList<>(); @@ -284,6 +293,7 @@ public class ApiScenarioControllerTests extends BaseTest { @Test @Order(1) public void add() throws Exception { + initModule(); initEnv(); initTestData(); @@ -482,6 +492,18 @@ public class ApiScenarioControllerTests extends BaseTest { } private void initTestData() { + Header header1 = new Header(); + header1.setKey("a"); + header1.setValue("aaa"); + + Header header2 = new Header(); + header2.setKey("c"); + header2.setValue("cc"); + + Header header3 = new Header(); + header3.setKey("Cookie"); + header3.setValue("b=c"); + ApiDefinitionAddRequest apiDefinitionAddRequest = new ApiDefinitionAddRequest(); apiDefinitionAddRequest.setName("test scenario"); apiDefinitionAddRequest.setProtocol(ApiConstants.HTTP_PROTOCOL); @@ -489,11 +511,22 @@ public class ApiScenarioControllerTests extends BaseTest { apiDefinitionAddRequest.setMethod("POST"); apiDefinitionAddRequest.setPath("/api/admin/posts"); apiDefinitionAddRequest.setStatus(ApiDefinitionStatus.PREPARE.getValue()); - apiDefinitionAddRequest.setModuleId("default"); + apiDefinitionAddRequest.setModuleId(moduleId); apiDefinitionAddRequest.setVersionId(extBaseProjectVersionMapper.getDefaultVersion(DEFAULT_PROJECT_ID)); apiDefinitionAddRequest.setDescription("描述内容"); apiDefinitionAddRequest.setName("test scenario"); MsHTTPElement msHttpElement = MsHTTPElementTest.getAddProcessorHttpElement(); + msHttpElement.setHeaders(List.of(header1, header2, header3)); + msHttpElement.setPath(apiDefinitionAddRequest.getPath()); + QueryParam queryParam1 = new QueryParam(); + queryParam1.setEncode(true); + queryParam1.setKey("aa"); + queryParam1.setValue("bbb"); + QueryParam queryParam2 = new QueryParam(); + queryParam2.setEncode(false); + queryParam2.setKey("aa2"); + queryParam2.setValue("bbb2"); + msHttpElement.setQuery(List.of(queryParam1, queryParam2)); apiDefinitionAddRequest.setRequest(getMsElementParam(msHttpElement)); apiDefinitionAddRequest.setResponse("{}"); apiDefinition = apiDefinitionService.create(apiDefinitionAddRequest, "admin"); @@ -654,6 +687,8 @@ public class ApiScenarioControllerTests extends BaseTest { pluginStepDetail.put("port", "port"); pluginStepDetail.put("projectId", DEFAULT_PROJECT_ID); request.getStepDetails().put(pluginStep.getId(), pluginStepDetail); + request.getScenarioConfig().getOtherConfig().setEnableCookieShare(true); + request.getScenarioConfig().getOtherConfig().setEnableGlobalCookie(false); Plugin plugin = addEnvTestPlugin(); this.requestPostWithOk(DEBUG, request); @@ -678,6 +713,14 @@ public class ApiScenarioControllerTests extends BaseTest { return pluginService.add(request, mockMultipartFile); } + private void initModule() { + ModuleCreateRequest request = new ModuleCreateRequest(); + request.setName("test"); + request.setProjectId(DEFAULT_PROJECT_ID); + request.setParentId(ModuleConstants.ROOT_NODE_PARENT_ID); + moduleId = apiDefinitionModuleService.add(request, "admin"); + } + private void initEnv() { EnvironmentRequest envRequest = new EnvironmentRequest(); envRequest.setProjectId(DEFAULT_PROJECT_ID); @@ -730,6 +773,41 @@ public class ApiScenarioControllerTests extends BaseTest { responseCodeAssertion.setName("test"); environmentConfig.getAssertionConfig().getAssertions().add(responseCodeAssertion); + KeyValueEnableParam header1 = new KeyValueEnableParam(); + header1.setKey("a"); + header1.setValue("aa"); + + KeyValueEnableParam header2 = new KeyValueEnableParam(); + header2.setKey("b"); + header2.setValue("bb"); + + KeyValueEnableParam header3 = new KeyValueEnableParam(); + header3.setKey("Cookie"); + header3.setValue("a=b"); + + HttpConfig httpNoneConfig = new HttpConfig(); + httpNoneConfig.setUrl("localhost:8081"); + httpNoneConfig.setType(HttpConfig.HttpConfigMatchType.NONE.name()); + httpNoneConfig.setHeaders(List.of(header1, header2, header3)); + + HttpConfig httpModuleConfig = new HttpConfig(); + httpModuleConfig.setUrl("localhost:8081"); + httpModuleConfig.setType(HttpConfig.HttpConfigMatchType.MODULE.name()); + SelectModule selectModule = new SelectModule(); + selectModule.setModuleId(moduleId); + selectModule.setContainChildModule(true); + httpModuleConfig.getModuleMatchRule().setModules(List.of(selectModule)); + httpModuleConfig.setHeaders(List.of(header1, header2, header3)); + + HttpConfig httpPathConfig = new HttpConfig(); + httpPathConfig.setUrl("localhost:8081"); + httpPathConfig.setType(HttpConfig.HttpConfigMatchType.PATH.name()); + httpPathConfig.getPathMatchRule().setPath("/test"); + httpPathConfig.getPathMatchRule().setCondition(HttpConfigPathMatchRule.MatchRuleCondition.CONTAINS.name()); + httpPathConfig.setHeaders(List.of(header1, header2, header3)); + + environmentConfig.setHttpConfig(List.of(httpNoneConfig, httpModuleConfig, httpPathConfig)); + environmentConfig.setPluginConfigMap(pluginConfigMap); envRequest.setConfig(environmentConfig); Environment environment = environmentService.add(envRequest, "admin", null); diff --git a/backend/services/project-management/src/main/java/io/metersphere/project/dto/environment/http/HttpConfig.java b/backend/services/project-management/src/main/java/io/metersphere/project/dto/environment/http/HttpConfig.java index 5ac046d000..e0b3972e2b 100644 --- a/backend/services/project-management/src/main/java/io/metersphere/project/dto/environment/http/HttpConfig.java +++ b/backend/services/project-management/src/main/java/io/metersphere/project/dto/environment/http/HttpConfig.java @@ -1,10 +1,12 @@ package io.metersphere.project.dto.environment.http; import io.metersphere.project.api.KeyValueEnableParam; +import io.metersphere.sdk.constants.ValueEnum; import io.metersphere.system.valid.EnumValue; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.Valid; import lombok.Data; +import org.apache.commons.lang3.StringUtils; import java.io.Serial; import java.io.Serializable; @@ -15,7 +17,9 @@ import java.util.List; public class HttpConfig implements Serializable { @Serial private static final long serialVersionUID = 1L; - + @Schema(description = "http协议类型(http/https)") + @EnumValue(enumClass = HttpProtocolType.class) + private String protocol = HttpProtocolType.HTTP.name(); @Schema(description = "环境域名") private String url; /** @@ -34,6 +38,24 @@ public class HttpConfig implements Serializable { @Schema(description = "请求头") private List<@Valid KeyValueEnableParam> headers = new ArrayList<>(0); + + public boolean isModuleMatchRule() { + return StringUtils.equals(HttpConfigMatchType.MODULE.name(), type); + } + + public boolean isPathMatchRule() { + return StringUtils.equals(HttpConfigMatchType.PATH.name(), type); + } + + public int getModuleMatchRuleOrder() { + if (isPathMatchRule()) { + return 0; + } else if (isModuleMatchRule()) { + return 1; + } + return 2; + } + /** * 启用条件匹配类型 */ @@ -51,4 +73,23 @@ public class HttpConfig implements Serializable { */ NONE } + + /** + * 启用条件匹配类型 + */ + public enum HttpProtocolType implements ValueEnum { + HTTP("http"), + HTTPS("https"); + + private String value; + + HttpProtocolType(String value) { + this.value = value; + } + + @Override + public String getValue() { + return this.value; + } + } } diff --git a/backend/services/project-management/src/main/java/io/metersphere/project/dto/environment/http/HttpConfigPathMatchRule.java b/backend/services/project-management/src/main/java/io/metersphere/project/dto/environment/http/HttpConfigPathMatchRule.java index 0ea990a37e..62a84ae86a 100644 --- a/backend/services/project-management/src/main/java/io/metersphere/project/dto/environment/http/HttpConfigPathMatchRule.java +++ b/backend/services/project-management/src/main/java/io/metersphere/project/dto/environment/http/HttpConfigPathMatchRule.java @@ -3,9 +3,11 @@ package io.metersphere.project.dto.environment.http; import io.metersphere.system.valid.EnumValue; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; +import org.apache.commons.lang3.StringUtils; import java.io.Serial; import java.io.Serializable; +import java.util.function.BiFunction; /** * @Author: jianxing @@ -30,10 +32,20 @@ public class HttpConfigPathMatchRule implements Serializable { /** * 包含 */ - CONTAINS, + CONTAINS((envPath, path) -> StringUtils.contains(path, envPath)), /** * 等于 */ - EQUALS + EQUALS((envPath, path) -> StringUtils.equals(path, envPath)); + + MatchRuleCondition(BiFunction matchFunc) { + this.matchFunc = matchFunc; + } + + private BiFunction matchFunc; + + public boolean match(String value, String expect) { + return matchFunc.apply(value, expect); + } } } diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/service/PluginLoadService.java b/backend/services/system-setting/src/main/java/io/metersphere/system/service/PluginLoadService.java index 0095347b52..280282e520 100644 --- a/backend/services/system-setting/src/main/java/io/metersphere/system/service/PluginLoadService.java +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/service/PluginLoadService.java @@ -60,7 +60,7 @@ public class PluginLoadService { * @param fileName * @return */ - public String loadPlugin(String fileName) { + public synchronized String loadPlugin(String fileName) { MsFileUtils.validateFileName(fileName); String filePath = LocalRepositoryDir.getPluginDir() + "/" + fileName; File file = new File(filePath); diff --git a/backend/services/system-setting/src/test/java/io/metersphere/system/base/BasePluginTestService.java b/backend/services/system-setting/src/test/java/io/metersphere/system/base/BasePluginTestService.java index dcba3b45cc..d7ba6051bd 100644 --- a/backend/services/system-setting/src/test/java/io/metersphere/system/base/BasePluginTestService.java +++ b/backend/services/system-setting/src/test/java/io/metersphere/system/base/BasePluginTestService.java @@ -56,7 +56,7 @@ public class BasePluginTestService { * @return * @throws Exception */ - public Plugin addJiraPlugin() throws Exception { + public synchronized Plugin addJiraPlugin() throws Exception { if (hasJiraPlugin()) { return jiraPlugin; } @@ -92,7 +92,7 @@ public class BasePluginTestService { return serviceIntegration; } - public Plugin getJiraPlugin() throws Exception { + public synchronized Plugin getJiraPlugin() throws Exception { if (!hasJiraPlugin()) { return this.addJiraPlugin(); } @@ -107,7 +107,7 @@ public class BasePluginTestService { return jiraPlugin != null; } - public void deleteJiraPlugin() { + public synchronized void deleteJiraPlugin() { if (jiraPlugin != null) { pluginService.delete(jiraPlugin.getId()); jiraPlugin = null;