From 00cb5c73436fd3ff125c4c11040e7cd5096ebe65 Mon Sep 17 00:00:00 2001 From: guoyuqi Date: Mon, 5 Dec 2022 19:05:40 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E6=8E=A5=E5=8F=A3=E6=B5=8B=E8=AF=95):=20?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E5=AF=BC=E5=85=A5=E4=BF=AE=E6=94=B9=E5=8A=A0?= =?UTF-8?q?=E7=89=88=E6=9C=AC=E7=BD=AE=E6=96=B0=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --story=1010629 --user=郭雨琦 https://www.tapd.cn/55049933/prong/stories/view/1155049933001010629 --- .../ApiDefinitionImportParamDTO.java | 9 + .../dto/definition/UpdateApiModuleDTO.java | 7 +- .../parse/scenario/ApiScenarioImportUtil.java | 8 +- .../definition/ApiDefinitionController.java | 1 + .../definition/ApiDefinitionImportUtil.java | 424 ++++++ .../ApiDefinitionImportUtilService.java | 1243 +++++++++++++++++ .../definition/ApiDefinitionService.java | 955 +------------ .../service/definition/ApiModuleService.java | 723 +--------- .../automation/scenario/EditApiScenario.vue | 44 +- .../complete/EditCompleteDubboApi.vue | 39 +- .../complete/EditCompleteHTTPApi.vue | 54 +- .../complete/EditCompleteSQLApi.vue | 39 +- .../complete/EditCompleteTCPApi.vue | 39 +- .../sdk-parent/frontend/src/api/version.js | 10 +- .../src/components/MsBorderPieChart.vue | 18 +- .../components/version/MxVersionHistory.vue | 17 +- .../frontend/src/i18n/lang/en-US.js | 5 +- .../frontend/src/i18n/lang/zh-CN.js | 5 +- .../frontend/src/i18n/lang/zh-TW.js | 5 +- .../request/ProjectVersionRequest.java | 4 + .../service/ProjectVersionService.java | 2 + .../src/business/test/EditPerformanceTest.vue | 32 +- .../business/case/components/TestCaseEdit.vue | 50 +- workstation/frontend/src/api/issue.js | 38 +- .../src/business/component/IssueTableList.vue | 12 +- .../dashboard/components/MyCaseCard.vue | 16 +- 26 files changed, 2099 insertions(+), 1700 deletions(-) create mode 100644 api-test/backend/src/main/java/io/metersphere/service/definition/ApiDefinitionImportUtil.java create mode 100644 api-test/backend/src/main/java/io/metersphere/service/definition/ApiDefinitionImportUtilService.java diff --git a/api-test/backend/src/main/java/io/metersphere/api/dto/definition/ApiDefinitionImportParamDTO.java b/api-test/backend/src/main/java/io/metersphere/api/dto/definition/ApiDefinitionImportParamDTO.java index c64d4315b7..5e67f96cb6 100644 --- a/api-test/backend/src/main/java/io/metersphere/api/dto/definition/ApiDefinitionImportParamDTO.java +++ b/api-test/backend/src/main/java/io/metersphere/api/dto/definition/ApiDefinitionImportParamDTO.java @@ -18,6 +18,13 @@ public class ApiDefinitionImportParamDTO { private List updateList; private List caseList; + private List repeatList; + + + + + + public ApiDefinitionImportParamDTO() { } @@ -28,4 +35,6 @@ public class ApiDefinitionImportParamDTO { this.updateList = updateList; this.caseList = caseList; } + + } diff --git a/api-test/backend/src/main/java/io/metersphere/api/dto/definition/UpdateApiModuleDTO.java b/api-test/backend/src/main/java/io/metersphere/api/dto/definition/UpdateApiModuleDTO.java index 117567e613..ed35085042 100644 --- a/api-test/backend/src/main/java/io/metersphere/api/dto/definition/UpdateApiModuleDTO.java +++ b/api-test/backend/src/main/java/io/metersphere/api/dto/definition/UpdateApiModuleDTO.java @@ -7,12 +7,15 @@ import lombok.Getter; import lombok.Setter; import java.util.List; +import java.util.Map; @Getter @Setter public class UpdateApiModuleDTO { - private List moduleList; - private List needUpdateList; + private Boolean fullCoverage; + private ApiModuleDTO chooseModule; + private Map idPathMap; + private Map moduleMap; private List definitionWithBLOBs; private List caseWithBLOBs; diff --git a/api-test/backend/src/main/java/io/metersphere/api/parse/scenario/ApiScenarioImportUtil.java b/api-test/backend/src/main/java/io/metersphere/api/parse/scenario/ApiScenarioImportUtil.java index f658904a03..bb54920dad 100644 --- a/api-test/backend/src/main/java/io/metersphere/api/parse/scenario/ApiScenarioImportUtil.java +++ b/api-test/backend/src/main/java/io/metersphere/api/parse/scenario/ApiScenarioImportUtil.java @@ -19,6 +19,7 @@ import io.metersphere.commons.utils.CommonBeanFactory; import io.metersphere.commons.utils.JSONUtil; import io.metersphere.commons.utils.SessionUtils; import io.metersphere.service.BaseCheckPermissionService; +import io.metersphere.service.definition.ApiDefinitionImportUtilService; import io.metersphere.service.definition.ApiDefinitionService; import io.metersphere.service.definition.ApiTestCaseService; import io.metersphere.service.scenario.ApiScenarioModuleService; @@ -231,6 +232,7 @@ public class ApiScenarioImportUtil { } else { test.setPath(object.optString("path")); } + ApiDefinitionImportUtilService apiDefinitionImportUtilService = CommonBeanFactory.getBean(ApiDefinitionImportUtilService.class); test.setCreateUser(SessionUtils.getUserId()); test.setProjectId(projectId); test.setCreateTime(System.currentTimeMillis()); @@ -259,7 +261,7 @@ public class ApiScenarioImportUtil { test.setResponse(httpResponse.toString()); test.setUserId(SessionUtils.getUserId()); test.setLatest(true); - test.setOrder(apiDefinitionService.getImportNextOrder(projectId)); + test.setOrder(apiDefinitionImportUtilService.getImportNextOrder(projectId)); if (test.getName().length() > 255) { test.setName(test.getName().substring(0, 255)); } @@ -294,6 +296,7 @@ public class ApiScenarioImportUtil { } else { apiTestCase.setNum(apiDefinition.getNum() * 1000 + caseNameSet.size() + 1); } + ApiDefinitionImportUtilService apiDefinitionImportUtilService = CommonBeanFactory.getBean(ApiDefinitionImportUtilService.class); object.put("id", apiTestCase.getId()); object.put("resourceId", apiTestCase.getId()); object.put("projectId", projectId); @@ -303,7 +306,7 @@ public class ApiScenarioImportUtil { objectNew.remove("refType"); objectNew.remove("referenced"); apiTestCase.setRequest(objectNew.toString()); - apiTestCase.setOrder(apiDefinitionService.getImportNextCaseOrder(projectId)); + apiTestCase.setOrder(apiDefinitionImportUtilService.getImportNextCaseOrder(projectId)); if (apiTestCase.getName().length() > 255) { apiTestCase.setName(apiTestCase.getName().substring(0, 255)); } @@ -331,5 +334,4 @@ public class ApiScenarioImportUtil { } } } - } diff --git a/api-test/backend/src/main/java/io/metersphere/controller/definition/ApiDefinitionController.java b/api-test/backend/src/main/java/io/metersphere/controller/definition/ApiDefinitionController.java index a7d302f01f..02b3fcce29 100644 --- a/api-test/backend/src/main/java/io/metersphere/controller/definition/ApiDefinitionController.java +++ b/api-test/backend/src/main/java/io/metersphere/controller/definition/ApiDefinitionController.java @@ -62,6 +62,7 @@ public class ApiDefinitionController { @Resource private ApiExecuteService apiExecuteService; + @PostMapping("/list/{goPage}/{pageSize}") @RequiresPermissions("PROJECT_API_DEFINITION:READ") public Pager> list(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody ApiDefinitionRequest request) { diff --git a/api-test/backend/src/main/java/io/metersphere/service/definition/ApiDefinitionImportUtil.java b/api-test/backend/src/main/java/io/metersphere/service/definition/ApiDefinitionImportUtil.java new file mode 100644 index 0000000000..4cf41662c9 --- /dev/null +++ b/api-test/backend/src/main/java/io/metersphere/service/definition/ApiDefinitionImportUtil.java @@ -0,0 +1,424 @@ +package io.metersphere.service.definition; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.metersphere.api.dto.definition.ApiDefinitionResult; +import io.metersphere.api.parse.api.ApiDefinitionImport; +import io.metersphere.base.domain.*; +import io.metersphere.base.mapper.ApiModuleMapper; +import io.metersphere.base.mapper.ApiTestCaseMapper; +import io.metersphere.base.mapper.EsbApiParamsMapper; +import io.metersphere.commons.constants.NoticeConstants; +import io.metersphere.commons.constants.PropertyConstant; +import io.metersphere.commons.enums.ApiTestDataStatus; +import io.metersphere.commons.utils.BeanUtils; +import io.metersphere.commons.utils.CommonBeanFactory; +import io.metersphere.commons.utils.SessionUtils; +import io.metersphere.i18n.Translator; +import io.metersphere.notice.sender.NoticeModel; +import io.metersphere.notice.service.NoticeSendService; +import io.metersphere.request.ApiSyncCaseRequest; +import io.metersphere.xpack.api.service.ApiDefinitionSyncService; +import org.apache.commons.beanutils.BeanMap; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.ibatis.session.SqlSession; + +import java.util.*; +import java.util.stream.Collectors; + +public class ApiDefinitionImportUtil { + public static final String HEADERS = "headers"; + public static final String ARGUMENTS = "arguments"; + public static final String REST = "rest"; + public static final String BODY = "body"; + public static final String JSONSCHEMA = "jsonSchema"; + public static final String PROPERTIES = "properties"; + + + public static Boolean checkIsSynchronize(ApiDefinitionWithBLOBs existApi, ApiDefinitionWithBLOBs apiDefinition) { + + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + + ApiSyncCaseRequest apiSyncCaseRequest = null; + Boolean toUpdate = false; + ApiDefinitionSyncService apiDefinitionSyncService = CommonBeanFactory.getBean(ApiDefinitionSyncService.class); + if (apiDefinitionSyncService != null) { + toUpdate = apiDefinitionSyncService.getProjectApplications(existApi.getProjectId()); + apiSyncCaseRequest = apiDefinitionSyncService.getApiSyncCaseRequest(existApi.getProjectId()); + } + //Compare the basic information of the API. If it contains the comparison that needs to be done for the synchronization information, + // put the data into the to-be-synchronized + if (apiSyncCaseRequest == null) { + return false; + } + Boolean diffBasicInfo = delBasicInfo(existApi, apiDefinition, apiSyncCaseRequest, toUpdate); + if (diffBasicInfo != null) return diffBasicInfo; + + Boolean diffApiRequest = delRequest(existApi, apiDefinition, objectMapper, apiSyncCaseRequest, toUpdate); + if (diffApiRequest != null) return diffApiRequest; + + Boolean diffResponse = delResponse(existApi, apiDefinition, objectMapper); + if (diffResponse != null) return diffResponse; + return false; + } + + /** + * 比较导入的与系统中重复的两个api的基础信息 + * + * @param existApi + * @param apiDefinition + * @param apiSyncCaseRequest + * @param toUpdate + * @return + */ + private static Boolean delBasicInfo(ApiDefinitionWithBLOBs existApi, ApiDefinitionWithBLOBs apiDefinition, ApiSyncCaseRequest apiSyncCaseRequest, Boolean toUpdate) { + if (!StringUtils.equals(apiDefinition.getMethod(), existApi.getMethod())) { + if (apiSyncCaseRequest.getMethod() && toUpdate) { + apiDefinition.setToBeUpdateTime(System.currentTimeMillis()); + } + return true; + } + if (!StringUtils.equals(apiDefinition.getProtocol(), existApi.getProtocol())) { + if (apiSyncCaseRequest.getProtocol() && toUpdate) { + apiDefinition.setToBeUpdateTime(System.currentTimeMillis()); + } + return true; + } + + if (!StringUtils.equals(apiDefinition.getPath(), existApi.getPath())) { + if (apiSyncCaseRequest.getPath() && toUpdate) { + apiDefinition.setToBeUpdateTime(System.currentTimeMillis()); + } + return true; + } + if (!StringUtils.equals(apiDefinition.getCreateUser(), existApi.getCreateUser())) { + return true; + } + + if (!StringUtils.equals(apiDefinition.getStatus(), existApi.getStatus()) && StringUtils.isNotBlank(existApi.getStatus()) && StringUtils.isNotBlank(apiDefinition.getStatus())) { + return true; + } + + if (!StringUtils.equals(apiDefinition.getTags(), existApi.getTags())) { + if (apiDefinition.getTags() != null && Objects.equals(apiDefinition.getTags(), StringUtils.EMPTY) && existApi.getTags() != null && Objects.equals(existApi.getTags(), StringUtils.EMPTY)) { + return true; + } + } + + if (!StringUtils.equals(existApi.getRemark(), apiDefinition.getRemark()) && StringUtils.isNotBlank(existApi.getRemark()) && StringUtils.isNotBlank(apiDefinition.getRemark())) { + return true; + } + + if (!StringUtils.equals(existApi.getDescription(), apiDefinition.getDescription()) && StringUtils.isNotBlank(existApi.getDescription()) && StringUtils.isNotBlank(apiDefinition.getDescription())) { + return true; + } + return null; + } + + /** + * 比较导入的与系统中重复的两个api的响应体信息 + * + * @param existApi + * @param apiDefinition + * @param objectMapper + * @return + */ + private static Boolean delRequest(ApiDefinitionWithBLOBs existApi, ApiDefinitionWithBLOBs apiDefinition, ObjectMapper objectMapper, ApiSyncCaseRequest apiSyncCaseRequest, Boolean toUpdate) { + JsonNode exApiRequest = null; + JsonNode apiRequest = null; + try { + exApiRequest = objectMapper.readTree(existApi.getRequest()); + apiRequest = objectMapper.readTree(apiDefinition.getRequest()); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + + if (exApiRequest == null || apiRequest == null) { + return exApiRequest != null || apiRequest != null; + } + + List compareProList = Arrays.asList(HEADERS, ARGUMENTS, REST); + + Map applicationMap = new HashMap<>(4); + applicationMap.put(HEADERS, apiSyncCaseRequest.getHeaders()); + applicationMap.put(ARGUMENTS, apiSyncCaseRequest.getQuery()); + applicationMap.put(REST, apiSyncCaseRequest.getRest()); + applicationMap.put(BODY, apiSyncCaseRequest.getBody()); + + boolean diffByNodes = false; + for (String property : compareProList) { + JsonNode exApiJsonNode = exApiRequest.get(property); + JsonNode apiJsonNode = apiRequest.get(property); + if (exApiJsonNode != null && apiJsonNode != null) { + diffByNodes = getDiffByArrayNodes(apiRequest, exApiRequest, objectMapper, property); + if (diffByNodes) { + if (toUpdate && applicationMap.get(property)) { + apiDefinition.setToBeUpdateTime(System.currentTimeMillis()); + } + break; + } + } + } + if (diffByNodes) { + return true; + } + return delBody(apiDefinition, objectMapper, toUpdate, exApiRequest, apiRequest, applicationMap); + } + + private static Boolean delResponse(ApiDefinitionWithBLOBs existApi, ApiDefinitionWithBLOBs apiDefinition, ObjectMapper objectMapper) { + JsonNode exApiResponse = null; + JsonNode apiResponse = null; + + if (StringUtils.isBlank(apiDefinition.getResponse()) || StringUtils.isBlank(existApi.getResponse())) { + return !StringUtils.isBlank(apiDefinition.getResponse()) || !StringUtils.isBlank(existApi.getResponse()); + } + + try { + exApiResponse = objectMapper.readTree(existApi.getResponse()); + apiResponse = objectMapper.readTree(apiDefinition.getResponse()); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + + if (exApiResponse == null || apiResponse == null) { + return exApiResponse != null || apiResponse != null; + } + + if (exApiResponse.get(HEADERS) != null && apiResponse.get(HEADERS) != null) { + if (!StringUtils.equals(exApiResponse.get(HEADERS).toString(), apiResponse.get(HEADERS).toString())) { + return true; + } + } + + if (exApiResponse.get(PropertyConstant.TYPE) != null && apiResponse.get(PropertyConstant.TYPE) != null) { + if (!StringUtils.equals(exApiResponse.get(PropertyConstant.TYPE).toString(), apiResponse.get(PropertyConstant.TYPE).toString())) { + return true; + } + } + + if (exApiResponse.get("name") != null && apiResponse.get("name") != null) { + if (!StringUtils.equals(exApiResponse.get("name").toString(), apiResponse.get("name").toString())) { + return true; + } + } + + if (exApiResponse.get(BODY) != null && apiResponse.get(BODY) != null) { + if (!StringUtils.equals(exApiResponse.get(BODY).toString(), apiResponse.get(BODY).toString())) { + return true; + } + } + + if (exApiResponse.get("statusCode") != null && apiResponse.get("statusCode") != null) { + if (!StringUtils.equals(exApiResponse.get("statusCode").toString(), apiResponse.get("statusCode").toString())) { + return true; + } + } + + if (exApiResponse.get("enable") != null && apiResponse.get("enable") != null) { + return !StringUtils.equals(exApiResponse.get("enable").toString(), apiResponse.get("enable").toString()); + } + return null; + } + + private static boolean getDiffByArrayNodes(JsonNode apiRequest, JsonNode exApiRequest, ObjectMapper objectMapper, String name) { + JsonNode apiNameNode = apiRequest.get(name); + JsonNode caseNameNode = exApiRequest.get(name); + if (apiNameNode == null || caseNameNode == null) { + return false; + } + Map apiMap = new HashMap<>(); + getKeyNameMap(apiNameNode, objectMapper, apiMap, "name"); + Map exApiMap = new HashMap<>(); + getKeyNameMap(caseNameNode, objectMapper, exApiMap, "name"); + if (apiMap.size() != exApiMap.size()) { + return true; + } + Set apiKey = apiMap.keySet(); + Set exApiKey = exApiMap.keySet(); + List collect = apiKey.stream().filter(exApiKey::contains).collect(Collectors.toList()); + if (collect.size() != apiKey.size()) { + return true; + } + return false; + } + + public static void getKeyNameMap(JsonNode apiNameNode, ObjectMapper objectMapper, Map nameMap, String nodeKey) { + for (int i = 0; i < apiNameNode.size(); i++) { + JsonNode apiName = apiNameNode.get(i); + if (apiName.has(nodeKey)) { + String keyName = null; + try { + keyName = objectMapper.writeValueAsString(apiName.get(nodeKey)); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + if (StringUtils.isNotBlank(keyName) && !StringUtils.equals(keyName, "\"\"") && !StringUtils.equals(keyName, "null")) { + try { + nameMap.put(apiName.get(nodeKey).toString(), objectMapper.writeValueAsString(apiName)); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } + } + } + } + + private static Boolean delBody(ApiDefinitionWithBLOBs apiDefinition, ObjectMapper objectMapper, Boolean toUpdate, JsonNode exApiRequest, JsonNode apiRequest, Map applicationMap) { + JsonNode exBodyNode = exApiRequest.get(BODY); + JsonNode bodyNode = apiRequest.get(BODY); + if (exBodyNode != null && bodyNode != null) { + JsonNode exRowNode = exBodyNode.get("raw"); + JsonNode rowNode = bodyNode.get("raw"); + if (exRowNode != null && rowNode != null) { + if (!StringUtils.equals(exRowNode.asText(), rowNode.asText())) { + if (applicationMap.get(BODY)) { + apiDefinition.setToBeUpdateTime(System.currentTimeMillis()); + } + return true; + } + } + + boolean diffByNodes = getDiffByArrayNodes(bodyNode, exBodyNode, objectMapper, "kvs"); + if (diffByNodes && toUpdate && applicationMap.get(BODY)) { + apiDefinition.setToBeUpdateTime(System.currentTimeMillis()); + } + if (diffByNodes) { + return true; + } + JsonNode exApiJsonSchema = exBodyNode.get(JSONSCHEMA); + JsonNode apiJsonSchema = bodyNode.get(JSONSCHEMA); + if (exApiJsonSchema == null || apiJsonSchema == null) { + return false; + } + + JsonNode exApiProperties = exApiJsonSchema.get(PROPERTIES); + JsonNode apiProperties = apiJsonSchema.get(PROPERTIES); + if (exApiProperties == null || apiProperties == null) { + return false; + } + boolean diffJsonschema = replenishCaseProperties(exApiProperties, apiProperties); + if (diffJsonschema && toUpdate && applicationMap.get(BODY)) { + apiDefinition.setToBeUpdateTime(System.currentTimeMillis()); + } + return diffJsonschema; + } + return null; + } + + + private static boolean replenishCaseProperties(JsonNode exApiProperties, JsonNode apiProperties) { + + Iterator> apiFields = apiProperties.fields(); + Iterator> exApiFields = exApiProperties.fields(); + boolean diffProp = false; + while (apiFields.hasNext()) { + Map.Entry apiNode = apiFields.next(); + if (diffProp) { + break; + } + if (exApiFields.hasNext()) { + Map.Entry exChildNode = null; + Map.Entry exNode = exApiFields.next(); + if (StringUtils.equalsIgnoreCase(apiNode.getKey(), exNode.getKey())) { + exChildNode = exNode; + } else { + diffProp = true; + } + if (exChildNode == null) { + continue; + } + JsonNode value = apiNode.getValue(); + JsonNode value1 = exChildNode.getValue(); + JsonNode apiPropertiesNode = value.get(PROPERTIES); + JsonNode exApiPropertiesNode = value1.get(PROPERTIES); + if (apiPropertiesNode == null || exApiPropertiesNode == null) { + continue; + } + replenishCaseProperties(exApiPropertiesNode, apiPropertiesNode); + } else { + return true; + } + } + return false; + } + + public static ApiTestCaseWithBLOBs addNewCase(ApiDefinitionWithBLOBs apiDefinition) { + ApiTestCaseWithBLOBs apiTestCase = new ApiTestCaseWithBLOBs(); + apiTestCase.setApiDefinitionId(apiDefinition.getId()); + apiTestCase.setProjectId(apiDefinition.getProjectId()); + apiTestCase.setName(apiDefinition.getName()); + apiTestCase.setRequest(apiDefinition.getRequest()); + return apiTestCase; + } + + public static void setModule(ApiDefinitionWithBLOBs item, ApiModuleMapper apiModuleMapper) { + if (item != null && StringUtils.isEmpty(item.getModuleId()) || "default-module".equals(item.getModuleId())) { + ApiModuleExample example = new ApiModuleExample(); + example.createCriteria().andProjectIdEqualTo(item.getProjectId()).andProtocolEqualTo(item.getProtocol()).andNameEqualTo("未规划接口"); + List modules = apiModuleMapper.selectByExample(example); + if (CollectionUtils.isNotEmpty(modules)) { + item.setModuleId(modules.get(0).getId()); + item.setModulePath(modules.get(0).getName()); + } + } + } + + public static void sendImportApiNotice(ApiDefinitionWithBLOBs apiDefinitionWithBLOBs, String context, String event, String tip) { + NoticeSendService noticeSendService = CommonBeanFactory.getBean(NoticeSendService.class); + BeanMap beanMap = new BeanMap(apiDefinitionWithBLOBs); + Map paramMap = new HashMap<>(beanMap); + paramMap.put("operator", SessionUtils.getUserId()); + NoticeModel noticeModel = NoticeModel.builder().operator(SessionUtils.getUserId()).context(context).testId(apiDefinitionWithBLOBs.getId()).subject(Translator.get(tip)).paramMap(paramMap).excludeSelf(true).event(event).build(); + noticeSendService.send(NoticeConstants.TaskType.API_DEFINITION_TASK, noticeModel); + } + + public static void sendImportCaseNotice(ApiTestCase apiTestCase, String context, String event, String tip) { + NoticeSendService noticeSendService = CommonBeanFactory.getBean(NoticeSendService.class); + BeanMap beanMap = new BeanMap(apiTestCase); + Map paramMap = new HashMap<>(beanMap); + paramMap.put("operator", SessionUtils.getUserId()); + NoticeModel noticeModel = NoticeModel.builder().operator(SessionUtils.getUserId()).context(context).testId(apiTestCase.getId()).subject(Translator.get(tip)).paramMap(paramMap).excludeSelf(true).event(event).build(); + noticeSendService.send(NoticeConstants.TaskType.API_DEFINITION_TASK, noticeModel); + } + + public static ApiDefinitionResult getApiDefinitionResult(ApiDefinitionWithBLOBs apiDefinition, boolean isUpdate) { + ApiDefinitionResult apiDefinitionResult = new ApiDefinitionResult(); + BeanUtils.copyBean(apiDefinitionResult, apiDefinition); + apiDefinitionResult.setUpdated(isUpdate); + return apiDefinitionResult; + } + + public static Map> getOldCaseMap(List repeatApiDefinitionWithBLOBs, ApiTestCaseMapper apiTestCaseMapper) { + Map> oldCaseMap; + List definitionIds = repeatApiDefinitionWithBLOBs.stream().map(ApiDefinition::getId).collect(Collectors.toList()); + ApiTestCaseExample testCaseExample = new ApiTestCaseExample(); + testCaseExample.createCriteria().andApiDefinitionIdIn(definitionIds); + testCaseExample.or(testCaseExample.createCriteria().andStatusNotEqualTo(ApiTestDataStatus.TRASH.getValue()).andStatusIsNull()); + List caseWithBLOBs = apiTestCaseMapper.selectByExampleWithBLOBs(testCaseExample); + oldCaseMap = caseWithBLOBs.stream().collect(Collectors.groupingBy(ApiTestCase::getApiDefinitionId)); + return oldCaseMap; + } + + public static void delEsbData(ApiDefinitionImport apiImport, SqlSession sqlSession) { + if (apiImport.getEsbApiParamsMap() != null && apiImport.getEsbApiParamsMap().size() > 0) { + EsbApiParamsMapper esbApiParamsMapper = sqlSession.getMapper(EsbApiParamsMapper.class); + for (EsbApiParamsWithBLOBs model : apiImport.getEsbApiParamsMap().values()) { + EsbApiParamsExample example = new EsbApiParamsExample(); + example.createCriteria().andResourceIdEqualTo(model.getResourceId()); + List exitModelList = esbApiParamsMapper.selectByExampleWithBLOBs(example); + if (exitModelList.isEmpty()) { + esbApiParamsMapper.insert(model); + } else { + model.setId(exitModelList.get(0).getId()); + esbApiParamsMapper.updateByPrimaryKeyWithBLOBs(model); + } + } + } + } + + +} diff --git a/api-test/backend/src/main/java/io/metersphere/service/definition/ApiDefinitionImportUtilService.java b/api-test/backend/src/main/java/io/metersphere/service/definition/ApiDefinitionImportUtilService.java new file mode 100644 index 0000000000..7f342e5d36 --- /dev/null +++ b/api-test/backend/src/main/java/io/metersphere/service/definition/ApiDefinitionImportUtilService.java @@ -0,0 +1,1243 @@ +package io.metersphere.service.definition; + +import io.metersphere.api.dto.ApiTestImportRequest; +import io.metersphere.api.dto.definition.*; +import io.metersphere.api.dto.definition.request.ElementUtil; +import io.metersphere.api.dto.definition.request.sampler.MsHTTPSamplerProxy; +import io.metersphere.api.dto.definition.request.sampler.MsTCPSampler; +import io.metersphere.api.dto.mock.config.MockConfigImportDTO; +import io.metersphere.api.parse.api.ApiDefinitionImport; +import io.metersphere.base.domain.*; +import io.metersphere.base.mapper.*; +import io.metersphere.base.mapper.ext.BaseProjectVersionMapper; +import io.metersphere.base.mapper.ext.ExtApiDefinitionMapper; +import io.metersphere.base.mapper.ext.ExtApiTestCaseMapper; +import io.metersphere.commons.constants.*; +import io.metersphere.commons.enums.ApiTestDataStatus; +import io.metersphere.commons.exception.MSException; +import io.metersphere.commons.utils.*; +import io.metersphere.dto.ProjectConfig; +import io.metersphere.i18n.Translator; +import io.metersphere.notice.sender.NoticeModel; +import io.metersphere.notice.service.NoticeSendService; +import io.metersphere.service.BaseProjectApplicationService; +import io.metersphere.service.MockConfigService; +import io.metersphere.service.ServiceUtils; +import io.metersphere.service.ext.ExtApiScheduleService; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections.MapUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.ibatis.session.ExecutorType; +import org.apache.ibatis.session.SqlSession; +import org.apache.ibatis.session.SqlSessionFactory; +import org.json.JSONObject; +import org.mybatis.spring.SqlSessionUtils; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.*; +import java.util.stream.Collectors; + +@Service +public class ApiDefinitionImportUtilService { + + private static final String SCHEDULE = "schedule"; + + public static final String BODY = "body"; + + private final ThreadLocal currentApiOrder = new ThreadLocal<>(); + private final ThreadLocal currentApiCaseOrder = new ThreadLocal<>(); + + @Resource + private ExtApiScheduleService extApiScheduleService; + @Resource + private NoticeSendService noticeSendService; + @Resource + private BaseProjectApplicationService projectApplicationService; + @Resource + private ApiModuleService apiModuleService; + @Resource + private SqlSessionFactory sqlSessionFactory; + @Resource + private BaseProjectVersionMapper baseProjectVersionMapper; + @Resource + private ProjectMapper projectMapper; + + @Resource + private ExtApiDefinitionMapper extApiDefinitionMapper; + @Resource + private ApiTestCaseMapper apiTestCaseMapper; + @Resource + private ExtApiTestCaseMapper extApiTestCaseMapper; + @Resource + private ApiTestCaseService apiTestCaseService; + + + public void checkUrl(ApiTestImportRequest request, Project project) { + if (StringUtils.isNotBlank(request.getSwaggerUrl())) { + if (!request.getPlatform().equalsIgnoreCase("Swagger2")) { + sendFailMessage(request, project); + MSException.throwException(Translator.get("file_format_does_not_meet_requirements")); + } + try { + UrlTestUtils.testUrl(request.getSwaggerUrl(), 30000); + } catch (Exception e) { + sendFailMessage(request, project); + MSException.throwException(e.getMessage()); + } + } + } + + public void sendImportNotice(ApiTestImportRequest request, List apiImportSendNoticeDTOS, Project project) { + if (StringUtils.equals(request.getType(), SCHEDULE)) { + String scheduleId = extApiScheduleService.getScheduleInfo(request.getResourceId()); + String context = request.getSwaggerUrl() + "导入成功"; + Map paramMap = new HashMap<>(); + paramMap.put("url", request.getSwaggerUrl()); + NoticeModel noticeModel = NoticeModel.builder().operator(project.getCreateUser()).context(context).testId(scheduleId).subject(Translator.get("swagger_url_scheduled_import_notification")).paramMap(paramMap).event(NoticeConstants.Event.EXECUTE_SUCCESSFUL).build(); + noticeSendService.send(NoticeConstants.Mode.SCHEDULE, StringUtils.EMPTY, noticeModel); + } + if (!StringUtils.equals(request.getType(), SCHEDULE) && CollectionUtils.isNotEmpty(apiImportSendNoticeDTOS)) { + for (ApiImportSendNoticeDTO apiImportSendNoticeDTO : apiImportSendNoticeDTOS) { + ApiDefinitionResult apiDefinitionResult = apiImportSendNoticeDTO.getApiDefinitionResult(); + if (apiDefinitionResult != null && !apiDefinitionResult.isUpdated()) { + String context = SessionUtils.getUserId().concat("新建了接口定义").concat(":").concat(apiDefinitionResult.getName()); + ApiDefinitionImportUtil.sendImportApiNotice(apiDefinitionResult, context, NoticeConstants.Event.CREATE, "api_create_notice"); + } + if (apiDefinitionResult != null && apiDefinitionResult.isUpdated()) { + String context = SessionUtils.getUserId().concat("更新了接口定义").concat(":").concat(apiDefinitionResult.getName()); + ApiDefinitionImportUtil.sendImportApiNotice(apiDefinitionResult, context, NoticeConstants.Event.UPDATE, "api_update_notice"); + } + if (CollectionUtils.isNotEmpty(apiImportSendNoticeDTO.getCaseDTOList())) { + for (ApiTestCaseDTO apiTestCaseDTO : apiImportSendNoticeDTO.getCaseDTOList()) { + if (apiTestCaseDTO.isUpdated()) { + String context = SessionUtils.getUserId().concat("更新了接口用例").concat(":").concat(apiTestCaseDTO.getName()); + ApiDefinitionImportUtil.sendImportCaseNotice(apiTestCaseDTO, context, NoticeConstants.Event.CASE_UPDATE, "api_case_update_notice"); + } else { + String context = SessionUtils.getUserId().concat("新建了接口用例").concat(":").concat(apiTestCaseDTO.getName()); + ApiDefinitionImportUtil.sendImportCaseNotice(apiTestCaseDTO, context, NoticeConstants.Event.CASE_CREATE, "api_case_create_notice"); + } + } + } + } + } + } + + public void checkFileSuffixName(ApiTestImportRequest request, String suffixName) { + if (suffixName.equalsIgnoreCase("jmx")) { + if (!request.getPlatform().equalsIgnoreCase("JMeter")) { + MSException.throwException(Translator.get("file_format_does_not_meet_requirements")); + } + } + if (suffixName.equalsIgnoreCase("har")) { + if (!request.getPlatform().equalsIgnoreCase("Har")) { + MSException.throwException(Translator.get("file_format_does_not_meet_requirements")); + } + } + if (suffixName.equalsIgnoreCase("json")) { + if (request.getPlatform().equalsIgnoreCase("Har") || request.getPlatform().equalsIgnoreCase("Jmeter")) { + MSException.throwException(Translator.get("file_format_does_not_meet_requirements")); + } + } + } + + public void sendFailMessage(ApiTestImportRequest request, Project project) { + if (StringUtils.equals(request.getType(), SCHEDULE)) { + String scheduleId = extApiScheduleService.getScheduleInfo(request.getResourceId()); + String context = request.getSwaggerUrl() + "导入失败"; + Map paramMap = new HashMap<>(); + paramMap.put("url", request.getSwaggerUrl()); + paramMap.put("projectId", request.getProjectId()); + NoticeModel noticeModel = NoticeModel.builder().operator(project.getCreateUser()).context(context).testId(scheduleId).subject(Translator.get("swagger_url_scheduled_import_notification")).paramMap(paramMap).event(NoticeConstants.Event.EXECUTE_FAILED).build(); + noticeSendService.send(NoticeConstants.Mode.SCHEDULE, StringUtils.EMPTY, noticeModel); + } + } + + public List importApi(ApiTestImportRequest request, ApiDefinitionImport apiImport) { + SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH); + currentApiCaseOrder.remove(); + currentApiOrder.remove(); + String defaultVersion = baseProjectVersionMapper.getDefaultVersion(request.getProjectId()); + request.setDefaultVersion(defaultVersion); + if (request.getVersionId() == null) { + request.setVersionId(defaultVersion); + } + List initData = apiImport.getData(); + + Project project = projectMapper.selectByPrimaryKey(request.getProjectId()); + ProjectConfig config = projectApplicationService.getSpecificTypeValue(project.getId(), ProjectApplicationType.URL_REPEATABLE.name()); + boolean urlRepeat = config.getUrlRepeatable(); + //过滤(一次只导入一个协议) + List filterData = initData.stream().filter(t -> t.getProtocol().equals(request.getProtocol())).collect(Collectors.toList()); + if (filterData.isEmpty()) { + return new ArrayList<>(); + } + //处理模块路径 + UpdateApiModuleDTO updateApiModuleDTO = apiModuleService.checkApiModule(request, apiImport, filterData, StringUtils.equals("fullCoverage", request.getModeId()), urlRepeat); + //重新设置模块路径后的数据 + List optionData = updateApiModuleDTO.getDefinitionWithBLOBs(); + // 新增的模块,key 为模块路径 + Map moduleMap = updateApiModuleDTO.getModuleMap(); + //导入的处理重复后的case + List optionDataCases = updateApiModuleDTO.getCaseWithBLOBs(); + Boolean fullCoverage = updateApiModuleDTO.getFullCoverage(); + //所有模块的ID 及其全路径的map + Map idPathMap = updateApiModuleDTO.getIdPathMap(); + ApiModuleDTO chooseModule = updateApiModuleDTO.getChooseModule(); + + Map> apiIdCaseMap = optionDataCases.stream().collect(Collectors.groupingBy(ApiTestCase::getApiDefinitionId)); + + ApiDefinitionMapper batchMapper = sqlSession.getMapper(ApiDefinitionMapper.class); + ExtApiDefinitionMapper extApiDefinitionMapper = sqlSession.getMapper(ExtApiDefinitionMapper.class); + ApiModuleMapper apiModuleMapper = sqlSession.getMapper(ApiModuleMapper.class); + + //系统内需要做更新操作的数据 + List toUpdateList = new ArrayList<>(); + //与当前导入接口重复的系统内所有数据 + List repeatList; + + //处理导入数据与已存在数据关系 + if (request.getProtocol().equals("HTTP")) { + if (urlRepeat) { + repeatList = dealHttpUrlRepeat(chooseModule, idPathMap, optionData, fullCoverage, request, moduleMap, toUpdateList, optionDataCases); + } else { + repeatList = dealHttpUrlNoRepeat(optionData, fullCoverage, request, moduleMap, toUpdateList, optionDataCases); + } + if (optionData.isEmpty()) { + moduleMap = new HashMap<>(); + } + + } else { + Map esbApiParamsMap = apiImport.getEsbApiParamsMap(); + + repeatList = dealRepeat(request, optionData, moduleMap, fullCoverage, idPathMap, chooseModule, esbApiParamsMap, toUpdateList, optionDataCases); + } + + if (MapUtils.isNotEmpty(moduleMap)) { + moduleMap.forEach((k,v)-> apiModuleMapper.insert(v)); + } + int num = 0; + if (!CollectionUtils.isEmpty(optionData) && optionData.get(0) != null && optionData.get(0).getProjectId() != null) { + num = getNextNum(optionData.get(0).getProjectId(),extApiDefinitionMapper); + } + //如果需要导入的数据为空。此时清空mock信息 + if (optionData.isEmpty()) { + apiImport.getMocks().clear(); + } + List apiImportSendNoticeDTOS = new ArrayList<>(); + for (int i = 0; i < optionData.size(); i++) { + ApiDefinitionWithBLOBs item = optionData.get(i); + List sameRefIds = null; + //这里把数据库里与该接口相同的所有接口筛选出来包括不同的版本 + //如果 sameRefIds 与 toUpdates 相同,就用 toUpdates 代替 sameRefIds,因为toUpdates 可能会修改模块路径 + if (CollectionUtils.isNotEmpty(repeatList)) { + sameRefIds = repeatList.stream().filter(t -> t.getRefId().equals(item.getRefId())).collect(Collectors.toList()); + List repeatIds = sameRefIds.stream().map(ApiDefinition::getId).collect(Collectors.toList()); + List toUpdates = toUpdateList.stream().filter(t -> t.getRefId().equals(item.getRefId())).collect(Collectors.toList()); + List toUpDateIds = toUpdates.stream().map(ApiDefinition::getId).collect(Collectors.toList()); + List reduce1 = repeatIds.stream().filter(t -> !toUpDateIds.contains(t)).collect(Collectors.toList()); + if (CollectionUtils.isEmpty(reduce1)) { + sameRefIds = toUpdates; + } + } + if (item.getVersionId().equalsIgnoreCase("trash")) { + if (CollectionUtils.isNotEmpty(sameRefIds)) { + for (ApiDefinitionWithBLOBs sameRefId : sameRefIds) { + batchMapper.updateByPrimaryKey(sameRefId); + } + } + continue; + } + List caseList = apiIdCaseMap.get(item.getId()); + ApiDefinitionImportUtil.setModule(item, apiModuleMapper); + if (item.getName().length() > 255) { + item.setName(item.getName().substring(0, 255)); + } + //如果是创建版本数据,则num和其他版本数据一致 + if (item.getVersionId() == null || (!item.getVersionId().equals("new") && !item.getVersionId().equals("update"))) { + item.setNum(num++); + } + //如果EsbData需要存储,则需要进行接口是否更新的判断 + ApiDefinitionImportParamDTO apiDefinitionImportParam = new ApiDefinitionImportParamDTO(item, request, apiImport.getMocks(), toUpdateList, caseList); + apiDefinitionImportParam.setRepeatList(sameRefIds); + if (apiImport.getEsbApiParamsMap() != null) { + String apiId = item.getId(); + EsbApiParamsWithBLOBs model = apiImport.getEsbApiParamsMap().get(apiId); + request.setModeId("fullCoverage");//标准版ESB数据导入不区分是否覆盖,默认都为覆盖 + + ApiImportSendNoticeDTO apiImportSendNoticeDTO = importCreate(batchMapper, apiDefinitionImportParam); + if (model != null) { + apiImport.getEsbApiParamsMap().remove(apiId); + model.setResourceId(item.getId()); + apiImport.getEsbApiParamsMap().put(item.getId(), model); + } + if (apiImportSendNoticeDTO != null) { + apiImportSendNoticeDTOS.add(apiImportSendNoticeDTO); + } + } else { + ApiImportSendNoticeDTO apiImportSendNoticeDTO = importCreate(batchMapper, apiDefinitionImportParam); + if (apiImportSendNoticeDTO != null) { + apiImportSendNoticeDTOS.add(apiImportSendNoticeDTO); + } + } + if (i % 300 == 0) { + sqlSession.flushStatements(); + } + } + + //判断EsbData是否需要存储 + ApiDefinitionImportUtil.delEsbData(apiImport, sqlSession); + + if (!CollectionUtils.isEmpty(apiImport.getMocks())) { + MockConfigService mockConfigService = CommonBeanFactory.getBean(MockConfigService.class); + mockConfigService.importMock(apiImport, sqlSession, request); + } + sqlSession.flushStatements(); + if (sqlSession != null && sqlSessionFactory != null) { + SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory); + } + + return apiImportSendNoticeDTOS; + } + + private List dealHttpUrlRepeat(ApiModuleDTO chooseModule, Map idPathMap, List optionData, + Boolean fullCoverage, ApiTestImportRequest request, Map moduleMap, + List toUpdateList, List optionDataCases) { + String updateVersionId = getUpdateVersionId(request); + Boolean fullCoverageApi = getFullCoverageApi(request); + String projectId = request.getProjectId(); + //系统内重复的数据 + List repeatApiDefinitionWithBLOBs = extApiDefinitionMapper.selectRepeatByBLOBs(optionData, projectId); + + //这个是名称加请求方式加路径加模块为key的map 就是为了去重 + Map optionMap = new HashMap<>(); + //这个是系统内重复的数据 + Map> repeatDataMap; + //按照原来的顺序 + if (chooseModule != null) { + //如果有选中的模块,则在选中的模块下过滤 过滤规则是 选择的模块路径+名称+method+path + String chooseModuleParentId = getChooseModuleParentId(chooseModule); + String chooseModulePath = getChooseModulePath(idPathMap, chooseModule, chooseModuleParentId); + //这样的过滤规则下可能存在重复接口,如果是覆盖模块,需要按照去重规则再次去重,否则就加上接口原有的模块 + if (fullCoverage) { + List singleOptionData = new ArrayList<>(); + removeHttpChooseModuleRepeat(optionData, singleOptionData, chooseModulePath); + optionData = singleOptionData; + optionMap = optionData.stream().collect(Collectors.toMap(t -> t.getName().concat(t.getMethod()).concat(t.getPath()).concat(chooseModulePath), api -> api)); + } else { + getChooseModuleUrlRepeatOptionMap(optionData, optionMap, chooseModulePath); + } + repeatDataMap = repeatApiDefinitionWithBLOBs.stream().filter(t -> t.getModuleId().equals(chooseModule.getId())).collect(Collectors.groupingBy(t -> t.getName().concat(t.getMethod()).concat(t.getPath()).concat(t.getModulePath()))); + } else { + //否则在整个系统中过滤 + getUrlRepeatOptionMap(optionData, optionMap); + repeatDataMap = repeatApiDefinitionWithBLOBs.stream().collect(Collectors.groupingBy(t -> t.getName().concat(t.getMethod()).concat(t.getPath()).concat(t.getModulePath()))); + } + Map> oldCaseMap = new HashMap<>(); + //重复接口的case + if (CollectionUtils.isNotEmpty(repeatApiDefinitionWithBLOBs)) { + oldCaseMap = ApiDefinitionImportUtil.getOldCaseMap(repeatApiDefinitionWithBLOBs, apiTestCaseMapper); + } + //覆盖接口 + if (fullCoverage) { + //允许覆盖模块,用导入的重复数据的最后一条覆盖查询的所有重复数据; case 在覆盖的时候,是拼接到原来的case,name唯一;不覆盖,就用原来的 + if (fullCoverageApi) { + if (CollectionUtils.isNotEmpty(repeatApiDefinitionWithBLOBs)) { + startCoverModule(toUpdateList, optionData, optionMap, repeatDataMap, updateVersionId, optionDataCases, oldCaseMap, null); + } + } else { + //覆盖但不覆盖模块 + if (CollectionUtils.isNotEmpty(repeatApiDefinitionWithBLOBs)) { + //过滤同一层级重复模块,导入文件没有新增接口无需创建接口模块 + moduleMap = judgeModuleMap(moduleMap, optionMap, repeatDataMap); + startCover(toUpdateList, optionData, optionMap, repeatDataMap, updateVersionId, optionDataCases, oldCaseMap,null); + } + } + } else { + //不覆盖,同一接口不做更新;可能创建新版本,case也直接创建, + if (CollectionUtils.isNotEmpty(repeatApiDefinitionWithBLOBs)) { + removeSameData(repeatDataMap, optionMap, optionData, moduleMap, optionDataCases); + } + } + //最后在整个体统内检查一遍(防止在有选择的模块时,未找到重复,直接创建的情况) + if (CollectionUtils.isNotEmpty(repeatApiDefinitionWithBLOBs)) { + repeatDataMap = repeatApiDefinitionWithBLOBs.stream().collect(Collectors.groupingBy(t -> t.getName().concat(t.getMethod()).concat(t.getPath()).concat(t.getModulePath()))); + optionMap = optionData.stream().collect(Collectors.toMap(t -> t.getName().concat(t.getMethod()).concat(t.getPath()).concat(t.getModulePath()), api -> api)); + if (fullCoverage) { + startCover(toUpdateList, optionData, optionMap, repeatDataMap, updateVersionId, optionDataCases, oldCaseMap,null); + } else { + //不覆盖,同一接口不做更新 + if (CollectionUtils.isNotEmpty(repeatApiDefinitionWithBLOBs)) { + removeSameData(repeatDataMap, optionMap, optionData, moduleMap, optionDataCases); + } + } + } + //将原来的case和更改的case组合在一起,为了同步的设置 + List caseIds = optionDataCases.stream().map(ApiTestCase::getId).filter(StringUtils::isNotBlank).collect(Collectors.toList()); + buildCases(optionDataCases, oldCaseMap, caseIds); + return repeatApiDefinitionWithBLOBs; + } + + private List dealHttpUrlNoRepeat(List optionData, + Boolean fullCoverage, ApiTestImportRequest request, Map moduleMap, + List toUpdateList, List optionDataCases) { + + //这个是名称加请求方式加路径加模块为key的map 就是为了去重 + Map optionMap; + + String updateVersionId = getUpdateVersionId(request); + Boolean fullCoverageApi = getFullCoverageApi(request); + String projectId = request.getProjectId(); + //系统内重复的数据 + List repeatApiDefinitionWithBLOBs = extApiDefinitionMapper.selectRepeatByBLOBs(optionData, projectId); + + //这个是系统内重复的数据 + Map> repeatDataMap = repeatApiDefinitionWithBLOBs.stream().collect(Collectors.groupingBy(t -> t.getMethod().concat(t.getPath()))); + + //按照原来的顺序 + optionMap = optionData.stream().collect(Collectors.toMap(t -> t.getMethod().concat(t.getPath()), api -> api)); + + Map> oldCaseMap = new HashMap<>(); + + //重复接口的case + if (CollectionUtils.isNotEmpty(repeatApiDefinitionWithBLOBs)) { + oldCaseMap = ApiDefinitionImportUtil.getOldCaseMap(repeatApiDefinitionWithBLOBs, apiTestCaseMapper); + } + + if (fullCoverage) { + if (fullCoverageApi) { + if (CollectionUtils.isNotEmpty(repeatApiDefinitionWithBLOBs)) { + startCoverModule(toUpdateList, optionData, optionMap, repeatDataMap, updateVersionId, optionDataCases, oldCaseMap, null); + } + } else { + //不覆盖模块 + if (CollectionUtils.isNotEmpty(repeatApiDefinitionWithBLOBs)) { + startCover(toUpdateList, optionData, optionMap, repeatDataMap, updateVersionId, optionDataCases, oldCaseMap,null); + } + } + } else { + //不覆盖,同一接口不做更新 + if (CollectionUtils.isNotEmpty(repeatApiDefinitionWithBLOBs)) { + removeSameData(repeatDataMap, optionMap, optionData, moduleMap, optionDataCases); + } + } + //将原来的case和更改的case组合在一起,为了同步的设置 + List caseIds = optionDataCases.stream().map(ApiTestCase::getId).filter(StringUtils::isNotBlank).collect(Collectors.toList()); + buildCases(optionDataCases, oldCaseMap, caseIds); + return repeatApiDefinitionWithBLOBs; + } + + private static String getUpdateVersionId(ApiTestImportRequest request) { + String updateVersionId; + if (request.getUpdateVersionId() != null) { + updateVersionId = request.getUpdateVersionId(); + } else { + updateVersionId = request.getDefaultVersion(); + } + return updateVersionId; + } + + private static Boolean getFullCoverageApi(ApiTestImportRequest request) { + Boolean fullCoverageApi = request.getCoverModule(); + if (fullCoverageApi == null) { + fullCoverageApi = false; + } + return fullCoverageApi; + } + + private static String getChooseModuleParentId(ApiModuleDTO chooseModule) { + if (chooseModule.getParentId() == null) { + chooseModule.setParentId(PropertyConstant.ROOT); + } + return chooseModule.getParentId(); + } + + private static String getChooseModulePath(Map idPathMap, ApiModuleDTO chooseModule, String chooseModuleParentId) { + String s; + if (chooseModuleParentId.equals(PropertyConstant.ROOT)) { + s = "/" + chooseModule.getName(); + } else { + s = idPathMap.get(chooseModule.getId()); + } + return s; + } + + private static void removeHttpChooseModuleRepeat(List optionData, List singleOptionData, String chooseModulePath) { + LinkedHashMap> methodPathMap = optionData.stream().collect(Collectors.groupingBy(t -> t.getName().concat(t.getMethod()).concat(t.getPath()).concat(chooseModulePath), LinkedHashMap::new, Collectors.toList())); + methodPathMap.forEach((k, v) -> singleOptionData.add(v.get(v.size() - 1))); + } + + private static void getChooseModuleUrlRepeatOptionMap(List optionData, Map optionMap, String chooseModulePath) { + for (ApiDefinitionWithBLOBs optionDatum : optionData) { + if (optionDatum.getModulePath() == null) { + optionMap.put(optionDatum.getName().concat(optionDatum.getMethod()).concat(optionDatum.getPath()).concat(chooseModulePath), optionDatum); + } else { + optionMap.put(optionDatum.getName().concat(optionDatum.getMethod()).concat(optionDatum.getPath()).concat(chooseModulePath).concat(optionDatum.getModulePath()), optionDatum); + } + } + } + + private static void getUrlRepeatOptionMap(List optionData, Map optionMap) { + for (ApiDefinitionWithBLOBs optionDatum : optionData) { + if (optionDatum.getModulePath() == null) { + optionMap.put(optionDatum.getName().concat(optionDatum.getMethod()).concat(optionDatum.getPath()), optionDatum); + } else { + optionMap.put(optionDatum.getName().concat(optionDatum.getMethod()).concat(optionDatum.getPath()).concat(optionDatum.getModulePath()), optionDatum); + } + } + } + + + private static void startCoverModule(List toUpdateList, List optionData, + Map methodPathMap, Map> repeatDataMap, + String updateVersionId, List optionDataCases, + Map> oldCaseMap, Map esbApiParamsMap) { + //临时记录修改数据后导入的数据 + List coverApiList = new ArrayList<>(); + //临时记录需要更新的系统数据 + List updateApiList = new ArrayList<>(); + repeatDataMap.forEach((k, v) -> { + //导入的与系统是相同接口的数据 + ApiDefinitionWithBLOBs apiDefinitionWithBLOBs = methodPathMap.get(k); + if (apiDefinitionWithBLOBs != null) { + //该接口的case + Map> definitionIdCaseMAp = optionDataCases.stream().collect(Collectors.groupingBy(ApiTestCase::getApiDefinitionId)); + Map caseNameMap = getDistinctCaseNameMap(definitionIdCaseMAp, apiDefinitionWithBLOBs); + //循环系统内重复接口 + int i = 0; + ApiDefinitionWithBLOBs latestApi = null; + for (ApiDefinitionWithBLOBs definitionWithBLOBs : v) { + if (definitionWithBLOBs.getLatest()) { + latestApi = definitionWithBLOBs; + } + if (!definitionWithBLOBs.getVersionId().equals(updateVersionId)) { + i += 1; + continue; + } + //组合case + if (MapUtils.isNotEmpty(caseNameMap)) { + buildCaseList(oldCaseMap, caseNameMap, definitionWithBLOBs, optionDataCases); + } + //在指定版本中更新接口内容并变更接口模块为当前导入选择的模块下创建导入文件中接口指定的模块 + updateEsb(esbApiParamsMap, definitionWithBLOBs.getId(), apiDefinitionWithBLOBs.getId()); + ApiDefinitionWithBLOBs api = new ApiDefinitionWithBLOBs(); + BeanUtils.copyBean(api, apiDefinitionWithBLOBs); + api.setId(definitionWithBLOBs.getId()); + setApiParam(api, updateVersionId, definitionWithBLOBs); + coverApiList.add(api); + } + if (i == v.size()) { + //如果系统内的所有版本都不是当前选择的数据更新版本,需要与lasted = 1 比较请求参数,参数一致,仅变更接口模块为当前导入接口的模块,不一致,新增并变更接口模块为当前导入接口的模块 + if (latestApi != null) { + Boolean hasChange = ApiDefinitionImportUtil.checkIsSynchronize(latestApi, apiDefinitionWithBLOBs); + if (!hasChange) { + for (ApiDefinitionWithBLOBs definitionWithBLOBs : v) { + definitionWithBLOBs.setModuleId(apiDefinitionWithBLOBs.getModuleId()); + definitionWithBLOBs.setModulePath(apiDefinitionWithBLOBs.getModulePath()); + definitionWithBLOBs.setUpdateTime(System.currentTimeMillis()); + updateApiList.add(definitionWithBLOBs); + } + apiDefinitionWithBLOBs.setVersionId("trash"); + } else { + addNewVersionApi(apiDefinitionWithBLOBs, latestApi, "update"); + for (ApiDefinitionWithBLOBs definitionWithBLOBs : v) { + definitionWithBLOBs.setModuleId(apiDefinitionWithBLOBs.getModuleId()); + definitionWithBLOBs.setModulePath(apiDefinitionWithBLOBs.getModulePath()); + definitionWithBLOBs.setUpdateTime(System.currentTimeMillis()); + updateApiList.add(definitionWithBLOBs); + } + } + } + } else { + for (ApiDefinitionWithBLOBs definitionWithBLOBs : v) { + definitionWithBLOBs.setModuleId(apiDefinitionWithBLOBs.getModuleId()); + definitionWithBLOBs.setModulePath(apiDefinitionWithBLOBs.getModulePath()); + definitionWithBLOBs.setUpdateTime(System.currentTimeMillis()); + updateApiList.add(definitionWithBLOBs); + } + optionData.remove(apiDefinitionWithBLOBs); + } + } + }); + buildOtherParam(toUpdateList, optionData, coverApiList, updateApiList); + } + + private static Map judgeModuleMap(Map moduleMap, Map methodPathMap, Map> repeatDataMap) { + Set repeatKeys = repeatDataMap.keySet(); + Set importKeys = methodPathMap.keySet(); + List repeatKeyList = new ArrayList<>(repeatKeys); + List importKeysList = new ArrayList<>(importKeys); + List intersection = repeatKeyList.stream().filter(importKeysList::contains).collect(Collectors.toList()); + if (intersection.size() == importKeysList.size()) { + //导入文件没有新增接口无需创建接口模块 + moduleMap = new HashMap<>(); + } + return moduleMap; + } + private static void addNewVersionApi(ApiDefinitionWithBLOBs apiDefinitionWithBLOBs, ApiDefinitionWithBLOBs v, String version) { + apiDefinitionWithBLOBs.setVersionId(version); + apiDefinitionWithBLOBs.setNum(v.getNum()); + apiDefinitionWithBLOBs.setStatus(v.getStatus()); + apiDefinitionWithBLOBs.setOrder(v.getOrder()); + apiDefinitionWithBLOBs.setRefId(v.getRefId()); + apiDefinitionWithBLOBs.setLatest(v.getLatest()); + } + + + public static int getNextNum(String projectId, ExtApiDefinitionMapper extApiDefinitionMapper) { + ApiDefinition apiDefinition = extApiDefinitionMapper.getNextNum(projectId); + if (apiDefinition == null || apiDefinition.getNum() == null) { + return 100001; + } else { + return Optional.of(apiDefinition.getNum() + 1).orElse(100001); + } + } + + private ApiImportSendNoticeDTO importCreate(ApiDefinitionMapper batchMapper, ApiDefinitionImportParamDTO apiDefinitionImportParamDTO) { + ApiImportSendNoticeDTO apiImportSendNoticeDTO = new ApiImportSendNoticeDTO(); + SaveApiDefinitionRequest saveReq = new SaveApiDefinitionRequest(); + ApiDefinitionWithBLOBs apiDefinition = apiDefinitionImportParamDTO.getApiDefinition(); + BeanUtils.copyBean(saveReq, apiDefinition); + + if (StringUtils.isEmpty(apiDefinition.getStatus())) { + apiDefinition.setStatus(ApiTestDataStatus.UNDERWAY.getValue()); + } + if (apiDefinition.getUserId() == null) { + apiDefinition.setUserId(Objects.requireNonNull(SessionUtils.getUser()).getId()); + } + + apiDefinition.setDescription(apiDefinition.getDescription()); + List collect = apiDefinitionImportParamDTO.getUpdateList().stream().filter(t -> t.getId().equals(apiDefinition.getId())).collect(Collectors.toList()); + apiDefinitionImportParamDTO.setUpdateList(collect); + ApiTestImportRequest apiTestImportRequest = apiDefinitionImportParamDTO.getApiTestImportRequest(); + List mocks = apiDefinitionImportParamDTO.getMocks(); + List caseList = apiDefinitionImportParamDTO.getCaseList(); + if (StringUtils.equals("fullCoverage", apiTestImportRequest.getModeId())) { + return _importCreate(batchMapper, apiDefinitionImportParamDTO); + } else if (StringUtils.equals("incrementalMerge", apiTestImportRequest.getModeId())) { + if (CollectionUtils.isEmpty(collect)) { + String originId = apiDefinition.getId(); + apiDefinition.setId(UUID.randomUUID().toString()); + apiDefinition.setCreateTime(System.currentTimeMillis()); + apiDefinition.setUpdateTime(System.currentTimeMillis()); + //postman 可能含有前置脚本,接口定义去掉脚本 + if (apiDefinition.getVersionId() != null && apiDefinition.getVersionId().equals("new")) { + apiDefinition.setLatest(apiTestImportRequest.getVersionId().equals(apiTestImportRequest.getDefaultVersion())); + } else { + apiDefinition.setOrder(getImportNextOrder(apiTestImportRequest.getProjectId())); + apiDefinition.setRefId(apiDefinition.getId()); + apiDefinition.setLatest(true); // 新增的接口 latest = true + } + if (StringUtils.isNotEmpty(apiTestImportRequest.getVersionId())) { + apiDefinition.setVersionId(apiTestImportRequest.getVersionId()); + } else { + apiDefinition.setVersionId(apiTestImportRequest.getDefaultVersion()); + } + boolean newCreate = !StringUtils.equals(ApiImportPlatform.Swagger2.name(), apiDefinitionImportParamDTO.getApiTestImportRequest().getPlatform()) + && !StringUtils.isNotBlank(apiDefinitionImportParamDTO.getApiTestImportRequest().getSwaggerUrl()) + && !StringUtils.equals("idea", apiDefinitionImportParamDTO.getApiTestImportRequest().getOrigin()); + caseList = setRequestAndAddNewCase(apiDefinition, caseList, newCreate); + reSetImportMocksApiId(mocks, originId, apiDefinition.getId(), apiDefinition.getNum()); + batchMapper.insert(apiDefinition); + List apiTestCaseDTOS = importCase(apiDefinition, caseList); + apiImportSendNoticeDTO.setCaseDTOList(apiTestCaseDTOS); + updateExitData(batchMapper, apiDefinitionImportParamDTO, apiDefinition); + /* extApiDefinitionMapper.clearLatestVersion(apiDefinition.getRefId()); + extApiDefinitionMapper.addLatestVersion(apiDefinition.getRefId());*/ + ApiDefinitionResult apiDefinitionResult = ApiDefinitionImportUtil.getApiDefinitionResult(apiDefinition, false); + apiImportSendNoticeDTO.setApiDefinitionResult(apiDefinitionResult); + return apiImportSendNoticeDTO; + } else { + //不覆盖的接口,如果没有sameRequest则不导入。此时清空mock信息 + mocks.clear(); + return null; + } + } else { + return _importCreate(batchMapper, apiDefinitionImportParamDTO); + } + } + + private static void updateExitData(ApiDefinitionMapper batchMapper, ApiDefinitionImportParamDTO apiDefinitionImportParamDTO, ApiDefinitionWithBLOBs apiDefinition) { + if (apiDefinition.getLatest()) { + List repeatList = apiDefinitionImportParamDTO.getRepeatList(); + for (ApiDefinitionWithBLOBs apiDefinitionWithBLOBs : repeatList) { + if (apiDefinitionWithBLOBs.getLatest()) { + apiDefinitionWithBLOBs.setLatest(false); + } + batchMapper.updateByPrimaryKey(apiDefinitionWithBLOBs); + } + } else { + List repeatList = apiDefinitionImportParamDTO.getRepeatList(); + for (ApiDefinitionWithBLOBs apiDefinitionWithBLOBs : repeatList) { + batchMapper.updateByPrimaryKey(apiDefinitionWithBLOBs); + } + } + } + + private List importCase(ApiDefinitionWithBLOBs apiDefinition, List caseList) { + if (CollectionUtils.isEmpty(caseList)) { + return new ArrayList<>(); + } + List apiTestCaseDTOS = new ArrayList<>(); + for (int i = 0; i < caseList.size(); i++) { + ApiTestCaseDTO apiTestCaseDTO = new ApiTestCaseDTO(); + ApiTestCaseWithBLOBs apiTestCaseWithBLOBs = caseList.get(i); + apiTestCaseWithBLOBs.setApiDefinitionId(apiDefinition.getId()); + apiTestCaseWithBLOBs.setToBeUpdated(apiDefinition.getToBeUpdated() != null && apiDefinition.getToBeUpdated() && StringUtils.equalsIgnoreCase(apiTestCaseWithBLOBs.getVersionId(), "old_case")); + apiTestCaseWithBLOBs.setVersionId(apiDefinition.getVersionId()); + if (apiTestCaseWithBLOBs.getCreateTime() == null) { + apiTestCaseWithBLOBs.setCreateTime(System.currentTimeMillis()); + } + apiTestCaseWithBLOBs.setUpdateTime(System.currentTimeMillis()); + + if (StringUtils.isBlank(apiTestCaseWithBLOBs.getCaseStatus())) { + apiTestCaseWithBLOBs.setCaseStatus(ApiTestDataStatus.PREPARE.getValue()); + } + if (StringUtils.isBlank(apiTestCaseWithBLOBs.getCreateUserId())) { + apiTestCaseWithBLOBs.setCreateUserId(apiDefinition.getUserId()); + } + if (apiTestCaseWithBLOBs.getOrder() == null) { + apiTestCaseWithBLOBs.setOrder(getImportNextCaseOrder(apiDefinition.getProjectId())); + } + if (apiTestCaseWithBLOBs.getNum() == null) { + apiTestCaseWithBLOBs.setNum(apiTestCaseService.getNextNum(apiDefinition.getId(), apiDefinition.getNum() + i, apiDefinition.getProjectId())); + } + + if (apiDefinition.getToBeUpdateTime() != null) { + apiTestCaseWithBLOBs.setToBeUpdateTime(apiDefinition.getToBeUpdateTime()); + } + + if (StringUtils.isBlank(apiTestCaseWithBLOBs.getPriority())) { + apiTestCaseWithBLOBs.setPriority("P0"); + } + apiTestCaseWithBLOBs.setStatus(StringUtils.EMPTY); + + if (StringUtils.isNotBlank(apiTestCaseWithBLOBs.getId())) { + BeanUtils.copyBean(apiTestCaseDTO, apiTestCaseWithBLOBs); + apiTestCaseDTO.setUpdated(true); + apiTestCaseMapper.updateByPrimaryKeyWithBLOBs(apiTestCaseWithBLOBs); + } else { + apiTestCaseWithBLOBs.setId(UUID.randomUUID().toString()); + apiTestCaseWithBLOBs.setCreateUserId(Objects.requireNonNull(SessionUtils.getUser()).getId()); + apiTestCaseWithBLOBs.setUpdateUserId(Objects.requireNonNull(SessionUtils.getUser()).getId()); + BeanUtils.copyBean(apiTestCaseDTO, apiTestCaseWithBLOBs); + apiTestCaseDTO.setUpdated(false); + apiTestCaseMapper.insert(apiTestCaseWithBLOBs); + } + apiTestCaseDTOS.add(apiTestCaseDTO); + } + return apiTestCaseDTOS; + } + + private List dealRepeat(ApiTestImportRequest request, List optionData, Map moduleMap, Boolean fullCoverage, Map idPathMap, ApiModuleDTO chooseModule, + Map esbApiParamsMap, List toUpdateList, List optionDataCases) { + String chooseModuleId = request.getModuleId(); + List repeatApiDefinitionWithBLOBs = getApiDefinitionWithBLOBsList(request, optionData); + //重复接口的case + Map> oldCaseMap = new HashMap<>(); + if (CollectionUtils.isNotEmpty(repeatApiDefinitionWithBLOBs)) { + oldCaseMap = ApiDefinitionImportUtil.getOldCaseMap(repeatApiDefinitionWithBLOBs, apiTestCaseMapper); + } + Map> repeatDataMap = null; + Map optionMap = new HashMap<>(); + + if (chooseModule != null) { + String chooseModuleParentId = getChooseModuleParentId(chooseModule); + String chooseModulePath = getChooseModulePath(idPathMap, chooseModule, chooseModuleParentId); + if (fullCoverage) { + List singleOptionData = new ArrayList<>(); + removeOtherChooseModuleRepeat(optionData, singleOptionData, chooseModulePath); + optionData = singleOptionData; + optionMap = optionData.stream().collect(Collectors.toMap(t -> t.getName().concat(chooseModulePath), api -> api)); + } else { + getNoHChooseModuleUrlRepeatOptionMap(optionData, optionMap, chooseModulePath); + } + repeatDataMap = repeatApiDefinitionWithBLOBs.stream().filter(t -> t.getModuleId().equals(chooseModuleId)).collect(Collectors.groupingBy(t -> t.getName().concat(t.getModulePath()))); + } else { + buildOptionMap(optionData, optionMap); + repeatDataMap = repeatApiDefinitionWithBLOBs.stream().collect(Collectors.groupingBy(t -> t.getName().concat(t.getModulePath()))); + } + Boolean fullCoverageApi = getFullCoverageApi(request); + String updateVersionId = getUpdateVersionId(request); + //处理数据 + if (fullCoverage) { + if (fullCoverageApi) { + startCoverModule(toUpdateList, optionData, optionMap, repeatDataMap, updateVersionId, optionDataCases, oldCaseMap,esbApiParamsMap); + } else { + //过滤同一层级重复模块,导入文件没有新增接口无需创建接口模块 + moduleMap = judgeModuleMap(moduleMap, optionMap, repeatDataMap); + startCover(toUpdateList, optionData, optionMap, repeatDataMap, updateVersionId, optionDataCases, oldCaseMap,esbApiParamsMap); + } + } else { + //不覆盖 + if (CollectionUtils.isNotEmpty(repeatApiDefinitionWithBLOBs)) { + removeSameData(repeatDataMap, optionMap, optionData, moduleMap, optionDataCases); + } + } + + //系统内检查重复 + if (CollectionUtils.isNotEmpty(repeatApiDefinitionWithBLOBs)) { + repeatDataMap = repeatApiDefinitionWithBLOBs.stream().collect(Collectors.groupingBy(t -> t.getName().concat(t.getModulePath()))); + optionMap = optionData.stream().collect(Collectors.toMap(t -> t.getName().concat(t.getModulePath()), api -> api)); + if (fullCoverage) { + startCover(toUpdateList, optionData, optionMap, repeatDataMap, updateVersionId, optionDataCases, oldCaseMap,esbApiParamsMap); + } else { + //不覆盖,同一接口不做更新 + if (CollectionUtils.isNotEmpty(repeatApiDefinitionWithBLOBs)) { + removeSameData(repeatDataMap, optionMap, optionData, moduleMap, optionDataCases); + } + } + } + + if (optionData.isEmpty()) { + moduleMap = new HashMap<>(); + } + //将原来的case和更改的case组合在一起,为了同步的设置 + List caseIds = optionDataCases.stream().map(ApiTestCase::getId).filter(StringUtils::isNotBlank).collect(Collectors.toList()); + buildCases(optionDataCases, oldCaseMap, caseIds); + + return repeatApiDefinitionWithBLOBs; + } + + private static void removeOtherChooseModuleRepeat(List optionData, List singleOptionData, String chooseModulePath) { + LinkedHashMap> methodPathMap = optionData.stream().collect(Collectors.groupingBy(t -> t.getName().concat(chooseModulePath), LinkedHashMap::new, Collectors.toList())); + methodPathMap.forEach((k, v) -> { + singleOptionData.add(v.get(v.size() - 1)); + }); + } + + private static void buildOptionMap(List optionData, Map optionMap) { + for (ApiDefinitionWithBLOBs optionDatum : optionData) { + if (optionDatum.getModulePath() == null) { + optionMap.put(optionDatum.getName(), optionDatum); + } else { + optionMap.put(optionDatum.getName().concat(optionDatum.getModulePath()), optionDatum); + } + } + } + + private static void getNoHChooseModuleUrlRepeatOptionMap(List optionData, Map optionMap, String chooseModulePath) { + for (ApiDefinitionWithBLOBs optionDatum : optionData) { + if (optionDatum.getModulePath() == null) { + optionMap.put(optionDatum.getName().concat(chooseModulePath), optionDatum); + } else { + optionMap.put(optionDatum.getName().concat(chooseModulePath).concat(optionDatum.getModulePath()), optionDatum); + } + } + } + + private List getApiDefinitionWithBLOBsList(ApiTestImportRequest request, List optionData) { + //处理数据 + List nameList = optionData.stream().map(ApiDefinitionWithBLOBs::getName).collect(Collectors.toList()); + String projectId = request.getProjectId(); + String protocol = request.getProtocol(); + //获取系统内重复数据 + return extApiDefinitionMapper.selectRepeatByProtocol(nameList, protocol, projectId); + } + + private static void startCover(List toUpdateList, List optionData, + Map methodPathMap, Map> repeatDataMap, + String updateVersionId, List optionDataCases, + Map> oldCaseMap, Map esbApiParamsMap) { + List coverApiList = new ArrayList<>(); + List updateApiList = new ArrayList<>(); + repeatDataMap.forEach((k, v) -> { + ApiDefinitionWithBLOBs apiDefinitionWithBLOBs = methodPathMap.get(k); + if (apiDefinitionWithBLOBs != null) { + //该接口的case + Map> definitionIdCaseMAp = optionDataCases.stream().collect(Collectors.groupingBy(ApiTestCase::getApiDefinitionId)); + Map caseNameMap = getDistinctCaseNameMap(definitionIdCaseMAp, apiDefinitionWithBLOBs); + int i = 0; + ApiDefinitionWithBLOBs latestApi = null; + for (ApiDefinitionWithBLOBs definitionWithBLOBs : v) { + if (definitionWithBLOBs.getLatest()) { + latestApi = definitionWithBLOBs; + } + if (!definitionWithBLOBs.getVersionId().equals(updateVersionId)) { + i += 1; + continue; + } + updateEsb(esbApiParamsMap, definitionWithBLOBs.getId(), apiDefinitionWithBLOBs.getId()); + //组合case + if (MapUtils.isNotEmpty(caseNameMap)) { + buildCaseList(oldCaseMap, caseNameMap, definitionWithBLOBs, optionDataCases); + } + + ApiDefinitionWithBLOBs api = new ApiDefinitionWithBLOBs(); + BeanUtils.copyBean(api, apiDefinitionWithBLOBs); + api.setId(definitionWithBLOBs.getId()); + api.setModuleId(definitionWithBLOBs.getModuleId()); + api.setModulePath(definitionWithBLOBs.getModulePath()); + setApiParam(api, updateVersionId, definitionWithBLOBs); + coverApiList.add(api); + updateApiList.add(definitionWithBLOBs); + } + if (i == v.size()) { + if (latestApi!=null) { + Boolean hasChange = ApiDefinitionImportUtil.checkIsSynchronize(latestApi, apiDefinitionWithBLOBs); + if (!hasChange) { + optionData.remove(apiDefinitionWithBLOBs); + } else { + //如果系统内的所有版本都不是当前选择的数据更新版本,则在数据更新版本这里新建数据 + //未开启模块覆盖 指定版本没有数据 请求参数数据不一致 将接口内容创建到系统原模块中的接口指定版本 + addNewVersionApi(apiDefinitionWithBLOBs, latestApi, "update"); + apiDefinitionWithBLOBs.setModuleId(latestApi.getModuleId()); + apiDefinitionWithBLOBs.setModulePath(latestApi.getModulePath()); + } + } + } else { + optionData.remove(apiDefinitionWithBLOBs); + } + } + }); + buildOtherParam(toUpdateList, optionData, coverApiList, updateApiList); + } + + private static Map getDistinctCaseNameMap(Map> definitionIdCaseMAp, ApiDefinitionWithBLOBs apiDefinitionWithBLOBs) { + if (MapUtils.isEmpty(definitionIdCaseMAp)) { + return null; + } + List caseWithBLOBs = definitionIdCaseMAp.get(apiDefinitionWithBLOBs.getId()); + if (CollectionUtils.isNotEmpty(caseWithBLOBs)) { + return caseWithBLOBs.stream().filter(t -> !StringUtils.equalsIgnoreCase("old_case", t.getVersionId())).collect(Collectors.toMap(ApiTestCase::getName, testCase -> testCase)); + } else { + return null; + } + } + + private static void buildCaseList(Map> oldCaseMap, + Map caseNameMap, + ApiDefinitionWithBLOBs definitionWithBLOBs, List optionDataCases) { + //找出系统内重复接口的case,表里可能一个接口有多个同名case的可能 + List oldApiTestCases = oldCaseMap.get(definitionWithBLOBs.getId()); + + Map> oldCaseNameMap; + //如果同名重复用例有多个,则覆盖最后的那个 + if (CollectionUtils.isNotEmpty(oldApiTestCases)) { + oldCaseNameMap = oldApiTestCases.stream().collect(Collectors.groupingBy(ApiTestCase::getName)); + caseNameMap.forEach((name, importCaseWithBLOBs) -> { + //如果导入的有重名,覆盖,接口ID替换成系统内的 + importCaseWithBLOBs.setApiDefinitionId(definitionWithBLOBs.getId()); + List caseWithBLOBs = oldCaseNameMap.get(name); + if (CollectionUtils.isNotEmpty(caseWithBLOBs)) { + for (int i = 0; i < caseWithBLOBs.size(); i++) { + int version = 0; + if (caseWithBLOBs.get(i).getVersion() != null) { + version = caseWithBLOBs.get(i).getVersion() + 1; + } + if (i == 0) { + //被覆盖数据 + importCaseWithBLOBs.setId(caseWithBLOBs.get(i).getId()); + importCaseWithBLOBs.setNum(caseWithBLOBs.get(i).getNum()); + importCaseWithBLOBs.setVersion(version); + importCaseWithBLOBs.setCreateUserId(caseWithBLOBs.get(i).getCreateUserId()); + importCaseWithBLOBs.setUpdateUserId(caseWithBLOBs.get(i).getCreateUserId()); + } else { + //同名的旧数据处理 + caseWithBLOBs.get(i).setVersionId("old_case"); + optionDataCases.add(caseWithBLOBs.get(i)); + } + } + oldCaseNameMap.remove(name); + } + }); + //不同名的旧数据处理 + oldCaseNameMap.forEach((k, v) -> { + if (CollectionUtils.isNotEmpty(v)) { + for (ApiTestCaseWithBLOBs apiTestCaseWithBLOBs : v) { + apiTestCaseWithBLOBs.setVersionId("old_case"); + optionDataCases.add(apiTestCaseWithBLOBs); + } + } + }); + } else { + //否则直接给新增用例赋值新的接口ID + caseNameMap.forEach((name, caseWithBLOBs1) -> { + caseWithBLOBs1.setApiDefinitionId(definitionWithBLOBs.getId()); + caseWithBLOBs1.setVersion(0); + }); + } + } + + private static void setApiParam(ApiDefinitionWithBLOBs apiDefinitionWithBLOBs, String versionId, ApiDefinitionWithBLOBs definitionWithBLOBs) { + apiDefinitionWithBLOBs.setVersionId(versionId); + apiDefinitionWithBLOBs.setNum(definitionWithBLOBs.getNum()); + apiDefinitionWithBLOBs.setStatus(definitionWithBLOBs.getStatus()); + apiDefinitionWithBLOBs.setOrder(definitionWithBLOBs.getOrder()); + apiDefinitionWithBLOBs.setRefId(definitionWithBLOBs.getRefId()); + apiDefinitionWithBLOBs.setLatest(definitionWithBLOBs.getLatest()); + apiDefinitionWithBLOBs.setCreateTime(definitionWithBLOBs.getCreateTime()); + apiDefinitionWithBLOBs.setUpdateTime(definitionWithBLOBs.getUpdateTime()); + } + + private static void buildOtherParam(List toUpdateList, List optionData, List coverApiList, List updateApiList) { + optionData.addAll(coverApiList); + toUpdateList.addAll(updateApiList); + + } + + private void removeSameData(Map> repeatDataMap, Map methodPathMap, + List optionData, Map moduleMap, + List optionDataCases) { + + Map> moduleOptionData = optionData.stream().collect(Collectors.groupingBy(ApiDefinition::getModulePath)); + repeatDataMap.forEach((k, v) -> { + ApiDefinitionWithBLOBs apiDefinitionWithBLOBs = methodPathMap.get(k); + if (apiDefinitionWithBLOBs != null) { + Map> definitionIdCaseMAp = optionDataCases.stream().collect(Collectors.groupingBy(ApiTestCase::getApiDefinitionId)); + List distinctNameCases = definitionIdCaseMAp.get(apiDefinitionWithBLOBs.getId()); + String modulePath = apiDefinitionWithBLOBs.getModulePath(); + List moduleData = moduleOptionData.get(modulePath); + if (moduleData != null && moduleData.size() <= 1) { + moduleMap.remove(modulePath); + removeModulePath(moduleMap, moduleOptionData, modulePath); + moduleData.remove(apiDefinitionWithBLOBs); + } + //不覆盖同一接口不做更新 注意原接口的update时间不变 + optionData.remove(apiDefinitionWithBLOBs); + if (CollectionUtils.isNotEmpty(distinctNameCases)) { + distinctNameCases.forEach(optionDataCases::remove); + } + } + }); + } + + private void removeModulePath(Map moduleMap, Map> moduleOptionData, String modulePath) { + if (StringUtils.isBlank(modulePath)) { + return; + } + String[] pathTree = apiModuleService.getPathTree(modulePath); + String lastPath = pathTree[pathTree.length - 1]; + String substring = modulePath.substring(0, modulePath.indexOf("/" + lastPath)); + if (moduleOptionData.get(substring) == null || moduleOptionData.get(substring).size() == 0) { + moduleMap.remove(substring); + removeModulePath(moduleMap, moduleOptionData, substring); + } + + } + + private static void buildCases(List optionDataCases, Map> oldCaseMap, List caseIds) { + if (MapUtils.isNotEmpty(oldCaseMap)) { + List oldCaseList = new ArrayList<>(); + Collection> values = oldCaseMap.values(); + for (List value : values) { + oldCaseList.addAll(value); + } + List collect = oldCaseList.stream().filter(t -> !caseIds.contains(t.getId())).collect(Collectors.toList()); + optionDataCases.addAll(collect); + } + } + + + + private static void updateEsb(Map esbApiParamsMap, String newId, String oldId) { + if (MapUtils.isNotEmpty(esbApiParamsMap)) { + EsbApiParamsWithBLOBs esbApiParamsWithBLOBs = esbApiParamsMap.get(oldId); + if (esbApiParamsWithBLOBs != null) { + esbApiParamsMap.remove(oldId); + esbApiParamsWithBLOBs.setResourceId(newId); + esbApiParamsMap.put(newId, esbApiParamsWithBLOBs); + } + } + } + + private ApiImportSendNoticeDTO _importCreate(ApiDefinitionMapper batchMapper, ApiDefinitionImportParamDTO apiDefinitionImportParamDTO) { + ApiDefinitionWithBLOBs apiDefinition = apiDefinitionImportParamDTO.getApiDefinition(); + ApiTestImportRequest apiTestImportRequest = apiDefinitionImportParamDTO.getApiTestImportRequest(); + List sameRequest = apiDefinitionImportParamDTO.getUpdateList(); + List mocks = apiDefinitionImportParamDTO.getMocks(); + List caseList = apiDefinitionImportParamDTO.getCaseList(); + String originId = apiDefinition.getId(); + ApiImportSendNoticeDTO apiImportSendNoticeDTO = new ApiImportSendNoticeDTO(); + if (CollectionUtils.isEmpty(sameRequest)) { + // 没有这个接口 新增 + apiDefinition.setId(UUID.randomUUID().toString()); + + apiDefinition.setCreateTime(System.currentTimeMillis()); + apiDefinition.setUpdateTime(System.currentTimeMillis()); + if (apiDefinition.getVersionId() != null && apiDefinition.getVersionId().equals("update")) { + if (StringUtils.isNotEmpty(apiTestImportRequest.getUpdateVersionId())) { + apiDefinition.setVersionId(apiTestImportRequest.getUpdateVersionId()); + } else { + apiDefinition.setVersionId(apiTestImportRequest.getDefaultVersion()); + } + apiDefinition.setLatest(apiTestImportRequest.getVersionId().equals(apiTestImportRequest.getDefaultVersion())); + } else { + apiDefinition.setRefId(apiDefinition.getId()); + apiDefinition.setLatest(true); // 新增接口 latest = true + apiDefinition.setOrder(getImportNextOrder(apiTestImportRequest.getProjectId())); + if (StringUtils.isNotEmpty(apiTestImportRequest.getVersionId())) { + apiDefinition.setVersionId(apiTestImportRequest.getVersionId()); + } else { + apiDefinition.setVersionId(apiTestImportRequest.getDefaultVersion()); + } + } + + reSetImportMocksApiId(mocks, originId, apiDefinition.getId(), apiDefinition.getNum()); + boolean newCreate = !StringUtils.equals(ApiImportPlatform.Swagger2.name(), apiDefinitionImportParamDTO.getApiTestImportRequest().getPlatform()) + && !StringUtils.isNotBlank(apiDefinitionImportParamDTO.getApiTestImportRequest().getSwaggerUrl()) + && !StringUtils.equals("idea", apiDefinitionImportParamDTO.getApiTestImportRequest().getOrigin()); + caseList = setRequestAndAddNewCase(apiDefinition, caseList, newCreate); + batchMapper.insert(apiDefinition); + ApiDefinitionResult apiDefinitionResult = ApiDefinitionImportUtil.getApiDefinitionResult(apiDefinition, false); + apiImportSendNoticeDTO.setApiDefinitionResult(apiDefinitionResult); + List apiTestCaseDTOS = importCase(apiDefinition, caseList); + apiImportSendNoticeDTO.setCaseDTOList(apiTestCaseDTOS); + } else { //如果存在则修改 + if (StringUtils.isEmpty(apiTestImportRequest.getUpdateVersionId())) { + apiTestImportRequest.setUpdateVersionId(apiTestImportRequest.getDefaultVersion()); + } + Optional apiOp = sameRequest.stream().filter(api -> StringUtils.equals(api.getVersionId(), apiTestImportRequest.getUpdateVersionId())).findFirst(); + + if (apiOp.isEmpty()) { + apiDefinition.setId(UUID.randomUUID().toString()); + apiDefinition.setCreateTime(System.currentTimeMillis()); + apiDefinition.setUpdateTime(System.currentTimeMillis()); + if (!apiDefinition.getVersionId().equals("update")) { + if (sameRequest.get(0).getRefId() != null) { + apiDefinition.setRefId(sameRequest.get(0).getRefId()); + } else { + apiDefinition.setRefId(apiDefinition.getId()); + } + apiDefinition.setNum(sameRequest.get(0).getNum()); // 使用第一个num当作本次的num + apiDefinition.setOrder(sameRequest.get(0).getOrder()); + } + //apiDefinition.setLatest(apiTestImportRequest.getVersionId().equals(apiTestImportRequest.getDefaultVersion())); + apiDefinition.setVersionId(apiTestImportRequest.getUpdateVersionId()); + + if (sameRequest.get(0).getUserId() != null) { + apiDefinition.setUserId(sameRequest.get(0).getUserId()); + } + batchMapper.insert(apiDefinition); + ApiDefinitionResult apiDefinitionResult = ApiDefinitionImportUtil.getApiDefinitionResult(apiDefinition, false); + apiImportSendNoticeDTO.setApiDefinitionResult(apiDefinitionResult); + List apiTestCaseDTOS = importCase(apiDefinition, caseList); + apiImportSendNoticeDTO.setCaseDTOList(apiTestCaseDTOS); + } else { + ApiDefinitionWithBLOBs existApi = apiOp.get(); + apiDefinition.setStatus(existApi.getStatus()); + apiDefinition.setOriginalState(existApi.getOriginalState()); + apiDefinition.setCaseStatus(existApi.getCaseStatus()); + apiDefinition.setNum(existApi.getNum()); //id 不变 + if (existApi.getRefId() != null) { + apiDefinition.setRefId(existApi.getRefId()); + } else { + apiDefinition.setRefId(apiDefinition.getId()); + } + apiDefinition.setVersionId(apiTestImportRequest.getUpdateVersionId()); + if (existApi.getUserId() != null) { + apiDefinition.setUserId(existApi.getUserId()); + } + apiDefinition.setUpdateTime(System.currentTimeMillis()); + if (!StringUtils.equalsIgnoreCase(apiTestImportRequest.getPlatform(), ApiImportPlatform.Metersphere.name())) { + apiDefinition.setTags(existApi.getTags()); // 其他格式 tag 不变,MS 格式替换 + } + apiDefinition.setId(existApi.getId()); + setRequestAndAddNewCase(apiDefinition, caseList, false); + apiDefinition.setOrder(existApi.getOrder()); + reSetImportMocksApiId(mocks, originId, apiDefinition.getId(), apiDefinition.getNum()); + batchMapper.updateByPrimaryKeyWithBLOBs(apiDefinition); + ApiDefinitionResult apiDefinitionResult = ApiDefinitionImportUtil.getApiDefinitionResult(apiDefinition, true); + apiImportSendNoticeDTO.setApiDefinitionResult(apiDefinitionResult); + List apiTestCaseDTOS = importCase(apiDefinition, caseList); + apiImportSendNoticeDTO.setCaseDTOList(apiTestCaseDTOS); + } + } + updateExitData(batchMapper, apiDefinitionImportParamDTO, apiDefinition); + /*extApiDefinitionMapper.clearLatestVersion(apiDefinition.getRefId()); + extApiDefinitionMapper.addLatestVersion(apiDefinition.getRefId());*/ + return apiImportSendNoticeDTO; + } + + + + + private static List setRequestAndAddNewCase(ApiDefinitionWithBLOBs apiDefinition, List caseList, boolean newCreate) { + boolean createCase = false; + if (StringUtils.equalsIgnoreCase(apiDefinition.getProtocol(), RequestTypeConstants.HTTP)) { + createCase = setImportHashTree(apiDefinition); + } else if (StringUtils.equalsIgnoreCase(apiDefinition.getProtocol(), RequestTypeConstants.TCP)) { + createCase = setImportTCPHashTree(apiDefinition); + } + if (newCreate && createCase && CollectionUtils.isEmpty(caseList)) { + ApiTestCaseWithBLOBs apiTestCaseWithBLOBs = ApiDefinitionImportUtil.addNewCase(apiDefinition); + caseList = new ArrayList<>(); + caseList.add(apiTestCaseWithBLOBs); + } + return caseList; + } + + private static void reSetImportMocksApiId(List mocks, String originId, String newId, int apiNum) { + if (CollectionUtils.isNotEmpty(mocks)) { + int index = 1; + for (MockConfigImportDTO item : mocks) { + if (StringUtils.equals(item.getApiId(), originId)) { + item.setApiId(newId); + } + item.setExpectNum(apiNum + "_" + index); + index++; + } + } + } + + + private static boolean setImportHashTree(ApiDefinitionWithBLOBs apiDefinition) { + String request = apiDefinition.getRequest(); + JSONObject jsonObject = JSONUtil.parseObject(request); + ElementUtil.dataFormatting(jsonObject); + MsHTTPSamplerProxy msHTTPSamplerProxy = JSONUtil.parseObject(jsonObject.toString(), MsHTTPSamplerProxy.class); + boolean createCase = CollectionUtils.isNotEmpty(msHTTPSamplerProxy.getHeaders()); + if (CollectionUtils.isNotEmpty(msHTTPSamplerProxy.getArguments()) && !createCase) { + createCase = true; + } + if (msHTTPSamplerProxy.getBody() != null && !createCase) { + createCase = true; + } + if (CollectionUtils.isNotEmpty(msHTTPSamplerProxy.getRest()) && !createCase) { + createCase = true; + } + msHTTPSamplerProxy.setId(apiDefinition.getId()); + msHTTPSamplerProxy.setHashTree(new LinkedList<>()); + apiDefinition.setRequest(JSON.toJSONString(msHTTPSamplerProxy)); + return createCase; + } + + private static boolean setImportTCPHashTree(ApiDefinitionWithBLOBs apiDefinition) { + String request = apiDefinition.getRequest(); + MsTCPSampler tcpSampler = JSON.parseObject(request, MsTCPSampler.class); + boolean createCase = CollectionUtils.isNotEmpty(tcpSampler.getParameters()); + if (StringUtils.isNotBlank(tcpSampler.getJsonDataStruct()) && !createCase) { + createCase = true; + } + if (StringUtils.isNotBlank(tcpSampler.getRawDataStruct()) && !createCase) { + createCase = true; + } + if (CollectionUtils.isNotEmpty(tcpSampler.getXmlDataStruct()) && !createCase) { + createCase = true; + } + tcpSampler.setId(apiDefinition.getId()); + tcpSampler.setHashTree(new LinkedList<>()); + apiDefinition.setRequest(JSON.toJSONString(tcpSampler)); + return createCase; + } + + public Long getImportNextOrder(String projectId) { + Long order = currentApiOrder.get(); + if (order == null) { + order = ServiceUtils.getNextOrder(projectId, extApiDefinitionMapper::getLastOrder); + } + order = order + ServiceUtils.ORDER_STEP; + currentApiOrder.set(order); + return order; + } + + public Long getImportNextCaseOrder(String projectId) { + Long order = currentApiCaseOrder.get(); + if (order == null) { + order = ServiceUtils.getNextOrder(projectId, extApiTestCaseMapper::getLastOrder); + } + order = order + ServiceUtils.ORDER_STEP; + currentApiCaseOrder.set(order); + return order; + } +} diff --git a/api-test/backend/src/main/java/io/metersphere/service/definition/ApiDefinitionService.java b/api-test/backend/src/main/java/io/metersphere/service/definition/ApiDefinitionService.java index c0fdc1a410..ba18eeec83 100644 --- a/api-test/backend/src/main/java/io/metersphere/service/definition/ApiDefinitionService.java +++ b/api-test/backend/src/main/java/io/metersphere/service/definition/ApiDefinitionService.java @@ -1,9 +1,5 @@ package io.metersphere.service.definition; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; import com.github.pagehelper.Page; import com.github.pagehelper.PageHelper; import io.github.ningyu.jmeter.plugin.dubbo.sample.ProviderService; @@ -13,12 +9,10 @@ import io.metersphere.api.dto.automation.ApiScenarioRequest; import io.metersphere.api.dto.automation.TcpTreeTableDataStruct; import io.metersphere.api.dto.datacount.ApiDataCountResult; import io.metersphere.api.dto.definition.*; -import io.metersphere.api.dto.definition.request.ElementUtil; import io.metersphere.api.dto.definition.request.assertions.document.DocumentElement; import io.metersphere.api.dto.definition.request.sampler.MsHTTPSamplerProxy; import io.metersphere.api.dto.definition.request.sampler.MsJDBCSampler; import io.metersphere.api.dto.definition.request.sampler.MsTCPSampler; -import io.metersphere.api.dto.mock.config.MockConfigImportDTO; import io.metersphere.api.dto.swaggerurl.SwaggerTaskResult; import io.metersphere.api.dto.swaggerurl.SwaggerUrlRequest; import io.metersphere.api.exec.api.ApiExecuteService; @@ -46,7 +40,6 @@ import io.metersphere.log.vo.api.DefinitionReference; import io.metersphere.notice.sender.NoticeModel; import io.metersphere.notice.service.NoticeSendService; import io.metersphere.plugin.core.MsTestElement; -import io.metersphere.request.ApiSyncCaseRequest; import io.metersphere.request.RelationshipEdgeRequest; import io.metersphere.request.ResetOrderRequest; import io.metersphere.request.SyncApiDefinitionRequest; @@ -165,16 +158,18 @@ public class ApiDefinitionService { @Resource private BaseTestResourcePoolService baseTestResourcePoolService; - private final ThreadLocal currentApiOrder = new ThreadLocal<>(); - private final ThreadLocal currentApiCaseOrder = new ThreadLocal<>(); + @Resource + private ApiDefinitionImportUtilService apiDefinitionImportUtilService; + + private static final String COPY = "Copy"; - private static final String SCHEDULE = "schedule"; + public static final String HEADERS = "headers"; public static final String ARGUMENTS = "arguments"; - public static final String REST = "rest"; public static final String BODY = "body"; - public static final String JSONSCHEMA = "jsonSchema"; - public static final String PROPERTIES = "properties"; + private static final String SCHEDULE = "schedule"; + + public List list(ApiDefinitionRequest request) { request = this.initRequest(request, true, true); @@ -523,6 +518,56 @@ public class ApiDefinitionService { } } + public ApiDefinitionImport apiTestImport(MultipartFile file, ApiTestImportRequest request) { + //通过platform,获取对应的导入解析类型。 + if (file != null) { + String originalFilename = file.getOriginalFilename(); + if (StringUtils.isNotBlank(originalFilename)) { + String suffixName = originalFilename.substring(originalFilename.indexOf(".") + 1); + apiDefinitionImportUtilService.checkFileSuffixName(request, suffixName); + } + } + ApiImportParser runService = ApiDefinitionImportParserFactory.getApiImportParser(request.getPlatform()); + ApiDefinitionImport apiImport = null; + Project project = projectMapper.selectByPrimaryKey(request.getProjectId()); + apiDefinitionImportUtilService.checkUrl(request, project); + if (StringUtils.equals(request.getType(), SCHEDULE)) { + request.setProtocol("HTTP"); + } + try { + apiImport = (ApiDefinitionImport) Objects.requireNonNull(runService).parse(file == null ? null : file.getInputStream(), request); + if (apiImport.getMocks() == null) { + apiImport.setMocks(new ArrayList<>()); + } + } catch (MSException e) { + // 发送通知 + apiDefinitionImportUtilService.sendFailMessage(request, project); + throw e; + } catch (Exception e) { + // 发送通知 + apiDefinitionImportUtilService.sendFailMessage(request, project); + LogUtil.error(e.getMessage(), e); + MSException.throwException(Translator.get("parse_data_error")); + } + + try { + List apiImportSendNoticeDTOS = apiDefinitionImportUtilService.importApi(request, apiImport); + if (CollectionUtils.isNotEmpty(apiImport.getData())) { + List names = apiImport.getData().stream().map(ApiDefinitionWithBLOBs::getName).collect(Collectors.toList()); + request.setName(String.join(",", names)); + List ids = apiImport.getData().stream().map(ApiDefinitionWithBLOBs::getId).collect(Collectors.toList()); + request.setId(JSON.toJSONString(ids)); + } + // 发送通知 + apiDefinitionImportUtilService.sendImportNotice(request, apiImportSendNoticeDTOS, project); + } catch (Exception e) { + apiDefinitionImportUtilService.sendFailMessage(request, project); + LogUtil.error(e); + MSException.throwException(Translator.get("user_import_format_wrong")); + } + return apiImport; + } + public void reduction(ApiBatchRequest request) { ServiceUtils.getSelectAllIds(request, request.getCondition(), (query) -> extApiDefinitionMapper.selectIds(query)); if (request.getIds() != null || !request.getIds().isEmpty()) { @@ -885,667 +930,20 @@ public class ApiDefinitionService { } } - private ApiImportSendNoticeDTO importCreate(ApiDefinitionMapper batchMapper, ExtApiDefinitionMapper extApiDefinitionMapper, ApiTestCaseMapper apiTestCaseMapper, ApiDefinitionImportParamDTO apiDefinitionImportParamDTO) { - ApiImportSendNoticeDTO apiImportSendNoticeDTO = new ApiImportSendNoticeDTO(); - SaveApiDefinitionRequest saveReq = new SaveApiDefinitionRequest(); - ApiDefinitionWithBLOBs apiDefinition = apiDefinitionImportParamDTO.getApiDefinition(); - BeanUtils.copyBean(saveReq, apiDefinition); - - if (StringUtils.isEmpty(apiDefinition.getStatus())) { - apiDefinition.setStatus(ApiTestDataStatus.UNDERWAY.getValue()); - } - if (apiDefinition.getUserId() == null) { - apiDefinition.setUserId(Objects.requireNonNull(SessionUtils.getUser()).getId()); - } - - apiDefinition.setDescription(apiDefinition.getDescription()); - List collect = apiDefinitionImportParamDTO.getUpdateList().stream().filter(t -> t.getId().equals(apiDefinition.getId())).collect(Collectors.toList()); - apiDefinitionImportParamDTO.setUpdateList(collect); - ApiTestImportRequest apiTestImportRequest = apiDefinitionImportParamDTO.getApiTestImportRequest(); - List mocks = apiDefinitionImportParamDTO.getMocks(); - List caseList = apiDefinitionImportParamDTO.getCaseList(); - if (StringUtils.equals("fullCoverage", apiTestImportRequest.getModeId())) { - return _importCreate(batchMapper, extApiDefinitionMapper, apiTestCaseMapper, apiDefinitionImportParamDTO); - } else if (StringUtils.equals("incrementalMerge", apiTestImportRequest.getModeId())) { - if (CollectionUtils.isEmpty(collect)) { - String originId = apiDefinition.getId(); - apiDefinition.setId(UUID.randomUUID().toString()); - apiDefinition.setCreateTime(System.currentTimeMillis()); - apiDefinition.setUpdateTime(System.currentTimeMillis()); - //postman 可能含有前置脚本,接口定义去掉脚本 - if (apiDefinition.getVersionId() != null && apiDefinition.getVersionId().equals("new")) { - apiDefinition.setLatest(apiTestImportRequest.getVersionId().equals(apiTestImportRequest.getDefaultVersion())); - } else { - apiDefinition.setOrder(getImportNextOrder(apiTestImportRequest.getProjectId())); - apiDefinition.setRefId(apiDefinition.getId()); - apiDefinition.setLatest(true); // 新增的接口 latest = true - } - if (StringUtils.isNotEmpty(apiTestImportRequest.getVersionId())) { - apiDefinition.setVersionId(apiTestImportRequest.getVersionId()); - } else { - apiDefinition.setVersionId(apiTestImportRequest.getDefaultVersion()); - } - boolean newCreate = !StringUtils.equals(ApiImportPlatform.Swagger2.name(), apiDefinitionImportParamDTO.getApiTestImportRequest().getPlatform()) - && !StringUtils.isNotBlank(apiDefinitionImportParamDTO.getApiTestImportRequest().getSwaggerUrl()) - && !StringUtils.equals("idea", apiDefinitionImportParamDTO.getApiTestImportRequest().getOrigin()); - caseList = setRequestAndAddNewCase(apiDefinition, caseList, newCreate); - reSetImportMocksApiId(mocks, originId, apiDefinition.getId(), apiDefinition.getNum()); - batchMapper.insert(apiDefinition); - List apiTestCaseDTOS = importCase(apiDefinition, apiTestCaseMapper, caseList); - apiImportSendNoticeDTO.setCaseDTOList(apiTestCaseDTOS); - extApiDefinitionMapper.clearLatestVersion(apiDefinition.getRefId()); - extApiDefinitionMapper.addLatestVersion(apiDefinition.getRefId()); - ApiDefinitionResult apiDefinitionResult = getApiDefinitionResult(apiDefinition, false); - apiImportSendNoticeDTO.setApiDefinitionResult(apiDefinitionResult); - return apiImportSendNoticeDTO; - } else { - //不覆盖的接口,如果没有sameRequest则不导入。此时清空mock信息 - mocks.clear(); - return null; - } - } else { - return _importCreate(batchMapper, extApiDefinitionMapper, apiTestCaseMapper, apiDefinitionImportParamDTO); - } - } - - private ApiDefinitionResult getApiDefinitionResult(ApiDefinitionWithBLOBs apiDefinition, boolean isUpdate) { - ApiDefinitionResult apiDefinitionResult = new ApiDefinitionResult(); - BeanUtils.copyBean(apiDefinitionResult, apiDefinition); - apiDefinitionResult.setUpdated(isUpdate); - return apiDefinitionResult; - } - - private List importCase(ApiDefinitionWithBLOBs apiDefinition, ApiTestCaseMapper apiTestCaseMapper, List caseList) { - if (CollectionUtils.isEmpty(caseList)) { - return new ArrayList<>(); - } - List apiTestCaseDTOS = new ArrayList<>(); - for (int i = 0; i < caseList.size(); i++) { - ApiTestCaseDTO apiTestCaseDTO = new ApiTestCaseDTO(); - ApiTestCaseWithBLOBs apiTestCaseWithBLOBs = caseList.get(i); - apiTestCaseWithBLOBs.setApiDefinitionId(apiDefinition.getId()); - apiTestCaseWithBLOBs.setToBeUpdated(apiDefinition.getToBeUpdated() != null && apiDefinition.getToBeUpdated() && StringUtils.equalsIgnoreCase(apiTestCaseWithBLOBs.getVersionId(), "old_case")); - apiTestCaseWithBLOBs.setVersionId(apiDefinition.getVersionId()); - if (apiTestCaseWithBLOBs.getCreateTime() == null) { - apiTestCaseWithBLOBs.setCreateTime(System.currentTimeMillis()); - } - apiTestCaseWithBLOBs.setUpdateTime(System.currentTimeMillis()); - - if (StringUtils.isBlank(apiTestCaseWithBLOBs.getCaseStatus())) { - apiTestCaseWithBLOBs.setCaseStatus(ApiTestDataStatus.PREPARE.getValue()); - } - if (StringUtils.isBlank(apiTestCaseWithBLOBs.getCreateUserId())) { - apiTestCaseWithBLOBs.setCreateUserId(apiDefinition.getUserId()); - } - if (apiTestCaseWithBLOBs.getOrder() == null) { - apiTestCaseWithBLOBs.setOrder(getImportNextCaseOrder(apiDefinition.getProjectId())); - } - if (apiTestCaseWithBLOBs.getNum() == null) { - apiTestCaseWithBLOBs.setNum(apiTestCaseService.getNextNum(apiDefinition.getId(), apiDefinition.getNum() + i, apiDefinition.getProjectId())); - } - - if (apiDefinition.getToBeUpdateTime() != null) { - apiTestCaseWithBLOBs.setToBeUpdateTime(apiDefinition.getToBeUpdateTime()); - } - - if (StringUtils.isBlank(apiTestCaseWithBLOBs.getPriority())) { - apiTestCaseWithBLOBs.setPriority("P0"); - } - apiTestCaseWithBLOBs.setStatus(StringUtils.EMPTY); - - if (StringUtils.isNotBlank(apiTestCaseWithBLOBs.getId())) { - BeanUtils.copyBean(apiTestCaseDTO, apiTestCaseWithBLOBs); - apiTestCaseDTO.setUpdated(true); - apiTestCaseMapper.updateByPrimaryKeyWithBLOBs(apiTestCaseWithBLOBs); - } else { - apiTestCaseWithBLOBs.setId(UUID.randomUUID().toString()); - apiTestCaseWithBLOBs.setCreateUserId(Objects.requireNonNull(SessionUtils.getUser()).getId()); - apiTestCaseWithBLOBs.setUpdateUserId(Objects.requireNonNull(SessionUtils.getUser()).getId()); - BeanUtils.copyBean(apiTestCaseDTO, apiTestCaseWithBLOBs); - apiTestCaseDTO.setUpdated(false); - apiTestCaseMapper.insert(apiTestCaseWithBLOBs); - } - apiTestCaseDTOS.add(apiTestCaseDTO); - } - return apiTestCaseDTOS; - } - public Long getImportNextOrder(String projectId) { - Long order = currentApiOrder.get(); - if (order == null) { - order = ServiceUtils.getNextOrder(projectId, extApiDefinitionMapper::getLastOrder); - } - order = (order == null ? 0 : order) + ServiceUtils.ORDER_STEP; - currentApiOrder.set(order); - return order; - } - - public Long getImportNextCaseOrder(String projectId) { - Long order = currentApiCaseOrder.get(); - if (order == null) { - order = ServiceUtils.getNextOrder(projectId, extApiTestCaseMapper::getLastOrder); - } - order = order + ServiceUtils.ORDER_STEP; - currentApiCaseOrder.set(order); - return order; - } - - private ApiImportSendNoticeDTO _importCreate(ApiDefinitionMapper batchMapper, ExtApiDefinitionMapper extApiDefinitionMapper, ApiTestCaseMapper apiTestCaseMapper, ApiDefinitionImportParamDTO apiDefinitionImportParamDTO) { - ApiDefinitionWithBLOBs apiDefinition = apiDefinitionImportParamDTO.getApiDefinition(); - ApiTestImportRequest apiTestImportRequest = apiDefinitionImportParamDTO.getApiTestImportRequest(); - List sameRequest = apiDefinitionImportParamDTO.getUpdateList(); - List mocks = apiDefinitionImportParamDTO.getMocks(); - List caseList = apiDefinitionImportParamDTO.getCaseList(); - String originId = apiDefinition.getId(); - ApiImportSendNoticeDTO apiImportSendNoticeDTO = new ApiImportSendNoticeDTO(); - if (CollectionUtils.isEmpty(sameRequest)) { // 没有这个接口 新增 - apiDefinition.setId(UUID.randomUUID().toString()); - - apiDefinition.setCreateTime(System.currentTimeMillis()); - apiDefinition.setUpdateTime(System.currentTimeMillis()); - if (apiDefinition.getVersionId() != null && apiDefinition.getVersionId().equals("update")) { - if (StringUtils.isNotEmpty(apiTestImportRequest.getUpdateVersionId())) { - apiDefinition.setVersionId(apiTestImportRequest.getUpdateVersionId()); - } else { - apiDefinition.setVersionId(apiTestImportRequest.getDefaultVersion()); - } - apiDefinition.setLatest(apiTestImportRequest.getVersionId().equals(apiTestImportRequest.getDefaultVersion())); - } else { - apiDefinition.setRefId(apiDefinition.getId()); - apiDefinition.setLatest(true); // 新增接口 latest = true - apiDefinition.setOrder(getImportNextOrder(apiTestImportRequest.getProjectId())); - if (StringUtils.isNotEmpty(apiTestImportRequest.getVersionId())) { - apiDefinition.setVersionId(apiTestImportRequest.getVersionId()); - } else { - apiDefinition.setVersionId(apiTestImportRequest.getDefaultVersion()); - } - } - - reSetImportMocksApiId(mocks, originId, apiDefinition.getId(), apiDefinition.getNum()); - boolean newCreate = !StringUtils.equals(ApiImportPlatform.Swagger2.name(), apiDefinitionImportParamDTO.getApiTestImportRequest().getPlatform()) - && !StringUtils.isNotBlank(apiDefinitionImportParamDTO.getApiTestImportRequest().getSwaggerUrl()) - && !StringUtils.equals("idea", apiDefinitionImportParamDTO.getApiTestImportRequest().getOrigin()); - caseList = setRequestAndAddNewCase(apiDefinition, caseList, newCreate); - batchMapper.insert(apiDefinition); - ApiDefinitionResult apiDefinitionResult = getApiDefinitionResult(apiDefinition, false); - apiImportSendNoticeDTO.setApiDefinitionResult(apiDefinitionResult); - List apiTestCaseDTOS = importCase(apiDefinition, apiTestCaseMapper, caseList); - apiImportSendNoticeDTO.setCaseDTOList(apiTestCaseDTOS); - } else { //如果存在则修改 - if (StringUtils.isEmpty(apiTestImportRequest.getUpdateVersionId())) { - apiTestImportRequest.setUpdateVersionId(apiTestImportRequest.getDefaultVersion()); - } - Optional apiOp = sameRequest.stream().filter(api -> StringUtils.equals(api.getVersionId(), apiTestImportRequest.getUpdateVersionId())).findFirst(); - - if (!apiOp.isPresent()) { - apiDefinition.setId(UUID.randomUUID().toString()); - apiDefinition.setCreateTime(System.currentTimeMillis()); - apiDefinition.setUpdateTime(System.currentTimeMillis()); - if (!apiDefinition.getVersionId().equals("update")) { - if (sameRequest.get(0).getRefId() != null) { - apiDefinition.setRefId(sameRequest.get(0).getRefId()); - } else { - apiDefinition.setRefId(apiDefinition.getId()); - } - apiDefinition.setNum(sameRequest.get(0).getNum()); // 使用第一个num当作本次的num - apiDefinition.setOrder(sameRequest.get(0).getOrder()); - } - apiDefinition.setLatest(apiTestImportRequest.getVersionId().equals(apiTestImportRequest.getDefaultVersion())); - apiDefinition.setVersionId(apiTestImportRequest.getUpdateVersionId()); - - if (sameRequest.get(0).getUserId() != null) { - apiDefinition.setUserId(sameRequest.get(0).getUserId()); - } - batchMapper.insert(apiDefinition); - ApiDefinitionResult apiDefinitionResult = getApiDefinitionResult(apiDefinition, false); - apiImportSendNoticeDTO.setApiDefinitionResult(apiDefinitionResult); - List apiTestCaseDTOS = importCase(apiDefinition, apiTestCaseMapper, caseList); - apiImportSendNoticeDTO.setCaseDTOList(apiTestCaseDTOS); - } else { - ApiDefinitionWithBLOBs existApi = apiOp.get(); - apiDefinition.setStatus(existApi.getStatus()); - apiDefinition.setOriginalState(existApi.getOriginalState()); - apiDefinition.setCaseStatus(existApi.getCaseStatus()); - apiDefinition.setNum(existApi.getNum()); //id 不变 - if (existApi.getRefId() != null) { - apiDefinition.setRefId(existApi.getRefId()); - } else { - apiDefinition.setRefId(apiDefinition.getId()); - } - apiDefinition.setVersionId(apiTestImportRequest.getUpdateVersionId()); - if (existApi.getUserId() != null) { - apiDefinition.setUserId(existApi.getUserId()); - } - //Check whether the content has changed, if not, do not change the creation time - if (StringUtils.equalsIgnoreCase(apiDefinition.getProtocol(), RequestTypeConstants.HTTP)) { - Boolean toChangeTime = checkIsSynchronize(existApi, apiDefinition); - if (toChangeTime) { - apiDefinition.setUpdateTime(System.currentTimeMillis()); - } else if (apiTestImportRequest.getCoverModule() != null && apiTestImportRequest.getCoverModule()) { - apiDefinition.setUpdateTime(System.currentTimeMillis()); - } - - } else { - apiDefinition.setUpdateTime(System.currentTimeMillis()); - } - - if (!StringUtils.equalsIgnoreCase(apiTestImportRequest.getPlatform(), ApiImportPlatform.Metersphere.name())) { - apiDefinition.setTags(existApi.getTags()); // 其他格式 tag 不变,MS 格式替换 - } - apiDefinition.setId(existApi.getId()); - setRequestAndAddNewCase(apiDefinition, caseList, false); - apiDefinition.setOrder(existApi.getOrder()); - reSetImportMocksApiId(mocks, originId, apiDefinition.getId(), apiDefinition.getNum()); - batchMapper.updateByPrimaryKeyWithBLOBs(apiDefinition); - ApiDefinitionResult apiDefinitionResult = getApiDefinitionResult(apiDefinition, true); - apiImportSendNoticeDTO.setApiDefinitionResult(apiDefinitionResult); - List apiTestCaseDTOS = importCase(apiDefinition, apiTestCaseMapper, caseList); - apiImportSendNoticeDTO.setCaseDTOList(apiTestCaseDTOS); - } - } - extApiDefinitionMapper.clearLatestVersion(apiDefinition.getRefId()); - extApiDefinitionMapper.addLatestVersion(apiDefinition.getRefId()); - return apiImportSendNoticeDTO; - } - - private List setRequestAndAddNewCase(ApiDefinitionWithBLOBs apiDefinition, List caseList, boolean newCreate) { - boolean createCase = false; - if (StringUtils.equalsIgnoreCase(apiDefinition.getProtocol(), RequestTypeConstants.HTTP)) { - createCase = setImportHashTree(apiDefinition); - } else if (StringUtils.equalsIgnoreCase(apiDefinition.getProtocol(), RequestTypeConstants.TCP)) { - createCase = setImportTCPHashTree(apiDefinition); - } - if (newCreate && createCase && CollectionUtils.isEmpty(caseList)) { - ApiTestCaseWithBLOBs apiTestCaseWithBLOBs = addNewCase(apiDefinition); - caseList = new ArrayList<>(); - caseList.add(apiTestCaseWithBLOBs); - } - return caseList; - } - - public void sendImportApiNotice(ApiDefinitionWithBLOBs apiDefinitionWithBLOBs, String context, String event, String tip) { - BeanMap beanMap = new BeanMap(apiDefinitionWithBLOBs); - Map paramMap = new HashMap<>(beanMap); - paramMap.put("operator", SessionUtils.getUserId()); - NoticeModel noticeModel = NoticeModel.builder().operator(SessionUtils.getUserId()).context(context).testId(apiDefinitionWithBLOBs.getId()).subject(Translator.get(tip)).paramMap(paramMap).excludeSelf(true).event(event).build(); - noticeSendService.send(NoticeConstants.TaskType.API_DEFINITION_TASK, noticeModel); - } - - public void sendImportCaseNotice(ApiTestCase apiTestCase, String context, String event, String tip) { - BeanMap beanMap = new BeanMap(apiTestCase); - Map paramMap = new HashMap<>(beanMap); - paramMap.put("operator", SessionUtils.getUserId()); - NoticeModel noticeModel = NoticeModel.builder().operator(SessionUtils.getUserId()).context(context).testId(apiTestCase.getId()).subject(Translator.get(tip)).paramMap(paramMap).excludeSelf(true).event(event).build(); - noticeSendService.send(NoticeConstants.TaskType.API_DEFINITION_TASK, noticeModel); - } - - public Boolean checkIsSynchronize(ApiDefinitionWithBLOBs existApi, ApiDefinitionWithBLOBs apiDefinition) { - - ObjectMapper objectMapper = new ObjectMapper(); - objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - - ApiSyncCaseRequest apiSyncCaseRequest = new ApiSyncCaseRequest(); - Boolean toUpdate = false; - ApiDefinitionSyncService apiDefinitionSyncService = CommonBeanFactory.getBean(ApiDefinitionSyncService.class); - if (apiDefinitionSyncService != null) { - toUpdate = apiDefinitionSyncService.getProjectApplications(existApi.getProjectId()); - apiSyncCaseRequest = apiDefinitionSyncService.getApiSyncCaseRequest(existApi.getProjectId()); - } - //Compare the basic information of the API. If it contains the comparison that needs to be done for the synchronization information, - // put the data into the to-be-synchronized - Boolean diffBasicInfo = delBasicInfo(existApi, apiDefinition, apiSyncCaseRequest, toUpdate); - if (diffBasicInfo != null) return diffBasicInfo; - - Boolean diffApiRequest = delRequest(existApi, apiDefinition, objectMapper, apiSyncCaseRequest, toUpdate); - if (diffApiRequest != null) return diffApiRequest; - - Boolean diffResponse = delResponse(existApi, apiDefinition, objectMapper); - if (diffResponse != null) return diffResponse; - return false; - } - - private Boolean delRequest(ApiDefinitionWithBLOBs existApi, ApiDefinitionWithBLOBs apiDefinition, ObjectMapper objectMapper, ApiSyncCaseRequest apiSyncCaseRequest, Boolean toUpdate) { - JsonNode exApiRequest = null; - JsonNode apiRequest = null; - try { - exApiRequest = objectMapper.readTree(existApi.getRequest()); - apiRequest = objectMapper.readTree(apiDefinition.getRequest()); - } catch (JsonProcessingException e) { - e.printStackTrace(); - } - - if (exApiRequest == null || apiRequest == null) { - return exApiRequest != null || apiRequest != null; - } - - List compareProList = Arrays.asList(HEADERS, ARGUMENTS, REST); - - Map applicationMap = new HashMap<>(4); - applicationMap.put(HEADERS, apiSyncCaseRequest.getHeaders()); - applicationMap.put(ARGUMENTS, apiSyncCaseRequest.getQuery()); - applicationMap.put(REST, apiSyncCaseRequest.getRest()); - applicationMap.put(BODY, apiSyncCaseRequest.getBody()); - - boolean diffByNodes = false; - for (String property : compareProList) { - JsonNode exApiJsonNode = exApiRequest.get(property); - JsonNode apiJsonNode = apiRequest.get(property); - if (exApiJsonNode != null && apiJsonNode != null) { - diffByNodes = getDiffByArrayNodes(apiRequest, exApiRequest, objectMapper, property); - if (diffByNodes) { - if (toUpdate && applicationMap.get(property)) { - apiDefinition.setToBeUpdateTime(System.currentTimeMillis()); - } - break; - } - } - } - if (diffByNodes) { - return true; - } - return delBody(apiDefinition, objectMapper, toUpdate, exApiRequest, apiRequest, applicationMap); - } - - private Boolean delBody(ApiDefinitionWithBLOBs apiDefinition, ObjectMapper objectMapper, Boolean toUpdate, JsonNode exApiRequest, JsonNode apiRequest, Map applicationMap) { - JsonNode exBodyNode = exApiRequest.get(BODY); - JsonNode bodyNode = apiRequest.get(BODY); - if (exBodyNode != null && bodyNode != null) { - JsonNode exRowNode = exBodyNode.get("raw"); - JsonNode rowNode = bodyNode.get("raw"); - if (exRowNode != null && rowNode != null) { - if (!StringUtils.equals(exRowNode.asText(), rowNode.asText())) { - if (applicationMap.get(BODY)) { - apiDefinition.setToBeUpdateTime(System.currentTimeMillis()); - } - return true; - } - } - - boolean diffByNodes = getDiffByArrayNodes(bodyNode, exBodyNode, objectMapper, "kvs"); - if (diffByNodes && toUpdate && applicationMap.get(BODY)) { - apiDefinition.setToBeUpdateTime(System.currentTimeMillis()); - } - if (diffByNodes) { - return true; - } - JsonNode exApiJsonSchema = exBodyNode.get(JSONSCHEMA); - JsonNode apiJsonSchema = bodyNode.get(JSONSCHEMA); - if (exApiJsonSchema == null || apiJsonSchema == null) { - return false; - } - - JsonNode exApiProperties = exApiJsonSchema.get(PROPERTIES); - JsonNode apiProperties = apiJsonSchema.get(PROPERTIES); - if (exApiProperties == null || apiProperties == null) { - return false; - } - boolean diffJsonschema = replenishCaseProperties(exApiProperties, apiProperties); - if (diffJsonschema && toUpdate && applicationMap.get(BODY)) { - apiDefinition.setToBeUpdateTime(System.currentTimeMillis()); - } - return diffJsonschema; - } - return null; - } - - private boolean replenishCaseProperties(JsonNode exApiProperties, JsonNode apiProperties) { - - Iterator> apiFields = apiProperties.fields(); - Iterator> exApiFields = exApiProperties.fields(); - boolean diffProp = false; - while (apiFields.hasNext()) { - Map.Entry apiNode = apiFields.next(); - if (diffProp) { - break; - } - if (exApiFields.hasNext()) { - Map.Entry exChildNode = null; - Map.Entry exNode = exApiFields.next(); - if (StringUtils.equalsIgnoreCase(apiNode.getKey(), exNode.getKey())) { - exChildNode = exNode; - } else { - diffProp = true; - } - if (exChildNode == null) { - continue; - } - JsonNode value = apiNode.getValue(); - JsonNode value1 = exChildNode.getValue(); - JsonNode apiPropertiesNode = value.get(PROPERTIES); - JsonNode exApiPropertiesNode = value1.get(PROPERTIES); - if (apiPropertiesNode == null || exApiPropertiesNode == null) { - continue; - } - replenishCaseProperties(exApiPropertiesNode, apiPropertiesNode); - } else { - return true; - } - } - return false; - } - - /** - * 比较导入的与系统中重复的两个api的基础信息 - * - * @param existApi - * @param apiDefinition - * @param apiSyncCaseRequest - * @param toUpdate - * @return - */ - private static Boolean delBasicInfo(ApiDefinitionWithBLOBs existApi, ApiDefinitionWithBLOBs apiDefinition, ApiSyncCaseRequest apiSyncCaseRequest, Boolean toUpdate) { - if (!StringUtils.equals(apiDefinition.getMethod(), existApi.getMethod())) { - if (apiSyncCaseRequest.getMethod() && toUpdate) { - apiDefinition.setToBeUpdateTime(System.currentTimeMillis()); - } - return true; - } - if (!StringUtils.equals(apiDefinition.getProtocol(), existApi.getProtocol())) { - if (apiSyncCaseRequest.getProtocol() && toUpdate) { - apiDefinition.setToBeUpdateTime(System.currentTimeMillis()); - } - return true; - } - - if (!StringUtils.equals(apiDefinition.getPath(), existApi.getPath())) { - if (apiSyncCaseRequest.getPath() && toUpdate) { - apiDefinition.setToBeUpdateTime(System.currentTimeMillis()); - } - return true; - } - if (!StringUtils.equals(apiDefinition.getCreateUser(), existApi.getCreateUser())) { - return true; - } - - if (!StringUtils.equals(apiDefinition.getStatus(), existApi.getStatus()) && StringUtils.isNotBlank(existApi.getStatus()) && StringUtils.isNotBlank(apiDefinition.getStatus())) { - return true; - } - - if (!StringUtils.equals(apiDefinition.getTags(), existApi.getTags())) { - if (apiDefinition.getTags() != null && Objects.equals(apiDefinition.getTags(), StringUtils.EMPTY) && existApi.getTags() != null && Objects.equals(existApi.getTags(), StringUtils.EMPTY)) { - return true; - } - } - - if (!StringUtils.equals(existApi.getRemark(), apiDefinition.getRemark()) && StringUtils.isNotBlank(existApi.getRemark()) && StringUtils.isNotBlank(apiDefinition.getRemark())) { - return true; - } - - if (!StringUtils.equals(existApi.getDescription(), apiDefinition.getDescription()) && StringUtils.isNotBlank(existApi.getDescription()) && StringUtils.isNotBlank(apiDefinition.getDescription())) { - return true; - } - return null; - } - /** - * 比较导入的与系统中重复的两个api的响应体信息 - * - * @param existApi - * @param apiDefinition - * @param objectMapper - * @return - */ - private static Boolean delResponse(ApiDefinitionWithBLOBs existApi, ApiDefinitionWithBLOBs apiDefinition, ObjectMapper objectMapper) { - JsonNode exApiResponse = null; - JsonNode apiResponse = null; - if (StringUtils.isBlank(apiDefinition.getResponse()) || StringUtils.isBlank(existApi.getResponse())) { - return !StringUtils.isBlank(apiDefinition.getResponse()) || !StringUtils.isBlank(existApi.getResponse()); - } - try { - exApiResponse = objectMapper.readTree(existApi.getResponse()); - apiResponse = objectMapper.readTree(apiDefinition.getResponse()); - } catch (JsonProcessingException e) { - e.printStackTrace(); - } - if (exApiResponse == null || apiResponse == null) { - return exApiResponse != null || apiResponse != null; - } - if (exApiResponse.get(HEADERS) != null && apiResponse.get(HEADERS) != null) { - if (!StringUtils.equals(exApiResponse.get(HEADERS).toString(), apiResponse.get(HEADERS).toString())) { - return true; - } - } - if (exApiResponse.get(PropertyConstant.TYPE) != null && apiResponse.get(PropertyConstant.TYPE) != null) { - if (!StringUtils.equals(exApiResponse.get(PropertyConstant.TYPE).toString(), apiResponse.get(PropertyConstant.TYPE).toString())) { - return true; - } - } - if (exApiResponse.get("name") != null && apiResponse.get("name") != null) { - if (!StringUtils.equals(exApiResponse.get("name").toString(), apiResponse.get("name").toString())) { - return true; - } - } - if (exApiResponse.get(BODY) != null && apiResponse.get(BODY) != null) { - if (!StringUtils.equals(exApiResponse.get(BODY).toString(), apiResponse.get(BODY).toString())) { - return true; - } - } - if (exApiResponse.get("statusCode") != null && apiResponse.get("statusCode") != null) { - if (!StringUtils.equals(exApiResponse.get("statusCode").toString(), apiResponse.get("statusCode").toString())) { - return true; - } - } - if (exApiResponse.get("enable") != null && apiResponse.get("enable") != null) { - return !StringUtils.equals(exApiResponse.get("enable").toString(), apiResponse.get("enable").toString()); - } - return null; - } - private boolean getDiffByArrayNodes(JsonNode apiRequest, JsonNode exApiRequest, ObjectMapper objectMapper, String name) { - JsonNode apiNameNode = apiRequest.get(name); - JsonNode caseNameNode = exApiRequest.get(name); - if (apiNameNode == null || caseNameNode == null) { - return false; - } - Map apiMap = new HashMap<>(); - getKeyNameMap(apiNameNode, objectMapper, apiMap, "name"); - Map exApiMap = new HashMap<>(); - getKeyNameMap(caseNameNode, objectMapper, exApiMap, "name"); - if (apiMap.size() != exApiMap.size()) { - return true; - } - Set apiKey = apiMap.keySet(); - Set exApiKey = exApiMap.keySet(); - List collect = apiKey.stream().filter(exApiKey::contains).collect(Collectors.toList()); - if (collect.size() != apiKey.size()) { - return true; - } - return false; - } - - public void getKeyNameMap(JsonNode apiNameNode, ObjectMapper objectMapper, Map nameMap, String nodeKey) { - for (int i = 0; i < apiNameNode.size(); i++) { - JsonNode apiName = apiNameNode.get(i); - if (apiName.has(nodeKey)) { - String keyName = null; - try { - keyName = objectMapper.writeValueAsString(apiName.get(nodeKey)); - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } - if (StringUtils.isNotBlank(keyName) && !StringUtils.equals(keyName, "\"\"") && !StringUtils.equals(keyName, "null")) { - try { - nameMap.put(apiName.get(nodeKey).toString(), objectMapper.writeValueAsString(apiName)); - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } - } - } - } - } - - private void reSetImportMocksApiId(List mocks, String originId, String newId, int apiNum) { - if (CollectionUtils.isNotEmpty(mocks)) { - int index = 1; - for (MockConfigImportDTO item : mocks) { - if (StringUtils.equals(item.getApiId(), originId)) { - item.setApiId(newId); - } - item.setExpectNum(apiNum + "_" + index); - index++; - } - } - } - - private ApiTestCaseWithBLOBs addNewCase(ApiDefinitionWithBLOBs apiDefinition) { - ApiTestCaseWithBLOBs apiTestCase = new ApiTestCaseWithBLOBs(); - apiTestCase.setApiDefinitionId(apiDefinition.getId()); - apiTestCase.setProjectId(apiDefinition.getProjectId()); - apiTestCase.setName(apiDefinition.getName()); - apiTestCase.setRequest(apiDefinition.getRequest()); - return apiTestCase; - } - - private boolean setImportHashTree(ApiDefinitionWithBLOBs apiDefinition) { - String request = apiDefinition.getRequest(); - JSONObject jsonObject = JSONUtil.parseObject(request); - ElementUtil.dataFormatting(jsonObject); - MsHTTPSamplerProxy msHTTPSamplerProxy = JSONUtil.parseObject(jsonObject.toString(), MsHTTPSamplerProxy.class); - boolean createCase = CollectionUtils.isNotEmpty(msHTTPSamplerProxy.getHeaders()); - if (CollectionUtils.isNotEmpty(msHTTPSamplerProxy.getArguments()) && !createCase) { - createCase = true; - } - if (msHTTPSamplerProxy.getBody() != null && !createCase) { - createCase = true; - } - if (CollectionUtils.isNotEmpty(msHTTPSamplerProxy.getRest()) && !createCase) { - createCase = true; - } - msHTTPSamplerProxy.setId(apiDefinition.getId()); - msHTTPSamplerProxy.setHashTree(new LinkedList<>()); - apiDefinition.setRequest(JSON.toJSONString(msHTTPSamplerProxy)); - return createCase; - } - - private boolean setImportTCPHashTree(ApiDefinitionWithBLOBs apiDefinition) { - String request = apiDefinition.getRequest(); - MsTCPSampler tcpSampler = JSON.parseObject(request, MsTCPSampler.class); - boolean createCase = CollectionUtils.isNotEmpty(tcpSampler.getParameters()); - if (StringUtils.isNotBlank(tcpSampler.getJsonDataStruct()) && !createCase) { - createCase = true; - } - if (StringUtils.isNotBlank(tcpSampler.getRawDataStruct()) && !createCase) { - createCase = true; - } - if (CollectionUtils.isNotEmpty(tcpSampler.getXmlDataStruct()) && !createCase) { - createCase = true; - } - tcpSampler.setId(apiDefinition.getId()); - tcpSampler.setHashTree(new LinkedList<>()); - apiDefinition.setRequest(JSON.toJSONString(tcpSampler)); - return createCase; - } /** @@ -1630,242 +1028,6 @@ public class ApiDefinitionService { } } - public ApiDefinitionImport apiTestImport(MultipartFile file, ApiTestImportRequest request) { - //通过platform,获取对应的导入解析类型。 - if (file != null) { - String originalFilename = file.getOriginalFilename(); - if (StringUtils.isNotBlank(originalFilename)) { - String suffixName = originalFilename.substring(originalFilename.indexOf(".") + 1); - checkFileSuffixName(request, suffixName); - } - } - ApiImportParser runService = ApiDefinitionImportParserFactory.getApiImportParser(request.getPlatform()); - ApiDefinitionImport apiImport = null; - Project project = projectMapper.selectByPrimaryKey(request.getProjectId()); - if (StringUtils.isNotBlank(request.getSwaggerUrl())) { - if (!request.getPlatform().equalsIgnoreCase("Swagger2")) { - this.sendFailMessage(request, project); - MSException.throwException(Translator.get("file_format_does_not_meet_requirements")); - } - try { - UrlTestUtils.testUrl(request.getSwaggerUrl(), 30000); - } catch (Exception e) { - this.sendFailMessage(request, project); - MSException.throwException(e.getMessage()); - } - } - if (StringUtils.equals(request.getType(), SCHEDULE)) { - request.setProtocol("HTTP"); - } - try { - apiImport = (ApiDefinitionImport) Objects.requireNonNull(runService).parse(file == null ? null : file.getInputStream(), request); - if (apiImport.getMocks() == null) { - apiImport.setMocks(new ArrayList<>()); - } - } catch (MSException e) { - // 发送通知 - this.sendFailMessage(request, project); - throw e; - } catch (Exception e) { - // 发送通知 - this.sendFailMessage(request, project); - LogUtil.error(e.getMessage(), e); - MSException.throwException(Translator.get("parse_data_error")); - } - - try { - List apiImportSendNoticeDTOS = importApi(request, apiImport); - if (CollectionUtils.isNotEmpty(apiImport.getData())) { - List names = apiImport.getData().stream().map(ApiDefinitionWithBLOBs::getName).collect(Collectors.toList()); - request.setName(String.join(",", names)); - List ids = apiImport.getData().stream().map(ApiDefinitionWithBLOBs::getId).collect(Collectors.toList()); - request.setId(JSON.toJSONString(ids)); - } - // 发送通知 - if (StringUtils.equals(request.getType(), SCHEDULE)) { - String scheduleId = scheduleService.getScheduleInfo(request.getResourceId()); - String context = request.getSwaggerUrl() + "导入成功"; - Map paramMap = new HashMap<>(); - paramMap.put("url", request.getSwaggerUrl()); - NoticeModel noticeModel = NoticeModel.builder().operator(project.getCreateUser()).context(context).testId(scheduleId).subject(Translator.get("swagger_url_scheduled_import_notification")).paramMap(paramMap).event(NoticeConstants.Event.EXECUTE_SUCCESSFUL).build(); - noticeSendService.send(NoticeConstants.Mode.SCHEDULE, StringUtils.EMPTY, noticeModel); - } - if (!StringUtils.equals(request.getType(), SCHEDULE) && CollectionUtils.isNotEmpty(apiImportSendNoticeDTOS)) { - for (ApiImportSendNoticeDTO apiImportSendNoticeDTO : apiImportSendNoticeDTOS) { - ApiDefinitionResult apiDefinitionResult = apiImportSendNoticeDTO.getApiDefinitionResult(); - if (apiDefinitionResult != null && !apiDefinitionResult.isUpdated()) { - String context = SessionUtils.getUserId().concat("新建了接口定义").concat(":").concat(apiDefinitionResult.getName()); - sendImportApiNotice(apiDefinitionResult, context, NoticeConstants.Event.CREATE, "api_create_notice"); - } - if (apiDefinitionResult != null && apiDefinitionResult.isUpdated()) { - String context = SessionUtils.getUserId().concat("更新了接口定义").concat(":").concat(apiDefinitionResult.getName()); - sendImportApiNotice(apiDefinitionResult, context, NoticeConstants.Event.UPDATE, "api_update_notice"); - } - if (CollectionUtils.isNotEmpty(apiImportSendNoticeDTO.getCaseDTOList())) { - for (ApiTestCaseDTO apiTestCaseDTO : apiImportSendNoticeDTO.getCaseDTOList()) { - if (apiTestCaseDTO.isUpdated()) { - String context = SessionUtils.getUserId().concat("更新了接口用例").concat(":").concat(apiTestCaseDTO.getName()); - sendImportCaseNotice(apiTestCaseDTO, context, NoticeConstants.Event.CASE_UPDATE, "api_case_update_notice"); - } else { - String context = SessionUtils.getUserId().concat("新建了接口用例").concat(":").concat(apiTestCaseDTO.getName()); - sendImportCaseNotice(apiTestCaseDTO, context, NoticeConstants.Event.CASE_CREATE, "api_case_create_notice"); - } - } - } - } - } - } catch (Exception e) { - this.sendFailMessage(request, project); - LogUtil.error(e); - MSException.throwException(Translator.get("user_import_format_wrong")); - } - return apiImport; - } - - - private void sendFailMessage(ApiTestImportRequest request, Project project) { - if (StringUtils.equals(request.getType(), SCHEDULE)) { - String scheduleId = scheduleService.getScheduleInfo(request.getResourceId()); - String context = request.getSwaggerUrl() + "导入失败"; - Map paramMap = new HashMap<>(); - paramMap.put("url", request.getSwaggerUrl()); - paramMap.put("projectId", request.getProjectId()); - NoticeModel noticeModel = NoticeModel.builder().operator(project.getCreateUser()).context(context).testId(scheduleId).subject(Translator.get("swagger_url_scheduled_import_notification")).paramMap(paramMap).event(NoticeConstants.Event.EXECUTE_FAILED).build(); - noticeSendService.send(NoticeConstants.Mode.SCHEDULE, StringUtils.EMPTY, noticeModel); - } - } - - private void checkFileSuffixName(ApiTestImportRequest request, String suffixName) { - if (suffixName.equalsIgnoreCase("jmx")) { - if (!request.getPlatform().equalsIgnoreCase("JMeter")) { - MSException.throwException(Translator.get("file_format_does_not_meet_requirements")); - } - } - if (suffixName.equalsIgnoreCase("har")) { - if (!request.getPlatform().equalsIgnoreCase("Har")) { - MSException.throwException(Translator.get("file_format_does_not_meet_requirements")); - } - } - if (suffixName.equalsIgnoreCase("json")) { - if (request.getPlatform().equalsIgnoreCase("Har") || request.getPlatform().equalsIgnoreCase("Jmeter")) { - MSException.throwException(Translator.get("file_format_does_not_meet_requirements")); - } - } - } - - private List importApi(ApiTestImportRequest request, ApiDefinitionImport apiImport) { - SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH); - currentApiCaseOrder.remove(); - currentApiOrder.remove(); - String defaultVersion = baseProjectVersionMapper.getDefaultVersion(request.getProjectId()); - request.setDefaultVersion(defaultVersion); - if (request.getVersionId() == null) { - request.setVersionId(defaultVersion); - } - List initData = apiImport.getData(); - - Project project = projectMapper.selectByPrimaryKey(request.getProjectId()); - ProjectConfig config = projectApplicationService.getSpecificTypeValue(project.getId(), ProjectApplicationType.URL_REPEATABLE.name()); - boolean urlRepeat = config.getUrlRepeatable(); - //过滤(一次只导入一个协议) - List filterData = initData.stream().filter(t -> t.getProtocol().equals(request.getProtocol())).collect(Collectors.toList()); - if (filterData.isEmpty()) { - return new ArrayList<>(); - } - UpdateApiModuleDTO updateApiModuleDTO = apiModuleService.checkApiModule(request, apiImport, filterData, StringUtils.equals("fullCoverage", request.getModeId()), urlRepeat); - List updateList = updateApiModuleDTO.getNeedUpdateList(); - List data = updateApiModuleDTO.getDefinitionWithBLOBs(); - List moduleList = updateApiModuleDTO.getModuleList(); - List caseWithBLOBs = updateApiModuleDTO.getCaseWithBLOBs(); - Map> apiIdCaseMap = caseWithBLOBs.stream().collect(Collectors.groupingBy(ApiTestCase::getApiDefinitionId)); - - ApiDefinitionMapper batchMapper = sqlSession.getMapper(ApiDefinitionMapper.class); - ApiTestCaseMapper apiTestCaseMapper = sqlSession.getMapper(ApiTestCaseMapper.class); - ExtApiDefinitionMapper extApiDefinitionMapper = sqlSession.getMapper(ExtApiDefinitionMapper.class); - ApiModuleMapper apiModuleMapper = sqlSession.getMapper(ApiModuleMapper.class); - - int num = 0; - if (!CollectionUtils.isEmpty(data) && data.get(0) != null && data.get(0).getProjectId() != null) { - num = getNextNum(data.get(0).getProjectId()); - } - - if (moduleList != null) { - for (ApiModule apiModule : moduleList) { - apiModuleMapper.insert(apiModule); - } - } - //如果需要导入的数据为空。此时清空mock信息 - if (data.isEmpty()) { - apiImport.getMocks().clear(); - } - List apiImportSendNoticeDTOS = new ArrayList<>(); - for (int i = 0; i < data.size(); i++) { - ApiDefinitionWithBLOBs item = data.get(i); - List caseList = apiIdCaseMap.get(item.getId()); - this.setModule(item); - if (item.getName().length() > 255) { - item.setName(item.getName().substring(0, 255)); - } - //如果是创建版本数据,则num和其他版本数据一致 - if (item.getVersionId() == null || (!item.getVersionId().equals("new") && !item.getVersionId().equals("update"))) { - item.setNum(num++); - } - //如果EsbData需要存储,则需要进行接口是否更新的判断 - ApiDefinitionImportParamDTO apiDefinitionImportParamDTO = new ApiDefinitionImportParamDTO(item, request, apiImport.getMocks(), updateList, caseList); - if (apiImport.getEsbApiParamsMap() != null) { - String apiId = item.getId(); - EsbApiParamsWithBLOBs model = apiImport.getEsbApiParamsMap().get(apiId); - request.setModeId("fullCoverage");//标准版ESB数据导入不区分是否覆盖,默认都为覆盖 - - ApiImportSendNoticeDTO apiImportSendNoticeDTO = importCreate(batchMapper, extApiDefinitionMapper, apiTestCaseMapper, apiDefinitionImportParamDTO); - if (model != null) { - apiImport.getEsbApiParamsMap().remove(apiId); - model.setResourceId(item.getId()); - apiImport.getEsbApiParamsMap().put(item.getId(), model); - } - if (apiImportSendNoticeDTO != null) { - apiImportSendNoticeDTOS.add(apiImportSendNoticeDTO); - } - } else { - ApiImportSendNoticeDTO apiImportSendNoticeDTO = importCreate(batchMapper, extApiDefinitionMapper, apiTestCaseMapper, apiDefinitionImportParamDTO); - if (apiImportSendNoticeDTO != null) { - apiImportSendNoticeDTOS.add(apiImportSendNoticeDTO); - } - } - if (i % 300 == 0) { - sqlSession.flushStatements(); - } - } - - //判断EsbData是否需要存储 - if (apiImport.getEsbApiParamsMap() != null && apiImport.getEsbApiParamsMap().size() > 0) { - EsbApiParamsMapper esbApiParamsMapper = sqlSession.getMapper(EsbApiParamsMapper.class); - for (EsbApiParamsWithBLOBs model : apiImport.getEsbApiParamsMap().values()) { - EsbApiParamsExample example = new EsbApiParamsExample(); - example.createCriteria().andResourceIdEqualTo(model.getResourceId()); - List exitModelList = esbApiParamsMapper.selectByExampleWithBLOBs(example); - if (exitModelList.isEmpty()) { - esbApiParamsMapper.insert(model); - } else { - model.setId(exitModelList.get(0).getId()); - esbApiParamsMapper.updateByPrimaryKeyWithBLOBs(model); - } - } - } - - if (!CollectionUtils.isEmpty(apiImport.getMocks())) { - MockConfigService mockConfigService = CommonBeanFactory.getBean(MockConfigService.class); - mockConfigService.importMock(apiImport, sqlSession, request); - } - sqlSession.flushStatements(); - if (sqlSession != null && sqlSessionFactory != null) { - SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory); - } - - return apiImportSendNoticeDTOS; - } - - public List getReference(ApiScenarioRequest request) { if (CollectionUtils.isEmpty(request.getIds())) { return new ArrayList<>(); @@ -3008,6 +2170,5 @@ public class ApiDefinitionService { } allSourceIdCount = apiExecutionInfoService.countSourceIdByProjectIdIsNull(); } - } } diff --git a/api-test/backend/src/main/java/io/metersphere/service/definition/ApiModuleService.java b/api-test/backend/src/main/java/io/metersphere/service/definition/ApiModuleService.java index 87b29beb83..a06b5c754e 100644 --- a/api-test/backend/src/main/java/io/metersphere/service/definition/ApiModuleService.java +++ b/api-test/backend/src/main/java/io/metersphere/service/definition/ApiModuleService.java @@ -13,8 +13,6 @@ import io.metersphere.base.domain.ApiDefinitionExample; import io.metersphere.base.domain.ApiDefinitionWithBLOBs; import io.metersphere.base.domain.ApiModule; import io.metersphere.base.domain.ApiModuleExample; -import io.metersphere.base.domain.ApiTestCase; -import io.metersphere.base.domain.ApiTestCaseExample; import io.metersphere.base.domain.ApiTestCaseWithBLOBs; import io.metersphere.base.domain.EsbApiParamsWithBLOBs; import io.metersphere.base.mapper.ApiDefinitionMapper; @@ -27,7 +25,6 @@ import io.metersphere.commons.constants.PropertyConstant; import io.metersphere.commons.constants.TestCaseConstants; import io.metersphere.commons.enums.ApiTestDataStatus; import io.metersphere.commons.exception.MSException; -import io.metersphere.commons.utils.BeanUtils; import io.metersphere.commons.utils.JSON; import io.metersphere.commons.utils.LogUtil; import io.metersphere.commons.utils.SessionUtils; @@ -57,9 +54,7 @@ import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.UUID; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Function; import java.util.stream.Collectors; @@ -76,8 +71,6 @@ public class ApiModuleService extends NodeTreeService { @Resource private ApiDefinitionService apiDefinitionService; @Resource - private ApiTestCaseMapper apiTestCaseMapper; - @Resource private ExtApiTestCaseMapper extApiTestCaseMapper; @Resource @@ -625,8 +618,6 @@ public class ApiModuleService extends NodeTreeService { Map idModuleMap, ApiTestImportRequest request, Boolean fullCoverage, List importCases, Map esbApiParamsMap) { List optionData = new ArrayList<>(); - //系统原有的需要更新的list, - List toUpdateList = new ArrayList<>(); //去重,TCP,SQL,DUBBO 模块下名称唯一 removeRepeatOrigin(data, fullCoverage, optionData); //上传文件时选的模块ID @@ -644,105 +635,7 @@ public class ApiModuleService extends NodeTreeService { //处理模块 setModule(moduleMap, pidChildrenMap, idPathMap, idModuleMap, optionData, chooseModule); - List repeatApiDefinitionWithBLOBs = getApiDefinitionWithBLOBsList(request, optionData); - //重复接口的case - Map> oldCaseMap = new HashMap<>(); - if (CollectionUtils.isNotEmpty(repeatApiDefinitionWithBLOBs)) { - oldCaseMap = getOldCaseMap(repeatApiDefinitionWithBLOBs); - } - Map repeatDataMap = null; - Map optionMap = new HashMap<>(); - - if (chooseModule != null) { - if (CollectionUtils.isNotEmpty(repeatApiDefinitionWithBLOBs)) { - String chooseModuleParentId = getChooseModuleParentId(chooseModule); - String chooseModulePath = getChooseModulePath(idPathMap, chooseModule, chooseModuleParentId); - if (fullCoverage) { - List singleOptionData = new ArrayList<>(); - removeOtherChooseModuleRepeat(optionData, singleOptionData, chooseModulePath); - optionData = singleOptionData; - optionMap = optionData.stream().collect(Collectors.toMap(t -> t.getName().concat(chooseModulePath), api -> api)); - } else { - getNoHChooseModuleUrlRepeatOptionMap(optionData, optionMap, chooseModulePath); - } - repeatDataMap = repeatApiDefinitionWithBLOBs.stream().filter(t -> t.getModuleId().equals(chooseModuleId)).collect(Collectors.toMap(t -> t.getName().concat(t.getModulePath()), api -> api)); - } - } else { - buildOptionMap(optionData, optionMap); - if (CollectionUtils.isNotEmpty(repeatApiDefinitionWithBLOBs)) { - repeatDataMap = repeatApiDefinitionWithBLOBs.stream().collect(Collectors.toMap(t -> t.getName().concat(t.getModulePath()), api -> api)); - } - } - Boolean fullCoverageApi = getFullCoverageApi(request); - String updateVersionId = getUpdateVersionId(request); - String versionId = getVersionId(request); - //处理数据 - if (fullCoverage) { - if (fullCoverageApi) { - coverModule(toUpdateList, optionMap, repeatDataMap, updateVersionId, optionDataCases, oldCaseMap, esbApiParamsMap); - } else { - moduleMap = cover(moduleMap, toUpdateList, optionMap, repeatDataMap, updateVersionId, optionDataCases, oldCaseMap, esbApiParamsMap); - } - } else { - //不覆盖 - removeRepeat(optionData, optionMap, repeatDataMap, moduleMap, versionId, optionDataCases); - } - - //系统内检查重复 - if (CollectionUtils.isNotEmpty(repeatApiDefinitionWithBLOBs)) { - repeatDataMap = repeatApiDefinitionWithBLOBs.stream().collect(Collectors.toMap(t -> t.getName().concat(t.getModulePath()), api -> api)); - optionMap = optionData.stream().collect(Collectors.toMap(t -> t.getName().concat(t.getModulePath()), api -> api)); - if (fullCoverage) { - cover(moduleMap, toUpdateList, optionMap, repeatDataMap, updateVersionId, optionDataCases, oldCaseMap, esbApiParamsMap); - } else { - //不覆盖,同一接口不做更新 - removeRepeat(optionData, optionMap, repeatDataMap, moduleMap, versionId, optionDataCases); - } - } - - if (optionData.isEmpty()) { - moduleMap = new HashMap<>(); - } - //将原来的case和更改的case组合在一起,为了同步的设置 - List caseIds = optionDataCases.stream().map(ApiTestCase::getId).filter(StringUtils::isNotBlank).collect(Collectors.toList()); - buildCases(optionDataCases, oldCaseMap, caseIds); - return getUpdateApiModuleDTO(moduleMap, toUpdateList, optionData, optionDataCases); - } - - private void getNoHChooseModuleUrlRepeatOptionMap(List optionData, Map optionMap, String chooseModulePath) { - for (ApiDefinitionWithBLOBs optionDatum : optionData) { - if (optionDatum.getModulePath() == null) { - optionMap.put(optionDatum.getName().concat(chooseModulePath), optionDatum); - } else { - optionMap.put(optionDatum.getName().concat(chooseModulePath).concat(optionDatum.getModulePath()), optionDatum); - } - } - } - - private void removeOtherChooseModuleRepeat(List optionData, List singleOptionData, String chooseModulePath) { - LinkedHashMap> methodPathMap = optionData.stream().collect(Collectors.groupingBy(t -> t.getName().concat(chooseModulePath), LinkedHashMap::new, Collectors.toList())); - methodPathMap.forEach((k, v) -> { - singleOptionData.add(v.get(v.size() - 1)); - }); - } - - private void buildOptionMap(List optionData, Map optionMap) { - for (ApiDefinitionWithBLOBs optionDatum : optionData) { - if (optionDatum.getModulePath() == null) { - optionMap.put(optionDatum.getName(), optionDatum); - } else { - optionMap.put(optionDatum.getName().concat(optionDatum.getModulePath()), optionDatum); - } - } - } - - private List getApiDefinitionWithBLOBsList(ApiTestImportRequest request, List optionData) { - //处理数据 - List nameList = optionData.stream().map(ApiDefinitionWithBLOBs::getName).collect(Collectors.toList()); - String projectId = request.getProjectId(); - String protocol = request.getProtocol(); - //获取系统内重复数据 - return extApiDefinitionMapper.selectRepeatByProtocol(nameList, protocol, projectId); + return getUpdateApiModuleDTO(chooseModule,idPathMap,optionData,fullCoverage, moduleMap,optionDataCases); } private UpdateApiModuleDTO dealHttp(List data, @@ -751,8 +644,6 @@ public class ApiModuleService extends NodeTreeService { Boolean fullCoverage, boolean urlRepeat, List importCases) { List optionData = new ArrayList<>(); - //系统原有的需要更新的list, - List toUpdateList = new ArrayList<>(); //去重 如果url可重复 则模块+名称+请求方式+路径 唯一,否则 请求方式+路径唯一, //覆盖模式留重复的最后一个,不覆盖留第一个 removeHttpRepeat(data, fullCoverage, urlRepeat, optionData); @@ -772,182 +663,12 @@ public class ApiModuleService extends NodeTreeService { //处理模块 setModule(moduleMap, pidChildrenMap, idPathMap, idModuleMap, optionData, chooseModule); - if (urlRepeat) { - optionData = dealHttpUrlRepeat(chooseModule, idPathMap, optionData, fullCoverage, request, moduleMap, toUpdateList, optionDataCases); - } else { - dealHttpUrlNoRepeat(optionData, fullCoverage, request, moduleMap, toUpdateList, optionDataCases); - } - if (optionData.isEmpty()) { - moduleMap = new HashMap<>(); - } - return getUpdateApiModuleDTO(moduleMap, toUpdateList, optionData, optionDataCases); + return getUpdateApiModuleDTO(chooseModule,idPathMap,optionData,fullCoverage, moduleMap,optionDataCases); } - private void dealHttpUrlNoRepeat(List optionData, - Boolean fullCoverage, ApiTestImportRequest request, Map moduleMap, - List toUpdateList, List optionDataCases) { - - //这个是名称加请求方式加路径加模块为key的map 就是为了去重 - Map optionMap; - - String updateVersionId = getUpdateVersionId(request); - String versionId = getVersionId(request); - Boolean fullCoverageApi = getFullCoverageApi(request); - String projectId = request.getProjectId(); - //系统内重复的数据 - List repeatApiDefinitionWithBLOBs = extApiDefinitionMapper.selectRepeatByBLOBs(optionData, projectId); - - //这个是系统内重复的数据 - Map> repeatDataMap = repeatApiDefinitionWithBLOBs.stream().collect(Collectors.groupingBy(t -> t.getMethod().concat(t.getPath()))); - - //按照原来的顺序 - optionMap = optionData.stream().collect(Collectors.toMap(t -> t.getMethod().concat(t.getPath()), api -> api)); - - Map> oldCaseMap = new HashMap<>(); - - //重复接口的case - if (CollectionUtils.isNotEmpty(repeatApiDefinitionWithBLOBs)) { - oldCaseMap = getOldCaseMap(repeatApiDefinitionWithBLOBs); - } - - if (fullCoverage) { - if (fullCoverageApi) { - if (CollectionUtils.isNotEmpty(repeatApiDefinitionWithBLOBs)) { - startCoverModule(toUpdateList, optionData, optionMap, repeatDataMap, updateVersionId, optionDataCases, oldCaseMap); - } - } else { - //不覆盖模块 - if (CollectionUtils.isNotEmpty(repeatApiDefinitionWithBLOBs)) { - startCover(toUpdateList, optionData, optionMap, repeatDataMap, updateVersionId, optionDataCases, oldCaseMap); - } - } - } else { - //不覆盖,同一接口不做更新 - if (CollectionUtils.isNotEmpty(repeatApiDefinitionWithBLOBs)) { - removeSameData(repeatDataMap, optionMap, optionData, moduleMap, versionId, optionDataCases); - } - } - //将原来的case和更改的case组合在一起,为了同步的设置 - List caseIds = optionDataCases.stream().map(ApiTestCase::getId).filter(StringUtils::isNotBlank).collect(Collectors.toList()); - buildCases(optionDataCases, oldCaseMap, caseIds); - } - - private List dealHttpUrlRepeat(ApiModuleDTO chooseModule, Map idPathMap, List optionData, - Boolean fullCoverage, ApiTestImportRequest request, Map moduleMap, - List toUpdateList, List optionDataCases) { - String updateVersionId = getUpdateVersionId(request); - String versionId = getVersionId(request); - Boolean fullCoverageApi = getFullCoverageApi(request); - String projectId = request.getProjectId(); - //系统内重复的数据 - List repeatApiDefinitionWithBLOBs = extApiDefinitionMapper.selectRepeatByBLOBs(optionData, projectId); - - //这个是名称加请求方式加路径加模块为key的map 就是为了去重 - Map optionMap = new HashMap<>(); - //这个是系统内重复的数据 - Map> repeatDataMap; - //按照原来的顺序 - if (chooseModule != null) { - //如果有选中的模块,则在选中的模块下过滤 过滤规则是 选择的模块路径+名称+method+path - String chooseModuleParentId = getChooseModuleParentId(chooseModule); - String chooseModulePath = getChooseModulePath(idPathMap, chooseModule, chooseModuleParentId); - //这样的过滤规则下可能存在重复接口,如果是覆盖模块,需要按照去重规则再次去重,否则就加上接口原有的模块 - if (fullCoverage) { - List singleOptionData = new ArrayList<>(); - removeHttpChooseModuleRepeat(optionData, singleOptionData, chooseModulePath); - optionData = singleOptionData; - optionMap = optionData.stream().collect(Collectors.toMap(t -> t.getName().concat(t.getMethod()).concat(t.getPath()).concat(chooseModulePath), api -> api)); - } else { - getChooseModuleUrlRepeatOptionMap(optionData, optionMap, chooseModulePath); - } - repeatDataMap = repeatApiDefinitionWithBLOBs.stream().filter(t -> t.getModuleId().equals(chooseModule.getId())).collect(Collectors.groupingBy(t -> t.getName().concat(t.getMethod()).concat(t.getPath()).concat(t.getModulePath()))); - } else { - //否则在整个系统中过滤 - getUrlRepeatOptionMap(optionData, optionMap); - repeatDataMap = repeatApiDefinitionWithBLOBs.stream().collect(Collectors.groupingBy(t -> t.getName().concat(t.getMethod()).concat(t.getPath()).concat(t.getModulePath()))); - } - Map> oldCaseMap = new HashMap<>(); - //重复接口的case - if (CollectionUtils.isNotEmpty(repeatApiDefinitionWithBLOBs)) { - oldCaseMap = getOldCaseMap(repeatApiDefinitionWithBLOBs); - } - //覆盖接口 - if (fullCoverage) { - //允许覆盖模块,用导入的重复数据的最后一条覆盖查询的所有重复数据; case 在覆盖的时候,是拼接到原来的case,name唯一;不覆盖,就用原来的 - if (fullCoverageApi) { - if (CollectionUtils.isNotEmpty(repeatApiDefinitionWithBLOBs)) { - startCoverModule(toUpdateList, optionData, optionMap, repeatDataMap, updateVersionId, optionDataCases, oldCaseMap); - } - } else { - //覆盖但不覆盖模块 - if (CollectionUtils.isNotEmpty(repeatApiDefinitionWithBLOBs)) { - //过滤同一层级重复模块,导入文件没有新增接口无需创建接口模块 - moduleMap = judgeModuleMap(moduleMap, optionMap, repeatDataMap); - startCover(toUpdateList, optionData, optionMap, repeatDataMap, updateVersionId, optionDataCases, oldCaseMap); - } - } - } else { - //不覆盖,同一接口不做更新;可能创建新版本,case也直接创建, - if (CollectionUtils.isNotEmpty(repeatApiDefinitionWithBLOBs)) { - removeSameData(repeatDataMap, optionMap, optionData, moduleMap, versionId, optionDataCases); - } - } - //最后在整个体统内检查一遍(防止在有选择的模块时,未找到重复,直接创建的情况) - if (CollectionUtils.isNotEmpty(repeatApiDefinitionWithBLOBs)) { - repeatDataMap = repeatApiDefinitionWithBLOBs.stream().collect(Collectors.groupingBy(t -> t.getName().concat(t.getMethod()).concat(t.getPath()).concat(t.getModulePath()))); - optionMap = optionData.stream().collect(Collectors.toMap(t -> t.getName().concat(t.getMethod()).concat(t.getPath()).concat(t.getModulePath()), api -> api)); - if (fullCoverage) { - startCover(toUpdateList, optionData, optionMap, repeatDataMap, updateVersionId, optionDataCases, oldCaseMap); - } else { - //不覆盖,同一接口不做更新 - if (CollectionUtils.isNotEmpty(repeatApiDefinitionWithBLOBs)) { - removeSameData(repeatDataMap, optionMap, optionData, moduleMap, versionId, optionDataCases); - } - } - } - //将原来的case和更改的case组合在一起,为了同步的设置 - List caseIds = optionDataCases.stream().map(ApiTestCase::getId).filter(StringUtils::isNotBlank).collect(Collectors.toList()); - buildCases(optionDataCases, oldCaseMap, caseIds); - return optionData; - } - - private void removeHttpChooseModuleRepeat(List optionData, List singleOptionData, String chooseModulePath) { - LinkedHashMap> methodPathMap = optionData.stream().collect(Collectors.groupingBy(t -> t.getName().concat(t.getMethod()).concat(t.getPath()).concat(chooseModulePath), LinkedHashMap::new, Collectors.toList())); - methodPathMap.forEach((k, v) -> singleOptionData.add(v.get(v.size() - 1))); - } - - private void getChooseModuleUrlRepeatOptionMap(List optionData, Map optionMap, String chooseModulePath) { - for (ApiDefinitionWithBLOBs optionDatum : optionData) { - if (optionDatum.getModulePath() == null) { - optionMap.put(optionDatum.getName().concat(optionDatum.getMethod()).concat(optionDatum.getPath()).concat(chooseModulePath), optionDatum); - } else { - optionMap.put(optionDatum.getName().concat(optionDatum.getMethod()).concat(optionDatum.getPath()).concat(chooseModulePath).concat(optionDatum.getModulePath()), optionDatum); - } - } - } - - private void getUrlRepeatOptionMap(List optionData, Map optionMap) { - for (ApiDefinitionWithBLOBs optionDatum : optionData) { - if (optionDatum.getModulePath() == null) { - optionMap.put(optionDatum.getName().concat(optionDatum.getMethod()).concat(optionDatum.getPath()), optionDatum); - } else { - optionMap.put(optionDatum.getName().concat(optionDatum.getMethod()).concat(optionDatum.getPath()).concat(optionDatum.getModulePath()), optionDatum); - } - } - } - - @NotNull - private Boolean getFullCoverageApi(ApiTestImportRequest request) { - Boolean fullCoverageApi = request.getCoverModule(); - if (fullCoverageApi == null) { - fullCoverageApi = false; - } - return fullCoverageApi; - } - @NotNull private Boolean getFullCoverage(ApiDefinitionImport apiImport, Boolean fullCoverage) { if (fullCoverage == null) { @@ -961,18 +682,6 @@ public class ApiModuleService extends NodeTreeService { return fullCoverage; } - private void buildCases(List optionDataCases, Map> oldCaseMap, List caseIds) { - if (MapUtils.isNotEmpty(oldCaseMap)) { - List oldCaseList = new ArrayList<>(); - Collection> values = oldCaseMap.values(); - for (List value : values) { - oldCaseList.addAll(value); - } - List collect = oldCaseList.stream().filter(t -> !caseIds.contains(t.getId())).collect(Collectors.toList()); - optionDataCases.addAll(collect); - } - } - private void removeRepeatCase(Boolean fullCoverage, List importCases, List optionDataCases) { LinkedHashMap> apiIdNameMap = importCases.stream().collect(Collectors.groupingBy(t -> t.getName().concat(t.getApiDefinitionId()), LinkedHashMap::new, Collectors.toList())); if (fullCoverage) { @@ -990,164 +699,17 @@ public class ApiModuleService extends NodeTreeService { } } - private Map> getOldCaseMap(List repeatApiDefinitionWithBLOBs) { - Map> oldCaseMap; - List definitionIds = repeatApiDefinitionWithBLOBs.stream().map(ApiDefinition::getId).collect(Collectors.toList()); - ApiTestCaseExample testCaseExample = new ApiTestCaseExample(); - testCaseExample.createCriteria().andApiDefinitionIdIn(definitionIds); - testCaseExample.or(testCaseExample.createCriteria().andStatusNotEqualTo(ApiTestDataStatus.TRASH.getValue()).andStatusIsNull()); - List caseWithBLOBs = apiTestCaseMapper.selectByExampleWithBLOBs(testCaseExample); - oldCaseMap = caseWithBLOBs.stream().collect(Collectors.groupingBy(ApiTestCase::getApiDefinitionId)); - return oldCaseMap; - } - - - private UpdateApiModuleDTO getUpdateApiModuleDTO(Map moduleMap, List toUpdateList, List optionData, List optionDataCases) { + private UpdateApiModuleDTO getUpdateApiModuleDTO(ApiModuleDTO chooseModule,Map idPathMap,List optionData,Boolean fullCoverage,Map moduleMap, List optionDataCases) { UpdateApiModuleDTO updateApiModuleDTO = new UpdateApiModuleDTO(); - updateApiModuleDTO.setModuleList(new ArrayList<>(moduleMap.values())); - updateApiModuleDTO.setNeedUpdateList(toUpdateList); + updateApiModuleDTO.setChooseModule(chooseModule); + updateApiModuleDTO.setIdPathMap(idPathMap); + updateApiModuleDTO.setFullCoverage(fullCoverage); + updateApiModuleDTO.setModuleMap(moduleMap); updateApiModuleDTO.setDefinitionWithBLOBs(optionData); updateApiModuleDTO.setCaseWithBLOBs(optionDataCases); return updateApiModuleDTO; } - private void removeRepeat(List optionData, Map nameModuleMap, - Map repeatDataMap, Map moduleMap, - String versionId, - List optionDataCases) { - if (MapUtils.isEmpty(nameModuleMap) || MapUtils.isEmpty(repeatDataMap)) { - return; - } - Map> moduleOptionData = optionData.stream().collect(Collectors.groupingBy(ApiDefinition::getModulePath)); - repeatDataMap.forEach((k, v) -> { - ApiDefinitionWithBLOBs apiDefinitionWithBLOBs = nameModuleMap.get(k); - if (apiDefinitionWithBLOBs == null) { - return; - } - Map> definitionIdCaseMAp = optionDataCases.stream().collect(Collectors.groupingBy(ApiTestCase::getApiDefinitionId)); - List distinctNameCases = definitionIdCaseMAp.get(apiDefinitionWithBLOBs.getId()); - String modulePath = apiDefinitionWithBLOBs.getModulePath(); - List moduleData = moduleOptionData.get(modulePath); - if (moduleData != null && moduleData.size() <= 1) { - moduleMap.remove(modulePath); - removeModulePath(moduleMap, moduleOptionData, modulePath); - moduleData.remove(apiDefinitionWithBLOBs); - } - //不覆盖选择版本,如果被选版本有同接口,不导入,否则创建新版本接口 - if (v.getVersionId().equals(versionId)) { - optionData.remove(apiDefinitionWithBLOBs); - if (CollectionUtils.isNotEmpty(distinctNameCases)) { - distinctNameCases.forEach(optionDataCases::remove); - } - } else { - //这里是为了标识当前数据是需要创建版本的,不是全新增的数据 - addNewVersionApi(apiDefinitionWithBLOBs, v, "new"); - } - }); - } - - private void addNewVersionApi(ApiDefinitionWithBLOBs apiDefinitionWithBLOBs, ApiDefinitionWithBLOBs v, String version) { - apiDefinitionWithBLOBs.setVersionId(version); - apiDefinitionWithBLOBs.setNum(v.getNum()); - apiDefinitionWithBLOBs.setStatus(v.getStatus()); - apiDefinitionWithBLOBs.setOrder(v.getOrder()); - apiDefinitionWithBLOBs.setRefId(v.getRefId()); - apiDefinitionWithBLOBs.setLatest(v.getLatest()); - } - - private Map cover(Map moduleMap, List toUpdateList, - Map nameModuleMap, Map repeatDataMap, - String updateVersionId, List optionDataCases, - Map> oldCaseMap, Map esbApiParamsMap) { - //覆盖但不覆盖模块 - if (MapUtils.isEmpty(nameModuleMap) || MapUtils.isEmpty(repeatDataMap)) { - return moduleMap; - } - //导入文件没有新增接口无需创建接口模块 - moduleMap = judgeModule(moduleMap, nameModuleMap, repeatDataMap); - repeatDataMap.forEach((k, v) -> { - ApiDefinitionWithBLOBs apiDefinitionWithBLOBs = nameModuleMap.get(k); - if (apiDefinitionWithBLOBs != null) { - //系统内重复的数据的版本如果不是选择的数据更新版本,则在数据更新版本新增,否则更新这个版本的数据 - if (!v.getVersionId().equals(updateVersionId)) { - addNewVersionApi(apiDefinitionWithBLOBs, v, "update"); - return; - } - Map> definitionIdCaseMAp = optionDataCases.stream().collect(Collectors.groupingBy(ApiTestCase::getApiDefinitionId)); - //该接口的case - Map caseNameMap = getDistinctCaseNameMap(definitionIdCaseMAp, apiDefinitionWithBLOBs); - updateEsb(esbApiParamsMap, v.getId(), apiDefinitionWithBLOBs.getId()); - //组合case - if (MapUtils.isNotEmpty(caseNameMap)) { - buildCaseList(oldCaseMap, caseNameMap, v, optionDataCases); - } - apiDefinitionWithBLOBs.setId(v.getId()); - apiDefinitionWithBLOBs.setModuleId(v.getModuleId()); - apiDefinitionWithBLOBs.setModulePath(v.getModulePath()); - setApiParam(apiDefinitionWithBLOBs, updateVersionId, v); - toUpdateList.add(v); - } - }); - return moduleMap; - } - - private void updateEsb(Map esbApiParamsMap, String newId, String oldId) { - if (MapUtils.isNotEmpty(esbApiParamsMap)) { - EsbApiParamsWithBLOBs esbApiParamsWithBLOBs = esbApiParamsMap.get(oldId); - if (esbApiParamsWithBLOBs != null) { - esbApiParamsMap.remove(oldId); - esbApiParamsWithBLOBs.setResourceId(newId); - esbApiParamsMap.put(newId, esbApiParamsWithBLOBs); - } - } - } - - private Map judgeModule(Map moduleMap, Map nameModuleMap, Map repeatDataMap) { - AtomicBoolean remove = new AtomicBoolean(true); - - if (repeatDataMap.size() >= nameModuleMap.size()) { - repeatDataMap.forEach((k, v) -> { - ApiDefinitionWithBLOBs apiDefinitionWithBLOBs = nameModuleMap.get(k); - if (apiDefinitionWithBLOBs == null) { - remove.set(false); - } - }); - if (remove.get()) { - moduleMap = new HashMap<>(); - } - } - return moduleMap; - } - - private void coverModule(List toUpdateList, Map nameModuleMap, - Map repeatDataMap, String updateVersionId, List optionDataCases, - Map> oldCaseMap, Map esbApiParamsMap) { - if (MapUtils.isEmpty(nameModuleMap) || MapUtils.isEmpty(repeatDataMap)) { - return; - } - repeatDataMap.forEach((k, v) -> { - ApiDefinitionWithBLOBs apiDefinitionWithBLOBs = nameModuleMap.get(k); - if (apiDefinitionWithBLOBs != null) { - //系统内重复的数据的版本如果不是选择的数据更新版本,则在数据更新版本新增,否则更新这个版本的数据 - if (!v.getVersionId().equals(updateVersionId)) { - addNewVersionApi(apiDefinitionWithBLOBs, v, "update"); - return; - } - //该接口的case - Map> definitionIdCaseMAp = optionDataCases.stream().collect(Collectors.groupingBy(ApiTestCase::getApiDefinitionId)); - Map caseNameMap = getDistinctCaseNameMap(definitionIdCaseMAp, apiDefinitionWithBLOBs); - updateEsb(esbApiParamsMap, v.getId(), apiDefinitionWithBLOBs.getId()); - //组合case - if (MapUtils.isNotEmpty(caseNameMap)) { - buildCaseList(oldCaseMap, caseNameMap, v, optionDataCases); - } - apiDefinitionWithBLOBs.setId(v.getId()); - setApiParam(apiDefinitionWithBLOBs, updateVersionId, v); - toUpdateList.add(v); - } - }); - } - private void removeRepeatOrigin(List data, Boolean fullCoverage, List optionData) { LinkedHashMap> methodPathMap = data.stream().collect(Collectors.groupingBy(t -> t.getName() + (t.getModulePath() == null ? StringUtils.EMPTY : t.getModulePath()), LinkedHashMap::new, Collectors.toList())); if (fullCoverage) { @@ -1175,257 +737,6 @@ public class ApiModuleService extends NodeTreeService { } } - private String getVersionId(ApiTestImportRequest request) { - String versionId; - if (request.getVersionId() == null) { - versionId = request.getDefaultVersion(); - } else { - versionId = request.getVersionId(); - } - return versionId; - } - - private String getUpdateVersionId(ApiTestImportRequest request) { - String updateVersionId; - if (request.getUpdateVersionId() != null) { - updateVersionId = request.getUpdateVersionId(); - } else { - updateVersionId = request.getDefaultVersion(); - } - return updateVersionId; - } - - private void removeSameData(Map> repeatDataMap, Map methodPathMap, - List optionData, Map moduleMap, String versionId, - List optionDataCases) { - - Map> moduleOptionData = optionData.stream().collect(Collectors.groupingBy(ApiDefinition::getModulePath)); - repeatDataMap.forEach((k, v) -> { - ApiDefinitionWithBLOBs apiDefinitionWithBLOBs = methodPathMap.get(k); - if (apiDefinitionWithBLOBs != null) { - Map> definitionIdCaseMAp = optionDataCases.stream().collect(Collectors.groupingBy(ApiTestCase::getApiDefinitionId)); - List distinctNameCases = definitionIdCaseMAp.get(apiDefinitionWithBLOBs.getId()); - String modulePath = apiDefinitionWithBLOBs.getModulePath(); - List moduleData = moduleOptionData.get(modulePath); - if (moduleData != null && moduleData.size() <= 1) { - moduleMap.remove(modulePath); - removeModulePath(moduleMap, moduleOptionData, modulePath); - moduleData.remove(apiDefinitionWithBLOBs); - } - //不覆盖选择版本,如果被选版本有同接口,不导入,否则创建新版本接口 - List sameVersionList = v.stream().filter(t -> t.getVersionId().equals(versionId)).collect(Collectors.toList()); - if (CollectionUtils.isNotEmpty(sameVersionList)) { - optionData.remove(apiDefinitionWithBLOBs); - if (CollectionUtils.isNotEmpty(distinctNameCases)) { - distinctNameCases.forEach(optionDataCases::remove); - } - } else { - for (ApiDefinitionWithBLOBs definitionWithBLOBs : v) { - addNewVersionApi(apiDefinitionWithBLOBs, definitionWithBLOBs, "new"); - } - } - } - }); - } - - private void setApiParam(ApiDefinitionWithBLOBs apiDefinitionWithBLOBs, String versionId, ApiDefinitionWithBLOBs definitionWithBLOBs) { - apiDefinitionWithBLOBs.setVersionId(versionId); - apiDefinitionWithBLOBs.setNum(definitionWithBLOBs.getNum()); - apiDefinitionWithBLOBs.setStatus(definitionWithBLOBs.getStatus()); - apiDefinitionWithBLOBs.setOrder(definitionWithBLOBs.getOrder()); - apiDefinitionWithBLOBs.setRefId(definitionWithBLOBs.getRefId()); - apiDefinitionWithBLOBs.setLatest(definitionWithBLOBs.getLatest()); - apiDefinitionWithBLOBs.setCreateTime(definitionWithBLOBs.getCreateTime()); - apiDefinitionWithBLOBs.setUpdateTime(definitionWithBLOBs.getUpdateTime()); - } - - private void removeModulePath(Map moduleMap, Map> moduleOptionData, String modulePath) { - if (StringUtils.isBlank(modulePath)) { - return; - } - String[] pathTree = getPathTree(modulePath); - String lastPath = pathTree[pathTree.length - 1]; - String substring = modulePath.substring(0, modulePath.indexOf("/" + lastPath)); - if (moduleOptionData.get(substring) == null || moduleOptionData.get(substring).size() == 0) { - moduleMap.remove(substring); - removeModulePath(moduleMap, moduleOptionData, substring); - } - - } - - private void startCoverModule(List toUpdateList, List optionData, - Map methodPathMap, Map> repeatDataMap, - String updateVersionId, List optionDataCases, - Map> oldCaseMap) { - List coverApiList = new ArrayList<>(); - List updateApiList = new ArrayList<>(); - repeatDataMap.forEach((k, v) -> { - //导入的与系统是相同接口的数据 - ApiDefinitionWithBLOBs apiDefinitionWithBLOBs = methodPathMap.get(k); - if (apiDefinitionWithBLOBs != null) { - //该接口的case - Map> definitionIdCaseMAp = optionDataCases.stream().collect(Collectors.groupingBy(ApiTestCase::getApiDefinitionId)); - Map caseNameMap = getDistinctCaseNameMap(definitionIdCaseMAp, apiDefinitionWithBLOBs); - //循环系统内重复接口 - int i = 0; - for (ApiDefinitionWithBLOBs definitionWithBLOBs : v) { - if (!definitionWithBLOBs.getVersionId().equals(updateVersionId)) { - i += 1; - continue; - } - //组合case - if (MapUtils.isNotEmpty(caseNameMap)) { - buildCaseList(oldCaseMap, caseNameMap, definitionWithBLOBs, optionDataCases); - } - - ApiDefinitionWithBLOBs api = new ApiDefinitionWithBLOBs(); - BeanUtils.copyBean(api, apiDefinitionWithBLOBs); - api.setId(definitionWithBLOBs.getId()); - setApiParam(api, updateVersionId, definitionWithBLOBs); - coverApiList.add(api); - updateApiList.add(definitionWithBLOBs); - } - if (i == v.size()) { - //如果系统内的所有版本都不是当前选择的数据更新版本,则在数据更新版本这里新建数据 - addNewVersionApi(apiDefinitionWithBLOBs, v.get(0), "update"); - - } else { - optionData.remove(apiDefinitionWithBLOBs); - } - } - }); - buildOtherParam(toUpdateList, optionData, coverApiList, updateApiList); - } - - private void buildCaseList(Map> oldCaseMap, - Map caseNameMap, - ApiDefinitionWithBLOBs definitionWithBLOBs, List optionDataCases) { - //找出系统内重复接口的case,表里可能一个接口有多个同名case的可能 - List oldApiTestCases = oldCaseMap.get(definitionWithBLOBs.getId()); - - Map> oldCaseNameMap; - //如果同名重复用例有多个,则覆盖最后的那个 - if (CollectionUtils.isNotEmpty(oldApiTestCases)) { - oldCaseNameMap = oldApiTestCases.stream().collect(Collectors.groupingBy(ApiTestCase::getName)); - caseNameMap.forEach((name, importCaseWithBLOBs) -> { - //如果导入的有重名,覆盖,接口ID替换成系统内的 - importCaseWithBLOBs.setApiDefinitionId(definitionWithBLOBs.getId()); - List caseWithBLOBs = oldCaseNameMap.get(name); - if (CollectionUtils.isNotEmpty(caseWithBLOBs)) { - for (int i = 0; i < caseWithBLOBs.size(); i++) { - int version = 0; - if (caseWithBLOBs.get(i).getVersion() != null) { - version = caseWithBLOBs.get(i).getVersion() + 1; - } - if (i == 0) { - //被覆盖数据 - importCaseWithBLOBs.setId(caseWithBLOBs.get(i).getId()); - importCaseWithBLOBs.setNum(caseWithBLOBs.get(i).getNum()); - importCaseWithBLOBs.setVersion(version); - importCaseWithBLOBs.setCreateUserId(caseWithBLOBs.get(i).getCreateUserId()); - importCaseWithBLOBs.setUpdateUserId(caseWithBLOBs.get(i).getCreateUserId()); - } else { - //同名的旧数据处理 - caseWithBLOBs.get(i).setVersionId("old_case"); - optionDataCases.add(caseWithBLOBs.get(i)); - } - } - oldCaseNameMap.remove(name); - } - }); - //不同名的旧数据处理 - oldCaseNameMap.forEach((k, v) -> { - if (CollectionUtils.isNotEmpty(v)) { - for (ApiTestCaseWithBLOBs apiTestCaseWithBLOBs : v) { - apiTestCaseWithBLOBs.setVersionId("old_case"); - optionDataCases.add(apiTestCaseWithBLOBs); - } - } - }); - } else { - //否则直接给新增用例赋值新的接口ID - caseNameMap.forEach((name, caseWithBLOBs1) -> { - caseWithBLOBs1.setApiDefinitionId(definitionWithBLOBs.getId()); - caseWithBLOBs1.setVersion(0); - }); - } - } - - private Map getDistinctCaseNameMap(Map> definitionIdCaseMAp, ApiDefinitionWithBLOBs apiDefinitionWithBLOBs) { - if (MapUtils.isEmpty(definitionIdCaseMAp)) { - return null; - } - List caseWithBLOBs = definitionIdCaseMAp.get(apiDefinitionWithBLOBs.getId()); - if (CollectionUtils.isNotEmpty(caseWithBLOBs)) { - return caseWithBLOBs.stream().filter(t -> !StringUtils.equalsIgnoreCase("old_case", t.getVersionId())).collect(Collectors.toMap(ApiTestCase::getName, testCase -> testCase)); - } else { - return null; - } - } - - private void startCover(List toUpdateList, List optionData, - Map methodPathMap, Map> repeatDataMap, - String updateVersionId, List optionDataCases, - Map> oldCaseMap) { - List coverApiList = new ArrayList<>(); - List updateApiList = new ArrayList<>(); - repeatDataMap.forEach((k, v) -> { - ApiDefinitionWithBLOBs apiDefinitionWithBLOBs = methodPathMap.get(k); - if (apiDefinitionWithBLOBs != null) { - //该接口的case - Map> definitionIdCaseMAp = optionDataCases.stream().collect(Collectors.groupingBy(ApiTestCase::getApiDefinitionId)); - Map caseNameMap = getDistinctCaseNameMap(definitionIdCaseMAp, apiDefinitionWithBLOBs); - int i = 0; - for (ApiDefinitionWithBLOBs definitionWithBLOBs : v) { - if (!definitionWithBLOBs.getVersionId().equals(updateVersionId)) { - i += 1; - continue; - } - //组合case - if (MapUtils.isNotEmpty(caseNameMap)) { - buildCaseList(oldCaseMap, caseNameMap, definitionWithBLOBs, optionDataCases); - } - - ApiDefinitionWithBLOBs api = new ApiDefinitionWithBLOBs(); - BeanUtils.copyBean(api, apiDefinitionWithBLOBs); - api.setId(definitionWithBLOBs.getId()); - api.setModuleId(definitionWithBLOBs.getModuleId()); - api.setModulePath(definitionWithBLOBs.getModulePath()); - setApiParam(api, updateVersionId, definitionWithBLOBs); - coverApiList.add(api); - updateApiList.add(definitionWithBLOBs); - } - if (i == v.size()) { - //如果系统内的所有版本都不是当前选择的数据更新版本,则在数据更新版本这里新建数据 - addNewVersionApi(apiDefinitionWithBLOBs, v.get(0), "update"); - - } else { - optionData.remove(apiDefinitionWithBLOBs); - } - } - }); - buildOtherParam(toUpdateList, optionData, coverApiList, updateApiList); - } - - private Map judgeModuleMap(Map moduleMap, Map methodPathMap, Map> repeatDataMap) { - Set repeatKeys = repeatDataMap.keySet(); - Set importKeys = methodPathMap.keySet(); - List repeatKeyList = new ArrayList<>(repeatKeys); - List importKeysList = new ArrayList<>(importKeys); - List intersection = repeatKeyList.stream().filter(importKeysList::contains).collect(Collectors.toList()); - if (intersection.size() == importKeysList.size()) { - //导入文件没有新增接口无需创建接口模块 - moduleMap = new HashMap<>(); - } - return moduleMap; - } - - private void buildOtherParam(List toUpdateList, List optionData, List coverApiList, List updateApiList) { - optionData.addAll(coverApiList); - toUpdateList.addAll(updateApiList); - - } - private void setModule(Map moduleMap, Map> pidChildrenMap, Map idPathMap, Map idModuleMap, List data, ApiModuleDTO chooseModule) { for (ApiDefinitionWithBLOBs datum : data) { @@ -1482,25 +793,7 @@ public class ApiModuleService extends NodeTreeService { datum.setModulePath(idPathMap.get(chooseModule.getId())); } } - - private String getChooseModulePath(Map idPathMap, ApiModuleDTO chooseModule, String chooseModuleParentId) { - String s; - if (chooseModuleParentId.equals(PropertyConstant.ROOT)) { - s = "/" + chooseModule.getName(); - } else { - s = idPathMap.get(chooseModule.getId()); - } - return s; - } - - private String getChooseModuleParentId(ApiModuleDTO chooseModule) { - if (chooseModule.getParentId() == null) { - chooseModule.setParentId(PropertyConstant.ROOT); - } - return chooseModule.getParentId(); - } - - private String[] getPathTree(String modulePath) { + public String[] getPathTree(String modulePath) { String substring = modulePath.substring(0, 1); if (substring.equals("/")) { modulePath = modulePath.substring(1); diff --git a/api-test/frontend/src/business/automation/scenario/EditApiScenario.vue b/api-test/frontend/src/business/automation/scenario/EditApiScenario.vue index bd2acca7dd..bce0edc86e 100644 --- a/api-test/frontend/src/business/automation/scenario/EditApiScenario.vue +++ b/api-test/frontend/src/business/automation/scenario/EditApiScenario.vue @@ -231,6 +231,8 @@ ref="versionHistory" :version-data="versionData" :current-id="currentScenario.id" + :has-latest="hasLatest" + @setLatest="setLatest" @compare="compare" @checkout="checkout" @create="create" @@ -574,10 +576,12 @@ import { saveScenario, } from '@/business/automation/api-automation'; import MsComponentConfig from './component/ComponentConfig'; -import { ENV_TYPE } from 'metersphere-frontend/src/utils/constants'; -import { mergeRequestDocumentData } from '@/business/definition/api-definition'; -import { getEnvironmentByProjectId } from 'metersphere-frontend/src/api/environment'; -import { useApiStore } from '@/store'; +import {ENV_TYPE} from 'metersphere-frontend/src/utils/constants'; +import {mergeRequestDocumentData} from '@/business/definition/api-definition'; +import {getEnvironmentByProjectId} from 'metersphere-frontend/src/api/environment'; +import {useApiStore} from '@/store'; +import {getDefaultVersion, setLatestVersionById} from 'metersphere-frontend/src/api/version'; + const store = useApiStore(); @@ -785,6 +789,8 @@ export default { oldUserName: '', debugReportId: '', isPreventReClick: false, + latestVersionId: '', + hasLatest: false }; }, created() { @@ -808,7 +814,7 @@ export default { this.initPlugins(); }); - this.getVersionHistory(); + this.getDefaultVersion(); }, mounted() { this.$nextTick(() => { @@ -2479,6 +2485,16 @@ export default { this.currentItem = data; this.$refs.scenarioVariableAdvance.open(); }, + getDefaultVersion() { + if (!hasLicense()) { + return; + } + getDefaultVersion(this.projectId) + .then(response => { + this.latestVersionId = response.data; + this.getVersionHistory(); + }); + }, getVersionHistory() { if (!hasLicense()) { return; @@ -2489,6 +2505,12 @@ export default { } else { this.versionData = response.data; } + let latestVersionData = response.data.filter((v) => v.versionId === this.latestVersionId); + if (latestVersionData.length > 0) { + this.hasLatest = false + } else { + this.hasLatest = true; + } }); }, compare(row) { @@ -2540,6 +2562,18 @@ export default { }, }); }, + setLatest(row) { + let param = { + projectId: this.projectId, + type: 'SCENARIO', + versionId: row.id, + resourceId: this.currentScenario.id + } + setLatestVersionById(param).then(() => { + this.$success(this.$t('commons.modify_success')); + this.checkout(row); + }); + }, }, }; diff --git a/api-test/frontend/src/business/definition/components/complete/EditCompleteDubboApi.vue b/api-test/frontend/src/business/definition/components/complete/EditCompleteDubboApi.vue index 382847bbac..3db26247a5 100644 --- a/api-test/frontend/src/business/definition/components/complete/EditCompleteDubboApi.vue +++ b/api-test/frontend/src/business/definition/components/complete/EditCompleteDubboApi.vue @@ -40,10 +40,12 @@ ref="versionHistory" :version-data="versionData" :current-id="basisData.id" + :has-latest="hasLatest" + @setLatest="setLatest" @compare="compare" @checkout="checkout" @create="create" - @del="del" /> + @del="del"/> {{ $t('commons.save') }} @@ -92,7 +94,7 @@ import { delDefinitionByRefId, getDefinitionById, getDefinitionByIdAndRefId, - getDefinitionVersions, + getDefinitionVersions, updateDefinitionFollows, } from '@/api/definition'; import MsBasisApi from './BasisApi'; import MsBasisParameters from '../request/dubbo/BasisParameters'; @@ -105,7 +107,8 @@ import { createComponent } from '.././jmeter/components'; import { TYPE_TO_C } from '@/business/automation/scenario/Setting'; import MsDialogFooter from 'metersphere-frontend/src/components/MsDialogFooter'; import { useApiStore } from '@/store'; -import { apiTestCaseCount } from '@/api/api-test-case'; +import {apiTestCaseCount} from '@/api/api-test-case'; +import {getDefaultVersion, setLatestVersionById} from 'metersphere-frontend/src/api/version'; const store = useApiStore(); const { Body } = require('@/business/definition/model/ApiTestModel'); @@ -161,7 +164,7 @@ export default { } }); if (hasLicense()) { - this.getVersionHistory(); + this.getDefaultVersion(); } }, data() { @@ -175,6 +178,8 @@ export default { newRequest: {}, newResponse: {}, createNewVersionVisible: false, + latestVersionId: '', + hasLatest: false }; }, methods: { @@ -234,6 +239,13 @@ export default { } } }, + getDefaultVersion() { + getDefaultVersion(this.basisData.projectId) + .then(response => { + this.latestVersionId = response.data; + this.getVersionHistory(); + }); + }, getVersionHistory() { getDefinitionVersions(this.basisData.id).then((response) => { if (this.basisData.isCopy) { @@ -241,6 +253,12 @@ export default { } else { this.versionData = response.data; } + let latestVersionData = response.data.filter((v) => v.versionId === this.latestVersionId); + if (latestVersionData.length > 0) { + this.hasLatest = false + } else { + this.hasLatest = true; + } }); }, compare(row) { @@ -409,6 +427,19 @@ export default { }, }); }, + setLatest(row) { + let param = { + projectId: this.basisData.projectId, + type: 'API', + versionId: row.id, + resourceId: this.basisData.id + } + setLatestVersionById(param).then(() => { + this.$success(this.$t('commons.modify_success')); + this.checkout(row); + }); + }, + }, computed: {}, diff --git a/api-test/frontend/src/business/definition/components/complete/EditCompleteHTTPApi.vue b/api-test/frontend/src/business/definition/components/complete/EditCompleteHTTPApi.vue index 318991d835..45ab7cc218 100644 --- a/api-test/frontend/src/business/definition/components/complete/EditCompleteHTTPApi.vue +++ b/api-test/frontend/src/business/definition/components/complete/EditCompleteHTTPApi.vue @@ -39,10 +39,13 @@ ref="versionHistory" :version-data="versionData" :current-id="httpForm.id" + :has-latest="hasLatest" @compare="compare" @checkout="checkout" @create="create" - @del="del" /> + @setLatest="setLatest" + @del="del" + /> { + this.latestVersionId = response.data; + this.getVersionHistory(); + }); + }, getVersionHistory() { getDefinitionVersions(this.httpForm.id).then((response) => { if (this.httpForm.isCopy) { @@ -768,6 +782,12 @@ export default { } else { this.versionData = response.data; } + let latestVersionData = response.data.filter((v) => v.versionId === this.latestVersionId); + if (latestVersionData.length > 0) { + this.hasLatest = false + } else { + this.hasLatest = true; + } }); }, compare(row) { @@ -954,6 +974,18 @@ export default { }, }); }, + setLatest(row) { + let param = { + projectId: this.projectId, + type: 'API', + versionId: row.id, + resourceId: this.basisData.id + } + setLatestVersionById(param).then(() => { + this.$success(this.$t('commons.modify_success')); + this.checkout(row); + }); + }, gotoApiMessage() { let apiResolve = this.$router.resolve({ path: '/project/messagesettings', @@ -1041,7 +1073,7 @@ export default { this.initMockEnvironment(); if (hasLicense()) { - this.getVersionHistory(); + this.getDefaultVersion(); this.getSyncRule(); this.getCitedScenarioCount(); this.getCaseCount(); diff --git a/api-test/frontend/src/business/definition/components/complete/EditCompleteSQLApi.vue b/api-test/frontend/src/business/definition/components/complete/EditCompleteSQLApi.vue index 9346d53b38..e9591f9bee 100644 --- a/api-test/frontend/src/business/definition/components/complete/EditCompleteSQLApi.vue +++ b/api-test/frontend/src/business/definition/components/complete/EditCompleteSQLApi.vue @@ -40,9 +40,11 @@ ref="versionHistory" :version-data="versionData" :current-id="basisData.id" + :has-latest="hasLatest" @compare="compare" @checkout="checkout" @create="create" + @setLatest="setLatest" @del="del" /> {{ $t('commons.save') }} @@ -101,14 +103,15 @@ import MsBasisApi from './BasisApi'; import MsBasisParameters from '../request/database/BasisParameters'; import MsChangeHistory from '@/business/history/ApiHistory'; import ApiOtherInfo from '@/business/definition/components/complete/ApiOtherInfo'; -import { getCurrentUser } from 'metersphere-frontend/src/utils/token'; -import { hasLicense } from 'metersphere-frontend/src/utils/permission'; +import {getCurrentProjectID, getCurrentUser} from 'metersphere-frontend/src/utils/token'; +import {hasLicense} from 'metersphere-frontend/src/utils/permission'; import SQLApiVersionDiff from './version/SQLApiVersionDiff'; import { createComponent } from '.././jmeter/components'; import { TYPE_TO_C } from '@/business/automation/scenario/Setting'; import MsDialogFooter from 'metersphere-frontend/src/components/MsDialogFooter'; import { useApiStore } from '@/store'; -import { apiTestCaseCount } from '@/api/api-test-case'; +import {apiTestCaseCount} from '@/api/api-test-case'; +import {getDefaultVersion, setLatestVersionById} from 'metersphere-frontend/src/api/version'; const store = useApiStore(); const { Body } = require('@/business/definition/model/ApiTestModel'); @@ -164,6 +167,8 @@ export default { newRequest: {}, newResponse: {}, createNewVersionVisible: false, + latestVersionId: '', + hasLatest: false }; }, created() { @@ -178,7 +183,7 @@ export default { }); if (hasLicense()) { - this.getVersionHistory(); + this.getDefaultVersion(); } if (!this.request.environmentId) { @@ -244,6 +249,13 @@ export default { } } }, + getDefaultVersion() { + getDefaultVersion(getCurrentProjectID()) + .then(response => { + this.latestVersionId = response.data; + this.getVersionHistory(); + }); + }, getVersionHistory() { getDefinitionVersions(this.basisData.id).then((response) => { if (this.basisData.isCopy) { @@ -251,6 +263,12 @@ export default { } else { this.versionData = response.data; } + let latestVersionData = response.data.filter((v) => v.versionId === this.latestVersionId); + if (latestVersionData.length > 0) { + this.hasLatest = false + } else { + this.hasLatest = true; + } }); }, compare(row) { @@ -421,6 +439,19 @@ export default { }, }); }, + setLatest(row) { + let param = { + projectId: getCurrentProjectID(), + type: 'API', + versionId: row.id, + resourceId: this.basisData.id + } + setLatestVersionById(param).then(() => { + this.$success(this.$t('commons.modify_success')); + this.checkout(row); + }); + }, + }, }; diff --git a/api-test/frontend/src/business/definition/components/complete/EditCompleteTCPApi.vue b/api-test/frontend/src/business/definition/components/complete/EditCompleteTCPApi.vue index 2f07ac6169..5fe48e2ca6 100644 --- a/api-test/frontend/src/business/definition/components/complete/EditCompleteTCPApi.vue +++ b/api-test/frontend/src/business/definition/components/complete/EditCompleteTCPApi.vue @@ -40,9 +40,11 @@ ref="versionHistory" :version-data="versionData" :current-id="basisData.id" + :has-latest="hasLatest" @compare="compare" @checkout="checkout" @create="create" + @setLatest="setLatest" @del="del" /> {{ $t('commons.save') }} @@ -110,7 +112,7 @@ import { delDefinitionByRefId, getDefinitionById, getDefinitionByIdAndRefId, - getDefinitionVersions, + getDefinitionVersions, updateDefinitionFollows, } from '@/api/definition'; import MsTcpBasicApi from './TCPBasicApi'; import MsTcpFormatParameters from '../request/tcp/TcpFormatParameters'; @@ -123,7 +125,8 @@ import { createComponent } from '.././jmeter/components'; import { TYPE_TO_C } from '@/business/automation/scenario/Setting'; import MsDialogFooter from 'metersphere-frontend/src/components/MsDialogFooter'; import { useApiStore } from '@/store'; -import { apiTestCaseCount } from '@/api/api-test-case'; +import {apiTestCaseCount} from '@/api/api-test-case'; +import {getDefaultVersion, setLatestVersionById} from 'metersphere-frontend/src/api/version'; const store = useApiStore(); const { Body } = require('@/business/definition/model/ApiTestModel'); @@ -171,6 +174,8 @@ export default { newResponse: {}, newApiProtocol: 'TCP', createNewVersionVisible: false, + latestVersionId: '', + hasLatest: false }; }, created: function () { @@ -200,7 +205,7 @@ export default { }); this.getMockInfo(); if (hasLicense()) { - this.getVersionHistory(); + this.getDefaultVersion(); } }, watch: { @@ -340,6 +345,14 @@ export default { } } }, + getDefaultVersion() { + getDefaultVersion(getCurrentProjectID()) + .then(response => { + this.latestVersionId = response.data; + this.getVersionHistory(); + }); + }, + getVersionHistory() { getDefinitionVersions(this.basisData.id).then((response) => { if (this.basisData.isCopy) { @@ -347,6 +360,12 @@ export default { } else { this.versionData = response.data; } + let latestVersionData = response.data.filter((v) => v.versionId === this.latestVersionId); + if (latestVersionData.length > 0) { + this.hasLatest = false + } else { + this.hasLatest = true; + } }); }, compare(row) { @@ -500,7 +519,7 @@ export default { this.$set(this.basisData, 'newVersionCase', this.basisData.caseTotal > 0); createMockConfig({ - projectId: this.projectId, + projectId: getCurrentProjectID(), apiId: this.basisData.id, }).then((response) => { this.$set(this.basisData, 'newVersionMock', response.data.mockExpectConfigList.length > 0); @@ -534,6 +553,18 @@ export default { }, }); }, + setLatest(row) { + let param = { + projectId: getCurrentProjectID(), + type: 'API', + versionId: row.id, + resourceId: this.basisData.id + } + setLatestVersionById(param).then(() => { + this.$success(this.$t('commons.modify_success')); + this.checkout(row); + }); + }, }, }; diff --git a/framework/sdk-parent/frontend/src/api/version.js b/framework/sdk-parent/frontend/src/api/version.js index d49f7e8f16..489f8428ef 100644 --- a/framework/sdk-parent/frontend/src/api/version.js +++ b/framework/sdk-parent/frontend/src/api/version.js @@ -1,10 +1,14 @@ -import {get} from "../plugins/request" +import {get, post} from "../plugins/request" import {hasLicense} from "../utils/permission"; export function getProjectVersions(projectId) { return get(`/project/version/get-project-versions/${projectId}`); } +export function getDefaultVersion(projectId) { + return get(`/project/version/get-default-version/${projectId}`); +} + export function getProjectMembers() { return get('/user/project/member/list'); } @@ -13,6 +17,10 @@ export function isProjectVersionEnable(projectId) { return get(`/project/version/enable/${projectId}`) } +export function setLatestVersionById(data) { + return post('/project/version/set/latest/version', data) +} + export function getVersionFilters(projectId) { return hasLicense() && projectId ? new Promise((resolve) => { getProjectVersions(projectId) diff --git a/framework/sdk-parent/frontend/src/components/MsBorderPieChart.vue b/framework/sdk-parent/frontend/src/components/MsBorderPieChart.vue index 910d3a9475..1bfc346967 100644 --- a/framework/sdk-parent/frontend/src/components/MsBorderPieChart.vue +++ b/framework/sdk-parent/frontend/src/components/MsBorderPieChart.vue @@ -70,6 +70,7 @@ export default { { type: 'pie', radius: this.radius, + minAngle: 3, itemStyle: { borderRadius: 0, borderColor: '#fff', @@ -77,7 +78,7 @@ export default { }, label: { show: false, - position:'outside', + position: 'outside', formatter: '{c}, {d}%', }, center: ['126', '128'], @@ -270,15 +271,16 @@ export default { } }, data:this.pieData, - formatter:function(name){ + formatter:function(name) { //通过name获取到数组对象中的单个对象 - let singleData = self.pieData.filter(function(item){ - return item.name === name - }) - if (!singleData[0].value) { - singleData[0].value = 0; + let singleData = self.pieData.filter(function (item) { + return item.name === name; + }); + let showValue = singleData[0].value; + if (showValue === '-') { + showValue = 0; } - return [`{name|${name}}`, `{num|${singleData[0].value}}`].join(""); + return [`{name|${name}}`, `{num|${showValue}}`].join(''); } }; }) diff --git a/framework/sdk-parent/frontend/src/components/version/MxVersionHistory.vue b/framework/sdk-parent/frontend/src/components/version/MxVersionHistory.vue index e24702978b..185b77e79b 100644 --- a/framework/sdk-parent/frontend/src/components/version/MxVersionHistory.vue +++ b/framework/sdk-parent/frontend/src/components/version/MxVersionHistory.vue @@ -51,6 +51,11 @@ {{ $t('commons.delete') }}  + + + {{ $t('project.version.set_new') }}  + @@ -66,7 +71,7 @@ import {getCurrentProjectID} from "../../utils/token"; import {hasLicense} from "../../utils/permission"; -import {getProjectMembers, getProjectVersions, isProjectVersionEnable} from "../../api/version"; +import {getDefaultVersion, getProjectMembers, getProjectVersions, isProjectVersionEnable} from "../../api/version"; export default { name: "MxVersionHistory", @@ -88,7 +93,11 @@ export default { default() { return getCurrentProjectID(); } - } + }, + hasLatest: { + type: Boolean, + default: false + }, }, data() { return { @@ -144,6 +153,10 @@ export default { this.loading = true; this.$emit('del', row); }, + setLatest(row) { + this.loading = true; + this.$emit('setLatest', row); + }, handleVersionOptions() { let versionData = this.versionData; if (versionData.length === 0) { diff --git a/framework/sdk-parent/frontend/src/i18n/lang/en-US.js b/framework/sdk-parent/frontend/src/i18n/lang/en-US.js index cd25d9bf7b..c7740f7840 100644 --- a/framework/sdk-parent/frontend/src/i18n/lang/en-US.js +++ b/framework/sdk-parent/frontend/src/i18n/lang/en-US.js @@ -888,6 +888,7 @@ const message = { delete_tip: 'This version has associated system resources, this operation will delete the associated resources', checkout: 'Checkout', compare: 'Compare', + set_new: 'SetNew', change_latest_tip: 'This operation will modify the default display of the interface, scene, test case and other list pages, which may take some time. Please wait! ' }, project_file: { @@ -1914,8 +1915,8 @@ const message = { no_cover_tip_scenario_1: "1. The same Scenario that already exists in the system will not be changed", no_cover_tip_scenario_2: "2. Add Scenario that do not exist in the system", import_version: 'Import version', - data_update_version: 'Api update version', - data_new_version: 'Api creation version', + data_update_version: 'New API created to', + data_new_version: 'The same API is updated to', latest_version: 'Latest version', }, home_page: { diff --git a/framework/sdk-parent/frontend/src/i18n/lang/zh-CN.js b/framework/sdk-parent/frontend/src/i18n/lang/zh-CN.js index c4fbea9654..f6154e836c 100644 --- a/framework/sdk-parent/frontend/src/i18n/lang/zh-CN.js +++ b/framework/sdk-parent/frontend/src/i18n/lang/zh-CN.js @@ -897,6 +897,7 @@ const message = { delete_tip: '此版本已经关联系统资源,此操作会将所关联的资源一并删除', checkout: '切换', compare: '对比', + set_new: '置新', change_latest_tip: '此操作会修改接口,场景,测试用例等列表页面的默认展示,可能会消耗一些时间。请耐心等待!' }, project_file: { @@ -1923,8 +1924,8 @@ const message = { no_cover_tip_scenario_1: "1. 系统已存在的同一场景,则不做变更", no_cover_tip_scenario_2: "2. 系统不存在的场景则新增", import_version: '导入版本', - data_update_version: '数据更新版本', - data_new_version: '数据创建版本', + data_update_version: '同一API更新到', + data_new_version: '新增API创建到', latest_version: '最新版本', }, home_page: { diff --git a/framework/sdk-parent/frontend/src/i18n/lang/zh-TW.js b/framework/sdk-parent/frontend/src/i18n/lang/zh-TW.js index bbfa93c43a..43122e0c18 100644 --- a/framework/sdk-parent/frontend/src/i18n/lang/zh-TW.js +++ b/framework/sdk-parent/frontend/src/i18n/lang/zh-TW.js @@ -893,6 +893,7 @@ const message = { delete_tip: '此版本已經關聯繫統資源,此操作會將所關聯的資源一併刪除', checkout: '切換', compare: '對比', + set_new: '置新', change_latest_tip: '此操作會修改接口,場景,測試用例等列表頁面的默認展示,可能會消耗一些時間。請耐心等待! ' }, project_file: { @@ -1919,8 +1920,8 @@ const message = { no_cover_tip_1: "1. 系統已存在的同一介面,則不做變更", no_cover_tip_2: "2. 系統不存在的介面則新增", import_version: '導入版本', - data_update_version: '數據更新版本', - data_new_version: '數據創建版本', + data_update_version: '同一API更新到', + data_new_version: '新增API創建到', latest_version: '最新版本', }, home_page: { diff --git a/framework/sdk-parent/sdk/src/main/java/io/metersphere/request/ProjectVersionRequest.java b/framework/sdk-parent/sdk/src/main/java/io/metersphere/request/ProjectVersionRequest.java index f9e303cdc3..98bea954b5 100644 --- a/framework/sdk-parent/sdk/src/main/java/io/metersphere/request/ProjectVersionRequest.java +++ b/framework/sdk-parent/sdk/src/main/java/io/metersphere/request/ProjectVersionRequest.java @@ -16,4 +16,8 @@ public class ProjectVersionRequest { private List orders; private Map> filters; private Map combine; + private String type; + private String versionId; + private String resourceId; + } diff --git a/framework/sdk-parent/xpack-interface/src/main/java/io/metersphere/xpack/version/service/ProjectVersionService.java b/framework/sdk-parent/xpack-interface/src/main/java/io/metersphere/xpack/version/service/ProjectVersionService.java index a87c0eb06d..4572e11644 100644 --- a/framework/sdk-parent/xpack-interface/src/main/java/io/metersphere/xpack/version/service/ProjectVersionService.java +++ b/framework/sdk-parent/xpack-interface/src/main/java/io/metersphere/xpack/version/service/ProjectVersionService.java @@ -22,4 +22,6 @@ public interface ProjectVersionService { void changeStatus(String id, String status); List getProjectVersionByIds(List versionIds); + + void setLatestVersionById(ProjectVersionRequest request); } diff --git a/performance-test/frontend/src/business/test/EditPerformanceTest.vue b/performance-test/frontend/src/business/test/EditPerformanceTest.vue index b0fc3ba77d..7144feb37b 100644 --- a/performance-test/frontend/src/business/test/EditPerformanceTest.vue +++ b/performance-test/frontend/src/business/test/EditPerformanceTest.vue @@ -34,6 +34,8 @@ :version-data="versionData" :current-id="testId" :is-read="isReadOnly" + :has-latest="hasLatest" + @setLatest="setLatest" @compare="compare" @checkout="checkout" @create="create" @del="del"/> { + this.latestVersionId = response.data; + this.getVersionHistory(); + }); + }, getVersionHistory() { let testId = undefined; if (this.testId) { @@ -600,6 +612,12 @@ export default { getTestVersionHistory(testId) .then(response => { this.versionData = response.data; + let latestVersionData = response.data.filter((v) => v.versionId === this.latestVersionId); + if (latestVersionData.length > 0) { + this.hasLatest = false + } else { + this.hasLatest = true; + } }); }, compare(row) { @@ -661,6 +679,18 @@ export default { } }); }, + setLatest(row) { + let param = { + projectId: getCurrentProjectID(), + type: 'PERFORMANCE', + versionId: row.id, + resourceId: this.test.id + } + setLatestVersionById(param).then(() => { + this.$success(this.$t('commons.modify_success')); + this.checkout(row); + }); + }, handleProjectChange() { if (this.$route.path.startsWith('/performance/test/edit')) { this.$nextTick(() => { diff --git a/test-track/frontend/src/business/case/components/TestCaseEdit.vue b/test-track/frontend/src/business/case/components/TestCaseEdit.vue index 2a512046f7..e8d50854cb 100644 --- a/test-track/frontend/src/business/case/components/TestCaseEdit.vue +++ b/test-track/frontend/src/business/case/components/TestCaseEdit.vue @@ -46,6 +46,8 @@ :is-read="currentTestCaseInfo.trashEnable || readOnly" @confirmOtherInfo="confirmOtherInfo" :current-project-id="currentProjectId" + :has-latest="hasLatest" + @setLatest="setLatest" @compare="compare" @checkout="checkout" @create="create" @del="del"/> { this.loading = false; @@ -990,6 +995,13 @@ export default { }); } }, + getDefaultVersion() { + getDefaultVersion(this.projectId) + .then(response => { + this.latestVersionId = response.data; + this.getVersionHistory(); + }); + }, getVersionHistory() { getTestCaseVersions(this.currentTestCaseInfo.id) .then(response => { @@ -1001,6 +1013,12 @@ export default { this.currentProjectId = getCurrentProjectID(); } this.versionData = response.data; + let latestVersionData = response.data.filter((v) => v.versionId === this.latestVersionId); + if (latestVersionData.length > 0) { + this.hasLatest = false + } else { + this.hasLatest = true; + } if (this.$refs.versionHistory) { this.$refs.versionHistory.loading = false; } @@ -1122,16 +1140,28 @@ export default { } }); }, + setLatest(row) { + let param = { + projectId: getCurrentProjectID(), + type: 'TEST_CASE', + versionId: row.id, + resourceId: this.currentTestCaseInfo.id + } + setLatestVersionById(param).then(() => { + this.$success(this.$t('commons.modify_success')); + this.checkout(row); + }); + }, hasOtherInfo() { return new Promise((resolve) => { - if (this.form.id) { - hasTestCaseOtherInfo(this.form.id) - .then((res) => { - resolve(res.data); - }); - } else { - resolve(); - } + if (this.form.id) { + hasTestCaseOtherInfo(this.form.id) + .then((res) => { + resolve(res.data); + }); + } else { + resolve(); + } } ); }, diff --git a/workstation/frontend/src/api/issue.js b/workstation/frontend/src/api/issue.js index 2e385753ca..b9c7b41071 100644 --- a/workstation/frontend/src/api/issue.js +++ b/workstation/frontend/src/api/issue.js @@ -4,7 +4,6 @@ import {hasLicense} from "metersphere-frontend/src/utils/permission"; import {getCurrentProjectID} from "metersphere-frontend/src/utils/token"; import {getUUID} from "metersphere-frontend/src/utils"; import {getCurrentProject} from "@/api/project"; -import {JIRA} from "metersphere-frontend/src/utils/constants"; export function getJiraIssueType(param) { return post('/issues/jira/issuetype', param); @@ -33,24 +32,27 @@ export function getDashboardIssues(page) { export function getIssuePartTemplateWithProject(callback) { getCurrentProject().then((response) => { let currentProject = response.data; - if (enableThirdPartTemplate(currentProject)) { - getIssueThirdPartTemplate() - .then((template) => { - if (callback) - callback(template, currentProject); - }); - } else { - getIssueTemplate() - .then((template) => { - if (callback) - callback(template, currentProject); - }); - } + enableThirdPartTemplate(currentProject.id) + .then((r) => { + if (r.data) { + getIssueThirdPartTemplate() + .then((template) => { + if (callback) + callback(template, currentProject); + }); + } else { + getIssueTemplate() + .then((template) => { + if (callback) + callback(template, currentProject); + }); + } + }); }); } -export function enableThirdPartTemplate(currentProject) { - return currentProject && currentProject.thirdPartTemplate && currentProject.platform === JIRA; +export function enableThirdPartTemplate(projectId) { + return get('/issues/third/part/template/enable/' + projectId); } export function getIssueThirdPartTemplate() { @@ -76,3 +78,7 @@ export function getIssuesWeekCount(workstationId) { return get('/workstation/issue/week/count/'+workstationId); } +export function getPlatformOption() { + return get( '/issues/platform/option'); +} + diff --git a/workstation/frontend/src/business/component/IssueTableList.vue b/workstation/frontend/src/business/component/IssueTableList.vue index 6d4f8221ea..3a102b6a89 100644 --- a/workstation/frontend/src/business/component/IssueTableList.vue +++ b/workstation/frontend/src/business/component/IssueTableList.vue @@ -157,7 +157,7 @@ import MsTableButton from "metersphere-frontend/src/components/MsTableButton"; import MsTablePagination from "metersphere-frontend/src/components/pagination/TablePagination"; import {ISSUE_PLATFORM_OPTION, ISSUE_STATUS_MAP, SYSTEM_FIELD_NAME_MAP} from "metersphere-frontend/src/utils/table-constants"; import MsTableHeader from "metersphere-frontend/src/components/MsTableHeader"; -import {getDashboardIssues, getIssuePartTemplateWithProject, getIssues} from "@/api/issue"; +import {getDashboardIssues, getIssuePartTemplateWithProject, getIssues, getPlatformOption} from "@/api/issue"; import { getCustomFieldValue, getCustomTableWidth, @@ -192,10 +192,14 @@ export default { issueTemplate: {}, members: [], isThirdPart: false, + platformOptions: [], }; }, activated() { - + getPlatformOption() + .then((r) => { + this.platformOptions = r.data; + }); }, props: { isFocus: { @@ -227,7 +231,9 @@ export default { }, computed: { platformFilters() { - return ISSUE_PLATFORM_OPTION; + let options = [...ISSUE_PLATFORM_OPTION]; + options.push(...this.platformOptions); + return options; }, issueStatusMap() { return ISSUE_STATUS_MAP; diff --git a/workstation/frontend/src/business/dashboard/components/MyCaseCard.vue b/workstation/frontend/src/business/dashboard/components/MyCaseCard.vue index 5e36081bcc..ca14c4a5ac 100644 --- a/workstation/frontend/src/business/dashboard/components/MyCaseCard.vue +++ b/workstation/frontend/src/business/dashboard/components/MyCaseCard.vue @@ -43,26 +43,26 @@ export default{ this.result = getMyCreatedCaseGroupContMap(isWeek).then(response => { let tableData = response.data const testCaseCount = { - value:tableData.testCaseCount===0?'':tableData.testCaseCount, - name:this.$t('workstation.table_name.track_case'), + value: tableData.testCaseCount === 0 ? '-' : tableData.testCaseCount, + name: this.$t('workstation.table_name.track_case'), } this.loadCharData.push(testCaseCount) const apiTestCaseCount = { - value:tableData.apiTestCaseCount===0?'':tableData.apiTestCaseCount, - name:this.$t('workstation.table_name.api_case'), + value: tableData.apiTestCaseCount === 0 ? '-' : tableData.apiTestCaseCount, + name: this.$t('workstation.table_name.api_case'), } this.loadCharData.push(apiTestCaseCount) const apiScenarioCaseCount = { - value:tableData.apiScenarioCaseCount===0?'':tableData.apiScenarioCaseCount, - name:this.$t('workstation.table_name.scenario_case'), + value: tableData.apiScenarioCaseCount === 0 ? '-' : tableData.apiScenarioCaseCount, + name: this.$t('workstation.table_name.scenario_case'), } this.loadCharData.push(apiScenarioCaseCount) const loadTestCount = { - value:tableData.loadTestCount===0?'':tableData.loadTestCount, - name:this.$t('test_track.plan.load_case.case'), + value: tableData.loadTestCount === 0 ? '-' : tableData.loadTestCount, + name: this.$t('test_track.plan.load_case.case'), } this.loadCharData.push(loadTestCount)