diff --git a/backend/src/main/java/io/metersphere/api/controller/APITestController.java b/backend/src/main/java/io/metersphere/api/controller/APITestController.java index c6dd7c5e5a..01290fc68e 100644 --- a/backend/src/main/java/io/metersphere/api/controller/APITestController.java +++ b/backend/src/main/java/io/metersphere/api/controller/APITestController.java @@ -18,10 +18,7 @@ import io.metersphere.api.service.*; import io.metersphere.base.domain.*; import io.metersphere.commons.constants.RoleConstants; import io.metersphere.commons.constants.ScheduleGroup; -import io.metersphere.commons.utils.CronUtils; -import io.metersphere.commons.utils.PageUtils; -import io.metersphere.commons.utils.Pager; -import io.metersphere.commons.utils.SessionUtils; +import io.metersphere.commons.utils.*; import io.metersphere.controller.request.QueryScheduleRequest; import io.metersphere.controller.request.ScheduleRequest; import io.metersphere.dto.ScheduleDao; @@ -398,6 +395,7 @@ public class APITestController { @PostMapping(value = "/genPerformanceTestXml", consumes = {"multipart/form-data"}) public JmxInfoDTO genPerformanceTest(@RequestPart("request") RunDefinitionRequest runRequest, @RequestPart(value = "files") List bodyFiles) throws Exception { + ParameterConfig config = new ParameterConfig(); config.setProjectId(runRequest.getProjectId()); diff --git a/backend/src/main/java/io/metersphere/api/dto/automation/parse/ApiScenarioImportUtil.java b/backend/src/main/java/io/metersphere/api/dto/automation/parse/ApiScenarioImportUtil.java index 39e4d8fee8..e3c5226f7e 100644 --- a/backend/src/main/java/io/metersphere/api/dto/automation/parse/ApiScenarioImportUtil.java +++ b/backend/src/main/java/io/metersphere/api/dto/automation/parse/ApiScenarioImportUtil.java @@ -13,10 +13,10 @@ import java.util.List; public class ApiScenarioImportUtil { public static ApiScenarioModule getSelectModule(String moduleId) { - ApiScenarioModuleService apiModuleService = CommonBeanFactory.getBean(ApiScenarioModuleService.class); + ApiScenarioModuleService apiScenarioModuleService = CommonBeanFactory.getBean(ApiScenarioModuleService.class); if (StringUtils.isNotBlank(moduleId) && !StringUtils.equals("root", moduleId)) { ApiScenarioModule module = new ApiScenarioModule(); - ApiScenarioModuleDTO moduleDTO = apiModuleService.getNode(moduleId); + ApiScenarioModuleDTO moduleDTO = apiScenarioModuleService.getNode(moduleId); if (moduleDTO != null) { BeanUtils.copyBean(module, moduleDTO); } @@ -25,6 +25,17 @@ public class ApiScenarioImportUtil { return null; } + public static String getSelectModulePath(String path, String pid) { + ApiScenarioModuleService apiScenarioModuleService = CommonBeanFactory.getBean(ApiScenarioModuleService.class); + if (StringUtils.isNotBlank(pid)) { + ApiScenarioModuleDTO moduleDTO = apiScenarioModuleService.getNode(pid); + if (moduleDTO != null) { + return getSelectModulePath(moduleDTO.getName() + "/" + path, moduleDTO.getParentId()); + } + } + return "/" + path; + } + public static ApiScenarioModule buildModule(ApiScenarioModule parentModule, String name, String projectId) { ApiScenarioModuleService apiModuleService = CommonBeanFactory.getBean(ApiScenarioModuleService.class); ApiScenarioModule module; diff --git a/backend/src/main/java/io/metersphere/api/dto/automation/parse/HarScenarioParser.java b/backend/src/main/java/io/metersphere/api/dto/automation/parse/HarScenarioParser.java index d0ed200928..f65dbce19c 100644 --- a/backend/src/main/java/io/metersphere/api/dto/automation/parse/HarScenarioParser.java +++ b/backend/src/main/java/io/metersphere/api/dto/automation/parse/HarScenarioParser.java @@ -18,6 +18,7 @@ import io.metersphere.commons.exception.MSException; import io.metersphere.commons.utils.CommonBeanFactory; import io.metersphere.commons.utils.LogUtil; import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; import java.io.InputStream; import java.util.ArrayList; @@ -62,16 +63,20 @@ public class HarScenarioParser extends HarScenarioAbstractParser } private void parseScenarioWithBLOBs(List scenarioWithBLOBsList, MsScenario msScenario, ApiTestImportRequest request) { - ApiScenarioModule module = ApiScenarioImportUtil.getSelectModule(request.getModuleId()); - if (module == null) { - ApiScenarioModuleService apiModuleService = CommonBeanFactory.getBean(ApiScenarioModuleService.class); - module = apiModuleService.getNewModule(msScenario.getName(), projectId, 1); + ApiScenarioModule selectModule = null; + if (StringUtils.isNotBlank(request.getModuleId())) { + selectModule = ApiScenarioImportUtil.getSelectModule(request.getModuleId()); } - + ApiScenarioModule module = ApiScenarioImportUtil.buildModule(selectModule, msScenario.getName(), this.projectId); ApiScenarioWithBLOBs scenarioWithBLOBs = parseScenario(msScenario); if (module != null) { scenarioWithBLOBs.setApiScenarioModuleId(module.getId()); - scenarioWithBLOBs.setModulePath("/" + module.getName()); + if (selectModule != null) { + String selectModulePath = ApiScenarioImportUtil.getSelectModulePath(selectModule.getName(), selectModule.getParentId()); + scenarioWithBLOBs.setModulePath(selectModulePath + "/" + module.getName()); + } else { + scenarioWithBLOBs.setModulePath("/" + module.getName()); + } } scenarioWithBLOBsList.add(scenarioWithBLOBs); } 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 6ca5d6d0fc..e891088121 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 @@ -112,7 +112,15 @@ public class MsJmeterParser extends ApiImportAbstractParser { private List paseObj(MsScenario msScenario, ApiTestImportRequest request) { List scenarioWithBLOBsList = new ArrayList<>(); ApiScenarioWithBLOBs scenarioWithBLOBs = new ApiScenarioWithBLOBs(); - ApiScenarioModule module = ApiScenarioImportUtil.buildModule(ApiScenarioImportUtil.getSelectModule(request.getModuleId()), msScenario.getName(), this.projectId); + ApiScenarioModule selectModule = null; + String selectModulePath = null; + if (StringUtils.isNotBlank(request.getModuleId())) { + selectModule = ApiScenarioImportUtil.getSelectModule(request.getModuleId()); + if (selectModule != null) { + selectModulePath = ApiScenarioImportUtil.getSelectModulePath(selectModule.getName(), selectModule.getParentId()); + } + } + ApiScenarioModule module = ApiScenarioImportUtil.buildModule(selectModule, msScenario.getName(), this.projectId); scenarioWithBLOBs.setName(msScenario.getName()); scenarioWithBLOBs.setProjectId(request.getProjectId()); if (msScenario != null && CollectionUtils.isNotEmpty(msScenario.getHashTree())) { @@ -120,7 +128,11 @@ public class MsJmeterParser extends ApiImportAbstractParser { } if (module != null) { scenarioWithBLOBs.setApiScenarioModuleId(module.getId()); - scenarioWithBLOBs.setModulePath("/" + module.getName()); + if (StringUtils.isNotBlank(selectModulePath)) { + scenarioWithBLOBs.setModulePath(selectModulePath + "/" + module.getName()); + } else { + scenarioWithBLOBs.setModulePath("/" + module.getName()); + } } scenarioWithBLOBs.setId(UUID.randomUUID().toString()); scenarioWithBLOBs.setScenarioDefinition(JSON.toJSONString(msScenario)); @@ -198,6 +210,12 @@ public class MsJmeterParser extends ApiImportAbstractParser { samplerProxy.setArguments(new ArrayList() {{ this.add(new KeyValue()); }}); + // 初始化body + Body body = new Body(); + body.init(); + body.initKvs(); + body.initBinary(); + samplerProxy.setBody(body); if (source != null && source.getHTTPFiles().length > 0) { samplerProxy.getBody().initBinary(); samplerProxy.getBody().setType(Body.FORM_DATA); diff --git a/backend/src/main/java/io/metersphere/api/dto/automation/parse/MsScenarioParser.java b/backend/src/main/java/io/metersphere/api/dto/automation/parse/MsScenarioParser.java index 2bfbc8e7e4..28b95d8e9b 100644 --- a/backend/src/main/java/io/metersphere/api/dto/automation/parse/MsScenarioParser.java +++ b/backend/src/main/java/io/metersphere/api/dto/automation/parse/MsScenarioParser.java @@ -17,11 +17,23 @@ import java.util.*; public class MsScenarioParser extends MsAbstractParser { + private ApiScenarioModule selectModule; + + private String selectModulePath; + @Override public ScenarioImport parse(InputStream source, ApiTestImportRequest request) { String testStr = getApiTestStr(source); this.projectId = request.getProjectId(); JSONObject testObject = JSONObject.parseObject(testStr, Feature.OrderedField); + + if (StringUtils.isNotBlank(request.getModuleId())) { + this.selectModule = ApiScenarioImportUtil.getSelectModule(request.getModuleId()); + if (this.selectModule != null) { + this.selectModulePath = ApiScenarioImportUtil.getSelectModulePath(this.selectModule.getName(), this.selectModule.getParentId()); + } + } + if (testObject.get("projectName") != null || testObject.get("projectId") != null ) { return parseMsFormat(testStr, request); } else { @@ -73,13 +85,19 @@ public class MsScenarioParser extends MsAbstractParser { modulePath = modulePath.substring(0, modulePath.length() - 1); } List modules = Arrays.asList(modulePath.split("/")); - ApiScenarioModule parent = ApiScenarioImportUtil.getSelectModule(importRequest.getModuleId()); + ApiScenarioModule parent = this.selectModule; Iterator iterator = modules.iterator(); while (iterator.hasNext()) { String item = iterator.next(); parent = ApiScenarioImportUtil.buildModule(parent, item, this.projectId); if (!iterator.hasNext()) { apiScenarioWithBLOBs.setApiScenarioModuleId(parent.getId()); + String path = apiScenarioWithBLOBs.getModulePath() == null ? "" : apiScenarioWithBLOBs.getModulePath(); + if (StringUtils.isNotBlank(this.selectModulePath)) { + apiScenarioWithBLOBs.setModulePath(this.selectModulePath + path); + } else if (StringUtils.isBlank(importRequest.getModuleId())) { + apiScenarioWithBLOBs.setModulePath("/默认模块" + path); + } } } } diff --git a/backend/src/main/java/io/metersphere/api/dto/automation/parse/PostmanScenarioParser.java b/backend/src/main/java/io/metersphere/api/dto/automation/parse/PostmanScenarioParser.java index 201feeed86..da669fc41b 100644 --- a/backend/src/main/java/io/metersphere/api/dto/automation/parse/PostmanScenarioParser.java +++ b/backend/src/main/java/io/metersphere/api/dto/automation/parse/PostmanScenarioParser.java @@ -46,11 +46,18 @@ public class PostmanScenarioParser extends PostmanAbstractParserParser scenarioWithBLOBsList, MsScenario msScenario, ApiTestImportRequest request) { - ApiScenarioModule module = ApiScenarioImportUtil.buildModule(ApiScenarioImportUtil.getSelectModule(request.getModuleId()), msScenario.getName(), this.projectId); + ApiScenarioModule selectModule = ApiScenarioImportUtil.getSelectModule(request.getModuleId()); + + ApiScenarioModule module = ApiScenarioImportUtil.buildModule(selectModule, msScenario.getName(), this.projectId); ApiScenarioWithBLOBs scenarioWithBLOBs = parseScenario(msScenario); if (module != null) { scenarioWithBLOBs.setApiScenarioModuleId(module.getId()); - scenarioWithBLOBs.setModulePath("/" + module.getName()); + if (selectModule != null) { + String selectModulePath = ApiScenarioImportUtil.getSelectModulePath(selectModule.getName(), selectModule.getParentId()); + scenarioWithBLOBs.setModulePath(selectModulePath + "/" + module.getName()); + } else { + scenarioWithBLOBs.setModulePath("/" + module.getName()); + } } scenarioWithBLOBsList.add(scenarioWithBLOBs); } diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/parse/ApiDefinitionImportUtil.java b/backend/src/main/java/io/metersphere/api/dto/definition/parse/ApiDefinitionImportUtil.java index 78df96ac1e..d78585271d 100644 --- a/backend/src/main/java/io/metersphere/api/dto/definition/parse/ApiDefinitionImportUtil.java +++ b/backend/src/main/java/io/metersphere/api/dto/definition/parse/ApiDefinitionImportUtil.java @@ -26,6 +26,24 @@ public class ApiDefinitionImportUtil { return null; } + public static String getSelectModulePath(String path, String pid) { + ApiModuleService apiModuleService = CommonBeanFactory.getBean(ApiModuleService.class); + if (StringUtils.isNotBlank(pid)) { + ApiModuleDTO moduleDTO = apiModuleService.getNode(pid); + if (moduleDTO != null) { + return getSelectModulePath(moduleDTO.getName() + "/" + path, moduleDTO.getParentId()); + } + } + return "/" + path; + } + + public static ApiModule getNodeTree(String projectId) { + ApiModuleService apiModuleService = CommonBeanFactory.getBean(ApiModuleService.class); + List nodeTrees = apiModuleService.getNodeTreeByProjectId(projectId, RequestType.HTTP); + + return null; + } + public static ApiModule buildModule(ApiModule parentModule, String name, String projectId) { ApiModuleService apiModuleService = CommonBeanFactory.getBean(ApiModuleService.class); ApiModule module; diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/parse/HarAbstractParser.java b/backend/src/main/java/io/metersphere/api/dto/definition/parse/HarAbstractParser.java index b437567bb0..62c4af2eb9 100644 --- a/backend/src/main/java/io/metersphere/api/dto/definition/parse/HarAbstractParser.java +++ b/backend/src/main/java/io/metersphere/api/dto/definition/parse/HarAbstractParser.java @@ -1,27 +1,10 @@ package io.metersphere.api.dto.definition.parse; - import io.metersphere.api.parse.ApiImportAbstractParser; -import io.metersphere.base.domain.ApiDefinitionWithBLOBs; -import io.metersphere.base.domain.ApiModule; - -import java.util.List; - /** * @author song.tianyang * @Date 2021/3/10 11:15 上午 * @Description */ public abstract class HarAbstractParser extends ApiImportAbstractParser { - - protected void buildModule(ApiModule parentModule, ApiDefinitionWithBLOBs apiDefinition, List tags) { - if (tags != null) { - tags.forEach(tag -> { - ApiModule module = ApiDefinitionImportUtil.buildModule(parentModule, tag, this.projectId); - apiDefinition.setModuleId(module.getId()); - }); - }else { - apiDefinition.setModuleId(parentModule.getId()); - } - } } diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/parse/HarParser.java b/backend/src/main/java/io/metersphere/api/dto/definition/parse/HarParser.java index 2d017c14bf..073277be33 100644 --- a/backend/src/main/java/io/metersphere/api/dto/definition/parse/HarParser.java +++ b/backend/src/main/java/io/metersphere/api/dto/definition/parse/HarParser.java @@ -61,7 +61,15 @@ public class HarParser extends HarAbstractParser { private List parseRequests(Har har, ApiTestImportRequest importRequest) { List results = new ArrayList<>(); - ApiModule parentNode = ApiDefinitionImportUtil.getSelectModule(importRequest.getModuleId()); + ApiModule selectModule = null; + String selectModulePath = null; + if (StringUtils.isNotBlank(importRequest.getModuleId())) { + selectModule = ApiDefinitionImportUtil.getSelectModule(importRequest.getModuleId()); + if (selectModule != null) { + selectModulePath = ApiDefinitionImportUtil.getSelectModulePath(selectModule.getName(), selectModule.getParentId()); + } + } + List harEntryList = new ArrayList<>(); if (har.log != null && har.log.entries != null) { @@ -103,7 +111,17 @@ public class HarParser extends HarAbstractParser { addBodyHeader(request); apiDefinition.setRequest(JSON.toJSONString(request)); apiDefinition.setResponse(JSON.toJSONString(parseResponse(entry.response))); - buildModule(parentNode, apiDefinition, null); + if (selectModule == null) { + apiDefinition.setModuleId("default-module"); + + } else { + apiDefinition.setModuleId(selectModule.getId()); + } + if (StringUtils.isNotBlank(selectModulePath)) { + apiDefinition.setModulePath(selectModulePath); + } else { + apiDefinition.setModulePath("/默认模块"); + } results.add(apiDefinition); } } diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/parse/MsDefinitionParser.java b/backend/src/main/java/io/metersphere/api/dto/definition/parse/MsDefinitionParser.java index ce76eddb03..43299a6220 100644 --- a/backend/src/main/java/io/metersphere/api/dto/definition/parse/MsDefinitionParser.java +++ b/backend/src/main/java/io/metersphere/api/dto/definition/parse/MsDefinitionParser.java @@ -19,11 +19,22 @@ import java.util.*; public class MsDefinitionParser extends MsAbstractParser { + private ApiModule selectModule; + + private String selectModulePath; + @Override public ApiDefinitionImport parse(InputStream source, ApiTestImportRequest request) { String testStr = getApiTestStr(source); JSONObject testObject = JSONObject.parseObject(testStr, Feature.OrderedField); this.projectId = request.getProjectId(); + if (StringUtils.isNotBlank(request.getModuleId())) { + this.selectModule = ApiDefinitionImportUtil.getSelectModule(request.getModuleId()); + if (this.selectModule != null) { + this.selectModulePath = ApiDefinitionImportUtil.getSelectModulePath(this.selectModule.getName(), this.selectModule.getParentId()); + } + } + if (testObject.get("projectName") != null || testObject.get("projectId") != null ) {// metersphere 格式导入 return parseMsFormat(testStr, request); } else { // chrome 插件录制格式导入 @@ -40,7 +51,7 @@ public class MsDefinitionParser extends MsAbstractParser { testObject.keySet().forEach(tag -> { String moduleId = null; if (isCreateModule) { - moduleId = ApiDefinitionImportUtil.buildModule(ApiDefinitionImportUtil.getSelectModule(importRequest.getModuleId()), tag, this.projectId).getId(); + moduleId = ApiDefinitionImportUtil.buildModule(this.selectModule, tag, this.projectId).getId(); } List msHTTPSamplerProxies = parseMsHTTPSamplerProxy(testObject, tag); for (MsHTTPSamplerProxy msHTTPSamplerProxy : msHTTPSamplerProxies) { @@ -113,13 +124,19 @@ public class MsDefinitionParser extends MsAbstractParser { modulePath = modulePath.substring(0, modulePath.length() - 1); } List modules = Arrays.asList(modulePath.split("/")); - ApiModule parent = ApiDefinitionImportUtil.getSelectModule(importRequest.getModuleId()); + ApiModule parent = this.selectModule; Iterator iterator = modules.iterator(); while (iterator.hasNext()) { String item = iterator.next(); parent = ApiDefinitionImportUtil.buildModule(parent, item, this.projectId); if (!iterator.hasNext()) { apiDefinition.setModuleId(parent.getId()); + String path = apiDefinition.getModulePath() == null ? "" : apiDefinition.getModulePath(); + if (StringUtils.isNotBlank(this.selectModulePath)) { + apiDefinition.setModulePath(this.selectModulePath + path); + } else if (StringUtils.isBlank(importRequest.getModuleId())){ + apiDefinition.setModulePath("/默认模块" + path); + } } } } diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/parse/PostmanDefinitionParser.java b/backend/src/main/java/io/metersphere/api/dto/definition/parse/PostmanDefinitionParser.java index d3de371f77..3df1c9b894 100644 --- a/backend/src/main/java/io/metersphere/api/dto/definition/parse/PostmanDefinitionParser.java +++ b/backend/src/main/java/io/metersphere/api/dto/definition/parse/PostmanDefinitionParser.java @@ -9,13 +9,22 @@ import io.metersphere.api.dto.parse.postman.PostmanKeyValue; import io.metersphere.api.parse.PostmanAbstractParserParser; import io.metersphere.base.domain.ApiDefinitionWithBLOBs; import io.metersphere.base.domain.ApiModule; +import org.apache.commons.lang3.StringUtils; +import io.metersphere.base.domain.ApiTestCaseWithBLOBs; +import io.metersphere.base.domain.Project; +import io.metersphere.base.mapper.ProjectMapper; +import io.metersphere.commons.utils.BeanUtils; +import io.metersphere.commons.utils.CommonBeanFactory; import java.io.InputStream; -import java.util.ArrayList; -import java.util.List; +import java.util.*; public class PostmanDefinitionParser extends PostmanAbstractParserParser { + private ApiModule selectModule; + + private String selectModulePath; + @Override public ApiDefinitionImport parse(InputStream source, ApiTestImportRequest request) { String testStr = getApiTestStr(source); @@ -24,33 +33,57 @@ public class PostmanDefinitionParser extends PostmanAbstractParserParser variables = postmanCollection.getVariable(); ApiDefinitionImport apiImport = new ApiDefinitionImport(); List results = new ArrayList<>(); + this.selectModule = ApiDefinitionImportUtil.getSelectModule(request.getModuleId()); + if (this.selectModule != null) { + this.selectModulePath = ApiDefinitionImportUtil.getSelectModulePath(this.selectModule.getName(), this.selectModule.getParentId()); + } + + ApiModule apiModule = ApiDefinitionImportUtil.buildModule(this.selectModule, postmanCollection.getInfo().getName(), this.projectId); + List cases = new ArrayList<>(); + Map repeatMap = new HashMap(); + ProjectMapper projectMapper = CommonBeanFactory.getBean(ProjectMapper.class); + Project project = projectMapper.selectByPrimaryKey(request.getProjectId()); parseItem(postmanCollection.getItem(), variables, results, - ApiDefinitionImportUtil.buildModule(ApiDefinitionImportUtil.getSelectModule(request.getModuleId()), postmanCollection.getInfo().getName(), this.projectId), true); + apiModule, apiModule.getName(), cases, repeatMap, project.getRepeatable()); apiImport.setData(results); + apiImport.setCases(cases); return apiImport; } - protected void parseItem(List items, List variables, List results, ApiModule parentModule, Boolean isCreateModule) { + protected void parseItem(List items, List variables, List results, + ApiModule parentModule, String path, List cases, Map repeatMap, Boolean repeatable) { for (PostmanItem item : items) { List childItems = item.getItem(); if (childItems != null) { ApiModule module = null; - if (isCreateModule) { - module = ApiDefinitionImportUtil.buildModule(parentModule, item.getName(), this.projectId); - } - parseItem(childItems, variables, results, module, isCreateModule); + module = ApiDefinitionImportUtil.buildModule(parentModule, item.getName(), this.projectId); + parseItem(childItems, variables, results, module, path + "/" + module.getName(), cases, repeatMap, repeatable); } else { MsHTTPSamplerProxy msHTTPSamplerProxy = parsePostman(item); ApiDefinitionWithBLOBs request = buildApiDefinition(msHTTPSamplerProxy.getId(), msHTTPSamplerProxy.getName(), msHTTPSamplerProxy.getPath(), msHTTPSamplerProxy.getMethod(), new ApiTestImportRequest()); request.setPath(msHTTPSamplerProxy.getPath()); request.setRequest(JSON.toJSONString(msHTTPSamplerProxy)); - - if (request != null) { - results.add(request); - } if (parentModule != null) { request.setModuleId(parentModule.getId()); + if (StringUtils.isNotBlank(this.selectModulePath)) { + request.setModulePath(this.selectModulePath + "/" + path); + } else { + request.setModulePath("/" + path); + } + } + if (request != null) { + if (repeatMap.keySet().contains(request.getMethod() + request.getPath()) + && (repeatable == null || repeatable == false)) { + ApiTestCaseWithBLOBs apiTestCase = new ApiTestCaseWithBLOBs(); + BeanUtils.copyBean(apiTestCase, request); + apiTestCase.setApiDefinitionId(repeatMap.get(request.getMethod() + request.getPath())); + apiTestCase.setPriority("P0"); + cases.add(apiTestCase); + } else { + repeatMap.put(request.getMethod() + request.getPath(), request.getId()); + results.add(request); + } } } } diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/parse/Swagger2Parser.java b/backend/src/main/java/io/metersphere/api/dto/definition/parse/Swagger2Parser.java index 8655751191..439957f149 100644 --- a/backend/src/main/java/io/metersphere/api/dto/definition/parse/Swagger2Parser.java +++ b/backend/src/main/java/io/metersphere/api/dto/definition/parse/Swagger2Parser.java @@ -56,7 +56,14 @@ public class Swagger2Parser extends SwaggerAbstractParser { List results = new ArrayList<>(); - ApiModule parentNode = ApiDefinitionImportUtil.getSelectModule(importRequest.getModuleId()); + ApiModule selectModule = null; + String selectModulePath = null; + if (StringUtils.isNotBlank(importRequest.getModuleId())) { + selectModule = ApiDefinitionImportUtil.getSelectModule(importRequest.getModuleId()); + if (selectModule != null) { + selectModulePath = ApiDefinitionImportUtil.getSelectModulePath(selectModule.getName(), selectModule.getParentId()); + } + } String basePath = swagger.getBasePath(); for (String pathName : pathNames) { @@ -76,7 +83,7 @@ public class Swagger2Parser extends SwaggerAbstractParser { } apiDefinition.setRequest(JSON.toJSONString(request)); apiDefinition.setResponse(JSON.toJSONString(parseResponse(operation, operation.getResponses()))); - buildModule(parentNode, apiDefinition, operation.getTags()); + buildModule(selectModule, apiDefinition, operation.getTags(), selectModulePath); results.add(apiDefinition); } } diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/parse/Swagger3Parser.java b/backend/src/main/java/io/metersphere/api/dto/definition/parse/Swagger3Parser.java index b450ff4ca5..3319eb5685 100644 --- a/backend/src/main/java/io/metersphere/api/dto/definition/parse/Swagger3Parser.java +++ b/backend/src/main/java/io/metersphere/api/dto/definition/parse/Swagger3Parser.java @@ -26,15 +26,12 @@ import io.swagger.v3.oas.models.parameters.*; import io.swagger.v3.oas.models.responses.ApiResponse; import io.swagger.v3.oas.models.responses.ApiResponses; import io.swagger.v3.parser.core.models.SwaggerParseResult; -import net.sf.saxon.ma.json.XMLToJsonFn; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; import org.apache.commons.lang3.StringUtils; -import org.fife.ui.rsyntaxtextarea.parser.XmlParser; import org.springframework.http.HttpMethod; import java.io.InputStream; -import java.math.BigDecimal; import java.util.*; @@ -84,7 +81,14 @@ public class Swagger3Parser extends SwaggerAbstractParser { List results = new ArrayList<>(); - ApiModule parentNode = ApiDefinitionImportUtil.getSelectModule(importRequest.getModuleId()); + ApiModule selectModule = null; + String selectModulePath = null; + if (StringUtils.isNotBlank(importRequest.getModuleId())) { + selectModule = ApiDefinitionImportUtil.getSelectModule(importRequest.getModuleId()); + if (selectModule != null) { + selectModulePath = ApiDefinitionImportUtil.getSelectModulePath(selectModule.getName(), selectModule.getParentId()); + } + } for (String pathName : pathNames) { PathItem pathItem = paths.get(pathName); @@ -112,7 +116,7 @@ public class Swagger3Parser extends SwaggerAbstractParser { } // 有数据的话,去掉 Kvs 里初始化的第一个全 null 的数据,否则有空行 apiDefinition.setRequest(JSON.toJSONString(request)); apiDefinition.setResponse(JSON.toJSONString(parseResponse(operation.getResponses()))); - buildModule(parentNode, apiDefinition, operation.getTags()); + buildModule(selectModule, apiDefinition, operation.getTags(), selectModulePath); results.add(apiDefinition); } } diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/parse/SwaggerAbstractParser.java b/backend/src/main/java/io/metersphere/api/dto/definition/parse/SwaggerAbstractParser.java index 86e37543a8..c0a4380896 100644 --- a/backend/src/main/java/io/metersphere/api/dto/definition/parse/SwaggerAbstractParser.java +++ b/backend/src/main/java/io/metersphere/api/dto/definition/parse/SwaggerAbstractParser.java @@ -3,16 +3,23 @@ package io.metersphere.api.dto.definition.parse; import io.metersphere.api.parse.ApiImportAbstractParser; import io.metersphere.base.domain.ApiDefinitionWithBLOBs; import io.metersphere.base.domain.ApiModule; +import org.apache.commons.lang3.StringUtils; import java.util.List; public abstract class SwaggerAbstractParser extends ApiImportAbstractParser { - protected void buildModule(ApiModule parentModule, ApiDefinitionWithBLOBs apiDefinition, List tags) { + protected void buildModule(ApiModule parentModule, ApiDefinitionWithBLOBs apiDefinition, + List tags, String selectModulePath) { if (tags != null) { tags.forEach(tag -> { ApiModule module = ApiDefinitionImportUtil.buildModule(parentModule, tag, this.projectId); apiDefinition.setModuleId(module.getId()); + if (StringUtils.isNotBlank(selectModulePath)) { + apiDefinition.setModulePath(selectModulePath + "/" + tag); + } else { + apiDefinition.setModulePath("/" + tag); + } }); } } 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 6ca67bba17..ce580ed8da 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 @@ -201,7 +201,7 @@ public abstract class MsTestElement { return null; } - protected void addCsvDataSet(HashTree tree, List variables,ParameterConfig config) { + protected void addCsvDataSet(HashTree tree, List variables, ParameterConfig config) { if (CollectionUtils.isNotEmpty(variables)) { List list = variables.stream().filter(ScenarioVariable::isCSVValid).collect(Collectors.toList()); if (CollectionUtils.isNotEmpty(list)) { @@ -276,10 +276,7 @@ public abstract class MsTestElement { if (element.getParent() == null) { return; } - if (MsTestElementConstants.LoopController.name().equals(element.getType())) { - return; - } - path.append(element.getResourceId()).append("/"); + path.append(StringUtils.isEmpty(element.getName()) ? element.getType() : element.getName()).append("^@~@^"); getFullPath(element.getParent(), path); } @@ -300,7 +297,6 @@ public abstract class MsTestElement { // 获取全路径以备后面使用 StringBuilder fullPath = new StringBuilder(); getFullPath(parent, fullPath); - return fullPath + "<->" + parent.getName(); } return ""; 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 7ca5dfbd1f..f4fedfeebc 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 @@ -369,8 +369,9 @@ public class MsHTTPSamplerProxy extends MsTestElement { headerManager.setName(StringUtils.isNotEmpty(this.getName()) ? this.getName() + "HeaderManager" : "HeaderManager"); headerManager.setProperty(TestElement.TEST_CLASS, HeaderManager.class.getName()); headerManager.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("HeaderPanel")); + // header 也支持 mock 参数 headers.stream().filter(KeyValue::isValid).filter(KeyValue::isEnable).forEach(keyValue -> - headerManager.add(new Header(keyValue.getName(), keyValue.getValue())) + headerManager.add(new Header(keyValue.getName(), ScriptEngineUtils.calculate(keyValue.getValue()))) ); if (headerManager.getHeaders().size() > 0) { tree.add(headerManager); 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 060fbd3215..e5f2027080 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 @@ -66,6 +66,8 @@ public class MsJmeterElement extends MsTestElement { } if (CollectionUtils.isNotEmpty(hashTree)) { for (MsTestElement el : hashTree) { + // 给所有孩子加一个父亲标志 + el.setParent(this); el.toHashTree(elementTree, el.getHashTree(), config); } } diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/Body.java b/backend/src/main/java/io/metersphere/api/dto/scenario/Body.java index 4ae0e7d279..39bbf1960f 100644 --- a/backend/src/main/java/io/metersphere/api/dto/scenario/Body.java +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/Body.java @@ -1,8 +1,12 @@ package io.metersphere.api.dto.scenario; +import com.alibaba.fastjson.JSONObject; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import io.metersphere.api.dto.scenario.request.BodyFile; import io.metersphere.commons.json.JSONSchemaGenerator; import io.metersphere.commons.utils.FileUtils; +import io.metersphere.commons.utils.ScriptEngineUtils; import lombok.Data; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; @@ -66,8 +70,16 @@ public class Body { sampler.setDoMultipart(true); } } else { - if (StringUtils.isNotEmpty(this.format) && "JSON-SCHEMA".equals(this.format) && this.getJsonSchema() != null) { - this.raw = JSONSchemaGenerator.getJson(com.alibaba.fastjson.JSON.toJSONString(this.getJsonSchema())); + if (StringUtils.isNotEmpty(this.format) && this.getJsonSchema() != null) { + if("JSON-SCHEMA".equals(this.format)) { + this.raw = JSONSchemaGenerator.getJson(com.alibaba.fastjson.JSON.toJSONString(this.getJsonSchema())); + } else { // json 文本也支持 mock 参数 + JSONObject jsonObject = com.alibaba.fastjson.JSON.parseObject(this.getRaw()); + jsonMockParse(jsonObject); + // 格式化 json + Gson gson = new GsonBuilder().serializeNulls().setPrettyPrinting().create(); + this.raw = gson.toJson(jsonObject); + } } KeyValue keyValue = new KeyValue("", "JSON-SCHEMA", this.getRaw(), true, true); sampler.setPostBodyRaw(true); @@ -78,6 +90,18 @@ public class Body { return body; } + private void jsonMockParse(JSONObject jsonObject) { + for(String key : jsonObject.keySet()) { + Object value = jsonObject.get(key); + if(value instanceof JSONObject) { + jsonMockParse((JSONObject) value); + } else if(value instanceof String) { + value = ScriptEngineUtils.calculate((String) value); + jsonObject.put(key, value); + } + } + } + private HTTPFileArg[] httpFileArgs(String requestId) { List list = new ArrayList<>(); if (CollectionUtils.isNotEmpty(this.getKvs())) { @@ -116,6 +140,12 @@ public class Body { return StringUtils.equals(type, XML); } + public void init() { + this.type = ""; + this.raw = ""; + this.format = ""; + } + public void initKvs() { this.kvs = new ArrayList<>(); this.kvs.add(new KeyValue()); diff --git a/backend/src/main/java/io/metersphere/api/jmeter/TestResult.java b/backend/src/main/java/io/metersphere/api/jmeter/TestResult.java index e5d705b46e..94e9a9213d 100644 --- a/backend/src/main/java/io/metersphere/api/jmeter/TestResult.java +++ b/backend/src/main/java/io/metersphere/api/jmeter/TestResult.java @@ -43,50 +43,16 @@ public class TestResult { private static final String SEPARATOR = "<->"; public void addScenario(ScenarioResult result) { - Map> requestResultMap = new LinkedHashMap<>(); if (result != null && CollectionUtils.isNotEmpty(result.getRequestResults())) { result.getRequestResults().forEach(item -> { if (StringUtils.isNotEmpty(item.getName()) && item.getName().indexOf(SEPARATOR) != -1) { String array[] = item.getName().split(SEPARATOR); - String scenarioName = item.getName().replace(array[0] + SEPARATOR, ""); - item.setName(array[0]); - if (requestResultMap.containsKey(scenarioName)) { - requestResultMap.get(scenarioName).add(item); - } else { - List requestResults = new LinkedList<>(); - requestResults.add(item); - requestResultMap.put(scenarioName, requestResults); - } + item.setName(array[1] + array[0]); item.getSubRequestResults().forEach(subItem -> { - subItem.setName(item.getName()); + subItem.setName(array[0]); }); - } else { - if (requestResultMap.containsKey(result.getName())) { - requestResultMap.get(result.getName()).add(item); - } else { - List requestResults = new LinkedList<>(); - requestResults.add(item); - requestResultMap.put(result.getName(), requestResults); - } - item.getSubRequestResults().forEach(subItem -> { - subItem.setName(item.getName()); - }); - } }); - } - if (!requestResultMap.isEmpty()) { - requestResultMap.forEach((k, v) -> { - ScenarioResult scenarioResult = new ScenarioResult(); - BeanUtils.copyBean(scenarioResult, result); - scenarioResult.setName(k); - if (k.indexOf(SEPARATOR) != -1) { - scenarioResult.setName(k.split(SEPARATOR)[1]); - } - scenarioResult.setRequestResults(v); - scenarios.add(scenarioResult); - }); - } else { scenarios.add(result); } } diff --git a/backend/src/main/java/io/metersphere/api/parse/ApiImportAbstractParser.java b/backend/src/main/java/io/metersphere/api/parse/ApiImportAbstractParser.java index 34cbaaa326..9f1ee10b21 100644 --- a/backend/src/main/java/io/metersphere/api/parse/ApiImportAbstractParser.java +++ b/backend/src/main/java/io/metersphere/api/parse/ApiImportAbstractParser.java @@ -198,20 +198,14 @@ public abstract class ApiImportAbstractParser implements ApiImportParser { } protected ApiScenarioWithBLOBs parseScenario(MsScenario msScenario) { -// ApiScenarioModule module = ApiScenarioImportUtil.buildModule(ApiScenarioImportUtil.getSelectModule(request.getModuleId()), msScenario.getName(), this.projectId); ApiScenarioWithBLOBs scenarioWithBLOBs = new ApiScenarioWithBLOBs(); scenarioWithBLOBs.setName(msScenario.getName()); scenarioWithBLOBs.setProjectId(this.projectId); if (msScenario != null && CollectionUtils.isNotEmpty(msScenario.getHashTree())) { scenarioWithBLOBs.setStepTotal(msScenario.getHashTree().size()); } -// if (module != null) { -// scenarioWithBLOBs.setApiScenarioModuleId(module.getId()); -// scenarioWithBLOBs.setModulePath("/" + module.getName()); -// } scenarioWithBLOBs.setId(UUID.randomUUID().toString()); scenarioWithBLOBs.setScenarioDefinition(JSON.toJSONString(msScenario)); return scenarioWithBLOBs; -// scenarioWithBLOBsList.add(scenarioWithBLOBs); } } diff --git a/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java b/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java index eb3847aaf5..d5b7926304 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java @@ -240,6 +240,19 @@ public class ApiDefinitionService { } } + private List getSameRequestWithName(SaveApiDefinitionRequest request) { + ApiDefinitionExample example = new ApiDefinitionExample(); + example.createCriteria() + .andMethodEqualTo(request.getMethod()) + .andStatusNotEqualTo("Trash") + .andPathEqualTo(request.getPath()) + .andNameEqualTo(request.getName()) + .andProjectIdEqualTo(request.getProjectId()) + .andIdNotEqualTo(request.getId()); + return apiDefinitionMapper.selectByExample(example); + + } + private ApiDefinition updateTest(SaveApiDefinitionRequest request) { checkNameExist(request); if (StringUtils.equals(request.getMethod(), "ESB")) { @@ -322,7 +335,8 @@ public class ApiDefinitionService { } private ApiDefinition importCreate(ApiDefinitionWithBLOBs apiDefinition, ApiDefinitionMapper batchMapper, - ApiTestCaseMapper apiTestCaseMapper, ApiTestImportRequest apiTestImportRequest, List cases) { + ApiTestCaseMapper apiTestCaseMapper, ApiTestImportRequest apiTestImportRequest, List cases, + Boolean repeatable) { SaveApiDefinitionRequest saveReq = new SaveApiDefinitionRequest(); BeanUtils.copyBean(saveReq, apiDefinition); apiDefinition.setCreateTime(System.currentTimeMillis()); @@ -335,7 +349,13 @@ public class ApiDefinitionService { } apiDefinition.setDescription(apiDefinition.getDescription()); - List sameRequest = getSameRequest(saveReq); + List sameRequest; + if (repeatable == null || repeatable == false) { + sameRequest = getSameRequest(saveReq); + } else { + // 如果勾选了允许重复,则判断更新要加上name字段 + sameRequest = getSameRequestWithName(saveReq); + } if (StringUtils.equals("fullCoverage", apiTestImportRequest.getModeId())) { _importCreate(sameRequest, batchMapper, apiDefinition, apiTestCaseMapper, apiTestImportRequest, cases); } else if (StringUtils.equals("incrementalMerge", apiTestImportRequest.getModeId())) { @@ -373,6 +393,8 @@ public class ApiDefinitionService { //如果存在则修改 apiDefinition.setId(sameRequest.get(0).getId()); String request = setImportHashTree(apiDefinition); + apiDefinition.setModuleId(sameRequest.get(0).getModuleId()); + apiDefinition.setModulePath(sameRequest.get(0).getModulePath()); apiDefinitionMapper.updateByPrimaryKeyWithBLOBs(apiDefinition); apiDefinition.setRequest(request); importApiCase(apiDefinition, apiTestCaseMapper, apiTestImportRequest, false); @@ -415,15 +437,13 @@ public class ApiDefinitionService { private void importMsCase(ApiDefinitionImport apiImport, SqlSession sqlSession, ApiTestCaseMapper apiTestCaseMapper) { List cases = apiImport.getCases(); - List caseNames = apiTestCaseService.listPorjectAllCaseName(SessionUtils.getCurrentProjectId()); - Set existCaseName = new HashSet<>(); - caseNames.forEach(item -> { - existCaseName.add(item); - }); + SaveApiTestCaseRequest checkRequest = new SaveApiTestCaseRequest(); if (CollectionUtils.isNotEmpty(cases)) { int batchCount = 0; cases.forEach(item -> { - if (!existCaseName.contains(item.getName())) { + checkRequest.setName(item.getName()); + checkRequest.setApiDefinitionId(item.getApiDefinitionId()); + if (!apiTestCaseService.hasSameCase(checkRequest)) { item.setId(UUID.randomUUID().toString()); item.setCreateTime(System.currentTimeMillis()); item.setUpdateTime(System.currentTimeMillis()); @@ -613,6 +633,7 @@ public class ApiDefinitionService { List data = apiImport.getData(); ApiDefinitionMapper batchMapper = sqlSession.getMapper(ApiDefinitionMapper.class); ApiTestCaseMapper apiTestCaseMapper = sqlSession.getMapper(ApiTestCaseMapper.class); + Project project = projectMapper.selectByPrimaryKey(request.getProjectId()); int num = 0; if (!CollectionUtils.isEmpty(data) && data.get(0) != null && data.get(0).getProjectId() != null) { num = getNextNum(data.get(0).getProjectId()); @@ -628,14 +649,14 @@ public class ApiDefinitionService { if (apiImport.getEsbApiParamsMap() != null) { String apiId = item.getId(); EsbApiParamsWithBLOBs model = apiImport.getEsbApiParamsMap().get(apiId); - importCreate(item, batchMapper, apiTestCaseMapper, request, apiImport.getCases()); + importCreate(item, batchMapper, apiTestCaseMapper, request, apiImport.getCases(), project.getRepeatable()); if (model != null) { apiImport.getEsbApiParamsMap().remove(apiId); model.setResourceId(item.getId()); apiImport.getEsbApiParamsMap().put(item.getId(), model); } } else { - importCreate(item, batchMapper, apiTestCaseMapper, request, apiImport.getCases()); + importCreate(item, batchMapper, apiTestCaseMapper, request, apiImport.getCases(), project.getRepeatable()); } if (i % 300 == 0) { sqlSession.flushStatements(); diff --git a/backend/src/main/java/io/metersphere/api/service/ApiModuleService.java b/backend/src/main/java/io/metersphere/api/service/ApiModuleService.java index b620710743..3c33f855f2 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiModuleService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiModuleService.java @@ -275,6 +275,9 @@ public class ApiModuleService extends NodeTreeService { @Override public ApiModuleDTO getNode(String id) { ApiModule module = apiModuleMapper.selectByPrimaryKey(id); + if (module == null) { + return null; + } ApiModuleDTO dto = JSON.parseObject(JSON.toJSONString(module), ApiModuleDTO.class); return dto; } diff --git a/backend/src/main/java/io/metersphere/api/service/ApiTestCaseService.java b/backend/src/main/java/io/metersphere/api/service/ApiTestCaseService.java index 341c68747e..64b5ef1642 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiTestCaseService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiTestCaseService.java @@ -81,10 +81,6 @@ public class ApiTestCaseService { private static final String BODY_FILE_DIR = FileUtils.BODY_FILE_DIR; - public List listPorjectAllCaseName(String projectId) { - return extApiTestCaseMapper.listPorjectAllCaseName(projectId); - } - public List list(ApiTestCaseRequest request) { request.setOrders(ServiceUtils.getDefaultOrder(request.getOrders())); List returnList = extApiTestCaseMapper.list(request); @@ -233,14 +229,24 @@ public class ApiTestCaseService { } } - private void checkNameExist(SaveApiTestCaseRequest request) { - ApiTestCaseExample example = new ApiTestCaseExample(); - example.createCriteria().andNameEqualTo(request.getName()).andApiDefinitionIdEqualTo(request.getApiDefinitionId()).andIdNotEqualTo(request.getId()); - if (apiTestCaseMapper.countByExample(example) > 0) { + public void checkNameExist(SaveApiTestCaseRequest request) { + if (hasSameCase(request)) { MSException.throwException(Translator.get("load_test_already_exists")); } } + public Boolean hasSameCase(SaveApiTestCaseRequest request) { + ApiTestCaseExample example = new ApiTestCaseExample(); + ApiTestCaseExample.Criteria criteria = example.createCriteria(); + criteria.andNameEqualTo(request.getName()).andApiDefinitionIdEqualTo(request.getApiDefinitionId()); + if (StringUtils.isNotBlank(request.getId())) { + criteria.andIdNotEqualTo(request.getId()); + } + if (apiTestCaseMapper.countByExample(example) > 0) { + return true; + } + return false; + } private ApiTestCase updateTest(SaveApiTestCaseRequest request) { checkNameExist(request); diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiTestCaseMapper.java b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiTestCaseMapper.java index 851f55eca4..bb66ee87c3 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiTestCaseMapper.java +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiTestCaseMapper.java @@ -12,8 +12,6 @@ import java.util.List; public interface ExtApiTestCaseMapper { - List listPorjectAllCaseName(@Param("projectId") String projectId); - List list(@Param("request") ApiTestCaseRequest request); List listSimple(@Param("request") ApiTestCaseRequest request); @@ -33,4 +31,4 @@ public interface ExtApiTestCaseMapper { ApiTestCaseInfo selectApiCaseInfoByPrimaryKey(String id); List selectEffectiveTestCaseByProjectId(String projectId); -} \ No newline at end of file +} diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiTestCaseMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiTestCaseMapper.xml index f5954cf80a..5b9992c749 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiTestCaseMapper.xml +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiTestCaseMapper.xml @@ -391,11 +391,8 @@ - - \ No newline at end of file + diff --git a/backend/src/main/java/io/metersphere/performance/engine/EngineFactory.java b/backend/src/main/java/io/metersphere/performance/engine/EngineFactory.java index 29edf085eb..ae851a0f0c 100644 --- a/backend/src/main/java/io/metersphere/performance/engine/EngineFactory.java +++ b/backend/src/main/java/io/metersphere/performance/engine/EngineFactory.java @@ -79,7 +79,7 @@ public class EngineFactory { } if (type == ResourcePoolTypeEnum.K8S) { try { - return ConstructorUtils.invokeConstructor(kubernetesTestEngineClass, loadTest); + return (Engine) ConstructorUtils.invokeConstructor(kubernetesTestEngineClass, loadTest); } catch (Exception e) { LogUtil.error(e); return null; diff --git a/frontend/package.json b/frontend/package.json index d67ac0605e..b42cc03d25 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -4,7 +4,8 @@ "private": true, "scripts": { "serve": "vue-cli-service serve", - "build": "vue-cli-service build", + "build": "export NODE_OPTIONS=--max_old_space_size=4096 && vue-cli-service build", + "build-win": "SET NODE_OPTIONS=--max_old_space_size=4096 && vue-cli-service build", "lint": "vue-cli-service lint" }, "dependencies": { @@ -49,7 +50,7 @@ "vue-float-action-button": "^0.6.6", "vue-i18n": "^8.15.3", "vue-jsonpath-picker": "^1.1.5", - "vue-minder-editor-plus": "^1.0.19", + "vue-minder-editor-plus": "^1.0.21", "vue-papa-parse": "^2.0.0", "vue-pdf": "^4.2.0", "vue-router": "^3.1.3", diff --git a/frontend/src/business/components/api/automation/report/ApiReportDetail.vue b/frontend/src/business/components/api/automation/report/ApiReportDetail.vue index 2fb0c281f8..169b7f3e8e 100644 --- a/frontend/src/business/components/api/automation/report/ApiReportDetail.vue +++ b/frontend/src/business/components/api/automation/report/ApiReportDetail.vue @@ -3,26 +3,21 @@
- - -
+
- - - + - + -
@@ -62,6 +57,7 @@ report: {}, loading: true, fails: [], + failsTreeNodes: [], totalTime: 0, isRequestResult: false, request: {}, @@ -69,6 +65,7 @@ scenarioName: null, reportExportVisible: false, requestType: undefined, + fullTreeNodes: [], } }, activated() { @@ -92,6 +89,8 @@ this.content = {}; this.fails = []; this.report = {}; + this.fullTreeNodes = []; + this.failsTreeNodes = []; this.isRequestResult = false; }, handleClick(tab, event) { @@ -102,17 +101,88 @@ }, formatResult(res) { let resMap = new Map; + let array = []; + let i = 0; if (res && res.scenarios) { res.scenarios.forEach(item => { if (item && item.requestResults) { item.requestResults.forEach(req => { resMap.set(req.id, req); + req.index = i; + i++; + array.push(req); }) } }) } + this.formatTree(array, this.fullTreeNodes); + this.sort(this.fullTreeNodes); this.$emit('refresh', resMap); }, + formatTree(array, tree) { + array.map((item) => { + let key = item.name; + let nodeArray = key.split('^@~@^'); + let children = tree; + // 循环构建子节点 + for (let i in nodeArray) { + if (!nodeArray[i]) { + continue; + } + let node = { + label: nodeArray[i], + value: item, + }; + if (i !== nodeArray.length) { + node.children = []; + } + + if (children.length === 0) { + children.push(node); + } + + let isExist = false; + for (let j in children) { + if (children[j].label === node.label) { + if (i !== nodeArray.length - 1 && !children[j].children) { + children[j].children = []; + } + children = (i === nodeArray.length - 1 ? children : children[j].children); + isExist = true; + break; + } + } + if (!isExist) { + children.push(node); + if (i !== nodeArray.length - 1 && !children[children.length - 1].children) { + children[children.length - 1].children = []; + } + children = (i === nodeArray.length - 1 ? children : children[children.length - 1].children); + } + } + }) + }, + recursiveSorting(arr) { + for (let i in arr) { + if (arr[i]) { + arr[i].index = Number(i) + 1; + if (arr[i].children && arr[i].children.length > 0) { + this.recursiveSorting(arr[i].children); + } + } + } + }, + sort(scenarioDefinition) { + for (let i in scenarioDefinition) { + // 排序 + if (scenarioDefinition[i]) { + scenarioDefinition[i].index = Number(i) + 1; + if (scenarioDefinition[i].children && scenarioDefinition[i].children.length > 0) { + this.recursiveSorting(scenarioDefinition[i].children); + } + } + } + }, getReport() { this.init(); if (this.reportId) { @@ -146,6 +216,7 @@ getFails() { if (this.isNotRunning) { this.fails = []; + let array = []; this.totalTime = 0 if (this.content.scenarios) { this.content.scenarios.forEach((scenario) => { @@ -158,11 +229,14 @@ if (!request.success) { let failRequest = Object.assign({}, request); failScenario.requestResults.push(failRequest); + array.push(request); } }) } }) } + this.formatTree(array, this.failsTreeNodes); + this.sort(this.failsTreeNodes); } }, computeTotalTime() { diff --git a/frontend/src/business/components/api/automation/report/components/RequestResult.vue b/frontend/src/business/components/api/automation/report/components/RequestResult.vue index 3af4ceb7aa..f3c9f6768c 100644 --- a/frontend/src/business/components/api/automation/report/components/RequestResult.vue +++ b/frontend/src/business/components/api/automation/report/components/RequestResult.vue @@ -1,55 +1,58 @@ diff --git a/frontend/src/business/components/api/automation/scenario/ApiScenarioList.vue b/frontend/src/business/components/api/automation/scenario/ApiScenarioList.vue index 6bad44c0f6..7a5ec6041d 100644 --- a/frontend/src/business/components/api/automation/scenario/ApiScenarioList.vue +++ b/frontend/src/business/components/api/automation/scenario/ApiScenarioList.vue @@ -19,6 +19,7 @@ @@ -617,7 +618,7 @@ export default { }); }, handleSelectAll(selection) { - _handleSelectAll(this, selection, this.tableData, this.selectRows); + _handleSelectAll(this, selection, this.tableData, this.selectRows, this.condition); setUnSelectIds(this.tableData, this.condition, this.selectRows); this.selectDataCounts = getSelectDataCounts(this.condition, this.total, this.selectRows); this.$emit('selection', selection); diff --git a/frontend/src/business/components/api/automation/scenario/EditApiScenario.vue b/frontend/src/business/components/api/automation/scenario/EditApiScenario.vue index e4dbd337e7..61cb5ea460 100644 --- a/frontend/src/business/components/api/automation/scenario/EditApiScenario.vue +++ b/frontend/src/business/components/api/automation/scenario/EditApiScenario.vue @@ -1210,7 +1210,7 @@ /deep/ .el-tree-node__content { height: 100%; - margin-top: 8px; + margin-top: 3px; vertical-align: center; } diff --git a/frontend/src/business/components/api/automation/scenario/common/ApiBaseComponent.vue b/frontend/src/business/components/api/automation/scenario/common/ApiBaseComponent.vue index 706f6e2cb3..4d64bfec94 100644 --- a/frontend/src/business/components/api/automation/scenario/common/ApiBaseComponent.vue +++ b/frontend/src/business/components/api/automation/scenario/common/ApiBaseComponent.vue @@ -14,7 +14,7 @@ - diff --git a/frontend/src/business/components/api/definition/components/ApiKeyValue.vue b/frontend/src/business/components/api/definition/components/ApiKeyValue.vue index 1919d167d3..ad36eb17d7 100644 --- a/frontend/src/business/components/api/definition/components/ApiKeyValue.vue +++ b/frontend/src/business/components/api/definition/components/ApiKeyValue.vue @@ -28,8 +28,24 @@ - +
+ + + + +
import {KeyValue} from "../model/ApiTestModel"; import Vue from 'vue'; + import MsApiVariableAdvance from "./ApiVariableAdvance"; + import {JMETER_FUNC, MOCKJS_FUNC} from "@/common/js/constants"; export default { name: "MsApiKeyValue", + components: {MsApiVariableAdvance}, props: { keyPlaceholder: String, valuePlaceholder: String, @@ -60,6 +79,10 @@ default: false }, suggestions: Array, + needMock: { + type: Boolean, + default: false + } }, data() { return { @@ -86,6 +109,20 @@ } }, methods: { + advanced() { + this.$refs.variableAdvance.open(); + }, + funcFilter(queryString) { + return (func) => { + return (func.name.toLowerCase().indexOf(queryString.toLowerCase()) > -1); + }; + }, + funcSearch(queryString, cb) { + let funcs = MOCKJS_FUNC.concat(JMETER_FUNC); + let results = queryString ? funcs.filter(this.funcFilter(queryString)) : funcs; + // 调用 callback 返回建议列表的数据 + cb(results); + }, moveBottom(index) { if (this.items.length < 2 || index === this.items.length - 2) { return; diff --git a/frontend/src/business/components/api/definition/components/ApiVariable.vue b/frontend/src/business/components/api/definition/components/ApiVariable.vue index 6ba88c2dbf..b4b0ae0db0 100644 --- a/frontend/src/business/components/api/definition/components/ApiVariable.vue +++ b/frontend/src/business/components/api/definition/components/ApiVariable.vue @@ -39,7 +39,7 @@ - + - + @@ -126,6 +126,7 @@ currentItem: null, requireds: REQUIRED, isSelectAll: true, + isActive: true } }, watch: { @@ -231,6 +232,7 @@ } else { item.contentType = 'text/plain'; } + this.reload(); }, selectAll() { this.parameters.forEach(item => { @@ -242,6 +244,12 @@ item.enable = false; }); }, + reload() { + this.isActive = false; + this.$nextTick(() => { + this.isActive = true; + }); + } }, created() { if (this.parameters.length === 0 || this.parameters[this.parameters.length - 1].name) { diff --git a/frontend/src/business/components/api/definition/components/list/ApiCaseSimpleList.vue b/frontend/src/business/components/api/definition/components/list/ApiCaseSimpleList.vue index 56d2a83abc..ed6844a605 100644 --- a/frontend/src/business/components/api/definition/components/list/ApiCaseSimpleList.vue +++ b/frontend/src/business/components/api/definition/components/list/ApiCaseSimpleList.vue @@ -23,6 +23,7 @@ diff --git a/frontend/src/business/components/api/definition/components/list/ApiList.vue b/frontend/src/business/components/api/definition/components/list/ApiList.vue index 0308c5bd39..ac8b91ab54 100644 --- a/frontend/src/business/components/api/definition/components/list/ApiList.vue +++ b/frontend/src/business/components/api/definition/components/list/ApiList.vue @@ -22,6 +22,7 @@ @@ -547,7 +548,7 @@ export default { }); }, handleSelectAll(selection) { - _handleSelectAll(this, selection, this.tableData, this.selectRows); + _handleSelectAll(this, selection, this.tableData, this.selectRows, this.condition); setUnSelectIds(this.tableData, this.condition, this.selectRows); this.selectDataCounts = getSelectDataCounts(this.condition, this.total, this.selectRows); }, diff --git a/frontend/src/business/components/api/definition/components/request/http/ApiHttpRequestForm.vue b/frontend/src/business/components/api/definition/components/request/http/ApiHttpRequestForm.vue index 28d1ee0a12..59955b49ef 100644 --- a/frontend/src/business/components/api/definition/components/request/http/ApiHttpRequestForm.vue +++ b/frontend/src/business/components/api/definition/components/request/http/ApiHttpRequestForm.vue @@ -18,7 +18,7 @@ {{$t("commons.batch_add")}} - + diff --git a/frontend/src/business/components/common/components/MsModuleMinder.vue b/frontend/src/business/components/common/components/MsModuleMinder.vue index 51178116a6..2d1451b384 100644 --- a/frontend/src/business/components/common/components/MsModuleMinder.vue +++ b/frontend/src/business/components/common/components/MsModuleMinder.vue @@ -9,6 +9,7 @@ :tags="tags" :height="height" :distinct-tags="distinctTags" + @afterMount="$emit('afterMount')" @save="save" /> @@ -17,6 +18,7 @@ diff --git a/frontend/src/business/components/common/cron/CrontabDay.vue b/frontend/src/business/components/common/cron/CrontabDay.vue index 873a0c3582..11142a0a5c 100644 --- a/frontend/src/business/components/common/cron/CrontabDay.vue +++ b/frontend/src/business/components/common/cron/CrontabDay.vue @@ -23,8 +23,8 @@ {{$t('schedule.cron.from')}} - {{$t('schedule.cron.day_unit')}}{{$t('schedule.cron.start')}},{{$t('schedule.cron.every')}} - {{$t('schedule.cron.day')}}{{$t('schedule.cron.execute_once')}} + {{$t('schedule.cron.day_unit')}}{{$t('schedule.cron.start')}},{{$t('schedule.cron.every')}} + {{$t('schedule.cron.day')}}{{$t('schedule.cron.execute_once')}} diff --git a/frontend/src/business/components/common/cron/CrontabHour.vue b/frontend/src/business/components/common/cron/CrontabHour.vue index 85be90a1ee..a09d5ea05f 100644 --- a/frontend/src/business/components/common/cron/CrontabHour.vue +++ b/frontend/src/business/components/common/cron/CrontabHour.vue @@ -17,8 +17,8 @@ {{$t('schedule.cron.from')}} - {{$t('schedule.cron.hours')}}{{$t('schedule.cron.start')}},{{$t('schedule.cron.every')}} - {{$t('schedule.cron.hours')}}{{$t('schedule.cron.execute_once')}} + {{$t('schedule.cron.hours')}}{{$t('schedule.cron.start')}},{{$t('schedule.cron.every')}} + {{$t('schedule.cron.hours')}}{{$t('schedule.cron.execute_once')}} diff --git a/frontend/src/business/components/common/cron/CrontabMin.vue b/frontend/src/business/components/common/cron/CrontabMin.vue index f2a9d68ca9..f3e898b9c0 100644 --- a/frontend/src/business/components/common/cron/CrontabMin.vue +++ b/frontend/src/business/components/common/cron/CrontabMin.vue @@ -17,8 +17,8 @@ {{$t('schedule.cron.from')}} - {{$t('schedule.cron.minutes')}}{{$t('schedule.cron.start')}},{{$t('schedule.cron.every')}} - {{$t('schedule.cron.minutes')}}{{$t('schedule.cron.execute_once')}} + {{$t('schedule.cron.minutes')}}{{$t('schedule.cron.start')}},{{$t('schedule.cron.every')}} + {{$t('schedule.cron.minutes')}}{{$t('schedule.cron.execute_once')}} diff --git a/frontend/src/business/components/common/cron/CrontabSecond.vue b/frontend/src/business/components/common/cron/CrontabSecond.vue index c0fd71b5eb..13a68b28b6 100644 --- a/frontend/src/business/components/common/cron/CrontabSecond.vue +++ b/frontend/src/business/components/common/cron/CrontabSecond.vue @@ -17,8 +17,8 @@ {{$t('schedule.cron.from')}} - {{$t('schedule.cron.seconds')}}{{$t('schedule.cron.start')}},{{$t('schedule.cron.every')}} - {{$t('schedule.cron.seconds')}}{{$t('schedule.cron.execute_once')}} + {{$t('schedule.cron.seconds')}}{{$t('schedule.cron.start')}},{{$t('schedule.cron.every')}} + {{$t('schedule.cron.seconds')}}{{$t('schedule.cron.execute_once')}} diff --git a/frontend/src/business/components/settings/organization/OrganizationMember.vue b/frontend/src/business/components/settings/organization/OrganizationMember.vue index 85f5db6fb6..e6182503f8 100644 --- a/frontend/src/business/components/settings/organization/OrganizationMember.vue +++ b/frontend/src/business/components/settings/organization/OrganizationMember.vue @@ -13,6 +13,7 @@ @@ -365,7 +366,7 @@ }); }, handleSelectAll(selection) { - _handleSelectAll(this, selection, this.tableData, this.selectRows); + _handleSelectAll(this, selection, this.tableData, this.selectRows, this.condition); setUnSelectIds(this.tableData, this.condition, this.selectRows); this.selectDataCounts = getSelectDataCounts(this.condition, this.total, this.selectRows); this.$emit('selection', selection); diff --git a/frontend/src/business/components/settings/system/User.vue b/frontend/src/business/components/settings/system/User.vue index 277a125398..6f6eb26d70 100644 --- a/frontend/src/business/components/settings/system/User.vue +++ b/frontend/src/business/components/settings/system/User.vue @@ -16,6 +16,7 @@ @@ -746,7 +747,7 @@ export default { }); }, handleSelectAll(selection) { - _handleSelectAll(this, selection, this.tableData, this.selectRows); + _handleSelectAll(this, selection, this.tableData, this.selectRows, this.condition); setUnSelectIds(this.tableData, this.condition, this.selectRows); this.selectDataCounts = getSelectDataCounts(this.condition, this.total, this.selectRows); this.$emit('selection', selection); diff --git a/frontend/src/business/components/settings/workspace/WorkspaceMember.vue b/frontend/src/business/components/settings/workspace/WorkspaceMember.vue index 7c65d2997d..704594e005 100644 --- a/frontend/src/business/components/settings/workspace/WorkspaceMember.vue +++ b/frontend/src/business/components/settings/workspace/WorkspaceMember.vue @@ -14,6 +14,7 @@ @@ -361,7 +362,7 @@ }); }, handleSelectAll(selection) { - _handleSelectAll(this, selection, this.tableData, this.selectRows); + _handleSelectAll(this, selection, this.tableData, this.selectRows, this.condition); setUnSelectIds(this.tableData, this.condition, this.selectRows); this.selectDataCounts = getSelectDataCounts(this.condition, this.total, this.selectRows); this.$emit('selection', selection); diff --git a/frontend/src/business/components/track/case/components/TestCaseList.vue b/frontend/src/business/components/track/case/components/TestCaseList.vue index ac70504b00..581e79845c 100644 --- a/frontend/src/business/components/track/case/components/TestCaseList.vue +++ b/frontend/src/business/components/track/case/components/TestCaseList.vue @@ -24,6 +24,7 @@ @@ -499,7 +500,7 @@ export default { this.$emit('testCaseDetail', row); }, handleSelectAll(selection) { - _handleSelectAll(this, selection, this.tableData, this.selectRows); + _handleSelectAll(this, selection, this.tableData, this.selectRows, this.condition); setUnSelectIds(this.tableData, this.condition, this.selectRows); this.selectDataCounts = getSelectDataCounts(this.condition, this.total, this.selectRows); }, diff --git a/frontend/src/business/components/track/common/minder/TestPlanMinder.vue b/frontend/src/business/components/track/common/minder/TestPlanMinder.vue index d9f3634096..55af1933fe 100644 --- a/frontend/src/business/components/track/common/minder/TestPlanMinder.vue +++ b/frontend/src/business/components/track/common/minder/TestPlanMinder.vue @@ -4,7 +4,10 @@ :tree-nodes="treeNodes" :data-map="dataMap" :tags="tags" - :distinct-tags="[...tags, $t('test_track.plan.plan_status_prepare')]" + :tag-enable="true" + :select-node="selectNode" + :distinct-tags="[...tags, this.$t('test_track.plan.plan_status_prepare')]" + @afterMount="handleAfterMount" @save="save" ref="minder" /> @@ -12,7 +15,10 @@