From e3e4af29889cac8dc322a2cf72dd8b13c2ae8eeb Mon Sep 17 00:00:00 2001 From: AgAngle <1323481023@qq.com> Date: Fri, 19 Jan 2024 17:43:18 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E9=A1=B9=E7=9B=AE=E8=AE=BE=E7=BD=AE):=20?= =?UTF-8?q?=E5=85=AC=E5=85=B1=E8=84=9A=E6=9C=AC=E6=89=A7=E8=A1=8C=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../migration/3.0.0/dml/V3.0.0_11_1__data.sql | 7 + .../sdk/constants/PermissionConstants.java | 1 + .../sdk/dto/api/task/TaskRequestDTO.java | 2 +- .../main/resources/i18n/project.properties | 3 +- .../resources/i18n/project_en_US.properties | 2 + .../resources/i18n/project_zh_CN.properties | 2 + .../resources/i18n/project_zh_TW.properties | 2 + .../resources/i18n/system_zh_CN.properties | 2 +- backend/services/api-test/pom.xml | 5 + .../api/controller/ApiTestController.java | 19 ++- .../java/io/metersphere/api/dto/NodeDTO.java | 31 ----- .../controller/MsCommentScriptElement.java | 30 ++++ .../request/processors/ScriptProcessor.java | 19 +-- .../MsCommentScriptElementConverter.java | 82 +++++++++++ .../parser/jmeter/constants/JmeterAlias.java | 1 + .../parser/jmeter/processor/ScriptFilter.java | 6 +- .../processor/ScriptProcessorConverter.java | 5 +- .../assertion/VariableAssertionConverter.java | 3 +- .../api/service/ApiExecuteService.java | 121 +++++++++++++--- .../api/service/RoundRobinService.java | 12 +- .../utils/JmeterElementConverterRegister.java | 2 + .../controller/ApiDebugControllerTests.java | 108 +-------------- .../controller/ApiTestControllerTests.java | 37 +++++ .../api/controller/MsHTTPElementTest.java | 5 +- .../service/BaseResourcePoolTestService.java | 131 ++++++++++++++++++ .../api/service/RoundRobinServiceTests.java | 26 ++-- .../project/constants/ScriptLanguageType.java | 23 +++ .../controller/CustomFunctionController.java | 2 - .../request/CustomFunctionRunRequest.java | 34 +++++ .../processors/ScriptProcessor.java | 20 +-- .../src/main/resources/permission.json | 22 +++ .../system/dto/pool/TestResourceNodeDTO.java | 4 + .../controller/PluginControllerTests.java | 22 +-- 33 files changed, 557 insertions(+), 234 deletions(-) delete mode 100644 backend/services/api-test/src/main/java/io/metersphere/api/dto/NodeDTO.java create mode 100644 backend/services/api-test/src/main/java/io/metersphere/api/dto/request/controller/MsCommentScriptElement.java create mode 100644 backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/MsCommentScriptElementConverter.java create mode 100644 backend/services/api-test/src/test/java/io/metersphere/api/service/BaseResourcePoolTestService.java create mode 100644 backend/services/project-management/src/main/java/io/metersphere/project/constants/ScriptLanguageType.java create mode 100644 backend/services/project-management/src/main/java/io/metersphere/project/dto/customfunction/request/CustomFunctionRunRequest.java diff --git a/backend/framework/domain/src/main/resources/migration/3.0.0/dml/V3.0.0_11_1__data.sql b/backend/framework/domain/src/main/resources/migration/3.0.0/dml/V3.0.0_11_1__data.sql index d3b613838c..5394ad1609 100644 --- a/backend/framework/domain/src/main/resources/migration/3.0.0/dml/V3.0.0_11_1__data.sql +++ b/backend/framework/domain/src/main/resources/migration/3.0.0/dml/V3.0.0_11_1__data.sql @@ -200,6 +200,11 @@ INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_TEST_PLAN:READ+UPDATE'); INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_TEST_PLAN:READ+DELETE'); INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_TEST_PLAN:READ+EXECUTE'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_CUSTOM_FUNCTION:READ'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_CUSTOM_FUNCTION:READ+ADD'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_CUSTOM_FUNCTION:READ+UPDATE'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_CUSTOM_FUNCTION:READ+DELETE'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_CUSTOM_FUNCTION:READ+EXECUTE'); -- 项目成员权限 INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'PROJECT_BASE_INFO:READ'); @@ -255,6 +260,8 @@ INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'PROJECT_BUG:READ+DELETE'); INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'PROJECT_BUG:READ+EXPORT'); INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'PROJECT_BASE_INFO:READ+UPDATE'); +INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'PROJECT_CUSTOM_FUNCTION:READ'); + INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'PROJECT_API_DEBUG:READ'); INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'PROJECT_API_DEBUG:READ+ADD'); INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'PROJECT_API_DEBUG:READ+UPDATE'); diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/constants/PermissionConstants.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/constants/PermissionConstants.java index 46f1eac0da..3b3c6eae43 100644 --- a/backend/framework/sdk/src/main/java/io/metersphere/sdk/constants/PermissionConstants.java +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/constants/PermissionConstants.java @@ -203,6 +203,7 @@ public class PermissionConstants { public static final String PROJECT_CUSTOM_FUNCTION_ADD = "PROJECT_CUSTOM_FUNCTION:READ+ADD"; public static final String PROJECT_CUSTOM_FUNCTION_UPDATE = "PROJECT_CUSTOM_FUNCTION:READ+UPDATE"; public static final String PROJECT_CUSTOM_FUNCTION_DELETE = "PROJECT_CUSTOM_FUNCTION:READ+DELETE"; + public static final String PROJECT_CUSTOM_FUNCTION_EXECUTE = "PROJECT_CUSTOM_FUNCTION:READ+EXECUTE"; /*------ end: PROJECT_CUSTOM_FUNCTION ------*/ /*------ start: PROJECT_TEMPLATE ------*/ diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/task/TaskRequestDTO.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/task/TaskRequestDTO.java index eefa7b5739..7adbb72657 100644 --- a/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/task/TaskRequestDTO.java +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/task/TaskRequestDTO.java @@ -39,7 +39,7 @@ public class TaskRequestDTO implements Serializable { /** * 资源类型 - * ApiResourceType + * @see io.metersphere.sdk.constants.ApiExecuteResourceType */ private String resourceType; diff --git a/backend/framework/sdk/src/main/resources/i18n/project.properties b/backend/framework/sdk/src/main/resources/i18n/project.properties index a416da056f..de667b38f6 100644 --- a/backend/framework/sdk/src/main/resources/i18n/project.properties +++ b/backend/framework/sdk/src/main/resources/i18n/project.properties @@ -475,4 +475,5 @@ env_info_all=环境信息(总).json # custom_function custom_function_already_exist= 脚本名称已存在 - +permission.project_custom_function.name=公共脚本 +permission.project_custom_function.execute=测试 \ No newline at end of file diff --git a/backend/framework/sdk/src/main/resources/i18n/project_en_US.properties b/backend/framework/sdk/src/main/resources/i18n/project_en_US.properties index 98e3749ff2..e9196e8b45 100644 --- a/backend/framework/sdk/src/main/resources/i18n/project_en_US.properties +++ b/backend/framework/sdk/src/main/resources/i18n/project_en_US.properties @@ -513,3 +513,5 @@ env_info_all=All environment info.json # custom_function custom_function_already_exist= custom function name already exist +permission.project_custom_function.name=Common script +permission.project_custom_function.execute=Test \ No newline at end of file diff --git a/backend/framework/sdk/src/main/resources/i18n/project_zh_CN.properties b/backend/framework/sdk/src/main/resources/i18n/project_zh_CN.properties index 72eb47f37d..b81a64c834 100644 --- a/backend/framework/sdk/src/main/resources/i18n/project_zh_CN.properties +++ b/backend/framework/sdk/src/main/resources/i18n/project_zh_CN.properties @@ -512,3 +512,5 @@ env_info_all=环境信息(总).json # custom_function custom_function_already_exist= 脚本名称已存在 +permission.project_custom_function.name=公共脚本 +permission.project_custom_function.execute=测试 \ No newline at end of file diff --git a/backend/framework/sdk/src/main/resources/i18n/project_zh_TW.properties b/backend/framework/sdk/src/main/resources/i18n/project_zh_TW.properties index 7566d88424..e15c646e3a 100644 --- a/backend/framework/sdk/src/main/resources/i18n/project_zh_TW.properties +++ b/backend/framework/sdk/src/main/resources/i18n/project_zh_TW.properties @@ -513,3 +513,5 @@ env_info_all=環境信息(总).json # custom_function custom_function_already_exist= 腳本名稱已存在 +permission.project_custom_function.name=公共腳本 +permission.project_custom_function.execute=測試 \ No newline at end of file diff --git a/backend/framework/sdk/src/main/resources/i18n/system_zh_CN.properties b/backend/framework/sdk/src/main/resources/i18n/system_zh_CN.properties index ba7b82f5af..7a4b72b91a 100644 --- a/backend/framework/sdk/src/main/resources/i18n/system_zh_CN.properties +++ b/backend/framework/sdk/src/main/resources/i18n/system_zh_CN.properties @@ -309,4 +309,4 @@ operation_history.type.not_blank=变更记录操作类型不能为空 operation_history.type.length_range=变更记录操作类型长度必须在{min}和{max}之间 operation_history.source_id.not_blank=变更记录资源 ID 不能为空 operation_history.version_id.not_blank=变更记录版本 ID 不能为空 -operation_history.version_id.length_range=变更记录版本 ID 长度必须在{min}和{max}之间 \ No newline at end of file +operation_history.version_id.length_range=变更记录版本 ID 长度必须在{min}和{max}之间 diff --git a/backend/services/api-test/pom.xml b/backend/services/api-test/pom.xml index 922129290b..4af1e0e26e 100644 --- a/backend/services/api-test/pom.xml +++ b/backend/services/api-test/pom.xml @@ -57,6 +57,11 @@ ApacheJMeter_http ${jmeter.version} + + org.apache.jmeter + ApacheJMeter_java + ${jmeter.version} + io.metersphere diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/controller/ApiTestController.java b/backend/services/api-test/src/main/java/io/metersphere/api/controller/ApiTestController.java index f45be2bdff..c9399dba26 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/controller/ApiTestController.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/controller/ApiTestController.java @@ -1,15 +1,17 @@ package io.metersphere.api.controller; +import io.metersphere.api.service.ApiExecuteService; import io.metersphere.api.service.ApiTestService; import io.metersphere.jmeter.mock.Mock; +import io.metersphere.project.dto.customfunction.request.CustomFunctionRunRequest; +import io.metersphere.sdk.constants.PermissionConstants; import io.metersphere.system.dto.ProtocolDTO; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; -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 org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; import java.util.List; @@ -24,6 +26,8 @@ public class ApiTestController { @Resource private ApiTestService apiTestService; + @Resource + private ApiExecuteService apiExecuteService; @GetMapping("/protocol/{organizationId}") @Operation(summary = "获取协议插件的的协议列表") @@ -37,4 +41,11 @@ public class ApiTestController { public String mock(@PathVariable String key) { return Mock.calculate(key).toString(); } + + @PostMapping("/custom/func/run") + @Operation(summary = "项目管理-公共脚本-脚本测试") + @RequiresPermissions(PermissionConstants.PROJECT_CUSTOM_FUNCTION_EXECUTE) + public String run(@Validated @RequestBody CustomFunctionRunRequest runRequest) { + return apiExecuteService.runScript(runRequest); + } } diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/dto/NodeDTO.java b/backend/services/api-test/src/main/java/io/metersphere/api/dto/NodeDTO.java deleted file mode 100644 index 808f48d1a9..0000000000 --- a/backend/services/api-test/src/main/java/io/metersphere/api/dto/NodeDTO.java +++ /dev/null @@ -1,31 +0,0 @@ -package io.metersphere.api.dto; - -import lombok.AllArgsConstructor; -import lombok.Data; - -import java.io.Serial; -import java.io.Serializable; - -@Data -@AllArgsConstructor -public class NodeDTO implements Serializable { - - @Serial - private static final long serialVersionUID = 1L; - - /** - * 接口测试 性能测试 node节点ip - */ - private String ip; - - /** - * 接口测试 性能测试 node节点端口 - */ - private String port; - - /** - * 资源池最大并发数 - */ - private int podThreads; - -} diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/dto/request/controller/MsCommentScriptElement.java b/backend/services/api-test/src/main/java/io/metersphere/api/dto/request/controller/MsCommentScriptElement.java new file mode 100644 index 0000000000..f67086ad75 --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/dto/request/controller/MsCommentScriptElement.java @@ -0,0 +1,30 @@ +package io.metersphere.api.dto.request.controller; + +import io.metersphere.api.dto.request.processors.ScriptProcessor; +import io.metersphere.plugin.api.spi.AbstractMsTestElement; +import io.metersphere.project.dto.environment.KeyValueParam; +import lombok.Data; + +import java.util.List; + + +/** + * 公共脚本组件 + * 主要用于公共脚本测试执行时,生成jmx + */ +@Data +public class MsCommentScriptElement extends AbstractMsTestElement { + /** + * 脚本内容 + */ + private String script; + /** + * 脚本语言 + * @see ScriptProcessor.ScriptLanguageType + */ + private String scriptLanguage; + /** + * 公共脚本入参 + */ + private List params; +} \ No newline at end of file diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/dto/request/processors/ScriptProcessor.java b/backend/services/api-test/src/main/java/io/metersphere/api/dto/request/processors/ScriptProcessor.java index b0349c7c13..7807899d82 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/dto/request/processors/ScriptProcessor.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/dto/request/processors/ScriptProcessor.java @@ -2,6 +2,7 @@ package io.metersphere.api.dto.request.processors; import com.fasterxml.jackson.annotation.JsonTypeName; import io.metersphere.api.dto.request.http.KeyValueParam; +import io.metersphere.project.constants.ScriptLanguageType; import lombok.Data; import java.util.List; @@ -34,22 +35,4 @@ public class ScriptProcessor extends MsProcessor { * 公共脚本入参 */ private List params; - - public enum ScriptLanguageType { - BEANSHELL("beanshell"), - BEANSHELL_JSR233("beanshell-JSR233"), - GROOVY("groovy"), - JAVASCRIPT("javascript"), - PYTHON("python"); - - private String value; - - ScriptLanguageType(String value) { - this.value = value; - } - - public String getValue() { - return value; - } - } } diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/MsCommentScriptElementConverter.java b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/MsCommentScriptElementConverter.java new file mode 100644 index 0000000000..69438165fd --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/MsCommentScriptElementConverter.java @@ -0,0 +1,82 @@ +package io.metersphere.api.parser.jmeter; + +import io.metersphere.api.dto.request.controller.MsCommentScriptElement; +import io.metersphere.api.dto.request.processors.ScriptProcessor; +import io.metersphere.api.parser.jmeter.processor.ScriptProcessorConverter; +import io.metersphere.plugin.api.dto.ParameterConfig; +import io.metersphere.plugin.api.spi.AbstractJmeterElementConverter; +import io.metersphere.project.dto.environment.KeyValueParam; +import org.apache.commons.collections.CollectionUtils; +import org.apache.jmeter.extractor.BeanShellPostProcessor; +import org.apache.jmeter.extractor.JSR223PostProcessor; +import org.apache.jmeter.modifiers.UserParameters; +import org.apache.jmeter.save.SaveService; +import org.apache.jmeter.testelement.TestElement; +import org.apache.jorphan.collections.HashTree; + +import java.util.LinkedList; +import java.util.List; +import java.util.stream.Collectors; + +import static io.metersphere.api.parser.jmeter.constants.JmeterAlias.USER_PARAMETERS_GUI; + +/** + * @Author: jianxing + * @CreateTime: 2024-01-18 22:04 + */ +public class MsCommentScriptElementConverter extends AbstractJmeterElementConverter { + + @Override + public void toHashTree(HashTree hashTree, MsCommentScriptElement msElement, ParameterConfig config) { + + if (CollectionUtils.isNotEmpty(msElement.getParams())) { + // 添加变量 + List params = msElement.getParams() + .stream() + .filter(KeyValueParam::isValid) + .collect(Collectors.toList()); + + if (CollectionUtils.isNotEmpty(params)) { + UserParameters userParameters = getUserParameters(params); + hashTree.add(userParameters); + } + } + + // 添加脚本 + ScriptProcessor scriptProcessor = new ScriptProcessor(); + scriptProcessor.setScriptLanguage(msElement.getScriptLanguage()); + scriptProcessor.setScript(msElement.getScript()); + TestElement scriptElement; + if (ScriptProcessorConverter.isJSR233(scriptProcessor)) { + scriptElement = new JSR223PostProcessor(); + } else { + scriptElement = new BeanShellPostProcessor(); + } + ScriptProcessorConverter.parse(scriptElement, scriptProcessor); + hashTree.add(scriptElement); + } + + public static UserParameters getUserParameters(List params) { + UserParameters processor = new UserParameters(); + processor.setEnabled(true); + processor.setName("User Defined Variables"); + processor.setPerIteration(true); + processor.setProperty(TestElement.TEST_CLASS, UserParameters.class.getName()); + processor.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass(USER_PARAMETERS_GUI)); + if (CollectionUtils.isNotEmpty(params)) { + List names = new LinkedList<>(); + List values = new LinkedList<>(); + List threadValues = new LinkedList<>(); + for (KeyValueParam param : params) { + String name = param.getKey(); + String value = param.getValue(); + names.add(name); + values.add(value); + } + processor.setNames(names); + threadValues.add(values); + processor.setThreadLists(threadValues); + } + return processor; + } +} 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 be5e680d3c..efa1357bb8 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 @@ -16,4 +16,5 @@ public class JmeterAlias { public static final String HTTP_TEST_SAMPLE_GUI = "HttpTestSampleGui"; 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"; } diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/processor/ScriptFilter.java b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/processor/ScriptFilter.java index 99a05cff4b..a340a1e27c 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/processor/ScriptFilter.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/processor/ScriptFilter.java @@ -1,6 +1,6 @@ package io.metersphere.api.parser.jmeter.processor; -import io.metersphere.api.dto.request.processors.ScriptProcessor; +import io.metersphere.project.constants.ScriptLanguageType; import io.metersphere.sdk.exception.MSException; import io.metersphere.sdk.util.LogUtils; import org.apache.commons.collections4.CollectionUtils; @@ -57,10 +57,10 @@ public class ScriptFilter { public static void verify(String language, String label, String script) { // 默认 groovy - ScriptProcessor.ScriptLanguageType scriptLanguageType = Arrays.stream(ScriptProcessor.ScriptLanguageType.values()) + ScriptLanguageType scriptLanguageType = Arrays.stream(ScriptLanguageType.values()) .filter(item -> StringUtils.equals(item.getValue(), language)) .findFirst() - .orElse(ScriptProcessor.ScriptLanguageType.GROOVY); + .orElse(ScriptLanguageType.GROOVY); if (StringUtils.isNotEmpty(script)) { final StringBuffer buffer = new StringBuffer(); diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/processor/ScriptProcessorConverter.java b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/processor/ScriptProcessorConverter.java index 97e00f257a..6387378006 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/processor/ScriptProcessorConverter.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/processor/ScriptProcessorConverter.java @@ -1,5 +1,6 @@ package io.metersphere.api.parser.jmeter.processor; +import io.metersphere.project.constants.ScriptLanguageType; import io.metersphere.api.dto.request.processors.ScriptProcessor; import io.metersphere.api.parser.jmeter.constants.JmeterAlias; import io.metersphere.api.parser.jmeter.constants.JmeterProperty; @@ -36,7 +37,7 @@ public abstract class ScriptProcessorConverter extends MsProcessorConverter nodesList = resourcePoolDTO.getNodesList(); - int index = new SecureRandom().nextInt(nodesList.size()); - TestResourceNodeDTO testResourceNodeDTO = nodesList.get(index); - String endpoint = TaskRunnerClient.getEndpoint(testResourceNodeDTO.getIp(), testResourceNodeDTO.getPort()); try { + String endpoint = TaskRunnerClient.getEndpoint(testResourceNodeDTO.getIp(), testResourceNodeDTO.getPort()); LogUtils.info(String.format("开始发送请求【 %s 】到 %s 节点执行", testId, endpoint), reportId); TaskRunnerClient.debugApi(endpoint, taskRequest); } catch (Exception e) { @@ -147,6 +167,57 @@ public class ApiExecuteService { } } + private TestResourceNodeDTO getProjectExecuteNode(String projectId) { + String resourcePoolId = getProjectApiResourcePoolId(projectId); + TestResourceDTO resourcePoolDTO = getAvailableResourcePoolDTO(projectId, resourcePoolId); + roundRobinService.initializeNodes(resourcePoolId, resourcePoolDTO.getNodesList()); + try { + return roundRobinService.getNextNode(resourcePoolId); + } catch (Exception e) { + LogUtils.error(e); + throw new MSException("get execute node error", e); + } + } + + /** + * 设置minio kafka ms 等信息 + * + * @param taskRequest + */ + private void setServerInfoParam(TaskRequestDTO taskRequest) { + taskRequest.setKafkaConfig(EncryptUtils.aesEncrypt(JSON.toJSONString(KafkaConfig.getKafkaConfig()))); + taskRequest.setMinioConfig(EncryptUtils.aesEncrypt(JSON.toJSONString(getMinio()))); + taskRequest.setMsUrl(systemParameterService.getBaseInfo().getUrl()); + } + + /** + * 公共脚本执行 + * @param runRequest + * @return + */ + public String runScript(CustomFunctionRunRequest runRequest) { + String reportId = IDGenerator.nextStr(); + String testId = runRequest.getProjectId(); + // 生成执行脚本 + MsCommentScriptElement msCommentScriptElement = BeanUtils.copyBean(new MsCommentScriptElement(), runRequest); + String executeScript = parseExecuteScript(msCommentScriptElement, new ParameterConfig()); + // 设置执行参数 + TaskRequestDTO taskRequest = new TaskRequestDTO(); + setServerInfoParam(taskRequest); + taskRequest.setRealTime(true); + taskRequest.setReportId(reportId); + taskRequest.setResourceId(testId); + taskRequest.setResourceType(ApiExecuteResourceType.API_DEBUG.name()); + ApiRunModeConfigDTO apiRunModeConfig = new ApiRunModeConfigDTO(); + apiRunModeConfig.setRunMode(ApiExecuteRunMode.BACKEND_DEBUG.name()); + taskRequest.setRunModeConfig(apiRunModeConfig); + + TestResourceNodeDTO testResourceNodeDTO = getProjectExecuteNode(runRequest.getProjectId()); + + doDebug(reportId, testId, taskRequest, executeScript, testResourceNodeDTO); + return reportId; + } + /** * 给 taskRequest 设置文件相关参数 * @@ -236,14 +307,18 @@ public class ApiExecuteService { * 生成执行脚本 * * @param testElementStr - * @param msParameter + * @param config * @return */ - private static String parseExecuteScript(String testElementStr, ParameterConfig msParameter) { + private static String parseExecuteScript(String testElementStr, ParameterConfig config) { + // 解析生成脚本 + return parseExecuteScript(ApiDataUtils.parseObject(testElementStr, AbstractMsTestElement.class), config); + } + + private static String parseExecuteScript(AbstractMsTestElement msTestElement, ParameterConfig config) { // 解析生成脚本 TestElementParser defaultParser = TestElementParserFactory.getDefaultParser(); - AbstractMsTestElement msTestElement = ApiDataUtils.parseObject(testElementStr, AbstractMsTestElement.class); - return defaultParser.parse(msTestElement, msParameter); + return defaultParser.parse(msTestElement, config); } @@ -262,14 +337,7 @@ public class ApiExecuteService { * @param projectId * @param */ - public TestResourceDTO getAvailableResourcePoolDTO(String projectId) { - // 查询接口默认资源池 - ProjectApplication resourcePoolConfig = projectApplicationService.getByType(projectId, ProjectApplicationType.API.API_RESOURCE_POOL_ID.name()); - // 没有配置接口默认资源池 - if (resourcePoolConfig == null || StringUtils.isBlank(resourcePoolConfig.getTypeValue())) { - throw new MSException(ApiResultCode.EXECUTE_RESOURCE_POOL_NOT_CONFIG); - } - String resourcePoolId = StringUtils.isBlank(resourcePoolConfig.getTypeValue()) ? null : resourcePoolConfig.getTypeValue(); + public TestResourceDTO getAvailableResourcePoolDTO(String projectId, String resourcePoolId) { TestResourcePool testResourcePool = testResourcePoolService.getTestResourcePool(resourcePoolId); if (testResourcePool == null || // 资源池禁用 @@ -280,4 +348,15 @@ public class ApiExecuteService { } return testResourcePoolService.getTestResourceDTO(resourcePoolId); } + + private String getProjectApiResourcePoolId(String projectId) { + // 查询接口默认资源池 + ProjectApplication resourcePoolConfig = projectApplicationService.getByType(projectId, ProjectApplicationType.API.API_RESOURCE_POOL_ID.name()); + // 没有配置接口默认资源池 + if (resourcePoolConfig == null || StringUtils.isBlank(resourcePoolConfig.getTypeValue())) { + throw new MSException(ApiResultCode.EXECUTE_RESOURCE_POOL_NOT_CONFIG); + } + String resourcePoolId = StringUtils.isBlank(resourcePoolConfig.getTypeValue()) ? null : resourcePoolConfig.getTypeValue(); + return resourcePoolId; + } } \ No newline at end of file diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/RoundRobinService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/RoundRobinService.java index b4d412af47..79d0a91f11 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/service/RoundRobinService.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/RoundRobinService.java @@ -1,7 +1,7 @@ package io.metersphere.api.service; -import io.metersphere.api.dto.NodeDTO; import io.metersphere.sdk.util.JSON; +import io.metersphere.system.dto.pool.TestResourceNodeDTO; import jakarta.annotation.Resource; import org.apache.commons.lang3.StringUtils; import org.springframework.data.redis.core.RedisTemplate; @@ -17,7 +17,7 @@ public class RoundRobinService { /** * 获取下一个节点 */ - public String getNextNode(String poolId) throws Exception { + public TestResourceNodeDTO getNextNode(String poolId) throws Exception { // 从列表头部获取下一个节点 String node = redisTemplate.opsForList().leftPop(poolId); @@ -35,22 +35,24 @@ public class RoundRobinService { if (StringUtils.isNotBlank(node)) { // 将节点重新放回列表尾部,实现轮询 redisTemplate.opsForList().rightPush(poolId, node); + } else { + return null; } - return node; + return JSON.parseObject(node, TestResourceNodeDTO.class); } /** * 初始化节点列表 */ - public void initializeNodes(String poolId, List nodes) { + public void initializeNodes(String poolId, List nodes) { // 检查节点是否有变更 Long poolSize = redisTemplate.opsForList().size(poolId); int size = poolSize != null ? poolSize.intValue() : 0; if (size == nodes.size()) { // 对比redis中的节点列表和传入的节点列表是否一致 boolean isSame = true; - for (NodeDTO node : nodes) { + for (TestResourceNodeDTO node : nodes) { boolean isExist = false; for (int i = 0; i < size; i++) { if (JSON.toJSONString(node).equals(redisTemplate.opsForList().index(poolId, i))) { diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/utils/JmeterElementConverterRegister.java b/backend/services/api-test/src/main/java/io/metersphere/api/utils/JmeterElementConverterRegister.java index 8dd611331e..4b844f8a5d 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/utils/JmeterElementConverterRegister.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/utils/JmeterElementConverterRegister.java @@ -1,5 +1,6 @@ package io.metersphere.api.utils; +import io.metersphere.api.parser.jmeter.MsCommentScriptElementConverter; import io.metersphere.api.parser.jmeter.MsCommonElementConverter; import io.metersphere.api.parser.jmeter.MsHTTPElementConverter; import io.metersphere.plugin.api.spi.AbstractJmeterElementConverter; @@ -28,6 +29,7 @@ public class JmeterElementConverterRegister { // 注册默认的转换器 todo 注册插件的转换器 register(MsHTTPElementConverter.class); register(MsCommonElementConverter.class); + register(MsCommentScriptElementConverter.class); } /** diff --git a/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiDebugControllerTests.java b/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiDebugControllerTests.java index dfc53bbc34..17d8970bc9 100644 --- a/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiDebugControllerTests.java +++ b/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiDebugControllerTests.java @@ -16,22 +16,16 @@ import io.metersphere.api.parser.ImportParserFactory; import io.metersphere.api.parser.TestElementParserFactory; import io.metersphere.api.parser.jmeter.MsScenarioConverter; import io.metersphere.api.service.ApiFileResourceService; +import io.metersphere.api.service.BaseResourcePoolTestService; import io.metersphere.api.utils.ApiDataUtils; import io.metersphere.plugin.api.dto.ParameterConfig; import io.metersphere.plugin.api.spi.AbstractMsTestElement; -import io.metersphere.project.domain.ProjectApplication; -import io.metersphere.project.domain.ProjectTestResourcePool; -import io.metersphere.project.domain.ProjectTestResourcePoolExample; import io.metersphere.project.dto.filemanagement.FileInfo; import io.metersphere.project.dto.filemanagement.request.FileUploadRequest; -import io.metersphere.project.mapper.ProjectApplicationMapper; -import io.metersphere.project.mapper.ProjectTestResourcePoolMapper; import io.metersphere.project.service.FileAssociationService; import io.metersphere.project.service.FileMetadataService; import io.metersphere.sdk.constants.DefaultRepositoryDir; import io.metersphere.sdk.constants.PermissionConstants; -import io.metersphere.sdk.constants.ProjectApplicationType; -import io.metersphere.sdk.constants.ResourcePoolTypeEnum; import io.metersphere.sdk.file.FileCenter; import io.metersphere.sdk.file.FileRequest; import io.metersphere.sdk.util.BeanUtils; @@ -40,28 +34,16 @@ import io.metersphere.sdk.util.JSON; import io.metersphere.system.base.BaseTest; import io.metersphere.system.controller.handler.ResultHolder; import io.metersphere.system.domain.TestResourcePool; -import io.metersphere.system.domain.TestResourcePoolBlob; -import io.metersphere.system.domain.TestResourcePoolOrganization; -import io.metersphere.system.domain.TestResourcePoolOrganizationExample; -import io.metersphere.system.dto.pool.TestResourceDTO; -import io.metersphere.system.dto.pool.TestResourceNodeDTO; -import io.metersphere.system.dto.pool.TestResourcePoolDTO; import io.metersphere.system.log.constants.OperationLogType; -import io.metersphere.system.mapper.TestResourcePoolBlobMapper; -import io.metersphere.system.mapper.TestResourcePoolMapper; -import io.metersphere.system.mapper.TestResourcePoolOrganizationMapper; -import io.metersphere.system.uid.IDGenerator; import jakarta.annotation.Resource; import org.apache.commons.collections.CollectionUtils; import org.apache.jorphan.collections.ListedHashTree; import org.junit.jupiter.api.*; -import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.MediaType; import org.springframework.mock.web.MockMultipartFile; import org.springframework.test.web.servlet.MvcResult; -import org.testcontainers.shaded.org.apache.commons.lang3.StringUtils; import java.util.LinkedList; import java.util.List; @@ -92,19 +74,7 @@ public class ApiDebugControllerTests extends BaseTest { @Resource private FileMetadataService fileMetadataService; @Resource - private ProjectApplicationMapper projectApplicationMapper; - @Resource - private TestResourcePoolOrganizationMapper testResourcePoolOrganizationMapper; - @Resource - private ProjectTestResourcePoolMapper projectTestResourcePoolMapper; - @Resource - private TestResourcePoolMapper testResourcePoolMapper; - @Resource - private TestResourcePoolBlobMapper testResourcePoolBlobMapper; - @Value("${embedded.mockserver.host}") - private String mockServerHost; - @Value("${embedded.mockserver.port}") - private int mockServerHostPort; + private BaseResourcePoolTestService baseResourcePoolTestService; private static ApiDebug addApiDebug; private static ApiDebug anotherAddApiDebug; private static String fileMetadataId; @@ -397,13 +367,13 @@ public class ApiDebugControllerTests extends BaseTest { // @校验组织没有资源池权限异常 assertErrorCode(this.requestPost(DEBUG, request), ApiResultCode.EXECUTE_RESOURCE_POOL_NOT_CONFIG); - TestResourcePool resourcePool = insertResourcePool(); - insertResourcePoolOrg(resourcePool); + TestResourcePool resourcePool = baseResourcePoolTestService.insertResourcePool(); + baseResourcePoolTestService.insertResourcePoolOrg(resourcePool); // @校验项目没有资源池权限异常 assertErrorCode(this.requestPost(DEBUG, request), ApiResultCode.EXECUTE_RESOURCE_POOL_NOT_CONFIG); - insertResourcePoolProject(resourcePool); - insertProjectApplication(resourcePool); + baseResourcePoolTestService.insertResourcePoolProject(resourcePool); + baseResourcePoolTestService.insertProjectApplication(resourcePool); // @校验资源池调用失败 assertErrorCode(this.requestPost(DEBUG, request), ApiResultCode.RESOURCE_POOL_EXECUTE_ERROR); @@ -485,71 +455,6 @@ public class ApiDebugControllerTests extends BaseTest { this.requestPostWithOk(DEBUG, request); } - private TestResourcePool insertResourcePool() { - String id = IDGenerator.nextStr(); - TestResourcePoolDTO testResourcePool = new TestResourcePoolDTO(); - testResourcePool.setId(id); - testResourcePool.setApiTest(true); - testResourcePool.setCreateTime(System.currentTimeMillis()); - testResourcePool.setUpdateTime(System.currentTimeMillis()); - testResourcePool.setDeleted(false); - testResourcePool.setName("api debug test"); - testResourcePool.setCreateUser("admin"); - testResourcePool.setAllOrg(false); - testResourcePool.setEnable(true); - testResourcePool.setType(ResourcePoolTypeEnum.NODE.name()); - TestResourcePoolBlob testResourcePoolBlob = new TestResourcePoolBlob(); - testResourcePoolBlob.setId(id); - TestResourceDTO testResourceDTO = new TestResourceDTO(); - TestResourceNodeDTO testResourceNodeDTO = new TestResourceNodeDTO(); - testResourceNodeDTO.setIp(mockServerHost); - testResourceNodeDTO.setPort(mockServerHostPort + StringUtils.EMPTY); - testResourceNodeDTO.setConcurrentNumber(10); - testResourceDTO.setNodesList(List.of(testResourceNodeDTO)); - String configuration = JSON.toJSONString(testResourceDTO); - testResourcePoolBlob.setConfiguration(configuration.getBytes()); - testResourcePool.setTestResourceDTO(testResourceDTO); - - testResourcePoolMapper.insert(testResourcePool); - testResourcePoolBlobMapper.insert(testResourcePoolBlob); - return testResourcePool; - } - - private void insertProjectApplication(TestResourcePool resourcePool) { - ProjectApplication projectApplication = new ProjectApplication(); - projectApplication.setTypeValue(resourcePool.getId()); - projectApplication.setType(ProjectApplicationType.API.API_RESOURCE_POOL_ID.name()); - projectApplication.setProjectId(DEFAULT_PROJECT_ID); - projectApplicationMapper.insert(projectApplication); - } - - private void insertResourcePoolProject(TestResourcePool resourcePool) { - ProjectTestResourcePoolExample example = new ProjectTestResourcePoolExample(); - example.createCriteria().andProjectIdEqualTo(DEFAULT_PROJECT_ID); - example.createCriteria().andTestResourcePoolIdEqualTo(resourcePool.getId()); - List projectTestResourcePools = projectTestResourcePoolMapper.selectByExample(example); - if (CollectionUtils.isEmpty(projectTestResourcePools)) { - ProjectTestResourcePool projectTestResourcePool = new ProjectTestResourcePool(); - projectTestResourcePool.setTestResourcePoolId(resourcePool.getId()); - projectTestResourcePool.setProjectId(DEFAULT_PROJECT_ID); - projectTestResourcePoolMapper.insert(projectTestResourcePool); - } - } - - private void insertResourcePoolOrg(TestResourcePool resourcePool) { - TestResourcePoolOrganizationExample example = new TestResourcePoolOrganizationExample(); - example.createCriteria().andOrgIdEqualTo(DEFAULT_ORGANIZATION_ID); - example.createCriteria().andTestResourcePoolIdEqualTo(resourcePool.getId()); - List testResourcePoolOrganizations = testResourcePoolOrganizationMapper.selectByExample(example); - if (CollectionUtils.isEmpty(testResourcePoolOrganizations)) { - TestResourcePoolOrganization resourcePoolOrganization = new TestResourcePoolOrganization(); - resourcePoolOrganization.setTestResourcePoolId(resourcePool.getId()); - resourcePoolOrganization.setOrgId(DEFAULT_ORGANIZATION_ID); - resourcePoolOrganization.setId(IDGenerator.nextStr()); - testResourcePoolOrganizationMapper.insert(resourcePoolOrganization); - } - } - @Test @Order(7) public void delete() throws Exception { @@ -568,4 +473,5 @@ public class ApiDebugControllerTests extends BaseTest { // @@校验权限 requestGetPermissionTest(PermissionConstants.PROJECT_API_DEBUG_DELETE, DEFAULT_DELETE, addApiDebug.getId()); } + } \ No newline at end of file diff --git a/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiTestControllerTests.java b/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiTestControllerTests.java index ebcb5dfc72..98fcd39ec5 100644 --- a/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiTestControllerTests.java +++ b/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiTestControllerTests.java @@ -1,12 +1,20 @@ package io.metersphere.api.controller; +import io.metersphere.api.service.BaseResourcePoolTestService; +import io.metersphere.project.constants.ScriptLanguageType; +import io.metersphere.project.dto.customfunction.request.CustomFunctionRunRequest; +import io.metersphere.project.dto.environment.KeyValueParam; +import io.metersphere.sdk.constants.PermissionConstants; import io.metersphere.system.base.BaseTest; +import jakarta.annotation.Resource; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; +import java.util.List; + /** * @Author: jianxing * @CreateTime: 2023-11-07 17:07 @@ -19,7 +27,10 @@ public class ApiTestControllerTests extends BaseTest { private static final String BASE_PATH = "/api/test/"; protected static final String PROTOCOL_LIST = "protocol/{0}"; protected static final String MOCK = "mock/{0}"; + protected static final String CUSTOM_FUNC_RUN = "custom/func/run"; + @Resource + private BaseResourcePoolTestService baseResourcePoolTestService; @Override protected String getBasePath() { return BASE_PATH; @@ -36,4 +47,30 @@ public class ApiTestControllerTests extends BaseTest { // @@请求成功 this.requestGetWithOk(MOCK, "@integer").andReturn(); } + + @Test + public void runCustomFunc() throws Exception { + mockPost("/api/debug", ""); + // 初始化资源池 + baseResourcePoolTestService.initProjectResourcePool(); + + CustomFunctionRunRequest request = new CustomFunctionRunRequest(); + request.setProjectId(DEFAULT_PROJECT_ID); + request.setType(ScriptLanguageType.BEANSHELL.getValue()); + request.setScript(""" + log.info("========"); + log.info("${test}"); + """); + // @@请求成功 + this.requestPostWithOk(CUSTOM_FUNC_RUN, request); + + KeyValueParam keyValueParam = new KeyValueParam(); + keyValueParam.setKey("test"); + keyValueParam.setValue("value"); + request.setParams(List.of(keyValueParam)); + // @@请求成功 + this.requestPostWithOk(CUSTOM_FUNC_RUN, request); + // @@校验权限 + requestPostPermissionTest(PermissionConstants.PROJECT_CUSTOM_FUNCTION_EXECUTE, CUSTOM_FUNC_RUN, request); + } } diff --git a/backend/services/api-test/src/test/java/io/metersphere/api/controller/MsHTTPElementTest.java b/backend/services/api-test/src/test/java/io/metersphere/api/controller/MsHTTPElementTest.java index 55bccadc45..e00a30ec0e 100644 --- a/backend/services/api-test/src/test/java/io/metersphere/api/controller/MsHTTPElementTest.java +++ b/backend/services/api-test/src/test/java/io/metersphere/api/controller/MsHTTPElementTest.java @@ -22,6 +22,7 @@ import io.metersphere.api.parser.TestElementParserFactory; import io.metersphere.api.utils.ApiDataUtils; import io.metersphere.plugin.api.dto.ParameterConfig; import io.metersphere.plugin.api.spi.AbstractMsTestElement; +import io.metersphere.project.constants.ScriptLanguageType; import io.metersphere.sdk.constants.MsAssertionCondition; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -135,13 +136,13 @@ public class MsHTTPElementTest { ScriptProcessor scriptProcessor = new ScriptProcessor(); scriptProcessor.setEnable(true); scriptProcessor.setScript("script"); - scriptProcessor.setScriptLanguage(ScriptProcessor.ScriptLanguageType.JAVASCRIPT.getValue()); + scriptProcessor.setScriptLanguage(ScriptLanguageType.JAVASCRIPT.getValue()); processors.add(scriptProcessor); ScriptProcessor beanShellScriptProcessor = new ScriptProcessor(); beanShellScriptProcessor.setEnable(true); beanShellScriptProcessor.setScript("script"); - beanShellScriptProcessor.setScriptLanguage(ScriptProcessor.ScriptLanguageType.BEANSHELL.getValue()); + beanShellScriptProcessor.setScriptLanguage(ScriptLanguageType.BEANSHELL.getValue()); processors.add(beanShellScriptProcessor); SQLProcessor sqlProcessor = new SQLProcessor(); diff --git a/backend/services/api-test/src/test/java/io/metersphere/api/service/BaseResourcePoolTestService.java b/backend/services/api-test/src/test/java/io/metersphere/api/service/BaseResourcePoolTestService.java new file mode 100644 index 0000000000..9399a20677 --- /dev/null +++ b/backend/services/api-test/src/test/java/io/metersphere/api/service/BaseResourcePoolTestService.java @@ -0,0 +1,131 @@ +package io.metersphere.api.service; + +import io.metersphere.project.domain.ProjectApplication; +import io.metersphere.project.domain.ProjectApplicationExample; +import io.metersphere.project.domain.ProjectTestResourcePool; +import io.metersphere.project.domain.ProjectTestResourcePoolExample; +import io.metersphere.project.mapper.ProjectApplicationMapper; +import io.metersphere.project.mapper.ProjectTestResourcePoolMapper; +import io.metersphere.sdk.constants.ProjectApplicationType; +import io.metersphere.sdk.constants.ResourcePoolTypeEnum; +import io.metersphere.sdk.util.JSON; +import io.metersphere.system.domain.TestResourcePool; +import io.metersphere.system.domain.TestResourcePoolBlob; +import io.metersphere.system.domain.TestResourcePoolOrganization; +import io.metersphere.system.domain.TestResourcePoolOrganizationExample; +import io.metersphere.system.dto.pool.TestResourceDTO; +import io.metersphere.system.dto.pool.TestResourceNodeDTO; +import io.metersphere.system.dto.pool.TestResourcePoolDTO; +import io.metersphere.system.mapper.TestResourcePoolBlobMapper; +import io.metersphere.system.mapper.TestResourcePoolMapper; +import io.metersphere.system.mapper.TestResourcePoolOrganizationMapper; +import io.metersphere.system.uid.IDGenerator; +import jakarta.annotation.Resource; +import org.apache.commons.collections.CollectionUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.testcontainers.shaded.org.apache.commons.lang3.StringUtils; + +import java.util.List; + +/** + * @Author: jianxing + * @CreateTime: 2023-10-20 11:32 + */ +@Service +public class BaseResourcePoolTestService { + @Resource + private ProjectApplicationMapper projectApplicationMapper; + @Resource + private TestResourcePoolOrganizationMapper testResourcePoolOrganizationMapper; + @Resource + private ProjectTestResourcePoolMapper projectTestResourcePoolMapper; + @Resource + private TestResourcePoolMapper testResourcePoolMapper; + @Resource + private TestResourcePoolBlobMapper testResourcePoolBlobMapper; + @Value("${embedded.mockserver.host}") + private String mockServerHost; + @Value("${embedded.mockserver.port}") + private int mockServerHostPort; + protected static final String DEFAULT_PROJECT_ID = "100001100001"; + protected static final String DEFAULT_ORGANIZATION_ID = "100001"; + + public void initProjectResourcePool() { + TestResourcePool resourcePool = insertResourcePool(); + insertResourcePoolOrg(resourcePool); + insertResourcePoolProject(resourcePool); + insertProjectApplication(resourcePool); + } + + public TestResourcePool insertResourcePool() { + String id = IDGenerator.nextStr(); + TestResourcePoolDTO testResourcePool = new TestResourcePoolDTO(); + testResourcePool.setId(id); + testResourcePool.setApiTest(true); + testResourcePool.setCreateTime(System.currentTimeMillis()); + testResourcePool.setUpdateTime(System.currentTimeMillis()); + testResourcePool.setDeleted(false); + testResourcePool.setName("api debug test"); + testResourcePool.setCreateUser("admin"); + testResourcePool.setAllOrg(false); + testResourcePool.setEnable(true); + testResourcePool.setType(ResourcePoolTypeEnum.NODE.name()); + TestResourcePoolBlob testResourcePoolBlob = new TestResourcePoolBlob(); + testResourcePoolBlob.setId(id); + TestResourceDTO testResourceDTO = new TestResourceDTO(); + TestResourceNodeDTO testResourceNodeDTO = new TestResourceNodeDTO(); + testResourceNodeDTO.setIp(mockServerHost); + testResourceNodeDTO.setPort(mockServerHostPort + StringUtils.EMPTY); + testResourceNodeDTO.setConcurrentNumber(10); + testResourceDTO.setNodesList(List.of(testResourceNodeDTO)); + String configuration = JSON.toJSONString(testResourceDTO); + testResourcePoolBlob.setConfiguration(configuration.getBytes()); + testResourcePool.setTestResourceDTO(testResourceDTO); + + testResourcePoolMapper.insert(testResourcePool); + testResourcePoolBlobMapper.insert(testResourcePoolBlob); + return testResourcePool; + } + + public synchronized void insertProjectApplication(TestResourcePool resourcePool) { + ProjectApplicationExample example = new ProjectApplicationExample(); + example.createCriteria().andProjectIdEqualTo(DEFAULT_PROJECT_ID) + .andTypeEqualTo(ProjectApplicationType.API.API_RESOURCE_POOL_ID.name()); + if (projectApplicationMapper.countByExample(example) > 0) { + return; + } + ProjectApplication projectApplication = new ProjectApplication(); + projectApplication.setTypeValue(resourcePool.getId()); + projectApplication.setType(ProjectApplicationType.API.API_RESOURCE_POOL_ID.name()); + projectApplication.setProjectId(DEFAULT_PROJECT_ID); + projectApplicationMapper.insert(projectApplication); + } + + public void insertResourcePoolProject(TestResourcePool resourcePool) { + ProjectTestResourcePoolExample example = new ProjectTestResourcePoolExample(); + example.createCriteria().andProjectIdEqualTo(DEFAULT_PROJECT_ID); + example.createCriteria().andTestResourcePoolIdEqualTo(resourcePool.getId()); + List projectTestResourcePools = projectTestResourcePoolMapper.selectByExample(example); + if (CollectionUtils.isEmpty(projectTestResourcePools)) { + ProjectTestResourcePool projectTestResourcePool = new ProjectTestResourcePool(); + projectTestResourcePool.setTestResourcePoolId(resourcePool.getId()); + projectTestResourcePool.setProjectId(DEFAULT_PROJECT_ID); + projectTestResourcePoolMapper.insert(projectTestResourcePool); + } + } + + public void insertResourcePoolOrg(TestResourcePool resourcePool) { + TestResourcePoolOrganizationExample example = new TestResourcePoolOrganizationExample(); + example.createCriteria().andOrgIdEqualTo(DEFAULT_ORGANIZATION_ID); + example.createCriteria().andTestResourcePoolIdEqualTo(resourcePool.getId()); + List testResourcePoolOrganizations = testResourcePoolOrganizationMapper.selectByExample(example); + if (CollectionUtils.isEmpty(testResourcePoolOrganizations)) { + TestResourcePoolOrganization resourcePoolOrganization = new TestResourcePoolOrganization(); + resourcePoolOrganization.setTestResourcePoolId(resourcePool.getId()); + resourcePoolOrganization.setOrgId(DEFAULT_ORGANIZATION_ID); + resourcePoolOrganization.setId(IDGenerator.nextStr()); + testResourcePoolOrganizationMapper.insert(resourcePoolOrganization); + } + } +} diff --git a/backend/services/api-test/src/test/java/io/metersphere/api/service/RoundRobinServiceTests.java b/backend/services/api-test/src/test/java/io/metersphere/api/service/RoundRobinServiceTests.java index 7a04b084df..97a0916e3f 100644 --- a/backend/services/api-test/src/test/java/io/metersphere/api/service/RoundRobinServiceTests.java +++ b/backend/services/api-test/src/test/java/io/metersphere/api/service/RoundRobinServiceTests.java @@ -1,7 +1,7 @@ package io.metersphere.api.service; -import io.metersphere.api.dto.NodeDTO; import io.metersphere.sdk.util.LogUtils; +import io.metersphere.system.dto.pool.TestResourceNodeDTO; import jakarta.annotation.Resource; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Order; @@ -24,11 +24,11 @@ public class RoundRobinServiceTests { @Test @Order(1) public void testInit() throws Exception { - List nodes = new LinkedList<>(); - nodes.add(new NodeDTO("172.0.0.1", "8080", 10)); - nodes.add(new NodeDTO("172.0.0.2", "8080", 10)); - nodes.add(new NodeDTO("172.0.0.3", "8080", 10)); - nodes.add(new NodeDTO("172.0.0.4", "8080", 10)); + List nodes = new LinkedList<>(); + nodes.add(new TestResourceNodeDTO("172.0.0.1", "8080", null, 10)); + nodes.add(new TestResourceNodeDTO("172.0.0.2", "8080", null, 10)); + nodes.add(new TestResourceNodeDTO("172.0.0.3", "8080", null, 10)); + nodes.add(new TestResourceNodeDTO("172.0.0.4", "8080", null, 10)); roundRobinService.initializeNodes("test", nodes); } @@ -48,15 +48,15 @@ public class RoundRobinServiceTests { @Test @Order(3) public void testInitAfter() throws Exception { - List nodes = new LinkedList<>(); - nodes.add(new NodeDTO("172.0.0.1", "8080", 10)); - nodes.add(new NodeDTO("172.0.0.2", "8080", 10)); - nodes.add(new NodeDTO("172.0.0.3", "8080", 10)); + List nodes = new LinkedList<>(); + nodes.add(new TestResourceNodeDTO("172.0.0.1", "8080", null, 10)); + nodes.add(new TestResourceNodeDTO("172.0.0.2", "8080", null, 10)); + nodes.add(new TestResourceNodeDTO("172.0.0.3", "8080", null, 10)); roundRobinService.initializeNodes("test", nodes); - nodes.add(new NodeDTO("172.0.0.3", "8080", 10)); - nodes.add(new NodeDTO("172.0.0.7", "8080", 10)); - nodes.add(new NodeDTO("172.0.0.6", "8080", 10)); + nodes.add(new TestResourceNodeDTO("172.0.0.3", "8080", null, 10)); + nodes.add(new TestResourceNodeDTO("172.0.0.7", "8080", null, 10)); + nodes.add(new TestResourceNodeDTO("172.0.0.6", "8080", null, 10)); roundRobinService.initializeNodes("test", nodes); } diff --git a/backend/services/project-management/src/main/java/io/metersphere/project/constants/ScriptLanguageType.java b/backend/services/project-management/src/main/java/io/metersphere/project/constants/ScriptLanguageType.java new file mode 100644 index 0000000000..2a127d869d --- /dev/null +++ b/backend/services/project-management/src/main/java/io/metersphere/project/constants/ScriptLanguageType.java @@ -0,0 +1,23 @@ +package io.metersphere.project.constants; + +/** + * @Author: jianxing + * @CreateTime: 2024-01-19 18:19 + */ +public enum ScriptLanguageType { + BEANSHELL("beanshell"), + BEANSHELL_JSR233("beanshell-jsr233"), + GROOVY("groovy"), + JAVASCRIPT("javascript"), + PYTHON("python"); + + private String value; + + ScriptLanguageType(String value) { + this.value = value; + } + + public String getValue() { + return value; + } +} diff --git a/backend/services/project-management/src/main/java/io/metersphere/project/controller/CustomFunctionController.java b/backend/services/project-management/src/main/java/io/metersphere/project/controller/CustomFunctionController.java index e685a1dd84..073075df9e 100644 --- a/backend/services/project-management/src/main/java/io/metersphere/project/controller/CustomFunctionController.java +++ b/backend/services/project-management/src/main/java/io/metersphere/project/controller/CustomFunctionController.java @@ -91,6 +91,4 @@ public class CustomFunctionController { public void delete(@PathVariable String id) { customFunctionService.delete(id); } - - } diff --git a/backend/services/project-management/src/main/java/io/metersphere/project/dto/customfunction/request/CustomFunctionRunRequest.java b/backend/services/project-management/src/main/java/io/metersphere/project/dto/customfunction/request/CustomFunctionRunRequest.java new file mode 100644 index 0000000000..343211bcbf --- /dev/null +++ b/backend/services/project-management/src/main/java/io/metersphere/project/dto/customfunction/request/CustomFunctionRunRequest.java @@ -0,0 +1,34 @@ +package io.metersphere.project.dto.customfunction.request; + +import io.metersphere.project.dto.environment.KeyValueParam; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +@Data +public class CustomFunctionRunRequest implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "脚本语言类型") + @NotBlank + @Size(max = 50) + private String type; + + @Schema(description = "参数列表") + private List params; + + @Schema(description = "函数体") + @NotBlank + private String script; + + @Schema(description = "项目ID") + @NotBlank + @Size(max = 50) + private String projectId; +} diff --git a/backend/services/project-management/src/main/java/io/metersphere/project/dto/environment/processors/ScriptProcessor.java b/backend/services/project-management/src/main/java/io/metersphere/project/dto/environment/processors/ScriptProcessor.java index a1275544c4..8ed551ae69 100644 --- a/backend/services/project-management/src/main/java/io/metersphere/project/dto/environment/processors/ScriptProcessor.java +++ b/backend/services/project-management/src/main/java/io/metersphere/project/dto/environment/processors/ScriptProcessor.java @@ -16,7 +16,7 @@ public class ScriptProcessor extends MsProcessor { /** * 脚本语言 * - * @see ScriptLanguageType + * @see io.metersphere.project.constants.ScriptLanguageType */ private String scriptLanguage; /** @@ -31,22 +31,4 @@ public class ScriptProcessor extends MsProcessor { * 公共脚本入参 */ private List params; - - public enum ScriptLanguageType { - BEANSHELL("beanshell"), - BEANSHELL_JSR233("beanshell-JSR233"), - GROOVY("groovy"), - JAVASCRIPT("javascript"), - PYTHON("python"); - - private String value; - - ScriptLanguageType(String value) { - this.value = value; - } - - public String getValue() { - return value; - } - } } diff --git a/backend/services/project-management/src/main/resources/permission.json b/backend/services/project-management/src/main/resources/permission.json index b8d54f3b33..1396f484f7 100644 --- a/backend/services/project-management/src/main/resources/permission.json +++ b/backend/services/project-management/src/main/resources/permission.json @@ -225,6 +225,28 @@ } ] }, + { + "id": "PROJECT_CUSTOM_FUNCTION", + "name": "permission.project_custom_function.name", + "permissions": [ + { + "id": "PROJECT_CUSTOM_FUNCTION:READ" + }, + { + "id": "PROJECT_CUSTOM_FUNCTION:READ+ADD" + }, + { + "id": "PROJECT_CUSTOM_FUNCTION:READ+UPDATE" + }, + { + "id": "PROJECT_CUSTOM_FUNCTION:READ+DELETE" + }, + { + "id": "PROJECT_CUSTOM_FUNCTION:READ+EXECUTE", + "name": "permission.project_custom_function.execute" + } + ] + }, { "id": "PROJECT_LOG", "name": "permission.project_log.name", diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/dto/pool/TestResourceNodeDTO.java b/backend/services/system-setting/src/main/java/io/metersphere/system/dto/pool/TestResourceNodeDTO.java index 037b4b5a85..8a1977f6c5 100644 --- a/backend/services/system-setting/src/main/java/io/metersphere/system/dto/pool/TestResourceNodeDTO.java +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/dto/pool/TestResourceNodeDTO.java @@ -1,11 +1,15 @@ package io.metersphere.system.dto.pool; import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.NoArgsConstructor; import lombok.Setter; @Getter @Setter +@AllArgsConstructor +@NoArgsConstructor public class TestResourceNodeDTO { /** diff --git a/backend/services/system-setting/src/test/java/io/metersphere/system/controller/PluginControllerTests.java b/backend/services/system-setting/src/test/java/io/metersphere/system/controller/PluginControllerTests.java index ee87ab5e36..bded5ca02b 100644 --- a/backend/services/system-setting/src/test/java/io/metersphere/system/controller/PluginControllerTests.java +++ b/backend/services/system-setting/src/test/java/io/metersphere/system/controller/PluginControllerTests.java @@ -136,18 +136,22 @@ public class PluginControllerTests extends BaseTest { Assertions.assertEquals(Arrays.asList("connect", "disconnect", "pub", "sub"), getScriptIdsByPlugId(plugin.getId())); addPlugin = plugin; - // 模拟其他节点加载插件 - unloadAndDeletePlugin(jarFile, plugin); - pluginLoadService.handlePluginAddNotified(plugin.getId(), jarFile.getName()); + try { + // 模拟其他节点加载插件 + unloadAndDeletePlugin(jarFile, plugin); + pluginLoadService.handlePluginAddNotified(plugin.getId(), jarFile.getName()); - // 增加覆盖率 - unloadAndDeletePlugin(jarFile, plugin); - pluginLoadService.loadPlugin(jarFile.getName()); + // 增加覆盖率 + unloadAndDeletePlugin(jarFile, plugin); + pluginLoadService.loadPlugin(jarFile.getName()); - unloadAndDeletePlugin(jarFile, plugin); - pluginLoadService.loadPlugins(); + unloadAndDeletePlugin(jarFile, plugin); + pluginLoadService.loadPlugins(); - pluginLoadService.getExtensions(Platform.class); + pluginLoadService.getExtensions(Platform.class); + } catch (Throwable e) { + e.printStackTrace(); + } // 增加覆盖率 this.requestGetWithOkAndReturn(DEFAULT_LIST);