diff --git a/backend/framework/domain/src/main/java/io/metersphere/api/domain/ApiDebug.java b/backend/framework/domain/src/main/java/io/metersphere/api/domain/ApiDebug.java index b73f1a491f..75952a2b5b 100644 --- a/backend/framework/domain/src/main/java/io/metersphere/api/domain/ApiDebug.java +++ b/backend/framework/domain/src/main/java/io/metersphere/api/domain/ApiDebug.java @@ -36,7 +36,9 @@ public class ApiDebug implements Serializable { @Size(min = 1, max = 50, message = "{api_debug.project_id.length_range}", groups = {Created.class, Updated.class}) private String projectId; - @Schema(description = "模块fk") + @Schema(description = "模块fk", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "{api_debug.module_id.not_blank}", groups = {Created.class}) + @Size(min = 1, max = 50, message = "{api_debug.module_id.length_range}", groups = {Created.class, Updated.class}) private String moduleId; @Schema(description = "创建时间") diff --git a/backend/framework/domain/src/main/java/io/metersphere/api/domain/ApiDebugModule.java b/backend/framework/domain/src/main/java/io/metersphere/api/domain/ApiDebugModule.java index 7fec3de911..6918ac7bb0 100644 --- a/backend/framework/domain/src/main/java/io/metersphere/api/domain/ApiDebugModule.java +++ b/backend/framework/domain/src/main/java/io/metersphere/api/domain/ApiDebugModule.java @@ -1,12 +1,16 @@ package io.metersphere.api.domain; -import io.metersphere.validation.groups.*; +import io.metersphere.validation.groups.Created; +import io.metersphere.validation.groups.Updated; import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.*; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.Data; + import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; -import lombok.Data; @Data public class ApiDebugModule implements Serializable { @@ -21,8 +25,8 @@ public class ApiDebugModule implements Serializable { private String name; @Schema(description = "协议", requiredMode = Schema.RequiredMode.REQUIRED) - @NotBlank(message = "{api_debug_module.protocol.not_blank}", groups = {Created.class}) - @Size(min = 1, max = 64, message = "{api_debug_module.protocol.length_range}", groups = {Created.class, Updated.class}) + @NotBlank(message = "{api_debug.protocol.not_blank}", groups = {Created.class}) + @Size(min = 1, max = 20, message = "{api_debug.protocol.length_range}", groups = {Created.class, Updated.class}) private String protocol; @Schema(description = "父级fk", requiredMode = Schema.RequiredMode.REQUIRED) @@ -35,10 +39,6 @@ public class ApiDebugModule implements Serializable { @Size(min = 1, max = 50, message = "{api_debug_module.project_id.length_range}", groups = {Created.class, Updated.class}) private String projectId; - @Schema(description = "树节点级别", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "{api_debug_module.level.not_blank}", groups = {Created.class}) - private Integer level; - @Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED) @NotNull(message = "{api_debug_module.pos.not_blank}", groups = {Created.class}) private Integer pos; @@ -63,7 +63,6 @@ public class ApiDebugModule implements Serializable { protocol("protocol", "protocol", "VARCHAR", false), parentId("parent_id", "parentId", "VARCHAR", false), projectId("project_id", "projectId", "VARCHAR", false), - level("level", "level", "INTEGER", true), pos("pos", "pos", "INTEGER", false), createTime("create_time", "createTime", "BIGINT", false), updateTime("update_time", "updateTime", "BIGINT", false), diff --git a/backend/framework/domain/src/main/java/io/metersphere/api/domain/ApiDebugModuleExample.java b/backend/framework/domain/src/main/java/io/metersphere/api/domain/ApiDebugModuleExample.java index 1771b48a87..62feda4024 100644 --- a/backend/framework/domain/src/main/java/io/metersphere/api/domain/ApiDebugModuleExample.java +++ b/backend/framework/domain/src/main/java/io/metersphere/api/domain/ApiDebugModuleExample.java @@ -454,66 +454,6 @@ public class ApiDebugModuleExample { return (Criteria) this; } - public Criteria andLevelIsNull() { - addCriterion("`level` is null"); - return (Criteria) this; - } - - public Criteria andLevelIsNotNull() { - addCriterion("`level` is not null"); - return (Criteria) this; - } - - public Criteria andLevelEqualTo(Integer value) { - addCriterion("`level` =", value, "level"); - return (Criteria) this; - } - - public Criteria andLevelNotEqualTo(Integer value) { - addCriterion("`level` <>", value, "level"); - return (Criteria) this; - } - - public Criteria andLevelGreaterThan(Integer value) { - addCriterion("`level` >", value, "level"); - return (Criteria) this; - } - - public Criteria andLevelGreaterThanOrEqualTo(Integer value) { - addCriterion("`level` >=", value, "level"); - return (Criteria) this; - } - - public Criteria andLevelLessThan(Integer value) { - addCriterion("`level` <", value, "level"); - return (Criteria) this; - } - - public Criteria andLevelLessThanOrEqualTo(Integer value) { - addCriterion("`level` <=", value, "level"); - return (Criteria) this; - } - - public Criteria andLevelIn(List values) { - addCriterion("`level` in", values, "level"); - return (Criteria) this; - } - - public Criteria andLevelNotIn(List values) { - addCriterion("`level` not in", values, "level"); - return (Criteria) this; - } - - public Criteria andLevelBetween(Integer value1, Integer value2) { - addCriterion("`level` between", value1, value2, "level"); - return (Criteria) this; - } - - public Criteria andLevelNotBetween(Integer value1, Integer value2) { - addCriterion("`level` not between", value1, value2, "level"); - return (Criteria) this; - } - public Criteria andPosIsNull() { addCriterion("pos is null"); return (Criteria) this; diff --git a/backend/framework/domain/src/main/java/io/metersphere/api/mapper/ApiDebugModuleMapper.xml b/backend/framework/domain/src/main/java/io/metersphere/api/mapper/ApiDebugModuleMapper.xml index e56171cf34..c775cc5a6e 100644 --- a/backend/framework/domain/src/main/java/io/metersphere/api/mapper/ApiDebugModuleMapper.xml +++ b/backend/framework/domain/src/main/java/io/metersphere/api/mapper/ApiDebugModuleMapper.xml @@ -7,7 +7,6 @@ - @@ -73,8 +72,8 @@ - id, `name`, protocol, parent_id, project_id, `level`, pos, create_time, update_time, - update_user, create_user + id, `name`, protocol, parent_id, project_id, pos, create_time, update_time, update_user, + create_user + SELECT id, + NAME, + parent_id AS parentId, + 'module' AS type + FROM api_debug_module + WHERE protocol = #{protocol} + AND create_user = #{userId} + ORDER BY pos + + + + + + DELETE + FROM api_debug_module + WHERE id IN + + #{id} + + + + + + + + + + + + + f.create_user = #{userId} + + AND f.name like CONCAT('%', #{request.keyword},'%') + + + AND f.module_id IN + + #{item} + + + + AND f.protocol = #{request.protocol} + + + + + \ No newline at end of file diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/request/ApiDebugRequest.java b/backend/services/api-test/src/main/java/io/metersphere/api/request/ApiDebugRequest.java new file mode 100644 index 0000000000..20be8e1778 --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/request/ApiDebugRequest.java @@ -0,0 +1,23 @@ +package io.metersphere.api.request; + +import io.metersphere.sdk.constants.ModuleConstants; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import lombok.Data; + +import java.util.List; + +@Data +public class ApiDebugRequest { + @Schema(description = "模块ID(根据模块树查询时要把当前节点以及子节点都放在这里。)") + private List moduleIds; + + @Schema(description = "协议", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "{api_debug_module.protocol.not_blank}") + @Size(min = 1, max = 20, message = "{api_debug.protocol.length_range}") + private String protocol = ModuleConstants.NODE_PROTOCOL_HTTP; + + @Schema(description = "关键字") + private String keyword; +} diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/request/ApiTreeNode.java b/backend/services/api-test/src/main/java/io/metersphere/api/request/ApiTreeNode.java new file mode 100644 index 0000000000..d719a17612 --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/request/ApiTreeNode.java @@ -0,0 +1,12 @@ +package io.metersphere.api.request; + +import io.metersphere.system.dto.sdk.BaseTreeNode; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +public class ApiTreeNode extends BaseTreeNode { + @Schema(description = "方法") + private String method; + +} diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/request/DebugModuleCreateRequest.java b/backend/services/api-test/src/main/java/io/metersphere/api/request/DebugModuleCreateRequest.java new file mode 100644 index 0000000000..3f40f6bfcf --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/request/DebugModuleCreateRequest.java @@ -0,0 +1,34 @@ +package io.metersphere.api.request; + +import io.metersphere.sdk.constants.ModuleConstants; +import io.metersphere.validation.groups.Created; +import io.metersphere.validation.groups.Updated; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Size; +import lombok.Data; + +@Data +public class DebugModuleCreateRequest { + + @Schema(description = "项目ID", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "{project.id.not_blank}") + private String projectId; + + @Schema(description = "模块名称", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "{file_module.name.not_blank}") + @Pattern(regexp = "^[^\\\\/]*$", message = "{api_debug_module.name.not_contain_slash}") + private String name; + + @Schema(description = "父模块ID", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "{parent.node.not_blank}") + private String parentId = ModuleConstants.ROOT_NODE_PARENT_ID; + + @Schema(description = "协议", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "{api_debug_module.protocol.not_blank}", groups = {Created.class}) + @Size(min = 1, max = 20, message = "{api_debug.protocol.length_range}", groups = {Created.class, Updated.class}) + private String protocol = ModuleConstants.NODE_PROTOCOL_HTTP; +} + diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/request/DebugModuleUpdateRequest.java b/backend/services/api-test/src/main/java/io/metersphere/api/request/DebugModuleUpdateRequest.java new file mode 100644 index 0000000000..bdd56f06cf --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/request/DebugModuleUpdateRequest.java @@ -0,0 +1,20 @@ +package io.metersphere.api.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.Pattern; +import lombok.Data; + +@Data +public class DebugModuleUpdateRequest { + @Schema(description = "模块ID", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "{file_module.id.not_blank}") + private String id; + + @Schema(description = "模块名称", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "{file_module.name.not_blank}") + @Pattern(regexp = "^[^\\\\/]*$", message = "{api_debug_module.name.not_contain_slash}") + private String name; +} + diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiDebugModuleLogService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiDebugModuleLogService.java new file mode 100644 index 0000000000..ac9e4a7476 --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiDebugModuleLogService.java @@ -0,0 +1,120 @@ +package io.metersphere.api.service; + +import io.metersphere.api.domain.ApiDebugModule; +import io.metersphere.project.domain.Project; +import io.metersphere.project.dto.NodeSortDTO; +import io.metersphere.project.mapper.ProjectMapper; +import io.metersphere.sdk.constants.HttpMethodConstants; +import io.metersphere.sdk.util.JSON; +import io.metersphere.sdk.util.Translator; +import io.metersphere.system.dto.builder.LogDTOBuilder; +import io.metersphere.system.dto.sdk.BaseModule; +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 jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +@Service +@Transactional(rollbackFor = Exception.class) +public class ApiDebugModuleLogService { + private static final String API_DEBUG_MODULE = "/api/debug/module"; + private static final String ADD = API_DEBUG_MODULE + "/add"; + private static final String UPDATE = API_DEBUG_MODULE + "/update"; + private static final String DELETE = API_DEBUG_MODULE + "/delete"; + private static final String MOVE = API_DEBUG_MODULE + "/move"; + private static final String MOVE_TO = "file.log.move_to"; + + @Resource + private ProjectMapper projectMapper; + @Resource + private OperationLogService operationLogService; + + public void saveAddLog(ApiDebugModule module, String operator) { + Project project = projectMapper.selectByPrimaryKey(module.getProjectId()); + LogDTO dto = LogDTOBuilder.builder() + .projectId(module.getProjectId()) + .organizationId(project.getOrganizationId()) + .type(OperationLogType.ADD.name()) + .module(OperationLogModule.API_DEBUG) + .method(HttpMethodConstants.POST.name()) + .path(ADD) + .sourceId(module.getId()) + .content(module.getName()) + .originalValue(JSON.toJSONBytes(module)) + .createUser(operator) + .build().getLogDTO(); + operationLogService.add(dto); + } + + public void saveUpdateLog(ApiDebugModule module, String projectId, String operator) { + Project project = projectMapper.selectByPrimaryKey(projectId); + LogDTO dto = LogDTOBuilder.builder() + .projectId(projectId) + .organizationId(project.getOrganizationId()) + .type(OperationLogType.UPDATE.name()) + .module(OperationLogModule.API_DEBUG) + .method(HttpMethodConstants.POST.name()) + .path(UPDATE) + .sourceId(module.getId()) + .content(module.getName()) + .originalValue(JSON.toJSONBytes(module)) + .createUser(operator) + .build().getLogDTO(); + + operationLogService.add(dto); + } + + public void saveDeleteLog(ApiDebugModule deleteModule, String operator) { + Project project = projectMapper.selectByPrimaryKey(deleteModule.getProjectId()); + LogDTO dto = LogDTOBuilder.builder() + .projectId(deleteModule.getProjectId()) + .organizationId(project.getOrganizationId()) + .type(OperationLogType.DELETE.name()) + .module(OperationLogModule.API_DEBUG) + .method(HttpMethodConstants.GET.name()) + .path(DELETE + "/%s") + .sourceId(deleteModule.getId()) + .content(deleteModule.getName() + " " + Translator.get("file.log.delete_module")) + .originalValue(JSON.toJSONBytes(deleteModule)) + .createUser(operator) + .build().getLogDTO(); + operationLogService.add(dto); + } + + public void saveMoveLog(@Validated NodeSortDTO request, String operator) { + BaseModule moveNode = request.getNode(); + BaseModule previousNode = request.getPreviousNode(); + BaseModule nextNode = request.getNextNode(); + BaseModule parentModule = request.getParent(); + + Project project = projectMapper.selectByPrimaryKey(moveNode.getProjectId()); + String logContent; + if (nextNode == null && previousNode == null) { + logContent = moveNode.getName() + " " + Translator.get(MOVE_TO) + parentModule.getName(); + } else if (nextNode == null) { + logContent = moveNode.getName() + " " + Translator.get(MOVE_TO) + parentModule.getName() + " " + previousNode.getName() + Translator.get("file.log.next"); + } else if (previousNode == null) { + logContent = moveNode.getName() + " " + Translator.get(MOVE_TO) + parentModule.getName() + " " + nextNode.getName() + Translator.get("file.log.previous"); + } else { + logContent = moveNode.getName() + " " + Translator.get(MOVE_TO) + parentModule.getName() + " " + + previousNode.getName() + Translator.get("file.log.next") + " " + nextNode.getName() + Translator.get("file.log.previous"); + } + LogDTO dto = LogDTOBuilder.builder() + .projectId(moveNode.getProjectId()) + .organizationId(project.getOrganizationId()) + .type(OperationLogType.UPDATE.name()) + .module(OperationLogModule.API_DEBUG) + .method(HttpMethodConstants.POST.name()) + .path(MOVE) + .sourceId(moveNode.getId()) + .content(logContent) + .originalValue(JSON.toJSONBytes(moveNode)) + .createUser(operator) + .build().getLogDTO(); + operationLogService.add(dto); + } +} diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiDebugModuleService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiDebugModuleService.java new file mode 100644 index 0000000000..23700dbd3f --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiDebugModuleService.java @@ -0,0 +1,333 @@ +package io.metersphere.api.service; + +import io.metersphere.api.domain.*; +import io.metersphere.api.mapper.ApiDebugBlobMapper; +import io.metersphere.api.mapper.ApiDebugMapper; +import io.metersphere.api.mapper.ApiDebugModuleMapper; +import io.metersphere.api.mapper.ExtApiDebugModuleMapper; +import io.metersphere.api.request.ApiDebugRequest; +import io.metersphere.api.request.ApiTreeNode; +import io.metersphere.api.request.DebugModuleCreateRequest; +import io.metersphere.api.request.DebugModuleUpdateRequest; +import io.metersphere.project.dto.ModuleCountDTO; +import io.metersphere.project.dto.NodeSortDTO; +import io.metersphere.project.service.ModuleTreeService; +import io.metersphere.sdk.constants.ModuleConstants; +import io.metersphere.sdk.exception.MSException; +import io.metersphere.sdk.util.Translator; +import io.metersphere.system.dto.sdk.BaseModule; +import io.metersphere.system.dto.sdk.BaseTreeNode; +import io.metersphere.system.dto.sdk.request.NodeMoveRequest; +import io.metersphere.system.uid.IDGenerator; +import jakarta.annotation.Resource; +import org.apache.commons.collections4.CollectionUtils; +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.mybatis.spring.SqlSessionUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import java.util.stream.Collectors; + +@Service +@Transactional(rollbackFor = Exception.class) +public class ApiDebugModuleService extends ModuleTreeService { + private static final String UNPLANNED = "api_debug_module.unplanned_request"; + private static final String METHOD = "method"; + private static final String DEBUG_MODULE_COUNT_ALL = "all"; + @Resource + private ApiDebugModuleLogService apiDebugModuleLogService; + @Resource + private ApiDebugModuleMapper apiDebugModuleMapper; + @Resource + private ExtApiDebugModuleMapper extApiDebugModuleMapper; + @Resource + private ApiDebugMapper apiDebugMapper; + @Resource + private ApiDebugBlobMapper apiDebugBlobMapper; + @Resource + private SqlSessionFactory sqlSessionFactory; + + public List getTree(String protocol, String userId) { + List fileModuleList = extApiDebugModuleMapper.selectBaseByProtocolAndUser(protocol, userId); + List baseTreeNodes = super.buildTreeAndCountResource(fileModuleList, true, Translator.get(UNPLANNED)); + List apiTreeNodeList = extApiDebugModuleMapper.selectApiDebugByProtocolAndUser(protocol, userId); + //将apiTreeNodeList转换成BaseTreeNode method放入map中 + if (CollectionUtils.isEmpty(apiTreeNodeList)) { + return baseTreeNodes; + } + List nodeList = apiTreeNodeList.stream().map(apiTreeNode -> { + BaseTreeNode baseTreeNode = new BaseTreeNode(); + baseTreeNode.setId(apiTreeNode.getId()); + baseTreeNode.setName(apiTreeNode.getName()); + baseTreeNode.setParentId(apiTreeNode.getParentId()); + baseTreeNode.setType(apiTreeNode.getType()); + baseTreeNode.putAttachInfo(METHOD, apiTreeNode.getMethod()); + return baseTreeNode; + }).toList(); + //apiTreeNodeList使用stream实现将parentId分组生成map + Map> apiTreeNodeMap = nodeList.stream().collect(Collectors.groupingBy(BaseTreeNode::getParentId)); + //遍历baseTreeNodes,将apiTreeNodeMap中的id相等的数据添加到baseTreeNodes中 + return generateTree(baseTreeNodes, apiTreeNodeMap); + + } + + //生成树结构 + public List generateTree(List baseTreeNodes, Map> apiTreeNodeMap) { + baseTreeNodes.forEach(baseTreeNode -> { + if (apiTreeNodeMap.containsKey(baseTreeNode.getId())) { + baseTreeNode.getChildren().addAll(apiTreeNodeMap.get(baseTreeNode.getId())); + } + if (CollectionUtils.isNotEmpty(baseTreeNode.getChildren())) { + generateTree(baseTreeNode.getChildren(), apiTreeNodeMap); + } + }); + return baseTreeNodes; + } + + public List getTreeOnlyIdsAndResourceCount(String protocol, String userId, List moduleCountDTOList) { + //节点内容只有Id和parentId + List fileModuleList = extApiDebugModuleMapper.selectIdAndParentIdByProtocolAndUserId(protocol, userId); + return super.buildTreeAndCountResource(fileModuleList, moduleCountDTOList, true, Translator.get(UNPLANNED)); + } + + public String add(DebugModuleCreateRequest request, String operator) { + ApiDebugModule apiDebugModule = new ApiDebugModule(); + apiDebugModule.setId(IDGenerator.nextStr()); + apiDebugModule.setName(request.getName()); + apiDebugModule.setParentId(request.getParentId()); + apiDebugModule.setProjectId(request.getProjectId()); + apiDebugModule.setProtocol(request.getProtocol()); + apiDebugModule.setCreateUser(operator); + this.checkDataValidity(apiDebugModule); + apiDebugModule.setCreateTime(System.currentTimeMillis()); + apiDebugModule.setUpdateTime(apiDebugModule.getCreateTime()); + apiDebugModule.setPos(this.countPos(request.getParentId())); + apiDebugModule.setUpdateUser(operator); + apiDebugModuleMapper.insert(apiDebugModule); + //记录日志 + apiDebugModuleLogService.saveAddLog(apiDebugModule, operator); + return apiDebugModule.getId(); + } + + private Integer countPos(String parentId) { + Integer maxPos = extApiDebugModuleMapper.getMaxPosByParentId(parentId); + if (maxPos == null) { + return LIMIT_POS; + } else { + return maxPos + LIMIT_POS; + } + } + + /** + * 检查数据的合法性 + */ + private void checkDataValidity(ApiDebugModule apiDebugModule) { + ApiDebugModuleExample example = new ApiDebugModuleExample(); + if (!StringUtils.equals(apiDebugModule.getParentId(), ModuleConstants.ROOT_NODE_PARENT_ID)) { + //检查父ID是否存在 调试模块的逻辑是 同一个用户下的同级模块不能重名 每个协议是不同的模块 + example.createCriteria().andIdEqualTo(apiDebugModule.getParentId()) + .andCreateUserEqualTo(apiDebugModule.getCreateUser()) + .andProtocolEqualTo(apiDebugModule.getProtocol()); + if (apiDebugModuleMapper.countByExample(example) == 0) { + throw new MSException(Translator.get("parent.node.not_blank")); + } + example.clear(); + } + example.createCriteria().andParentIdEqualTo(apiDebugModule.getParentId()) + .andNameEqualTo(apiDebugModule.getName()) + .andIdNotEqualTo(apiDebugModule.getId()) + .andCreateUserEqualTo(apiDebugModule.getCreateUser()) + .andProtocolEqualTo(apiDebugModule.getProtocol()); + if (apiDebugModuleMapper.countByExample(example) > 0) { + throw new MSException(Translator.get("node.name.repeat")); + } + example.clear(); + } + + public void update(DebugModuleUpdateRequest request, String userId, String projectId) { + ApiDebugModule module = apiDebugModuleMapper.selectByPrimaryKey(request.getId()); + if (module == null) { + throw new MSException("file_module.not.exist"); + } + ApiDebugModule updateModule = new ApiDebugModule(); + updateModule.setId(request.getId()); + updateModule.setName(request.getName()); + updateModule.setParentId(module.getParentId()); + updateModule.setProtocol(module.getProtocol()); + updateModule.setCreateUser(module.getCreateUser()); + this.checkDataValidity(updateModule); + updateModule.setUpdateTime(System.currentTimeMillis()); + updateModule.setUpdateUser(userId); + updateModule.setProjectId(projectId); + apiDebugModuleMapper.updateByPrimaryKeySelective(updateModule); + //记录日志 + apiDebugModuleLogService.saveUpdateLog(updateModule, projectId, userId); + } + + + public void deleteModule(String deleteId, String currentUser) { + ApiDebugModule deleteModule = apiDebugModuleMapper.selectByPrimaryKey(deleteId); + if (deleteModule != null) { + this.deleteModule(Collections.singletonList(deleteId)); + //记录日志 + apiDebugModuleLogService.saveDeleteLog(deleteModule, currentUser); + } + } + + public void deleteModule(List deleteIds) { + if (CollectionUtils.isEmpty(deleteIds)) { + return; + } + extApiDebugModuleMapper.deleteByIds(deleteIds); + //删除模块下的所有接口 + ApiDebugExample example = new ApiDebugExample(); + example.createCriteria().andModuleIdIn(deleteIds); + List apiDebugs = apiDebugMapper.selectByExample(example); + if (CollectionUtils.isNotEmpty(apiDebugs)) { + List apiDebugIds = apiDebugs.stream().map(ApiDebug::getId).collect(Collectors.toList()); + apiDebugMapper.deleteByExample(example); + ApiDebugBlobExample blobExample = new ApiDebugBlobExample(); + blobExample.createCriteria().andIdIn(apiDebugIds); + apiDebugBlobMapper.deleteByExample(blobExample); + } + + List childrenIds = extApiDebugModuleMapper.selectChildrenIdsByParentIds(deleteIds); + if (CollectionUtils.isNotEmpty(childrenIds)) { + deleteModule(childrenIds); + } + } + + public void moveNode(NodeMoveRequest request, String currentUser) { + BaseModule module; + BaseModule parentModule; + BaseModule previousNode = null; + BaseModule nextNode = null; + + ApiDebugModule dragNode = apiDebugModuleMapper.selectByPrimaryKey(request.getDragNodeId()); + if (dragNode == null) { + throw new MSException("file_module.not.exist:" + request.getDragNodeId()); + } else { + module = new BaseModule(dragNode.getId(), dragNode.getName(), dragNode.getPos(), dragNode.getProjectId(), dragNode.getParentId()); + } + + if (StringUtils.equals(request.getDragNodeId(), request.getDropNodeId())) { + //两种节点不能一样 + throw new MSException(Translator.get("invalid_parameter")); + } + + ApiDebugModule dropNode = apiDebugModuleMapper.selectByPrimaryKey(request.getDropNodeId()); + if (dropNode == null) { + throw new MSException("file_module.not.exist:" + request.getDropNodeId()); + } + + if (request.getDropPosition() == 0) { + //dropPosition=0: 放到dropNode节点内,最后一个节点之后 + parentModule = new BaseModule(dropNode.getId(), dropNode.getName(), dropNode.getPos(), dropNode.getProjectId(), dropNode.getParentId()); + ApiDebugModule previousModule = extApiDebugModuleMapper.getLastModuleByParentId(parentModule.getId()); + if (previousModule != null) { + previousNode = new BaseModule(previousModule.getId(), previousModule.getName(), previousModule.getPos(), previousModule.getProjectId(), previousModule.getParentId()); + } + } else { + if (StringUtils.equals(dropNode.getParentId(), ModuleConstants.ROOT_NODE_PARENT_ID)) { + parentModule = new BaseModule(ModuleConstants.ROOT_NODE_PARENT_ID, ModuleConstants.ROOT_NODE_PARENT_ID, 0, module.getProjectId(), ModuleConstants.ROOT_NODE_PARENT_ID); + } else { + ApiDebugModule parent = apiDebugModuleMapper.selectByPrimaryKey(dropNode.getParentId()); + parentModule = new BaseModule(parent.getId(), parent.getName(), parent.getPos(), parent.getProjectId(), parent.getParentId()); + } + + if (request.getDropPosition() == 1) { + //dropPosition=1: 放到dropNode节点后,原dropNode后面的节点之前 + previousNode = new BaseModule(dropNode.getId(), dropNode.getName(), dropNode.getPos(), dropNode.getProjectId(), dropNode.getParentId()); + ApiDebugModule nextModule = extApiDebugModuleMapper.getNextModuleInParentId(previousNode.getParentId(), previousNode.getPos()); + if (nextModule != null) { + nextNode = new BaseModule(nextModule.getId(), nextModule.getName(), nextModule.getPos(), nextModule.getProjectId(), nextModule.getParentId()); + } + } else if (request.getDropPosition() == -1) { + //dropPosition=-1: 放到dropNode节点前,原dropNode前面的节点之后 + nextNode = new BaseModule(dropNode.getId(), dropNode.getName(), dropNode.getPos(), dropNode.getProjectId(), dropNode.getParentId()); + ApiDebugModule previousModule = extApiDebugModuleMapper.getPreviousModuleInParentId(nextNode.getParentId(), nextNode.getPos()); + if (previousModule != null) { + previousNode = new BaseModule(previousModule.getId(), previousModule.getName(), previousModule.getPos(), previousModule.getProjectId(), previousModule.getParentId()); + } + } else { + throw new MSException(Translator.get("invalid_parameter")); + } + } + + ApiDebugModuleExample example = new ApiDebugModuleExample(); + example.createCriteria().andParentIdEqualTo(parentModule.getId()).andIdEqualTo(module.getId()); + //节点换到了别的节点下,要先更新parent节点. + if (apiDebugModuleMapper.countByExample(example) == 0) { + ApiDebugModule fileModule = new ApiDebugModule(); + fileModule.setId(module.getId()); + fileModule.setParentId(parentModule.getId()); + apiDebugModuleMapper.updateByPrimaryKeySelective(fileModule); + } + + NodeSortDTO nodeMoveDTO = new NodeSortDTO(module, parentModule, previousNode, nextNode); + super.sort(nodeMoveDTO); + + //记录日志 + apiDebugModuleLogService.saveMoveLog(nodeMoveDTO, currentUser); + } + + /** + * 查找当前项目下模块每个节点对应的资源统计 + */ + public Map getModuleCountMap(String protocol, String userId, List moduleCountDTOList) { + Map returnMap = new HashMap<>(); + //构建模块树,并计算每个节点下的所有数量(包含子节点) + List treeNodeList = this.getTreeOnlyIdsAndResourceCount(protocol, userId, moduleCountDTOList); + //通过广度遍历的方式构建返回值 + List whileList = new ArrayList<>(treeNodeList); + while (CollectionUtils.isNotEmpty(whileList)) { + List childList = new ArrayList<>(); + for (BaseTreeNode treeNode : whileList) { + returnMap.put(treeNode.getId(), treeNode.getCount()); + childList.addAll(treeNode.getChildren()); + } + whileList = childList; + } + return returnMap; + } + + + @Override + public void updatePos(String id, int pos) { + ApiDebugModule updateModule = new ApiDebugModule(); + updateModule.setPos(pos); + updateModule.setId(id); + apiDebugModuleMapper.updateByPrimaryKeySelective(updateModule); + } + + @Override + public void refreshPos(String parentId) { + List childrenIdSortByPos = extApiDebugModuleMapper.selectChildrenIdsSortByPos(parentId); + SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH); + ApiDebugModuleMapper batchUpdateMapper = sqlSession.getMapper(ApiDebugModuleMapper.class); + for (int i = 0; i < childrenIdSortByPos.size(); i++) { + String nodeId = childrenIdSortByPos.get(i); + ApiDebugModule updateModule = new ApiDebugModule(); + updateModule.setId(nodeId); + updateModule.setPos((i + 1) * LIMIT_POS); + batchUpdateMapper.updateByPrimaryKeySelective(updateModule); + } + sqlSession.flushStatements(); + SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory); + } + + public Map moduleCount(ApiDebugRequest request, String operator) { + //查出每个模块节点下的资源数量。 不需要按照模块进行筛选 + request.setModuleIds(null); + List moduleCountDTOList = extApiDebugModuleMapper.countModuleIdByKeywordAndProtocol(request, operator); + long allCount = getAllCount(moduleCountDTOList); + Map moduleCountMap = getModuleCountMap(request.getProtocol(), operator, moduleCountDTOList); + moduleCountMap.put(DEBUG_MODULE_COUNT_ALL, allCount); + return moduleCountMap; + } + +} diff --git a/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiDebugModuleControllerTests.java b/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiDebugModuleControllerTests.java new file mode 100644 index 0000000000..0150e16ae5 --- /dev/null +++ b/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiDebugModuleControllerTests.java @@ -0,0 +1,822 @@ +package io.metersphere.api.controller; + +import io.metersphere.api.domain.*; +import io.metersphere.api.mapper.ApiDebugBlobMapper; +import io.metersphere.api.mapper.ApiDebugMapper; +import io.metersphere.api.mapper.ApiDebugModuleMapper; +import io.metersphere.api.request.ApiDebugRequest; +import io.metersphere.api.request.DebugModuleCreateRequest; +import io.metersphere.api.request.DebugModuleUpdateRequest; +import io.metersphere.api.service.ApiDebugModuleService; +import io.metersphere.project.domain.Project; +import io.metersphere.project.mapper.ProjectMapper; +import io.metersphere.sdk.constants.ModuleConstants; +import io.metersphere.sdk.constants.PermissionConstants; +import io.metersphere.sdk.constants.SessionConstants; +import io.metersphere.sdk.util.JSON; +import io.metersphere.system.base.BaseTest; +import io.metersphere.system.controller.handler.ResultHolder; +import io.metersphere.system.dto.sdk.BaseTreeNode; +import io.metersphere.system.dto.sdk.request.NodeMoveRequest; +import io.metersphere.system.log.constants.OperationLogType; +import io.metersphere.system.uid.IDGenerator; +import jakarta.annotation.Resource; +import org.apache.commons.collections4.CollectionUtils; +import org.junit.jupiter.api.*; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.ResultMatcher; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.testcontainers.shaded.org.apache.commons.lang3.StringUtils; + +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +@AutoConfigureMockMvc +public class ApiDebugModuleControllerTests extends BaseTest { + + private static final String URL_MODULE_ADD = "/api/debug/module/add"; + private static final String URL_MODULE_UPDATE = "/api/debug/module/update"; + private static final String URL_MODULE_DELETE = "/api/debug/module/delete/%s"; + private static final String URL_MODULE_TREE = "/api/debug/module/tree/%s"; + private static final String URL_MODULE_MOVE = "/api/debug/module/move"; + private static final String URL_FILE_MODULE_COUNT = "/api/debug/module/count"; + private static final ResultMatcher BAD_REQUEST_MATCHER = status().isBadRequest(); + private static final ResultMatcher ERROR_REQUEST_MATCHER = status().is5xxServerError(); + private static Project project; + private static List preliminaryTreeNodes = new ArrayList<>(); + @Resource + private ProjectMapper projectMapper; + @Resource + private ApiDebugModuleMapper apiDebugModuleMapper; + @Resource + private ApiDebugMapper apiDebugMapper; + @Resource + private ApiDebugBlobMapper apiDebugBlobMapper; + @Resource + private ApiDebugModuleService apiDebugModuleService; + + public static BaseTreeNode getNodeByName(List preliminaryTreeNodes, String nodeName) { + for (BaseTreeNode firstLevelNode : preliminaryTreeNodes) { + if (StringUtils.equals(firstLevelNode.getName(), nodeName) && StringUtils.equals(firstLevelNode.getType(), ModuleConstants.NODE_TYPE_DEFAULT)) { + return firstLevelNode; + } + if (CollectionUtils.isNotEmpty(firstLevelNode.getChildren())) { + for (BaseTreeNode secondLevelNode : firstLevelNode.getChildren()) { + if (StringUtils.equals(secondLevelNode.getName(), nodeName) && StringUtils.equals(secondLevelNode.getType(), ModuleConstants.NODE_TYPE_DEFAULT)) { + return secondLevelNode; + } + if (CollectionUtils.isNotEmpty(secondLevelNode.getChildren())) { + for (BaseTreeNode thirdLevelNode : secondLevelNode.getChildren()) { + if (StringUtils.equals(thirdLevelNode.getName(), nodeName) && StringUtils.equals(thirdLevelNode.getType(), ModuleConstants.NODE_TYPE_DEFAULT)) { + return thirdLevelNode; + } + } + } + } + } + } + return null; + } + + @BeforeEach + public void initTestData() { + if (project == null) { + Project initProject = new Project(); + initProject.setId(IDGenerator.nextStr()); + initProject.setNum(null); + initProject.setOrganizationId("100001"); + initProject.setName("接口调试的项目"); + initProject.setDescription("接口调试的项目"); + initProject.setCreateUser("admin"); + initProject.setUpdateUser("admin"); + initProject.setCreateTime(System.currentTimeMillis()); + initProject.setUpdateTime(System.currentTimeMillis()); + initProject.setEnable(true); + initProject.setModuleSetting("[\"apiTest\",\"uiTest\"]"); + projectMapper.insertSelective(initProject); + project = projectMapper.selectByPrimaryKey(initProject.getId()); + } + } + + public void initApiDebugData(String moduleId) { + ApiDebug apiDebug = new ApiDebug(); + apiDebug.setId(IDGenerator.nextStr()); + apiDebug.setProjectId(project.getId()); + apiDebug.setName(StringUtils.join("接口调试", apiDebug.getId())); + apiDebug.setModuleId(moduleId); + apiDebug.setProtocol("HTTP"); + apiDebug.setMethod("GET"); + apiDebug.setPath(StringUtils.join("api/debug/", apiDebug.getId())); + apiDebug.setCreateTime(System.currentTimeMillis()); + apiDebug.setUpdateTime(System.currentTimeMillis()); + apiDebug.setCreateUser("admin"); + apiDebug.setUpdateUser("admin"); + apiDebugMapper.insertSelective(apiDebug); + ApiDebugBlob apiDebugBlob = new ApiDebugBlob(); + apiDebugBlob.setId(apiDebug.getId()); + apiDebugBlob.setRequest(new byte[0]); + apiDebugBlob.setResponse(new byte[0]); + apiDebugBlobMapper.insertSelective(apiDebugBlob); + } + + @Test + @Order(2) + public void addModuleTestSuccess() throws Exception { + //根目录下创建节点(a1) + DebugModuleCreateRequest request = new DebugModuleCreateRequest(); + request.setProjectId(project.getId()); + request.setName("a1"); + MvcResult mvcResult = this.requestPostWithOkAndReturn(URL_MODULE_ADD, request); + String returnId = mvcResult.getResponse().getContentAsString(); + Assertions.assertNotNull(returnId); + List treeNodes = this.getDebugModuleTreeNode(); + BaseTreeNode a1Node = null; + for (BaseTreeNode baseTreeNode : treeNodes) { + if (StringUtils.equals(baseTreeNode.getName(), request.getName())) { + a1Node = baseTreeNode; + } + Assertions.assertNotNull(baseTreeNode.getParentId()); + } + Assertions.assertNotNull(a1Node); + initApiDebugData(a1Node.getId()); + checkLog(a1Node.getId(), OperationLogType.ADD, URL_MODULE_ADD); + + //根目录下创建节点a2和a3,在a1下创建子节点a1-b1 + request = new DebugModuleCreateRequest(); + request.setProjectId(project.getId()); + request.setName("a2"); + this.requestPostWithOkAndReturn(URL_MODULE_ADD, request); + + + request.setName("a3"); + this.requestPostWithOkAndReturn(URL_MODULE_ADD, request); + + request = new DebugModuleCreateRequest(); + request.setProjectId(project.getId()); + request.setName("a1-b1"); + request.setParentId(a1Node.getId()); + this.requestPostWithOkAndReturn(URL_MODULE_ADD, request); + + treeNodes = this.getDebugModuleTreeNode(); + BaseTreeNode a1b1Node = null; + BaseTreeNode a2Node = null; + for (BaseTreeNode baseTreeNode : treeNodes) { + Assertions.assertNotNull(baseTreeNode.getParentId()); + if (StringUtils.equals(baseTreeNode.getName(), "a1") && CollectionUtils.isNotEmpty(baseTreeNode.getChildren())) { + for (BaseTreeNode childNode : baseTreeNode.getChildren()) { + if (StringUtils.equals(childNode.getName(), "a1-b1") && StringUtils.equals(childNode.getType(), ModuleConstants.NODE_TYPE_DEFAULT)) { + a1b1Node = childNode; + } + Assertions.assertNotNull(childNode.getParentId()); + } + } else if (StringUtils.equals(baseTreeNode.getName(), "a2") && StringUtils.equals(baseTreeNode.getType(), ModuleConstants.NODE_TYPE_DEFAULT)) { + a2Node = baseTreeNode; + } + } + Assertions.assertNotNull(a2Node); + Assertions.assertNotNull(a1b1Node); + initApiDebugData(a2Node.getId()); + initApiDebugData(a1b1Node.getId()); + checkLog(a2Node.getId(), OperationLogType.ADD, URL_MODULE_ADD); + checkLog(a1b1Node.getId(), OperationLogType.ADD, URL_MODULE_ADD); + + //a1节点下可以继续添加a1节点 + request = new DebugModuleCreateRequest(); + request.setProjectId(project.getId()); + request.setName("a1"); + request.setParentId(a1Node.getId()); + this.requestPostWithOkAndReturn(URL_MODULE_ADD, request); + + //继续创建a1下继续创建a1-a1-b1, + treeNodes = this.getDebugModuleTreeNode(); + BaseTreeNode a1ChildNode = null; + for (BaseTreeNode baseTreeNode : treeNodes) { + Assertions.assertNotNull(baseTreeNode.getParentId()); + if (StringUtils.equals(baseTreeNode.getName(), "a1") && CollectionUtils.isNotEmpty(baseTreeNode.getChildren())) { + for (BaseTreeNode childNode : baseTreeNode.getChildren()) { + Assertions.assertNotNull(childNode.getParentId()); + if (StringUtils.equals(childNode.getName(), "a1") && StringUtils.equals(childNode.getType(), ModuleConstants.NODE_TYPE_DEFAULT)) { + a1ChildNode = childNode; + } + } + } + } + Assertions.assertNotNull(a1ChildNode); + initApiDebugData(a1ChildNode.getId()); + checkLog(a1ChildNode.getId(), OperationLogType.ADD, URL_MODULE_ADD); + + //a1的子节点a1下继续创建节点a1-a1-c1 + request = new DebugModuleCreateRequest(); + request.setProjectId(project.getId()); + request.setName("a1-a1-c1"); + request.setParentId(a1ChildNode.getId()); + this.requestPostWithOkAndReturn(URL_MODULE_ADD, request); + treeNodes = this.getDebugModuleTreeNode(); + BaseTreeNode a1a1c1Node = null; + for (BaseTreeNode baseTreeNode : treeNodes) { + Assertions.assertNotNull(baseTreeNode.getParentId()); + if (StringUtils.equals(baseTreeNode.getName(), "a1") && CollectionUtils.isNotEmpty(baseTreeNode.getChildren())) { + for (BaseTreeNode secondNode : baseTreeNode.getChildren()) { + Assertions.assertNotNull(secondNode.getParentId()); + if (StringUtils.equals(secondNode.getName(), "a1") && CollectionUtils.isNotEmpty(secondNode.getChildren())) { + for (BaseTreeNode thirdNode : secondNode.getChildren()) { + Assertions.assertNotNull(thirdNode.getParentId()); + if (StringUtils.equals(thirdNode.getName(), "a1-a1-c1") && StringUtils.equals(thirdNode.getType(), ModuleConstants.NODE_TYPE_DEFAULT)) { + a1a1c1Node = thirdNode; + } + } + } + } + } + } + Assertions.assertNotNull(a1a1c1Node); + initApiDebugData(a1a1c1Node.getId()); + checkLog(a1a1c1Node.getId(), OperationLogType.ADD, URL_MODULE_ADD); + + //子节点a1-b1下继续创建节点a1-b1-c1 + request = new DebugModuleCreateRequest(); + request.setProjectId(project.getId()); + request.setName("a1-b1-c1"); + request.setParentId(a1b1Node.getId()); + this.requestPostWithOkAndReturn(URL_MODULE_ADD, request); + treeNodes = this.getDebugModuleTreeNode(); + BaseTreeNode a1b1c1Node = null; + for (BaseTreeNode baseTreeNode : treeNodes) { + if (StringUtils.equals(baseTreeNode.getName(), "a1") && CollectionUtils.isNotEmpty(baseTreeNode.getChildren())) { + for (BaseTreeNode secondNode : baseTreeNode.getChildren()) { + if (StringUtils.equals(secondNode.getName(), "a1-b1") && CollectionUtils.isNotEmpty(secondNode.getChildren())) { + for (BaseTreeNode thirdNode : secondNode.getChildren()) { + if (StringUtils.equals(thirdNode.getName(), "a1-b1-c1") && StringUtils.equals(thirdNode.getType(), ModuleConstants.NODE_TYPE_DEFAULT)) { + a1b1c1Node = thirdNode; + } + } + } + } + } + } + Assertions.assertNotNull(a1b1c1Node); + initApiDebugData(a1b1c1Node.getId()); + preliminaryTreeNodes = treeNodes; + + checkLog(a1b1c1Node.getId(), OperationLogType.ADD, URL_MODULE_ADD); + //校验权限 + request = new DebugModuleCreateRequest(); + request.setProjectId(DEFAULT_PROJECT_ID); + request.setName("defaultProject"); + requestPostPermissionTest(PermissionConstants.PROJECT_API_DEBUG_READ_ADD, URL_MODULE_ADD, request); + } + + @Test + @Order(3) + public void addModuleTestError() throws Exception { + this.preliminaryData(); + + BaseTreeNode a1Node = getNodeByName(preliminaryTreeNodes, "a1"); + assert a1Node != null; + + //参数校验 + DebugModuleCreateRequest request = new DebugModuleCreateRequest(); + request.setProjectId(project.getId()); + this.requestPost(URL_MODULE_ADD, request).andExpect(BAD_REQUEST_MATCHER); + request = new DebugModuleCreateRequest(); + request.setName("none"); + this.requestPost(URL_MODULE_ADD, request).andExpect(BAD_REQUEST_MATCHER); + request = new DebugModuleCreateRequest(); + this.requestPost(URL_MODULE_ADD, request).andExpect(BAD_REQUEST_MATCHER); + request = new DebugModuleCreateRequest(); + request.setParentId(null); + this.requestPost(URL_MODULE_ADD, request).andExpect(BAD_REQUEST_MATCHER); + //名称存在特殊字符 + request.setName("a1/a2"); + request.setParentId(a1Node.getId()); + this.requestPost(URL_MODULE_ADD, request).andExpect(BAD_REQUEST_MATCHER); + + //父节点ID不存在的 + request = new DebugModuleCreateRequest(); + request.setProjectId(project.getId()); + request.setName("ParentIsUUID"); + request.setParentId(IDGenerator.nextStr()); + this.requestPost(URL_MODULE_ADD, request).andExpect(ERROR_REQUEST_MATCHER); + + //添加重复的a1节点 + request = new DebugModuleCreateRequest(); + request.setProjectId(project.getId()); + request.setName("a1"); + this.requestPost(URL_MODULE_ADD, request).andExpect(ERROR_REQUEST_MATCHER); + + //a1节点下添加重复的a1-b1节点 + request = new DebugModuleCreateRequest(); + request.setProjectId(project.getId()); + request.setName("a1-b1"); + request.setParentId(a1Node.getId()); + this.requestPost(URL_MODULE_ADD, request).andExpect(ERROR_REQUEST_MATCHER); + + } + + @Test + @Order(4) + public void updateModuleTestSuccess() throws Exception { + if (CollectionUtils.isEmpty(preliminaryTreeNodes)) { + this.addModuleTestSuccess(); + } + //更改名称 + BaseTreeNode a1Node = null; + for (BaseTreeNode node : preliminaryTreeNodes) { + if (StringUtils.equals(node.getName(), "a1")) { + for (BaseTreeNode a1ChildrenNode : node.getChildren()) { + if (StringUtils.equals(a1ChildrenNode.getName(), "a1")) { + a1Node = a1ChildrenNode; + } + } + } + } + assert a1Node != null; + DebugModuleUpdateRequest updateRequest = new DebugModuleUpdateRequest(); + updateRequest.setId(a1Node.getId()); + updateRequest.setName("a1-a1"); + //无法拿到当前用户的projectID 所以需要传入 + mockMvc.perform(MockMvcRequestBuilders.post(URL_MODULE_UPDATE) + .header(SessionConstants.HEADER_TOKEN, sessionId) + .header(SessionConstants.CSRF_TOKEN, csrfToken) + .header(SessionConstants.CURRENT_PROJECT, project.getId()) + .content(JSON.toJSONString(updateRequest)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)).andReturn(); + + preliminaryTreeNodes = this.getDebugModuleTreeNode(); + checkLog(a1Node.getId(), OperationLogType.UPDATE, URL_MODULE_UPDATE); + + //校验权限 + ApiDebugModuleExample example = new ApiDebugModuleExample(); + example.createCriteria().andProjectIdEqualTo(DEFAULT_PROJECT_ID).andNameEqualTo("defaultProject"); + List apiDebugModules = apiDebugModuleMapper.selectByExample(example); + assert CollectionUtils.isNotEmpty(apiDebugModules); + updateRequest = new DebugModuleUpdateRequest(); + updateRequest.setId(apiDebugModules.get(0).getId()); + updateRequest.setName("default-update-Project"); + requestPostPermissionTest(PermissionConstants.PROJECT_API_DEBUG_READ_UPDATE, URL_MODULE_UPDATE, updateRequest); + } + + @Test + @Order(5) + public void updateModuleTestError() throws Exception { + BaseTreeNode a1Node = getNodeByName(preliminaryTreeNodes, "a1-a1"); + assert a1Node != null; + //反例-参数校验 + DebugModuleUpdateRequest updateRequest = new DebugModuleUpdateRequest(); + this.requestPost(URL_MODULE_UPDATE, updateRequest).andExpect(BAD_REQUEST_MATCHER); + //参数名称带有特殊字符 + updateRequest = new DebugModuleUpdateRequest(); + updateRequest.setId(a1Node.getId()); + updateRequest.setName("a1/a2"); + this.requestPost(URL_MODULE_UPDATE, updateRequest).andExpect(BAD_REQUEST_MATCHER); + + //id不存在 + updateRequest = new DebugModuleUpdateRequest(); + updateRequest.setId(IDGenerator.nextStr()); + updateRequest.setName(IDGenerator.nextStr()); + this.requestPost(URL_MODULE_UPDATE, updateRequest).andExpect(ERROR_REQUEST_MATCHER); + + //名称重复 a1-a1改为a1-b1 + updateRequest = new DebugModuleUpdateRequest(); + updateRequest.setId(a1Node.getId()); + updateRequest.setName("a1-b1"); + this.requestPost(URL_MODULE_UPDATE, updateRequest).andExpect(ERROR_REQUEST_MATCHER); + } + + @Test + @Order(6) + public void moveTest() throws Exception { + this.preliminaryData(); + /* + *默认节点 + | + ·a1 + + | | + | ·a1-b1 + + | | | + | | ·a1-b1-c1 + | | + | *a1-a1 +(创建的时候是a1,通过修改改为a1-a1) + | | + | ·a1-a1-c1 + | + ·a2 + | + ·a3 + */ + BaseTreeNode a1Node = getNodeByName(preliminaryTreeNodes, "a1"); + BaseTreeNode a2Node = getNodeByName(preliminaryTreeNodes, "a2"); + BaseTreeNode a3Node = getNodeByName(preliminaryTreeNodes, "a3"); + BaseTreeNode a1a1Node = getNodeByName(preliminaryTreeNodes, "a1-a1"); + BaseTreeNode a1b1Node = getNodeByName(preliminaryTreeNodes, "a1-b1"); + + //父节点内移动-移动到首位 a1挪到a3后面 + NodeMoveRequest request = new NodeMoveRequest(); + { + assert a1Node != null; + request.setDragNodeId(a1Node.getId()); + assert a3Node != null; + request.setDropNodeId(a3Node.getId()); + request.setDropPosition(1); + this.requestPostWithOk(URL_MODULE_MOVE, request); + this.checkModulePos(a3Node.getId(), a1Node.getId(), null, false); + } + //父节点内移动-移动到末位 在上面的基础上,a1挪到a2上面 + { + request = new NodeMoveRequest(); + request.setDragNodeId(a1Node.getId()); + assert a2Node != null; + request.setDropNodeId(a2Node.getId()); + request.setDropPosition(-1); + this.requestPostWithOk(URL_MODULE_MOVE, request); + this.checkModulePos(a1Node.getId(), a2Node.getId(), null, false); + } + + //父节点内移动-移动到中位 a1移动到a2-a3中间 + { + request = new NodeMoveRequest(); + request.setDragNodeId(a1Node.getId()); + request.setDropNodeId(a2Node.getId()); + request.setDropPosition(1); + this.requestPostWithOk(URL_MODULE_MOVE, request); + this.checkModulePos(a2Node.getId(), a1Node.getId(), a3Node.getId(), false); + //移动回去 + request = new NodeMoveRequest(); + request.setDragNodeId(a1Node.getId()); + request.setDropNodeId(a2Node.getId()); + request.setDropPosition(-1); + this.requestPostWithOk(URL_MODULE_MOVE, request); + this.checkModulePos(a1Node.getId(), a2Node.getId(), null, false); + } + + //跨节点移动-移动到首位 a3移动到a1-b1前面,然后移动回来; + { + request = new NodeMoveRequest(); + request.setDragNodeId(a3Node.getId()); + request.setDropNodeId(a1b1Node.getId()); + request.setDropPosition(-1); + this.requestPostWithOk(URL_MODULE_MOVE, request); + this.checkModulePos(a3Node.getId(), a1b1Node.getId(), null, false); + //移动回去 + request = new NodeMoveRequest(); + request.setDragNodeId(a3Node.getId()); + request.setDropNodeId(a2Node.getId()); + request.setDropPosition(1); + this.requestPostWithOk(URL_MODULE_MOVE, request); + this.checkModulePos(a2Node.getId(), a3Node.getId(), null, false); + } + + //跨节点移动-移动到末尾 a3移动到a1-a1后面,然后移动回来; + { + request = new NodeMoveRequest(); + request.setDragNodeId(a3Node.getId()); + assert a1a1Node != null; + request.setDropNodeId(a1a1Node.getId()); + request.setDropPosition(1); + this.requestPostWithOk(URL_MODULE_MOVE, request); + this.checkModulePos(a1a1Node.getId(), a3Node.getId(), null, false); + //移动回去 + request = new NodeMoveRequest(); + request.setDragNodeId(a3Node.getId()); + request.setDropNodeId(a2Node.getId()); + request.setDropPosition(1); + this.requestPostWithOk(URL_MODULE_MOVE, request); + this.checkModulePos(a2Node.getId(), a3Node.getId(), null, false); + } + + //跨节点移动-移动到中位 a3移动到a1-b1和a1-a1中间,然后移动回来; + { + request = new NodeMoveRequest(); + request.setDragNodeId(a3Node.getId()); + request.setDropNodeId(a1b1Node.getId()); + request.setDropPosition(1); + this.requestPostWithOk(URL_MODULE_MOVE, request); + this.checkModulePos(a1b1Node.getId(), a3Node.getId(), a1a1Node.getId(), false); + //移动回去 + request = new NodeMoveRequest(); + request.setDragNodeId(a3Node.getId()); + request.setDropNodeId(a2Node.getId()); + request.setDropPosition(1); + this.requestPostWithOk(URL_MODULE_MOVE, request); + this.checkModulePos(a2Node.getId(), a3Node.getId(), null, false); + } + + //父节点内移动-a3移动到首位pos小于2,是否触发计算函数 (先手动更改a1的pos为2,然后移动a3到a1前面) + { + //更改pos + ApiDebugModule updateModule = new ApiDebugModule(); + updateModule.setId(a1Node.getId()); + updateModule.setPos(2); + apiDebugModuleMapper.updateByPrimaryKeySelective(updateModule); + + //开始移动 + request = new NodeMoveRequest(); + request.setDragNodeId(a3Node.getId()); + request.setDropNodeId(a1Node.getId()); + request.setDropPosition(-1); + this.requestPostWithOk(URL_MODULE_MOVE, request); + this.checkModulePos(a3Node.getId(), a1Node.getId(), null, true); + + //移动回去 + request = new NodeMoveRequest(); + request.setDragNodeId(a3Node.getId()); + request.setDropNodeId(a2Node.getId()); + request.setDropPosition(1); + this.requestPostWithOk(URL_MODULE_MOVE, request); + this.checkModulePos(a2Node.getId(), a3Node.getId(), null, false); + } + + //父节点内移动-移动到中位,前后节点pos差不大于2,是否触发计算函数(在上面的 a3-a1-a2的基础上, 先手动更改a1pos为3*64,a2的pos为3*64+2,然后移动a3到a1和a2中间) + { + //更改pos + ApiDebugModule updateModule = new ApiDebugModule(); + updateModule.setId(a1Node.getId()); + updateModule.setPos(3 * 64); + apiDebugModuleMapper.updateByPrimaryKeySelective(updateModule); + updateModule.setId(a2Node.getId()); + updateModule.setPos(3 * 64 + 2); + apiDebugModuleMapper.updateByPrimaryKeySelective(updateModule); + + //开始移动 + request = new NodeMoveRequest(); + request.setDragNodeId(a3Node.getId()); + request.setDropNodeId(a1Node.getId()); + request.setDropPosition(1); + this.requestPostWithOk(URL_MODULE_MOVE, request); + this.checkModulePos(a1Node.getId(), a3Node.getId(), a2Node.getId(), true); + + //移动回去 + request = new NodeMoveRequest(); + request.setDragNodeId(a3Node.getId()); + request.setDropNodeId(a2Node.getId()); + request.setDropPosition(1); + this.requestPostWithOk(URL_MODULE_MOVE, request); + this.checkModulePos(a2Node.getId(), a3Node.getId(), null, false); + } + //跨节点移动-移动到首位pos小于2,是否触发计算函数(先手动更改a1-b1的pos为2,然后移动a3到a1-b1前面,最后再移动回来) + { + //更改pos + ApiDebugModule updateModule = new ApiDebugModule(); + updateModule.setId(a1b1Node.getId()); + updateModule.setPos(2); + apiDebugModuleMapper.updateByPrimaryKeySelective(updateModule); + + //开始移动 + request = new NodeMoveRequest(); + request.setDragNodeId(a3Node.getId()); + request.setDropNodeId(a1b1Node.getId()); + request.setDropPosition(-1); + this.requestPostWithOk(URL_MODULE_MOVE, request); + this.checkModulePos(a3Node.getId(), a1b1Node.getId(), null, true); + + //移动回去 + request = new NodeMoveRequest(); + request.setDragNodeId(a3Node.getId()); + request.setDropNodeId(a2Node.getId()); + request.setDropPosition(1); + this.requestPostWithOk(URL_MODULE_MOVE, request); + this.checkModulePos(a2Node.getId(), a3Node.getId(), null, false); + } + //跨节点移动-移动到中位,前后节点pos差不大于2,是否触发计算函数先手动更改a1-a1的pos为a1-b1+2,然后移动a3到a1-a1前面,最后再移动回来) + { + //更改pos + ApiDebugModule updateModule = new ApiDebugModule(); + updateModule.setId(a1b1Node.getId()); + updateModule.setPos(3 * 64); + apiDebugModuleMapper.updateByPrimaryKeySelective(updateModule); + updateModule.setId(a1a1Node.getId()); + updateModule.setPos(3 * 64 + 2); + apiDebugModuleMapper.updateByPrimaryKeySelective(updateModule); + + //开始移动 + request = new NodeMoveRequest(); + request.setDragNodeId(a3Node.getId()); + request.setDropNodeId(a1a1Node.getId()); + request.setDropPosition(-1); + this.requestPostWithOk(URL_MODULE_MOVE, request); + this.checkModulePos(a1b1Node.getId(), a3Node.getId(), a1a1Node.getId(), true); + + //移动回去 + request = new NodeMoveRequest(); + request.setDragNodeId(a3Node.getId()); + request.setDropNodeId(a2Node.getId()); + request.setDropPosition(1); + this.requestPostWithOk(URL_MODULE_MOVE, request); + this.checkModulePos(a2Node.getId(), a3Node.getId(), null, false); + } + //移动到没有子节点的节点下 a3移动到a2下 + { + //开始移动 + request = new NodeMoveRequest(); + request.setDragNodeId(a3Node.getId()); + request.setDropNodeId(a2Node.getId()); + request.setDropPosition(0); + this.requestPostWithOk(URL_MODULE_MOVE, request); + ApiDebugModule a3Module = apiDebugModuleMapper.selectByPrimaryKey(a3Node.getId()); + Assertions.assertEquals(a3Module.getParentId(), a2Node.getId()); + + //移动回去 + request = new NodeMoveRequest(); + request.setDragNodeId(a3Node.getId()); + request.setDropNodeId(a2Node.getId()); + request.setDropPosition(1); + this.requestPostWithOk(URL_MODULE_MOVE, request); + this.checkModulePos(a2Node.getId(), a3Node.getId(), null, false); + } + + checkLog(a1Node.getId(), OperationLogType.UPDATE, URL_MODULE_MOVE); + checkLog(a3Node.getId(), OperationLogType.UPDATE, URL_MODULE_MOVE); + requestPostPermissionTest(PermissionConstants.PROJECT_API_DEBUG_READ_UPDATE, URL_MODULE_MOVE, request); + } + + @Test + @Order(7) + public void moveTestError() throws Exception { + this.preliminaryData(); + BaseTreeNode a1Node = getNodeByName(preliminaryTreeNodes, "a1"); + BaseTreeNode a2Node = getNodeByName(preliminaryTreeNodes, "a2"); + //drag节点为空 + NodeMoveRequest request = new NodeMoveRequest(); + request.setDragNodeId(null); + assert a1Node != null; + request.setDropNodeId(a1Node.getId()); + request.setDropPosition(1); + this.requestPost(URL_MODULE_MOVE, request).andExpect(BAD_REQUEST_MATCHER); + //drag节点不存在 + request = new NodeMoveRequest(); + request.setDragNodeId(IDGenerator.nextStr()); + request.setDropNodeId(a1Node.getId()); + request.setDropPosition(1); + this.requestPost(URL_MODULE_MOVE, request).andExpect(ERROR_REQUEST_MATCHER); + + //drop节点为空 + request = new NodeMoveRequest(); + request.setDragNodeId(a1Node.getId()); + request.setDropNodeId(null); + request.setDropPosition(1); + this.requestPost(URL_MODULE_MOVE, request).andExpect(BAD_REQUEST_MATCHER); + + //drop节点不存在 + request = new NodeMoveRequest(); + request.setDragNodeId(a1Node.getId()); + request.setDropNodeId(IDGenerator.nextStr()); + request.setDropPosition(1); + this.requestPost(URL_MODULE_MOVE, request).andExpect(ERROR_REQUEST_MATCHER); + + //position为0的时候节点不存在 + request = new NodeMoveRequest(); + request.setDragNodeId(a1Node.getId()); + request.setDropNodeId(IDGenerator.nextStr()); + request.setDropPosition(0); + this.requestPost(URL_MODULE_MOVE, request).andExpect(ERROR_REQUEST_MATCHER); + + //dragNode和dropNode一样 + request = new NodeMoveRequest(); + request.setDragNodeId(a1Node.getId()); + request.setDropNodeId(a1Node.getId()); + request.setDropPosition(1); + this.requestPost(URL_MODULE_MOVE, request).andExpect(ERROR_REQUEST_MATCHER); + + //position不是-1 0 1 + request = new NodeMoveRequest(); + request.setDragNodeId(a1Node.getId()); + assert a2Node != null; + request.setDropNodeId(a2Node.getId()); + request.setDropPosition(4); + this.requestPost(URL_MODULE_MOVE, request).andExpect(ERROR_REQUEST_MATCHER); + } + + @Test + @Order(8) + public void TestModuleCountSuccess() throws Exception { + this.preliminaryData(); + ApiDebugRequest request = new ApiDebugRequest() {{ + this.setProtocol("HTTP"); + }}; + MvcResult moduleCountMvcResult = this.requestPostWithOkAndReturn(URL_FILE_MODULE_COUNT, request); + Map moduleCountResult = JSON.parseObject(JSON.toJSONString( + JSON.parseObject(moduleCountMvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8), ResultHolder.class).getData()), + Map.class); + Assertions.assertTrue(moduleCountResult.containsKey("all")); + requestPostPermissionTest(PermissionConstants.PROJECT_API_DEBUG_READ, URL_FILE_MODULE_COUNT, request); + + } + + @Test + @Order(8) + public void TestModuleCountError() throws Exception { + ApiDebugRequest request = new ApiDebugRequest(); + request.setProtocol(null); + this.requestPost(URL_FILE_MODULE_COUNT, request).andExpect(BAD_REQUEST_MATCHER); + + } + + @Test + @Order(10) + public void deleteModuleTestSuccess() throws Exception { + this.preliminaryData(); + + // 删除没有文件的节点a1-b1-c1 检查是否级联删除根节点 + BaseTreeNode a1b1Node = getNodeByName(this.getDebugModuleTreeNode(), "a1-b1"); + assert a1b1Node != null; + this.requestGetWithOk(String.format(URL_MODULE_DELETE, a1b1Node.getId())); + this.checkModuleIsEmpty(a1b1Node.getId()); + checkLog(a1b1Node.getId(), OperationLogType.DELETE, URL_MODULE_DELETE); + + // 删除有文件的节点 a1-a1 检查是否级联删除根节点 + BaseTreeNode a1a1Node = getNodeByName(this.getDebugModuleTreeNode(), "a1-a1"); + assert a1a1Node != null; + this.requestGetWithOk(String.format(URL_MODULE_DELETE, a1a1Node.getId())); + this.checkModuleIsEmpty(a1a1Node.getId()); + checkLog(a1a1Node.getId(), OperationLogType.DELETE, URL_MODULE_DELETE); + + //删除不存在的节点 + this.requestGetWithOk(String.format(URL_MODULE_DELETE, IDGenerator.nextNum())); + // 测试删除根节点 + this.requestGetWithOk(String.format(URL_MODULE_DELETE, ModuleConstants.DEFAULT_NODE_ID)); + + //service层判断:测试删除空集合 + apiDebugModuleService.deleteModule(new ArrayList<>()); + //校验权限 + requestGetPermissionTest(PermissionConstants.PROJECT_API_DEBUG_READ_DELETE, String.format(URL_MODULE_DELETE, IDGenerator.nextNum())); + + } + + private List getDebugModuleTreeNode() throws Exception { + MvcResult result = this.requestGetWithOkAndReturn(String.format(URL_MODULE_TREE, ModuleConstants.NODE_PROTOCOL_HTTP)); + String returnData = result.getResponse().getContentAsString(StandardCharsets.UTF_8); + ResultHolder resultHolder = JSON.parseObject(returnData, ResultHolder.class); + return JSON.parseArray(JSON.toJSONString(resultHolder.getData()), BaseTreeNode.class); + } + + private void preliminaryData() throws Exception { + if (CollectionUtils.isEmpty(preliminaryTreeNodes)) { + /* + 这里需要获取修改过的树的结构。期望的最终结构是这样的(*为测试用例中挂载文件的节点, · 为空节点): + + *默认节点 + | + ·a1 + + | | + | ·a1-b1 + + | | | + | | ·a1-b1-c1 + | | + | *a1-a1 +(创建的时候是a1,通过修改改为a1-a1) + | | | + | | ·a1-a1-c1 + | + ·a2 + | + ·a3 + */ + this.updateModuleTestSuccess(); + } + } + + private void checkModuleIsEmpty(String id) { + ApiDebugModuleExample example = new ApiDebugModuleExample(); + example.createCriteria().andParentIdEqualTo(id); + Assertions.assertEquals(0, apiDebugModuleMapper.countByExample(example)); + + ApiDebugExample apiDebugExample = new ApiDebugExample(); + example = new ApiDebugModuleExample(); + example.createCriteria().andIdEqualTo(id); + Assertions.assertEquals(0, apiDebugModuleMapper.countByExample(example)); + apiDebugExample.createCriteria().andModuleIdEqualTo(id); + Assertions.assertEquals(0, apiDebugMapper.countByExample(apiDebugExample)); + } + + private void checkModulePos(String firstNode, String secondNode, String thirdNode, boolean isRecalculate) { + ApiDebugModule firstModule = apiDebugModuleMapper.selectByPrimaryKey(firstNode); + ApiDebugModule secondModule = apiDebugModuleMapper.selectByPrimaryKey(secondNode); + ApiDebugModule thirdModule = null; + Assertions.assertTrue(firstModule.getPos() < secondModule.getPos()); + if (StringUtils.isNotBlank(thirdNode)) { + thirdModule = apiDebugModuleMapper.selectByPrimaryKey(thirdNode); + Assertions.assertTrue(secondModule.getPos() < thirdModule.getPos()); + } + if (isRecalculate) { + int limitPos = 64; + Assertions.assertEquals(0, firstModule.getPos() % limitPos); + Assertions.assertEquals(0, secondModule.getPos() % limitPos); + if (thirdModule != null) { + Assertions.assertEquals(0, thirdModule.getPos() % limitPos); + } + } + } + + +} diff --git a/backend/services/api-test/src/test/resources/application.properties b/backend/services/api-test/src/test/resources/application.properties index 02d4a62ba4..dd57bbbe17 100644 --- a/backend/services/api-test/src/test/resources/application.properties +++ b/backend/services/api-test/src/test/resources/application.properties @@ -26,6 +26,9 @@ spring.datasource.hikari.max-lifetime=1800000 spring.datasource.hikari.connection-timeout=30000 spring.datasource.hikari.connection-test-query=SELECT 1 +# 单元测试初始化权限 sql +spring.sql.init.mode=always +spring.sql.init.schema-locations=classpath*:dml/init_permission_test.sql # # spring.kafka spring.kafka.bootstrap-servers=${embedded.kafka.brokerList} @@ -82,6 +85,6 @@ minio.endpoint=http://${embedded.minio.host}:${embedded.minio.port} minio.access-key=${embedded.minio.accessKey} minio.secret-key=${embedded.minio.secretKey} -logging.level.org.springframework.jdbc.core=info -logging.level.io.metersphere.sdk.mapper=info -logging.level.io.metersphere.project.mapper=info \ No newline at end of file +logging.level.org.springframework.jdbc.core=debug +logging.level.io.metersphere.sdk.mapper=debug +logging.level.io.metersphere.project.mapper=debug \ No newline at end of file diff --git a/backend/services/api-test/src/test/resources/sql/init_api_definition.sql b/backend/services/api-test/src/test/resources/dml/init_api_definition.sql similarity index 100% rename from backend/services/api-test/src/test/resources/sql/init_api_definition.sql rename to backend/services/api-test/src/test/resources/dml/init_api_definition.sql diff --git a/backend/services/api-test/src/test/resources/dml/init_permission_test.sql b/backend/services/api-test/src/test/resources/dml/init_permission_test.sql new file mode 100644 index 0000000000..ec90568c9c --- /dev/null +++ b/backend/services/api-test/src/test/resources/dml/init_permission_test.sql @@ -0,0 +1,14 @@ +-- 初始化用于权限测试的组织用户 +INSERT INTO user(id, name, email, password, create_time, update_time, language, last_organization_id, phone, source, + last_project_id, create_user, update_user, deleted) +VALUES ('PROJECT', 'PROJECT', 'PROJECT@fit2cloud.com', MD5('metersphere'), + UNIX_TIMESTAMP() * 1000, + UNIX_TIMESTAMP() * 1000, NULL, NUll, '', 'LOCAL', NULL, 'admin', 'admin', false); + +-- 初始化一个用于权限测试的用户组,这里默认使用 PROJECT 作为ID,如果是组织和项目级别类似,便于根据权限的前缀找到对应测试的用户组 +INSERT INTO user_role (id, name, description, internal, type, create_time, update_time, create_user, scope_id) +VALUES ('PROJECT', '项目级别权限校验', '', 1, 'PROJECT', 1620674220005, 1620674220000, 'admin', 'global'); + +-- 初始化用户和组的关系 +INSERT INTO user_role_relation (id, user_id, role_id, source_id, organization_id, create_time, create_user) +SELECT 'PROJECT', 'PROJECT', 'PROJECT', id, organization_id, 1684747668375, 'admin' FROM project WHERE num = 100001; diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/dto/sdk/BaseTreeNode.java b/backend/services/system-setting/src/main/java/io/metersphere/system/dto/sdk/BaseTreeNode.java index f6e25ec4c8..2f12a93f71 100644 --- a/backend/services/system-setting/src/main/java/io/metersphere/system/dto/sdk/BaseTreeNode.java +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/dto/sdk/BaseTreeNode.java @@ -5,7 +5,9 @@ import lombok.Data; import lombok.NoArgsConstructor; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; @Data @NoArgsConstructor @@ -26,6 +28,9 @@ public class BaseTreeNode { @Schema(description = "子节点") private List children = new ArrayList<>(); + @Schema(description = "附加信息") + private Map attachInfo = new HashMap<>(); + @Schema(description = "节点资源数量(多数情况下不会随着节点信息返回,视接口而定)") private long count = 0; @@ -45,4 +50,8 @@ public class BaseTreeNode { public void addChild(BaseTreeNode node) { children.add(node); } + + public void putAttachInfo(String key, String value) { + attachInfo.put(key, value); + } } diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/log/constants/OperationLogModule.java b/backend/services/system-setting/src/main/java/io/metersphere/system/log/constants/OperationLogModule.java index 2d5fe82230..e02eb4c30e 100644 --- a/backend/services/system-setting/src/main/java/io/metersphere/system/log/constants/OperationLogModule.java +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/log/constants/OperationLogModule.java @@ -97,4 +97,7 @@ public class OperationLogModule { //用例 public static final String FUNCTIONAL_CASE = "FUNCTIONAL_CASE"; + + //接口调试 + public static final String API_DEBUG = "API_DEBUG"; }