From d32000e0bde2167e4c8009910e8a74409e2087f3 Mon Sep 17 00:00:00 2001 From: "song.tianyang" Date: Wed, 30 Dec 2020 14:42:48 +0800 Subject: [PATCH 01/23] =?UTF-8?q?feat:=20=E6=8E=A5=E5=8F=A3=E5=AE=9A?= =?UTF-8?q?=E4=B9=89-=E6=A1=88=E4=BE=8B=E8=A1=A8=E6=A0=BC=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E5=85=A8=E9=83=A8=E6=95=B0=E6=8D=AE=E6=89=B9=E9=87=8F?= =?UTF-8?q?=E6=93=8D=E4=BD=9C=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 接口定义-案例表格增加全部数据批量操作支持 --- .../api/controller/ApiTestCaseController.java | 16 +++-- .../dto/definition/ApiTestBatchRequest.java | 51 ++++++++++++++ .../api/service/ApiAutomationService.java | 1 + .../api/service/ApiDefinitionService.java | 1 + .../api/service/ApiTestCaseService.java | 45 ++++++++++-- .../components/list/ApiCaseSimpleList.vue | 68 +++++++++++++++++-- 6 files changed, 169 insertions(+), 13 deletions(-) create mode 100644 backend/src/main/java/io/metersphere/api/dto/definition/ApiTestBatchRequest.java diff --git a/backend/src/main/java/io/metersphere/api/controller/ApiTestCaseController.java b/backend/src/main/java/io/metersphere/api/controller/ApiTestCaseController.java index 015848897b..40ab590b92 100644 --- a/backend/src/main/java/io/metersphere/api/controller/ApiTestCaseController.java +++ b/backend/src/main/java/io/metersphere/api/controller/ApiTestCaseController.java @@ -3,10 +3,7 @@ package io.metersphere.api.controller; import com.github.pagehelper.Page; import com.github.pagehelper.PageHelper; import io.metersphere.api.dto.ApiCaseBatchRequest; -import io.metersphere.api.dto.definition.ApiTestCaseDTO; -import io.metersphere.api.dto.definition.ApiTestCaseRequest; -import io.metersphere.api.dto.definition.ApiTestCaseResult; -import io.metersphere.api.dto.definition.SaveApiTestCaseRequest; +import io.metersphere.api.dto.definition.*; import io.metersphere.api.service.ApiTestCaseService; import io.metersphere.base.domain.ApiTestCaseWithBLOBs; import io.metersphere.commons.constants.RoleConstants; @@ -81,11 +78,22 @@ public class ApiTestCaseController { apiTestCaseService.editApiBath(request); } + @PostMapping("/batch/editByParam") + @RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR) + public void editApiBathByParam(@RequestBody ApiTestBatchRequest request) { + apiTestCaseService.editApiBathByParam(request); + } + @PostMapping("/deleteBatch") public void deleteBatch(@RequestBody List ids) { apiTestCaseService.deleteBatch(ids); } + @PostMapping("/deleteBatchByParam") + public void deleteBatchByParam(@RequestBody ApiTestBatchRequest request) { + apiTestCaseService.deleteBatchByParam(request); + } + @PostMapping("/relevance") public void testPlanRelevance(@RequestBody ApiCaseRelevanceRequest request) { apiTestCaseService.relevanceByCase(request); diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/ApiTestBatchRequest.java b/backend/src/main/java/io/metersphere/api/dto/definition/ApiTestBatchRequest.java new file mode 100644 index 0000000000..fae3495019 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/ApiTestBatchRequest.java @@ -0,0 +1,51 @@ +package io.metersphere.api.dto.definition; + +import io.metersphere.base.domain.ApiDefinitionWithBLOBs; +import io.metersphere.base.domain.ApiTestCaseWithBLOBs; +import io.metersphere.controller.request.OrderRequest; +import lombok.Getter; +import lombok.Setter; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Getter +@Setter +public class ApiTestBatchRequest extends ApiTestCaseWithBLOBs { + private List ids; + private List orders; + private String projectId; + + /** + * isSelectAllDate:选择的数据是否是全部数据(全部数据是不受分页影响的数据) + * filters: 数据状态 + * name:如果是全部数据,那么表格如果历经查询,查询参数是什么 + * moduleIds: 哪些模块的数据 + * unSelectIds:是否在页面上有未勾选的数据,有的话他们的ID是哪些。 + * filters/name/moduleIds/unSeelctIds 只在isSelectAllDate为true时需要。为了让程序能明确批量的范围。 + */ + private boolean isSelectAllDate; + + private Map> filters; + + private String name; + + private List moduleIds; + + private List unSelectIds; + + private String protocol; + + private String status; + + public void cleanSelectParam() { + filters = new HashMap<>(); + name = null; + moduleIds = new ArrayList<>(); + protocol = null; + status = null; + } + +} diff --git a/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java b/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java index 1d2ec06127..9465a3caea 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java @@ -385,6 +385,7 @@ public class ApiAutomationService { selectRequest.setName(name); selectRequest.setProjectId(projectId); selectRequest.setFilters(filters); + selectRequest.setWorkspaceId(SessionUtils.getCurrentWorkspaceId()); List list = extApiScenarioMapper.list(selectRequest); List allIds = list.stream().map(ApiScenarioDTO::getId).collect(Collectors.toList()); List ids = allIds.stream().filter(id -> !unSelectIds.contains(id)).collect(Collectors.toList()); 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 07f30a8a03..092cf07e13 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java @@ -485,6 +485,7 @@ public class ApiDefinitionService { request.setName(name); request.setModuleIds(moduleIds); request.setProjectId(projectId); + request.setWorkspaceId(SessionUtils.getCurrentWorkspaceId()); List resList = extApiDefinitionMapper.list(request); List ids = new ArrayList<>(0); if (!resList.isEmpty()) { 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 81f160bc51..6219e5e609 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiTestCaseService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiTestCaseService.java @@ -1,12 +1,11 @@ package io.metersphere.api.service; import com.alibaba.fastjson.JSONObject; +import io.metersphere.api.dto.automation.ApiScenarioDTO; +import io.metersphere.api.dto.automation.ApiScenarioRequest; import io.metersphere.api.dto.datacount.ApiDataCountResult; -import io.metersphere.api.dto.definition.ApiTestCaseRequest; -import io.metersphere.api.dto.definition.ApiTestCaseResult; -import io.metersphere.api.dto.definition.SaveApiTestCaseRequest; +import io.metersphere.api.dto.definition.*; import io.metersphere.api.dto.ApiCaseBatchRequest; -import io.metersphere.api.dto.definition.ApiTestCaseDTO; import io.metersphere.base.domain.*; import io.metersphere.base.mapper.ApiDefinitionMapper; import io.metersphere.base.mapper.ApiTestCaseMapper; @@ -26,6 +25,7 @@ import org.apache.ibatis.session.ExecutorType; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.aspectj.util.FileUtil; +import org.python.antlr.ast.Str; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; @@ -348,4 +348,41 @@ public class ApiTestCaseService { List list = extApiTestCaseMapper.getRequest(request); return list.stream().collect(Collectors.toMap(ApiTestCaseWithBLOBs::getId, ApiTestCaseWithBLOBs::getRequest)); } + + public void deleteBatchByParam(ApiTestBatchRequest request) { + List ids = request.getIds(); + if(request.isSelectAllDate()){ + ids = this.getAllApiCaseIdsByFontedSelect(request.getFilters(),request.getModuleIds(),request.getName(),request.getProjectId(),request.getProtocol(),request.getUnSelectIds(),request.getStatus()); + } + this.deleteBatch(ids); + } + + public void editApiBathByParam(ApiTestBatchRequest request) { + List ids = request.getIds(); + if(request.isSelectAllDate()){ + ids = this.getAllApiCaseIdsByFontedSelect(request.getFilters(),request.getModuleIds(),request.getName(),request.getProjectId(),request.getProtocol(),request.getUnSelectIds(),request.getStatus()); + } + request.cleanSelectParam(); + ApiTestCaseExample apiDefinitionExample = new ApiTestCaseExample(); + apiDefinitionExample.createCriteria().andIdIn(ids); + ApiTestCaseWithBLOBs apiDefinitionWithBLOBs = new ApiTestCaseWithBLOBs(); + BeanUtils.copyBean(apiDefinitionWithBLOBs, request); + apiDefinitionWithBLOBs.setUpdateTime(System.currentTimeMillis()); + apiTestCaseMapper.updateByExampleSelective(apiDefinitionWithBLOBs, apiDefinitionExample); + } + + private List getAllApiCaseIdsByFontedSelect(Map> filters,ListmoduleIds, String name, String projectId, String protocol,List unSelectIds,String status) { + ApiTestCaseRequest selectRequest = new ApiTestCaseRequest(); + selectRequest.setFilters(filters); + selectRequest.setModuleIds(moduleIds); + selectRequest.setName(name); + selectRequest.setProjectId(projectId); + selectRequest.setProtocol(protocol); + selectRequest.setStatus(status); + selectRequest.setWorkspaceId(SessionUtils.getCurrentWorkspaceId()); + List list = extApiTestCaseMapper.list(selectRequest); + List allIds = list.stream().map(ApiTestCaseResult::getId).collect(Collectors.toList()); + List ids = allIds.stream().filter(id -> !unSelectIds.contains(id)).collect(Collectors.toList()); + return ids; + } } 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 c2fb3b5fbc..dad5520ea2 100644 --- a/frontend/src/business/components/api/definition/components/list/ApiCaseSimpleList.vue +++ b/frontend/src/business/components/api/definition/components/list/ApiCaseSimpleList.vue @@ -7,6 +7,7 @@ + + + + + + + {{$t('api_test.batch_menus.select_all_data',[total])}} + + + {{$t('api_test.batch_menus.select_show_data',[tableData.length])}} + + + @@ -143,7 +157,10 @@ pageSize: 10, total: 0, screenHeight: document.documentElement.clientHeight - 330,//屏幕高度 - environmentId: undefined + environmentId: undefined, + selectAll: false, + unSelection:[], + selectDataCounts:0, } }, props: { @@ -216,7 +233,9 @@ this.condition.status = "Trash"; this.condition.moduleIds = []; } - + this.selectAll = false; + this.unSelection = []; + this.selectDataCounts = 0; this.condition.projectId = getCurrentProjectID(); if (this.currentProtocol != null) { @@ -226,6 +245,7 @@ this.result = this.$post('/api/testcase/list/' + this.currentPage + "/" + this.pageSize, this.condition, response => { this.total = response.data.itemCount; this.tableData = response.data.listObject; + this.unSelection = response.data.listObject.map(s=>s.id); }); } }, @@ -237,6 +257,7 @@ // }, handleSelect(selection, row) { _handleSelect(this, selection, row, this.selectRows); + this.selectRowsCount(this.selectRows) }, showExecResult(row) { this.visible = false; @@ -256,6 +277,7 @@ }, handleSelectAll(selection) { _handleSelectAll(this, selection, this.tableData, this.selectRows); + this.selectRowsCount(this.selectRows) }, search() { this.initTable(); @@ -294,7 +316,13 @@ confirmButtonText: this.$t('commons.confirm'), callback: (action) => { if (action === 'confirm') { - this.$post('/api/testcase/deleteBatch/', Array.from(this.selectRows).map(row => row.id), () => { + let obj = {}; + obj.projectId = getCurrentProjectID(); + obj.selectAllDate = this.isSelectAllDate; + obj.unSelectIds = this.unSelection; + obj.ids = Array.from(this.selectRows).map(row => row.id); + obj = Object.assign(obj, this.condition); + this.$post('/api/testcase/deleteBatchByParam/', obj , () => { this.selectRows.clear(); this.initTable(); this.$success(this.$t('commons.delete_success')); @@ -327,7 +355,12 @@ let param = {}; param[form.type] = form.value; param.ids = ids; - this.$post('/api/testcase/batch/edit', param, () => { + + param.projectId = getCurrentProjectID(); + param.selectAllDate = this.isSelectAllDate; + param.unSelectIds = this.unSelection; + param = Object.assign(param, this.condition); + this.$post('/api/testcase/batch/editByParam', param, () => { this.$success(this.$t('commons.save_success')); this.initTable(); }); @@ -355,6 +388,31 @@ }, setEnvironment(data) { this.environmentId = data.id; + }, + selectRowsCount(selection){ + let selectedIDs = this.getIds(selection); + let allIDs = this.tableData.map(s=>s.id); + this.unSelection = allIDs.filter(function (val) { + return selectedIDs.indexOf(val) === -1 + }); + if(this.isSelectAllDate){ + this.selectDataCounts =this.total - this.unSelection.length; + }else { + this.selectDataCounts =selection.size; + } + }, + isSelectDataAll(dataType) { + this.isSelectAllDate = dataType; + this.selectRowsCount(this.selectRows) + //如果已经全选,不需要再操作了 + if (this.selectRows.size != this.tableData.length) { + this.$refs.caseTable.toggleAllSelection(true); + } + }, + getIds(rowSets){ + let rowArray = Array.from(rowSets) + let ids = rowArray.map(s=>s.id); + return ids; } }, } From c8ceae2845f5ee389e006db8cd7dc49d9c56db90 Mon Sep 17 00:00:00 2001 From: "song.tianyang" Date: Wed, 30 Dec 2020 16:53:18 +0800 Subject: [PATCH 02/23] =?UTF-8?q?refactor:=20=E5=88=9B=E5=BB=BA=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E3=80=81=E5=9C=BA=E6=99=AF=E6=A8=A1=E5=9D=97=E4=B8=BA?= =?UTF-8?q?=E7=A9=BA=E4=B8=8B=E6=8B=89=E6=A1=86=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 创建接口、场景模块为空时,下拉框增加"去添加"的按钮,点击即可增加 --- .../components/api/definition/ApiDefinition.vue | 6 +++++- .../api/definition/components/ApiConfig.vue | 11 +++++++---- .../definition/components/complete/BasisApi.vue | 17 +++++++++++++++-- .../complete/EditCompleteDubboApi.vue | 7 +++++-- .../components/complete/EditCompleteHTTPApi.vue | 16 +++++++++++++++- .../components/complete/EditCompleteSQLApi.vue | 5 ++++- .../components/complete/EditCompleteTCPApi.vue | 7 +++++-- .../definition/components/module/ApiModule.vue | 7 +++++++ frontend/src/i18n/en-US.js | 4 ++++ frontend/src/i18n/zh-CN.js | 4 ++++ frontend/src/i18n/zh-TW.js | 4 ++++ 11 files changed, 75 insertions(+), 13 deletions(-) diff --git a/frontend/src/business/components/api/definition/ApiDefinition.vue b/frontend/src/business/components/api/definition/ApiDefinition.vue index a394f1ca61..4f6293dec4 100644 --- a/frontend/src/business/components/api/definition/ApiDefinition.vue +++ b/frontend/src/business/components/api/definition/ApiDefinition.vue @@ -63,7 +63,7 @@
- @@ -215,6 +215,10 @@ this.apiTabs = tabs.filter(tab => tab.name !== targetName); this.refresh(); }, + //创建左侧树的根目录模块 + createRootModel(){ + this.$refs.nodeTree.createRootModel(); + }, handleTabsEdit(targetName, action, api) { if (!getCurrentProjectID()) { this.$warning(this.$t('commons.check_project_tip')); diff --git a/frontend/src/business/components/api/definition/components/ApiConfig.vue b/frontend/src/business/components/api/definition/components/ApiConfig.vue index 2f3ef68ada..393c6e780a 100644 --- a/frontend/src/business/components/api/definition/components/ApiConfig.vue +++ b/frontend/src/business/components/api/definition/components/ApiConfig.vue @@ -2,16 +2,16 @@
- - - -
@@ -75,6 +75,9 @@ this.$emit('runTest', data); }) }, + createRootModelInTree(){ + this.$emit("createRootModel"); + }, getMaintainerOptions() { let workspaceId = localStorage.getItem(WORKSPACE_ID); this.$post('/user/ws/member/tester/list', {workspaceId: workspaceId}, response => { 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 13544bc4da..e964545917 100644 --- a/frontend/src/business/components/api/definition/components/complete/BasisApi.vue +++ b/frontend/src/business/components/api/definition/components/complete/BasisApi.vue @@ -11,7 +11,17 @@ - +
+ +
+
+ +
+ {{$t('api_test.definition.select_comp.no_data')}}, + {{$t('api_test.definition.select_comp.add_data')}} +
+
+
@@ -115,7 +125,10 @@ this.$emit('callback'); } }) - } + }, + createModules(){ + this.$emit("createRootModelInTree"); + }, } } diff --git a/frontend/src/business/components/api/definition/components/complete/EditCompleteDubboApi.vue b/frontend/src/business/components/api/definition/components/complete/EditCompleteDubboApi.vue index 84380d6fa3..fb8a41a3ce 100644 --- a/frontend/src/business/components/api/definition/components/complete/EditCompleteDubboApi.vue +++ b/frontend/src/business/components/api/definition/components/complete/EditCompleteDubboApi.vue @@ -16,7 +16,7 @@
- + @@ -71,7 +71,10 @@ this.basisData.request = this.request; this.$emit('runTest', this.basisData); } - } + }, + createRootModelInTree(){ + this.$emit("createRootModelInTree"); + }, }, computed: {} 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 9648a9bbb3..1561e5484b 100644 --- a/frontend/src/business/components/api/definition/components/complete/EditCompleteHTTPApi.vue +++ b/frontend/src/business/components/api/definition/components/complete/EditCompleteHTTPApi.vue @@ -36,7 +36,17 @@ - +
+ +
+
+ +
+ {{$t('api_test.definition.select_comp.no_data')}}, + {{$t('api_test.definition.select_comp.add_data')}} +
+
+
@@ -96,6 +106,7 @@ import {REQ_METHOD, API_STATUS} from "../../model/JsonData"; import MsJsr233Processor from "../processor/Jsr233Processor"; import {KeyValue} from "../../model/ApiTestModel"; + // import {append} from "./../../../../track/common/NodeTree"; export default { name: "MsAddCompleteHttpApi", @@ -161,6 +172,9 @@ } }) }, + createModules(){ + this.$emit("createRootModelInTree"); + }, getPath(id) { if (id === null) { return null; diff --git a/frontend/src/business/components/api/definition/components/complete/EditCompleteSQLApi.vue b/frontend/src/business/components/api/definition/components/complete/EditCompleteSQLApi.vue index 54bc58ea4e..4ca16a2d0b 100644 --- a/frontend/src/business/components/api/definition/components/complete/EditCompleteSQLApi.vue +++ b/frontend/src/business/components/api/definition/components/complete/EditCompleteSQLApi.vue @@ -15,7 +15,7 @@
- + @@ -70,6 +70,9 @@ this.$emit('runTest', this.basisData); } }, + createRootModelInTree(){ + this.$emit("createRootModelInTree"); + }, }, } diff --git a/frontend/src/business/components/api/definition/components/complete/EditCompleteTCPApi.vue b/frontend/src/business/components/api/definition/components/complete/EditCompleteTCPApi.vue index 5e24359337..6d26a4da03 100644 --- a/frontend/src/business/components/api/definition/components/complete/EditCompleteTCPApi.vue +++ b/frontend/src/business/components/api/definition/components/complete/EditCompleteTCPApi.vue @@ -15,7 +15,7 @@
- + @@ -70,7 +70,10 @@ this.basisData.request = this.request; this.$emit('runTest', this.basisData); } - } + }, + createRootModelInTree(){ + this.$emit("createRootModelInTree"); + }, }, } 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 cb9844960a..73e221ba19 100644 --- a/frontend/src/business/components/api/definition/components/module/ApiModule.vue +++ b/frontend/src/business/components/api/definition/components/module/ApiModule.vue @@ -176,6 +176,13 @@ this.$emit("nodeSelectEvent", node, nodeIds, pNodes); } }, + //创建根目录的模块---供父类使用 + createRootModel(){ + let dataArr = this.$refs.nodeTree.extendTreeNodes; + if(dataArr.length>0){ + this.$refs.nodeTree.append({},dataArr[0]); + } + }, exportAPI() { this.$emit('exportAPI'); }, diff --git a/frontend/src/i18n/en-US.js b/frontend/src/i18n/en-US.js index bfd52faaa2..ea681e8867 100644 --- a/frontend/src/i18n/en-US.js +++ b/frontend/src/i18n/en-US.js @@ -505,6 +505,10 @@ export default { api_case_status: "Ise case status", api_case_passing_rate: "Use case pass rate", create_tip: "Note: Detailed interface information can be filled out on the edit page", + select_comp:{ + no_data:"No Data", + add_data:"Add Data" + }, request: { grade_info: "Filter by rank", run_env: "Operating environment", diff --git a/frontend/src/i18n/zh-CN.js b/frontend/src/i18n/zh-CN.js index cf5240928b..e5af616f2e 100644 --- a/frontend/src/i18n/zh-CN.js +++ b/frontend/src/i18n/zh-CN.js @@ -503,6 +503,10 @@ export default { api_case_status: "用例状态", api_case_passing_rate: "用例通过率", create_tip: "注: 详细的接口信息可以在编辑页面填写", + select_comp:{ + no_data:"无数据", + add_data:"去添加" + }, request: { grade_info: "按等级筛选", run_env: "运行环境", diff --git a/frontend/src/i18n/zh-TW.js b/frontend/src/i18n/zh-TW.js index a0aee26147..aa11d8a947 100644 --- a/frontend/src/i18n/zh-TW.js +++ b/frontend/src/i18n/zh-TW.js @@ -503,6 +503,10 @@ export default { api_case_status: "用例狀態", api_case_passing_rate: "用例通過率", create_tip: "註: 詳細的接口信息可以在編輯頁面填寫", + select_comp:{ + no_data:"無數據", + add_data:"去添加" + }, request: { grade_info: "按等級筛选", run_env: "運行環境", From 56d0a24fd8d78de295ed90ece30343b2d48ce528 Mon Sep 17 00:00:00 2001 From: fit2-zhao Date: Wed, 30 Dec 2020 16:55:12 +0800 Subject: [PATCH 03/23] =?UTF-8?q?fix(=E6=8E=A5=E5=8F=A3=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E5=8C=96):=20=E4=BF=AE=E5=A4=8D=E9=80=9A=E7=94=A8=E8=AF=B7?= =?UTF-8?q?=E6=B1=82=E5=A4=B4=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/dto/definition/request/MsScenario.java | 6 ------ .../api/dto/definition/request/MsTestElement.java | 10 ---------- .../request/sampler/MsHTTPSamplerProxy.java | 12 +++++++++--- 3 files changed, 9 insertions(+), 19 deletions(-) diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/MsScenario.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/MsScenario.java index ae76ff73db..19097a14bf 100644 --- a/backend/src/main/java/io/metersphere/api/dto/definition/request/MsScenario.java +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/MsScenario.java @@ -109,12 +109,6 @@ public class MsScenario extends MsTestElement { arguments.addArgument(keyValue.getName(), keyValue.getValue(), "=") ); } - if (config != null && config.getConfig() != null && config.getConfig().getCommonConfig() != null - && CollectionUtils.isNotEmpty(config.getConfig().getHttpConfig().getHeaders())) { - config.getConfig().getHttpConfig().getHeaders().stream().filter(KeyValue::isValid).filter(KeyValue::isEnable).forEach(keyValue -> - arguments.addArgument(keyValue.getName(), keyValue.getValue(), "=") - ); - } return arguments; } } 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 b54ffdff16..54f8dc608e 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 @@ -149,16 +149,6 @@ public abstract class MsTestElement { arguments.addArgument(keyValue.getName(), keyValue.getValue(), "=") ); } - if (config != null && config.getConfig() != null && config.getConfig().getCommonConfig() != null - && CollectionUtils.isNotEmpty(config.getConfig().getHttpConfig().getHeaders())) { - arguments.setEnabled(true); - arguments.setName(name + "Variables"); - arguments.setProperty(TestElement.TEST_CLASS, Arguments.class.getName()); - arguments.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("ArgumentsPanel")); - config.getConfig().getHttpConfig().getHeaders().stream().filter(KeyValue::isValid).filter(KeyValue::isEnable).forEach(keyValue -> - arguments.addArgument(keyValue.getName(), keyValue.getValue(), "=") - ); - } return arguments; } } 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 cc4947e9ff..2c3b7d0f98 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 @@ -189,9 +189,15 @@ public class MsHTTPSamplerProxy extends MsTestElement { } final HashTree httpSamplerTree = tree.add(sampler); - if (CollectionUtils.isNotEmpty(this.headers)) { - setHeader(httpSamplerTree); + // 通用请求Headers + if (config != null && config.getConfig() != null && config.getConfig().getHttpConfig() != null + && CollectionUtils.isNotEmpty(config.getConfig().getHttpConfig().getHeaders())) { + setHeader(httpSamplerTree, config.getConfig().getHttpConfig().getHeaders()); } + if (CollectionUtils.isNotEmpty(this.headers)) { + setHeader(httpSamplerTree, this.headers); + } + //判断是否要开启DNS if (config != null && config.getConfig() != null && config.getConfig().getCommonConfig() != null && config.getConfig().getCommonConfig().isEnableHost()) { @@ -266,7 +272,7 @@ public class MsHTTPSamplerProxy extends MsTestElement { return arguments; } - public void setHeader(HashTree tree) { + public void setHeader(HashTree tree, List headers) { HeaderManager headerManager = new HeaderManager(); headerManager.setEnabled(true); headerManager.setName(this.getName() + "Headers"); From 1631803bbd1c9f1e50bfb63a8b59f2e4efb3cfaa Mon Sep 17 00:00:00 2001 From: shiziyuan9527 Date: Wed, 30 Dec 2020 18:15:07 +0800 Subject: [PATCH 04/23] =?UTF-8?q?refactor(=E6=B5=8B=E8=AF=95=E8=AE=A1?= =?UTF-8?q?=E5=88=92):=20=E6=B5=8B=E8=AF=95=E8=AE=A1=E5=88=92=E4=BC=98?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/metersphere/base/domain/TestPlan.java | 5 +- .../base/domain/TestPlanExample.java | 70 +++++++++++++++++++ .../base/mapper/TestPlanMapper.xml | 31 ++++++-- .../base/mapper/ext/ExtTestPlanMapper.xml | 18 +++-- .../testcase/QueryTestPlanRequest.java | 2 + .../track/service/TestPlanProjectService.java | 15 ++-- .../track/service/TestPlanService.java | 52 +++++++++----- .../V59__test_plan_add_project_id.sql | 55 +++++++++++++++ .../track/plan/components/TestPlanEdit.vue | 10 +-- .../track/plan/components/TestPlanList.vue | 4 +- frontend/src/i18n/en-US.js | 2 + frontend/src/i18n/zh-CN.js | 2 + frontend/src/i18n/zh-TW.js | 2 + 13 files changed, 224 insertions(+), 44 deletions(-) create mode 100644 backend/src/main/resources/db/migration/V59__test_plan_add_project_id.sql diff --git a/backend/src/main/java/io/metersphere/base/domain/TestPlan.java b/backend/src/main/java/io/metersphere/base/domain/TestPlan.java index f66f9aa094..e08e5acda2 100644 --- a/backend/src/main/java/io/metersphere/base/domain/TestPlan.java +++ b/backend/src/main/java/io/metersphere/base/domain/TestPlan.java @@ -1,8 +1,7 @@ package io.metersphere.base.domain; -import lombok.Data; - import java.io.Serializable; +import lombok.Data; @Data public class TestPlan implements Serializable { @@ -40,6 +39,8 @@ public class TestPlan implements Serializable { private String creator; + private String projectId; + private String tags; private static final long serialVersionUID = 1L; diff --git a/backend/src/main/java/io/metersphere/base/domain/TestPlanExample.java b/backend/src/main/java/io/metersphere/base/domain/TestPlanExample.java index 4512f8f5d2..9c32388ed3 100644 --- a/backend/src/main/java/io/metersphere/base/domain/TestPlanExample.java +++ b/backend/src/main/java/io/metersphere/base/domain/TestPlanExample.java @@ -1233,6 +1233,76 @@ public class TestPlanExample { addCriterion("creator not between", value1, value2, "creator"); return (Criteria) this; } + + public Criteria andProjectIdIsNull() { + addCriterion("project_id is null"); + return (Criteria) this; + } + + public Criteria andProjectIdIsNotNull() { + addCriterion("project_id is not null"); + return (Criteria) this; + } + + public Criteria andProjectIdEqualTo(String value) { + addCriterion("project_id =", value, "projectId"); + return (Criteria) this; + } + + public Criteria andProjectIdNotEqualTo(String value) { + addCriterion("project_id <>", value, "projectId"); + return (Criteria) this; + } + + public Criteria andProjectIdGreaterThan(String value) { + addCriterion("project_id >", value, "projectId"); + return (Criteria) this; + } + + public Criteria andProjectIdGreaterThanOrEqualTo(String value) { + addCriterion("project_id >=", value, "projectId"); + return (Criteria) this; + } + + public Criteria andProjectIdLessThan(String value) { + addCriterion("project_id <", value, "projectId"); + return (Criteria) this; + } + + public Criteria andProjectIdLessThanOrEqualTo(String value) { + addCriterion("project_id <=", value, "projectId"); + return (Criteria) this; + } + + public Criteria andProjectIdLike(String value) { + addCriterion("project_id like", value, "projectId"); + return (Criteria) this; + } + + public Criteria andProjectIdNotLike(String value) { + addCriterion("project_id not like", value, "projectId"); + return (Criteria) this; + } + + public Criteria andProjectIdIn(List values) { + addCriterion("project_id in", values, "projectId"); + return (Criteria) this; + } + + public Criteria andProjectIdNotIn(List values) { + addCriterion("project_id not in", values, "projectId"); + return (Criteria) this; + } + + public Criteria andProjectIdBetween(String value1, String value2) { + addCriterion("project_id between", value1, value2, "projectId"); + return (Criteria) this; + } + + public Criteria andProjectIdNotBetween(String value1, String value2) { + addCriterion("project_id not between", value1, value2, "projectId"); + return (Criteria) this; + } } public static class Criteria extends GeneratedCriteria { diff --git a/backend/src/main/java/io/metersphere/base/mapper/TestPlanMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/TestPlanMapper.xml index d423f2d073..933036e208 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/TestPlanMapper.xml +++ b/backend/src/main/java/io/metersphere/base/mapper/TestPlanMapper.xml @@ -19,6 +19,7 @@ + @@ -84,7 +85,7 @@ id, workspace_id, report_id, `name`, description, `status`, stage, principal, test_case_match_rule, executor_match_rule, create_time, update_time, actual_end_time, planned_start_time, - planned_end_time, actual_start_time, creator + planned_end_time, actual_start_time, creator, project_id tags @@ -143,15 +144,15 @@ stage, principal, test_case_match_rule, executor_match_rule, create_time, update_time, actual_end_time, planned_start_time, planned_end_time, - actual_start_time, creator, tags - ) + actual_start_time, creator, project_id, + tags) values (#{id,jdbcType=VARCHAR}, #{workspaceId,jdbcType=VARCHAR}, #{reportId,jdbcType=VARCHAR}, #{name,jdbcType=VARCHAR}, #{description,jdbcType=VARCHAR}, #{status,jdbcType=VARCHAR}, #{stage,jdbcType=VARCHAR}, #{principal,jdbcType=VARCHAR}, #{testCaseMatchRule,jdbcType=VARCHAR}, #{executorMatchRule,jdbcType=VARCHAR}, #{createTime,jdbcType=BIGINT}, #{updateTime,jdbcType=BIGINT}, #{actualEndTime,jdbcType=BIGINT}, #{plannedStartTime,jdbcType=BIGINT}, #{plannedEndTime,jdbcType=BIGINT}, - #{actualStartTime,jdbcType=BIGINT}, #{creator,jdbcType=VARCHAR}, #{tags,jdbcType=LONGVARCHAR} - ) + #{actualStartTime,jdbcType=BIGINT}, #{creator,jdbcType=VARCHAR}, #{projectId,jdbcType=VARCHAR}, + #{tags,jdbcType=LONGVARCHAR}) insert into test_plan @@ -207,6 +208,9 @@ creator, + + project_id, + tags, @@ -263,6 +267,9 @@ #{creator,jdbcType=VARCHAR}, + + #{projectId,jdbcType=VARCHAR}, + #{tags,jdbcType=LONGVARCHAR}, @@ -328,6 +335,9 @@ creator = #{record.creator,jdbcType=VARCHAR}, + + project_id = #{record.projectId,jdbcType=VARCHAR}, + tags = #{record.tags,jdbcType=LONGVARCHAR}, @@ -355,6 +365,7 @@ planned_end_time = #{record.plannedEndTime,jdbcType=BIGINT}, actual_start_time = #{record.actualStartTime,jdbcType=BIGINT}, creator = #{record.creator,jdbcType=VARCHAR}, + project_id = #{record.projectId,jdbcType=VARCHAR}, tags = #{record.tags,jdbcType=LONGVARCHAR} @@ -378,7 +389,8 @@ planned_start_time = #{record.plannedStartTime,jdbcType=BIGINT}, planned_end_time = #{record.plannedEndTime,jdbcType=BIGINT}, actual_start_time = #{record.actualStartTime,jdbcType=BIGINT}, - creator = #{record.creator,jdbcType=VARCHAR} + creator = #{record.creator,jdbcType=VARCHAR}, + project_id = #{record.projectId,jdbcType=VARCHAR} @@ -434,6 +446,9 @@ creator = #{creator,jdbcType=VARCHAR}, + + project_id = #{projectId,jdbcType=VARCHAR}, + tags = #{tags,jdbcType=LONGVARCHAR}, @@ -458,6 +473,7 @@ planned_end_time = #{plannedEndTime,jdbcType=BIGINT}, actual_start_time = #{actualStartTime,jdbcType=BIGINT}, creator = #{creator,jdbcType=VARCHAR}, + project_id = #{projectId,jdbcType=VARCHAR}, tags = #{tags,jdbcType=LONGVARCHAR} where id = #{id,jdbcType=VARCHAR} @@ -478,7 +494,8 @@ planned_start_time = #{plannedStartTime,jdbcType=BIGINT}, planned_end_time = #{plannedEndTime,jdbcType=BIGINT}, actual_start_time = #{actualStartTime,jdbcType=BIGINT}, - creator = #{creator,jdbcType=VARCHAR} + creator = #{creator,jdbcType=VARCHAR}, + project_id = #{projectId,jdbcType=VARCHAR} where id = #{id,jdbcType=VARCHAR} \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanMapper.xml index b7c5a14959..587b589566 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanMapper.xml +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanMapper.xml @@ -98,9 +98,9 @@ select distinct test_plan.* from test_plan - inner join test_plan_project on test_plan.id = test_plan_project.test_plan_id test_plan.workspace_id = #{request.workspaceId} - and test_plan_project.project_id = #{request.projectId} + and test_plan.project_id = #{request.projectId} and (test_plan.principal = #{request.principal} diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanTestCaseMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanTestCaseMapper.xml index 13eda218c7..d8d7c2f912 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanTestCaseMapper.xml +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanTestCaseMapper.xml @@ -272,13 +272,11 @@ select distinct plan_id from test_plan_test_case inner join test_plan on test_plan_test_case.plan_id = test_plan.id - inner join test_plan_project - on test_plan.id = test_plan_project.test_plan_id test_plan_test_case.executor = #{userId} and test_plan.workspace_id = #{workspaceId} - and test_plan_project.project_id = #{projectId} + and test_plan.project_id = #{projectId} diff --git a/frontend/src/business/components/track/home/components/RelatedTestPlanList.vue b/frontend/src/business/components/track/home/components/RelatedTestPlanList.vue index 39e0e55d17..cad7300910 100644 --- a/frontend/src/business/components/track/home/components/RelatedTestPlanList.vue +++ b/frontend/src/business/components/track/home/components/RelatedTestPlanList.vue @@ -58,11 +58,11 @@ - - + + + + + @@ -94,12 +94,12 @@ } this.result = this.$post('/test/plan/list/all/relate', {}, response => { this.tableData = response.data; - for (let i = 0; i < this.tableData.length; i++) { - let path = "/test/plan/project/name/" + this.tableData[i].id; - this.$get(path, res => { - this.$set(this.tableData[i], "projectName", res.data); - }) - } + // for (let i = 0; i < this.tableData.length; i++) { + // let path = "/test/plan/project/name/" + this.tableData[i].id; + // this.$get(path, res => { + // this.$set(this.tableData[i], "projectName", res.data); + // }) + // } }); }, intoPlan(row, event, column) { From 03135592b0f56268192b5e0b5f0054fbdd706770 Mon Sep 17 00:00:00 2001 From: shiziyuan9527 Date: Wed, 30 Dec 2020 18:59:24 +0800 Subject: [PATCH 06/23] =?UTF-8?q?refactor:=20=E6=B5=8B=E8=AF=95=E8=AE=A1?= =?UTF-8?q?=E5=88=92=E6=89=80=E5=B1=9E=E9=A1=B9=E7=9B=AE=E5=90=8D=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/automation/scenario/testplan/TestPlanList.vue | 2 -- 1 file changed, 2 deletions(-) diff --git a/frontend/src/business/components/api/automation/scenario/testplan/TestPlanList.vue b/frontend/src/business/components/api/automation/scenario/testplan/TestPlanList.vue index e40e4411e1..c677e2b95c 100644 --- a/frontend/src/business/components/api/automation/scenario/testplan/TestPlanList.vue +++ b/frontend/src/business/components/api/automation/scenario/testplan/TestPlanList.vue @@ -230,9 +230,7 @@ let path = "/test/plan/project"; this.$post(path, {planId: this.tableData[i].id}, res => { let arr = res.data; - let projectName = arr.map(data => data.name).join("、"); let projectIds = arr.map(data => data.id); - this.$set(this.tableData[i], "projectName", projectName); this.$set(this.tableData[i], "projectIds", projectIds); }) } From e9454d273bf8bdd8d15fac31087eafaffaffceb0 Mon Sep 17 00:00:00 2001 From: shiziyuan9527 Date: Wed, 30 Dec 2020 20:31:49 +0800 Subject: [PATCH 07/23] =?UTF-8?q?fix(=E6=B5=8B=E8=AF=95=E8=AE=A1=E5=88=92)?= =?UTF-8?q?:=20=E4=BF=AE=E5=A4=8D=E7=BC=96=E8=BE=91=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E8=AE=A1=E5=88=92=E7=BC=BA=E9=99=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/metersphere/track/service/TestPlanService.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/backend/src/main/java/io/metersphere/track/service/TestPlanService.java b/backend/src/main/java/io/metersphere/track/service/TestPlanService.java index 002ade66fc..fa68a27d6c 100644 --- a/backend/src/main/java/io/metersphere/track/service/TestPlanService.java +++ b/backend/src/main/java/io/metersphere/track/service/TestPlanService.java @@ -213,6 +213,8 @@ public class TestPlanService { // 关联的项目下的用例idList List caseIds = null; + // 测试计划所属项目下的用例不解除关联 + projectIds.add(testPlan.getProjectId()); if (!CollectionUtils.isEmpty(projectIds)) { TestCaseExample example = new TestCaseExample(); example.createCriteria().andProjectIdIn(projectIds); @@ -220,7 +222,7 @@ public class TestPlanService { caseIds = caseList.stream().map(TestCase::getId).collect(Collectors.toList()); } - // 取消关联所属项目下的用例和计划的关系 + // 取消关联项目下的用例和计划的关系 TestPlanTestCaseExample testPlanTestCaseExample = new TestPlanTestCaseExample(); TestPlanTestCaseExample.Criteria criteria = testPlanTestCaseExample.createCriteria().andPlanIdEqualTo(testPlan.getId()); if (!CollectionUtils.isEmpty(caseIds)) { From aed55a97090826f396eb080d1cf174be518ca4d7 Mon Sep 17 00:00:00 2001 From: chenjianxing Date: Wed, 30 Dec 2020 22:00:00 +0800 Subject: [PATCH 08/23] =?UTF-8?q?refactor:=20=E6=94=AF=E6=8C=81swagger=203?= =?UTF-8?q?.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/pom.xml | 12 +- .../api/parse/ApiImportAbstractParser.java | 37 +- .../metersphere/api/parse/Swagger2Parser.java | 32 +- .../metersphere/api/parse/Swagger3Parser.java | 365 ++++++++++++++++++ .../metersphere/commons/utils/XMLUtils.java | 47 +++ .../controller/TestController.java | 10 + frontend/src/i18n/en-US.js | 2 +- frontend/src/i18n/zh-CN.js | 2 +- frontend/src/i18n/zh-TW.js | 2 +- 9 files changed, 465 insertions(+), 44 deletions(-) create mode 100644 backend/src/main/java/io/metersphere/api/parse/Swagger3Parser.java create mode 100644 backend/src/main/java/io/metersphere/commons/utils/XMLUtils.java diff --git a/backend/pom.xml b/backend/pom.xml index 7c1e20dd8e..0e4ec205fe 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -270,12 +270,12 @@ 1.0.51 - - - - - - + + + io.swagger.parser.v3 + swagger-parser + 2.0.18 + 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 4b80284658..c3b33ae0c8 100644 --- a/backend/src/main/java/io/metersphere/api/parse/ApiImportAbstractParser.java +++ b/backend/src/main/java/io/metersphere/api/parse/ApiImportAbstractParser.java @@ -70,6 +70,30 @@ public abstract class ApiImportAbstractParser implements ApiImportParser { } } + protected String getBodyType(String contentType) { + String bodyType = ""; + switch (contentType) { + case "application/x-www-form-urlencoded": + bodyType = Body.WWW_FROM; + break; + case "multipart/form-data": + bodyType = Body.FORM_DATA; + break; + case "application/json": + bodyType = Body.JSON; + break; + case "application/xml": + bodyType = Body.XML; + break; + case "application/octet-stream": + bodyType = Body.BINARY; + break; + default: + bodyType = Body.RAW; + } + return bodyType; + } + protected ApiDefinitionResult buildApiDefinition(String id, String name, String path, String method) { ApiDefinitionResult apiDefinition = new ApiDefinitionResult(); apiDefinition.setName(name); @@ -150,17 +174,4 @@ public abstract class ApiImportAbstractParser implements ApiImportParser { headers.add(new KeyValue(key, value, description)); } } -// protected void addHeader(HttpRequest request, String key, String value) { -// List headers = Optional.ofNullable(request.getHeaders()).orElse(new ArrayList<>()); -// boolean hasContentType = false; -// for (KeyValue header : headers) { -// if (StringUtils.equalsIgnoreCase(header.getName(), key)) { -// hasContentType = true; -// } -// } -// if (!hasContentType) { -// headers.save(new KeyValue(key, value)); -// } -// request.setHeaders(headers); -// } } diff --git a/backend/src/main/java/io/metersphere/api/parse/Swagger2Parser.java b/backend/src/main/java/io/metersphere/api/parse/Swagger2Parser.java index 4ce1a57eb0..2a04cd5898 100644 --- a/backend/src/main/java/io/metersphere/api/parse/Swagger2Parser.java +++ b/backend/src/main/java/io/metersphere/api/parse/Swagger2Parser.java @@ -32,11 +32,19 @@ public class Swagger2Parser extends ApiImportAbstractParser { @Override public ApiDefinitionImport parse(InputStream source, ApiTestImportRequest request) { Swagger swagger; + String sourceStr = ""; if (StringUtils.isNotBlank(request.getSwaggerUrl())) { swagger = new SwaggerParser().read(request.getSwaggerUrl()); } else { - swagger = new SwaggerParser().readWithInfo(getApiTestStr(source)).getSwagger(); + sourceStr = getApiTestStr(source); + swagger = new SwaggerParser().readWithInfo(sourceStr).getSwagger(); } + + if (swagger == null || swagger.getSwagger() == null) { + Swagger3Parser swagger3Parser = new Swagger3Parser(); + return swagger3Parser.parse(sourceStr, request); + } + ApiDefinitionImport definitionImport = new ApiDefinitionImport(); this.projectId = request.getProjectId(); definitionImport.setData(parseRequests(swagger, request.isSaved())); @@ -144,27 +152,7 @@ public class Swagger2Parser extends ApiImportAbstractParser { return Body.RAW; } String contentType = operation.getConsumes().get(0); - String bodyType = ""; - switch (contentType) { - case "application/x-www-form-urlencoded": - bodyType = Body.WWW_FROM; - break; - case "multipart/form-data": - bodyType = Body.FORM_DATA; - break; - case "application/json": - bodyType = Body.JSON; - break; - case "application/xml": - bodyType = Body.XML; - break; - case "": - bodyType = Body.BINARY; - break; - default: - bodyType = Body.RAW; - } - return bodyType; + return getBodyType(contentType); } private void parsePathParameters(Parameter parameter, List rests) { diff --git a/backend/src/main/java/io/metersphere/api/parse/Swagger3Parser.java b/backend/src/main/java/io/metersphere/api/parse/Swagger3Parser.java new file mode 100644 index 0000000000..135c6f7a28 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/parse/Swagger3Parser.java @@ -0,0 +1,365 @@ +package io.metersphere.api.parse; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import io.metersphere.api.dto.ApiTestImportRequest; +import io.metersphere.api.dto.definition.ApiDefinitionResult; +import io.metersphere.api.dto.definition.parse.ApiDefinitionImport; +import io.metersphere.api.dto.definition.request.sampler.MsHTTPSamplerProxy; +import io.metersphere.api.dto.definition.response.HttpResponse; +import io.metersphere.api.dto.scenario.Body; +import io.metersphere.api.dto.scenario.KeyValue; +import io.metersphere.api.dto.scenario.request.RequestType; +import io.metersphere.api.service.ApiModuleService; +import io.metersphere.base.domain.ApiModule; +import io.metersphere.commons.exception.MSException; +import io.metersphere.commons.utils.CommonBeanFactory; +import io.metersphere.commons.utils.LogUtil; +import io.metersphere.commons.utils.XMLUtils; +import io.swagger.models.parameters.FormParameter; +import io.swagger.parser.OpenAPIParser; +import io.swagger.v3.oas.models.*; +import io.swagger.v3.oas.models.headers.Header; +import io.swagger.v3.oas.models.media.*; +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 org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections.MapUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.http.HttpMethod; + +import java.io.InputStream; +import java.util.*; + + +public class Swagger3Parser extends ApiImportAbstractParser { + + private Components components; + + @Override + public ApiDefinitionImport parse(InputStream source, ApiTestImportRequest request) { + String sourceStr = ""; + if (StringUtils.isBlank(request.getSwaggerUrl())) { + sourceStr = getApiTestStr(source); + } + return parse(sourceStr, request); + } + + public ApiDefinitionImport parse(String sourceStr, ApiTestImportRequest request) { + SwaggerParseResult result; + if (StringUtils.isNotBlank(request.getSwaggerUrl())) { + result = new OpenAPIParser().readLocation("https://petstore3.swagger.io/api/v3/openapi.json", null, null); + } else { + result = new OpenAPIParser().readContents(sourceStr, null, null); + } + + if (result == null) { + MSException.throwException("解析失败,请确认选择的是 swagger 格式!"); + } + + OpenAPI openAPI = result.getOpenAPI(); + + if (result.getMessages() != null) { + result.getMessages().forEach(msg -> LogUtil.error(msg)); // validation errors and warnings + } + + ApiDefinitionImport definitionImport = new ApiDefinitionImport(); + this.projectId = request.getProjectId(); + definitionImport.setData(parseRequests(openAPI, request.isSaved())); + return definitionImport; + } + + private List parseRequests(OpenAPI openAPI, boolean isSaved) { + Paths paths = openAPI.getPaths(); + + Set pathNames = paths.keySet(); + + this.components = openAPI.getComponents(); + + List results = new ArrayList<>(); + + for (String pathName : pathNames) { + PathItem pathItem = paths.get(pathName); + + Map operationsMap = new HashMap<>(); + operationsMap.put(HttpMethod.GET.name(), pathItem.getGet()); + operationsMap.put(HttpMethod.POST.name(), pathItem.getPost()); + operationsMap.put(HttpMethod.DELETE.name(), pathItem.getDelete()); + operationsMap.put(HttpMethod.PUT.name(), pathItem.getPut()); + operationsMap.put(HttpMethod.PATCH.name(), pathItem.getPatch()); + operationsMap.put(HttpMethod.HEAD.name(), pathItem.getHead()); + operationsMap.put(HttpMethod.OPTIONS.name(), pathItem.getOptions()); + operationsMap.put(HttpMethod.TRACE.name(), pathItem.getTrace()); + + for (String method : operationsMap.keySet()) { + Operation operation = operationsMap.get(method); + if (operation != null) { + MsHTTPSamplerProxy request = buildRequest(operation, pathName, method); + ApiDefinitionResult apiDefinition = buildApiDefinition(request.getId(), operation, pathName, method); + parseParameters(operation, request); + parseRequestBody(operation.getRequestBody(), request.getBody()); + apiDefinition.setRequest(JSON.toJSONString(request)); + apiDefinition.setResponse(JSON.toJSONString(parseResponse(operation.getResponses()))); + buildModule(apiDefinition, operation, isSaved); + results.add(apiDefinition); + } + } + } + + return results; + } + + private void buildModule(ApiDefinitionResult apiDefinition, Operation operation, boolean isSaved) { + List tags = operation.getTags(); + if (tags != null) { + tags.forEach(tag -> { + apiModuleService = CommonBeanFactory.getBean(ApiModuleService.class); + ApiModule module = apiModuleService.getNewModule(tag, this.projectId, 1); + if (isSaved) { + createModule(module); + } + apiDefinition.setModuleId(module.getId()); + }); + } + } + + private ApiDefinitionResult buildApiDefinition(String id, Operation operation, String path, String method) { + String name = ""; + if (StringUtils.isNotBlank(operation.getSummary())) { + name = operation.getSummary(); + } else { + name = operation.getOperationId(); + } + return buildApiDefinition(id, name, path, method); + } + + private MsHTTPSamplerProxy buildRequest(Operation operation, String path, String method) { + String name = ""; + if (StringUtils.isNotBlank(operation.getSummary())) { + name = operation.getSummary(); + } else { + name = operation.getOperationId(); + } + return buildRequest(name, path, method); + } + + private void parseParameters(Operation operation, MsHTTPSamplerProxy request) { + + List parameters = operation.getParameters(); + + if (CollectionUtils.isEmpty(parameters)) { + return; + } + + // todo 路径变量 {xxx} 是否要转换 + parameters.forEach(parameter -> { + if (parameter instanceof QueryParameter) { + parseQueryParameters(parameter, request.getArguments()); + } else if (parameter instanceof PathParameter) { + parsePathParameters(parameter, request.getRest()); + } else if (parameter instanceof HeaderParameter) { + parseHeaderParameters(parameter, request.getHeaders()); + } else if (parameter instanceof CookieParameter) { + parseCookieParameters(parameter, request.getHeaders()); + } + }); + } + + private void parsePathParameters(Parameter parameter, List rests) { + PathParameter pathParameter = (PathParameter) parameter; + rests.add(new KeyValue(pathParameter.getName(), "", getDefaultStringValue(parameter.getDescription()))); + } + + private String getDefaultStringValue(String val) { + return StringUtils.isBlank(val) ? "" : val; + } + + private void parseCookieParameters(Parameter parameter, List headers) { + CookieParameter cookieParameter = (CookieParameter) parameter; + addCookie(headers, cookieParameter.getName(), "", getDefaultStringValue(cookieParameter.getDescription())); + } + + private void parseHeaderParameters(Parameter parameter, List headers) { + HeaderParameter headerParameter = (HeaderParameter) parameter; + addHeader(headers, headerParameter.getName(), "", getDefaultStringValue(headerParameter.getDescription())); + } + + private HttpResponse parseResponse(ApiResponses responses) { + HttpResponse msResponse = new HttpResponse(); + msResponse.setBody(new Body()); + msResponse.setHeaders(new ArrayList<>()); + msResponse.setType(RequestType.HTTP); + // todo 状态码要调整? + msResponse.setStatusCode(new ArrayList<>()); + if (responses != null) { + responses.forEach((responseCode, response) -> { + msResponse.getStatusCode().add(new KeyValue(responseCode, responseCode)); + parseResponseHeader(response, msResponse.getHeaders()); + parseResponseBody(response, msResponse.getBody()); + }); + } + return msResponse; + } + + private void parseResponseHeader(ApiResponse response, List msHeaders) { + Map headers = response.getHeaders(); + if (headers != null) { + headers.forEach((k, v) -> { + msHeaders.add(new KeyValue(k, "", v.getDescription())); + }); + } + } + + private void parseResponseBody(ApiResponse response, Body body) { + body.setRaw(response.getDescription()); + Content content = response.getContent(); + if (content == null) { + body.setType(Body.RAW); + body.setRaw(response.getDescription()); + } else { + parseBody(response.getContent(), body); + } + } + + private void parseRequestBody(RequestBody requestBody, Body body) { + if (requestBody == null) { + return; + } + parseBody(requestBody.getContent(), body); + } + + private void parseBody(Content content, Body body) { + if (content == null) { + return; + } + // 多个contentType ,优先取json + String contentType = ""; + MediaType mediaType = content.get(contentType); + if (mediaType == null) { + Set contentTypes = content.keySet(); + contentType = contentTypes.iterator().next(); + if (StringUtils.isBlank(contentType)) { + return; + } + mediaType = content.get(contentType); + } else { + contentType = org.springframework.http.MediaType.APPLICATION_JSON_VALUE; + } + + Set refSet = new HashSet<>(); + Map binaryKeyMap = new HashMap(); + Schema schema = mediaType.getSchema(); + Object bodyData = parseSchema(schema, refSet, binaryKeyMap); + + if (bodyData == null) { + return; + } + + body.setType(getBodyType(contentType)); + + if (StringUtils.equals(contentType, org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED_VALUE)) { + parseKvBody(schema, body, bodyData, binaryKeyMap); + } else if (StringUtils.equals(contentType, org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE)) { + body.setRaw(bodyData.toString()); + } else if (StringUtils.equals(contentType, org.springframework.http.MediaType.APPLICATION_JSON_VALUE)) { + body.setRaw(bodyData.toString()); + } else if (StringUtils.equals(contentType, org.springframework.http.MediaType.APPLICATION_XML_VALUE)) { + body.setRaw(parseXmlBody(schema, bodyData)); + } else if (StringUtils.equals(contentType, org.springframework.http.MediaType.APPLICATION_OCTET_STREAM_VALUE)) { + parseKvBody(schema, body, bodyData, binaryKeyMap); + } else { + body.setRaw(bodyData.toString()); + } + } + + private void parseKvBody(Schema schema, Body body, Object data, Map binaryKeyMap) { + if (data instanceof JSONObject) { + ((JSONObject) data).forEach((k, v) -> { + KeyValue kv = new KeyValue(k, v.toString()); + if (binaryKeyMap.keySet().contains(k)) { + kv.setDescription(binaryKeyMap.get(k)); + kv.setType("file"); + } + body.getKvs().add(kv); + }); + } else { + KeyValue kv = new KeyValue(schema.getName(), data.toString(), schema.getDescription()); + if (binaryKeyMap.keySet().contains(schema.getName())) { + kv.setDescription(binaryKeyMap.get(schema.getDescription())); + kv.setType("file"); + } + body.getKvs().add(kv); + } + } + + private String parseXmlBody(Schema schema, Object data) { + if (data instanceof JSONObject) { + return XMLUtils.jsonToXmlStr((JSONObject) data); + } else { + JSONObject object = new JSONObject(); + object.put(schema.getName(), getDefaultValueByPropertyType(schema)); + return XMLUtils.jsonToXmlStr(object); + } + } + + private Schema getModelByRef(String ref) { + if (StringUtils.isBlank(ref)) { + return null; + } + if (ref.split("/").length > 3) { + ref = ref.replace("#/components/schemas/", ""); + } + return this.components.getSchemas().get(ref); + } + + private Object parseSchema(Schema schema, Set refSet, Map binaryKeyMap) { + if (StringUtils.isNotBlank(schema.get$ref())) { + refSet.add(schema.get$ref()); + Object propertiesResult = parseSchemaProperties(getModelByRef(schema.get$ref()), refSet, binaryKeyMap); + return propertiesResult == null ? getDefaultValueByPropertyType(schema) : propertiesResult; + } else if (schema instanceof ArraySchema) { + JSONArray jsonArray = new JSONArray(); + Schema items = ((ArraySchema) schema).getItems(); + parseSchema(items, refSet, binaryKeyMap); + jsonArray.add(parseSchema(items, refSet, binaryKeyMap)); + return jsonArray; + } else if (schema instanceof BinarySchema) { + binaryKeyMap.put(schema.getName(), schema.getDescription()); + return getDefaultValueByPropertyType(schema); + } else { + Object propertiesResult = parseSchemaProperties(schema, refSet, binaryKeyMap); + return propertiesResult == null ? getDefaultValueByPropertyType(schema) : propertiesResult; + } + } + + private Object parseSchemaProperties(Schema schema, Set refSet, Map binaryKeyMap) { + Map properties = schema.getProperties(); + if (MapUtils.isEmpty(properties)) { + return null; + } + JSONObject jsonObject = new JSONObject(); + properties.forEach((key, value) -> { + jsonObject.put(key, parseSchema(value, refSet, binaryKeyMap)); + }); + return jsonObject; + } + + private Object getDefaultValueByPropertyType(Schema value) { + Object example = value.getExample(); + if (value instanceof IntegerSchema) { + return example == null ? 0 : example; + } else if (value instanceof NumberSchema) { + return example == null ? 0.0 : example; + } else {// todo 其他类型? + return getDefaultStringValue(value.getDescription()); + } + } + + private void parseQueryParameters(Parameter parameter, List arguments) { + QueryParameter queryParameter = (QueryParameter) parameter; + arguments.add(new KeyValue(queryParameter.getName(), "", getDefaultStringValue(queryParameter.getDescription()))); + } +} diff --git a/backend/src/main/java/io/metersphere/commons/utils/XMLUtils.java b/backend/src/main/java/io/metersphere/commons/utils/XMLUtils.java new file mode 100644 index 0000000000..10eb61872c --- /dev/null +++ b/backend/src/main/java/io/metersphere/commons/utils/XMLUtils.java @@ -0,0 +1,47 @@ +package io.metersphere.commons.utils; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import org.apache.commons.lang3.StringUtils; + +import java.util.Map; +import java.util.Set; + +public class XMLUtils { + + private static void jsonToXmlStr(JSONObject jObj, StringBuffer buffer) { + Set> se = jObj.entrySet(); + for (Map.Entry en : se) { + if ("com.alibaba.fastjson.JSONObject".equals(en.getValue().getClass().getName())) { + buffer.append("<").append(en.getKey()).append(">"); + JSONObject jo = jObj.getJSONObject(en.getKey()); + jsonToXmlStr(jo, buffer); + buffer.append(""); + } 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(">"); + if (StringUtils.isNotBlank(jarray.getString(i))) { + JSONObject jsonobject = jarray.getJSONObject(i); + jsonToXmlStr(jsonobject, buffer); + buffer.append(""); + } + } + } else if ("java.lang.String".equals(en.getValue().getClass().getName())) { + buffer.append("<").append(en.getKey()).append(">").append(en.getValue()); + buffer.append(""); + } + } + } + + public static String jsonToXmlStr(JSONObject jObj) { + StringBuffer buffer = new StringBuffer(); + buffer.append(""); + try { + jsonToXmlStr(jObj, buffer); + } catch (Exception e) { + LogUtil.error(e.getMessage(), e); + } + return buffer.toString(); + } +} diff --git a/backend/src/main/java/io/metersphere/controller/TestController.java b/backend/src/main/java/io/metersphere/controller/TestController.java index 2e2e761937..b5ef022631 100644 --- a/backend/src/main/java/io/metersphere/controller/TestController.java +++ b/backend/src/main/java/io/metersphere/controller/TestController.java @@ -5,6 +5,7 @@ import io.metersphere.base.domain.User; import io.metersphere.commons.utils.SessionUtils; import io.metersphere.controller.handler.annotation.NoResultHolder; import org.apache.commons.lang3.StringUtils; +import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; @@ -35,6 +36,15 @@ public class TestController { return jsonObject; } + @PostMapping(value = "/wwwform", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) + public Object testWwwForm(String id, User user, String name) { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("id", id); + jsonObject.put("user", user.getName()); + jsonObject.put("name", name); + return jsonObject; + } + @NoResultHolder @GetMapping(value = "/xml") public String getXmlString() { diff --git a/frontend/src/i18n/en-US.js b/frontend/src/i18n/en-US.js index bfd52faaa2..1ffe3fd1d3 100644 --- a/frontend/src/i18n/en-US.js +++ b/frontend/src/i18n/en-US.js @@ -795,7 +795,7 @@ export default { export_tip: "Export Tip", ms_tip: "Support for MeterSphere JSON format", ms_export_tip: "Export jSON-formatted files via MeterSphere website or browser plug-ins", - swagger_tip: "Only Swagger2.x json files are supported", + swagger_tip: "Swagger 2.0 and 3.0 json files are supported", postman_tip: "Only Postman Collection V2.1 json files are supported", postman_export_tip: "Export the test collection by Postman", swagger_export_tip: "Export jSON-formatted files via Swagger website", diff --git a/frontend/src/i18n/zh-CN.js b/frontend/src/i18n/zh-CN.js index cf5240928b..25ff5e99db 100644 --- a/frontend/src/i18n/zh-CN.js +++ b/frontend/src/i18n/zh-CN.js @@ -797,7 +797,7 @@ export default { ms_tip: "支持 Metersphere json 格式", ms_export_tip: "通过 Metersphere 接口测试页面或者浏览器插件导出 json 格式文件", postman_tip: "只支持 Postman Collection v2.1 格式的 json 文件", - swagger_tip: "只支持 Swagger 2.x 版本的 json 文件", + swagger_tip: "支持 Swagger 2.0 与 3.0 版本的 json 文件", post_export_tip: "通过 Postman 导出测试集合", swagger_export_tip: "通过 Swagger 页面导出", suffixFormatErr: "文件格式不符合要求", diff --git a/frontend/src/i18n/zh-TW.js b/frontend/src/i18n/zh-TW.js index a0aee26147..e93f1f20d4 100644 --- a/frontend/src/i18n/zh-TW.js +++ b/frontend/src/i18n/zh-TW.js @@ -796,7 +796,7 @@ export default { ms_tip: "支持 Metersphere json 格式", ms_export_tip: "通過 Metersphere 接口測試頁面或者瀏覽器插件導出 json 格式文件", postman_tip: "只支持 Postman Collection v2.1 格式的 json 文件", - swagger_tip: "只支持 Swagger 2.x 版本的 json 文件", + swagger_tip: "支持 Swagger 2.0 與 3.0版本的 json 文件", post_export_tip: "通過 Postman 導出測試集合", swagger_export_tip: "通過 Swagger 頁面導出", suffixFormatErr: "文件格式不符合要求", From 9279deb3e9343e1382184fadf52b536b786f492c Mon Sep 17 00:00:00 2001 From: zwp201301 Date: Thu, 31 Dec 2020 09:30:29 +0800 Subject: [PATCH 09/23] =?UTF-8?q?Bug=E4=BF=AE=E5=A4=8D=EF=BC=9Aswagger?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E7=9A=84=E5=85=A5=E5=8F=82=E5=BF=85=E5=A1=AB?= =?UTF-8?q?=E4=B8=8E=E9=9D=9E=E5=BF=85=E5=A1=AB=E3=80=82=E7=9B=AE=E5=89=8D?= =?UTF-8?q?=E6=98=AF=E6=B2=A1=E6=9C=89=E5=88=A4=E6=96=ADswagger=E7=9A=84?= =?UTF-8?q?=E5=BF=85=E5=A1=AB=E4=B8=8E=E5=90=A6=EF=BC=8C=E5=85=A8=E9=83=A8?= =?UTF-8?q?=E5=9B=BA=E5=AE=9A=E4=B8=BA=E5=BF=85=E5=A1=AB=E3=80=82=20(#1025?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1、KeyValue中的required属性不能写死,需要在对象构造的时候初始化; 2、HeaderParameter和CookieParameter类型参数,增加required属性; 3、FormParameter、QueryParameter、HeaderParameter和CookieParameter均增加required属性动态传递给KeyValue --- .../io/metersphere/api/dto/scenario/KeyValue.java | 9 +++++++++ .../api/parse/ApiImportAbstractParser.java | 12 ++++++------ .../io/metersphere/api/parse/Swagger2Parser.java | 12 ++++++++---- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/KeyValue.java b/backend/src/main/java/io/metersphere/api/dto/scenario/KeyValue.java index 328517e501..06317a0745 100644 --- a/backend/src/main/java/io/metersphere/api/dto/scenario/KeyValue.java +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/KeyValue.java @@ -40,6 +40,15 @@ public class KeyValue { this.required = true; } + public KeyValue(String name, String value, String description, String contentType, boolean required) { + this.name = name; + this.value = value; + this.description = description; + this.contentType = contentType; + this.enable = true; + this.required = required; + } + public boolean isValid() { return (StringUtils.isNotBlank(name) || StringUtils.isNotBlank(value)) && !StringUtils.equalsIgnoreCase(type, "file"); } 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 c3b33ae0c8..1d0366f428 100644 --- a/backend/src/main/java/io/metersphere/api/parse/ApiImportAbstractParser.java +++ b/backend/src/main/java/io/metersphere/api/parse/ApiImportAbstractParser.java @@ -142,10 +142,10 @@ public abstract class ApiImportAbstractParser implements ApiImportParser { } protected void addCookie(List headers, String key, String value) { - addCookie(headers, key, value, ""); + addCookie(headers, key, value, "", "", true); } - protected void addCookie(List headers, String key, String value, String description) { + protected void addCookie(List headers, String key, String value, String description, String contentType, boolean required) { boolean hasCookie = false; for (KeyValue header : headers) { if (StringUtils.equalsIgnoreCase("Cookie", header.getName())) { @@ -155,15 +155,15 @@ public abstract class ApiImportAbstractParser implements ApiImportParser { } } if (!hasCookie) { - addHeader(headers, "Cookie", key + "=" + value + ";", description); + addHeader(headers, "Cookie", key + "=" + value + ";", description, "", required); } } protected void addHeader(List headers, String key, String value) { - addHeader(headers, key, value, ""); + addHeader(headers, key, value, "", "", true); } - protected void addHeader(List headers, String key, String value, String description) { + protected void addHeader(List headers, String key, String value, String description, String contentType, boolean required) { boolean hasContentType = false; for (KeyValue header : headers) { if (StringUtils.equalsIgnoreCase(header.getName(), key)) { @@ -171,7 +171,7 @@ public abstract class ApiImportAbstractParser implements ApiImportParser { } } if (!hasContentType) { - headers.add(new KeyValue(key, value, description)); + headers.add(new KeyValue(key, value, description, contentType, required)); } } } diff --git a/backend/src/main/java/io/metersphere/api/parse/Swagger2Parser.java b/backend/src/main/java/io/metersphere/api/parse/Swagger2Parser.java index 2a04cd5898..61d15ed5ce 100644 --- a/backend/src/main/java/io/metersphere/api/parse/Swagger2Parser.java +++ b/backend/src/main/java/io/metersphere/api/parse/Swagger2Parser.java @@ -166,12 +166,14 @@ public class Swagger2Parser extends ApiImportAbstractParser { private void parseCookieParameters(Parameter parameter, List headers) { CookieParameter cookieParameter = (CookieParameter) parameter; - addCookie(headers, cookieParameter.getName(), "", getDefaultStringValue(cookieParameter.getDescription())); + addCookie(headers, cookieParameter.getName(), "", getDefaultStringValue(cookieParameter.getDescription()), + "", parameter.getRequired()); } private void parseHeaderParameters(Parameter parameter, List headers) { HeaderParameter headerParameter = (HeaderParameter) parameter; - addHeader(headers, headerParameter.getName(), "", getDefaultStringValue(headerParameter.getDescription())); + addHeader(headers, headerParameter.getName(), "", getDefaultStringValue(headerParameter.getDescription()), + "", parameter.getRequired()); } private HttpResponse parseResponse(Map responses) { @@ -299,7 +301,8 @@ public class Swagger2Parser extends ApiImportAbstractParser { private void parseFormDataParameters(FormParameter parameter, Body body) { List keyValues = Optional.ofNullable(body.getKvs()).orElse(new ArrayList<>()); - KeyValue kv = new KeyValue(parameter.getName(), "", getDefaultStringValue(parameter.getDescription())); + KeyValue kv = new KeyValue(parameter.getName(), "", getDefaultStringValue(parameter.getDescription()), + "", parameter.getRequired()); if (StringUtils.equals(parameter.getType(), "file")) { kv.setType("file"); } @@ -309,6 +312,7 @@ public class Swagger2Parser extends ApiImportAbstractParser { private void parseQueryParameters(Parameter parameter, List arguments) { QueryParameter queryParameter = (QueryParameter) parameter; - arguments.add(new KeyValue(queryParameter.getName(), "", getDefaultStringValue(queryParameter.getDescription()))); + arguments.add(new KeyValue(queryParameter.getName(), "", getDefaultStringValue(queryParameter.getDescription()), + "", queryParameter.getRequired())); } } From f978a3312ce022744698e71185236262ae565633 Mon Sep 17 00:00:00 2001 From: BugKing Date: Thu, 31 Dec 2020 09:42:49 +0800 Subject: [PATCH 10/23] =?UTF-8?q?build:=20pull=20request=20=E4=B8=8D?= =?UTF-8?q?=E5=86=8D=E8=A7=A6=E5=8F=91=20github=20=E6=9E=84=E5=BB=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build-push.yml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/.github/workflows/build-push.yml b/.github/workflows/build-push.yml index f455ffdc38..bad86b0985 100644 --- a/.github/workflows/build-push.yml +++ b/.github/workflows/build-push.yml @@ -4,12 +4,7 @@ on: push: branches: - master - - v1* - pull_request: - branches: - - master - - v1* - + - v1* workflow_dispatch: jobs: From f40726a74a9eafd79b829f391807d3ae474fb999 Mon Sep 17 00:00:00 2001 From: chenjianxing Date: Thu, 31 Dec 2020 09:46:03 +0800 Subject: [PATCH 11/23] =?UTF-8?q?fix:=20=E8=A7=A3=E5=86=B3=E5=86=B2?= =?UTF-8?q?=E7=AA=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/io/metersphere/api/parse/Swagger3Parser.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/main/java/io/metersphere/api/parse/Swagger3Parser.java b/backend/src/main/java/io/metersphere/api/parse/Swagger3Parser.java index 135c6f7a28..86db4d676c 100644 --- a/backend/src/main/java/io/metersphere/api/parse/Swagger3Parser.java +++ b/backend/src/main/java/io/metersphere/api/parse/Swagger3Parser.java @@ -179,12 +179,12 @@ public class Swagger3Parser extends ApiImportAbstractParser { private void parseCookieParameters(Parameter parameter, List headers) { CookieParameter cookieParameter = (CookieParameter) parameter; - addCookie(headers, cookieParameter.getName(), "", getDefaultStringValue(cookieParameter.getDescription())); + addCookie(headers, cookieParameter.getName(), "", getDefaultStringValue(cookieParameter.getDescription()), "", true); } private void parseHeaderParameters(Parameter parameter, List headers) { HeaderParameter headerParameter = (HeaderParameter) parameter; - addHeader(headers, headerParameter.getName(), "", getDefaultStringValue(headerParameter.getDescription())); + addHeader(headers, headerParameter.getName(), "", getDefaultStringValue(headerParameter.getDescription()), "", true); } private HttpResponse parseResponse(ApiResponses responses) { From d3cd2f48435784fc3a26ae71126833521e1edc75 Mon Sep 17 00:00:00 2001 From: fit2-zhao Date: Thu, 31 Dec 2020 10:44:20 +0800 Subject: [PATCH 12/23] =?UTF-8?q?fix(=E6=8E=A5=E5=8F=A3=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E5=8C=96):=20=E4=BF=AE=E5=A4=8D=E6=89=A7=E8=A1=8C=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B=E4=B8=BA=E7=A9=BA=E6=97=B6=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../definition/request/sampler/MsHTTPSamplerProxy.java | 10 ++++++---- .../definition/components/response/RequestMetric.vue | 2 +- .../definition/components/response/ResponseResult.vue | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) 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 2c3b7d0f98..4770e79c04 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 @@ -181,11 +181,13 @@ public class MsHTTPSamplerProxy extends MsTestElement { } // 请求体 if (!StringUtils.equals(this.getMethod(), "GET")) { - List bodyParams = this.body.getBodyParams(sampler, this.getId()); - if (this.body.getType().equals("Form Data")) { - sampler.setDoMultipart(true); + if (this.body != null) { + List bodyParams = this.body.getBodyParams(sampler, this.getId()); + if (StringUtils.isNotEmpty(this.body.getType()) && this.body.getType().equals("Form Data")) { + sampler.setDoMultipart(true); + } + sampler.setArguments(httpArguments(bodyParams)); } - sampler.setArguments(httpArguments(bodyParams)); } final HashTree httpSamplerTree = tree.add(sampler); diff --git a/frontend/src/business/components/api/definition/components/response/RequestMetric.vue b/frontend/src/business/components/api/definition/components/response/RequestMetric.vue index cb504b5703..660da3ecae 100644 --- a/frontend/src/business/components/api/definition/components/response/RequestMetric.vue +++ b/frontend/src/business/components/api/definition/components/response/RequestMetric.vue @@ -27,7 +27,7 @@ computed: { error() { - return this.response.responseCode >= 400; + return this.response && this.response.responseCode && this.response.responseCode >= 400; } } } diff --git a/frontend/src/business/components/api/definition/components/response/ResponseResult.vue b/frontend/src/business/components/api/definition/components/response/ResponseResult.vue index 547feb25ac..78c9c31698 100644 --- a/frontend/src/business/components/api/definition/components/response/ResponseResult.vue +++ b/frontend/src/business/components/api/definition/components/response/ResponseResult.vue @@ -41,7 +41,7 @@
Body : -
{{response.body}}
+
{{response.body ? response.body:""}}
From ab495f21f4f9e7fbc67b0f08c98524735bb3000b Mon Sep 17 00:00:00 2001 From: chenjianxing Date: Thu, 31 Dec 2020 10:48:01 +0800 Subject: [PATCH 13/23] =?UTF-8?q?fix:=20swagger=203=20=E5=A0=86=E6=A0=88?= =?UTF-8?q?=E6=BA=A2=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/io/metersphere/api/parse/Swagger3Parser.java | 5 ++++- .../api/definition/components/module/ApiModule.vue | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/backend/src/main/java/io/metersphere/api/parse/Swagger3Parser.java b/backend/src/main/java/io/metersphere/api/parse/Swagger3Parser.java index 86db4d676c..994eb55346 100644 --- a/backend/src/main/java/io/metersphere/api/parse/Swagger3Parser.java +++ b/backend/src/main/java/io/metersphere/api/parse/Swagger3Parser.java @@ -51,7 +51,7 @@ public class Swagger3Parser extends ApiImportAbstractParser { public ApiDefinitionImport parse(String sourceStr, ApiTestImportRequest request) { SwaggerParseResult result; if (StringUtils.isNotBlank(request.getSwaggerUrl())) { - result = new OpenAPIParser().readLocation("https://petstore3.swagger.io/api/v3/openapi.json", null, null); + result = new OpenAPIParser().readLocation(request.getSwaggerUrl(), null, null); } else { result = new OpenAPIParser().readContents(sourceStr, null, null); } @@ -317,6 +317,9 @@ public class Swagger3Parser extends ApiImportAbstractParser { private Object parseSchema(Schema schema, Set refSet, Map binaryKeyMap) { if (StringUtils.isNotBlank(schema.get$ref())) { + if (refSet.contains(schema.get$ref())) { + return new JSONObject(); + } refSet.add(schema.get$ref()); Object propertiesResult = parseSchemaProperties(getModelByRef(schema.get$ref()), refSet, binaryKeyMap); return propertiesResult == null ? getDefaultValueByPropertyType(schema) : propertiesResult; 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 73e221ba19..fd7e2ea3ab 100644 --- a/frontend/src/business/components/api/definition/components/module/ApiModule.vue +++ b/frontend/src/business/components/api/definition/components/module/ApiModule.vue @@ -194,7 +194,7 @@ }, refresh() { this.list(); - this.$emit("refreshTable"); + this.$emit("refresh"); }, } } From 4e66f2e855ce16b3f133cf5782702204a82651c1 Mon Sep 17 00:00:00 2001 From: BugKing Date: Thu, 31 Dec 2020 11:00:44 +0800 Subject: [PATCH 14/23] =?UTF-8?q?refactor:=20=E5=A2=9E=E5=8A=A0=20v1.6.1?= =?UTF-8?q?=20issue=20=E6=A8=A1=E6=9D=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/ISSUE_TEMPLATE/v1.6.1-issue.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/v1.6.1-issue.md diff --git a/.github/ISSUE_TEMPLATE/v1.6.1-issue.md b/.github/ISSUE_TEMPLATE/v1.6.1-issue.md new file mode 100644 index 0000000000..394331d5ef --- /dev/null +++ b/.github/ISSUE_TEMPLATE/v1.6.1-issue.md @@ -0,0 +1,13 @@ +--- +name: v1.6.1 问题 +about: 提交关于 v1.6.1 的缺陷与建议 +title: "[v1.6.1]" +labels: v1.6.1 +assignees: 'luty2018' + +--- +**问题/需求描述** +简要描述您碰到的问题或您面临的需求 + +**截图** +如果有截图,请附上截图. From 3abb555f43383810c30d5ff2492316559496c583 Mon Sep 17 00:00:00 2001 From: LuoTing <36292514+luty2018@users.noreply.github.com> Date: Thu, 31 Dec 2020 11:03:51 +0800 Subject: [PATCH 15/23] Update v1.6.1-issue.md --- .github/ISSUE_TEMPLATE/v1.6.1-issue.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/v1.6.1-issue.md b/.github/ISSUE_TEMPLATE/v1.6.1-issue.md index 394331d5ef..86cee0b8dc 100644 --- a/.github/ISSUE_TEMPLATE/v1.6.1-issue.md +++ b/.github/ISSUE_TEMPLATE/v1.6.1-issue.md @@ -1,6 +1,6 @@ --- name: v1.6.1 问题 -about: 提交关于 v1.6.1 的缺陷与建议 +about: 提交关于 v1.6.1 的缺陷与建议,赢取lv同款鼠标垫!马克杯!文化衫! title: "[v1.6.1]" labels: v1.6.1 assignees: 'luty2018' From 6fb72f294e76abe7cc26d52c5a99d445770e0b24 Mon Sep 17 00:00:00 2001 From: LuoTing <36292514+luty2018@users.noreply.github.com> Date: Thu, 31 Dec 2020 11:04:49 +0800 Subject: [PATCH 16/23] Update v1.6.1-issue.md --- .github/ISSUE_TEMPLATE/v1.6.1-issue.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/v1.6.1-issue.md b/.github/ISSUE_TEMPLATE/v1.6.1-issue.md index 86cee0b8dc..2d7ee1033e 100644 --- a/.github/ISSUE_TEMPLATE/v1.6.1-issue.md +++ b/.github/ISSUE_TEMPLATE/v1.6.1-issue.md @@ -1,6 +1,6 @@ --- name: v1.6.1 问题 -about: 提交关于 v1.6.1 的缺陷与建议,赢取lv同款鼠标垫!马克杯!文化衫! +about: 提交关于 v1.6.1 的缺陷与建议,赢取Lv同款鼠标垫!马克杯!文化衫! title: "[v1.6.1]" labels: v1.6.1 assignees: 'luty2018' From 35a49a9ddba2fc0c94902eff3e999b4abd6279fb Mon Sep 17 00:00:00 2001 From: BugKing Date: Thu, 31 Dec 2020 11:07:28 +0800 Subject: [PATCH 17/23] refactor: Update issue templates --- .github/ISSUE_TEMPLATE/----.md | 16 ++++++---------- .github/ISSUE_TEMPLATE/bug---.md | 2 +- .github/ISSUE_TEMPLATE/question.md | 11 ----------- .github/ISSUE_TEMPLATE/v1.6.1-issue.md | 5 +++-- 4 files changed, 10 insertions(+), 24 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/question.md diff --git a/.github/ISSUE_TEMPLATE/----.md b/.github/ISSUE_TEMPLATE/----.md index 4c3f7c6133..d69e3f7f52 100644 --- a/.github/ISSUE_TEMPLATE/----.md +++ b/.github/ISSUE_TEMPLATE/----.md @@ -1,14 +1,10 @@ --- -name: 需求建议 -about: 提出针对本项目的想法和建议 -title: "[FEATURE]" -labels: enhancement -assignees: '' +name: 问题咨询 +about: 提出针对本项目安装部署、使用及其他方面的相关问题 +title: "[QUESTION]" +labels: question +assignees: wangzhen-fit2cloud --- -**请描述您的需求或者改进建议.** - - - -**请描述你建议的实现方案** +**请描述您的问题.** diff --git a/.github/ISSUE_TEMPLATE/bug---.md b/.github/ISSUE_TEMPLATE/bug---.md index 25dd8e93b8..85c2e46fcc 100644 --- a/.github/ISSUE_TEMPLATE/bug---.md +++ b/.github/ISSUE_TEMPLATE/bug---.md @@ -3,7 +3,7 @@ name: Bug 提交 about: 提交产品缺陷帮助我们更好的改进 title: "[BUG]" labels: bug -assignees: '' +assignees: AgAngle --- diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md deleted file mode 100644 index a081dc73f4..0000000000 --- a/.github/ISSUE_TEMPLATE/question.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -name: 问题咨询 -about: 提出针对本项目安装部署、使用及其他方面的相关问题 -title: "[QUESTION]" -labels: question -assignees: 'wangzhen-fit2cloud' - ---- - -**请描述您的问题.** - diff --git a/.github/ISSUE_TEMPLATE/v1.6.1-issue.md b/.github/ISSUE_TEMPLATE/v1.6.1-issue.md index 2d7ee1033e..900054e72e 100644 --- a/.github/ISSUE_TEMPLATE/v1.6.1-issue.md +++ b/.github/ISSUE_TEMPLATE/v1.6.1-issue.md @@ -1,11 +1,12 @@ --- name: v1.6.1 问题 -about: 提交关于 v1.6.1 的缺陷与建议,赢取Lv同款鼠标垫!马克杯!文化衫! +about: 提交关于 v1.6.1 的缺陷与建议 title: "[v1.6.1]" labels: v1.6.1 -assignees: 'luty2018' +assignees: luty2018 --- + **问题/需求描述** 简要描述您碰到的问题或您面临的需求 From fd8cf3b768a997d9318fa427e6ac31a60b68966d Mon Sep 17 00:00:00 2001 From: BugKing Date: Thu, 31 Dec 2020 11:15:28 +0800 Subject: [PATCH 18/23] =?UTF-8?q?refactor:=20=E4=BF=AE=E6=94=B9=20issue=20?= =?UTF-8?q?=E6=A8=A1=E6=9D=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/ISSUE_TEMPLATE/{bug---.md => bug.md} | 2 +- .github/ISSUE_TEMPLATE/feature.md | 14 ++++++++++++++ .github/ISSUE_TEMPLATE/{----.md => question.md} | 0 .github/ISSUE_TEMPLATE/v1.6.1-issue.md | 2 +- 4 files changed, 16 insertions(+), 2 deletions(-) rename .github/ISSUE_TEMPLATE/{bug---.md => bug.md} (95%) create mode 100644 .github/ISSUE_TEMPLATE/feature.md rename .github/ISSUE_TEMPLATE/{----.md => question.md} (100%) diff --git a/.github/ISSUE_TEMPLATE/bug---.md b/.github/ISSUE_TEMPLATE/bug.md similarity index 95% rename from .github/ISSUE_TEMPLATE/bug---.md rename to .github/ISSUE_TEMPLATE/bug.md index 85c2e46fcc..651ca6f6fb 100644 --- a/.github/ISSUE_TEMPLATE/bug---.md +++ b/.github/ISSUE_TEMPLATE/bug.md @@ -1,5 +1,5 @@ --- -name: Bug 提交 +name: BUG 提交 about: 提交产品缺陷帮助我们更好的改进 title: "[BUG]" labels: bug diff --git a/.github/ISSUE_TEMPLATE/feature.md b/.github/ISSUE_TEMPLATE/feature.md new file mode 100644 index 0000000000..45b67d7fe3 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature.md @@ -0,0 +1,14 @@ +--- +name: 需求建议 +about: 提出针对本项目的想法和建议 +title: "[FEATURE]" +labels: enhancement +assignees: '' + +--- + +**请描述您的需求或者改进建议.** + + + +**请描述你建议的实现方案** \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/----.md b/.github/ISSUE_TEMPLATE/question.md similarity index 100% rename from .github/ISSUE_TEMPLATE/----.md rename to .github/ISSUE_TEMPLATE/question.md diff --git a/.github/ISSUE_TEMPLATE/v1.6.1-issue.md b/.github/ISSUE_TEMPLATE/v1.6.1-issue.md index 900054e72e..4d25616e97 100644 --- a/.github/ISSUE_TEMPLATE/v1.6.1-issue.md +++ b/.github/ISSUE_TEMPLATE/v1.6.1-issue.md @@ -1,6 +1,6 @@ --- name: v1.6.1 问题 -about: 提交关于 v1.6.1 的缺陷与建议 +about: 提交关于 v1.6.1 的缺陷与建议,赢取Lv同款鼠标垫!马克杯!文化衫! title: "[v1.6.1]" labels: v1.6.1 assignees: luty2018 From 69414cae58a99d4d5ac5416be56d8888c7589032 Mon Sep 17 00:00:00 2001 From: chenjianxing Date: Thu, 31 Dec 2020 11:22:51 +0800 Subject: [PATCH 19/23] =?UTF-8?q?fix=EF=BC=9A=E5=AF=BC=E5=87=BAjson?= =?UTF-8?q?=E8=B7=AF=E5=BE=84=E4=B8=BA=E7=A9=BA=E6=98=AF=EF=BC=8C=E6=A8=A1?= =?UTF-8?q?=E5=9D=97=E6=98=BE=E7=A4=BAid?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/main/java/io/metersphere/api/parse/MsParser.java | 4 +++- .../components/api/definition/components/module/ApiModule.vue | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/backend/src/main/java/io/metersphere/api/parse/MsParser.java b/backend/src/main/java/io/metersphere/api/parse/MsParser.java index 5e768adbba..512965d760 100644 --- a/backend/src/main/java/io/metersphere/api/parse/MsParser.java +++ b/backend/src/main/java/io/metersphere/api/parse/MsParser.java @@ -41,7 +41,9 @@ public class MsParser extends ApiImportAbstractParser { List data = apiDefinitionImport.getData(); data.forEach(apiDefinition -> { String id = UUID.randomUUID().toString(); -// apiDefinition.setModuleId(null); + if (StringUtils.isBlank(apiDefinition.getModulePath())) { + apiDefinition.setModuleId(null); + } parseModule(apiDefinition, importRequest.isSaved()); apiDefinition.setId(id); apiDefinition.setProjectId(this.projectId); 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 fd7e2ea3ab..73e221ba19 100644 --- a/frontend/src/business/components/api/definition/components/module/ApiModule.vue +++ b/frontend/src/business/components/api/definition/components/module/ApiModule.vue @@ -194,7 +194,7 @@ }, refresh() { this.list(); - this.$emit("refresh"); + this.$emit("refreshTable"); }, } } From 53943f41768da6f0a28fa0995ebd5107991439bc Mon Sep 17 00:00:00 2001 From: chenjianxing Date: Thu, 31 Dec 2020 13:58:42 +0800 Subject: [PATCH 20/23] =?UTF-8?q?refactor:=20=E4=BB=A3=E7=A0=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/dto/scenario/KeyValue.java | 11 ++-- .../api/parse/ApiImportAbstractParser.java | 4 +- .../metersphere/api/parse/Swagger2Parser.java | 9 ++-- .../metersphere/api/parse/Swagger3Parser.java | 52 +++++++++++-------- backend/src/main/java/io/metersphere/xpack | 2 +- 5 files changed, 40 insertions(+), 38 deletions(-) diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/KeyValue.java b/backend/src/main/java/io/metersphere/api/dto/scenario/KeyValue.java index 06317a0745..2d5191ac4e 100644 --- a/backend/src/main/java/io/metersphere/api/dto/scenario/KeyValue.java +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/KeyValue.java @@ -32,12 +32,7 @@ public class KeyValue { } public KeyValue(String name, String value, String description, String contentType) { - this.name = name; - this.value = value; - this.description = description; - this.contentType = contentType; - this.enable = true; - this.required = true; + this(name, value, description, contentType, true); } public KeyValue(String name, String value, String description, String contentType, boolean required) { @@ -49,6 +44,10 @@ public class KeyValue { this.required = required; } + public KeyValue(String name, String value, String description, boolean required) { + this(name, value, description, "", required); + } + public boolean isValid() { return (StringUtils.isNotBlank(name) || StringUtils.isNotBlank(value)) && !StringUtils.equalsIgnoreCase(type, "file"); } 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 1d0366f428..0fde847e07 100644 --- a/backend/src/main/java/io/metersphere/api/parse/ApiImportAbstractParser.java +++ b/backend/src/main/java/io/metersphere/api/parse/ApiImportAbstractParser.java @@ -142,10 +142,10 @@ public abstract class ApiImportAbstractParser implements ApiImportParser { } protected void addCookie(List headers, String key, String value) { - addCookie(headers, key, value, "", "", true); + addCookie(headers, key, value, "", true); } - protected void addCookie(List headers, String key, String value, String description, String contentType, boolean required) { + protected void addCookie(List headers, String key, String value, String description, boolean required) { boolean hasCookie = false; for (KeyValue header : headers) { if (StringUtils.equalsIgnoreCase("Cookie", header.getName())) { diff --git a/backend/src/main/java/io/metersphere/api/parse/Swagger2Parser.java b/backend/src/main/java/io/metersphere/api/parse/Swagger2Parser.java index 61d15ed5ce..d1a312941b 100644 --- a/backend/src/main/java/io/metersphere/api/parse/Swagger2Parser.java +++ b/backend/src/main/java/io/metersphere/api/parse/Swagger2Parser.java @@ -166,8 +166,7 @@ public class Swagger2Parser extends ApiImportAbstractParser { private void parseCookieParameters(Parameter parameter, List headers) { CookieParameter cookieParameter = (CookieParameter) parameter; - addCookie(headers, cookieParameter.getName(), "", getDefaultStringValue(cookieParameter.getDescription()), - "", parameter.getRequired()); + addCookie(headers, cookieParameter.getName(), "", getDefaultStringValue(cookieParameter.getDescription()), parameter.getRequired()); } private void parseHeaderParameters(Parameter parameter, List headers) { @@ -301,8 +300,7 @@ public class Swagger2Parser extends ApiImportAbstractParser { private void parseFormDataParameters(FormParameter parameter, Body body) { List keyValues = Optional.ofNullable(body.getKvs()).orElse(new ArrayList<>()); - KeyValue kv = new KeyValue(parameter.getName(), "", getDefaultStringValue(parameter.getDescription()), - "", parameter.getRequired()); + KeyValue kv = new KeyValue(parameter.getName(), "", getDefaultStringValue(parameter.getDescription()), parameter.getRequired()); if (StringUtils.equals(parameter.getType(), "file")) { kv.setType("file"); } @@ -312,7 +310,6 @@ public class Swagger2Parser extends ApiImportAbstractParser { private void parseQueryParameters(Parameter parameter, List arguments) { QueryParameter queryParameter = (QueryParameter) parameter; - arguments.add(new KeyValue(queryParameter.getName(), "", getDefaultStringValue(queryParameter.getDescription()), - "", queryParameter.getRequired())); + arguments.add(new KeyValue(queryParameter.getName(), "", getDefaultStringValue(queryParameter.getDescription()), queryParameter.getRequired())); } } diff --git a/backend/src/main/java/io/metersphere/api/parse/Swagger3Parser.java b/backend/src/main/java/io/metersphere/api/parse/Swagger3Parser.java index 994eb55346..3e8ab93dd9 100644 --- a/backend/src/main/java/io/metersphere/api/parse/Swagger3Parser.java +++ b/backend/src/main/java/io/metersphere/api/parse/Swagger3Parser.java @@ -17,7 +17,6 @@ import io.metersphere.commons.exception.MSException; import io.metersphere.commons.utils.CommonBeanFactory; import io.metersphere.commons.utils.LogUtil; import io.metersphere.commons.utils.XMLUtils; -import io.swagger.models.parameters.FormParameter; import io.swagger.parser.OpenAPIParser; import io.swagger.v3.oas.models.*; import io.swagger.v3.oas.models.headers.Header; @@ -179,12 +178,12 @@ public class Swagger3Parser extends ApiImportAbstractParser { private void parseCookieParameters(Parameter parameter, List headers) { CookieParameter cookieParameter = (CookieParameter) parameter; - addCookie(headers, cookieParameter.getName(), "", getDefaultStringValue(cookieParameter.getDescription()), "", true); + addCookie(headers, cookieParameter.getName(), "", getDefaultStringValue(cookieParameter.getDescription()), parameter.getRequired()); } private void parseHeaderParameters(Parameter parameter, List headers) { HeaderParameter headerParameter = (HeaderParameter) parameter; - addHeader(headers, headerParameter.getName(), "", getDefaultStringValue(headerParameter.getDescription()), "", true); + addHeader(headers, headerParameter.getName(), "", getDefaultStringValue(headerParameter.getDescription()), "", parameter.getRequired()); } private HttpResponse parseResponse(ApiResponses responses) { @@ -250,9 +249,9 @@ public class Swagger3Parser extends ApiImportAbstractParser { } Set refSet = new HashSet<>(); - Map binaryKeyMap = new HashMap(); + Map infoMap = new HashMap(); Schema schema = mediaType.getSchema(); - Object bodyData = parseSchema(schema, refSet, binaryKeyMap); + Object bodyData = parseSchema(schema, refSet, infoMap); if (bodyData == null) { return; @@ -261,7 +260,7 @@ public class Swagger3Parser extends ApiImportAbstractParser { body.setType(getBodyType(contentType)); if (StringUtils.equals(contentType, org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED_VALUE)) { - parseKvBody(schema, body, bodyData, binaryKeyMap); + parseKvBody(schema, body, bodyData, infoMap); } else if (StringUtils.equals(contentType, org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE)) { body.setRaw(bodyData.toString()); } else if (StringUtils.equals(contentType, org.springframework.http.MediaType.APPLICATION_JSON_VALUE)) { @@ -269,27 +268,34 @@ public class Swagger3Parser extends ApiImportAbstractParser { } else if (StringUtils.equals(contentType, org.springframework.http.MediaType.APPLICATION_XML_VALUE)) { body.setRaw(parseXmlBody(schema, bodyData)); } else if (StringUtils.equals(contentType, org.springframework.http.MediaType.APPLICATION_OCTET_STREAM_VALUE)) { - parseKvBody(schema, body, bodyData, binaryKeyMap); + parseKvBody(schema, body, bodyData, infoMap); } else { body.setRaw(bodyData.toString()); } } - private void parseKvBody(Schema schema, Body body, Object data, Map binaryKeyMap) { + private void parseKvBody(Schema schema, Body body, Object data, Map infoMap) { if (data instanceof JSONObject) { ((JSONObject) data).forEach((k, v) -> { KeyValue kv = new KeyValue(k, v.toString()); - if (binaryKeyMap.keySet().contains(k)) { - kv.setDescription(binaryKeyMap.get(k)); - kv.setType("file"); + Schema schemaInfo = infoMap.get(k); + if (schemaInfo != null) { + kv.setDescription(schemaInfo.getDescription()); +// kv.setRequired(schemaInfo.getRequired()); + if (schemaInfo instanceof BinarySchema) { + kv.setType("file"); + } } body.getKvs().add(kv); }); } else { KeyValue kv = new KeyValue(schema.getName(), data.toString(), schema.getDescription()); - if (binaryKeyMap.keySet().contains(schema.getName())) { - kv.setDescription(binaryKeyMap.get(schema.getDescription())); - kv.setType("file"); + Schema schemaInfo = infoMap.get(schema.getName()); + if (schemaInfo != null) { + kv.setDescription(schemaInfo.getDescription()); + if (schemaInfo instanceof BinarySchema) { + kv.setType("file"); + } } body.getKvs().add(kv); } @@ -315,37 +321,37 @@ public class Swagger3Parser extends ApiImportAbstractParser { return this.components.getSchemas().get(ref); } - private Object parseSchema(Schema schema, Set refSet, Map binaryKeyMap) { + private Object parseSchema(Schema schema, Set refSet, Map infoMap) { + infoMap.put(schema.getName(), schema); if (StringUtils.isNotBlank(schema.get$ref())) { if (refSet.contains(schema.get$ref())) { return new JSONObject(); } refSet.add(schema.get$ref()); - Object propertiesResult = parseSchemaProperties(getModelByRef(schema.get$ref()), refSet, binaryKeyMap); + Object propertiesResult = parseSchemaProperties(getModelByRef(schema.get$ref()), refSet, infoMap); return propertiesResult == null ? getDefaultValueByPropertyType(schema) : propertiesResult; } else if (schema instanceof ArraySchema) { JSONArray jsonArray = new JSONArray(); Schema items = ((ArraySchema) schema).getItems(); - parseSchema(items, refSet, binaryKeyMap); - jsonArray.add(parseSchema(items, refSet, binaryKeyMap)); + parseSchema(items, refSet, infoMap); + jsonArray.add(parseSchema(items, refSet, infoMap)); return jsonArray; } else if (schema instanceof BinarySchema) { - binaryKeyMap.put(schema.getName(), schema.getDescription()); return getDefaultValueByPropertyType(schema); } else { - Object propertiesResult = parseSchemaProperties(schema, refSet, binaryKeyMap); + Object propertiesResult = parseSchemaProperties(schema, refSet, infoMap); return propertiesResult == null ? getDefaultValueByPropertyType(schema) : propertiesResult; } } - private Object parseSchemaProperties(Schema schema, Set refSet, Map binaryKeyMap) { + private Object parseSchemaProperties(Schema schema, Set refSet, Map infoMap) { Map properties = schema.getProperties(); if (MapUtils.isEmpty(properties)) { return null; } JSONObject jsonObject = new JSONObject(); properties.forEach((key, value) -> { - jsonObject.put(key, parseSchema(value, refSet, binaryKeyMap)); + jsonObject.put(key, parseSchema(value, refSet, infoMap)); }); return jsonObject; } @@ -363,6 +369,6 @@ public class Swagger3Parser extends ApiImportAbstractParser { private void parseQueryParameters(Parameter parameter, List arguments) { QueryParameter queryParameter = (QueryParameter) parameter; - arguments.add(new KeyValue(queryParameter.getName(), "", getDefaultStringValue(queryParameter.getDescription()))); + arguments.add(new KeyValue(queryParameter.getName(), "", getDefaultStringValue(queryParameter.getDescription()), parameter.getRequired())); } } diff --git a/backend/src/main/java/io/metersphere/xpack b/backend/src/main/java/io/metersphere/xpack index 068127ce59..f27d1609d7 160000 --- a/backend/src/main/java/io/metersphere/xpack +++ b/backend/src/main/java/io/metersphere/xpack @@ -1 +1 @@ -Subproject commit 068127ce59ea8b016434ed52a9de4a7a4b13bdb4 +Subproject commit f27d1609d77f7d6c988d37d709466e844d350e17 From d4fd50b274557f123b2de03e295cb084961a37ee Mon Sep 17 00:00:00 2001 From: chenjianxing Date: Thu, 31 Dec 2020 14:06:20 +0800 Subject: [PATCH 21/23] =?UTF-8?q?refactor:=20=E6=B5=8B=E8=AF=95=E7=94=A8?= =?UTF-8?q?=E4=BE=8B=E5=90=8D=E7=A7=B0=E9=95=BF=E5=BA=A6=E5=A2=9E=E5=A4=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/db/migration/V59__test_plan_add_project_id.sql | 1 + .../business/components/track/case/components/TestCaseEdit.vue | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/src/main/resources/db/migration/V59__test_plan_add_project_id.sql b/backend/src/main/resources/db/migration/V59__test_plan_add_project_id.sql index 8f3cea94a6..9312102fb3 100644 --- a/backend/src/main/resources/db/migration/V59__test_plan_add_project_id.sql +++ b/backend/src/main/resources/db/migration/V59__test_plan_add_project_id.sql @@ -1,4 +1,5 @@ alter table test_plan add project_id varchar(50) null comment '测试计划所属项目'; +ALTER TABLE api_test_case MODIFY COLUMN name varchar(255) NOT NULL COMMENT 'Test name'; DROP PROCEDURE IF EXISTS test_cursor; DELIMITER // diff --git a/frontend/src/business/components/track/case/components/TestCaseEdit.vue b/frontend/src/business/components/track/case/components/TestCaseEdit.vue index 5125544200..8fd3e6ca53 100644 --- a/frontend/src/business/components/track/case/components/TestCaseEdit.vue +++ b/frontend/src/business/components/track/case/components/TestCaseEdit.vue @@ -307,7 +307,7 @@ export default { rules: { name: [ {required: true, message: this.$t('test_track.case.input_name'), trigger: 'blur'}, - {max: 50, message: this.$t('test_track.length_less_than') + '50', trigger: 'blur'} + {max: 255, message: this.$t('test_track.length_less_than') + '255', trigger: 'blur'} ], module: [{required: true, message: this.$t('test_track.case.input_module'), trigger: 'change'}], maintainer: [{required: true, message: this.$t('test_track.case.input_maintainer'), trigger: 'change'}], From 2d344f28e322d3c8468dc24543b426e5931392b9 Mon Sep 17 00:00:00 2001 From: LuoTing <36292514+luty2018@users.noreply.github.com> Date: Thu, 31 Dec 2020 14:27:53 +0800 Subject: [PATCH 22/23] Update v1.6.1-issue.md --- .github/ISSUE_TEMPLATE/v1.6.1-issue.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/v1.6.1-issue.md b/.github/ISSUE_TEMPLATE/v1.6.1-issue.md index 4d25616e97..8dbbdf6871 100644 --- a/.github/ISSUE_TEMPLATE/v1.6.1-issue.md +++ b/.github/ISSUE_TEMPLATE/v1.6.1-issue.md @@ -1,5 +1,5 @@ --- -name: v1.6.1 问题 +name: v1.6反馈 about: 提交关于 v1.6.1 的缺陷与建议,赢取Lv同款鼠标垫!马克杯!文化衫! title: "[v1.6.1]" labels: v1.6.1 From 83945506994d3a9dc82e99b6d0a7bcf4fc563138 Mon Sep 17 00:00:00 2001 From: LuoTing <36292514+luty2018@users.noreply.github.com> Date: Thu, 31 Dec 2020 14:28:40 +0800 Subject: [PATCH 23/23] Update v1.6.1-issue.md --- .github/ISSUE_TEMPLATE/v1.6.1-issue.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/v1.6.1-issue.md b/.github/ISSUE_TEMPLATE/v1.6.1-issue.md index 8dbbdf6871..f1c3ff67b8 100644 --- a/.github/ISSUE_TEMPLATE/v1.6.1-issue.md +++ b/.github/ISSUE_TEMPLATE/v1.6.1-issue.md @@ -1,8 +1,8 @@ --- name: v1.6反馈 -about: 提交关于 v1.6.1 的缺陷与建议,赢取Lv同款鼠标垫!马克杯!文化衫! -title: "[v1.6.1]" -labels: v1.6.1 +about: 提交关于 v1.6的缺陷与建议,赢取Lv同款鼠标垫!马克杯!文化衫! +title: "[v1.6]" +labels: v1.6 assignees: luty2018 ---