feat(接口测试): 处理执行时引用公共脚本

This commit is contained in:
AgAngle 2024-02-23 14:44:38 +08:00 committed by Craftsman
parent 03fde1b4a3
commit 7a453643ad
24 changed files with 445 additions and 84 deletions

View File

@ -0,0 +1,42 @@
package io.metersphere.api.dto;
import io.metersphere.api.dto.request.MsCommonElement;
import io.metersphere.api.dto.request.http.MsHTTPElement;
import lombok.Data;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 执行场景解析参数时的临时参数
*
* @Author: jianxing
* @CreateTime: 2024-02-22 11:27
*/
@Data
public class ApiScenarioParseParam {
/**
* 步骤详情 Map
* key 为步骤ID
* value 为步骤详情字符串
*/
private Map<String, String> stepDetailMap;
/**
* 资源的请求内容 Map
* key 为资源ID
* value 为请求详情字符串
*/
private Map<String, String> resourceDetailMap;
/**
* MsHTTPElement Map
* key stepType
* value MsHTTPElement
*/
private Map<String, List<MsHTTPElement>> stepTypeHttpElementMap = new HashMap<>();
/**
* 场景中所有的 MsCommonElement 列表
*/
private List<MsCommonElement> commonElements = new ArrayList<>();
}

View File

@ -16,6 +16,7 @@ import org.apache.jorphan.collections.HashTree;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import static io.metersphere.api.parser.jmeter.constants.JmeterAlias.USER_PARAMETERS_GUI;
@ -53,6 +54,9 @@ public class MsCommentScriptElementConverter extends AbstractJmeterElementConver
scriptElement = new BeanShellSampler();
}
ScriptProcessorConverter.parse(scriptElement, scriptProcessor);
// 添加公共脚本的参数
Optional.ofNullable(ScriptProcessorConverter.getScriptArguments(scriptProcessor))
.ifPresent(hashTree::add);
hashTree.add(scriptElement);
}

View File

@ -20,4 +20,5 @@ public class JmeterAlias {
public static final String COOKIE_PANEL = "CookiePanel";
public static final String HEADER_PANEL = "HeaderPanel";
public static final String AUTH_PANEL = "AuthPanel";
public static final String ARGUMENTS_PANEL = "ArgumentsPanel";
}

View File

@ -8,6 +8,8 @@ import org.apache.jmeter.protocol.java.sampler.JSR223Sampler;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jorphan.collections.HashTree;
import java.util.Optional;
/**
* 环境场景级前置处理器处理
*
@ -31,6 +33,11 @@ public class ScenarioScriptProcessorConverter extends ScriptProcessorConverter {
}
parse(processor, scriptProcessor);
// 添加公共脚本的参数
Optional.ofNullable(getScriptArguments(scriptProcessor))
.ifPresent(hashTree::add);
// 标记当前处理器是否关联场景结果
processor.setName("ASSOCIATE_RESULT_PROCESSOR_" + associateScenarioResult);
hashTree.add(processor);

View File

@ -7,6 +7,8 @@ import org.apache.jmeter.extractor.JSR223PostProcessor;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jorphan.collections.HashTree;
import java.util.Optional;
/**
* @Author: jianxing
* @CreateTime: 2023-12-26 14:49
@ -17,7 +19,6 @@ public class ScriptPostProcessorConverter extends ScriptProcessorConverter {
if (!needParse(scriptProcessor, config) || !scriptProcessor.isValid()) {
return;
}
// todo 处理公共脚本
TestElement processor;
if (isJSR233(scriptProcessor)) {
processor = new JSR223PostProcessor();
@ -25,6 +26,11 @@ public class ScriptPostProcessorConverter extends ScriptProcessorConverter {
processor = new BeanShellPostProcessor();
}
parse(processor, scriptProcessor);
// 添加公共脚本的参数
Optional.ofNullable(getScriptArguments(scriptProcessor))
.ifPresent(hashTree::add);
hashTree.add(processor);
}
}

View File

@ -1,12 +1,14 @@
package io.metersphere.api.parser.jmeter.processor;
import io.metersphere.project.api.processor.ScriptProcessor;
import io.metersphere.plugin.api.dto.ParameterConfig;
import io.metersphere.project.api.processor.ScriptProcessor;
import org.apache.jmeter.modifiers.BeanShellPreProcessor;
import org.apache.jmeter.modifiers.JSR223PreProcessor;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jorphan.collections.HashTree;
import java.util.Optional;
/**
* @Author: jianxing
* @CreateTime: 2023-12-26 14:49
@ -17,7 +19,6 @@ public class ScriptPreProcessorConverter extends ScriptProcessorConverter {
if (!needParse(scriptProcessor, config) || !scriptProcessor.isValid()) {
return;
}
// todo 处理公共脚本
TestElement processor;
if (isJSR233(scriptProcessor)) {
processor = new JSR223PreProcessor();
@ -25,6 +26,11 @@ public class ScriptPreProcessorConverter extends ScriptProcessorConverter {
processor = new BeanShellPreProcessor();
}
parse(processor, scriptProcessor);
// 添加公共脚本的参数
Optional.ofNullable(getScriptArguments(scriptProcessor))
.ifPresent(hashTree::add);
hashTree.add(processor);
}
}

View File

@ -1,14 +1,22 @@
package io.metersphere.api.parser.jmeter.processor;
import io.metersphere.plugin.api.constants.ElementProperty;
import io.metersphere.project.constants.ScriptLanguageType;
import io.metersphere.project.api.processor.ScriptProcessor;
import io.metersphere.api.parser.jmeter.constants.JmeterAlias;
import io.metersphere.api.parser.jmeter.constants.JmeterProperty;
import io.metersphere.plugin.api.constants.ElementProperty;
import io.metersphere.project.api.KeyValueParam;
import io.metersphere.project.api.processor.ScriptProcessor;
import io.metersphere.project.constants.ScriptLanguageType;
import io.metersphere.project.dto.CommonScriptInfo;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.save.SaveService;
import org.apache.jmeter.testelement.TestElement;
import java.util.List;
import static io.metersphere.api.parser.jmeter.constants.JmeterAlias.ARGUMENTS_PANEL;
/**
* @Author: jianxing
@ -38,9 +46,48 @@ public abstract class ScriptProcessorConverter extends MsProcessorConverter<Scri
testElement.setProperty(JmeterProperty.CACHE_KEY, cacheKey);
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());
testElement.setProperty(ElementProperty.PROJECT_ID.name(), scriptProcessor.getProjectId());
String scriptLanguage = scriptProcessor.getScriptLanguage();
String script = scriptProcessor.getScript();
if (scriptProcessor.isEnableCommonScript()) {
scriptLanguage = scriptProcessor.getCommonScriptInfo().getScriptLanguage();
script = scriptProcessor.getCommonScriptInfo().getScript();
}
if (scriptLanguage == null) {
scriptLanguage = ScriptLanguageType.BEANSHELL.name();
}
testElement.setProperty(JmeterProperty.SCRIPT, script);
testElement.setProperty(JmeterProperty.SCRIPT_LANGUAGE, scriptLanguage.toLowerCase());
}
public static Arguments getScriptArguments(ScriptProcessor scriptProcessor) {
if (scriptProcessor == null || !scriptProcessor.isEnableCommonScript() || !scriptProcessor.isValid()) {
return null;
}
CommonScriptInfo commonScriptInfo = scriptProcessor.getCommonScriptInfo();
if (CollectionUtils.isEmpty(commonScriptInfo.getParams())) {
return null;
}
List<KeyValueParam> params = commonScriptInfo.getParams()
.stream()
.filter(KeyValueParam::isValid)
.toList();
if (CollectionUtils.isEmpty(commonScriptInfo.getParams())) {
return null;
}
Arguments arguments = new Arguments();
arguments.setEnabled(true);
arguments.setName(scriptProcessor.getName() + "_Arguments");
arguments.setProperty(TestElement.TEST_CLASS, Arguments.class.getName());
arguments.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass(ARGUMENTS_PANEL));
for (KeyValueParam param : params) {
arguments.addArgument(param.getKey(), param.getValue(), "=");
}
return arguments;
}
public static boolean isJSR233(ScriptProcessor scriptProcessor) {

View File

@ -1,14 +1,15 @@
package io.metersphere.api.parser.jmeter.processor.assertion;
import io.metersphere.project.api.assertion.MsScriptAssertion;
import io.metersphere.project.api.processor.ScriptProcessor;
import io.metersphere.api.parser.jmeter.processor.ScriptProcessorConverter;
import io.metersphere.plugin.api.dto.ParameterConfig;
import io.metersphere.project.api.assertion.MsScriptAssertion;
import io.metersphere.project.api.processor.ScriptProcessor;
import io.metersphere.sdk.util.BeanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.assertions.JSR223Assertion;
import org.apache.jorphan.collections.HashTree;
import java.util.Optional;
/**
* @Author: jianxing
* @CreateTime: 2023-12-27 21:01
@ -16,17 +17,18 @@ import org.apache.jorphan.collections.HashTree;
public class ScriptAssertionConverter extends AssertionConverter<MsScriptAssertion> {
@Override
public void parse(HashTree hashTree, MsScriptAssertion msAssertion, ParameterConfig config, boolean isIgnoreStatus) {
if (!needParse(msAssertion, config) || !isValid(msAssertion)) {
if (!needParse(msAssertion, config) || !msAssertion.isValid()) {
return;
}
JSR223Assertion jsr223Assertion = new JSR223Assertion();
ScriptProcessorConverter.parse(jsr223Assertion, BeanUtils.copyBean(new ScriptProcessor(), msAssertion));
ScriptProcessor scriptProcessor = BeanUtils.copyBean(new ScriptProcessor(), msAssertion);
ScriptProcessorConverter.parse(jsr223Assertion, scriptProcessor);
// 添加公共脚本的参数
Optional.ofNullable(ScriptProcessorConverter.getScriptArguments(scriptProcessor))
.ifPresent(hashTree::add);
hashTree.add(jsr223Assertion);
}
public boolean isValid(MsScriptAssertion msAssertion) {
// todo 公共脚本库
return StringUtils.isNotBlank(msAssertion.getScript());
}
}

View File

@ -3,16 +3,25 @@ package io.metersphere.api.service;
import io.metersphere.api.dto.ApiFile;
import io.metersphere.api.dto.definition.ResponseBinaryBody;
import io.metersphere.api.dto.definition.ResponseBody;
import io.metersphere.api.dto.request.MsCommonElement;
import io.metersphere.api.dto.request.http.MsHTTPElement;
import io.metersphere.api.dto.request.http.body.BinaryBody;
import io.metersphere.api.dto.request.http.body.Body;
import io.metersphere.api.dto.request.http.body.FormDataBody;
import io.metersphere.api.dto.request.http.body.FormDataKV;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import io.metersphere.project.api.KeyValueParam;
import io.metersphere.project.api.processor.MsProcessor;
import io.metersphere.project.api.processor.ScriptProcessor;
import io.metersphere.project.domain.CustomFunction;
import io.metersphere.project.domain.CustomFunctionBlob;
import io.metersphere.project.domain.FileAssociation;
import io.metersphere.project.domain.FileMetadata;
import io.metersphere.project.dto.CommonScriptInfo;
import io.metersphere.project.service.CustomFunctionService;
import io.metersphere.project.service.FileAssociationService;
import io.metersphere.project.service.FileMetadataService;
import io.metersphere.sdk.util.JSON;
import jakarta.annotation.Resource;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
@ -22,6 +31,8 @@ import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
@ -35,8 +46,12 @@ public class ApiCommonService {
private FileAssociationService fileAssociationService;
@Resource
private FileMetadataService fileMetadataService;
@Resource
private CustomFunctionService customFunctionService;
/**
* 根据 fileId 查找 MsHTTPElement 中的 ApiFile
*
* @param fileId
* @param msTestElement
* @return
@ -63,24 +78,26 @@ public class ApiCommonService {
/**
* 设置关联的文件的最新信息
* 包括文件别名和是否被删除
*
* @param resourceId
* @param msTestElement
*/
public void updateLinkFileInfo(String resourceId, AbstractMsTestElement msTestElement) {
updateLinkFileInfo(resourceId, getApiFiles(msTestElement));
public void setLinkFileInfo(String resourceId, AbstractMsTestElement msTestElement) {
setLinkFileInfo(resourceId, getApiFiles(msTestElement));
}
/**
* 设置关联的文件的最新信息
* 包括文件别名和是否被删除
*
* @param resourceId
* @param responseBody
*/
public void updateLinkFileInfo(String resourceId, ResponseBody responseBody) {
updateLinkFileInfo(resourceId, getApiBodyFiles(responseBody));
public void setLinkFileInfo(String resourceId, ResponseBody responseBody) {
setLinkFileInfo(resourceId, getApiBodyFiles(responseBody));
}
private void updateLinkFileInfo(String resourceId, List<ApiFile> apiFiles) {
private void setLinkFileInfo(String resourceId, List<ApiFile> apiFiles) {
List<ApiFile> linkFiles = apiFiles.stream()
.filter(file -> !file.getLocal() && !file.getDelete())
.toList();
@ -114,7 +131,6 @@ public class ApiCommonService {
/**
*
* @param body
* @return
*/
@ -155,8 +171,96 @@ public class ApiCommonService {
public void replaceApiFileInfo(List<ApiFile> updateFiles, FileMetadata newFileMetadata) {
for (ApiFile updateFile : updateFiles) {
updateFile.setFileId(newFileMetadata.getId());
// todo 重新设置文件名
// updateFile.setFileName();
updateFile.setFileName(newFileMetadata.getOriginalName());
}
}
/**
* 设置使用脚本前后置的公共脚本信息
* @param msTestElement
*/
public void setEnableCommonScriptProcessorInfo(AbstractMsTestElement msTestElement) {
MsCommonElement msCommonElement = getMsCommonElement(msTestElement);
Optional.ofNullable(msCommonElement).ifPresent(item -> setEnableCommonScriptProcessorInfo(List.of(item)));
}
/**
* 设置使用脚本前后置的公共脚本信息
*
* @param commonElements
*/
public void setEnableCommonScriptProcessorInfo(List<MsCommonElement> commonElements) {
List<ScriptProcessor> scriptsProcessors = getEnableCommonScriptProcessors(commonElements);
List<String> commonScriptIds = scriptsProcessors.stream()
.map(processor -> processor.getCommonScriptInfo().getId())
.toList();
Map<String, CustomFunctionBlob> customFunctionBlobMap = customFunctionService.getBlobByIds(commonScriptIds).stream()
.collect(Collectors.toMap(CustomFunctionBlob::getId, Function.identity()));
Map<String, CustomFunction> customFunctionMap = customFunctionService.getByIds(commonScriptIds).stream()
.collect(Collectors.toMap(CustomFunction::getId, Function.identity()));
for (ScriptProcessor processor : scriptsProcessors) {
CommonScriptInfo commonScriptInfo = processor.getCommonScriptInfo();
CustomFunctionBlob customFunctionBlob = customFunctionBlobMap.get(commonScriptInfo.getId());
CustomFunction customFunction = customFunctionMap.get(commonScriptInfo.getId());
// 设置公共脚本信息
Optional.ofNullable(customFunctionBlob.getParams()).ifPresent(paramsBlob -> {
List<KeyValueParam> commonParams = JSON.parseArray(new String(paramsBlob), KeyValueParam.class);
// 替换用户输入值
commonParams.forEach(commonParam ->
Optional.ofNullable(commonScriptInfo.getParams()).ifPresent(params ->
params.stream()
.filter(param -> StringUtils.equals(commonParam.getKey(), param.getKey()))
.findFirst()
.ifPresent(param -> commonParam.setValue(param.getValue()))
)
);
commonScriptInfo.setParams(commonParams);
});
Optional.ofNullable(customFunctionBlob.getScript()).ifPresent(script ->
commonScriptInfo.setScript(new String(script)));
commonScriptInfo.setScriptLanguage(customFunction.getType());
commonScriptInfo.setName(customFunction.getName());
}
}
/**
* 获取使用公共脚本的前后置
*
* @param commonElements
* @return
*/
private List<ScriptProcessor> getEnableCommonScriptProcessors(List<MsCommonElement> commonElements) {
List<MsProcessor> processors = new ArrayList<>();
for (MsCommonElement commonElement : commonElements) {
processors.addAll(commonElement.getPreProcessorConfig().getProcessors());
processors.addAll(commonElement.getPostProcessorConfig().getProcessors());
}
// 获取使用公共脚本的前后置
List<ScriptProcessor> scriptsProcessors = processors.stream()
.filter(processor -> processor instanceof ScriptProcessor)
.map(processor -> (ScriptProcessor) processor)
.filter(ScriptProcessor::getEnable)
.filter(ScriptProcessor::isEnableCommonScript)
.filter(ScriptProcessor::isValid)
.collect(Collectors.toList());
return scriptsProcessors;
}
public MsCommonElement getMsCommonElement(AbstractMsTestElement msTestElement) {
if (CollectionUtils.isNotEmpty(msTestElement.getChildren())) {
for (AbstractMsTestElement child : msTestElement.getChildren()) {
if (child instanceof MsCommonElement msCommonElement) {
return msCommonElement;
}
}
}
return null;
}
}

View File

@ -132,9 +132,6 @@ public class ApiExecuteService {
parameterConfig.setGlobalParams(getGlobalParam(request));
// todo 获取接口插件和jar包
// todo 处理公共脚本
// todo 接口用例 method 获取定义中的数据库字段
String executeScript = parseExecuteScript(request.getTestElement(), parameterConfig);
doDebug(reportId, testId, taskRequest, executeScript, request.getProjectId());
@ -318,7 +315,7 @@ public class ApiExecuteService {
List<FileMetadata> fileMetadataList = fileManagementService.findJarByProjectId(List.of(taskRequest.getProjectId()));
taskRequest.setFuncJars(fileMetadataList.stream()
.map(file -> {
String fileName = file.getName() + "." + file.getType();
String fileName = file.getOriginalName();
ApiExecuteFileInfo tempFileInfo = getApiExecuteFileInfo(file.getId(), fileName, file.getProjectId(), file.getStorage());
if (StorageType.isGit(file.getStorage())) {
// 设置Git信息

View File

@ -78,7 +78,8 @@ public class ApiDebugService {
ApiDebugDTO apiDebugDTO = new ApiDebugDTO();
BeanUtils.copyBean(apiDebugDTO, apiDebug);
AbstractMsTestElement msTestElement = ApiDataUtils.parseObject(new String(apiDebugBlob.getRequest()), AbstractMsTestElement.class);
apiCommonService.updateLinkFileInfo(id, msTestElement);
apiCommonService.setLinkFileInfo(id, msTestElement);
apiCommonService.setEnableCommonScriptProcessorInfo(msTestElement);
apiDebugDTO.setRequest(msTestElement);
apiDebugDTO.setResponse(apiDebugDTO.getResponse());
return apiDebugDTO;
@ -218,6 +219,10 @@ public class ApiDebugService {
paramConfig.setTestElementClassPluginIdMap(apiPluginService.getTestElementPluginMap());
paramConfig.setTestElementClassProtocalMap(apiPluginService.getTestElementProtocolMap());
paramConfig.setReportId(reportId);
// 设置使用脚本前后置的公共脚本信息
apiCommonService.setEnableCommonScriptProcessorInfo(runRequest.getTestElement());
apiExecuteService.debug(runRequest, paramConfig);
return runRequest.getReportId();
}

View File

@ -872,13 +872,14 @@ public class ApiDefinitionService {
Optional<ApiDefinitionBlob> apiDefinitionBlobOptional = Optional.ofNullable(apiDefinitionBlobMapper.selectByPrimaryKey(id));
apiDefinitionBlobOptional.ifPresent(blob -> {
AbstractMsTestElement msTestElement = ApiDataUtils.parseObject(new String(blob.getRequest()), AbstractMsTestElement.class);
apiCommonService.updateLinkFileInfo(id, msTestElement);
apiCommonService.setLinkFileInfo(id, msTestElement);
apiCommonService.setEnableCommonScriptProcessorInfo(msTestElement);
apiDefinitionDTO.setRequest(msTestElement);
// blob.getResponse() null 时不进行转换
if (blob.getResponse() != null) {
List<HttpResponse> httpResponses = ApiDataUtils.parseArray(new String(blob.getResponse()), HttpResponse.class);
for (HttpResponse httpResponse : httpResponses) {
apiCommonService.updateLinkFileInfo(id, httpResponse.getBody());
apiCommonService.setLinkFileInfo(id, httpResponse.getBody());
}
apiDefinitionDTO.setResponse(httpResponses);
}

View File

@ -210,7 +210,10 @@ public class ApiTestCaseService {
example.createCriteria().andCaseIdEqualTo(id).andUserIdEqualTo(userId);
List<ApiTestCaseFollower> followers = apiTestCaseFollowerMapper.selectByExample(example);
apiTestCaseDTO.setFollow(CollectionUtils.isNotEmpty(followers));
apiTestCaseDTO.setRequest(ApiDataUtils.parseObject(new String(testCaseBlob.getRequest()), AbstractMsTestElement.class));
AbstractMsTestElement msTestElement = ApiDataUtils.parseObject(new String(testCaseBlob.getRequest()), AbstractMsTestElement.class);
apiCommonService.setLinkFileInfo(id, msTestElement);
apiCommonService.setEnableCommonScriptProcessorInfo(msTestElement);
apiTestCaseDTO.setRequest(msTestElement);
return apiTestCaseDTO;
}

View File

@ -1054,11 +1054,11 @@ public class ApiScenarioService {
Map<String, List<String>> refResourceMap = new HashMap<>();
buildRefResourceIdMap(steps, refResourceMap);
ApiScenarioParseParam parseParam = new ApiScenarioParseParam();
// 查询引用的资源详情
Map<String, String> resourceBlobMap = getResourceBlobMap(refResourceMap);
parseParam.setResourceDetailMap(getResourceDetailMap(refResourceMap));
// 查询复制的步骤详情
Map<String, String> detailMap = getStepDetailMap(steps, request.getStepDetails());
parseParam.setStepDetailMap(getStepDetailMap(steps, request.getStepDetails()));
// 解析生成待执行的场景树
MsScenario msScenario = new MsScenario();
@ -1068,10 +1068,11 @@ public class ApiScenarioService {
// 获取场景环境相关配置
ApiScenarioParseEnvInfo scenarioParseEnvInfo = getScenarioParseEnvInfo(refResourceMap, request.getEnvironmentId(), request.getGrouped());
Map<String, List<MsHTTPElement>> stepTypeHttpElementMap = new HashMap<>();
parseStep2MsElement(msScenario, steps, resourceBlobMap, detailMap, stepTypeHttpElementMap, scenarioParseEnvInfo);
parseStep2MsElement(msScenario, steps, parseParam, scenarioParseEnvInfo);
// 设置 HttpElement 的模块信息
setHttpElementModuleId(stepTypeHttpElementMap);
setHttpElementModuleId(parseParam.getStepTypeHttpElementMap());
// 设置使用脚本前后置的公共脚本信息
apiCommonService.setEnableCommonScriptProcessorInfo(parseParam.getCommonElements());
ApiResourceRunRequest runRequest = BeanUtils.copyBean(new ApiResourceRunRequest(), request);
runRequest.setProjectId(request.getProjectId());
@ -1230,13 +1231,15 @@ public class ApiScenarioService {
*/
private void parseStep2MsElement(AbstractMsTestElement parentElement,
List<? extends ApiScenarioStepCommonDTO> steps,
Map<String, String> resourceBlobMap,
Map<String, String> stepDetailMap,
Map<String, List<MsHTTPElement>> stepTypeHttpElementMap,
ApiScenarioParseParam parseParam,
ApiScenarioParseEnvInfo scenarioParseEnvInfo) {
if (CollectionUtils.isNotEmpty(steps)) {
parentElement.setChildren(new LinkedList<>());
}
Map<String, String> stepDetailMap = parseParam.getStepDetailMap();
Map<String, String> resourceDetailMap = parseParam.getResourceDetailMap();
Map<String, List<MsHTTPElement>> stepTypeHttpElementMap = parseParam.getStepTypeHttpElementMap();
for (ApiScenarioStepCommonDTO step : steps) {
StepParser stepParser = StepParserFactory.getStepParser(step.getStepType());
if (BooleanUtils.isFalse(step.getEnable())) {
@ -1245,7 +1248,7 @@ public class ApiScenarioService {
setPartialRefStepEnable(step, stepDetailMap);
// 将步骤详情解析生成对应的MsTestElement
AbstractMsTestElement msTestElement = stepParser.parseTestElement(step, resourceBlobMap.get(step.getResourceId()), stepDetailMap.get(step.getId()));
AbstractMsTestElement msTestElement = stepParser.parseTestElement(step, resourceDetailMap.get(step.getResourceId()), stepDetailMap.get(step.getId()));
if (msTestElement != null) {
if (msTestElement instanceof MsHTTPElement msHTTPElement) {
// 暂存http类型的步骤
@ -1255,11 +1258,13 @@ public class ApiScenarioService {
msTestElement.setProjectId(step.getProjectId());
msTestElement.setResourceId(step.getResourceId());
setMsScenarioParam(scenarioParseEnvInfo, step, msTestElement);
// 记录 msCommonElement
Optional.ofNullable(apiCommonService.getMsCommonElement(msTestElement))
.ifPresent(msCommonElement -> parseParam.getCommonElements().add(msCommonElement));
parentElement.getChildren().add(msTestElement);
}
if (CollectionUtils.isNotEmpty(step.getChildren())) {
parseStep2MsElement(msTestElement, step.getChildren(), resourceBlobMap,
stepDetailMap, stepTypeHttpElementMap, scenarioParseEnvInfo);
parseStep2MsElement(msTestElement, step.getChildren(), parseParam, scenarioParseEnvInfo);
}
}
}
@ -1383,7 +1388,7 @@ public class ApiScenarioService {
return stepDetails;
}
private Map<String, String> getResourceBlobMap(Map<String, List<String>> refResourceMap) {
private Map<String, String> getResourceDetailMap(Map<String, List<String>> refResourceMap) {
Map<String, String> resourceBlobMap = new HashMap<>();
List<String> apiIds = refResourceMap.get(ApiScenarioStepType.API.name());
List<ApiDefinitionBlob> apiDefinitionBlobs = apiDefinitionService.getBlobByIds(apiIds);
@ -1645,13 +1650,14 @@ public class ApiScenarioService {
}
StepParser stepParser = StepParserFactory.getStepParser(step.getStepType());
Object stepDetail = stepParser.parseDetail(step);
if (stepDetail instanceof MsHTTPElement msHTTPElement) {
if (stepDetail instanceof AbstractMsTestElement msTestElement) {
// 设置关联的文件的最新信息
if (isRef(step.getRefType())) {
apiCommonService.updateLinkFileInfo(step.getResourceId(), msHTTPElement);
apiCommonService.setLinkFileInfo(step.getResourceId(), msTestElement);
} else {
apiCommonService.updateLinkFileInfo(step.getScenarioId(), msHTTPElement);
apiCommonService.setLinkFileInfo(step.getScenarioId(), msTestElement);
}
apiCommonService.setEnableCommonScriptProcessorInfo(msTestElement);
}
return stepDetail;
}

View File

@ -28,10 +28,18 @@ import io.metersphere.api.service.BaseFileManagementTestService;
import io.metersphere.api.service.BaseResourcePoolTestService;
import io.metersphere.api.utils.ApiDataUtils;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import io.metersphere.project.api.KeyValueEnableParam;
import io.metersphere.project.api.KeyValueParam;
import io.metersphere.project.api.processor.ScriptProcessor;
import io.metersphere.project.constants.ScriptLanguageType;
import io.metersphere.project.domain.CustomFunction;
import io.metersphere.project.domain.ProjectTestResourcePool;
import io.metersphere.project.domain.ProjectTestResourcePoolExample;
import io.metersphere.project.dto.CommonScriptInfo;
import io.metersphere.project.dto.customfunction.request.CustomFunctionRequest;
import io.metersphere.project.dto.filemanagement.FileInfo;
import io.metersphere.project.mapper.ProjectTestResourcePoolMapper;
import io.metersphere.project.service.CustomFunctionService;
import io.metersphere.project.service.FileAssociationService;
import io.metersphere.sdk.constants.DefaultRepositoryDir;
import io.metersphere.sdk.constants.PermissionConstants;
@ -93,6 +101,8 @@ public class ApiDebugControllerTests extends BaseTest {
private ProjectTestResourcePoolMapper projectTestResourcePoolMapper;
@Resource
private TestResourcePoolMapper testResourcePoolMapper;
@Resource
private CustomFunctionService customFunctionService;
private static ApiDebug addApiDebug;
private static ApiDebug anotherAddApiDebug;
private static String fileMetadataId;
@ -363,7 +373,7 @@ public class ApiDebugControllerTests extends BaseTest {
ApiDebugDTO copyApiDebugDTO = BeanUtils.copyBean(new ApiDebugDTO(), apiDebugMapper.selectByPrimaryKey(addApiDebug.getId()));
ApiDebugBlob apiDebugBlob = apiDebugBlobMapper.selectByPrimaryKey(addApiDebug.getId());
AbstractMsTestElement msTestElement = ApiDataUtils.parseObject(new String(apiDebugBlob.getRequest()), AbstractMsTestElement.class);
apiCommonService.updateLinkFileInfo(addApiDebug.getId(), msTestElement);
apiCommonService.setLinkFileInfo(addApiDebug.getId(), msTestElement);
copyApiDebugDTO.setRequest(msTestElement);
Assertions.assertEquals(apiDebugDTO, copyApiDebugDTO);
@ -477,6 +487,22 @@ public class ApiDebugControllerTests extends BaseTest {
msAssertionConfig.setAssertions(MsHTTPElementTest.getGeneralXmlAssertions());
msCommonElement = new MsCommonElement();
msCommonElement.setAssertionConfig(msAssertionConfig);
// 测试公共脚本
ScriptProcessor scriptProcessor = new ScriptProcessor();
scriptProcessor.setEnable(true);
scriptProcessor.setName("test");
scriptProcessor.setScriptLanguage(ScriptLanguageType.JAVASCRIPT.name());
CustomFunction customFunction = addCustomFunction();
scriptProcessor.setCommonScriptInfo(new CommonScriptInfo());
scriptProcessor.getCommonScriptInfo().setId(customFunction.getId());
scriptProcessor.setEnableCommonScript(true);
KeyValueParam keyValueParam = new KeyValueParam();
keyValueParam.setKey("a");
keyValueParam.setValue("bb");
scriptProcessor.getCommonScriptInfo().setParams(List.of(keyValueParam));
msCommonElement.getPostProcessorConfig().getProcessors().add(scriptProcessor);
linkedList = new LinkedList();
linkedList.add(msCommonElement);
msHTTPElement = MsHTTPElementTest.getMsHttpElement();
@ -485,7 +511,6 @@ public class ApiDebugControllerTests extends BaseTest {
request.setRequest(getMsElementParam(msHTTPElement));
this.requestPostWithOk(DEBUG, request);
// 测试请求体
MockMultipartFile file = getMockMultipartFile();
String fileId = doUploadTempFile(file);
@ -513,6 +538,19 @@ public class ApiDebugControllerTests extends BaseTest {
requestPostPermissionTest(PermissionConstants.PROJECT_API_DEBUG_EXECUTE, DEBUG, request);
}
private CustomFunction addCustomFunction() {
CustomFunctionRequest request = new CustomFunctionRequest();
request.setScript("aaa");
KeyValueEnableParam keyValueEnableParam = new KeyValueEnableParam();
keyValueEnableParam.setKey("a");
keyValueEnableParam.setValue("b");
request.setParams(JSON.toJSONString(keyValueEnableParam));
request.setProjectId(DEFAULT_PROJECT_ID);
request.setType(ScriptLanguageType.BEANSHELL.name());
request.setName(IDGenerator.nextStr());
return customFunctionService.add(request, "admin");
}
/**
* 测试关联的文件更新
*

View File

@ -348,11 +348,11 @@ public class ApiDefinitionControllerTests extends BaseTest {
}
if (apiDefinitionBlob != null) {
AbstractMsTestElement msTestElement = ApiDataUtils.parseObject(new String(apiDefinitionBlob.getRequest()), AbstractMsTestElement.class);
apiCommonService.updateLinkFileInfo(apiDefinition.getId(), msTestElement);
apiCommonService.setLinkFileInfo(apiDefinition.getId(), msTestElement);
copyApiDefinitionDTO.setRequest(msTestElement);
List<HttpResponse> httpResponses = ApiDataUtils.parseArray(new String(apiDefinitionBlob.getResponse()), HttpResponse.class);
for (HttpResponse httpResponse : httpResponses) {
apiCommonService.updateLinkFileInfo(apiDefinition.getId(), httpResponse.getBody());
apiCommonService.setLinkFileInfo(apiDefinition.getId(), httpResponse.getBody());
}
copyApiDefinitionDTO.setResponse(httpResponses);
}

View File

@ -415,12 +415,14 @@ public class ApiTestCaseControllerTests extends BaseTest {
ApiDefinition apiDefinition = apiDefinitionMapper.selectByPrimaryKey(apiTestCase.getApiDefinitionId());
copyApiDebugDTO.setMethod(apiDefinition.getMethod());
copyApiDebugDTO.setPath(apiDefinition.getPath());
ApiTestCaseBlob apiDebugBlob = apiTestCaseBlobMapper.selectByPrimaryKey(apiTestCase.getId());
ApiTestCaseBlob apiTestCaseBlob = apiTestCaseBlobMapper.selectByPrimaryKey(apiTestCase.getId());
ApiTestCaseFollowerExample example = new ApiTestCaseFollowerExample();
example.createCriteria().andCaseIdEqualTo(apiTestCase.getId()).andUserIdEqualTo("admin");
List<ApiTestCaseFollower> followers = apiTestCaseFollowerMapper.selectByExample(example);
copyApiDebugDTO.setFollow(CollectionUtils.isNotEmpty(followers));
copyApiDebugDTO.setRequest(ApiDataUtils.parseObject(new String(apiDebugBlob.getRequest()), AbstractMsTestElement.class));
AbstractMsTestElement msTestElement = ApiDataUtils.parseObject(new String(apiTestCaseBlob.getRequest()), AbstractMsTestElement.class);
apiCommonService.setLinkFileInfo(apiTestCase.getId(), msTestElement);
copyApiDebugDTO.setRequest(msTestElement);
Assertions.assertEquals(apiDebugDTO, copyApiDebugDTO);
this.requestGetWithOk(GET + anotherApiTestCase.getId())
.andReturn();

View File

@ -27,6 +27,7 @@ import io.metersphere.project.api.processor.extract.RegexExtract;
import io.metersphere.project.api.processor.extract.ResultMatchingExtract;
import io.metersphere.project.api.processor.extract.XPathExtract;
import io.metersphere.project.constants.ScriptLanguageType;
import io.metersphere.project.dto.CommonScriptInfo;
import io.metersphere.sdk.constants.MsAssertionCondition;
import io.metersphere.sdk.util.BeanUtils;
import org.junit.jupiter.api.Assertions;
@ -363,7 +364,8 @@ public class MsHTTPElementTest {
assertions.add(responseTimeAssertion);
MsScriptAssertion scriptAssertion = new MsScriptAssertion();
scriptAssertion.setScriptId("1111");
scriptAssertion.setCommonScriptInfo(new CommonScriptInfo());
scriptAssertion.getCommonScriptInfo().setId("1111");
scriptAssertion.setScript("1111");
scriptAssertion.setName("1111");
assertions.add(scriptAssertion);

View File

@ -1,11 +1,12 @@
package io.metersphere.project.api.assertion;
import com.fasterxml.jackson.annotation.JsonTypeName;
import io.metersphere.project.api.KeyValueParam;
import io.metersphere.project.constants.ScriptLanguageType;
import io.metersphere.project.dto.CommonScriptInfo;
import jakarta.validation.Valid;
import lombok.Data;
import java.util.List;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
/**
* 变量断言
@ -27,13 +28,23 @@ public class MsScriptAssertion extends MsAssertion {
/**
* 是否启用公共脚本
*/
private Boolean enableCommonScript;
private Boolean enableCommonScript = false;
/**
* 脚本ID
* 公共脚本信息
* {@link CommonScriptInfo}
*/
private String scriptId;
/**
* 公共脚本入参
*/
private List<KeyValueParam> params;
@Valid
private CommonScriptInfo commonScriptInfo;
public boolean isValid() {
if (isEnableCommonScript()) {
return commonScriptInfo != null && StringUtils.isNotBlank(commonScriptInfo.getId());
} else {
return StringUtils.isNotBlank(script);
}
}
public boolean isEnableCommonScript() {
return BooleanUtils.isTrue(enableCommonScript);
}
}

View File

@ -2,16 +2,15 @@ package io.metersphere.project.api.processor;
import com.fasterxml.jackson.annotation.JsonTypeName;
import io.metersphere.project.constants.ScriptLanguageType;
import io.metersphere.project.api.KeyValueParam;
import io.metersphere.project.dto.CommonScriptInfo;
import io.metersphere.system.valid.EnumValue;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import java.util.List;
/**
* @Author: jianxing
@ -29,6 +28,7 @@ public class ScriptProcessor extends MsProcessor {
* {@link ScriptLanguageType}
*/
@Size(max = 20)
@NotBlank
@EnumValue(enumClass = ScriptLanguageType.class)
private String scriptLanguage;
/**
@ -38,20 +38,22 @@ public class ScriptProcessor extends MsProcessor {
*/
private Boolean enableCommonScript = false;
/**
* 公共脚本ID
*/
@Size(max = 50)
private String scriptId;
/**
* 公共脚本入参
* 公共脚本信息
* {@link CommonScriptInfo}
*/
@Valid
private List<KeyValueParam> params;
private CommonScriptInfo commonScriptInfo;
public boolean isValid() {
if (BooleanUtils.isTrue(enableCommonScript) && StringUtils.isBlank(scriptId)) {
return false;
if (isEnableCommonScript()) {
return commonScriptInfo != null && StringUtils.isNotBlank(commonScriptInfo.getId());
} else {
return StringUtils.isNotBlank(script);
}
return StringUtils.isNotBlank(script);
}
public boolean isEnableCommonScript() {
return BooleanUtils.isTrue(enableCommonScript);
}
}

View File

@ -0,0 +1,40 @@
package io.metersphere.project.dto;
import io.metersphere.project.api.KeyValueParam;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Size;
import lombok.Data;
import java.util.List;
/**
* @Author: jianxing
* @CreateTime: 2024-02-23 13:34
*/
@Data
public class CommonScriptInfo {
/**
* 公共脚本ID
*/
@Size(min = 1, max = 50)
private String id;
/**
* 公共脚本的名字
* 页面展示需要
*/
private String name;
/**
* 公共脚本的内容
* 执行时设置
*/
private String script;
/**
* 脚本语言
*/
private String scriptLanguage;
/**
* 公共脚本入参
*/
@Valid
private List<KeyValueParam> params;
}

View File

@ -2,6 +2,7 @@ package io.metersphere.project.service;
import io.metersphere.project.domain.CustomFunction;
import io.metersphere.project.domain.CustomFunctionBlob;
import io.metersphere.project.domain.CustomFunctionBlobExample;
import io.metersphere.project.domain.CustomFunctionExample;
import io.metersphere.project.dto.customfunction.CustomFunctionDTO;
import io.metersphere.project.dto.customfunction.request.CustomFunctionPageRequest;
@ -162,4 +163,23 @@ public class CustomFunctionService {
}
}
public List<CustomFunctionBlob> getBlobByIds(List<String> commonScriptIds) {
if (CollectionUtils.isEmpty(commonScriptIds)) {
return List.of();
}
CustomFunctionBlobExample example = new CustomFunctionBlobExample();
example.createCriteria()
.andIdIn(commonScriptIds);
return customFunctionBlobMapper.selectByExampleWithBLOBs(example);
}
public List<CustomFunction> getByIds(List<String> commonScriptIds) {
if (CollectionUtils.isEmpty(commonScriptIds)) {
return List.of();
}
CustomFunctionExample example = new CustomFunctionExample();
example.createCriteria()
.andIdIn(commonScriptIds);
return customFunctionMapper.selectByExample(example);
}
}

View File

@ -9,6 +9,7 @@ import io.metersphere.project.dto.customfunction.request.CustomFunctionUpdateReq
import io.metersphere.project.enums.result.ProjectResultCode;
import io.metersphere.project.mapper.CustomFunctionBlobMapper;
import io.metersphere.project.mapper.CustomFunctionMapper;
import io.metersphere.project.service.CustomFunctionService;
import io.metersphere.sdk.constants.PermissionConstants;
import io.metersphere.sdk.util.BeanUtils;
import io.metersphere.sdk.util.JSON;
@ -29,6 +30,8 @@ import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* @author: LAN
@ -54,7 +57,8 @@ public class CustomFunctionControllerTests extends BaseTest {
@Resource
private CustomFunctionBlobMapper customFunctionBlobMapper;
@Resource
private CustomFunctionService customFunctionService;
@Test
@Order(1)
@ -328,6 +332,16 @@ public class CustomFunctionControllerTests extends BaseTest {
request.setFilter(filters);
}
@Test
@Order(11)
public void testUncoveredFunc() {
// 在项目管理模块没有调用手动调用
customFunctionService.getBlobByIds(List.of());
customFunctionService.getByIds(List.of());
Assertions.assertEquals(customFunctionService.getBlobByIds(List.of(customFunction.getId())).size(), 1);
Assertions.assertEquals(customFunctionService.getByIds(List.of(customFunction.getId())).size(), 1);
}
@Test
@Order(12)
public void testDel() throws Exception {
@ -348,5 +362,4 @@ public class CustomFunctionControllerTests extends BaseTest {
requestGetPermissionTest(PermissionConstants.PROJECT_CUSTOM_FUNCTION_DELETE, DELETE + "222");
}
}

View File

@ -6,6 +6,7 @@ import io.metersphere.project.api.assertion.*;
import io.metersphere.project.api.assertion.body.*;
import io.metersphere.project.api.processor.SQLProcessor;
import io.metersphere.project.api.processor.ScriptProcessor;
import io.metersphere.project.dto.CommonScriptInfo;
import io.metersphere.project.dto.environment.*;
import io.metersphere.project.dto.environment.auth.AuthConfig;
import io.metersphere.project.dto.environment.common.CommonParams;
@ -409,7 +410,8 @@ public class EnvironmentControllerTests extends BaseTest {
assertions.add(responseTimeAssertion);
MsScriptAssertion scriptAssertion = new MsScriptAssertion();
scriptAssertion.setScriptId("1111");
scriptAssertion.setCommonScriptInfo(new CommonScriptInfo());
scriptAssertion.getCommonScriptInfo().setId("1111");
scriptAssertion.setScript("1111");
scriptAssertion.setName("1111");
assertions.add(scriptAssertion);
@ -671,7 +673,7 @@ public class EnvironmentControllerTests extends BaseTest {
//校验日志
checkLog(response.getId(), OperationLogType.ADD);
//后置脚本 todo
//后置脚本
envConfig.setPostProcessorConfig(createEnvironmentProcessorConfig());
request.setName("postScript");
request.setConfig(envConfig);