diff --git a/backend/framework/plugin/plugin-api-sdk/src/main/java/io/metersphere/plugin/api/spi/AbstractJmeterElementConverter.java b/backend/framework/plugin/plugin-api-sdk/src/main/java/io/metersphere/plugin/api/spi/AbstractJmeterElementConverter.java index e6aa2fa8ba..64f57c91cd 100644 --- a/backend/framework/plugin/plugin-api-sdk/src/main/java/io/metersphere/plugin/api/spi/AbstractJmeterElementConverter.java +++ b/backend/framework/plugin/plugin-api-sdk/src/main/java/io/metersphere/plugin/api/spi/AbstractJmeterElementConverter.java @@ -18,7 +18,7 @@ import java.util.function.Function; * @createTime 2021-10-30 10:07 * 将 MsTestElement 具体实现类转换为 HashTree */ -public abstract class AbstractJmeterElementConverter implements JmeterElementConverter { +public abstract class AbstractJmeterElementConverter implements JmeterElementConverter { public Class testElementClass; @@ -60,11 +60,6 @@ public abstract class AbstractJmeterElementConverter im } } - /** - * 将 MsTestElement 具体实现类转换为 HashTree - */ - public abstract void toHashTree(HashTree tree, T element, ParameterConfig config); - /** * 解析 MsTestElement 的子节点 */ diff --git a/backend/framework/plugin/plugin-api-sdk/src/main/java/io/metersphere/plugin/api/spi/AbstractMsElementConverter.java b/backend/framework/plugin/plugin-api-sdk/src/main/java/io/metersphere/plugin/api/spi/AbstractMsElementConverter.java new file mode 100644 index 0000000000..accd26b234 --- /dev/null +++ b/backend/framework/plugin/plugin-api-sdk/src/main/java/io/metersphere/plugin/api/spi/AbstractMsElementConverter.java @@ -0,0 +1,49 @@ +package io.metersphere.plugin.api.spi; + + +import lombok.Setter; +import org.apache.jmeter.testelement.TestElement; +import org.apache.jorphan.collections.HashTree; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.function.Function; + +/** + * @author jianxing + * @createTime 2021-10-30 10:07 + * 将 Jmeter 的 TestElement 转换成 MsTestElement + */ +public abstract class AbstractMsElementConverter implements MsElementConverter { + + public Class testElementClass; + + /** + * 获取转换器的函数 + */ + @Setter + private static Function, AbstractMsElementConverter> getConverterFunc; + + public AbstractMsElementConverter() { + Type genericSuperclass = getClass().getGenericSuperclass(); + if (genericSuperclass instanceof ParameterizedType parameterizedType) { + // 获取泛型的具体类型,即 MsTestElement 的具体实现类 + testElementClass = ((Class) parameterizedType.getActualTypeArguments()[0]); + } + } + + /** + * 解析 MsTestElement 的子节点 + */ + public void parseChild(AbstractMsTestElement parentMsElement, TestElement parentElement, HashTree hashTree) { + HashTree currentHashtree = hashTree.get(parentElement); + if (currentHashtree == null) { + return; + } + for (Object key : currentHashtree.keySet()) { + if (key instanceof TestElement testElement) { + getConverterFunc.apply(testElement.getClass()).toMsElement(parentMsElement, testElement, currentHashtree); + } + } + } +} diff --git a/backend/framework/plugin/plugin-api-sdk/src/main/java/io/metersphere/plugin/api/spi/AbstractMsTestElement.java b/backend/framework/plugin/plugin-api-sdk/src/main/java/io/metersphere/plugin/api/spi/AbstractMsTestElement.java index 310f08ab7d..4c379a2176 100644 --- a/backend/framework/plugin/plugin-api-sdk/src/main/java/io/metersphere/plugin/api/spi/AbstractMsTestElement.java +++ b/backend/framework/plugin/plugin-api-sdk/src/main/java/io/metersphere/plugin/api/spi/AbstractMsTestElement.java @@ -46,7 +46,7 @@ public abstract class AbstractMsTestElement implements MsTestElement { /** * 子组件 */ - private LinkedList children; + private LinkedList children = new LinkedList<>(); /** * 父组件 */ diff --git a/backend/framework/plugin/plugin-api-sdk/src/main/java/io/metersphere/plugin/api/spi/JmeterElementConverter.java b/backend/framework/plugin/plugin-api-sdk/src/main/java/io/metersphere/plugin/api/spi/JmeterElementConverter.java index 99ad794026..aeebafdfb3 100644 --- a/backend/framework/plugin/plugin-api-sdk/src/main/java/io/metersphere/plugin/api/spi/JmeterElementConverter.java +++ b/backend/framework/plugin/plugin-api-sdk/src/main/java/io/metersphere/plugin/api/spi/JmeterElementConverter.java @@ -1,6 +1,8 @@ package io.metersphere.plugin.api.spi; +import io.metersphere.plugin.api.dto.ParameterConfig; +import org.apache.jorphan.collections.HashTree; import org.pf4j.ExtensionPoint; /** @@ -8,5 +10,10 @@ import org.pf4j.ExtensionPoint; * @createTime 2021-10-30 10:07 * 将 MsTestElement 具体实现类转换为 HashTree */ -public interface JmeterElementConverter extends ExtensionPoint { +public interface JmeterElementConverter extends ExtensionPoint { + + /** + * 将 MsTestElement 具体实现类转换为 HashTree + */ + void toHashTree(HashTree tree, T element, ParameterConfig config); } diff --git a/backend/framework/plugin/plugin-api-sdk/src/main/java/io/metersphere/plugin/api/spi/MsElementConverter.java b/backend/framework/plugin/plugin-api-sdk/src/main/java/io/metersphere/plugin/api/spi/MsElementConverter.java new file mode 100644 index 0000000000..c34985c830 --- /dev/null +++ b/backend/framework/plugin/plugin-api-sdk/src/main/java/io/metersphere/plugin/api/spi/MsElementConverter.java @@ -0,0 +1,19 @@ +package io.metersphere.plugin.api.spi; + + +import org.apache.jmeter.testelement.TestElement; +import org.apache.jorphan.collections.HashTree; +import org.pf4j.ExtensionPoint; + +/** + * @author jianxing + * @createTime 2021-10-30 10:07 + * 将 MsTestElement 具体实现类转换为 HashTree + */ +public interface MsElementConverter extends ExtensionPoint { + + /** + * 将 MsTestElement 具体实现类转换为 HashTree + */ + void toMsElement(AbstractMsTestElement parent, T element, HashTree hashTree); +} diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/utils/JmeterElementConverterRegister.java b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/JmeterElementConverterRegister.java similarity index 92% rename from backend/services/api-test/src/main/java/io/metersphere/api/utils/JmeterElementConverterRegister.java rename to backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/JmeterElementConverterRegister.java index 7681e7728d..1de3a5bb54 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/utils/JmeterElementConverterRegister.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/JmeterElementConverterRegister.java @@ -1,9 +1,5 @@ -package io.metersphere.api.utils; +package io.metersphere.api.parser.jmeter; -import io.metersphere.api.parser.jmeter.MsCommonElementConverter; -import io.metersphere.api.parser.jmeter.MsHTTPElementConverter; -import io.metersphere.api.parser.jmeter.MsScenarioConverter; -import io.metersphere.api.parser.jmeter.MsScriptElementConverter; import io.metersphere.api.parser.jmeter.child.MsCsvChildPreConverter; import io.metersphere.api.parser.jmeter.controller.MsConstantTimerControllerConverter; import io.metersphere.api.parser.jmeter.controller.MsIfControllerConverter; diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/JmeterTestElementParser.java b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/JmeterTestElementParser.java index 786d4dff38..266c27f081 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/JmeterTestElementParser.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/parser/jmeter/JmeterTestElementParser.java @@ -4,7 +4,6 @@ import io.metersphere.api.dto.ApiParamConfig; import io.metersphere.api.dto.request.MsScenario; import io.metersphere.api.dto.scenario.ScenarioOtherConfig; import io.metersphere.api.parser.TestElementParser; -import io.metersphere.api.utils.JmeterElementConverterRegister; import io.metersphere.plugin.api.dto.ParameterConfig; import io.metersphere.plugin.api.spi.AbstractJmeterElementConverter; import io.metersphere.plugin.api.spi.AbstractMsProtocolTestElement; diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/parser/ms/HTTPSamplerConverter.java b/backend/services/api-test/src/main/java/io/metersphere/api/parser/ms/HTTPSamplerConverter.java new file mode 100644 index 0000000000..eb74231d3c --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/parser/ms/HTTPSamplerConverter.java @@ -0,0 +1,21 @@ +package io.metersphere.api.parser.ms; + +import io.metersphere.api.dto.request.http.MsHTTPElement; +import io.metersphere.plugin.api.spi.AbstractMsElementConverter; +import io.metersphere.plugin.api.spi.AbstractMsTestElement; +import org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy; +import org.apache.jorphan.collections.HashTree; + +/** + * @Author: jianxing + * @CreateTime: 2024-08-28 11:36 + */ +public class HTTPSamplerConverter extends AbstractMsElementConverter { + @Override + public void toMsElement(AbstractMsTestElement parent, HTTPSamplerProxy httpSampler, HashTree hashTree) { + MsHTTPElement msHTTPElement = new MsHTTPElement(); + // todo 解析HTTP请求 + parent.getChildren().add(msHTTPElement); + parseChild(msHTTPElement, httpSampler, hashTree); + } +} 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 new file mode 100644 index 0000000000..32a52811af --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/parser/ms/JmeterGeneralElementConverter.java @@ -0,0 +1,19 @@ +package io.metersphere.api.parser.ms; + +import io.metersphere.plugin.api.spi.AbstractMsElementConverter; +import io.metersphere.plugin.api.spi.AbstractMsTestElement; +import org.apache.jmeter.testelement.TestElement; +import org.apache.jorphan.collections.HashTree; + +/** + * @Author: jianxing + * @CreateTime: 2024-08-28 13:57 + * 作为通用的解析器,如果遇到不支持的组件,会使用这个解析器 + */ +public class JmeterGeneralElementConverter extends AbstractMsElementConverter { + @Override + public void toMsElement(AbstractMsTestElement parent, TestElement element, HashTree hashTree) { + // 不做任何处理,直接解析子元素 + parseChild(parent, 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 new file mode 100644 index 0000000000..f0ad85b50e --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/parser/ms/MsElementConverterRegister.java @@ -0,0 +1,76 @@ +package io.metersphere.api.parser.ms; + +import io.metersphere.plugin.api.spi.AbstractMsElementConverter; +import io.metersphere.plugin.sdk.util.PluginLogUtils; +import org.apache.jmeter.testelement.TestElement; + +import java.lang.reflect.InvocationTargetException; +import java.util.HashMap; +import java.util.Map; + +/** + * @Author: jianxing + * @CreateTime: 2023-11-03 17:14 + */ +public class MsElementConverterRegister { + + /** + * 解析器集合 + * key 为 TestElement 实现类的 Class + * value 为对应的转换器 + */ + private static final Map, AbstractMsElementConverter> parserMap = new HashMap<>(); + private static final JmeterGeneralElementConverter generalElementConverter = new JmeterGeneralElementConverter(); + + static { + // 设置获取转换器的方法 + AbstractMsElementConverter.setGetConverterFunc(MsElementConverterRegister::getConverter); + + // 注册默认的转换器 + register(TestPlanConverter.class); + register(ThreadGroupConverter.class); + register(HTTPSamplerConverter.class); + } + + /** + * 注册 TestElement 对应的转换器 + * + * @param elementConverterClass 转换器的类 + */ + public static void register(Class> elementConverterClass) { + try { + AbstractMsElementConverter elementConverter = elementConverterClass.getDeclaredConstructor().newInstance(); + // 注册到解析器集合中 + parserMap.put(elementConverter.testElementClass, elementConverter); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | + NoSuchMethodException e) { + handleRegistrationException(elementConverterClass, e); + } + } + + /** + * 获取对应组件的转换器 + * + * @param TestElementClass 组件的类 + * @return 转换器 + */ + public static AbstractMsElementConverter getConverter(Class TestElementClass) { + AbstractMsElementConverter converter = parserMap.get(TestElementClass); + if (converter == null) { + // 如果没有对应的转换器,则使用通用的转换器 + return generalElementConverter; + } + return converter; + } + + /** + * 处理注册转换器时的异常 + * + * @param elementConverterClass 转换器的类 + * @param e 异常 + */ + private static void handleRegistrationException(Class elementConverterClass, Exception e) { + PluginLogUtils.error("注册转换器失败: " + elementConverterClass, e); + } +} + diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/parser/ms/MsTestElementParser.java b/backend/services/api-test/src/main/java/io/metersphere/api/parser/ms/MsTestElementParser.java new file mode 100644 index 0000000000..783c45c4da --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/parser/ms/MsTestElementParser.java @@ -0,0 +1,25 @@ +package io.metersphere.api.parser.ms; + +import io.metersphere.api.dto.request.MsScenario; +import io.metersphere.plugin.api.spi.AbstractMsTestElement; +import org.apache.jmeter.testelement.TestElement; +import org.apache.jorphan.collections.HashTree; + +/** + * @Author: jianxing + * @CreateTime: 2023-10-27 10:07 + *

+ * 将 hashTree 转换为 MsTestElement + */ +public class MsTestElementParser { + + public AbstractMsTestElement parse(HashTree hashTree) { + MsScenario msScenario = new MsScenario(); + for (Object key : hashTree.keySet()) { + if (key instanceof TestElement testElement) { + MsElementConverterRegister.getConverter(testElement.getClass()).toMsElement(msScenario, testElement, hashTree); + } + } + return msScenario; + } +} diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/parser/ms/TestPlanConverter.java b/backend/services/api-test/src/main/java/io/metersphere/api/parser/ms/TestPlanConverter.java new file mode 100644 index 0000000000..d6e6d1d08f --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/parser/ms/TestPlanConverter.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.testelement.TestPlan; +import org.apache.jorphan.collections.HashTree; + +/** + * @Author: jianxing + * @CreateTime: 2023-10-27 10:07 + *

+ * 脚本解析器 + */ +public class TestPlanConverter extends AbstractMsElementConverter { + @Override + public void toMsElement(AbstractMsTestElement parent, TestPlan element, HashTree hashTree) { + // 测试计划不做任何处理,直接解析子元素 + parseChild(parent, element, hashTree); + } +} 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 new file mode 100644 index 0000000000..c9f8f4ac49 --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/parser/ms/ThreadGroupConverter.java @@ -0,0 +1,24 @@ +package io.metersphere.api.parser.ms; + + +import io.metersphere.api.dto.request.MsScenario; +import io.metersphere.plugin.api.spi.AbstractMsElementConverter; +import io.metersphere.plugin.api.spi.AbstractMsTestElement; +import org.apache.jmeter.threads.ThreadGroup; +import org.apache.jorphan.collections.HashTree; + +/** + * @Author: jianxing + * @CreateTime: 2023-10-27 10:07 + *

+ * 脚本解析器 + */ +public class ThreadGroupConverter extends AbstractMsElementConverter { + @Override + public void toMsElement(AbstractMsTestElement parent, ThreadGroup element, HashTree hashTree) { + MsScenario msScenario = new MsScenario(); + // todo 解析线程组 + parent.getChildren().add(msScenario); + parseChild(msScenario, element, hashTree); + } +} diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiPluginChangeService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiPluginChangeService.java index 0c4ae386b8..cd98fa963c 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiPluginChangeService.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiPluginChangeService.java @@ -1,7 +1,7 @@ package io.metersphere.api.service; import io.metersphere.api.utils.ApiDataUtils; -import io.metersphere.api.utils.JmeterElementConverterRegister; +import io.metersphere.api.parser.jmeter.JmeterElementConverterRegister; import io.metersphere.plugin.api.spi.AbstractApiPlugin; import io.metersphere.plugin.api.spi.AbstractJmeterElementConverter; import io.metersphere.plugin.api.spi.JmeterElementConverter; diff --git a/backend/services/api-test/src/test/java/io/metersphere/api/utils/MsTestElementParserTest.java b/backend/services/api-test/src/test/java/io/metersphere/api/utils/MsTestElementParserTest.java new file mode 100644 index 0000000000..531972179d --- /dev/null +++ b/backend/services/api-test/src/test/java/io/metersphere/api/utils/MsTestElementParserTest.java @@ -0,0 +1,46 @@ +package io.metersphere.api.utils; + +import io.metersphere.api.parser.ms.MsTestElementParser; +import io.metersphere.plugin.api.spi.MsTestElement; +import org.apache.jmeter.save.SaveService; +import org.apache.jorphan.collections.HashTree; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.util.ReflectionUtils; + +import java.io.File; +import java.io.FileInputStream; +import java.lang.reflect.Field; + +/** + * @Author: jianxing + * @CreateTime: 2024-08-28 11:46 + */ +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +@AutoConfigureMockMvc +public class MsTestElementParserTest { + + @Test + public void testParse() throws Exception { + File httpJmx = new File( + this.getClass().getClassLoader().getResource("file/http.jmx") + .getPath() + ); + Object scriptWrapper = SaveService.loadElement(new FileInputStream(httpJmx)); + HashTree hashTree = getHashTree(scriptWrapper); + MsTestElementParser parser = new MsTestElementParser(); + MsTestElement msTestElement = parser.parse(hashTree); + Assertions.assertNotNull(msTestElement); + } + + private HashTree getHashTree(Object scriptWrapper) throws Exception { + Field field = scriptWrapper.getClass().getDeclaredField("testPlan"); + ReflectionUtils.makeAccessible(field); + return (HashTree) field.get(scriptWrapper); + } +} diff --git a/backend/services/api-test/src/test/resources/file/http.jmx b/backend/services/api-test/src/test/resources/file/http.jmx new file mode 100644 index 0000000000..9f14ecc23a --- /dev/null +++ b/backend/services/api-test/src/test/resources/file/http.jmx @@ -0,0 +1,76 @@ + + + + + false + false + true + + + + + + + 1 + 1 + 0 + 0 + continue + false + + 1 + false + + + + + true + + testVar + + + + 2024-08-22 16:15:34 + + + + + + + + + 2138842179805184 + 2138842179805184 + 172481082966700000 + 100001100001 + GET + https://demo.halo.run/dddfsfs + 60000 + 60000 + true + false + + + + + + + demo + P@ssw0rd123.. + + + + + + + + + false + true + false + + + + + + \ No newline at end of file