diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/controller/definition/ApiDefinitionController.java b/backend/services/api-test/src/main/java/io/metersphere/api/controller/definition/ApiDefinitionController.java index 57b2b790fb..86c08130be 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/controller/definition/ApiDefinitionController.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/controller/definition/ApiDefinitionController.java @@ -202,7 +202,7 @@ public class ApiDefinitionController { @RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_IMPORT) @Operation(summary = "接口测试-接口管理-导入接口定义") public ApiDefinitionImport testCaseImport(@RequestPart(value = "file", required = false) MultipartFile file, @RequestPart("request") ImportRequest request) { - return apiDefinitionService.apiTestImport(file, request, SessionUtils.getUserId(), SessionUtils.getCurrentProjectId()); + return apiDefinitionService.apiTestImport(file, request, SessionUtils.getUser(), SessionUtils.getCurrentProjectId()); } @PostMapping("/operation-history") diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/controller/definition/ApiDefinitionModuleController.java b/backend/services/api-test/src/main/java/io/metersphere/api/controller/definition/ApiDefinitionModuleController.java index 68c1a5ef8a..36483cad45 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/controller/definition/ApiDefinitionModuleController.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/controller/definition/ApiDefinitionModuleController.java @@ -89,7 +89,7 @@ public class ApiDefinitionModuleController { @RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_READ) @CheckOwner(resourceId = "#request.projectId", resourceType = "project") public List getTrashTree(@RequestBody @Validated ApiModuleRequest request) { - return apiDefinitionModuleService.getTrashTree(request, true); + return apiDefinitionModuleService.getTrashTree(request); } @PostMapping("/env/tree") diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/controller/definition/ApiTestCaseController.java b/backend/services/api-test/src/main/java/io/metersphere/api/controller/definition/ApiTestCaseController.java index 2e7869de7d..a564fb8494 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/controller/definition/ApiTestCaseController.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/controller/definition/ApiTestCaseController.java @@ -216,7 +216,7 @@ public class ApiTestCaseController { public Pager> operationHistoryList(@Validated @RequestBody OperationHistoryRequest request) { Page page = PageHelper.startPage(request.getCurrent(), request.getPageSize(), StringUtils.isNotBlank(request.getSortString()) ? request.getSortString() : "create_time desc"); - return PageUtils.setPageInfo(page, apiTestCaseService.getHistoryList(request)); + return PageUtils.setPageInfo(page, apiTestCaseService.operationHistoryList(request)); } diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/dto/request/ImportRequest.java b/backend/services/api-test/src/main/java/io/metersphere/api/dto/request/ImportRequest.java index 6804a89aa1..d1b91244f9 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/dto/request/ImportRequest.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/dto/request/ImportRequest.java @@ -14,7 +14,7 @@ public class ImportRequest { private String projectId; @Schema(description = "导入的swagger地址") private String swaggerUrl; - @Schema(description = "如果是定时任务的时候 需要传入创建人id") + @Schema(description = "如果是定时任务的时候 需要传入创建人id", requiredMode = Schema.RequiredMode.REQUIRED) private String userId; private String versionId; // 新导入选择的版本 private String updateVersionId; // 覆盖导入已存在的接口选择的版本 diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/parser/api/Swagger3Parser.java b/backend/services/api-test/src/main/java/io/metersphere/api/parser/api/Swagger3Parser.java index 08a879b2f7..6f7652a5c6 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/parser/api/Swagger3Parser.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/parser/api/Swagger3Parser.java @@ -14,7 +14,6 @@ import io.metersphere.api.utils.ApiDataUtils; import io.metersphere.sdk.exception.MSException; import io.metersphere.sdk.util.LogUtils; import io.metersphere.sdk.util.Translator; -import io.metersphere.system.utils.SessionUtils; import io.swagger.parser.OpenAPIParser; import io.swagger.v3.oas.models.*; import io.swagger.v3.oas.models.media.*; @@ -283,11 +282,7 @@ public class Swagger3Parser implements ImportParser { apiDefinition.setProtocol("HTTP"); apiDefinition.setMethod(method); apiDefinition.setProjectId(this.projectId); - if (StringUtils.equalsIgnoreCase("schedule", importRequest.getType())) { - apiDefinition.setCreateUser(importRequest.getUserId()); - } else { - apiDefinition.setCreateUser(SessionUtils.getUserId()); - } + apiDefinition.setCreateUser(importRequest.getUserId()); apiDefinition.setModulePath(CollectionUtils.isNotEmpty(operation.getTags()) ? StringUtils.join("/", operation.getTags().get(0)) : StringUtils.EMPTY); apiDefinition.setResponse(new ArrayList<>()); return apiDefinition; @@ -622,6 +617,9 @@ public class Swagger3Parser implements ImportParser { } else { itemsSchema = items; } + if (itemsSchema == null) { + return jsonSchemaArray; + } if (itemsSchema instanceof IntegerSchema integerSchema) { jsonSchemaArray.setItems(parseInteger(integerSchema)); } else if (itemsSchema instanceof StringSchema stringSchema) { diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiDefinitionImportUtilService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiDefinitionImportUtilService.java index d65667949d..43bd220e08 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiDefinitionImportUtilService.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiDefinitionImportUtilService.java @@ -34,11 +34,15 @@ import io.metersphere.sdk.exception.MSException; import io.metersphere.sdk.util.BeanUtils; import io.metersphere.sdk.util.JSON; import io.metersphere.sdk.util.Translator; +import io.metersphere.system.dto.sdk.ApiDefinitionCaseDTO; import io.metersphere.system.dto.sdk.BaseTreeNode; +import io.metersphere.system.dto.sdk.SessionUser; import io.metersphere.system.log.constants.OperationLogModule; import io.metersphere.system.log.constants.OperationLogType; import io.metersphere.system.log.dto.LogDTO; import io.metersphere.system.log.service.OperationLogService; +import io.metersphere.system.notice.constants.NoticeConstants; +import io.metersphere.system.notice.sender.AfterReturningNoticeSendService; import io.metersphere.system.uid.IDGenerator; import io.metersphere.system.uid.NumGenerator; import jakarta.annotation.Resource; @@ -60,7 +64,6 @@ import java.util.stream.Collectors; public class ApiDefinitionImportUtilService { private static final String UNPLANNED_API = "api_unplanned_request"; - private static final String MODULE = "modulePath"; public static final Long ORDER_STEP = 5000L; private final ThreadLocal currentApiOrder = new ThreadLocal<>(); private final ThreadLocal currentApiCaseOrder = new ThreadLocal<>(); @@ -78,33 +81,37 @@ public class ApiDefinitionImportUtilService { @Resource private ApiDefinitionBlobMapper apiDefinitionBlobMapper; @Resource - private ApiTestCaseService apiTestCaseService; - @Resource private ApiDefinitionModuleService apiDefinitionModuleService; @Resource private ProjectMapper projectMapper; @Resource private OperationLogService operationLogService; + @Resource + private AfterReturningNoticeSendService afterReturningNoticeSendService; + + private static final String FILE_JMX = "jmx"; + private static final String FILE_HAR = "har"; + private static final String FILE_JSON = "json"; public void checkFileSuffixName(ImportRequest request, String suffixName) { - if ("jmx".equalsIgnoreCase(suffixName)) { + if (FILE_JMX.equalsIgnoreCase(suffixName)) { if (!ApiImportPlatform.Jmeter.name().equalsIgnoreCase(request.getPlatform())) { throw new MSException(Translator.get("file_format_does_not_meet_requirements")); } } - if ("har".equalsIgnoreCase(suffixName)) { + if (FILE_HAR.equalsIgnoreCase(suffixName)) { if (!ApiImportPlatform.Har.name().equalsIgnoreCase(request.getPlatform())) { throw new MSException(Translator.get("file_format_does_not_meet_requirements")); } } - if ("json".equalsIgnoreCase(suffixName)) { + if (FILE_JSON.equalsIgnoreCase(suffixName)) { if (ApiImportPlatform.Har.name().equalsIgnoreCase(request.getPlatform()) || ApiImportPlatform.Jmeter.name().equalsIgnoreCase(request.getPlatform())) { throw new MSException(Translator.get("file_format_does_not_meet_requirements")); } } } - public void importApi(ImportRequest request, ApiDefinitionImport apiImport) { + public void importApi(ImportRequest request, ApiDefinitionImport apiImport, SessionUser user) { String defaultVersion = extBaseProjectVersionMapper.getDefaultVersion(request.getProjectId()); request.setDefaultVersion(defaultVersion); if (request.getVersionId() == null) { @@ -126,11 +133,11 @@ public class ApiDefinitionImportUtilService { } //处理数据,判断数据是否重复 - dealWithData(request, filterData); + dealWithData(request, filterData, user); } - private void dealWithData(ImportRequest request, List importData) { + private void dealWithData(ImportRequest request, List importData, SessionUser user) { //查询数据库中所有的数据, 用于判断是否重复 ApiDefinitionPageRequest pageRequest = new ApiDefinitionPageRequest(); pageRequest.setProjectId(request.getProjectId()); @@ -139,12 +146,12 @@ public class ApiDefinitionImportUtilService { List apiLists = extApiDefinitionMapper.importList(pageRequest); List apiModules = this.buildTreeData(request.getProjectId(), request.getProtocol()); //将apiModules转换成新的map 要求key是attachInfo中的modulePath 使用stream实现 - Map modulePathMap = apiModules.stream().collect(Collectors.toMap(t -> t.getAttachInfo().get(MODULE), t -> t)); + Map modulePathMap = apiModules.stream().collect(Collectors.toMap(BaseTreeNode::getPath, t -> t)); Map idModuleMap = apiModules.stream().collect(Collectors.toMap(BaseTreeNode::getId, apiModuleDTO -> apiModuleDTO)); //如果导入的时候选择了模块,需要把所有的导入数据的模块路径前面拼接上选择的模块路径 if (StringUtils.isNotBlank(request.getModuleId())) { BaseTreeNode baseTreeNode = idModuleMap.get(request.getModuleId()); - String modulePath = baseTreeNode.getAttachInfo().get(MODULE); + String modulePath = baseTreeNode.getPath(); importData.forEach(t -> { t.setModulePath(modulePath + t.getModulePath()); }); @@ -152,7 +159,7 @@ public class ApiDefinitionImportUtilService { //去掉apiLists中不存在的模块数据 apiLists = apiLists.stream().filter(t -> modulePathMap.containsKey(t.getModulePath())).toList(); apiLists.forEach(t -> { - t.setModulePath(idModuleMap.get(t.getModuleId()) != null ? idModuleMap.get(t.getModuleId()).getAttachInfo().get(MODULE) : StringUtils.EMPTY); + t.setModulePath(idModuleMap.get(t.getModuleId()) != null ? idModuleMap.get(t.getModuleId()).getPath() : StringUtils.EMPTY); }); ApiDeatlWithData apiDeatlWithData = new ApiDeatlWithData(); //判断数据是否是唯一的 @@ -162,7 +169,7 @@ public class ApiDefinitionImportUtilService { getNeedUpdateData(request, apiDeatlWithData, apiDeatlWithDataUpdate); //数据入库 - insertData(modulePathMap, idModuleMap, apiDeatlWithDataUpdate, request); + insertData(modulePathMap, idModuleMap, apiDeatlWithDataUpdate, request, user); } @@ -181,7 +188,7 @@ public class ApiDefinitionImportUtilService { return order; } - public Long getImportNextCaseOrder(String projectId) { + /* public Long getImportNextCaseOrder(String projectId) { Long order = currentApiCaseOrder.get(); if (order == null) { order = apiTestCaseService.getNextOrder(projectId); @@ -189,7 +196,7 @@ public class ApiDefinitionImportUtilService { order = order + ORDER_STEP; currentApiCaseOrder.set(order); return order; - } + }*/ public Long getImportNextModuleOrder(String projectId) { Long order = currentModuleOrder.get(); @@ -205,7 +212,7 @@ public class ApiDefinitionImportUtilService { public void insertData(Map modulePathMap, Map idModuleMap, ApiDeatlWithDataUpdate apiDeatlWithDataUpdate, - ImportRequest request) { + ImportRequest request, SessionUser user) { //先判断是否需要新增模块 List addModuleData = apiDeatlWithDataUpdate.getAddModuleData(); List updateModuleData = apiDeatlWithDataUpdate.getUpdateModuleData(); @@ -228,15 +235,20 @@ public class ApiDefinitionImportUtilService { //解析modulePath 格式为/a/b/c String[] split = item.split("/"); //一层一层的创建 - for (int i = 0; i < split.length; i++) { - String path = StringUtils.join(split, "/", 0, i + 1); + for (int i = 1; i < split.length; i++) { + String modulePath = StringUtils.join(split, "/", 1, i + 1); + String path = StringUtils.join("/", modulePath); BaseTreeNode baseTreeNode = modulePathMap.get(path); if (baseTreeNode == null) { //创建模块 BaseTreeNode module = new BaseTreeNode(); module.setId(IDGenerator.nextStr()); module.setName(split[i]); - module.setParentId(i == 0 ? ModuleConstants.ROOT_NODE_PARENT_ID : modulePathMap.get(StringUtils.join(split, "/", 0, i)).getId()); + if (i != 1) { + String parentId = path.substring(0, path.lastIndexOf("/" + split[i])); + module.setParentId(modulePathMap.get(parentId).getId()); + } + module.setPath(path); addModuleList.add(module); modulePathMap.put(path, module); idModuleMap.put(module.getId(), module); @@ -295,10 +307,14 @@ public class ApiDefinitionImportUtilService { }); Map logData = apiDeatlWithDataUpdate.getLogData(); Project project = projectMapper.selectByPrimaryKey(request.getProjectId()); + List updateLists = new ArrayList<>(); if (MapUtils.isNotEmpty(logData)) { logData.forEach((k, v) -> { ApiDefinitionDTO apiDefinitionDTO = new ApiDefinitionDTO(); BeanUtils.copyBean(apiDefinitionDTO, v); + ApiDefinitionCaseDTO apiDefinitionCaseDTO = new ApiDefinitionCaseDTO(); + BeanUtils.copyBean(apiDefinitionCaseDTO, v); + updateLists.add(apiDefinitionCaseDTO); LogDTO dto = new LogDTO( project.getId(), project.getOrganizationId(), @@ -314,6 +330,7 @@ public class ApiDefinitionImportUtilService { operationLogs.add(dto); }); } + List createLists = new ArrayList<>(); addModuleData.forEach(t -> { ApiDefinition apiDefinition = new ApiDefinition(); BeanUtils.copyBean(apiDefinition, t); @@ -352,11 +369,22 @@ public class ApiDefinitionImportUtilService { dto.setPath("/api/definition/import"); dto.setMethod(HttpMethodConstants.POST.name()); dto.setOriginalValue(JSON.toJSONBytes(apiDefinitionDTO)); + + ApiDefinitionCaseDTO apiDefinitionCaseDTO = new ApiDefinitionCaseDTO(); + BeanUtils.copyBean(apiDefinitionCaseDTO, t); + createLists.add(apiDefinitionCaseDTO); }); sqlSession.flushStatements(); SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory); operationLogService.batchAdd(operationLogs); + //发送通知 + List createResources = new ArrayList<>(); + createResources.addAll(JSON.parseArray(JSON.toJSONString(createLists), Map.class)); + afterReturningNoticeSendService.sendNotice(NoticeConstants.TaskType.API_DEFINITION_TASK, NoticeConstants.Event.CREATE, createResources, user, request.getProjectId()); + List updateResources = new ArrayList<>(); + updateResources.addAll(JSON.parseArray(JSON.toJSONString(updateResources), Map.class)); + afterReturningNoticeSendService.sendNotice(NoticeConstants.TaskType.API_DEFINITION_TASK, NoticeConstants.Event.UPDATE, updateResources, user, request.getProjectId()); } private void getNeedUpdateData(ImportRequest request, ApiDeatlWithData apiDeatlWithData, ApiDeatlWithDataUpdate apiDeatlWithDataUpdate) { @@ -367,6 +395,7 @@ public class ApiDefinitionImportUtilService { List updateModuleData = new ArrayList<>(); List updateRequestData = new ArrayList<>(); List addData = new ArrayList<>(); + Map logMap = new HashMap<>(); //判断参数是否一样 一样的参数需要判断是否需要覆盖模块 如果需要就要update数据, 如果不需要 就直接跳过 if (CollectionUtils.isNotEmpty(sameList) && getFullCoverage(request.getCoverData())) { //需要覆盖数据的 会判断是否需要覆盖模块 @@ -376,7 +405,6 @@ public class ApiDefinitionImportUtilService { ApiDefinitionBlobExample blobExample = new ApiDefinitionBlobExample(); blobExample.createCriteria().andIdIn(sameIds); List apiDefinitionBlobs = apiDefinitionBlobMapper.selectByExampleWithBLOBs(blobExample); - Map logMap = new HashMap<>(); Map blobMap = apiDefinitionBlobs.stream().collect(Collectors.toMap(ApiDefinitionBlob::getId, t -> t)); //判断参数是否一样 for (ApiDefinitionImportDTO apiDefinitionDTO : sameData) { @@ -412,6 +440,7 @@ public class ApiDefinitionImportUtilService { apiDeatlWithDataUpdate.setUpdateModuleData(updateModuleData); apiDeatlWithDataUpdate.setUpdateRequestData(updateRequestData); apiDeatlWithDataUpdate.setAddModuleData(addData); + apiDeatlWithDataUpdate.setLogData(logMap); } private void checkApiDataOnly(ImportRequest request, @@ -453,7 +482,7 @@ public class ApiDefinitionImportUtilService { if (CollectionUtils.isNotEmpty(dbHeaders) || CollectionUtils.isNotEmpty(importHeaders)) { List dbHeaderKeys = dbHeaders.stream().map(Header::getKey).toList(); List importHeaderKeys = importHeaders.stream().map(Header::getKey).toList(); - if (!paramsIsSame(dbHeaderKeys, importHeaderKeys)) { + if (paramsIsSame(dbHeaderKeys, importHeaderKeys)) { return false; } } @@ -463,7 +492,7 @@ public class ApiDefinitionImportUtilService { if (CollectionUtils.isNotEmpty(dbQuery) || CollectionUtils.isNotEmpty(importQuery)) { List dbQueryKeys = dbQuery.stream().map(QueryParam::getKey).toList(); List importQueryKeys = importQuery.stream().map(QueryParam::getKey).toList(); - if (!paramsIsSame(dbQueryKeys, importQueryKeys)) { + if (paramsIsSame(dbQueryKeys, importQueryKeys)) { return false; } } @@ -472,7 +501,7 @@ public class ApiDefinitionImportUtilService { if (CollectionUtils.isNotEmpty(dbRest) || CollectionUtils.isNotEmpty(importRest)) { List dbRestKeys = dbRest.stream().map(RestParam::getKey).toList(); List importRestKeys = importRest.stream().map(RestParam::getKey).toList(); - if (!paramsIsSame(dbRestKeys, importRestKeys)) { + if (paramsIsSame(dbRestKeys, importRestKeys)) { return false; } } @@ -493,7 +522,7 @@ public class ApiDefinitionImportUtilService { if (CollectionUtils.isNotEmpty(fromValues) || CollectionUtils.isNotEmpty(importFromValues)) { List dbFormKeys = fromValues.stream().map(FormDataKV::getKey).toList(); List importFormKeys = importFromValues.stream().map(FormDataKV::getKey).toList(); - if (!paramsIsSame(dbFormKeys, importFormKeys)) { + if (paramsIsSame(dbFormKeys, importFormKeys)) { return false; } } @@ -507,7 +536,7 @@ public class ApiDefinitionImportUtilService { if (CollectionUtils.isNotEmpty(wwwValues) || CollectionUtils.isNotEmpty(importWwwValues)) { List dbWwwKeys = wwwValues.stream().map(FormDataKV::getKey).toList(); List importWwwKeys = importWwwValues.stream().map(FormDataKV::getKey).toList(); - if (!paramsIsSame(dbWwwKeys, importWwwKeys)) { + if (paramsIsSame(dbWwwKeys, importWwwKeys)) { return false; } } @@ -542,6 +571,14 @@ public class ApiDefinitionImportUtilService { } //判断jsonschema的参数是否一样 + + /** + * 判断jsonschema的参数是否一样 + * + * @param jsonSchema 数据库中的数据 + * @param importJsonSchema 导入的生成的jsonschema + * @return true 一样 false 不一样 一样的话就不需要更新 + */ private static boolean jsonSchemaIsSame(JsonSchemaItem jsonSchema, JsonSchemaItem importJsonSchema) { boolean same = true; if (jsonSchema == null && importJsonSchema == null) { @@ -557,7 +594,7 @@ public class ApiDefinitionImportUtilService { if (MapUtils.isNotEmpty(properties) || MapUtils.isNotEmpty(importProperties)) { List dbJsonKeys = properties.keySet().stream().toList(); List importJsonKeys = importProperties.keySet().stream().toList(); - if (!paramsIsSame(dbJsonKeys, importJsonKeys)) { + if (paramsIsSame(dbJsonKeys, importJsonKeys)) { return false; } //遍历判断每个参数是否一样 @@ -585,11 +622,11 @@ public class ApiDefinitionImportUtilService { private static boolean paramsIsSame(List dbRestKeys, List importRestKeys) { if (dbRestKeys.size() != importRestKeys.size()) { - return false; + return true; } //看看是否有差集 List differenceRest = dbRestKeys.stream().filter(t -> !importRestKeys.contains(t)).toList(); - return CollectionUtils.isEmpty(differenceRest); + return !CollectionUtils.isEmpty(differenceRest); } private Boolean getFullCoverage(Boolean fullCoverage) { @@ -602,24 +639,23 @@ public class ApiDefinitionImportUtilService { /** * 构造树结构 但是这里需要module的path * - * @param projectId - * @param protocol - * @return + * @param projectId 项目id + * @param protocol 协议 + * @return 树结构 */ public List buildTreeData(String projectId, String protocol) { ApiModuleRequest request = new ApiModuleRequest(); request.setProjectId(projectId); request.setProtocol(protocol); - List fileModuleList = extApiDefinitionModuleMapper.selectBaseByRequest(request); - return this.buildTreeAndCountResource(fileModuleList, true, Translator.get(UNPLANNED_API)); - + List apiModuleList = extApiDefinitionModuleMapper.selectBaseByRequest(request); + return this.buildTreeAndCountResource(apiModuleList, true, Translator.get(UNPLANNED_API)); } public List buildTreeAndCountResource(List traverseList, boolean haveVirtualRootNode, String virtualRootName) { List baseTreeNodeList = new ArrayList<>(); if (haveVirtualRootNode) { BaseTreeNode defaultNode = new BaseTreeNode(ModuleConstants.DEFAULT_NODE_ID, virtualRootName, ModuleConstants.NODE_TYPE_DEFAULT, ModuleConstants.ROOT_NODE_PARENT_ID); - defaultNode.setAttachInfo(Map.of(MODULE, StringUtils.join("/", defaultNode.getName()))); + defaultNode.setPath(StringUtils.join("/", defaultNode.getName())); baseTreeNodeList.add(defaultNode); } int lastSize = 0; @@ -630,13 +666,13 @@ public class ApiDefinitionImportUtilService { for (BaseTreeNode treeNode : traverseList) { if (StringUtils.equalsIgnoreCase(treeNode.getParentId(), ModuleConstants.ROOT_NODE_PARENT_ID)) { BaseTreeNode node = new BaseTreeNode(treeNode.getId(), treeNode.getName(), treeNode.getType(), treeNode.getParentId()); - node.setAttachInfo(Map.of(MODULE, StringUtils.join("/", node.getName()))); + node.setPath(StringUtils.join("/", node.getName())); baseTreeNodeList.add(node); baseTreeNodeMap.put(treeNode.getId(), node); } else { if (baseTreeNodeMap.containsKey(treeNode.getParentId())) { BaseTreeNode node = new BaseTreeNode(treeNode.getId(), treeNode.getName(), treeNode.getType(), treeNode.getParentId()); - node.setAttachInfo(Map.of(MODULE, StringUtils.join(baseTreeNodeMap.get(treeNode.getParentId()).getAttachInfo().get(MODULE), "/", node.getName()))); + node.setPath(StringUtils.join(baseTreeNodeMap.get(treeNode.getParentId()).getPath(), "/", node.getName())); baseTreeNodeList.add(node); } } @@ -646,5 +682,4 @@ public class ApiDefinitionImportUtilService { return baseTreeNodeList; } - } diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiDefinitionModuleService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiDefinitionModuleService.java index 029bb4c1c7..bb819f5a0b 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiDefinitionModuleService.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiDefinitionModuleService.java @@ -126,15 +126,6 @@ public class ApiDefinitionModuleService extends ModuleTreeService { example.clear(); } - private String getRootNodeId(ApiDefinitionModule module) { - if (StringUtils.equals(module.getParentId(), ModuleConstants.ROOT_NODE_PARENT_ID)) { - return module.getId(); - } else { - ApiDefinitionModule parentModule = apiDefinitionModuleMapper.selectByPrimaryKey(module.getParentId()); - return this.getRootNodeId(parentModule); - } - } - public void update(ModuleUpdateRequest request, String userId) { ApiDefinitionModule module = checkModuleExist(request.getId()); ApiDefinitionModule updateModule = new ApiDefinitionModule(); @@ -270,20 +261,36 @@ public class ApiDefinitionModuleService extends ModuleTreeService { return moduleCountMap; } - public List getTrashTree(ApiModuleRequest request, boolean deleted) { + public List getTrashTree(ApiModuleRequest request) { ApiDefinitionExample example = new ApiDefinitionExample(); - example.createCriteria().andProjectIdEqualTo(request.getProjectId()).andDeletedEqualTo(true).andProtocolEqualTo(request.getProtocol()); + example.createCriteria() + .andProjectIdEqualTo(request.getProjectId()) + .andDeletedEqualTo(true) + .andProtocolEqualTo(request.getProtocol()); List apiDefinitions = apiDefinitionMapper.selectByExample(example); if (CollectionUtils.isEmpty(apiDefinitions)) { return new ArrayList<>(); } List moduleIds = apiDefinitions.stream().map(ApiDefinition::getModuleId).distinct().toList(); - List baseTreeNodes = extApiDefinitionModuleMapper.selectBaseByIds(moduleIds); - super.buildTreeAndCountResource(baseTreeNodes, true, Translator.get(UNPLANNED_API)); - List apiTreeNodeList = extApiDefinitionModuleMapper.selectApiDataByRequest(request, deleted); - return apiDebugModuleService.getBaseTreeNodes(apiTreeNodeList, baseTreeNodes); + List baseTreeNodes = getNodeByNodeIds(moduleIds); + return super.buildTreeAndCountResource(baseTreeNodes, true, Translator.get(UNPLANNED_API)); } + public List getNodeByNodeIds(List moduleIds) { + List finalModuleIds = new ArrayList<>(moduleIds); + List totalList = new ArrayList<>(); + while (CollectionUtils.isNotEmpty(finalModuleIds)) { + List modules = extApiDefinitionModuleMapper.selectBaseByIds(finalModuleIds); + totalList.addAll(modules); + List finalModuleIdList = finalModuleIds; + List parentModuleIds = modules.stream().map(BaseTreeNode::getParentId).filter(parentId -> !StringUtils.equalsIgnoreCase(parentId, ModuleConstants.ROOT_NODE_PARENT_ID) && !finalModuleIdList.contains(parentId)).toList(); + finalModuleIds.clear(); + finalModuleIds = new ArrayList<>(parentModuleIds); + } + return totalList.stream().distinct().toList(); + } + + public EnvApiTreeDTO envTree(EnvApiModuleRequest request) { EnvApiTreeDTO envApiTreeDTO = new EnvApiTreeDTO(); ApiModuleRequest apiModuleRequest = new ApiModuleRequest(); diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiDefinitionService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiDefinitionService.java index b2415c9ecb..f1b67c32ec 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiDefinitionService.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiDefinitionService.java @@ -7,7 +7,6 @@ import io.metersphere.api.domain.*; import io.metersphere.api.dto.debug.ApiFileResourceUpdateRequest; import io.metersphere.api.dto.definition.*; import io.metersphere.api.dto.definition.importdto.ApiDefinitionImport; -import io.metersphere.api.dto.definition.importdto.ApiDefinitionImportDTO; import io.metersphere.api.dto.request.ImportRequest; import io.metersphere.api.mapper.*; import io.metersphere.api.parser.ImportParser; @@ -28,7 +27,7 @@ import io.metersphere.sdk.util.*; import io.metersphere.system.dto.OperationHistoryDTO; import io.metersphere.system.dto.request.OperationHistoryRequest; import io.metersphere.system.dto.request.OperationHistoryVersionRequest; -import io.metersphere.system.dto.sdk.OptionDTO; +import io.metersphere.system.dto.sdk.SessionUser; import io.metersphere.system.dto.table.TableBatchProcessDTO; import io.metersphere.system.log.constants.OperationLogModule; import io.metersphere.system.service.OperationHistoryService; @@ -63,6 +62,7 @@ public class ApiDefinitionService { private static final String ALL_API = "api_definition_module.api.all"; private static final String UNPLANNED_API = "api_unplanned_request"; + private static final String API_TABLE = "api_definition"; @Resource private ApiDefinitionMapper apiDefinitionMapper; @@ -886,7 +886,7 @@ public class ApiDefinitionService { return apiDefinitionDocDTO; } - public ApiDefinitionImport apiTestImport(MultipartFile file, ImportRequest request, String userId, String projectId) { + public ApiDefinitionImport apiTestImport(MultipartFile file, ImportRequest request, SessionUser user, String projectId) { if (file != null) { String originalFilename = file.getOriginalFilename(); if (StringUtils.isNotBlank(originalFilename)) { @@ -894,38 +894,25 @@ public class ApiDefinitionService { apiDefinitionImportUtilService.checkFileSuffixName(request, suffixName); } } - request.setUserId(userId); if (StringUtils.isBlank(request.getProjectId())) { request.setProjectId(projectId); } ImportParser runService = ImportParserFactory.getImportParser(request.getPlatform()); ApiDefinitionImport apiImport = null; if (StringUtils.equals(request.getType(), "SCHEDULE")) { - request.setProtocol("HTTP"); + request.setProtocol(ModuleConstants.NODE_PROTOCOL_HTTP); } try { apiImport = (ApiDefinitionImport) Objects.requireNonNull(runService).parse(file == null ? null : file.getInputStream(), request); //TODO 处理mock数据 - // 发送通知 } catch (Exception e) { - // TODO 发送通知 LogUtils.error(e.getMessage(), e); throw new MSException(Translator.get("parse_data_error")); } try { - apiDefinitionImportUtilService.importApi(request, apiImport); - apiDefinitionMapper.selectByExample(new ApiDefinitionExample()); - if (CollectionUtils.isNotEmpty(apiImport.getData())) { - List names = apiImport.getData().stream().map(ApiDefinitionImportDTO::getName).collect(Collectors.toList()); - request.setName(String.join(",", names)); - List ids = apiImport.getData().stream().map(ApiDefinitionImportDTO::getId).collect(Collectors.toList()); - request.setId(JSON.toJSONString(ids)); - } - // 发送通知 - //apiDefinitionImportUtilService.sendImportNotice(request, apiImportSendNoticeDTOS, project); + apiDefinitionImportUtilService.importApi(request, apiImport, user); } catch (Exception e) { - //apiDefinitionImportUtilService.sendFailMessage(request, project); LogUtils.error(e); throw new MSException(Translator.get("user_import_format_wrong")); } @@ -933,18 +920,11 @@ public class ApiDefinitionService { } public List list(OperationHistoryRequest request) { - List operationHistoryList = operationHistoryService.list(request); - if (CollectionUtils.isNotEmpty(operationHistoryList)) { - List apiIds = operationHistoryList.stream() - .map(OperationHistoryDTO::getSourceId).toList(); - - Map apiMap = extApiDefinitionMapper.selectVersionOptionByIds(apiIds).stream() - .collect(Collectors.toMap(OptionDTO::getId, OptionDTO::getName)); - - operationHistoryList.forEach(item -> item.setVersionName(apiMap.getOrDefault(item.getSourceId(), StringUtils.EMPTY))); + XpackApiDefinitionService xpackApiDefinitionService = CommonBeanFactory.getBean(XpackApiDefinitionService.class); + if (xpackApiDefinitionService != null) { + return xpackApiDefinitionService.listHis(request, API_TABLE); } - - return operationHistoryList; + return new ArrayList<>(); } /** diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiTestCaseService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiTestCaseService.java index 1d899630b9..ff3dfd586d 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiTestCaseService.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiTestCaseService.java @@ -17,13 +17,9 @@ import io.metersphere.sdk.domain.Environment; import io.metersphere.sdk.domain.EnvironmentExample; import io.metersphere.sdk.exception.MSException; import io.metersphere.sdk.mapper.EnvironmentMapper; -import io.metersphere.sdk.util.BeanUtils; -import io.metersphere.sdk.util.FileAssociationSourceUtil; -import io.metersphere.sdk.util.SubListUtils; -import io.metersphere.sdk.util.Translator; +import io.metersphere.sdk.util.*; import io.metersphere.system.dto.OperationHistoryDTO; import io.metersphere.system.dto.request.OperationHistoryRequest; -import io.metersphere.system.dto.sdk.OptionDTO; import io.metersphere.system.dto.sdk.request.PosRequest; import io.metersphere.system.log.constants.OperationLogModule; import io.metersphere.system.service.OperationHistoryService; @@ -85,6 +81,7 @@ public class ApiTestCaseService { private ApiDefinitionModuleMapper apiDefinitionModuleMapper; @Resource private OperationHistoryService operationHistoryService; + private static final String CASE_TABLE = "api_test_case"; private void checkProjectExist(String projectId) { Project project = projectMapper.selectByPrimaryKey(projectId); @@ -371,10 +368,13 @@ public class ApiTestCaseService { public List doSelectIds(ApiTestCaseBatchRequest request, boolean deleted) { if (request.isSelectAll()) { List ids = extApiTestCaseMapper.getIds(request, deleted); - if (CollectionUtils.isNotEmpty(request.getExcludeIds())) { + if (org.apache.commons.collections4.CollectionUtils.isNotEmpty(request.getSelectIds())) { + ids.addAll(request.getSelectIds()); + } + if (org.apache.commons.collections4.CollectionUtils.isNotEmpty(request.getExcludeIds())) { ids.removeAll(request.getExcludeIds()); } - return ids; + return new ArrayList<>(ids.stream().distinct().toList()); } else { request.getSelectIds().removeAll(request.getExcludeIds()); return request.getSelectIds(); @@ -560,19 +560,13 @@ public class ApiTestCaseService { return executeList; } - public List getHistoryList(OperationHistoryRequest request) { - List operationHistoryList = operationHistoryService.list(request); - if (CollectionUtils.isNotEmpty(operationHistoryList)) { - List apiIds = operationHistoryList.stream() - .map(OperationHistoryDTO::getSourceId).toList(); - Map apiMap = extApiTestCaseMapper.selectVersionOptionByIds(apiIds).stream() - .collect(Collectors.toMap(OptionDTO::getId, OptionDTO::getName)); - - operationHistoryList.forEach(item -> item.setVersionName(apiMap.getOrDefault(item.getSourceId(), StringUtils.EMPTY))); + public List operationHistoryList(OperationHistoryRequest request) { + XpackApiDefinitionService xpackApiDefinitionService = CommonBeanFactory.getBean(XpackApiDefinitionService.class); + if (xpackApiDefinitionService != null) { + return xpackApiDefinitionService.listHis(request, CASE_TABLE); } - - return operationHistoryList; + return new ArrayList<>(); } public void updatePriority(String id, String priority, String userId) { diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/XpackApiDefinitionService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/XpackApiDefinitionService.java new file mode 100644 index 0000000000..b24eaf813c --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/XpackApiDefinitionService.java @@ -0,0 +1,20 @@ +package io.metersphere.api.service.definition; + +import io.metersphere.system.dto.OperationHistoryDTO; +import io.metersphere.system.dto.request.OperationHistoryRequest; + +import java.util.List; + +/** + * 接口管理Xpack功能接口 + */ +public interface XpackApiDefinitionService { + + /** + * 功能用例变更历史分页列表 + * + * @param request 请求参数 + * @return 变更历史集合 + */ + List listHis(OperationHistoryRequest request, String table); +} diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/scenario/ApiScenarioModuleService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/scenario/ApiScenarioModuleService.java index 6f18ff884e..c5e508785e 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/service/scenario/ApiScenarioModuleService.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/scenario/ApiScenarioModuleService.java @@ -260,7 +260,21 @@ public class ApiScenarioModuleService extends ModuleTreeService { return new ArrayList<>(); } List moduleIds = apiScenarios.stream().map(ApiScenario::getModuleId).distinct().toList(); - List baseTreeNodes = extApiScenarioModuleMapper.selectBaseByIds(moduleIds); + List baseTreeNodes = getNodeByNodeIds(moduleIds); return super.buildTreeAndCountResource(baseTreeNodes, true, Translator.get(UNPLANNED_SCENARIO)); } + + public List getNodeByNodeIds(List moduleIds) { + List finalModuleIds = new ArrayList<>(moduleIds); + List totalList = new ArrayList<>(); + while (CollectionUtils.isNotEmpty(finalModuleIds)) { + List modules = extApiScenarioModuleMapper.selectBaseByIds(finalModuleIds); + totalList.addAll(modules); + List finalModuleIdList = finalModuleIds; + List parentModuleIds = modules.stream().map(BaseTreeNode::getParentId).filter(parentId -> !StringUtils.equalsIgnoreCase(parentId, ModuleConstants.ROOT_NODE_PARENT_ID) && !finalModuleIdList.contains(parentId)).toList(); + finalModuleIds.clear(); + finalModuleIds = new ArrayList<>(parentModuleIds); + } + return totalList.stream().distinct().toList(); + } } diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/schedule/SwaggerUrlImportJob.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/schedule/SwaggerUrlImportJob.java index 9f07a1580a..cd6cd16f1e 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/service/schedule/SwaggerUrlImportJob.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/schedule/SwaggerUrlImportJob.java @@ -8,7 +8,11 @@ import io.metersphere.api.service.definition.ApiDefinitionScheduleService; import io.metersphere.api.service.definition.ApiDefinitionService; import io.metersphere.sdk.util.BeanUtils; import io.metersphere.sdk.util.CommonBeanFactory; +import io.metersphere.system.dto.sdk.SessionUser; +import io.metersphere.system.dto.user.UserDTO; import io.metersphere.system.schedule.BaseScheduleJob; +import io.metersphere.system.service.UserService; +import io.metersphere.system.utils.SessionUtils; import org.quartz.JobDataMap; import org.quartz.JobExecutionContext; import org.quartz.JobKey; @@ -17,10 +21,12 @@ import org.quartz.TriggerKey; public class SwaggerUrlImportJob extends BaseScheduleJob { private ApiDefinitionService apiDefinitionService; private ApiDefinitionScheduleService apiDefinitionScheduleService; + private UserService userService; public SwaggerUrlImportJob() { apiDefinitionService = CommonBeanFactory.getBean(ApiDefinitionService.class); apiDefinitionScheduleService = CommonBeanFactory.getBean(ApiDefinitionScheduleService.class); + userService = CommonBeanFactory.getBean(UserService.class); } @Override @@ -34,7 +40,10 @@ public class SwaggerUrlImportJob extends BaseScheduleJob { request.setUserId(jobDataMap.getString("userId")); request.setType("SCHEDULE"); request.setResourceId(resourceId); - apiDefinitionService.apiTestImport(null, request, request.getUserId(), request.getProjectId()); + + UserDTO userDTO = userService.getUserDTOByKeyword(request.getUserId()); + SessionUser user = SessionUser.fromUser(userDTO, SessionUtils.getSessionId()); + apiDefinitionService.apiTestImport(null, request, user, request.getProjectId()); } public static JobKey getJobKey(String resourceId) { diff --git a/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiDefinitionModuleControllerTests.java b/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiDefinitionModuleControllerTests.java index f8b0edb43b..a70b80ebbd 100644 --- a/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiDefinitionModuleControllerTests.java +++ b/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiDefinitionModuleControllerTests.java @@ -269,6 +269,30 @@ public class ApiDefinitionModuleControllerTests extends BaseTest { initApiDebugData(a1ChildNode.getId()); checkLog(a1ChildNode.getId(), OperationLogType.ADD, URL_MODULE_ADD); + //继续创建a2下继续创建a2-a1 + request = new ModuleCreateRequest(); + request.setProjectId(project.getId()); + request.setName("a2-a1"); + request.setParentId(a2Node.getId()); + this.requestPostWithOkAndReturn(URL_MODULE_ADD, request); + treeNodes = this.getModuleTreeNode(); + BaseTreeNode a2ChildNode = null; + for (BaseTreeNode baseTreeNode : treeNodes) { + Assertions.assertNotNull(baseTreeNode.getParentId()); + if (StringUtils.equals(baseTreeNode.getName(), "a2") && CollectionUtils.isNotEmpty(baseTreeNode.getChildren())) { + for (BaseTreeNode childNode : baseTreeNode.getChildren()) { + Assertions.assertNotNull(childNode.getParentId()); + if (StringUtils.equals(childNode.getName(), "a2-a1") && StringUtils.equals(childNode.getType(), ModuleConstants.NODE_TYPE_DEFAULT)) { + a2ChildNode = childNode; + } + } + } + } + Assertions.assertNotNull(a2ChildNode); + initApiDebugData(a2ChildNode.getId()); + checkLog(a2ChildNode.getId(), OperationLogType.ADD, URL_MODULE_ADD); + + //a1的子节点a1下继续创建节点a1-a1-c1 request = new ModuleCreateRequest(); request.setProjectId(project.getId()); @@ -812,7 +836,6 @@ public class ApiDefinitionModuleControllerTests extends BaseTest { @Order(10) public void deleteModuleTestSuccess() throws Exception { this.preliminaryData(); - this.getModuleTrashTreeNode(); // 删除没有文件的节点a1-b1-c1 检查是否级联删除根节点 BaseTreeNode a1b1Node = getNodeByName(this.getModuleTreeNode(), "a1-b1"); @@ -843,10 +866,19 @@ public class ApiDefinitionModuleControllerTests extends BaseTest { @Test @Order(11) public void getModuleTrashTreeNode() throws Exception { + BaseTreeNode a2a1Node = getNodeByName(this.getModuleTreeNode(), "a2-a1"); + //将模块为a2-a1的节点数据放入回收站 + ApiDefinitionExample example = new ApiDefinitionExample(); + assert a2a1Node != null; + example.createCriteria().andModuleIdEqualTo(a2a1Node.getId()); + ApiDefinition apiDefinition = new ApiDefinition(); + apiDefinition.setDeleted(true); + apiDefinitionMapper.updateByExampleSelective(apiDefinition, example); MvcResult result = this.requestPostWithOkAndReturn(URL_MODULE_TRASH_TREE, new ApiModuleRequest() {{ this.setProtocol("HTTP"); this.setProjectId(project.getId()); }}); + String returnData = result.getResponse().getContentAsString(StandardCharsets.UTF_8); ResultHolder resultHolder = JSON.parseObject(returnData, ResultHolder.class); JSON.parseArray(JSON.toJSONString(resultHolder.getData()), BaseTreeNode.class); diff --git a/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiTestCaseControllerTests.java b/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiTestCaseControllerTests.java index 9600827a0f..684cd17ce1 100644 --- a/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiTestCaseControllerTests.java +++ b/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiTestCaseControllerTests.java @@ -31,6 +31,7 @@ import io.metersphere.system.dto.OperationHistoryDTO; import io.metersphere.system.dto.request.OperationHistoryRequest; import io.metersphere.system.dto.sdk.request.PosRequest; import io.metersphere.system.log.constants.OperationLogType; +import io.metersphere.system.service.OperationHistoryService; import io.metersphere.system.uid.IDGenerator; import io.metersphere.system.uid.NumGenerator; import io.metersphere.system.utils.Pager; @@ -115,6 +116,8 @@ public class ApiTestCaseControllerTests extends BaseTest { private ApiReportService apiReportService; @Resource private ProjectVersionMapper projectVersionMapper; + @Resource + private OperationHistoryService operationHistoryService; public static T parseObjectFromMvcResult(MvcResult mvcResult, Class parseClass) { try { @@ -839,6 +842,16 @@ public class ApiTestCaseControllerTests extends BaseTest { Assertions.assertNotNull(returnPager); List reportDTOS = JSON.parseArray(JSON.toJSONString(returnPager.getList()), OperationHistoryDTO.class); reportDTOS.forEach(reportDTO -> Assertions.assertEquals(reportDTO.getSourceId(), first.getId())); + + List operationHistoryDTOS = operationHistoryService.listWidthLimit(request, "api_test_case"); + Assertions.assertTrue(org.apache.commons.collections4.CollectionUtils.isNotEmpty(operationHistoryDTOS)); + + request = new OperationHistoryRequest(); + request.setProjectId(DEFAULT_PROJECT_ID); + request.setSourceId("111"); + request.setPageSize(10); + request.setCurrent(1); + responsePost(HISTORY, request); } @Test