From 8f9286252c22570c12072b9421e668d09f041dfc Mon Sep 17 00:00:00 2001 From: fit2-zhao Date: Fri, 19 Feb 2021 15:07:19 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E6=8E=A5=E5=8F=A3=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E5=8C=96):=20=E5=AF=BC=E5=85=A5JMX=20JDBC=E7=B1=BB=E5=9E=8B=20?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E6=BA=90=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/dto/automation/ImportPoolsDTO.java | 20 +++ .../dto/automation/parse/MsJmeterParser.java | 140 ++++++++++++++++-- .../dto/definition/request/MsTestElement.java | 6 +- .../request/sampler/MsHTTPSamplerProxy.java | 1 + .../request/unknown/MsJmeterElement.java | 50 ++++++- .../api/service/ApiAutomationService.java | 1 + .../service/ApiTestEnvironmentService.java | 5 + .../api/automation/scenario/Setting.js | 1 + 8 files changed, 207 insertions(+), 17 deletions(-) create mode 100644 backend/src/main/java/io/metersphere/api/dto/automation/ImportPoolsDTO.java diff --git a/backend/src/main/java/io/metersphere/api/dto/automation/ImportPoolsDTO.java b/backend/src/main/java/io/metersphere/api/dto/automation/ImportPoolsDTO.java new file mode 100644 index 0000000000..d5067a539b --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/automation/ImportPoolsDTO.java @@ -0,0 +1,20 @@ +package io.metersphere.api.dto.automation; + +import io.metersphere.api.dto.scenario.DatabaseConfig; +import io.metersphere.base.domain.ApiTestEnvironmentWithBLOBs; +import lombok.Getter; +import lombok.Setter; + +import java.util.Map; + +@Getter +@Setter +public class ImportPoolsDTO { + private String envId; + + private Boolean isCreate; + + private ApiTestEnvironmentWithBLOBs testEnvironmentWithBLOBs; + + private Map dataSources; +} diff --git a/backend/src/main/java/io/metersphere/api/dto/automation/parse/MsJmeterParser.java b/backend/src/main/java/io/metersphere/api/dto/automation/parse/MsJmeterParser.java index 5f6b685104..0e49a1e192 100644 --- a/backend/src/main/java/io/metersphere/api/dto/automation/parse/MsJmeterParser.java +++ b/backend/src/main/java/io/metersphere/api/dto/automation/parse/MsJmeterParser.java @@ -1,10 +1,12 @@ package io.metersphere.api.dto.automation.parse; import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; import io.github.ningyu.jmeter.plugin.dubbo.sample.DubboSample; import io.github.ningyu.jmeter.plugin.dubbo.sample.MethodArgument; import io.github.ningyu.jmeter.plugin.util.Constants; import io.metersphere.api.dto.ApiTestImportRequest; +import io.metersphere.api.dto.automation.ImportPoolsDTO; import io.metersphere.api.dto.definition.request.MsScenario; import io.metersphere.api.dto.definition.request.MsTestElement; import io.metersphere.api.dto.definition.request.assertions.*; @@ -30,13 +32,20 @@ import io.metersphere.api.dto.definition.request.sampler.dubbo.MsRegistryCenter; import io.metersphere.api.dto.definition.request.timer.MsConstantTimer; import io.metersphere.api.dto.definition.request.unknown.MsJmeterElement; import io.metersphere.api.dto.scenario.Body; +import io.metersphere.api.dto.scenario.DatabaseConfig; import io.metersphere.api.dto.scenario.KeyValue; +import io.metersphere.api.dto.scenario.environment.EnvironmentConfig; import io.metersphere.api.dto.scenario.request.BodyFile; import io.metersphere.api.dto.scenario.request.RequestType; +import io.metersphere.api.service.ApiTestEnvironmentService; import io.metersphere.base.domain.ApiScenarioModule; import io.metersphere.base.domain.ApiScenarioWithBLOBs; +import io.metersphere.base.domain.ApiTestEnvironmentExample; +import io.metersphere.base.domain.ApiTestEnvironmentWithBLOBs; import io.metersphere.commons.constants.LoopConstants; import io.metersphere.commons.utils.BeanUtils; +import io.metersphere.commons.utils.CommonBeanFactory; +import io.metersphere.commons.utils.LogUtil; import org.apache.commons.collections.CollectionUtils; import org.apache.jmeter.assertions.*; import org.apache.jmeter.config.ConfigTestElement; @@ -52,6 +61,7 @@ import org.apache.jmeter.modifiers.JSR223PreProcessor; import org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy; import org.apache.jmeter.protocol.http.util.HTTPFileArg; import org.apache.jmeter.protocol.java.sampler.JSR223Sampler; +import org.apache.jmeter.protocol.jdbc.config.DataSourceElement; import org.apache.jmeter.protocol.jdbc.sampler.JDBCSampler; import org.apache.jmeter.protocol.tcp.sampler.TCPSampler; import org.apache.jmeter.save.SaveService; @@ -60,24 +70,26 @@ import org.apache.jmeter.testelement.TestPlan; import org.apache.jmeter.timers.ConstantTimer; import org.apache.jorphan.collections.HashTree; +import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.UUID; +import java.util.*; public class MsJmeterParser extends ScenarioImportAbstractParser { + private final String ENV_NAME = "导入数据环境"; + @Override public ScenarioImport parse(InputStream inputSource, ApiTestImportRequest request) { try { Object scriptWrapper = SaveService.loadElement(inputSource); HashTree testPlan = this.getHashTree(scriptWrapper); + // 优先初始化数据源 + preInitPool(request.getProjectId(), testPlan); + MsScenario scenario = new MsScenario(); scenario.setReferenced("REF"); jmterHashTree(testPlan, scenario); this.projectId = request.getProjectId(); - ScenarioImport scenarioImport = new ScenarioImport(); scenarioImport.setData(paseObj(scenario, request)); scenarioImport.setProjectid(request.getProjectId()); @@ -157,7 +169,7 @@ public class MsJmeterParser extends ScenarioImportAbstractParser { samplerProxy.setPath(source.getPath()); samplerProxy.setMethod(source.getMethod()); if (source.getUrl() != null) { - samplerProxy.setUrl(source.getUrl().toString()); + // samplerProxy.setUrl(source.getUrl().toString()); } samplerProxy.setId(UUID.randomUUID().toString()); samplerProxy.setType("HTTPSamplerProxy"); @@ -238,6 +250,87 @@ public class MsJmeterParser extends ScenarioImportAbstractParser { } } + /** + * 优先初始化数据池 + */ + private void preInitPool(String projectId, HashTree hashTree) { + // 初始化已有数据池 + initDataSource(projectId, ENV_NAME); + // 添加当前jmx 中新的数据池 + preCreatePool(hashTree); + // 更新数据源 + ApiTestEnvironmentService environmentService = CommonBeanFactory.getBean(ApiTestEnvironmentService.class); + if (dataPools.getIsCreate()) { + dataPools.getTestEnvironmentWithBLOBs().setConfig(JSON.toJSONString(dataPools.getDataSources().values())); + environmentService.add(dataPools.getTestEnvironmentWithBLOBs()); + } else { + dataPools.getTestEnvironmentWithBLOBs().setConfig(JSON.toJSONString(dataPools.getDataSources().values())); + environmentService.update(dataPools.getTestEnvironmentWithBLOBs()); + } + } + + private void preCreatePool(HashTree tree) { + for (Object key : tree.keySet()) { + // JDBC 数据池 + if (key instanceof DataSourceElement) { + DataSourceElement dataSourceElement = (DataSourceElement) key; + if (dataPools != null && dataPools.getDataSources().containsKey(dataSourceElement.getDataSource())) { + DatabaseConfig config = dataPools.getDataSources().get(dataSourceElement.getDataSource()); + DatabaseConfig newConfig = new DatabaseConfig(); + BeanUtils.copyBean(newConfig, dataSourceElement); + newConfig.setId(config.getId()); + dataPools.getDataSources().put(dataSourceElement.getDataSource(), newConfig); + } else { + DatabaseConfig newConfig = new DatabaseConfig(); + BeanUtils.copyBean(newConfig, dataSourceElement); + newConfig.setId(UUID.randomUUID().toString()); + dataPools.getDataSources().put(dataSourceElement.getDataSource(), newConfig); + } + } + + // 递归子项 + HashTree node = tree.get(key); + if (node != null) { + preCreatePool(node); + } + } + } + + private ImportPoolsDTO dataPools; + + private void initDataSource(String projectId, String name) { + ApiTestEnvironmentService environmentService = CommonBeanFactory.getBean(ApiTestEnvironmentService.class); + ApiTestEnvironmentExample example = new ApiTestEnvironmentExample(); + example.createCriteria().andProjectIdEqualTo(projectId).andNameEqualTo(name); + + List environments = environmentService.selectByExampleWithBLOBs(example); + dataPools = new ImportPoolsDTO(); + if (CollectionUtils.isNotEmpty(environments)) { + dataPools.setIsCreate(false); + dataPools.setTestEnvironmentWithBLOBs(environments.get(0)); + Map dataSources = new HashMap<>(); + environments.forEach(environment -> { + if (environment != null && environment.getConfig() != null) { + EnvironmentConfig envConfig = JSONObject.parseObject(environment.getConfig(), EnvironmentConfig.class); + if (envConfig != null && CollectionUtils.isNotEmpty(envConfig.getDatabaseConfigs())) { + envConfig.getDatabaseConfigs().forEach(item -> { + dataSources.put(item.getName(), item); + }); + } + } + dataPools.setEnvId(environment.getId()); + dataPools.setDataSources(dataSources); + }); + } else { + dataPools.setIsCreate(true); + ApiTestEnvironmentWithBLOBs apiTestEnvironmentWithBLOBs = new ApiTestEnvironmentWithBLOBs(); + apiTestEnvironmentWithBLOBs.setId(UUID.randomUUID().toString()); + apiTestEnvironmentWithBLOBs.setName(ENV_NAME); + apiTestEnvironmentWithBLOBs.setProjectId(projectId); + dataPools.setTestEnvironmentWithBLOBs(apiTestEnvironmentWithBLOBs); + } + } + private void convertJDBCSampler(MsJDBCSampler msJDBCSampler, JDBCSampler jdbcSampler) { msJDBCSampler.setType("JDBCSampler"); msJDBCSampler.setName(jdbcSampler.getName()); @@ -303,7 +396,9 @@ public class MsJmeterParser extends ScenarioImportAbstractParser { ResponseAssertion assertion = (ResponseAssertion) key; assertionRegex.setDescription(assertion.getName()); assertionRegex.setAssumeSuccess(assertion.getAssumeSuccess()); - assertionRegex.setExpression(assertion.getTestStrings().getStringValue()); + if (assertion.getTestStrings() != null && !assertion.getTestStrings().isEmpty()) { + assertionRegex.setExpression(assertion.getTestStrings().get(0).getStringValue()); + } if (assertion.isTestFieldRequestData()) { assertionRegex.setSubject("Response Data"); } @@ -333,8 +428,9 @@ public class MsJmeterParser extends ScenarioImportAbstractParser { MsAssertionJSR223 msAssertionJSR223 = new MsAssertionJSR223(); JSR223Assertion jsr223Assertion = (JSR223Assertion) key; msAssertionJSR223.setName(jsr223Assertion.getName()); - msAssertionJSR223.setScript(jsr223Assertion.getScript()); - msAssertionJSR223.setScriptLanguage(jsr223Assertion.getScriptLanguage()); + msAssertionJSR223.setDesc(jsr223Assertion.getName()); + msAssertionJSR223.setScript(jsr223Assertion.getPropertyAsString("script")); + msAssertionJSR223.setScriptLanguage(jsr223Assertion.getPropertyAsString("scriptLanguage")); assertions.setName(jsr223Assertion.getName()); assertions.getJsr223().add(msAssertionJSR223); @@ -347,6 +443,23 @@ public class MsJmeterParser extends ScenarioImportAbstractParser { } } + /** + * 把节点对象转成XML,不然执行时无法正常转换 + * + * @param obj + * @return + */ + public static String objToXml(Object obj) { + try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { + SaveService.saveElement(obj, baos); + return baos.toString(); + } catch (Exception e) { + e.printStackTrace(); + LogUtil.warn("HashTree error, can't log jmx scenarioDefinition"); + } + return null; + } + private void jmterHashTree(HashTree tree, MsTestElement scenario) { for (Object key : tree.keySet()) { MsTestElement elementNode; @@ -358,7 +471,7 @@ public class MsJmeterParser extends ScenarioImportAbstractParser { scenario.setName(((TestPlan) key).getName()); elementNode = new MsJmeterElement(); elementNode.setName(((TestPlan) key).getName()); - ((MsJmeterElement) elementNode).setJmeterElement(key); + ((MsJmeterElement) elementNode).setJmeterElement(objToXml(key)); } // 线程组 else if (key instanceof ThreadGroup) { @@ -412,12 +525,12 @@ public class MsJmeterParser extends ScenarioImportAbstractParser { ((MsJSR223PreProcessor) elementNode).setScript(jsr223Sampler.getPropertyAsString("script")); ((MsJSR223PreProcessor) elementNode).setScriptLanguage(jsr223Sampler.getPropertyAsString("scriptLanguage")); } - // 提取参数 + // 断言规则 else if (key instanceof ResponseAssertion || key instanceof JSONPathAssertion || key instanceof XPath2Assertion || key instanceof JSR223Assertion || key instanceof DurationAssertion) { elementNode = new MsAssertions(); convertMsAssertions((MsAssertions) elementNode, key); } - // 断言规则 + // 提取参数 else if (key instanceof RegexExtractor || key instanceof XPath2Extractor || key instanceof JSONPostProcessor) { elementNode = new MsExtract(); convertMsExtract((MsExtract) elementNode, key); @@ -475,8 +588,9 @@ public class MsJmeterParser extends ScenarioImportAbstractParser { elementNode.setType("JmeterElement"); TestElement testElement = (TestElement) key; elementNode.setName(testElement.getName()); - ((MsJmeterElement) elementNode).setJmeterElement(key); + ((MsJmeterElement) elementNode).setJmeterElement(objToXml(key)); } + elementNode.setEnable(((TestElement) key).isEnabled()); elementNode.setResourceId(UUID.randomUUID().toString()); elementNode.setId(UUID.randomUUID().toString()); elementNode.setIndex(scenario.getHashTree().size() + 1 + ""); diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/MsTestElement.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/MsTestElement.java index 7052e1df60..fc98f55213 100644 --- a/backend/src/main/java/io/metersphere/api/dto/definition/request/MsTestElement.java +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/MsTestElement.java @@ -111,8 +111,10 @@ public abstract class MsTestElement { * todo 公共环境逐层传递,如果自身有环境 以自身引用环境为准否则以公共环境作为请求环境 */ public void toHashTree(HashTree tree, List hashTree, ParameterConfig config) { - for (MsTestElement el : hashTree) { - el.toHashTree(tree, el.hashTree, config); + if (CollectionUtils.isNotEmpty(hashTree)) { + for (MsTestElement el : hashTree) { + el.toHashTree(tree, el.hashTree, config); + } } } diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsHTTPSamplerProxy.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsHTTPSamplerProxy.java index 4e35756c1a..573a65505d 100644 --- a/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsHTTPSamplerProxy.java +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsHTTPSamplerProxy.java @@ -148,6 +148,7 @@ public class MsHTTPSamplerProxy extends MsTestElement { String envPath = StringUtils.equals(urlObject.getPath(), "/") ? "" : urlObject.getPath(); if (StringUtils.isNotBlank(this.getPath()) && !isUrl) { envPath += this.getPath(); + sampler.setPath(envPath); } if (CollectionUtils.isNotEmpty(this.getRest()) && this.isRest()) { envPath = getRestParameters(URLDecoder.decode(envPath, "UTF-8")); diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/unknown/MsJmeterElement.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/unknown/MsJmeterElement.java index 1e97f0758d..5f5b2d44b5 100644 --- a/backend/src/main/java/io/metersphere/api/dto/definition/request/unknown/MsJmeterElement.java +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/unknown/MsJmeterElement.java @@ -2,8 +2,19 @@ package io.metersphere.api.dto.definition.request.unknown; import com.alibaba.fastjson.annotation.JSONType; import io.metersphere.api.dto.definition.request.MsTestElement; +import io.metersphere.api.dto.definition.request.ParameterConfig; import lombok.Data; import lombok.EqualsAndHashCode; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.jmeter.save.SaveService; +import org.apache.jmeter.testelement.TestPlan; +import org.apache.jmeter.threads.ThreadGroup; +import org.apache.jorphan.collections.HashTree; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.util.List; /** * 暂时存放所有未知的Jmeter Element对象 @@ -14,6 +25,41 @@ import lombok.EqualsAndHashCode; public class MsJmeterElement extends MsTestElement { private String type = "JmeterElement"; - private Object jmeterElement; - + private String jmeterElement; + + @Override + public void toHashTree(HashTree tree, List hashTree, ParameterConfig config) { + if (!this.isEnable()) { + return; + } + try { + InputStream inputSource = getStrToStream(jmeterElement); + if (inputSource != null) { + Object scriptWrapper = SaveService.loadElement(inputSource); + HashTree elementTree = tree; + if (!(scriptWrapper instanceof TestPlan) && !(scriptWrapper instanceof ThreadGroup)) { + elementTree = tree.add(scriptWrapper); + } + if (CollectionUtils.isNotEmpty(hashTree)) { + for (MsTestElement el : hashTree) { + el.toHashTree(elementTree, el.getHashTree(), config); + } + } + } + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + public static InputStream getStrToStream(String sInputString) { + if (StringUtils.isNotEmpty(sInputString)) { + try { + ByteArrayInputStream tInputStringStream = new ByteArrayInputStream(sInputString.getBytes()); + return tInputStringStream; + } catch (Exception ex) { + ex.printStackTrace(); + } + } + return null; + } } diff --git a/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java b/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java index ea37f0a9ff..e8ca928efe 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java @@ -529,6 +529,7 @@ public class ApiAutomationService { ParameterConfig config = new ParameterConfig(); config.setConfig(envConfig); HashTree hashTree = request.getTestElement().generateHashTree(config); + System.out.println(request.getTestElement().getJmx(hashTree)); // 调用执行方法 createScenarioReport(request.getId(), request.getScenarioId(), request.getScenarioName(), ReportTriggerMode.MANUAL.name(), request.getExecuteType(), request.getProjectId(), SessionUtils.getUserId()); diff --git a/backend/src/main/java/io/metersphere/api/service/ApiTestEnvironmentService.java b/backend/src/main/java/io/metersphere/api/service/ApiTestEnvironmentService.java index 60b63afbc2..bb3ea479a0 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiTestEnvironmentService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiTestEnvironmentService.java @@ -26,6 +26,11 @@ public class ApiTestEnvironmentService { return apiTestEnvironmentMapper.selectByExampleWithBLOBs(example); } + public List selectByExampleWithBLOBs(ApiTestEnvironmentExample example) { + return apiTestEnvironmentMapper.selectByExampleWithBLOBs(example); + } + + public ApiTestEnvironmentWithBLOBs get(String id) { return apiTestEnvironmentMapper.selectByPrimaryKey(id); } diff --git a/frontend/src/business/components/api/automation/scenario/Setting.js b/frontend/src/business/components/api/automation/scenario/Setting.js index a25c6d9f72..b147a83658 100644 --- a/frontend/src/business/components/api/automation/scenario/Setting.js +++ b/frontend/src/business/components/api/automation/scenario/Setting.js @@ -14,6 +14,7 @@ export const ELEMENTS = new Map([ ['JSR223PostProcessor', []], ['Assertions', []], ['Extract', []], + ['JmeterElement', []], ['CustomizeReq', ["ConstantTimer", "JSR223PreProcessor", "JSR223PostProcessor", "Assertions", "Extract"]], ])