diff --git a/.gitignore b/.gitignore index 165db4e885..309a8126a5 100644 --- a/.gitignore +++ b/.gitignore @@ -14,7 +14,8 @@ yarn-debug.log* yarn-error.log* # Editor directories and files -.idea +.idea/* +!.idea/icon.png **/*.iml .vscode *.suo diff --git a/.idea/icon.png b/.idea/icon.png new file mode 100644 index 0000000000..15fb97a693 Binary files /dev/null and b/.idea/icon.png differ diff --git a/Jenkinsfile b/Jenkinsfile index 994ddca141..653ff55bbd 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -13,6 +13,9 @@ pipeline { stage('Build/Test') { steps { configFileProvider([configFile(fileId: 'metersphere-maven', targetLocation: 'settings.xml')]) { + sh "cd frontend" + sh "yarn install" + sh "cd .." sh "mvn clean package --settings ./settings.xml" } } diff --git a/backend/pom.xml b/backend/pom.xml index be89a74d2e..c54fc88b7c 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -283,6 +283,13 @@ 2.0.22 + + + org.codehaus.groovy + groovy-jsr223 + 2.5.10 + + org.graalvm.sdk 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 a830b1d656..8655751191 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 @@ -149,6 +149,14 @@ public class Swagger2Parser extends SwaggerAbstractParser { return getBodyType(contentType); } + private String getResponseBodyType(Operation operation) { + if (CollectionUtils.isEmpty(operation.getProduces())) { + return Body.JSON; + } + String contentType = operation.getProduces().get(0); + return getBodyType(contentType); + } + private void parsePathParameters(Parameter parameter, List rests) { PathParameter pathParameter = (PathParameter) parameter; rests.add(new KeyValue(pathParameter.getName(), "", getDefaultStringValue(parameter.getDescription()))); @@ -175,14 +183,17 @@ public class Swagger2Parser extends SwaggerAbstractParser { msResponse.getBody().setKvs(new ArrayList<>()); msResponse.setHeaders(new ArrayList<>()); msResponse.setType(RequestType.HTTP); - msResponse.getBody().setType(getBodyType(operation)); + msResponse.getBody().setType(getResponseBodyType(operation)); // todo 状态码要调整? msResponse.setStatusCode(new ArrayList<>()); - if (responses != null) { + if (responses != null && responses.size() > 0) { responses.forEach((responseCode, response) -> { msResponse.getStatusCode().add(new KeyValue(responseCode, responseCode)); + String body = parseSchema(response.getResponseSchema()); + if (StringUtils.isNotBlank(body)) { + msResponse.getBody().setRaw(body); + } parseResponseHeader(response, msResponse.getHeaders()); - parseResponseBodyParameters(response, msResponse.getBody()); }); } return msResponse; @@ -197,10 +208,6 @@ public class Swagger2Parser extends SwaggerAbstractParser { } } - private void parseResponseBodyParameters(Response response, Body body) { - body.setRaw(parseSchema(response.getResponseSchema())); - } - private void parseRequestBodyParameters(Parameter parameter, Body body) { BodyParameter bodyParameter = (BodyParameter) parameter; body.setRaw(parseSchema(bodyParameter.getSchema())); 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 5e96e31159..dd25a0c8b5 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,12 +26,15 @@ 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.*; @@ -334,6 +337,16 @@ public class Swagger3Parser extends SwaggerAbstractParser { } else if (schema instanceof BinarySchema) { return getDefaultValueByPropertyType(schema); } else { + if(schema.getType() != null) { // 特判属性不是对象的情况,直接将基本类型赋值进去 + if(StringUtils.equals(schema.getType(), "string")) { + String example = (String) schema.getExample(); + return example == null ? "" : example; + } else if(StringUtils.equals(schema.getType(), "boolean")) { + return schema.getExample(); + } else if(StringUtils.equals(schema.getType(), "double")) { + return schema.getExample(); + } + } Object propertiesResult = parseSchemaProperties(schema, refSet, infoMap); return propertiesResult == null ? getDefaultValueByPropertyType(schema) : propertiesResult; } @@ -432,7 +445,9 @@ public class Swagger3Parser extends SwaggerAbstractParser { JSONObject requestBody = buildRequestBody(requestObject); swaggerApiInfo.setRequestBody(requestBody); // 设置响应体 - swaggerApiInfo.setResponses(new JSONObject()); +// JSONObject reponseObject = JSON.parseObject(apiDefinition.getResponse()); +// swaggerApiInfo.setResponses(buildResponseBody(reponseObject)); + //..... // 设置请求参数列表 List paramsList = buildParameters(requestObject); swaggerApiInfo.setParameters(paramsList); @@ -459,6 +474,9 @@ public class Swagger3Parser extends SwaggerAbstractParser { if(params != null) { for(int i = 0; i < params.size(); ++i) { JSONObject param = params.getJSONObject(i); // 对于每个参数: + if(param.get("name") == null || StringUtils.isEmpty(((String) param.get("name")))) { + continue; + } // 否则无参数的情况,可能多出一行空行 SwaggerParams swaggerParam = new SwaggerParams(); swaggerParam.setIn(typeMap.get(type)); // 利用 map,根据 request 的 key 设置对应的参数类型 swaggerParam.setDescription((String) param.get("description")); @@ -480,9 +498,20 @@ public class Swagger3Parser extends SwaggerAbstractParser { put("Form Data", org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE); put("WWW_FORM", org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED_VALUE); }}; + JSONObject bodyInfo = new JSONObject(); + if(request.getJSONObject("body") != null && request.getJSONObject("body").containsKey("raw")) { + String bodyType = request.getJSONObject("body").getString("type"); + if(bodyType.equals("JSON")) { + bodyInfo = buildRequestBodyJsonInfo(request.getJSONObject("body").getJSONObject("raw")); + } else if(bodyType.equals("XML")) { + String xmlText = request.getJSONObject("body").getString("raw"); + JSONObject xmlToJson = XMLUtils.XmlToJson(xmlText); + bodyInfo = buildRequestBodyJsonInfo(xmlToJson); + } + } String type = request.getJSONObject("body").getString("type"); JSONObject requestBody = new JSONObject(); - JSONObject schema = new JSONObject(); + JSONObject schema = bodyInfo; // 需要转换导出 JSONObject typeName = new JSONObject(); schema.put("type", null); schema.put("format", null); @@ -494,4 +523,55 @@ public class Swagger3Parser extends SwaggerAbstractParser { requestBody.put("content", content); return requestBody; } + + // 将请求体中的一个 json 对象转换成 swagger 格式的 json 对象返回 + private JSONObject buildRequestBodyJsonInfo(JSONObject requestBody) { + JSONObject schema = new JSONObject(); + schema.put("type", "object"); + JSONObject properties = buildSchema(requestBody); + schema.put("properties", properties); + return schema; + } + + // 设置一个 json 对象的属性在 swagger 格式中的类型、值 + private JSONObject buildSchema(JSONObject requestBody) { + JSONObject schema = new JSONObject(); + for(String key : requestBody.keySet()) { + Object param = requestBody.get(key); + JSONObject parsedParam = new JSONObject(); + if(param instanceof java.lang.String) { + parsedParam.put("type", "string"); + parsedParam.put("example", param == null? "" : param); + } else if(param instanceof java.lang.Integer) { + parsedParam.put("type", "integer"); + parsedParam.put("format", "int64"); + parsedParam.put("example", param); + } else if(param instanceof JSONObject) { + parsedParam = buildRequestBodyJsonInfo((JSONObject) param); + } else if(param instanceof java.lang.Boolean) { + parsedParam.put("type", "boolean"); + parsedParam.put("example", param); + } else if(param instanceof java.math.BigDecimal) { // double 类型会被 fastJson 转换为 BigDecimal + parsedParam.put("type", "double"); + parsedParam.put("example", param); + } else { // JSONOArray + parsedParam.put("type", "array"); + JSONObject item = new JSONObject(); + if(((JSONArray) param).size() > 0) { + if(((JSONArray) param).get(0) instanceof JSONObject) { /// + item = buildRequestBodyJsonInfo((JSONObject) ((JSONArray) param).get(0)); + } + } + parsedParam.put("items", item); + } + schema.put(key, parsedParam); + } + return schema; + } + + private JSONObject buildResponseBody(JSONObject reponse) { + JSONObject responseBody = new JSONObject(); + + return responseBody; + } } 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 2d88dde7f5..d70423500e 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 @@ -130,7 +130,7 @@ public class MsHTTPSamplerProxy extends MsTestElement { } // 1.8 之前历史数据 - if(StringUtils.isEmpty(this.getProjectId()) && config.getConfig()!= null && !config.getConfig().isEmpty()){ + if (StringUtils.isEmpty(this.getProjectId()) && config.getConfig() != null && !config.getConfig().isEmpty()) { this.setProjectId("historyProjectID"); } @@ -154,10 +154,11 @@ public class MsHTTPSamplerProxy extends MsTestElement { } URL urlObject = new URL(url); sampler.setDomain(URLDecoder.decode(urlObject.getHost(), "UTF-8")); - if (urlObject.getPort() > 0 && urlObject.getPort() != 10990 && StringUtils.isNotEmpty(this.getPort()) && this.getPort().startsWith("${")) { - sampler.setPort(urlObject.getPort()); - } else { + + if (urlObject.getPort() > 0 && urlObject.getPort() == 10990 && StringUtils.isNotEmpty(this.getPort()) && this.getPort().startsWith("${")) { sampler.setProperty("HTTPSampler.port", this.getPort()); + } else { + sampler.setPort(urlObject.getPort()); } sampler.setProtocol(urlObject.getProtocol()); sampler.setPath(urlObject.getPath()); 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 4cce2bef3a..9f969416cb 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java @@ -21,10 +21,7 @@ import io.metersphere.api.dto.scenario.environment.EnvironmentConfig; import io.metersphere.api.jmeter.JMeterService; import io.metersphere.api.parse.ApiImportParser; import io.metersphere.base.domain.*; -import io.metersphere.base.mapper.ApiScenarioMapper; -import io.metersphere.base.mapper.ApiScenarioReportMapper; -import io.metersphere.base.mapper.TestCaseReviewScenarioMapper; -import io.metersphere.base.mapper.TestPlanApiScenarioMapper; +import io.metersphere.base.mapper.*; import io.metersphere.base.mapper.ext.*; import io.metersphere.commons.constants.*; import io.metersphere.commons.exception.MSException; @@ -61,6 +58,8 @@ import java.util.stream.Collectors; @Service @Transactional(rollbackFor = Exception.class) public class ApiAutomationService { + @Resource + ApiScenarioModuleMapper apiScenarioModuleMapper; @Resource private ExtScheduleMapper extScheduleMapper; @Resource @@ -242,9 +241,14 @@ public class ApiAutomationService { } else { scenario.setUserId(request.getUserId()); } - if (StringUtils.isEmpty(request.getApiScenarioModuleId())) { - scenario.setApiScenarioModuleId("root"); - scenario.setModulePath("/默认模块"); + if (StringUtils.isEmpty(request.getApiScenarioModuleId()) || StringUtils.isEmpty(request.getModulePath()) || "default-module".equals(request.getApiScenarioModuleId())) { + ApiScenarioModuleExample example = new ApiScenarioModuleExample(); + example.createCriteria().andProjectIdEqualTo(request.getProjectId()).andNameEqualTo("默认模块"); + List modules = apiScenarioModuleMapper.selectByExample(example); + if (CollectionUtils.isNotEmpty(modules)) { + scenario.setApiScenarioModuleId(modules.get(0).getId()); + scenario.setModulePath(modules.get(0).getName()); + } } return scenario; } 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 af9f596c12..435846891b 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java @@ -94,6 +94,8 @@ public class ApiDefinitionService { private ApiTestEnvironmentService environmentService; @Resource private EsbApiParamService esbApiParamService; + @Resource + ApiModuleMapper apiModuleMapper; private static Cache cache = Cache.newHardMemoryCache(0, 3600 * 24); @@ -262,9 +264,14 @@ public class ApiDefinitionService { test.setEnvironmentId(request.getEnvironmentId()); test.setUserId(request.getUserId()); test.setTags(request.getTags()); - if (StringUtils.isEmpty(request.getModuleId())) { - test.setModulePath("/默认模块"); - test.setModuleId("root"); + if (StringUtils.isEmpty(request.getModulePath()) || StringUtils.isEmpty(request.getModuleId()) || "default-module".equals(request.getModuleId())) { + ApiModuleExample example = new ApiModuleExample(); + example.createCriteria().andProjectIdEqualTo(test.getProjectId()).andProtocolEqualTo(test.getProtocol()).andNameEqualTo("默认模块"); + List modules = apiModuleMapper.selectByExample(example); + if (CollectionUtils.isNotEmpty(modules)) { + test.setModuleId(modules.get(0).getId()); + test.setModulePath(modules.get(0).getName()); + } } apiDefinitionMapper.updateByPrimaryKeySelective(test); return test; @@ -290,9 +297,14 @@ public class ApiDefinitionService { test.setStatus(APITestStatus.Underway.name()); test.setModulePath(request.getModulePath()); test.setModuleId(request.getModuleId()); - if (StringUtils.isEmpty(request.getModuleId())) { - test.setModulePath("/默认模块"); - test.setModuleId("root"); + if (StringUtils.isEmpty(request.getModulePath()) || StringUtils.isEmpty(request.getModuleId()) || "default-module".equals(request.getModuleId())) { + ApiModuleExample example = new ApiModuleExample(); + example.createCriteria().andProjectIdEqualTo(test.getProjectId()).andProtocolEqualTo(test.getProtocol()).andNameEqualTo("默认模块"); + List modules = apiModuleMapper.selectByExample(example); + if (CollectionUtils.isNotEmpty(modules)) { + test.setModuleId(modules.get(0).getId()); + test.setModulePath("/默认模块"); + } } test.setResponse(JSONObject.toJSONString(request.getResponse())); test.setEnvironmentId(request.getEnvironmentId()); @@ -604,9 +616,14 @@ public class ApiDefinitionService { } for (int i = 0; i < data.size(); i++) { ApiDefinitionWithBLOBs item = data.get(i); - if (StringUtils.isEmpty(item.getModuleId())) { - item.setModuleId("root"); - item.setModulePath("/默认模块"); + if (StringUtils.isEmpty(item.getModuleId()) || StringUtils.isEmpty(item.getModulePath()) || "default-module".equals(item.getModuleId())) { + ApiModuleExample example = new ApiModuleExample(); + example.createCriteria().andProjectIdEqualTo(item.getProjectId()).andProtocolEqualTo(item.getProtocol()).andNameEqualTo("默认模块"); + List modules = apiModuleMapper.selectByExample(example); + if (CollectionUtils.isNotEmpty(modules)) { + item.setModuleId(modules.get(0).getId()); + item.setModulePath(modules.get(0).getName()); + } } if (item.getName().length() > 255) { item.setName(item.getName().substring(0, 255)); 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 a0eddeeca5..b620710743 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiModuleService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiModuleService.java @@ -39,8 +39,6 @@ public class ApiModuleService extends NodeTreeService { @Resource ExtApiModuleMapper extApiModuleMapper; @Resource - private ApiDefinitionMapper apiDefinitionMapper; - @Resource private ExtApiDefinitionMapper extApiDefinitionMapper; @Resource private TestPlanProjectService testPlanProjectService; @@ -61,6 +59,22 @@ public class ApiModuleService extends NodeTreeService { } public List getNodeTreeByProjectId(String projectId, String protocol) { + // 判断当前项目下是否有默认模块,没有添加默认模块 + ApiModuleExample example = new ApiModuleExample(); + example.createCriteria().andProjectIdEqualTo(projectId).andProtocolEqualTo(protocol).andNameEqualTo("默认模块"); + long count = apiModuleMapper.countByExample(example); + if (count <= 0) { + ApiModule record = new ApiModule(); + record.setId(UUID.randomUUID().toString()); + record.setName("默认模块"); + record.setProtocol(protocol); + record.setPos(1.0); + record.setLevel(1); + record.setCreateTime(System.currentTimeMillis()); + record.setUpdateTime(System.currentTimeMillis()); + record.setProjectId(projectId); + apiModuleMapper.insert(record); + } List apiModules = extApiModuleMapper.getNodeTreeByProjectId(projectId, protocol); return getNodeTrees(apiModules); } diff --git a/backend/src/main/java/io/metersphere/api/service/ApiScenarioModuleService.java b/backend/src/main/java/io/metersphere/api/service/ApiScenarioModuleService.java index 23493f60c5..18ad89eed4 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiScenarioModuleService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiScenarioModuleService.java @@ -53,6 +53,22 @@ public class ApiScenarioModuleService extends NodeTreeService getNodeTreeByProjectId(String projectId) { + // 判断当前项目下是否有默认模块,没有添加默认模块 + ApiScenarioModuleExample example = new ApiScenarioModuleExample(); + example.createCriteria().andProjectIdEqualTo(projectId).andNameEqualTo("默认模块"); + long count = apiScenarioModuleMapper.countByExample(example); + if (count <= 0) { + ApiScenarioModule record = new ApiScenarioModule(); + record.setId(UUID.randomUUID().toString()); + record.setName("默认模块"); + record.setPos(1.0); + record.setLevel(1); + record.setCreateTime(System.currentTimeMillis()); + record.setUpdateTime(System.currentTimeMillis()); + record.setProjectId(projectId); + apiScenarioModuleMapper.insert(record); + } + List nodes = extApiScenarioModuleMapper.getNodeTreeByProjectId(projectId); return getNodeTrees(nodes); } diff --git a/backend/src/main/java/io/metersphere/commons/utils/XMLUtils.java b/backend/src/main/java/io/metersphere/commons/utils/XMLUtils.java index 10eb61872c..f8ab892f8c 100644 --- a/backend/src/main/java/io/metersphere/commons/utils/XMLUtils.java +++ b/backend/src/main/java/io/metersphere/commons/utils/XMLUtils.java @@ -2,46 +2,140 @@ package io.metersphere.commons.utils; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; +import io.metersphere.commons.exception.MSException; +import io.metersphere.i18n.Translator; import org.apache.commons.lang3.StringUtils; -import java.util.Map; -import java.util.Set; +import java.util.*; +import java.util.regex.*; public class XMLUtils { - private static void jsonToXmlStr(JSONObject jObj, StringBuffer buffer) { + private static void jsonToXmlStr(JSONObject jObj, StringBuffer buffer, StringBuffer tab) { Set> se = jObj.entrySet(); + StringBuffer nowTab = new StringBuffer(tab.toString()); for (Map.Entry en : se) { if ("com.alibaba.fastjson.JSONObject".equals(en.getValue().getClass().getName())) { - buffer.append("<").append(en.getKey()).append(">"); + buffer.append(tab).append("<").append(en.getKey()).append(">\n"); JSONObject jo = jObj.getJSONObject(en.getKey()); - jsonToXmlStr(jo, buffer); - buffer.append(""); + jsonToXmlStr(jo, buffer, nowTab.append("\t")); + buffer.append(tab).append("\n"); } else if ("com.alibaba.fastjson.JSONArray".equals(en.getValue().getClass().getName())) { JSONArray jarray = jObj.getJSONArray(en.getKey()); for (int i = 0; i < jarray.size(); i++) { - buffer.append("<").append(en.getKey()).append(">"); + buffer.append(tab).append("<").append(en.getKey()).append(">\n"); if (StringUtils.isNotBlank(jarray.getString(i))) { JSONObject jsonobject = jarray.getJSONObject(i); - jsonToXmlStr(jsonobject, buffer); - buffer.append(""); + jsonToXmlStr(jsonobject, buffer, nowTab.append("\t")); + buffer.append(tab).append("\n"); } } } else if ("java.lang.String".equals(en.getValue().getClass().getName())) { - buffer.append("<").append(en.getKey()).append(">").append(en.getValue()); - buffer.append(""); + buffer.append(tab).append("<").append(en.getKey()).append(">").append(en.getValue()); + buffer.append("\n"); } } } public static String jsonToXmlStr(JSONObject jObj) { StringBuffer buffer = new StringBuffer(); - buffer.append(""); + buffer.append("\n"); try { - jsonToXmlStr(jObj, buffer); + jsonToXmlStr(jObj, buffer, new StringBuffer("")); } catch (Exception e) { LogUtil.error(e.getMessage(), e); } return buffer.toString(); } + + // 传入完整的 xml 文本,转换成 json 对象 + public static JSONObject XmlToJson(String xml) { + JSONObject result = new JSONObject(); + List list = preProcessXml(xml); + try { + result = (JSONObject) XmlTagToJsonObject(list); + } catch (Exception e) { + LogUtil.error(e.getMessage(), e); + MSException.throwException(Translator.get("illegal_xml_format")); + } + return result; + } + + // 预处理 xml 文本,转换成 tag + data 的列表 + private static List preProcessXml(String xml) { + int begin = xml.indexOf("?>"); + if(begin != -1) { + if(begin + 2 >= xml.length()) { + return null; + } + xml = xml.substring(begin + 2); + } // 若存在,则去除 + String rgex = "\\s*"; + Pattern pattern = Pattern.compile(rgex); + Matcher m = pattern.matcher(xml); + xml = m.replaceAll(""); + rgex = ">"; + pattern = Pattern.compile(rgex); + m = pattern.matcher(xml); + xml = m.replaceAll("> "); + rgex = "\\s* list) { + if(list == null || list.size() == 0) + return null; + Stack tagStack = new Stack<>(); // tag 栈 + Stack valueStack = new Stack<>(); // 数据栈 + valueStack.push(new JSONObject()); // 最终结果将存放在第一个入栈的元素中 + for(String item : list) { + String beginTag = isBeginTag(item), endTag = isEndTag(item); // 判断当前 tag 是开始还是结尾 + if(beginTag != null) { + tagStack.push(beginTag); + valueStack.push(new JSONObject()); + } else if(endTag != null) { + if(endTag.equals(tagStack.peek())) { // 是一对 tag + Object topValue = valueStack.peek(); + if(topValue instanceof String) { // 栈顶是纯数据 xml 节点 + valueStack.pop(); + } + valueStack.pop(); + if(valueStack.peek() instanceof JSONObject) { + ((JSONObject) valueStack.peek()).put(tagStack.peek(), topValue); + } + tagStack.pop(); + } + } else { + valueStack.push(item); + } + } + if(valueStack.empty()) + return null; + return valueStack.peek(); + } + + private static String isEndTag(String tagLine) { + String rgex = ""; + Pattern pattern = Pattern.compile(rgex);// 匹配的模式     + Matcher m = pattern.matcher(tagLine); + if (m.find()) { + return m.group(1); + } + return null; + } + + private static String isBeginTag(String tagLine) { + String rgex = "<(\\w*)>"; + Pattern pattern = Pattern.compile(rgex);// 匹配的模式     + Matcher m = pattern.matcher(tagLine); + if (m.find()) { + return m.group(1); + } + return null; + } + } diff --git a/backend/src/main/java/io/metersphere/performance/parse/xml/reader/jmx/JmeterDocumentParser.java b/backend/src/main/java/io/metersphere/performance/parse/xml/reader/jmx/JmeterDocumentParser.java index 09fbda18a5..a8d33e4391 100644 --- a/backend/src/main/java/io/metersphere/performance/parse/xml/reader/jmx/JmeterDocumentParser.java +++ b/backend/src/main/java/io/metersphere/performance/parse/xml/reader/jmx/JmeterDocumentParser.java @@ -701,9 +701,11 @@ public class JmeterDocumentParser implements DocumentParser { switch (unit) { case "M": duration = String.valueOf(Long.parseLong(duration) * 60); + rampUp = String.valueOf(Long.parseLong(rampUp) * 60); break; case "H": duration = String.valueOf(Long.parseLong(duration) * 60 * 60); + rampUp = String.valueOf(Long.parseLong(rampUp) * 60 * 60); break; default: break; @@ -804,9 +806,11 @@ public class JmeterDocumentParser implements DocumentParser { switch (unit) { case "M": hold = String.valueOf(Long.parseLong(hold) * 60); + rampUp = String.valueOf(Long.parseLong(rampUp) * 60); break; case "H": hold = String.valueOf(Long.parseLong(hold) * 60 * 60); + rampUp = String.valueOf(Long.parseLong(rampUp) * 60 * 60); break; default: break; diff --git a/backend/src/main/java/io/metersphere/track/service/TestCaseNodeService.java b/backend/src/main/java/io/metersphere/track/service/TestCaseNodeService.java index 2e6658799e..636deb6ac0 100644 --- a/backend/src/main/java/io/metersphere/track/service/TestCaseNodeService.java +++ b/backend/src/main/java/io/metersphere/track/service/TestCaseNodeService.java @@ -107,6 +107,21 @@ public class TestCaseNodeService extends NodeTreeService { } public List getNodeTreeByProjectId(String projectId) { + // 判断当前项目下是否有默认模块,没有添加默认模块 + TestCaseNodeExample example = new TestCaseNodeExample(); + example.createCriteria().andProjectIdEqualTo(projectId).andNameEqualTo("默认模块"); + long count = testCaseNodeMapper.countByExample(example); + if (count <= 0) { + TestCaseNode record = new TestCaseNode(); + record.setId(UUID.randomUUID().toString()); + record.setName("默认模块"); + record.setPos(1.0); + record.setLevel(1); + record.setCreateTime(System.currentTimeMillis()); + record.setUpdateTime(System.currentTimeMillis()); + record.setProjectId(projectId); + testCaseNodeMapper.insert(record); + } List testCaseNodes = extTestCaseNodeMapper.getNodeTreeByProjectId(projectId); return getNodeTrees(testCaseNodes); } diff --git a/backend/src/main/java/io/metersphere/track/service/TestCaseService.java b/backend/src/main/java/io/metersphere/track/service/TestCaseService.java index 1ed153236f..881d60f382 100644 --- a/backend/src/main/java/io/metersphere/track/service/TestCaseService.java +++ b/backend/src/main/java/io/metersphere/track/service/TestCaseService.java @@ -52,6 +52,8 @@ import java.util.stream.Collectors; @Service @Transactional(rollbackFor = Exception.class) public class TestCaseService { + @Resource + TestCaseNodeMapper testCaseNodeMapper; @Resource TestCaseMapper testCaseMapper; @@ -90,6 +92,18 @@ public class TestCaseService { @Resource TestCaseTestMapper testCaseTestMapper; + private void setNode(TestCaseWithBLOBs testCase){ + if (StringUtils.isEmpty(testCase.getNodeId()) || StringUtils.isEmpty(testCase.getNodePath()) || "default-module".equals(testCase.getNodeId())) { + TestCaseNodeExample example = new TestCaseNodeExample(); + example.createCriteria().andProjectIdEqualTo(testCase.getProjectId()).andNameEqualTo("默认模块"); + List nodes = testCaseNodeMapper.selectByExample(example); + if (CollectionUtils.isNotEmpty(nodes)) { + testCase.setNodeId(nodes.get(0).getId()); + testCase.setNodePath("/" + nodes.get(0).getName()); + } + } + } + public TestCaseWithBLOBs addTestCase(TestCaseWithBLOBs testCase) { testCase.setName(testCase.getName()); checkTestCaseExist(testCase); @@ -100,7 +114,7 @@ public class TestCaseService { testCase.setReviewStatus(TestCaseReviewStatus.Prepare.name()); testCase.setDemandId(testCase.getDemandId()); testCase.setDemandName(testCase.getDemandName()); - + this.setNode(testCase); testCaseMapper.insert(testCase); return testCase; } @@ -179,7 +193,7 @@ public class TestCaseService { String steps = tc.getSteps(); String remark = tc.getRemark(); if (StringUtils.equals(steps, testCase.getSteps()) && StringUtils.equals(remark, caseRemark)) { - //MSException.throwException(Translator.get("test_case_already_exists")); + //MSException.throwException(Translator.get("test_case_already_exists")); isExt = true; } } @@ -195,16 +209,16 @@ public class TestCaseService { * 根据id和pojectId查询id是否在数据库中存在。 * 在数据库中单id的话是可重复的,id与projectId的组合是唯一的 */ - public Integer checkIdExist(Integer id, String projectId){ + public Integer checkIdExist(Integer id, String projectId) { TestCaseExample example = new TestCaseExample(); TestCaseExample.Criteria criteria = example.createCriteria(); if (null != id) { criteria.andNumEqualTo(id); criteria.andProjectIdEqualTo(projectId); long count = testCaseMapper.countByExample(example); //查询是否有包含此ID的数据 - if(count == 0){ //如果ID不存在 + if (count == 0) { //如果ID不存在 return null; - }else { //有对应ID的数据 + } else { //有对应ID的数据 return id; } } @@ -415,7 +429,7 @@ public class TestCaseService { testcase.setSort(sort.getAndIncrement()); testcase.setNum(num.decrementAndGet()); testcase.setReviewStatus(TestCaseReviewStatus.Prepare.name()); - mapper.insert(testcase); + mapper.insert(testcase); }); } sqlSession.flushStatements(); @@ -443,6 +457,7 @@ public class TestCaseService { /** * 把Excel中带ID的数据更新到数据库 + * * @param testCases * @param projectId */ @@ -820,14 +835,14 @@ public class TestCaseService { if (files != null) { files.forEach(file -> { - final FileMetadata fileMetadata = fileService.saveFile(file,testCaseWithBLOBs.getProjectId()); + final FileMetadata fileMetadata = fileService.saveFile(file, testCaseWithBLOBs.getProjectId()); TestCaseFile testCaseFile = new TestCaseFile(); testCaseFile.setFileId(fileMetadata.getId()); testCaseFile.setCaseId(request.getId()); testCaseFileMapper.insert(testCaseFile); }); } - + this.setNode(request); editTestCase(request); return request.getId(); } diff --git a/backend/src/main/java/org/apache/jmeter/util/JSR223TestElement.java b/backend/src/main/java/org/apache/jmeter/util/JSR223TestElement.java index 9f70de2d49..ba173d2d98 100644 --- a/backend/src/main/java/org/apache/jmeter/util/JSR223TestElement.java +++ b/backend/src/main/java/org/apache/jmeter/util/JSR223TestElement.java @@ -24,6 +24,7 @@ import java.io.FileReader; import java.io.IOException; import java.io.Serializable; import java.util.Collections; +import java.util.List; import java.util.Map; import java.util.Properties; @@ -34,6 +35,10 @@ import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.ScriptException; +import io.metersphere.base.domain.JarConfig; +import io.metersphere.commons.utils.CommonBeanFactory; +import io.metersphere.commons.utils.LogUtil; +import io.metersphere.service.JarConfigService; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.collections.map.LRUMap; import org.apache.commons.lang3.StringUtils; @@ -45,9 +50,11 @@ import org.apache.jmeter.threads.JMeterContext; import org.apache.jmeter.threads.JMeterContextService; import org.apache.jmeter.threads.JMeterVariables; import org.apache.jorphan.util.JOrphanUtils; +import org.codehaus.groovy.jsr223.GroovyScriptEngineImpl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; + /** * Base class for JSR223 Test elements * @@ -166,6 +173,9 @@ public abstract class JSR223TestElement extends ScriptingTestElement */ protected Object processFileOrScript(ScriptEngine scriptEngine, final Bindings pBindings) throws IOException, ScriptException { + + this.loadGroovyJar(scriptEngine); + Bindings bindings = pBindings; if (bindings == null) { bindings = scriptEngine.createBindings(); @@ -250,6 +260,37 @@ public abstract class JSR223TestElement extends ScriptingTestElement } } + /** + * groovy 使用的是自己的类加载器, + * 这里再执行脚本前,使用 groovy的加载器加载jar包, + * 解决groovy脚本无法使用jar包的问题 + * @Auth jianxing + * @param scriptEngine + */ + public static void loadGroovyJar(ScriptEngine scriptEngine) { + if (scriptEngine instanceof GroovyScriptEngineImpl) { + GroovyScriptEngineImpl groovyScriptEngine = (GroovyScriptEngineImpl) scriptEngine; + + JarConfigService jarConfigService = CommonBeanFactory.getBean(JarConfigService.class); + List jars = jarConfigService.list(); + + jars.forEach(jarConfig -> { + try { + String path = jarConfig.getPath(); + File file = new File(path); + if (file.isDirectory() && !path.endsWith("/")) { + file = new File(path + "/"); + } + groovyScriptEngine.getClassLoader().addURL(file.toURI().toURL()); + } catch (Exception e) { + e.printStackTrace(); + LogUtil.error(e.getMessage(), e); + } + }); + + } + } + /** * @return boolean true if element is not compilable or if compilation succeeds * @throws IOException if script is missing @@ -351,4 +392,4 @@ public abstract class JSR223TestElement extends ScriptingTestElement public void setScriptLanguage(String s) { scriptLanguage = s; } -} \ No newline at end of file +} diff --git a/backend/src/main/resources/db/migration/V79__v1.8.1_release.sql b/backend/src/main/resources/db/migration/V79__v1.8.1_release.sql new file mode 100644 index 0000000000..4c177ecbc6 --- /dev/null +++ b/backend/src/main/resources/db/migration/V79__v1.8.1_release.sql @@ -0,0 +1,5 @@ +-- file name length change +ALTER TABLE file_metadata + MODIFY name VARCHAR(250) NOT NULL COMMENT 'File name'; +-- api_scenario_report modify column length +ALTER TABLE api_scenario_report MODIFY COLUMN name VARCHAR(300); \ No newline at end of file diff --git a/backend/src/main/resources/i18n/messages_en_US.properties b/backend/src/main/resources/i18n/messages_en_US.properties index 7983790370..ecb733d2cd 100644 --- a/backend/src/main/resources/i18n/messages_en_US.properties +++ b/backend/src/main/resources/i18n/messages_en_US.properties @@ -75,6 +75,7 @@ organization_does_not_belong_to_user=The current organization does not belong to organization_id_is_null=Organization ID cannot be null #api api_load_script_error=Load script error +illegal_xml_format=illegal XML format api_report_is_null="Report is null, can't update" api_test_environment_already_exists="Api test environment already exists" api_test=API Test diff --git a/backend/src/main/resources/i18n/messages_zh_CN.properties b/backend/src/main/resources/i18n/messages_zh_CN.properties index 9d335a49e3..ef493c103e 100644 --- a/backend/src/main/resources/i18n/messages_zh_CN.properties +++ b/backend/src/main/resources/i18n/messages_zh_CN.properties @@ -75,6 +75,7 @@ organization_does_not_belong_to_user=当前组织不属于当前用户 organization_id_is_null=组织 ID 不能为空 #api api_load_script_error=读取脚本失败 +illegal_xml_format=不合法的 XML 格式 api_report_is_null="测试报告是未生成,无法更新" api_test_environment_already_exists="已存在该名称的环境配置" api_test=接口测试 diff --git a/backend/src/main/resources/i18n/messages_zh_TW.properties b/backend/src/main/resources/i18n/messages_zh_TW.properties index 6cab6f1a81..85650bc3c6 100644 --- a/backend/src/main/resources/i18n/messages_zh_TW.properties +++ b/backend/src/main/resources/i18n/messages_zh_TW.properties @@ -75,6 +75,7 @@ organization_does_not_belong_to_user=當前組織不屬於當前用戶 organization_id_is_null=組織 ID 不能為空 #api api_load_script_error=讀取腳本失敗 +illegal_xml_format=不合法的 XML 格式 api_report_is_null="測試報告是未生成,無法更新" api_test_environment_already_exists="已存在該名稱的環境配置" api_test=接口測試 diff --git a/frontend/src/business/components/api/automation/ApiAutomation.vue b/frontend/src/business/components/api/automation/ApiAutomation.vue index 11dc10b0f9..180b05750b 100644 --- a/frontend/src/business/components/api/automation/ApiAutomation.vue +++ b/frontend/src/business/components/api/automation/ApiAutomation.vue @@ -190,13 +190,17 @@ this.activeName = name; let currentScenario = { status: "Underway", principal: getCurrentUser().id, - apiScenarioModuleId: "root", id: getUUID(), + apiScenarioModuleId: "default-module", id: getUUID(), modulePath: "/" + this.$t("commons.module_title") }; if (this.nodeTree && this.nodeTree.length > 0) { currentScenario.apiScenarioModuleId = this.nodeTree[0].id; currentScenario.modulePath = this.nodeTree[0].path; } + + if (this.selectNodeIds && this.selectNodeIds.length > 0) { + currentScenario.apiScenarioModuleId = this.selectNodeIds[0]; + } this.tabs.push({label: label, name: name, currentScenario: currentScenario}); } if (tab.name === 'edit') { diff --git a/frontend/src/business/components/api/automation/scenario/AddBasisScenario.vue b/frontend/src/business/components/api/automation/scenario/AddBasisScenario.vue index ae28794b28..43a3b5d57b 100644 --- a/frontend/src/business/components/api/automation/scenario/AddBasisScenario.vue +++ b/frontend/src/business/components/api/automation/scenario/AddBasisScenario.vue @@ -117,6 +117,9 @@ if (this.currentModule && this.currentModule.id != "root") { this.scenarioForm.modulePath = this.currentModule.method !== undefined ? this.currentModule.method : null; this.scenarioForm.apiScenarioModuleId = this.currentModule.id; + }else{ + this.scenarioForm.modulePath = this.$t("commons.module_title"); + this.scenarioForm.apiScenarioModuleId = "default-module"; } }, getMaintainerOptions() { diff --git a/frontend/src/business/components/api/automation/scenario/ApiScenarioModule.vue b/frontend/src/business/components/api/automation/scenario/ApiScenarioModule.vue index fc3266b5bc..c93547beca 100644 --- a/frontend/src/business/components/api/automation/scenario/ApiScenarioModule.vue +++ b/frontend/src/business/components/api/automation/scenario/ApiScenarioModule.vue @@ -29,7 +29,7 @@ @refresh="refresh" ref="basisScenario"/> - + @@ -82,9 +82,7 @@ trashEnable: false }, data: [], - extendTreeNodes: [], currentModule: undefined, - moduleOptions: [], operators: [ { label: this.$t('api_test.automation.add_scenario'), @@ -141,11 +139,9 @@ this.result = this.$get("/api/automation/module/list/" + this.projectId, response => { if (response.data != undefined && response.data != null) { this.data = response.data; - let moduleOptions = []; this.data.forEach(node => { - buildNodePath(node, {path: ''}, moduleOptions); + buildTree(node, {path: ''}); }); - this.moduleOptions = moduleOptions } }); this.$refs.apiImport.open(this.currentModule); @@ -163,11 +159,9 @@ this.result = this.$get("/api/automation/module/list/" + this.projectId, response => { if (response.data != undefined && response.data != null) { this.data = response.data; - let moduleOptions = []; this.data.forEach(node => { - buildNodePath(node, {path: ''}, moduleOptions); + buildTree(node, {path: ''}); }); - this.moduleOptions = moduleOptions } }); this.$refs.apiImport.open(this.currentModule); @@ -188,17 +182,10 @@ this.result = this.$get(url, response => { if (response.data != undefined && response.data != null) { this.data = response.data; - this.extendTreeNodes = []; - this.extendTreeNodes.unshift({ - "id": "root", - "name": this.$t('commons.module_title'), - "level": 0, - "children": this.data, - }); - this.extendTreeNodes.forEach(node => { + this.data.forEach(node => { buildTree(node, {path: ''}); }); - this.$emit('setModuleOptions', this.extendTreeNodes); + this.$emit('setModuleOptions', this.data); this.$emit('setNodeTree', this.data); if (this.$refs.nodeTree) { this.$refs.nodeTree.filter(this.condition.filterText); diff --git a/frontend/src/business/components/api/automation/scenario/api/AddBasisApi.vue b/frontend/src/business/components/api/automation/scenario/api/AddBasisApi.vue index 31f10c7792..09349bb9e6 100644 --- a/frontend/src/business/components/api/automation/scenario/api/AddBasisApi.vue +++ b/frontend/src/business/components/api/automation/scenario/api/AddBasisApi.vue @@ -84,7 +84,7 @@ callback(); }; return { - httpForm: {environmentId: "", moduleId: "root"}, + httpForm: {environmentId: "", moduleId: "default-module"}, moduleOptions: [], httpVisible: false, currentModule: {}, @@ -251,14 +251,7 @@ let url = "/api/module/list/" + getCurrentProjectID() + "/" + data.protocol; this.result = this.$get(url, response => { if (response.data != undefined && response.data != null) { - let data = response.data; - this.moduleOptions = []; - this.moduleOptions.unshift({ - "id": "root", - "name": this.$t('commons.module_title'), - "level": 0, - "children": data, - }); + this.moduleOptions = response.data; this.moduleOptions.forEach(node => { buildTree(node, {path: ''}); }); @@ -282,7 +275,7 @@ data.protocol = "DUBBO"; } data.id = getUUID(); - this.httpForm = {id: data.id, name: data.name, protocol: data.protocol, path: data.path, method: api.method, userId: getCurrentUser().id, request: data, moduleId: "root"}; + this.httpForm = {id: data.id, name: data.name, protocol: data.protocol, path: data.path, method: api.method, userId: getCurrentUser().id, request: data, moduleId: "default-module"}; this.getMaintainerOptions(); this.list(data); this.httpVisible = true; 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 2cff5e9ca6..706f6e2cb3 100644 --- a/frontend/src/business/components/api/automation/scenario/common/ApiBaseComponent.vue +++ b/frontend/src/business/components/api/automation/scenario/common/ApiBaseComponent.vue @@ -32,7 +32,7 @@ - + diff --git a/frontend/src/business/components/api/definition/ApiDefinition.vue b/frontend/src/business/components/api/definition/ApiDefinition.vue index fdf74a00a1..3abdc02222 100644 --- a/frontend/src/business/components/api/definition/ApiDefinition.vue +++ b/frontend/src/business/components/api/definition/ApiDefinition.vue @@ -296,12 +296,15 @@ } let api = { status: "Underway", method: "GET", userId: getCurrentUser().id, - url: "", protocol: this.currentProtocol, environmentId: "", moduleId: 'root', modulePath: "/" + this.$t("commons.module_title") + url: "", protocol: this.currentProtocol, environmentId: "", moduleId: 'default-module', modulePath: "/" + this.$t("commons.module_title") }; if (this.nodeTree && this.nodeTree.length > 0) { api.moduleId = this.nodeTree[0].id; api.modulePath = this.nodeTree[0].path; } + if (this.selectNodeIds && this.selectNodeIds.length > 0) { + api.moduleId = this.selectNodeIds[0]; + } this.handleTabsEdit(this.$t('api_test.definition.request.title'), e, api); }, handleTabClose() { diff --git a/frontend/src/business/components/api/definition/components/basis/AddBasisApi.vue b/frontend/src/business/components/api/definition/components/basis/AddBasisApi.vue index 7bd6e6124a..055a927db7 100644 --- a/frontend/src/business/components/api/definition/components/basis/AddBasisApi.vue +++ b/frontend/src/business/components/api/definition/components/basis/AddBasisApi.vue @@ -154,6 +154,9 @@ if (this.currentModule != null) { this.httpForm.modulePath = this.currentModule.method != undefined ? this.currentModule.method : null; this.httpForm.moduleId = this.currentModule.id; + } else { + this.httpForm.modulePath = this.$t("commons.module_title"); + this.httpForm.moduleId = "default-module"; } }, diff --git a/frontend/src/business/components/api/definition/components/case/ApiCaseItem.vue b/frontend/src/business/components/api/definition/components/case/ApiCaseItem.vue index 59e5afaa62..1aa89b01ab 100644 --- a/frontend/src/business/components/api/definition/components/case/ApiCaseItem.vue +++ b/frontend/src/business/components/api/definition/components/case/ApiCaseItem.vue @@ -275,7 +275,7 @@ } }, addModule(row) { - this.saveApi(row, "root"); + this.saveApi(row, "default-module"); }, saveApi(row, module) { let data = this.api; diff --git a/frontend/src/business/components/api/definition/components/module/ApiModule.vue b/frontend/src/business/components/api/definition/components/module/ApiModule.vue index d5b90d1b4e..1d2e735ff6 100644 --- a/frontend/src/business/components/api/definition/components/module/ApiModule.vue +++ b/frontend/src/business/components/api/definition/components/module/ApiModule.vue @@ -20,7 +20,7 @@ :condition="condition" :current-module="currentModule" :is-read-only="isReadOnly" - :moduleOptions="extendTreeNodes" + :moduleOptions="data" @exportAPI="exportAPI" @saveAsEdit="saveAsEdit" @refreshTable="$emit('refreshTable')" @@ -62,7 +62,6 @@ }, data: [], currentModule: {}, - extendTreeNodes: [], } }, props: { @@ -131,17 +130,10 @@ this.result = this.$get(url, response => { if (response.data != undefined && response.data != null) { this.data = response.data; - this.extendTreeNodes = []; - this.extendTreeNodes.unshift({ - "id": "root", - "name": this.$t('commons.module_title'), - "level": 0, - "children": this.data, - }); - this.extendTreeNodes.forEach(node => { + this.data.forEach(node => { buildTree(node, {path: ''}); }); - this.$emit('setModuleOptions', this.extendTreeNodes); + this.$emit('setModuleOptions', this.data); this.$emit('setNodeTree', this.data); if (this.$refs.nodeTree) { this.$refs.nodeTree.filter(this.condition.filterText); diff --git a/frontend/src/business/components/performance/test/components/ExistFiles.vue b/frontend/src/business/components/performance/test/components/ExistFiles.vue index bcecb0713c..e264843c2b 100644 --- a/frontend/src/business/components/performance/test/components/ExistFiles.vue +++ b/frontend/src/business/components/performance/test/components/ExistFiles.vue @@ -154,35 +154,39 @@ export default { } let rows = this.existFiles.filter(f => this.selectIds.has(f.id)); + let jmxIds = []; for (let i = 0; i < rows.length; i++) { let row = rows[i]; if (this.tableData.filter(f => f.name === row.name).length > 0) { - this.$error(this.$t('load_test.delete_file') + ', name: ' + row.name); - this.selectIds.clear(); - return; + setTimeout(() => { + this.$warning(this.$t('load_test.delete_file') + 'name: ' + row.name); + }, 100); + continue; } + if (row.type.toUpperCase() === 'JMX') { + jmxIds.push(row.id); + } + this.tableData.push({ + name: row.name, + size: (row.size / 1024).toFixed(2) + ' KB', + type: row.type.toUpperCase(), + updateTime: row.lastModified, + }); } if (this.loadType === 'resource') { rows.forEach(row => { this.fileList.push(row); - this.tableData.push({ - name: row.name, - size: (row.size / 1024).toFixed(2) + ' KB', - type: row.type.toUpperCase(), - updateTime: row.lastModified, - }); }) this.$success(this.$t('test_track.case.import.success')); - this.loadFileVisible = false; - this.selectIds.clear(); + this.close(); return; } - this.getJmxContents(); + this.getJmxContents(jmxIds); }, - getJmxContents() { - this.projectLoadingResult = this.$post('/performance/export/jmx', [...this.selectIds], (response) => { + getJmxContents(jmxIds) { + this.projectLoadingResult = this.$post('/performance/export/jmx', jmxIds, (response) => { let data = response.data; if (!data) { return; @@ -195,18 +199,11 @@ export default { }); let file = new File([d.jmx], d.name); this.uploadList.push(file); - this.tableData.push({ - name: file.name, - size: (file.size / 1024).toFixed(2) + ' KB', - type: 'JMX', - updateTime: file.lastModified, - }); }); this.$emit('fileChange', this.scenarios); this.$success(this.$t('test_track.case.import.success')); - this.loadFileVisible = false; - this.selectIds.clear(); + this.close(); }); }, beforeUploadFile(file) { diff --git a/frontend/src/business/components/track/case/components/TestCaseCreate.vue b/frontend/src/business/components/track/case/components/TestCaseCreate.vue index 98d5bbec1e..31b6d3d606 100644 --- a/frontend/src/business/components/track/case/components/TestCaseCreate.vue +++ b/frontend/src/business/components/track/case/components/TestCaseCreate.vue @@ -114,8 +114,8 @@ export default { this.testCaseForm.nodePath = this.currentModule.path; this.testCaseForm.nodeId = this.currentModule.id; } else { - this.testCaseForm.nodePath = "/全部用例" - this.testCaseForm.nodeId = "root" + this.testCaseForm.nodePath = "/默认模块" + this.testCaseForm.nodeId = "default-module" } this.result = this.$post(path, this.testCaseForm, response => { this.testCaseForm.id = response.data.id diff --git a/frontend/src/business/components/track/case/components/TestCaseEdit.vue b/frontend/src/business/components/track/case/components/TestCaseEdit.vue index 5ed25a3951..96e8d81efa 100644 --- a/frontend/src/business/components/track/case/components/TestCaseEdit.vue +++ b/frontend/src/business/components/track/case/components/TestCaseEdit.vue @@ -35,7 +35,7 @@ - @@ -314,8 +314,8 @@ export default { dialogFormVisible: false, form: { name: '', - module: 'root', - nodePath:'', + module: 'default-module', + nodePath:'/默认模块', maintainer: getCurrentUser().id, priority: 'P0', type: '', @@ -416,7 +416,7 @@ export default { }; }); }, 1000); - if(this.selectNode && this.selectNode.data){ + if(this.selectNode && this.selectNode.data && !this.form.id){ this.form.module = this.selectNode.data.id; this.form.nodePath = this.selectNode.data.path; } @@ -432,7 +432,16 @@ export default { created() { this.loadOptions(); this.addListener(); // 添加 ctrl s 监听 - + if(this.selectNode && this.selectNode.data && !this.form.id){ + this.form.module = this.selectNode.data.id; + this.form.nodePath = this.selectNode.data.path; + }else{ + this.form.module =this.treeNodes && this.length>0? this.treeNodes[0].id:""; + } + if (this.type === 'edit' || this.type === 'copy') { + this.form.module = this.currentTestCaseInfo.nodeId; + this.form.nodePath = this.currentTestCaseInfo.nodePath; + } }, methods: { setModule(id,data) { @@ -836,16 +845,11 @@ export default { this.getTestOptions() }, getModuleOptions() { - this.moduleOptions = []; - this.moduleOptions.unshift({ - "id": "root", - "name": this.$t('commons.module_title'), - "level": 0, - "children": this.treeNodes, - }); - this.moduleOptions.forEach(node => { - buildTree(node, {path: ''}); + let moduleOptions = []; + this.treeNodes.forEach(node => { + buildNodePath(node, {path: ''}, moduleOptions); }); + this.moduleOptions = moduleOptions; }, getMaintainerOptions() { let workspaceId = localStorage.getItem(WORKSPACE_ID); diff --git a/frontend/src/business/components/track/common/NodeTree.vue b/frontend/src/business/components/track/common/NodeTree.vue index 656b412984..fb4929c35e 100644 --- a/frontend/src/business/components/track/common/NodeTree.vue +++ b/frontend/src/business/components/track/common/NodeTree.vue @@ -32,7 +32,7 @@ { this.treeNodes = response.data; + this.treeNodes.forEach(node => { + buildTree(node, {path: ''}); + }); if (this.$refs.nodeTree) { this.$refs.nodeTree.filter(); } diff --git a/frontend/src/business/components/xpack b/frontend/src/business/components/xpack index 2115bd28a9..07951ba17a 160000 --- a/frontend/src/business/components/xpack +++ b/frontend/src/business/components/xpack @@ -1 +1 @@ -Subproject commit 2115bd28a90854d2b6276a90878934715498c584 +Subproject commit 07951ba17aef6f29e50cfd68e40de3266f9a60cd