feat(接口测试): 解析前后置操作

This commit is contained in:
AgAngle 2023-12-27 18:50:10 +08:00 committed by Craftsman
parent 63581bb769
commit 34279ebcc8
31 changed files with 771 additions and 58 deletions

View File

@ -9,4 +9,9 @@ import lombok.Data;
@Data @Data
public class ParameterConfig { public class ParameterConfig {
private String reportId; private String reportId;
/**
* 解析时是否解析 enable false 的组件
* 导出时需要解析
*/
private Boolean parseDisabledElement = false;
} }

View File

@ -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<KeyValueParam> params;
}

View File

@ -14,7 +14,6 @@ import lombok.Data;
@JsonSubTypes.Type(value = ScriptProcessor.class), @JsonSubTypes.Type(value = ScriptProcessor.class),
@JsonSubTypes.Type(value = SQLProcessor.class), @JsonSubTypes.Type(value = SQLProcessor.class),
@JsonSubTypes.Type(value = TimeWaitingProcessor.class), @JsonSubTypes.Type(value = TimeWaitingProcessor.class),
@JsonSubTypes.Type(value = CommonScriptProcessor.class),
@JsonSubTypes.Type(value = ExtractPostProcessor.class), @JsonSubTypes.Type(value = ExtractPostProcessor.class),
}) })
public abstract class MsProcessor { public abstract class MsProcessor {

View File

@ -1,8 +1,11 @@
package io.metersphere.api.dto.request.processors; package io.metersphere.api.dto.request.processors;
import com.fasterxml.jackson.annotation.JsonTypeName; import com.fasterxml.jackson.annotation.JsonTypeName;
import io.metersphere.api.dto.request.http.KeyValueParam;
import lombok.Data; import lombok.Data;
import java.util.List;
/** /**
* @Author: jianxing * @Author: jianxing
* @CreateTime: 2023-11-06 21:12 * @CreateTime: 2023-11-06 21:12
@ -10,7 +13,43 @@ import lombok.Data;
@Data @Data
@JsonTypeName("SCRIPT") @JsonTypeName("SCRIPT")
public class ScriptProcessor extends MsProcessor { public class ScriptProcessor extends MsProcessor {
/**
* 脚本内容
*/
private String script; private String script;
/**
* 脚本语言
* @see ScriptLanguageType
*/
private String scriptLanguage; private String scriptLanguage;
private Boolean jsrEnable; /**
* 是否启用公共脚本
*/
private Boolean enableCommonScript;
/**
* 脚本ID
*/
private String scriptId;
/**
* 公共脚本入参
*/
private List<KeyValueParam> 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;
}
}
} }

View File

@ -3,6 +3,7 @@ package io.metersphere.api.dto.request.processors.extract;
import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonTypeInfo;
import lombok.Data; import lombok.Data;
import org.apache.commons.lang3.StringUtils;
@Data @Data
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "extractType") @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "extractType")
@ -13,19 +14,19 @@ import lombok.Data;
}) })
public abstract class MsExtract { public abstract class MsExtract {
/** /**
* 参数 * 变量
*/ */
private String paramName; private String variableName;
/** /**
* 参数类型 * 参数类型
*/ */
private String paramType; private String variableType;
/**
* 提取范围
*/
private String extractScope;
/** /**
* 表达式 * 表达式
*/ */
private String expression; private String expression;
public boolean isValid() {
return StringUtils.isNotBlank(variableName) && StringUtils.isNotBlank(expression);
}
} }

View File

@ -8,18 +8,54 @@ import lombok.Data;
public class RegexExtract extends ResultMatchingExtract { public class RegexExtract extends ResultMatchingExtract {
/** /**
* 表达式匹配规则 * 表达式匹配规则
* 值为 ExpressionRuleType * @see ExpressionRuleType
*/ */
private String expressionMatchingRule; private String expressionMatchingRule;
/**
* 提取范围
* @see ExtractScope
*/
private String extractScope;
public enum ExpressionRuleType { 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;
}
} }
} }

View File

@ -8,6 +8,17 @@ import lombok.Data;
*/ */
@Data @Data
@JsonTypeName("X_PATH") @JsonTypeName("X_PATH")
public class XPathExtract extends MsExtract { public class XPathExtract extends ResultMatchingExtract {
private String responseFormat; private String responseFormat;
public enum ResponseFormat {
/**
* XML
*/
XML,
/**
* HTML
*/
HTML
}
} }

View File

@ -2,6 +2,8 @@ package io.metersphere.api.parser.jmeter;
import io.metersphere.api.dto.request.MsCommonElement; 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.dto.ParameterConfig;
import io.metersphere.plugin.api.spi.AbstractJmeterElementConverter; import io.metersphere.plugin.api.spi.AbstractJmeterElementConverter;
import org.apache.jorphan.collections.HashTree; import org.apache.jorphan.collections.HashTree;
@ -17,6 +19,28 @@ public class MsCommonElementConverter extends AbstractJmeterElementConverter<MsC
@Override @Override
public void toHashTree(HashTree tree, MsCommonElement element, ParameterConfig config) { public void toHashTree(HashTree tree, MsCommonElement element, ParameterConfig config) {
// todo 解析前后置和断言 // todo 开关默认开启关闭则运行该接口时不执行全局前置
// preProcessorConfig.getEnableGlobal();
// 解析前置处理器
handlePreProcessor(tree, element.getPreProcessorConfig(), config);
// 解析后置处理器
handlePostProcessor(tree, element.getPostProcessorConfig(), config);
}
private void handlePreProcessor(HashTree tree, MsProcessorConfig preProcessorConfig, ParameterConfig config) {
if (preProcessorConfig == null || preProcessorConfig.getProcessors() == null) {
return;
}
preProcessorConfig.getProcessors()
.forEach(processor -> 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));
} }
} }

View File

@ -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";
}

View File

@ -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";
}

View File

@ -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<ExtractPostProcessor> {
@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));
}
}

View File

@ -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
* <p>
* body 解析器
*/
public abstract class MsProcessorConverter<T extends MsProcessor> {
/**
* 解析对应的前后置处理器
* @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;
}
}

View File

@ -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<Class, MsProcessorConverter> preConverterMap = new HashMap<>();
private static Map<Class, MsProcessorConverter> 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);
}
}

View File

@ -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<String, List<String>> 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<String> 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<String> 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);
}
}
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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<ScriptProcessor> {
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());
}
}

View File

@ -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 等环境开发完之后补充
}
}

View File

@ -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 等环境开发完之后补充
}
}

View File

@ -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<SQLProcessor> {
protected void parse(TestElement testElement, ScriptProcessor scriptProcessor) {
// todo 等环境开发完之后补充
}
}

View File

@ -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<TimeWaitingProcessor> {
@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);
}
}

View File

@ -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<T extends MsExtract> {
/**
* 解析对应的提取器
* @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;
}
}

View File

@ -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<Class, ExtractConverter> 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);
}
}

View File

@ -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<JSONPathExtract> {
@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);
}
}

View File

@ -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<RegexExtract> {
@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);
}
}

View File

@ -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<XPathExtract> {
@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;
}
}

View File

@ -0,0 +1 @@
os.system

View File

@ -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.*;
import io.metersphere.api.dto.request.processors.extract.JSONPathExtract; 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.RegexExtract;
import io.metersphere.api.dto.request.processors.extract.ResultMatchingExtract;
import io.metersphere.api.dto.request.processors.extract.XPathExtract; 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.api.utils.ApiDataUtils;
import io.metersphere.plugin.api.dto.ParameterConfig;
import io.metersphere.plugin.api.spi.AbstractMsTestElement; import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import io.metersphere.sdk.constants.MsAssertionCondition; import io.metersphere.sdk.constants.MsAssertionCondition;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
@ -121,8 +125,7 @@ public class MsHTTPElementTest {
} }
@Test @Test
public void msProcessorTest() { public void processorParseTest() {
MsHTTPElement msHTTPElement = getMsHttpElement(); MsHTTPElement msHTTPElement = getMsHttpElement();
List processors = new ArrayList<>(); List processors = new ArrayList<>();
@ -130,10 +133,15 @@ public class MsHTTPElementTest {
ScriptProcessor scriptProcessor = new ScriptProcessor(); ScriptProcessor scriptProcessor = new ScriptProcessor();
scriptProcessor.setEnable(true); scriptProcessor.setEnable(true);
scriptProcessor.setScript("script"); scriptProcessor.setScript("script");
scriptProcessor.setScriptLanguage("js"); scriptProcessor.setScriptLanguage(ScriptProcessor.ScriptLanguageType.JAVASCRIPT.getValue());
scriptProcessor.setJsrEnable(true);
processors.add(scriptProcessor); 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 sqlProcessor = new SQLProcessor();
sqlProcessor.setScript("script"); sqlProcessor.setScript("script");
sqlProcessor.setEnable(true); sqlProcessor.setEnable(true);
@ -152,39 +160,68 @@ public class MsHTTPElementTest {
timeWaitingProcessor.setEnable(true); timeWaitingProcessor.setEnable(true);
processors.add(timeWaitingProcessor); processors.add(timeWaitingProcessor);
CommonScriptProcessor commonScriptProcessor = new CommonScriptProcessor(); List postProcessors = new ArrayList<>();
commonScriptProcessor.setEnable(true);
commonScriptProcessor.setScriptId("11111");
KeyValueParam commonScriptParam = new KeyValueParam();
commonScriptParam.setKey("11");
commonScriptParam.setValue("11");
commonScriptProcessor.setParams(List.of(commonScriptParam));
processors.add(commonScriptProcessor);
ExtractPostProcessor extractPostProcessor = new ExtractPostProcessor(); ExtractPostProcessor extractPostProcessor = new ExtractPostProcessor();
RegexExtract regexExtract = new RegexExtract(); 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 jsonPathExtract = new JSONPathExtract();
jsonPathExtract.setExpression(""); jsonPathExtract.setExpression("test");
jsonPathExtract.setVariableName("test");
jsonPathExtract.setResultMatchingRule(ResultMatchingExtract.ResultMatchingRuleType.RANDOM.name());
XPathExtract xPathExtract = new XPathExtract(); XPathExtract xPathExtract = new XPathExtract();
xPathExtract.setExpression(""); xPathExtract.setExpression("test");
extractPostProcessor.setExtractors(List.of(regexExtract, jsonPathExtract, xPathExtract)); xPathExtract.setVariableName("test");
processors.add(extractPostProcessor); 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 msProcessorConfig = new MsProcessorConfig();
msProcessorConfig.setProcessors(processors); msProcessorConfig.setProcessors(processors);
MsProcessorConfig msPostProcessorConfig = new MsProcessorConfig();
msPostProcessorConfig.setProcessors(postProcessors);
MsCommonElement msCommonElement = new MsCommonElement(); MsCommonElement msCommonElement = new MsCommonElement();
msCommonElement.setPreProcessorConfig(msProcessorConfig); msCommonElement.setPreProcessorConfig(msProcessorConfig);
msCommonElement.setPostProcessorConfig(msProcessorConfig); msCommonElement.setPostProcessorConfig(msPostProcessorConfig);
LinkedList linkedList = new LinkedList(); LinkedList linkedList = new LinkedList();
linkedList.add(msCommonElement); linkedList.add(msCommonElement);
msHTTPElement.setChildren(linkedList); msHTTPElement.setChildren(linkedList);
// 测试序列化
String json = ApiDataUtils.toJSONString(msHTTPElement); String json = ApiDataUtils.toJSONString(msHTTPElement);
Assertions.assertNotNull(json); Assertions.assertNotNull(json);
Assertions.assertEquals(ApiDataUtils.parseObject(json, AbstractMsTestElement.class), msHTTPElement); 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 @Test
@ -256,7 +293,7 @@ public class MsHTTPElementTest {
msHTTPElement.setPath("/test"); msHTTPElement.setPath("/test");
msHTTPElement.setMethod("GET"); msHTTPElement.setMethod("GET");
msHTTPElement.setName("name"); msHTTPElement.setName("name");
msHTTPElement.setEnable(false); msHTTPElement.setEnable(true);
Header header = new Header(); Header header = new Header();
header.setEnable(false); header.setEnable(false);

View File

@ -307,6 +307,7 @@ public class PluginLoadService {
public void handlePluginAddNotified(String pluginId, String fileName) { public void handlePluginAddNotified(String pluginId, String fileName) {
if (!hasPlugin(pluginId)) { if (!hasPlugin(pluginId)) {
loadPluginFromRepository(fileName); loadPluginFromRepository(fileName);
msPluginManager.startPlugin(pluginId);
} }
} }