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 839807b786..bc00da7ba4 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java @@ -1099,6 +1099,10 @@ public class ApiAutomationService { @Override public void run() { List reportIds = new LinkedList<>(); + //记录串行执行中的环境参数,供下一个场景执行时使用。 > + Map> execute_env_param_datas = new LinkedHashMap<>(); + ApiTestEnvironmentService apiTestEnvironmentService = CommonBeanFactory.getBean(ApiTestEnvironmentService.class); + HashTreeUtil hashTreeUtil = new HashTreeUtil(); for (String key : executeQueue.keySet()) { reportIds.add(key); APIScenarioReportResult report = executeQueue.get(key).getReport(); @@ -1112,6 +1116,13 @@ public class ApiAutomationService { apiScenarioReportMapper.updateByPrimaryKey(report); } try { + if(!execute_env_param_datas.isEmpty()){ + try { + HashTree hashTree = executeQueue.get(key).getHashTree(); + hashTreeUtil.setEnvParamsMapToHashTree(hashTree,execute_env_param_datas); + executeQueue.get(key).setHashTree(hashTree); + }catch (Exception e){} + } Future future = executorService.submit(new SerialScenarioExecTask(jMeterService, apiScenarioReportMapper, executeQueue.get(key), request)); ApiScenarioReport scenarioReport = future.get(); // 如果开启失败结束执行,则判断返回结果状态 @@ -1121,6 +1132,12 @@ public class ApiAutomationService { break; } } + + try { + Map> envParamsMap = hashTreeUtil.getEnvParamsDataByHashTree(executeQueue.get(key).getHashTree(),apiTestEnvironmentService); + execute_env_param_datas = hashTreeUtil.mergeParamDataMap(execute_env_param_datas,envParamsMap); + }catch (Exception e){} + } catch (Exception e) { reportIds.remove(key); LogUtil.error("执行终止:" + e.getMessage()); @@ -1156,7 +1173,6 @@ public class ApiAutomationService { } } } - /** * 生成HashTree * diff --git a/backend/src/main/java/io/metersphere/commons/utils/HashTreeUtil.java b/backend/src/main/java/io/metersphere/commons/utils/HashTreeUtil.java new file mode 100644 index 0000000000..98612f0228 --- /dev/null +++ b/backend/src/main/java/io/metersphere/commons/utils/HashTreeUtil.java @@ -0,0 +1,198 @@ +package io.metersphere.commons.utils; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import io.metersphere.api.dto.RunningParamKeys; +import io.metersphere.api.service.ApiTestEnvironmentService; +import io.metersphere.base.domain.ApiTestEnvironmentWithBLOBs; +import org.apache.commons.lang3.StringUtils; +import org.apache.jmeter.config.Argument; +import org.apache.jmeter.config.Arguments; +import org.apache.jmeter.extractor.JSR223PostProcessor; +import org.apache.jmeter.modifiers.JSR223PreProcessor; +import org.apache.jmeter.protocol.java.sampler.JSR223Sampler; +import org.apache.jorphan.collections.HashTree; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author song.tianyang + * 2021/7/28 3:37 下午 + */ +public class HashTreeUtil { + + public Map> getEnvParamsDataByHashTree(HashTree hashTree, ApiTestEnvironmentService apiTestEnvironmentService){ + Map> returnMap = new HashMap<>(); + Map> envParamMap = this.getEnvParamsMapByHashTree(hashTree); + + for (Map.Entry> entry:envParamMap.entrySet()) { + String envId = entry.getKey(); + List params = entry.getValue(); + ApiTestEnvironmentWithBLOBs environment = apiTestEnvironmentService.get(envId); + if(environment != null && environment.getConfig() != null){ + try { + JSONObject configJson = JSONObject.parseObject(environment.getConfig()); + if(configJson.containsKey("commonConfig")){ + JSONObject commonConfig = configJson.getJSONObject("commonConfig"); + if(commonConfig.containsKey("variables")){ + Map envHeadMap = new HashMap<>(); + JSONArray variablesArr = commonConfig.getJSONArray("variables"); + for (int i = 0; i < variablesArr.size(); i++) { + JSONObject object = variablesArr.getJSONObject(i); + if(object.containsKey("name") && object.containsKey("value")){ + boolean isEnable = true; + if(object.containsKey("enable")){ + isEnable = object.getBoolean("enable"); + } + if(isEnable){ + envHeadMap.put(object.getString("name"),object.getString("value")); + } + } + } + for (String param :params) { + String value = envHeadMap.get(param); + if(value == null){ + value = ""; + } + if(returnMap.containsKey(envId)){ + returnMap.get(envId).put(param,value); + }else { + Map map = new HashMap<>(); + map.put(param,value); + returnMap.put(envId,map); + } + } + } + } + }catch (Exception ignored){} + } + } + + return returnMap; + } + + public synchronized void setEnvParamsMapToHashTree(HashTree hashTree,Map> envParamsMap) { + if(hashTree != null ){ + Map allParamMap = new HashMap<>(); + for (Map paramMap:envParamsMap.values()) { + allParamMap.putAll(paramMap); + } + for(Object hashTreeKey : hashTree.keySet()){ + HashTree itemTree = hashTree.get(hashTreeKey); + + try{ + if(hashTreeKey instanceof Arguments){ + Arguments arguments = (Arguments)hashTreeKey; + + List argumentList = new ArrayList<>(); + for (int i = 0; i < arguments.getArgumentCount(); i ++){ + Argument item = arguments.getArgument(i); + String name = item.getPropertyAsString("Argument.name"); + if(allParamMap.containsKey(name)){ + item.setProperty("Argument.value",allParamMap.get(name)); + } + argumentList.add(item); + } + + ((Arguments)hashTreeKey).removeAllArguments(); + for (Argument argument:argumentList) { + ((Arguments)hashTreeKey).addArgument(argument); + } + + } + }catch (Exception ignored){ + } + this.setEnvParamsMapToHashTree(itemTree,envParamsMap); + + } + } + } + public synchronized Map> getEnvParamsMapByHashTree(HashTree hashTree) { + Map> returnMap = new HashMap<>(); + if(hashTree != null ){ + for(Object hashTreeKey : hashTree.keySet()){ + HashTree itemTree = hashTree.get(hashTreeKey); + + String scriptValue = ""; + try{ + if(hashTreeKey instanceof JSR223PostProcessor){ + JSR223PostProcessor postProcessor = (JSR223PostProcessor)hashTreeKey; + scriptValue = postProcessor.getPropertyAsString("script"); + }else if(hashTreeKey instanceof JSR223PreProcessor){ + JSR223PreProcessor processor = (JSR223PreProcessor)hashTreeKey; + scriptValue = processor.getPropertyAsString("script"); + }else if(hashTreeKey instanceof JSR223Sampler){ + JSR223Sampler processor = (JSR223Sampler)hashTreeKey; + scriptValue = processor.getPropertyAsString("script"); + } + }catch (Exception ignored){ + } + + if(StringUtils.isNotEmpty(scriptValue)){ + if(scriptValue.contains(RunningParamKeys.RUNNING_PARAMS_PREFIX)){ + String [] paramsArr = scriptValue.split(RunningParamKeys.RUNNING_PARAMS_PREFIX); + for(int i = 1; i < paramsArr.length;i++){ + String paramItem = paramsArr[i]; + if(StringUtils.contains(paramItem,".")){ + String envId = paramItem.split("\\.")[0]; + String otherStr = paramItem.substring(envId.length()+3); + String firstChar = otherStr.substring(0,1); + String [] envParamsStr = otherStr.split(firstChar); + if(envParamsStr.length > 1){ + String param = envParamsStr[1]; + if(returnMap.containsKey(envId)){ + returnMap.get(envId).add(param); + }else { + List list = new ArrayList<>(); + list.add(param); + returnMap.put(envId,list); + } + } + } + } + } + } + + Map> itemMap = this.getEnvParamsMapByHashTree(itemTree); + + for (Map.Entry> entry:itemMap.entrySet()) { + String envId = entry.getKey(); + List params = entry.getValue(); + + if(returnMap.containsKey(envId)){ + for (String param:params) { + if(!returnMap.get(envId).contains(param)){ + returnMap.get(envId).add(param); + } + } + }else { + returnMap.put(envId,params); + } + } + } + } + return returnMap; + } + + public Map> mergeParamDataMap(Map> execute_env_param_dataMap, Map> envParamsMap) { + if(execute_env_param_dataMap == null){ + execute_env_param_dataMap = new HashMap<>(); + } + if(envParamsMap == null){ + return execute_env_param_dataMap; + } + for (Map.Entry> paramEnvMapEntry:envParamsMap.entrySet()) { + String envId = paramEnvMapEntry.getKey(); + Map map = paramEnvMapEntry.getValue(); + if(execute_env_param_dataMap.containsKey(envId)){ + execute_env_param_dataMap.get(envId).putAll(map); + }else { + execute_env_param_dataMap.put(envId,map); + } + } + return execute_env_param_dataMap; + } +} diff --git a/backend/src/main/java/io/metersphere/excel/utils/EasyExcelExporter.java b/backend/src/main/java/io/metersphere/excel/utils/EasyExcelExporter.java index 04a137adfe..16337005aa 100644 --- a/backend/src/main/java/io/metersphere/excel/utils/EasyExcelExporter.java +++ b/backend/src/main/java/io/metersphere/excel/utils/EasyExcelExporter.java @@ -42,30 +42,16 @@ public class EasyExcelExporter { } } - public void exportByCustomWriteHandler(HttpServletResponse response, Set excludeColumnFiledNames, List data, String fileName, String sheetName, WriteHandler writeHandler) { + public void exportByCustomWriteHandler(HttpServletResponse response, List> headList, List> data, String fileName, String sheetName) { + if(CollectionUtils.isEmpty(headList)){ + headList = new ArrayList<>(); + } response.setContentType("application/vnd.ms-excel"); response.setCharacterEncoding("utf-8"); - List> list = new ArrayList<>(); -// ListaaaList = new ArrayList<>(); -// aaaList.add("aaaaa"); -// ListnameList = new ArrayList<>(); -// nameList.add("所属模块"); -// Listname2List = new ArrayList<>(); -// name2List.add("用例名称"); -// list.add(aaaList); -// list.add(nameList); -// list.add(name2List); try { response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8") + ".xlsx"); - if(CollectionUtils.isNotEmpty(excludeColumnFiledNames)){ - EasyExcel.write(response.getOutputStream()).head(list). - registerWriteHandler(writeHandler). -// excludeColumnFiledNames(excludeColumnFiledNames). -// includeColumnFiledNames(list). - sheet(sheetName).doWrite(data); - }else { - EasyExcel.write(response.getOutputStream(), this.clazz).registerWriteHandler(writeHandler).sheet(sheetName).doWrite(data); - } + EasyExcel.write(response.getOutputStream()).head(headList). + sheet(sheetName).doWrite(data); } catch (UnsupportedEncodingException e) { LogUtil.error(e.getMessage(), e); throw new ExcelException("Utf-8 encoding is not supported"); @@ -76,6 +62,9 @@ public class EasyExcelExporter { } public void exportByCustomWriteHandler(HttpServletResponse response, List> headList, List> data, String fileName, String sheetName, WriteHandler writeHandler) { + if(CollectionUtils.isEmpty(headList)){ + headList = new ArrayList<>(); + } response.setContentType("application/vnd.ms-excel"); response.setCharacterEncoding("utf-8"); try { 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 486f97d3f0..5d7689480c 100644 --- a/backend/src/main/java/io/metersphere/track/service/TestCaseService.java +++ b/backend/src/main/java/io/metersphere/track/service/TestCaseService.java @@ -811,7 +811,6 @@ public class TestCaseService { List list = new ArrayList<>(); StringBuilder path = new StringBuilder(""); List types = TestCaseConstants.Type.getValues(); -// List methods = TestCaseConstants.Method.getValues(); SessionUser user = SessionUtils.getUser(); TestCaseExcelDataFactory factory = new TestCaseExcelDataFactory(); for (int i = 1; i <= 5; i++) { @@ -821,11 +820,6 @@ public class TestCaseService { data.setNodePath(path.toString()); data.setPriority("P" + i % 4); String type = types.get(i % 3); -// if (StringUtils.equals(TestCaseConstants.Type.Functional.getValue(), type)) { -// data.setMethod(TestCaseConstants.Method.Manual.getValue()); -// } else { -// data.setMethod(methods.get(i % 2)); -// } data.setPrerequisite(Translator.get("preconditions_optional")); data.setStepDesc("1. " + Translator.get("step_tip_separate") + "\n2. " + Translator.get("step_tip_order") + "\n3. " + Translator.get("step_tip_optional")); @@ -836,37 +830,97 @@ public class TestCaseService { } list.add(new TestCaseExcelData()); -// TestCaseExcelData explain = new TestCaseExcelData(); -// explain.setName(Translator.get("do_not_modify_header_order") + "," + Translator.get("num_needed_modify_testcase") + "," + Translator.get("num_needless_create_testcase")); -// explain.setNodePath(Translator.get("module_created_automatically")); -// explain.setType(Translator.get("options") + "(functional、performance、api)"); -// explain.setTags(Translator.get("tag_tip_pattern")); -//// explain.setMethod(Translator.get("options") + "(manual、auto)"); -// explain.setPriority(Translator.get("options") + "(P0、P1、P2、P3)"); -// explain.setMaintainer(Translator.get("please_input_workspace_member")); -// list.add(explain); return list; } public void testCaseExport(HttpServletResponse response, TestCaseBatchRequest request) { try { - EasyExcelExporter easyExcelExporter = new EasyExcelExporter(new TestCaseExcelDataFactory().getExcelDataByLocal()); +// EasyExcelExporter easyExcelExporter = new EasyExcelExporter(new TestCaseExcelDataFactory().getExcelDataByLocal()); +// List datas = generateTestCaseExcel(request); +// easyExcelExporter.export(response,datas,Translator.get("test_case_import_template_name"), Translator.get("test_case_import_template_sheet")); + + TestCaseExcelData testCaseExcelData = new TestCaseExcelDataFactory().getTestCaseExcelDataLocal(); List datas = generateTestCaseExcel(request); - easyExcelExporter.export(response, datas, + boolean importFileNeedNum = true; + TestCaseTemplateService testCaseTemplateService = CommonBeanFactory.getBean(TestCaseTemplateService.class); + TestCaseTemplateDao testCaseTemplate = testCaseTemplateService.getTemplate(request.getProjectId()); + List customFields = null; + if (testCaseTemplate == null) { + customFields = new ArrayList<>(); + } else { + customFields = testCaseTemplate.getCustomFields(); + } + + List> headList = testCaseExcelData.getHead(importFileNeedNum, customFields); + List> testCaseDataByExcelList = this.generateTestCaseExcel(headList,datas); + EasyExcelExporter easyExcelExporter = new EasyExcelExporter(testCaseExcelData.getClass()); + easyExcelExporter.exportByCustomWriteHandler(response,headList, testCaseDataByExcelList, Translator.get("test_case_import_template_name"), Translator.get("test_case_import_template_sheet")); - if (CollectionUtils.isNotEmpty(datas)) { - List names = datas.stream().map(TestCaseExcelData::getName).collect(Collectors.toList()); - request.setName(String.join(",", names)); - List ids = request.getIds(); - request.setId(JSON.toJSONString(ids)); - } + } catch (Exception e) { LogUtil.error(e.getMessage(), e); MSException.throwException(e); } } + private List> generateTestCaseExcel(List> headListParams,List datas) { + List> returnDatas = new ArrayList<>(); + //转化excel头 + List headList = new ArrayList<>(); + for (List list:headListParams){ + for (String head : list){ + headList.add(head); + } + } + for(TestCaseExcelData model : datas){ + List list = new ArrayList<>(); + Map customDataMaps = model.getCustomDatas(); + if(customDataMaps == null){ + customDataMaps = new HashMap<>(); + } + for(String head : headList){ + if(StringUtils.equalsAnyIgnoreCase(head,"ID")){ + list.add(model.getCustomNum()); + }else if(StringUtils.equalsAnyIgnoreCase(head,"Name","用例名稱","用例名称")){ + list.add(model.getName()); + }else if(StringUtils.equalsAnyIgnoreCase(head,"Module","所屬模塊","所属模块")){ + list.add(model.getNodePath()); + }else if(StringUtils.equalsAnyIgnoreCase(head,"Tag","標簽","标签")){ + String tags = ""; + try { + if(model.getTags()!=null){ + JSONArray arr = JSONArray.parseArray(model.getTags()); + for(int i = 0; i < arr.size(); i ++){ + tags += arr.getString(i) + ","; + } + } + }catch (Exception e){} + list.add(tags); + }else if(StringUtils.equalsAnyIgnoreCase(head,"Prerequisite","前置條件","前置条件")){ + list.add(model.getPrerequisite()); + }else if(StringUtils.equalsAnyIgnoreCase(head,"Remark","備註","备注")){ + list.add(model.getRemark()); + }else if(StringUtils.equalsAnyIgnoreCase(head,"Step description","步驟描述","步骤描述")){ + list.add(model.getStepDesc()); + }else if(StringUtils.equalsAnyIgnoreCase(head,"Step result","預期結果","预期结果")){ + list.add(model.getStepResult()); + }else if(StringUtils.equalsAnyIgnoreCase(head,"Edit Model","編輯模式","编辑模式")){ + list.add(model.getStepModel()); + }else if(StringUtils.equalsAnyIgnoreCase(head,"Priority","用例等級","用例等级")){ + list.add(model.getPriority()); + }else { + String value = customDataMaps.get(head); + if(value == null){ + value = ""; + } + list.add(value); + } + } + returnDatas.add(list); + } + return returnDatas; + } private List generateTestCaseExcel(TestCaseBatchRequest request) { ServiceUtils.getSelectAllIds(request, request.getCondition(), (query) -> extTestCaseMapper.selectIds(query)); @@ -961,6 +1015,18 @@ public class TestCaseService { } data.setMaintainer(t.getMaintainer()); data.setStatus(t.getStatus()); + String customFields = t.getCustomFields(); + try{ + JSONArray customFieldsArr = JSONArray.parseArray(customFields); + Map map = new HashMap<>(); + for(int index = 0; index < customFieldsArr.size(); index ++){ + JSONObject obj = customFieldsArr.getJSONObject(index); + if(obj.containsKey("name") && obj.containsKey("value")){ + map.put(obj.getString("name"),obj.getString("value")); + } + } + data.setCustomDatas(map); + }catch (Exception e){} list.add(data); }); return list; diff --git a/backend/src/main/resources/i18n/messages_en_US.properties b/backend/src/main/resources/i18n/messages_en_US.properties index 65ee4e79c9..b9d494227d 100644 --- a/backend/src/main/resources/i18n/messages_en_US.properties +++ b/backend/src/main/resources/i18n/messages_en_US.properties @@ -149,7 +149,7 @@ options_yes=Yes options_no=No required=Required password_format_is_incorrect=Valid password: 8-30 digits, English upper and lower case letters + numbers + special characters (optional) -please_input_workspace_member=Please input workspace merber +please_input_workspace_member=Please input workspace merber's number test_case_report_template_repeat=The workspace has the same name template plan_name_already_exists=Test plan name already exists test_case_already_exists_excel=There are duplicate test cases in the import file diff --git a/backend/src/main/resources/i18n/messages_zh_CN.properties b/backend/src/main/resources/i18n/messages_zh_CN.properties index 95f547f9b7..daaa25d2cf 100644 --- a/backend/src/main/resources/i18n/messages_zh_CN.properties +++ b/backend/src/main/resources/i18n/messages_zh_CN.properties @@ -149,7 +149,7 @@ options_yes=是 options_no=否 required=必填 password_format_is_incorrect=有效密码:8-30位,英文大小写字母+数字+特殊字符(可选) -please_input_workspace_member=请填写该工作空间相关人员 +please_input_workspace_member=请填写该工作空间的相关人员ID test_case_report_template_repeat=同一工作空间下不能存在同名模版 plan_name_already_exists=测试计划名称已存在 test_case_already_exists_excel=文件中存在多条相同用例 diff --git a/backend/src/main/resources/i18n/messages_zh_TW.properties b/backend/src/main/resources/i18n/messages_zh_TW.properties index a4a6e13b6a..b35f5bf614 100644 --- a/backend/src/main/resources/i18n/messages_zh_TW.properties +++ b/backend/src/main/resources/i18n/messages_zh_TW.properties @@ -149,7 +149,7 @@ options_yes=是 options_no=否 required=必填 password_format_is_incorrect=有效密碼:8-30位,英文大小寫字母+數字+特殊字符(可選) -please_input_workspace_member=請填寫該工作空間相關人員 +please_input_workspace_member=請填寫該工作空間的相關人員ID test_case_report_template_repeat=同壹工作空間下不能存在同名模版 plan_name_already_exists=測試計劃名稱已存在 test_case_already_exists_excel=文件中存在多條相同用例 diff --git a/frontend/src/business/components/api/automation/scenario/EditApiScenario.vue b/frontend/src/business/components/api/automation/scenario/EditApiScenario.vue index 602d3d0b05..a79ed7ec2e 100644 --- a/frontend/src/business/components/api/automation/scenario/EditApiScenario.vue +++ b/frontend/src/business/components/api/automation/scenario/EditApiScenario.vue @@ -45,7 +45,7 @@ diff --git a/frontend/src/business/components/api/definition/components/complete/BasisApi.vue b/frontend/src/business/components/api/definition/components/complete/BasisApi.vue index 4231c30ef8..bde57dfd55 100644 --- a/frontend/src/business/components/api/definition/components/complete/BasisApi.vue +++ b/frontend/src/business/components/api/definition/components/complete/BasisApi.vue @@ -30,7 +30,7 @@ diff --git a/frontend/src/business/components/api/definition/components/complete/EditCompleteHTTPApi.vue b/frontend/src/business/components/api/definition/components/complete/EditCompleteHTTPApi.vue index 1186521db1..c3d71042d0 100644 --- a/frontend/src/business/components/api/definition/components/complete/EditCompleteHTTPApi.vue +++ b/frontend/src/business/components/api/definition/components/complete/EditCompleteHTTPApi.vue @@ -42,7 +42,7 @@ diff --git a/frontend/src/business/components/api/definition/components/complete/TCPBasicApi.vue b/frontend/src/business/components/api/definition/components/complete/TCPBasicApi.vue index 4f355237d3..cface9d85e 100644 --- a/frontend/src/business/components/api/definition/components/complete/TCPBasicApi.vue +++ b/frontend/src/business/components/api/definition/components/complete/TCPBasicApi.vue @@ -37,7 +37,7 @@