diff --git a/backend/framework/plugin/plugin-api-sdk/src/main/java/io/metersphere/plugin/api/dto/ParameterConfig.java b/backend/framework/plugin/plugin-api-sdk/src/main/java/io/metersphere/plugin/api/dto/ParameterConfig.java index 32e0877539..b4dc044fae 100644 --- a/backend/framework/plugin/plugin-api-sdk/src/main/java/io/metersphere/plugin/api/dto/ParameterConfig.java +++ b/backend/framework/plugin/plugin-api-sdk/src/main/java/io/metersphere/plugin/api/dto/ParameterConfig.java @@ -9,4 +9,9 @@ import lombok.Data; @Data public class ParameterConfig { private String reportId; + /** + * 解析时,是否解析 enable 为 false 的组件 + * 导出时,需要解析 + */ + private Boolean parseDisabledElement = false; } diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/dto/request/processors/CommonScriptProcessor.java b/backend/services/api-test/src/main/java/io/metersphere/api/dto/request/processors/CommonScriptProcessor.java deleted file mode 100644 index 6ec8dd8403..0000000000 --- a/backend/services/api-test/src/main/java/io/metersphere/api/dto/request/processors/CommonScriptProcessor.java +++ /dev/null @@ -1,25 +0,0 @@ -package io.metersphere.api.dto.request.processors; - -import com.fasterxml.jackson.annotation.JsonTypeName; -import io.metersphere.api.dto.request.http.KeyValueParam; -import lombok.Data; - -import java.util.List; - -/** - * 公共脚本处理器 - * @Author: jianxing - * @CreateTime: 2023-11-07 09:59 - */ -@Data -@JsonTypeName("COMMON_SCRIPT") -public class CommonScriptProcessor extends MsProcessor { - /** - * 脚本ID - */ - private String scriptId; - /** - * 入参 - */ - private List params; -} diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/dto/request/processors/MsProcessor.java b/backend/services/api-test/src/main/java/io/metersphere/api/dto/request/processors/MsProcessor.java index affd9aa976..a9769b6997 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/dto/request/processors/MsProcessor.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/dto/request/processors/MsProcessor.java @@ -14,7 +14,6 @@ import lombok.Data; @JsonSubTypes.Type(value = ScriptProcessor.class), @JsonSubTypes.Type(value = SQLProcessor.class), @JsonSubTypes.Type(value = TimeWaitingProcessor.class), - @JsonSubTypes.Type(value = CommonScriptProcessor.class), @JsonSubTypes.Type(value = ExtractPostProcessor.class), }) public abstract class MsProcessor { 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 9ce74d2144..163e76e9c6 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 @@ -1,8 +1,11 @@ package io.metersphere.api.dto.request.processors; import com.fasterxml.jackson.annotation.JsonTypeName; +import io.metersphere.api.dto.request.http.KeyValueParam; import lombok.Data; +import java.util.List; + /** * @Author: jianxing * @CreateTime: 2023-11-06 21:12 @@ -10,7 +13,43 @@ import lombok.Data; @Data @JsonTypeName("SCRIPT") public class ScriptProcessor extends MsProcessor { + /** + * 脚本内容 + */ private String script; + /** + * 脚本语言 + * @see ScriptLanguageType + */ private String scriptLanguage; - private Boolean jsrEnable; + /** + * 是否启用公共脚本 + */ + private Boolean enableCommonScript; + /** + * 脚本ID + */ + private String scriptId; + /** + * 公共脚本入参 + */ + 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/dto/request/processors/extract/MsExtract.java b/backend/services/api-test/src/main/java/io/metersphere/api/dto/request/processors/extract/MsExtract.java index 38cb2647ef..fe8a8595c6 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/dto/request/processors/extract/MsExtract.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/dto/request/processors/extract/MsExtract.java @@ -3,6 +3,7 @@ package io.metersphere.api.dto.request.processors.extract; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; import lombok.Data; +import org.apache.commons.lang3.StringUtils; @Data @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "extractType") @@ -13,19 +14,19 @@ import lombok.Data; }) public abstract class MsExtract { /** - * 参数名 + * 变量名 */ - private String paramName; + private String variableName; /** * 参数类型 */ - private String paramType; - /** - * 提取范围 - */ - private String extractScope; + private String variableType; /** * 表达式 */ private String expression; + + public boolean isValid() { + return StringUtils.isNotBlank(variableName) && StringUtils.isNotBlank(expression); + } } diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/dto/request/processors/extract/RegexExtract.java b/backend/services/api-test/src/main/java/io/metersphere/api/dto/request/processors/extract/RegexExtract.java index 79ee0a072b..c1e15354a4 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/dto/request/processors/extract/RegexExtract.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/dto/request/processors/extract/RegexExtract.java @@ -8,18 +8,54 @@ import lombok.Data; public class RegexExtract extends ResultMatchingExtract { /** * 表达式匹配规则 - * 值为 ExpressionRuleType + * @see ExpressionRuleType */ private String expressionMatchingRule; + /** + * 提取范围 + * @see ExtractScope + */ + private String extractScope; public enum ExpressionRuleType { /** * 匹配表达式 */ - EXPRESSION, + EXPRESSION("$1$"), /** * 匹配组 */ - GROUP + GROUP("$0$"); + + private String value; + + ExpressionRuleType(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } + + public enum ExtractScope { + BODY("false"), + REQUEST_HEADERS("request_headers"), + UNESCAPED_BODY("unescaped"), + BODY_AS_DOCUMENT("as_document"), + RESPONSE_HEADERS("true"), + URL("URL"), + RESPONSE_CODE("code"), + RESPONSE_MESSAGE("message"); + + private String value; + + ExtractScope(String value) { + this.value = value; + } + + public String getValue() { + return value; + } } } diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/dto/request/processors/extract/XPathExtract.java b/backend/services/api-test/src/main/java/io/metersphere/api/dto/request/processors/extract/XPathExtract.java index 86542a9d94..ce920a7741 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/dto/request/processors/extract/XPathExtract.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/dto/request/processors/extract/XPathExtract.java @@ -8,6 +8,17 @@ import lombok.Data; */ @Data @JsonTypeName("X_PATH") -public class XPathExtract extends MsExtract { +public class XPathExtract extends ResultMatchingExtract { private String responseFormat; + + public enum ResponseFormat { + /** + * XML + */ + XML, + /** + * HTML + */ + HTML + } } \ No newline at end of file diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/MsCommonElementConverter.java b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/MsCommonElementConverter.java index 6b630d8ad7..c5fd7904e8 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/MsCommonElementConverter.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/MsCommonElementConverter.java @@ -2,6 +2,8 @@ package io.metersphere.api.parser.jmeter; import io.metersphere.api.dto.request.MsCommonElement; +import io.metersphere.api.dto.request.processors.MsProcessorConfig; +import io.metersphere.api.parser.jmeter.processor.MsProcessorConverterFactory; import io.metersphere.plugin.api.dto.ParameterConfig; import io.metersphere.plugin.api.spi.AbstractJmeterElementConverter; import org.apache.jorphan.collections.HashTree; @@ -17,6 +19,28 @@ public class MsCommonElementConverter extends AbstractJmeterElementConverter MsProcessorConverterFactory.getPreConverter(processor.getClass()).parse(tree, processor, config)); + } + + private void handlePostProcessor(HashTree tree, MsProcessorConfig postProcessorConfig , ParameterConfig config) { + if (postProcessorConfig == null || postProcessorConfig.getProcessors() == null) { + return; + } + postProcessorConfig.getProcessors() + .forEach(processor -> MsProcessorConverterFactory.getPostConverter(processor.getClass()).parse(tree, processor, config)); } } 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 new file mode 100644 index 0000000000..946eaac3b4 --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/constants/JmeterAlias.java @@ -0,0 +1,14 @@ +package io.metersphere.api.parser.jmeter.constants; + +/** + * @Author: jianxing + * @CreateTime: 2023-12-26 19:45 + */ +public class JmeterAlias { + public static final String TEST_BEAN_GUI = "TestBeanGUI"; + public static final String CONSTANT_TIMER_GUI = "ConstantTimerGui"; + public static final String REGEX_EXTRACTOR_GUI = "RegexExtractorGui"; + public static final String JSON_POST_PROCESSOR_GUI = "JSONPostProcessorGui"; + public static final String X_PATH_EXTRACTOR_GUI = "XPathExtractorGui"; + public static final String X_PATH2_EXTRACTOR_GUI = "XPath2ExtractorGui"; +} diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/constants/JmeterProperty.java b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/constants/JmeterProperty.java new file mode 100644 index 0000000000..c9820ef228 --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/constants/JmeterProperty.java @@ -0,0 +1,12 @@ +package io.metersphere.api.parser.jmeter.constants; + +/** + * @Author: jianxing + * @CreateTime: 2023-12-26 19:45 + */ +public class JmeterProperty { + public static final String SCRIPT = "script"; + public static final String CACHE_KEY = "cacheKey"; + public static final String BEAN_SAMPLER_QUERY = "BeanShellSampler.query"; + public static final String SCRIPT_LANGUAGE = "scriptLanguage"; +} diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/processor/ExtractPostProcessorConverter.java b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/processor/ExtractPostProcessorConverter.java new file mode 100644 index 0000000000..7950ffda05 --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/processor/ExtractPostProcessorConverter.java @@ -0,0 +1,28 @@ +package io.metersphere.api.parser.jmeter.processor; + +import io.metersphere.api.dto.request.processors.ExtractPostProcessor; +import io.metersphere.api.dto.request.processors.extract.MsExtract; +import io.metersphere.api.parser.jmeter.processor.extract.ExtractConverterFactory; +import io.metersphere.plugin.api.dto.ParameterConfig; +import org.apache.jorphan.collections.HashTree; + + +/** + * @Author: jianxing + * @CreateTime: 2023-12-26 14:49 + */ +public class ExtractPostProcessorConverter extends MsProcessorConverter { + + @Override + public void parse(HashTree hashTree, ExtractPostProcessor processor, ParameterConfig config) { + if (!needParse(processor, config) || processor.getExtractors() == null) { + return; + } + processor.getExtractors() + .stream() + .filter(MsExtract::isValid) + .forEach(extract -> + ExtractConverterFactory.getConverter(extract.getClass()) + .parse(hashTree, extract, config)); + } +} diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/processor/MsProcessorConverter.java b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/processor/MsProcessorConverter.java new file mode 100644 index 0000000000..d44c1d9300 --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/processor/MsProcessorConverter.java @@ -0,0 +1,32 @@ +package io.metersphere.api.parser.jmeter.processor; + + +import io.metersphere.api.dto.request.processors.MsProcessor; +import io.metersphere.plugin.api.dto.ParameterConfig; +import org.apache.commons.lang3.BooleanUtils; +import org.apache.jorphan.collections.HashTree; + +/** + * @Author: jianxing + * @CreateTime: 2023-10-27 10:07 + *

+ * body 解析器 + */ +public abstract class MsProcessorConverter { + + /** + * 解析对应的前后置处理器 + * @param hashTree + * @param processor + * @param config + */ + public abstract void parse(HashTree hashTree, T processor, ParameterConfig config); + + protected boolean needParse(MsProcessor processor, ParameterConfig config) { + // 如果组件是启用的,或者设置了解析禁用的组件,则返回 true + if (BooleanUtils.isTrue(processor.getEnable()) || config.getParseDisabledElement()) { + return true; + } + return false; + } +} diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/processor/MsProcessorConverterFactory.java b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/processor/MsProcessorConverterFactory.java new file mode 100644 index 0000000000..1b5842095c --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/processor/MsProcessorConverterFactory.java @@ -0,0 +1,38 @@ +package io.metersphere.api.parser.jmeter.processor; + +import io.metersphere.api.dto.request.processors.ExtractPostProcessor; +import io.metersphere.api.dto.request.processors.SQLProcessor; +import io.metersphere.api.dto.request.processors.ScriptProcessor; +import io.metersphere.api.dto.request.processors.TimeWaitingProcessor; + +import java.util.HashMap; +import java.util.Map; + +/** + * @Author: jianxing + * @CreateTime: 2023-12-21 19:19 + */ +public class MsProcessorConverterFactory { + + private static Map preConverterMap = new HashMap<>(); + private static Map postConverterMap = new HashMap<>(); + + static { + preConverterMap.put(ScriptProcessor.class, new ScriptPreProcessorConverter()); + preConverterMap.put(SQLProcessor.class, new SqlPreProcessorConverter()); + preConverterMap.put(TimeWaitingProcessor.class, new TimeWaitingProcessorConverter()); + + postConverterMap.put(ScriptProcessor.class, new ScriptPostProcessorConverter()); + postConverterMap.put(SQLProcessor.class, new SqlPostProcessorConverter()); + postConverterMap.put(TimeWaitingProcessor.class, new TimeWaitingProcessorConverter()); + postConverterMap.put(ExtractPostProcessor.class, new ExtractPostProcessorConverter()); + } + + public static MsProcessorConverter getPreConverter(Class processorClass) { + return preConverterMap.get(processorClass); + } + + public static MsProcessorConverter getPostConverter(Class processorClass) { + return postConverterMap.get(processorClass); + } +} 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 new file mode 100644 index 0000000000..99a05cff4b --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/processor/ScriptFilter.java @@ -0,0 +1,87 @@ +package io.metersphere.api.parser.jmeter.processor; + +import io.metersphere.api.dto.request.processors.ScriptProcessor; +import io.metersphere.sdk.exception.MSException; +import io.metersphere.sdk.util.LogUtils; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; + +import java.io.InputStream; +import java.nio.charset.Charset; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class ScriptFilter { + public static final String beanshell = "/blacklist/beanshell.bk"; + public static final String groovy = "/blacklist/groovy.bk"; + public static final String python = "/blacklist/python.bk"; + // 关键字内容较小,全局缓存下来避免重复读取 + public static final Map> scriptCache = new HashMap<>(); + + static { + // 初始化安全过滤脚本 + ScriptFilter.initScript(ScriptFilter.beanshell); + ScriptFilter.initScript(ScriptFilter.python); + ScriptFilter.initScript(ScriptFilter.groovy); + } + + public static void initScript(String path) { + try { + InputStream in = ScriptFilter.class.getResourceAsStream(path); + List bks = IOUtils.readLines(in, Charset.defaultCharset()); + if (CollectionUtils.isNotEmpty(bks)) { + scriptCache.put(path, bks); + } + } catch (Exception ex) { + LogUtils.error(ex.getMessage()); + } + } + + private static void blackList(StringBuffer buffer, String script, String path) { + try { + List bks = scriptCache.get(path); + if (CollectionUtils.isNotEmpty(bks)) { + bks.forEach(item -> { + if (script.contains(item) && script.indexOf(item) != -1) { + buffer.append(item).append(","); + } + }); + } + } catch (Exception ex) { + LogUtils.error(ex.getMessage()); + } + } + + public static void verify(String language, String label, String script) { + // 默认 groovy + ScriptProcessor.ScriptLanguageType scriptLanguageType = Arrays.stream(ScriptProcessor.ScriptLanguageType.values()) + .filter(item -> StringUtils.equals(item.getValue(), language)) + .findFirst() + .orElse(ScriptProcessor.ScriptLanguageType.GROOVY); + + if (StringUtils.isNotEmpty(script)) { + final StringBuffer buffer = new StringBuffer(); + switch (scriptLanguageType) { + case BEANSHELL: + blackList(buffer, script, beanshell); + break; + case PYTHON: + blackList(buffer, script, python); + break; + default: + blackList(buffer, script, groovy); + break; + } + if (StringUtils.isNotEmpty(buffer.toString())) { + String message = "脚本内包含敏感函数:【" + buffer.toString().substring(0, buffer.toString().length() - 1) + "】"; + if (StringUtils.isNotEmpty(label)) { + message = label + "," + message; + } + throw new MSException(message); + } + } + } +} diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/processor/ScriptPostProcessorConverter.java b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/processor/ScriptPostProcessorConverter.java new file mode 100644 index 0000000000..8007a691cd --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/processor/ScriptPostProcessorConverter.java @@ -0,0 +1,30 @@ +package io.metersphere.api.parser.jmeter.processor; + +import io.metersphere.api.dto.request.processors.ScriptProcessor; +import io.metersphere.plugin.api.dto.ParameterConfig; +import org.apache.jmeter.extractor.BeanShellPostProcessor; +import org.apache.jmeter.extractor.JSR223PostProcessor; +import org.apache.jmeter.testelement.TestElement; +import org.apache.jorphan.collections.HashTree; + +/** + * @Author: jianxing + * @CreateTime: 2023-12-26 14:49 + */ +public class ScriptPostProcessorConverter extends ScriptProcessorConverter { + @Override + public void parse(HashTree hashTree, ScriptProcessor scriptProcessor, ParameterConfig config) { + if (!needParse(scriptProcessor, config)) { + return; + } + // todo 处理公共脚本 + TestElement processor; + if (isJSR233(scriptProcessor)) { + processor = new JSR223PostProcessor(); + } else { + processor = new BeanShellPostProcessor(); + } + parse(processor, scriptProcessor); + hashTree.add(processor); + } +} diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/processor/ScriptPreProcessorConverter.java b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/processor/ScriptPreProcessorConverter.java new file mode 100644 index 0000000000..48ce4e3804 --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/processor/ScriptPreProcessorConverter.java @@ -0,0 +1,30 @@ +package io.metersphere.api.parser.jmeter.processor; + +import io.metersphere.api.dto.request.processors.ScriptProcessor; +import io.metersphere.plugin.api.dto.ParameterConfig; +import org.apache.jmeter.modifiers.BeanShellPreProcessor; +import org.apache.jmeter.modifiers.JSR223PreProcessor; +import org.apache.jmeter.testelement.TestElement; +import org.apache.jorphan.collections.HashTree; + +/** + * @Author: jianxing + * @CreateTime: 2023-12-26 14:49 + */ +public class ScriptPreProcessorConverter extends ScriptProcessorConverter { + @Override + public void parse(HashTree hashTree, ScriptProcessor scriptProcessor, ParameterConfig config) { + if (!needParse(scriptProcessor, config)) { + return; + } + // todo 处理公共脚本 + TestElement processor; + if (isJSR233(scriptProcessor)) { + processor = new JSR223PreProcessor(); + } else { + processor = new BeanShellPreProcessor(); + } + parse(processor, scriptProcessor); + hashTree.add(processor); + } +} 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 new file mode 100644 index 0000000000..521e2fa8da --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/processor/ScriptProcessorConverter.java @@ -0,0 +1,42 @@ +package io.metersphere.api.parser.jmeter.processor; + +import io.metersphere.api.dto.request.processors.ScriptProcessor; +import io.metersphere.api.parser.jmeter.constants.JmeterAlias; +import io.metersphere.api.parser.jmeter.constants.JmeterProperty; +import org.apache.commons.lang3.StringUtils; +import org.apache.jmeter.save.SaveService; +import org.apache.jmeter.testelement.TestElement; + + +/** + * @Author: jianxing + * @CreateTime: 2023-12-26 14:49 + */ +public abstract class ScriptProcessorConverter extends MsProcessorConverter { + + public static final String ENV_VARIABLE_EXPRESSION = "${__metersphere_env_id}"; + public static final String MS_RUNNING_ENV_PREFIX = "MS.ENV."; + protected void parse(TestElement testElement, ScriptProcessor scriptProcessor) { + // 脚本安全校验 + ScriptFilter.verify(scriptProcessor.getScriptLanguage(), scriptProcessor.getName(), scriptProcessor.getScript()); + + testElement.setEnabled(scriptProcessor.getEnable()); + String name = StringUtils.isEmpty(scriptProcessor.getName()) ? scriptProcessor.getClass().getSimpleName() : scriptProcessor.getName(); + testElement.setName(name); + + // todo 替换环境变量 +// String evnId = scriptProcessor.getEnvironmentId(); +// if (StringUtils.isNotEmpty(scriptProcessor.getScript())) { +// scriptProcessor.setScript(StringUtils.replace(scriptProcessor.getScript(), ENV_VARIABLE_EXPRESSION, "\"" + MS_RUNNING_ENV_PREFIX + evnId + ".\"")); +// } + testElement.setProperty(JmeterProperty.CACHE_KEY, false); + testElement.setProperty(TestElement.TEST_CLASS, testElement.getClass().getSimpleName()); + testElement.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass(JmeterAlias.TEST_BEAN_GUI)); + testElement.setProperty(JmeterProperty.SCRIPT_LANGUAGE, scriptProcessor.getScriptLanguage()); + testElement.setProperty(JmeterProperty.SCRIPT, scriptProcessor.getScript()); + } + + protected boolean isJSR233(ScriptProcessor scriptProcessor) { + return !StringUtils.equals(scriptProcessor.getScriptLanguage(), ScriptProcessor.ScriptLanguageType.BEANSHELL.getValue()); + } +} diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/processor/SqlPostProcessorConverter.java b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/processor/SqlPostProcessorConverter.java new file mode 100644 index 0000000000..2047cab758 --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/processor/SqlPostProcessorConverter.java @@ -0,0 +1,19 @@ +package io.metersphere.api.parser.jmeter.processor; + +import io.metersphere.api.dto.request.processors.SQLProcessor; +import io.metersphere.plugin.api.dto.ParameterConfig; +import org.apache.jorphan.collections.HashTree; + +/** + * @Author: jianxing + * @CreateTime: 2023-12-26 14:49 + */ +public class SqlPostProcessorConverter extends SqlProcessorConverter { + @Override + public void parse(HashTree hashTree, SQLProcessor scriptProcessor, ParameterConfig config) { + if (!needParse(scriptProcessor, config)) { + return; + } + // todo 等环境开发完之后,补充 + } +} diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/processor/SqlPreProcessorConverter.java b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/processor/SqlPreProcessorConverter.java new file mode 100644 index 0000000000..4605b2257b --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/processor/SqlPreProcessorConverter.java @@ -0,0 +1,19 @@ +package io.metersphere.api.parser.jmeter.processor; + +import io.metersphere.api.dto.request.processors.SQLProcessor; +import io.metersphere.plugin.api.dto.ParameterConfig; +import org.apache.jorphan.collections.HashTree; + +/** + * @Author: jianxing + * @CreateTime: 2023-12-26 14:49 + */ +public class SqlPreProcessorConverter extends SqlProcessorConverter { + @Override + public void parse(HashTree hashTree, SQLProcessor scriptProcessor, ParameterConfig config) { + if (!needParse(scriptProcessor, config)) { + return; + } + // todo 等环境开发完之后,补充 + } +} diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/processor/SqlProcessorConverter.java b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/processor/SqlProcessorConverter.java new file mode 100644 index 0000000000..2fbcf8c50b --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/processor/SqlProcessorConverter.java @@ -0,0 +1,17 @@ +package io.metersphere.api.parser.jmeter.processor; + +import io.metersphere.api.dto.request.processors.SQLProcessor; +import io.metersphere.api.dto.request.processors.ScriptProcessor; +import org.apache.jmeter.testelement.TestElement; + + +/** + * @Author: jianxing + * @CreateTime: 2023-12-26 14:49 + */ +public abstract class SqlProcessorConverter extends MsProcessorConverter { + + protected void parse(TestElement testElement, ScriptProcessor scriptProcessor) { + // todo 等环境开发完之后,补充 + } +} diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/processor/TimeWaitingProcessorConverter.java b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/processor/TimeWaitingProcessorConverter.java new file mode 100644 index 0000000000..68731f5d96 --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/processor/TimeWaitingProcessorConverter.java @@ -0,0 +1,28 @@ +package io.metersphere.api.parser.jmeter.processor; + +import io.metersphere.api.dto.request.processors.TimeWaitingProcessor; +import io.metersphere.plugin.api.dto.ParameterConfig; +import org.apache.jmeter.save.SaveService; +import org.apache.jmeter.testelement.TestElement; +import org.apache.jmeter.timers.ConstantTimer; +import org.apache.jorphan.collections.HashTree; + +import static io.metersphere.api.parser.jmeter.constants.JmeterAlias.CONSTANT_TIMER_GUI; + + +/** + * @Author: jianxing + * @CreateTime: 2023-12-26 14:49 + */ +public class TimeWaitingProcessorConverter extends MsProcessorConverter { + @Override + public void parse(HashTree hashTree, TimeWaitingProcessor processor, ParameterConfig config) { + ConstantTimer constantTimer = new ConstantTimer(); + constantTimer.setEnabled(processor.getEnable()); + constantTimer.setName(processor.getDelay() + " ms"); + constantTimer.setProperty(TestElement.TEST_CLASS, ConstantTimer.class.getName()); + constantTimer.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass(CONSTANT_TIMER_GUI)); + constantTimer.setDelay(processor.getDelay().toString()); + hashTree.add(constantTimer); + } +} diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/processor/extract/ExtractConverter.java b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/processor/extract/ExtractConverter.java new file mode 100644 index 0000000000..c06fc083fc --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/processor/extract/ExtractConverter.java @@ -0,0 +1,39 @@ +package io.metersphere.api.parser.jmeter.processor.extract; + +import io.metersphere.api.dto.request.processors.extract.MsExtract; +import io.metersphere.api.dto.request.processors.extract.ResultMatchingExtract; +import io.metersphere.plugin.api.dto.ParameterConfig; +import org.apache.commons.lang3.StringUtils; +import org.apache.jorphan.collections.HashTree; + +/** + * @Author: jianxing + * @CreateTime: 2023-12-27 10:32 + */ +public abstract class ExtractConverter { + /** + * 解析对应的提取器 + * @param hashTree + * @param extract + * @param config + */ + public abstract void parse(HashTree hashTree, T extract, ParameterConfig config); + + /** + * 处理匹配多条等匹配规则 + * @param extract + * @return + */ + protected Integer parseResultMatchingRule(ResultMatchingExtract extract) { + String resultMatchingRule = extract.getResultMatchingRule(); + if (StringUtils.equals(resultMatchingRule, ResultMatchingExtract.ResultMatchingRuleType.ALL.name())) { + return -1; + } else if (StringUtils.equals(resultMatchingRule, ResultMatchingExtract.ResultMatchingRuleType.RANDOM.name())) { + return 0; + } else if (StringUtils.equals(resultMatchingRule, ResultMatchingExtract.ResultMatchingRuleType.SPECIFIC.name()) + && extract.getResultMatchingRuleNum() != null) { + return extract.getResultMatchingRuleNum(); + } + return 1; + } +} diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/processor/extract/ExtractConverterFactory.java b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/processor/extract/ExtractConverterFactory.java new file mode 100644 index 0000000000..83f2625a14 --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/processor/extract/ExtractConverterFactory.java @@ -0,0 +1,26 @@ +package io.metersphere.api.parser.jmeter.processor.extract; + +import io.metersphere.api.dto.request.processors.extract.JSONPathExtract; +import io.metersphere.api.dto.request.processors.extract.RegexExtract; +import io.metersphere.api.dto.request.processors.extract.XPathExtract; + +import java.util.HashMap; +import java.util.Map; + +/** + * @Author: jianxing + * @CreateTime: 2023-12-27 10:31 + */ +public class ExtractConverterFactory { + private static Map converterMap = new HashMap<>(); + + static { + converterMap.put(RegexExtract.class, new RegexExtractConverter()); + converterMap.put(JSONPathExtract.class, new JSONPathExtractConverter()); + converterMap.put(XPathExtract.class, new XPathExtractConverter()); + } + + public static ExtractConverter getConverter(Class processorClass) { + return converterMap.get(processorClass); + } +} diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/processor/extract/JSONPathExtractConverter.java b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/processor/extract/JSONPathExtractConverter.java new file mode 100644 index 0000000000..57bfe1fa70 --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/processor/extract/JSONPathExtractConverter.java @@ -0,0 +1,30 @@ +package io.metersphere.api.parser.jmeter.processor.extract; + +import io.metersphere.api.dto.request.processors.extract.JSONPathExtract; +import io.metersphere.plugin.api.dto.ParameterConfig; +import org.apache.jmeter.extractor.json.jsonpath.JSONPostProcessor; +import org.apache.jmeter.save.SaveService; +import org.apache.jmeter.testelement.TestElement; +import org.apache.jorphan.collections.HashTree; + +import static io.metersphere.api.parser.jmeter.constants.JmeterAlias.JSON_POST_PROCESSOR_GUI; + +/** + * @Author: jianxing + * @CreateTime: 2023-12-27 10:32 + */ +public class JSONPathExtractConverter extends ExtractConverter { + + @Override + public void parse(HashTree hashTree, JSONPathExtract msExtract, ParameterConfig config) { + JSONPostProcessor extractor = new JSONPostProcessor(); + extractor.setName(msExtract.getVariableName()); + extractor.setProperty(TestElement.TEST_CLASS, JSONPostProcessor.class.getName()); + extractor.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass(JSON_POST_PROCESSOR_GUI)); + extractor.setRefNames(msExtract.getVariableName()); + extractor.setJsonPathExpressions(msExtract.getExpression()); + // 处理匹配多条等匹配规则 + extractor.setMatchNumbers(parseResultMatchingRule(msExtract).toString()); + hashTree.add(extractor); + } +} diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/processor/extract/RegexExtractConverter.java b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/processor/extract/RegexExtractConverter.java new file mode 100644 index 0000000000..eb2ec3e990 --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/processor/extract/RegexExtractConverter.java @@ -0,0 +1,39 @@ +package io.metersphere.api.parser.jmeter.processor.extract; + +import io.metersphere.api.dto.request.processors.extract.RegexExtract; +import io.metersphere.plugin.api.dto.ParameterConfig; +import org.apache.commons.lang3.StringUtils; +import org.apache.jmeter.extractor.RegexExtractor; +import org.apache.jmeter.save.SaveService; +import org.apache.jmeter.testelement.TestElement; +import org.apache.jorphan.collections.HashTree; + +import static io.metersphere.api.parser.jmeter.constants.JmeterAlias.REGEX_EXTRACTOR_GUI; + +/** + * @Author: jianxing + * @CreateTime: 2023-12-27 10:32 + */ +public class RegexExtractConverter extends ExtractConverter { + @Override + public void parse(HashTree hashTree, RegexExtract msExtract, ParameterConfig config) { + RegexExtractor extractor = new RegexExtractor(); + extractor.setName(msExtract.getVariableName()); + extractor.setProperty(TestElement.TEST_CLASS, RegexExtractor.class.getName()); + extractor.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass(REGEX_EXTRACTOR_GUI)); + extractor.setRefName(msExtract.getVariableName()); + extractor.setRegex(msExtract.getExpression()); + extractor.setUseField(msExtract.getExtractScope()); + + // 处理匹配多条等匹配规则 + extractor.setMatchNumber(parseResultMatchingRule(msExtract)); + + // $1$提取 JSON 响应中的第一个匹配项 $0$用于提取整个 JSON 响应 + if (StringUtils.isBlank(msExtract.getExpressionMatchingRule())) { + extractor.setTemplate(RegexExtract.ExpressionRuleType.EXPRESSION.getValue()); + } else { + extractor.setTemplate(msExtract.getExpressionMatchingRule()); + } + hashTree.add(extractor); + } +} diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/processor/extract/XPathExtractConverter.java b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/processor/extract/XPathExtractConverter.java new file mode 100644 index 0000000000..6d6110f490 --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/processor/extract/XPathExtractConverter.java @@ -0,0 +1,54 @@ +package io.metersphere.api.parser.jmeter.processor.extract; + +import io.metersphere.api.dto.request.processors.extract.XPathExtract; +import io.metersphere.plugin.api.dto.ParameterConfig; +import org.apache.commons.lang3.StringUtils; +import org.apache.jmeter.extractor.XPath2Extractor; +import org.apache.jmeter.extractor.XPathExtractor; +import org.apache.jmeter.save.SaveService; +import org.apache.jmeter.testelement.TestElement; +import org.apache.jorphan.collections.HashTree; + +import static io.metersphere.api.parser.jmeter.constants.JmeterAlias.X_PATH2_EXTRACTOR_GUI; +import static io.metersphere.api.parser.jmeter.constants.JmeterAlias.X_PATH_EXTRACTOR_GUI; + +/** + * @Author: jianxing + * @CreateTime: 2023-12-27 10:32 + */ +public class XPathExtractConverter extends ExtractConverter { + + @Override + public void parse(HashTree hashTree, XPathExtract msExtract, ParameterConfig config) { + if (StringUtils.equals(msExtract.getResponseFormat(), XPathExtract.ResponseFormat.HTML.name())) { + hashTree.add(parseXPathExtract(msExtract)); + } else { + hashTree.add(parseXPath2Extract(msExtract)); + } + } + + private XPathExtractor parseXPathExtract(XPathExtract msExtract) { + XPathExtractor extractor = new XPathExtractor(); + extractor.setTolerant(true); + extractor.setName(msExtract.getVariableName()); + extractor.setProperty(TestElement.TEST_CLASS, XPathExtractor.class.getName()); + extractor.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass(X_PATH_EXTRACTOR_GUI)); + extractor.setRefName(msExtract.getVariableName()); + extractor.setXPathQuery(msExtract.getExpression()); + // 处理匹配多条等匹配规则 + extractor.setMatchNumber(parseResultMatchingRule(msExtract)); + return extractor; + } + + private XPath2Extractor parseXPath2Extract(XPathExtract msExtract) { + XPath2Extractor extractor = new XPath2Extractor(); + extractor.setName(msExtract.getVariableName()); + extractor.setProperty(TestElement.TEST_CLASS, XPathExtractor.class.getName()); + extractor.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass(X_PATH2_EXTRACTOR_GUI)); + extractor.setRefName(msExtract.getVariableName()); + extractor.setXPathQuery(msExtract.getExpression()); + // 处理匹配多条等匹配规则 + extractor.setMatchNumber(parseResultMatchingRule(msExtract)); + return extractor; + } +} diff --git a/backend/services/api-test/src/main/resources/blacklist/beanshell.bk b/backend/services/api-test/src/main/resources/blacklist/beanshell.bk new file mode 100644 index 0000000000..e69de29bb2 diff --git a/backend/services/api-test/src/main/resources/blacklist/groovy.bk b/backend/services/api-test/src/main/resources/blacklist/groovy.bk new file mode 100644 index 0000000000..e69de29bb2 diff --git a/backend/services/api-test/src/main/resources/blacklist/python.bk b/backend/services/api-test/src/main/resources/blacklist/python.bk new file mode 100644 index 0000000000..4c486af918 --- /dev/null +++ b/backend/services/api-test/src/main/resources/blacklist/python.bk @@ -0,0 +1 @@ +os.system \ No newline at end of file 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 8c15c12ebe..c64eb52924 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 @@ -13,8 +13,12 @@ import io.metersphere.api.dto.request.http.body.*; import io.metersphere.api.dto.request.processors.*; import io.metersphere.api.dto.request.processors.extract.JSONPathExtract; import io.metersphere.api.dto.request.processors.extract.RegexExtract; +import io.metersphere.api.dto.request.processors.extract.ResultMatchingExtract; import io.metersphere.api.dto.request.processors.extract.XPathExtract; +import io.metersphere.api.parser.TestElementParser; +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.sdk.constants.MsAssertionCondition; import org.junit.jupiter.api.Assertions; @@ -121,8 +125,7 @@ public class MsHTTPElementTest { } @Test - public void msProcessorTest() { - + public void processorParseTest() { MsHTTPElement msHTTPElement = getMsHttpElement(); List processors = new ArrayList<>(); @@ -130,10 +133,15 @@ public class MsHTTPElementTest { ScriptProcessor scriptProcessor = new ScriptProcessor(); scriptProcessor.setEnable(true); scriptProcessor.setScript("script"); - scriptProcessor.setScriptLanguage("js"); - scriptProcessor.setJsrEnable(true); + scriptProcessor.setScriptLanguage(ScriptProcessor.ScriptLanguageType.JAVASCRIPT.getValue()); processors.add(scriptProcessor); + ScriptProcessor beanShellScriptProcessor = new ScriptProcessor(); + beanShellScriptProcessor.setEnable(true); + beanShellScriptProcessor.setScript("script"); + beanShellScriptProcessor.setScriptLanguage(ScriptProcessor.ScriptLanguageType.BEANSHELL.getValue()); + processors.add(beanShellScriptProcessor); + SQLProcessor sqlProcessor = new SQLProcessor(); sqlProcessor.setScript("script"); sqlProcessor.setEnable(true); @@ -152,39 +160,68 @@ public class MsHTTPElementTest { timeWaitingProcessor.setEnable(true); processors.add(timeWaitingProcessor); - CommonScriptProcessor commonScriptProcessor = new CommonScriptProcessor(); - commonScriptProcessor.setEnable(true); - commonScriptProcessor.setScriptId("11111"); - KeyValueParam commonScriptParam = new KeyValueParam(); - commonScriptParam.setKey("11"); - commonScriptParam.setValue("11"); - commonScriptProcessor.setParams(List.of(commonScriptParam)); - processors.add(commonScriptProcessor); + List postProcessors = new ArrayList<>(); ExtractPostProcessor extractPostProcessor = new ExtractPostProcessor(); RegexExtract regexExtract = new RegexExtract(); - regexExtract.setExpressionMatchingRule(""); + regexExtract.setVariableName("test"); + regexExtract.setExpressionMatchingRule("$1$"); + regexExtract.setExpression("test"); + + RegexExtract regexExtract2 = new RegexExtract(); + regexExtract2.setVariableName("test"); + regexExtract2.setExpressionMatchingRule("$0$"); + regexExtract2.setResultMatchingRule(ResultMatchingExtract.ResultMatchingRuleType.ALL.name()); + regexExtract2.setExtractScope("unescaped"); + regexExtract2.setExpression("test"); + JSONPathExtract jsonPathExtract = new JSONPathExtract(); - jsonPathExtract.setExpression(""); + jsonPathExtract.setExpression("test"); + jsonPathExtract.setVariableName("test"); + jsonPathExtract.setResultMatchingRule(ResultMatchingExtract.ResultMatchingRuleType.RANDOM.name()); + XPathExtract xPathExtract = new XPathExtract(); - xPathExtract.setExpression(""); - extractPostProcessor.setExtractors(List.of(regexExtract, jsonPathExtract, xPathExtract)); - processors.add(extractPostProcessor); + xPathExtract.setExpression("test"); + xPathExtract.setVariableName("test"); + xPathExtract.setResultMatchingRule(ResultMatchingExtract.ResultMatchingRuleType.SPECIFIC.name()); + xPathExtract.setResultMatchingRuleNum(2); + + XPathExtract xPathExtract2 = new XPathExtract(); + xPathExtract2.setExpression("test"); + xPathExtract2.setVariableName("test"); + xPathExtract2.setResultMatchingRule(ResultMatchingExtract.ResultMatchingRuleType.SPECIFIC.name()); + xPathExtract2.setResultMatchingRuleNum(2); + xPathExtract2.setResponseFormat(XPathExtract.ResponseFormat.HTML.name()); + + extractPostProcessor.setExtractors(List.of(regexExtract, regexExtract2, jsonPathExtract, xPathExtract, xPathExtract2)); + postProcessors.addAll(processors); + postProcessors.add(extractPostProcessor); MsProcessorConfig msProcessorConfig = new MsProcessorConfig(); msProcessorConfig.setProcessors(processors); + MsProcessorConfig msPostProcessorConfig = new MsProcessorConfig(); + msPostProcessorConfig.setProcessors(postProcessors); + MsCommonElement msCommonElement = new MsCommonElement(); msCommonElement.setPreProcessorConfig(msProcessorConfig); - msCommonElement.setPostProcessorConfig(msProcessorConfig); + msCommonElement.setPostProcessorConfig(msPostProcessorConfig); LinkedList linkedList = new LinkedList(); linkedList.add(msCommonElement); msHTTPElement.setChildren(linkedList); + // 测试序列化 String json = ApiDataUtils.toJSONString(msHTTPElement); Assertions.assertNotNull(json); Assertions.assertEquals(ApiDataUtils.parseObject(json, AbstractMsTestElement.class), msHTTPElement); + + // 测试脚本解析 + ParameterConfig parameterConfig = new ParameterConfig(); + parameterConfig.setReportId("reportId"); + TestElementParser defaultParser = TestElementParserFactory.getDefaultParser(); + AbstractMsTestElement msTestElement = ApiDataUtils.parseObject(json, AbstractMsTestElement.class); + defaultParser.parse(msTestElement, parameterConfig); } @Test @@ -256,7 +293,7 @@ public class MsHTTPElementTest { msHTTPElement.setPath("/test"); msHTTPElement.setMethod("GET"); msHTTPElement.setName("name"); - msHTTPElement.setEnable(false); + msHTTPElement.setEnable(true); Header header = new Header(); header.setEnable(false); 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 c14a1326bd..cd84bb9b53 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 @@ -307,6 +307,7 @@ public class PluginLoadService { public void handlePluginAddNotified(String pluginId, String fileName) { if (!hasPlugin(pluginId)) { loadPluginFromRepository(fileName); + msPluginManager.startPlugin(pluginId); } }