diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/dto/request/MsJMeterComponent.java b/backend/services/api-test/src/main/java/io/metersphere/api/dto/request/MsJMeterComponent.java index e7a01fef17..d1cbcb90ab 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/dto/request/MsJMeterComponent.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/dto/request/MsJMeterComponent.java @@ -7,5 +7,5 @@ import lombok.EqualsAndHashCode; @Data @EqualsAndHashCode(callSuper = true) public class MsJMeterComponent extends AbstractMsTestElement { - + private String testElementContent; } diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/dto/request/MsThreadGroup.java b/backend/services/api-test/src/main/java/io/metersphere/api/dto/request/MsThreadGroup.java new file mode 100644 index 0000000000..93d49405f0 --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/dto/request/MsThreadGroup.java @@ -0,0 +1,10 @@ +package io.metersphere.api.dto.request; + +import io.metersphere.plugin.api.spi.AbstractMsTestElement; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = true) +public class MsThreadGroup extends AbstractMsTestElement { +} diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/parser/api/dataimport/JmeterParserApiScenario.java b/backend/services/api-test/src/main/java/io/metersphere/api/parser/api/dataimport/JmeterParserApiScenario.java index 288846857c..066e81233b 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/parser/api/dataimport/JmeterParserApiScenario.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/parser/api/dataimport/JmeterParserApiScenario.java @@ -4,6 +4,7 @@ import io.metersphere.api.constants.ApiScenarioStatus; import io.metersphere.api.constants.ApiScenarioStepRefType; import io.metersphere.api.constants.ApiScenarioStepType; import io.metersphere.api.dto.request.MsJMeterComponent; +import io.metersphere.api.dto.request.MsThreadGroup; import io.metersphere.api.dto.request.controller.*; import io.metersphere.api.dto.request.http.MsHTTPElement; import io.metersphere.api.dto.scenario.ApiScenarioImportDetail; @@ -26,7 +27,10 @@ import org.apache.jorphan.collections.HashTree; import java.io.InputStream; import java.lang.reflect.Field; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; public class JmeterParserApiScenario implements ApiScenarioImportParser { @@ -39,31 +43,71 @@ public class JmeterParserApiScenario implements ApiScenarioImportParser { MsTestElementParser parser = new MsTestElementParser(); AbstractMsTestElement msTestElement = parser.parse(hashTree); Map polymorphicNameMap = parser.getPolymorphicNameMap(request.getProjectId()); - String scenarioName = StringUtils.trim(parser.parseTestPlanName(hashTree)); - return Collections.singletonList(this.parseImportFile(request.getProjectId(), msTestElement, polymorphicNameMap, scenarioName)); + return this.parseImportFile(request.getProjectId(), msTestElement, polymorphicNameMap); } catch (Exception e) { LogUtils.error(e); throw new MSException("当前JMX版本不兼容"); } } - private ApiScenarioImportDetail parseImportFile(String projectId, AbstractMsTestElement msElementList, Map polymorphicNameMap, String scenarioName) { - ApiScenarioImportDetail apiScenarioDetail = new ApiScenarioImportDetail(); - apiScenarioDetail.setName(scenarioName); - apiScenarioDetail.setPriority("P0"); - apiScenarioDetail.setStatus(ApiScenarioStatus.UNDERWAY.name()); - apiScenarioDetail.setGrouped(false); - apiScenarioDetail.setDeleted(false); - apiScenarioDetail.setLatest(true); - apiScenarioDetail.setProjectId(projectId); + private List parseImportFile(String projectId, AbstractMsTestElement msElementList, Map polymorphicNameMap) { + List scenarioTestElementList = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(msElementList.getChildren())) { + for (AbstractMsTestElement msTestElement : msElementList.getChildren()) { + if (msTestElement instanceof MsThreadGroup) { + scenarioTestElementList.add(msTestElement); + } + } + } - ApiScenarioStepParseResult stepParseResult = this.parseScenarioStep(msElementList.getChildren(), projectId, polymorphicNameMap); + if (CollectionUtils.isEmpty(scenarioTestElementList)) { + // 无法分辨ThreadGroup,当做一个场景来处理 + scenarioTestElementList.add(msElementList); + } - apiScenarioDetail.setSteps(stepParseResult.getStepList()); - apiScenarioDetail.setStepDetails(stepParseResult.getStepDetails()); + List importList = new ArrayList<>(); + for (AbstractMsTestElement msTestElement : scenarioTestElementList) { + ApiScenarioImportDetail apiScenarioDetail = new ApiScenarioImportDetail(); + apiScenarioDetail.setName(StringUtils.trim(msTestElement.getName())); + apiScenarioDetail.setPriority("P0"); + apiScenarioDetail.setStatus(ApiScenarioStatus.UNDERWAY.name()); + apiScenarioDetail.setGrouped(false); + apiScenarioDetail.setDeleted(false); + apiScenarioDetail.setLatest(true); + apiScenarioDetail.setProjectId(projectId); + ApiScenarioStepParseResult stepParseResult = this.parseScenarioStep(msTestElement.getChildren(), projectId, polymorphicNameMap); + apiScenarioDetail.setSteps(stepParseResult.getStepList()); + apiScenarioDetail.setStepDetails(stepParseResult.getStepDetails()); + apiScenarioDetail.setStepTotal(CollectionUtils.size(apiScenarioDetail.getSteps())); - apiScenarioDetail.setStepTotal(CollectionUtils.size(apiScenarioDetail.getSteps())); - return apiScenarioDetail; + importList.add(apiScenarioDetail); + } + importList = this.apiScenarioRename(importList); + return importList; + } + + public List apiScenarioRename(List scenarioList) { + List returnList = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(scenarioList)) { + List nameList = new ArrayList<>(); + for (ApiScenarioImportDetail scenario : scenarioList) { + String uniqueName = getUniqueName(scenario.getName(), nameList); + scenario.setName(uniqueName); + nameList.add(uniqueName); + returnList.add(scenario); + } + } + return returnList; + } + + private String getUniqueName(String originalName, List existenceNameList) { + String returnName = originalName; + int index = 1; + while (existenceNameList.contains(returnName)) { + returnName = originalName + " - " + index; + index++; + } + return returnName; } private ApiScenarioStepParseResult parseScenarioStep(List msElementList, String projectId, Map polymorphicNameMap) { @@ -94,6 +138,7 @@ public class JmeterParserApiScenario implements ApiScenarioImportParser { apiScenarioStep.setStepType(this.getStepType(msTestElement)); apiScenarioStep.setConfig(new HashMap<>()); apiScenarioStep.setRefType(ApiScenarioStepRefType.DIRECT.name()); + stepBlobContent = JSON.toJSONString(msTestElement).getBytes(); } else { apiScenarioStep.setStepType(this.getStepType(msTestElement)); apiScenarioStep.setConfig(JSON.toJSONString(msTestElement)); diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/JmeterElementConverterRegister.java b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/JmeterElementConverterRegister.java index e51162cb14..a8eae4fe13 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/JmeterElementConverterRegister.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/JmeterElementConverterRegister.java @@ -42,6 +42,7 @@ public class JmeterElementConverterRegister { register(MsLoopControllerConverter.class); register(MsOnceOnlyControllerConverter.class); register(MsConstantTimerControllerConverter.class); + register(MsJMeterComponentConverter.class); // 注册转换器拦截器 AbstractJmeterElementConverter.registerConvertInterceptor(new RetryInterceptor()); diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/MsJMeterComponentConverter.java b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/MsJMeterComponentConverter.java new file mode 100644 index 0000000000..edaac472e9 --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/MsJMeterComponentConverter.java @@ -0,0 +1,52 @@ +package io.metersphere.api.parser.jmeter; + + +import io.metersphere.api.dto.request.MsJMeterComponent; +import io.metersphere.plugin.api.dto.ParameterConfig; +import io.metersphere.plugin.api.spi.AbstractJmeterElementConverter; +import org.apache.commons.lang3.StringUtils; +import org.apache.jmeter.save.SaveService; +import org.apache.jmeter.testelement.TestElement; +import org.apache.jorphan.collections.HashTree; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; + +/** + * @Author: jianxing + * @CreateTime: 2023-10-27 10:07 + *

+ * 脚本解析器 + */ +public class MsJMeterComponentConverter extends AbstractJmeterElementConverter { + + @Override + public void toHashTree(HashTree tree, MsJMeterComponent jMeterComponent, ParameterConfig msParameter) { + HashTree elementTree = null; + try (InputStream inputSource = getStrToStream(jMeterComponent.getTestElementContent())) { + if (inputSource != null) { + Object scriptWrapper = SaveService.loadElement(inputSource); + if (scriptWrapper instanceof TestElement) { + ((TestElement) scriptWrapper).setName(jMeterComponent.getName()); + ((TestElement) scriptWrapper).setEnabled(jMeterComponent.getEnable()); + } + elementTree = tree.add(scriptWrapper); + } + } catch (Exception ignore) { + } + if (elementTree != null) { + parseChild(elementTree, jMeterComponent, msParameter); + } else { + parseChild(tree, jMeterComponent, msParameter); + } + + } + + public static InputStream getStrToStream(String sInputString) { + if (StringUtils.isNotEmpty(sInputString)) { + return new ByteArrayInputStream(sInputString.getBytes()); + } + return null; + } + +} diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/parser/ms/DebugSampleConverter.java b/backend/services/api-test/src/main/java/io/metersphere/api/parser/ms/DebugSampleConverter.java new file mode 100644 index 0000000000..0179bd5794 --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/parser/ms/DebugSampleConverter.java @@ -0,0 +1,20 @@ +package io.metersphere.api.parser.ms; + + +import io.metersphere.plugin.api.spi.AbstractMsElementConverter; +import io.metersphere.plugin.api.spi.AbstractMsTestElement; +import org.apache.jmeter.sampler.DebugSampler; +import org.apache.jorphan.collections.HashTree; + +/** + * @Author: jianxing + * @CreateTime: 2023-10-27 10:07 + *

+ * 脚本解析器 + */ +public class DebugSampleConverter extends AbstractMsElementConverter { + @Override + public void toMsElement(AbstractMsTestElement parent, DebugSampler element, HashTree hashTree) { + // debug不做处理 + } +} diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/parser/ms/JmeterGeneralElementConverter.java b/backend/services/api-test/src/main/java/io/metersphere/api/parser/ms/JmeterGeneralElementConverter.java index 32a52811af..0216684348 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/parser/ms/JmeterGeneralElementConverter.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/parser/ms/JmeterGeneralElementConverter.java @@ -1,19 +1,30 @@ package io.metersphere.api.parser.ms; +import io.metersphere.api.dto.request.MsJMeterComponent; import io.metersphere.plugin.api.spi.AbstractMsElementConverter; +import io.metersphere.plugin.api.spi.AbstractMsProtocolTestElement; import io.metersphere.plugin.api.spi.AbstractMsTestElement; +import org.apache.jmeter.save.SaveService; import org.apache.jmeter.testelement.TestElement; import org.apache.jorphan.collections.HashTree; -/** - * @Author: jianxing - * @CreateTime: 2024-08-28 13:57 - * 作为通用的解析器,如果遇到不支持的组件,会使用这个解析器 - */ +import java.io.ByteArrayOutputStream; + public class JmeterGeneralElementConverter extends AbstractMsElementConverter { @Override public void toMsElement(AbstractMsTestElement parent, TestElement element, HashTree hashTree) { - // 不做任何处理,直接解析子元素 - parseChild(parent, element, hashTree); + if (parent instanceof AbstractMsProtocolTestElement) { + // 在请求类型的组件下,如果存在未知子组件,暂时不解析 + return; + } + MsJMeterComponent msJMeterComponent = new MsJMeterComponent(); + msJMeterComponent.setName(element.getName()); + try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { + SaveService.saveElement(element, baos); + msJMeterComponent.setTestElementContent(baos.toString()); + } catch (Exception ignore) { + } + parent.getChildren().add(msJMeterComponent); + parseChild(msJMeterComponent, element, hashTree); } } diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/parser/ms/MsElementConverterRegister.java b/backend/services/api-test/src/main/java/io/metersphere/api/parser/ms/MsElementConverterRegister.java index 66109add22..bd1a1b80ab 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/parser/ms/MsElementConverterRegister.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/parser/ms/MsElementConverterRegister.java @@ -12,6 +12,7 @@ import org.apache.jmeter.testelement.TestElement; import java.lang.reflect.InvocationTargetException; import java.util.HashMap; import java.util.Map; +import java.util.Objects; /** * @Author: jianxing @@ -33,7 +34,6 @@ public class MsElementConverterRegister { // 注册默认的转换器 register(TestPlanConverter.class); - register(ThreadGroupConverter.class); register(HTTPSamplerConverter.class); register(HeaderManagerConverter.class); @@ -47,10 +47,14 @@ public class MsElementConverterRegister { register(XPathExtractorConverter.class); register(JSONPathAssertionConverter.class); register(XPathAssertionConverter.class); + register(ThreadGroupConverter.class); register(BeanShellPreProcessConverter.class); register(JDBCPreProcessConverter.class); register(JSR223PreProcessConverter.class); + + register(ResultCollectorConverter.class); + register(DebugSampleConverter.class); } /** @@ -86,11 +90,8 @@ public class MsElementConverterRegister { */ public static AbstractMsElementConverter getConverter(Class TestElementClass) { AbstractMsElementConverter converter = parserMap.get(TestElementClass); - if (converter == null) { - // 如果没有对应的转换器,则使用通用的转换器 - return generalElementConverter; - } - return converter; + // 如果没有对应的转换器,则使用通用的转换器 + return Objects.requireNonNullElse(converter, generalElementConverter); } /** diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/parser/ms/ResultCollectorConverter.java b/backend/services/api-test/src/main/java/io/metersphere/api/parser/ms/ResultCollectorConverter.java new file mode 100644 index 0000000000..2c623b7e96 --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/parser/ms/ResultCollectorConverter.java @@ -0,0 +1,20 @@ +package io.metersphere.api.parser.ms; + + +import io.metersphere.plugin.api.spi.AbstractMsElementConverter; +import io.metersphere.plugin.api.spi.AbstractMsTestElement; +import org.apache.jmeter.reporters.ResultCollector; +import org.apache.jorphan.collections.HashTree; + +/** + * @Author: jianxing + * @CreateTime: 2023-10-27 10:07 + *

+ * 脚本解析器 + */ +public class ResultCollectorConverter extends AbstractMsElementConverter { + @Override + public void toMsElement(AbstractMsTestElement parent, ResultCollector element, HashTree hashTree) { + // resultController不做处理 + } +} diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/parser/ms/ThreadGroupConverter.java b/backend/services/api-test/src/main/java/io/metersphere/api/parser/ms/ThreadGroupConverter.java index 92ea43f714..ffb4e9a500 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/parser/ms/ThreadGroupConverter.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/parser/ms/ThreadGroupConverter.java @@ -1,7 +1,7 @@ package io.metersphere.api.parser.ms; -import io.metersphere.api.dto.request.MsJMeterComponent; +import io.metersphere.api.dto.request.MsThreadGroup; import io.metersphere.plugin.api.spi.AbstractMsElementConverter; import io.metersphere.plugin.api.spi.AbstractMsTestElement; import org.apache.jmeter.threads.ThreadGroup; @@ -16,7 +16,7 @@ import org.apache.jorphan.collections.HashTree; public class ThreadGroupConverter extends AbstractMsElementConverter { @Override public void toMsElement(AbstractMsTestElement parent, ThreadGroup element, HashTree hashTree) { - MsJMeterComponent msJMeterComponent = new MsJMeterComponent(); + MsThreadGroup msJMeterComponent = new MsThreadGroup(); msJMeterComponent.setName(element.getName()); parent.getChildren().add(msJMeterComponent); parseChild(msJMeterComponent, element, hashTree); diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/parser/step/JMeterComponentStepParser.java b/backend/services/api-test/src/main/java/io/metersphere/api/parser/step/JMeterComponentStepParser.java new file mode 100644 index 0000000000..0403166d3d --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/parser/step/JMeterComponentStepParser.java @@ -0,0 +1,19 @@ +package io.metersphere.api.parser.step; + +import io.metersphere.api.domain.ApiScenarioStep; +import io.metersphere.api.dto.request.MsJMeterComponent; +import io.metersphere.api.dto.scenario.ApiScenarioStepCommonDTO; +import io.metersphere.api.utils.ApiDataUtils; +import io.metersphere.plugin.api.spi.AbstractMsTestElement; + +public class JMeterComponentStepParser extends StepParser { + @Override + public AbstractMsTestElement parseTestElement(ApiScenarioStepCommonDTO step, String resourceBlob, String stepDetail) { + return ApiDataUtils.parseObject(stepDetail, MsJMeterComponent.class); + } + + @Override + public Object parseDetail(ApiScenarioStep step) { + return null; + } +} diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/parser/step/StepParserFactory.java b/backend/services/api-test/src/main/java/io/metersphere/api/parser/step/StepParserFactory.java index 07222b2820..7b36b52e09 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/parser/step/StepParserFactory.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/parser/step/StepParserFactory.java @@ -22,6 +22,8 @@ public abstract class StepParserFactory { stepParserMap.put(ApiScenarioStepType.LOOP_CONTROLLER.name(), new LoopControllerStepParser()); stepParserMap.put(ApiScenarioStepType.ONCE_ONLY_CONTROLLER.name(), new OnceOnlyControllerStepParser()); stepParserMap.put(ApiScenarioStepType.IF_CONTROLLER.name(), new IfControllerStepParser()); + stepParserMap.put(ApiScenarioStepType.JMETER_COMPONENT.name(), new JMeterComponentStepParser()); + } public static StepParser getStepParser(String stepType) { diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiScenarioDataTransferService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiScenarioDataTransferService.java index bc37af9237..626976baad 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiScenarioDataTransferService.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiScenarioDataTransferService.java @@ -518,7 +518,7 @@ public class ApiScenarioDataTransferService { } if (modulePathMap.containsKey(modulePath)) { List existenceScenarios = extApiScenarioMapper.selectBaseInfoByModuleId(modulePathMap.get(modulePath).getId()); - Map existenceNameIdMap = existenceScenarios.stream().collect(Collectors.toMap(ApiScenario::getName, ApiScenario::getId)); + Map existenceNameIdMap = existenceScenarios.stream().collect(Collectors.toMap(ApiScenario::getName, ApiScenario::getId, (k1, k2) -> k1)); String finalModulePath = modulePath; scenarios.forEach(scenario -> { if (existenceNameIdMap.containsKey(scenario.getName())) { diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/utils/ApiDataUtils.java b/backend/services/api-test/src/main/java/io/metersphere/api/utils/ApiDataUtils.java index a2b70b5a78..98f88bfc9c 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/utils/ApiDataUtils.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/utils/ApiDataUtils.java @@ -13,6 +13,7 @@ import com.fasterxml.jackson.databind.jsontype.NamedType; import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.type.CollectionType; import io.metersphere.api.dto.request.MsCommonElement; +import io.metersphere.api.dto.request.MsJMeterComponent; import io.metersphere.api.dto.request.controller.*; import io.metersphere.api.dto.request.http.MsHTTPElement; import io.metersphere.sdk.exception.MSException; @@ -42,6 +43,7 @@ public class ApiDataUtils { namedTypes.add(new NamedType(MsOnceOnlyController.class, MsOnceOnlyController.class.getSimpleName())); namedTypes.add(new NamedType(MsConstantTimerController.class, MsConstantTimerController.class.getSimpleName())); namedTypes.add(new NamedType(MsScriptElement.class, MsScriptElement.class.getSimpleName())); + namedTypes.add(new NamedType(MsJMeterComponent.class, MsJMeterComponent.class.getSimpleName())); setObjectMapper(objectMapper); namedTypes.forEach(objectMapper::registerSubtypes); }