diff --git a/backend/src/main/java/io/metersphere/api/controller/ApiDefinitionController.java b/backend/src/main/java/io/metersphere/api/controller/ApiDefinitionController.java index 7d15fb739c..15860e80ec 100644 --- a/backend/src/main/java/io/metersphere/api/controller/ApiDefinitionController.java +++ b/backend/src/main/java/io/metersphere/api/controller/ApiDefinitionController.java @@ -14,10 +14,7 @@ import io.metersphere.api.service.ApiDefinitionService; import io.metersphere.api.service.ApiTestEnvironmentService; import io.metersphere.api.service.EsbApiParamService; import io.metersphere.api.service.EsbImportService; -import io.metersphere.base.domain.ApiDefinition; -import io.metersphere.base.domain.ApiDefinitionWithBLOBs; -import io.metersphere.base.domain.ApiTestEnvironmentWithBLOBs; -import io.metersphere.base.domain.Schedule; +import io.metersphere.base.domain.*; import io.metersphere.commons.constants.NoticeConstants; import io.metersphere.commons.constants.OperLogConstants; import io.metersphere.commons.constants.PermissionConstants; @@ -26,6 +23,7 @@ import io.metersphere.commons.utils.PageUtils; import io.metersphere.commons.utils.Pager; import io.metersphere.controller.request.ResetOrderRequest; import io.metersphere.controller.request.ScheduleRequest; +import io.metersphere.dto.RelationshipEdgeDTO; import io.metersphere.log.annotation.MsAuditLog; import io.metersphere.notice.annotation.SendNotice; import io.metersphere.service.CheckPermissionService; @@ -305,4 +303,15 @@ public class ApiDefinitionController { public void orderCase(@RequestBody ResetOrderRequest request) { apiDefinitionService.updateOrder(request); } + + @GetMapping("/relationship/{id}/{relationshipType}") + public List getRelationshipApi(@PathVariable("id") String id, @PathVariable("relationshipType") String relationshipType) { + return apiDefinitionService.getRelationshipApi(id, relationshipType); + } + + @PostMapping("/relationship/relate/{goPage}/{pageSize}") + public Pager< List> getRelationshipRelateList(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody ApiDefinitionRequest request) { + Page page = PageHelper.startPage(goPage, pageSize, true); + return PageUtils.setPageInfo(page, apiDefinitionService.getRelationshipRelateList(request)); + } } diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/SaveApiDefinitionRequest.java b/backend/src/main/java/io/metersphere/api/dto/definition/SaveApiDefinitionRequest.java index 7fc27ede0c..c8e9161603 100644 --- a/backend/src/main/java/io/metersphere/api/dto/definition/SaveApiDefinitionRequest.java +++ b/backend/src/main/java/io/metersphere/api/dto/definition/SaveApiDefinitionRequest.java @@ -33,7 +33,7 @@ public class SaveApiDefinitionRequest { private String modulePath; private String method; - + private MsTestElement request; private Response response; @@ -44,6 +44,8 @@ public class SaveApiDefinitionRequest { private String followPeople; + private String remark; + private Schedule schedule; private String triggerMode; diff --git a/backend/src/main/java/io/metersphere/api/service/ApiAutomationRelationshipEdgeService.java b/backend/src/main/java/io/metersphere/api/service/ApiAutomationRelationshipEdgeService.java index 27157644c3..3a1e5d057b 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiAutomationRelationshipEdgeService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiAutomationRelationshipEdgeService.java @@ -4,5 +4,5 @@ import io.metersphere.base.domain.ApiScenarioWithBLOBs; public interface ApiAutomationRelationshipEdgeService { // 初始化引用关系 - public void initRelationshipEdge(ApiScenarioWithBLOBs before, ApiScenarioWithBLOBs now); + void initRelationshipEdge(ApiScenarioWithBLOBs before, ApiScenarioWithBLOBs now); } diff --git a/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java b/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java index 647b529cbd..aa9943e5c0 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java @@ -172,7 +172,7 @@ public class ApiAutomationService { public List listAll(ApiScenarioBatchRequest request) { ServiceUtils.getSelectAllIds(request, request.getCondition(), - (query) -> extApiScenarioMapper.selectIdsByQuery((ApiScenarioRequest) query)); + (query) -> extApiScenarioMapper.selectIdsByQuery(query)); List list = extApiScenarioMapper.selectIds(request.getIds()); return list; } @@ -183,7 +183,7 @@ public class ApiAutomationService { public List idAll(ApiScenarioBatchRequest request) { ServiceUtils.getSelectAllIds(request, request.getCondition(), - (query) -> extApiScenarioMapper.selectIdsByQuery((ApiScenarioRequest) query)); + (query) -> extApiScenarioMapper.selectIdsByQuery(query)); return request.getIds(); } @@ -1004,7 +1004,7 @@ public class ApiAutomationService { */ public String modeRun(RunScenarioRequest request) { ServiceUtils.getSelectAllIds(request, request.getCondition(), - (query) -> extApiScenarioMapper.selectIdsByQuery((ApiScenarioRequest) query)); + (query) -> extApiScenarioMapper.selectIdsByQuery(query)); List ids = request.getIds(); // 生成集成报告 @@ -1454,7 +1454,7 @@ public class ApiAutomationService { */ public String execute(RunScenarioRequest request) { ServiceUtils.getSelectAllIds(request, request.getCondition(), - (query) -> extApiScenarioMapper.selectIdsByQuery((ApiScenarioRequest) query)); + (query) -> extApiScenarioMapper.selectIdsByQuery(query)); List ids = request.getIds(); //检查是否有正在执行中的情景 // this.checkScenarioIsRunning(ids); @@ -1770,7 +1770,7 @@ public class ApiAutomationService { public void bathEdit(ApiScenarioBatchRequest request) { ServiceUtils.getSelectAllIds(request, request.getCondition(), - (query) -> extApiScenarioMapper.selectIdsByQuery((ApiScenarioRequest) query)); + (query) -> extApiScenarioMapper.selectIdsByQuery(query)); if (StringUtils.isNotBlank(request.getEnvironmentId())) { bathEditEnv(request); @@ -2027,7 +2027,7 @@ public class ApiAutomationService { private List getExportResult(ApiScenarioBatchRequest request) { ServiceUtils.getSelectAllIds(request, request.getCondition(), - (query) -> extApiScenarioMapper.selectIdsByQuery((ApiScenarioRequest) query)); + (query) -> extApiScenarioMapper.selectIdsByQuery(query)); ApiScenarioExample example = new ApiScenarioExample(); example.createCriteria().andIdIn(request.getIds()); List apiScenarioWithBLOBs = apiScenarioMapper.selectByExampleWithBLOBs(example); @@ -2145,14 +2145,14 @@ public class ApiAutomationService { public void removeToGcByBatch(ApiScenarioBatchRequest request) { ServiceUtils.getSelectAllIds(request, request.getCondition(), - (query) -> extApiScenarioMapper.selectIdsByQuery((ApiScenarioRequest) query)); + (query) -> extApiScenarioMapper.selectIdsByQuery(query)); this.removeToGc(request.getIds()); } public void deleteBatchByCondition(ApiScenarioBatchRequest request) { ServiceUtils.getSelectAllIds(request, request.getCondition(), - (query) -> extApiScenarioMapper.selectIdsByQuery((ApiScenarioRequest) query)); + (query) -> extApiScenarioMapper.selectIdsByQuery(query)); this.deleteBatch(request.getIds()); } @@ -2367,7 +2367,7 @@ public class ApiAutomationService { request.setIds(new ArrayList<>(0)); } ServiceUtils.getSelectAllIds(request, request.getCondition(), - (query) -> extApiScenarioMapper.selectIdsByQuery((ApiScenarioRequest) query)); + (query) -> extApiScenarioMapper.selectIdsByQuery(query)); if (!request.getIds().isEmpty()) { ApiScenarioExample example = new ApiScenarioExample(); @@ -2400,7 +2400,7 @@ public class ApiAutomationService { public List listWithIds(ApiScenarioBatchRequest request) { ServiceUtils.getSelectAllIds(request, request.getCondition(), - (query) -> extApiScenarioMapper.selectIdsByQuery((ApiScenarioRequest) query)); + (query) -> extApiScenarioMapper.selectIdsByQuery(query)); List list = extApiScenarioMapper.listWithIds(request.getIds()); return list; } @@ -2470,7 +2470,7 @@ public class ApiAutomationService { public List batchGenPerformanceTestJmx(ApiScenarioBatchRequest request) { ServiceUtils.getSelectAllIds(request, request.getCondition(), - (query) -> extApiScenarioMapper.selectIdsByQuery((ApiScenarioRequest) query)); + (query) -> extApiScenarioMapper.selectIdsByQuery(query)); List returnList = new ArrayList<>(); List ids = request.getIds(); @@ -2495,7 +2495,7 @@ public class ApiAutomationService { public BatchOperaResponse batchCopy(ApiScenarioBatchRequest batchRequest) { ServiceUtils.getSelectAllIds(batchRequest, batchRequest.getCondition(), - (query) -> extApiScenarioMapper.selectIdsByQuery((ApiScenarioRequest) query)); + (query) -> extApiScenarioMapper.selectIdsByQuery(query)); List apiScenarioList = extApiScenarioMapper.selectIds(batchRequest.getIds()); StringBuffer stringBuffer = new StringBuffer(); for (ApiScenarioWithBLOBs apiModel : apiScenarioList) { @@ -2555,7 +2555,7 @@ public class ApiAutomationService { public DeleteCheckResult checkBeforeDelete(ApiScenarioBatchRequest request) { ServiceUtils.getSelectAllIds(request, request.getCondition(), - (query) -> extApiScenarioMapper.selectIdsByQuery((ApiScenarioRequest) query)); + (query) -> extApiScenarioMapper.selectIdsByQuery(query)); List deleteIds = request.getIds(); DeleteCheckResult result = new DeleteCheckResult(); List checkMsgList = new ArrayList<>(); diff --git a/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java b/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java index 304688daee..09f64d90b6 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java @@ -33,6 +33,7 @@ import io.metersphere.commons.utils.*; import io.metersphere.controller.request.ResetOrderRequest; import io.metersphere.controller.request.ScheduleRequest; import io.metersphere.dto.BaseSystemConfigDTO; +import io.metersphere.dto.RelationshipEdgeDTO; import io.metersphere.i18n.Translator; import io.metersphere.job.sechedule.SwaggerUrlImportJob; import io.metersphere.log.utils.ReflexObjectUtil; @@ -43,6 +44,7 @@ import io.metersphere.log.vo.api.DefinitionReference; import io.metersphere.notice.sender.NoticeModel; import io.metersphere.notice.service.NoticeSendService; import io.metersphere.service.FileService; +import io.metersphere.service.RelationshipEdgeService; import io.metersphere.service.ScheduleService; import io.metersphere.service.SystemParameterService; import io.metersphere.track.request.testcase.ApiCaseRelevanceRequest; @@ -115,6 +117,8 @@ public class ApiDefinitionService { private NoticeSendService noticeSendService; @Resource private ExtApiTestCaseMapper extApiTestCaseMapper; + @Resource + private RelationshipEdgeService relationshipEdgeService; private static Cache cache = Cache.newHardMemoryCache(0, 3600); @@ -444,6 +448,7 @@ public class ApiDefinitionService { test.setResponse(JSONObject.toJSONString(request.getResponse())); test.setEnvironmentId(request.getEnvironmentId()); test.setUserId(request.getUserId()); + test.setRemark(request.getRemark()); test.setFollowPeople(request.getFollowPeople()); if (StringUtils.isNotEmpty(request.getTags()) && !StringUtils.equals(request.getTags(), "[]")) { test.setTags(request.getTags()); @@ -482,6 +487,7 @@ public class ApiDefinitionService { test.setModulePath(request.getModulePath()); test.setModuleId(request.getModuleId()); test.setFollowPeople(request.getFollowPeople()); + test.setRemark(request.getRemark()); test.setOrder(ServiceUtils.getNextOrder(request.getProjectId(), extApiDefinitionMapper::getLastOrder)); if (StringUtils.isEmpty(request.getModuleId()) || "default-module".equals(request.getModuleId())) { ApiModuleExample example = new ApiModuleExample(); @@ -1560,4 +1566,41 @@ public class ApiDefinitionService { public long countQuotedApiByProjectId(String projectId) { return extApiDefinitionMapper.countQuotedApiByProjectId(projectId); } + + public List getRelationshipApi(String id, String relationshipType) { + List relationshipEdges= relationshipEdgeService.getRelationshipEdgeByType(id, relationshipType); + List ids = relationshipEdgeService.getRelationIdsByType(relationshipType, relationshipEdges); + + if (CollectionUtils.isNotEmpty(ids)) { + ApiDefinitionExample example = new ApiDefinitionExample(); + example.createCriteria().andIdIn(ids); + List apiDefinitions = apiDefinitionMapper.selectByExample(example); + Map apiMap = apiDefinitions.stream().collect(Collectors.toMap(ApiDefinition::getId, i -> i)); + List results = new ArrayList<>(); + for (RelationshipEdge relationshipEdge : relationshipEdges) { + RelationshipEdgeDTO relationshipEdgeDTO = new RelationshipEdgeDTO(); + BeanUtils.copyBean(relationshipEdgeDTO, relationshipEdge); + ApiDefinition apiDefinition; + if (StringUtils.equals(relationshipType, "PRE")) { + apiDefinition = apiMap.get(relationshipEdge.getTargetId()); + } else { + apiDefinition = apiMap.get(relationshipEdge.getSourceId()); + } + relationshipEdgeDTO.setTargetName(apiDefinition.getName()); + relationshipEdgeDTO.setCreator(apiDefinition.getCreateUser()); + relationshipEdgeDTO.setTargetNum(apiDefinition.getNum()); + results.add(relationshipEdgeDTO); + } + return results; + } + return new ArrayList<>(); + } + + public List getRelationshipRelateList(ApiDefinitionRequest request) { + request = this.initRequest(request, true, true); + List relationshipIds = relationshipEdgeService.getRelationshipIds(request.getId()); + request.setNotInIds(relationshipIds); + request.setId(null); // 去掉id的查询条件 + return extApiDefinitionMapper.list(request); + } } diff --git a/backend/src/main/java/io/metersphere/base/domain/ApiDefinitionWithBLOBs.java b/backend/src/main/java/io/metersphere/base/domain/ApiDefinitionWithBLOBs.java index 2cf8975d43..38aa023e8b 100644 --- a/backend/src/main/java/io/metersphere/base/domain/ApiDefinitionWithBLOBs.java +++ b/backend/src/main/java/io/metersphere/base/domain/ApiDefinitionWithBLOBs.java @@ -15,5 +15,7 @@ public class ApiDefinitionWithBLOBs extends ApiDefinition implements Serializabl private String response; + private String remark; + private static final long serialVersionUID = 1L; -} \ No newline at end of file +} diff --git a/backend/src/main/java/io/metersphere/base/mapper/ApiDefinitionMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ApiDefinitionMapper.xml index 217daf76c4..0d260750f3 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ApiDefinitionMapper.xml +++ b/backend/src/main/java/io/metersphere/base/mapper/ApiDefinitionMapper.xml @@ -32,6 +32,7 @@ + @@ -92,13 +93,13 @@ - id, project_id, `name`, `method`, protocol, `path`, module_path, environment_id, - schedule, `status`, module_id, user_id, create_time, update_time, num, tags, original_state, - create_user, case_total, case_status, case_passing_rate, delete_time, delete_user_id, + id, project_id, `name`, `method`, protocol, `path`, module_path, environment_id, + schedule, `status`, module_id, user_id, create_time, update_time, num, tags, original_state, + create_user, case_total, case_status, case_passing_rate, delete_time, delete_user_id, follow_people, `order` - description, request, response + description, request, response, remark @@ -438,6 +445,9 @@ response = #{record.response,jdbcType=LONGVARCHAR}, + + remark = #{record.remark,jdbcType=LONGVARCHAR}, + @@ -472,7 +482,8 @@ `order` = #{record.order,jdbcType=BIGINT}, description = #{record.description,jdbcType=LONGVARCHAR}, request = #{record.request,jdbcType=LONGVARCHAR}, - response = #{record.response,jdbcType=LONGVARCHAR} + response = #{record.response,jdbcType=LONGVARCHAR}, + remark = #{record.remark,jdbcType=LONGVARCHAR} @@ -592,6 +603,9 @@ response = #{response,jdbcType=LONGVARCHAR}, + + remark = #{remark,jdbcType=LONGVARCHAR}, + where id = #{id,jdbcType=VARCHAR} @@ -623,7 +637,8 @@ `order` = #{order,jdbcType=BIGINT}, description = #{description,jdbcType=LONGVARCHAR}, request = #{request,jdbcType=LONGVARCHAR}, - response = #{response,jdbcType=LONGVARCHAR} + response = #{response,jdbcType=LONGVARCHAR}, + remark = #{remark,jdbcType=LONGVARCHAR} where id = #{id,jdbcType=VARCHAR} @@ -654,4 +669,4 @@ `order` = #{order,jdbcType=BIGINT} where id = #{id,jdbcType=VARCHAR} - \ No newline at end of file + diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiDefinitionMapper.java b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiDefinitionMapper.java index 38548bcaf3..1f36bd3e97 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiDefinitionMapper.java +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiDefinitionMapper.java @@ -6,13 +6,14 @@ import io.metersphere.api.dto.definition.ApiDefinitionRequest; import io.metersphere.api.dto.definition.ApiDefinitionResult; import io.metersphere.api.dto.definition.ApiSwaggerUrlDTO; import io.metersphere.base.domain.ApiDefinition; -import io.metersphere.base.domain.ApiDefinitionExample; import io.metersphere.base.domain.ApiDefinitionExampleWithOperation; import io.metersphere.controller.request.BaseQueryRequest; +import io.metersphere.dto.RelationshipGraphData; import org.apache.ibatis.annotations.Param; import java.util.List; import java.util.Map; +import java.util.Set; public interface ExtApiDefinitionMapper { List selectScheduleList(@Param("projectId") String projectId); @@ -68,4 +69,6 @@ public interface ExtApiDefinitionMapper { Long getLastOrder(@Param("projectId")String projectId, @Param("baseOrder") Long baseOrder); long countQuotedApiByProjectId(String projectId); + + List getForGraph(@Param("ids") Set ids); } diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiDefinitionMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiDefinitionMapper.xml index f0fa65bfe7..456a937577 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiDefinitionMapper.xml +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiDefinitionMapper.xml @@ -238,7 +238,7 @@ api_definition.name,api_definition.protocol,api_definition.path,api_definition.module_id,api_definition.module_path,api_definition.method, api_definition.description,api_definition.request,api_definition.response,api_definition.environment_id, api_definition.status, api_definition.user_id, api_definition.create_time, api_definition.update_time, project.name as - project_name, user.name as user_name,deleteUser.name AS delete_user,api_definition.delete_time + project_name, user.name as user_name,deleteUser.name AS delete_user,api_definition.delete_time, api_definition.remark from api_definition left join project on api_definition.project_id = project.id left join user on api_definition.user_id = user.id @@ -602,6 +602,12 @@ AND api_definition.module_id = #{request.moduleId} + + and api_definition.id not in + + #{id} + + AND api_definition.module_id in @@ -667,5 +673,14 @@ ) ) + diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiScenarioMapper.java b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiScenarioMapper.java index c5710d43b2..275380ea9e 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiScenarioMapper.java +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiScenarioMapper.java @@ -6,6 +6,7 @@ import io.metersphere.api.dto.datacount.ApiDataCountResult; import io.metersphere.base.domain.ApiScenario; import io.metersphere.base.domain.ApiScenarioExampleWithOperation; import io.metersphere.base.domain.ApiScenarioWithBLOBs; +import io.metersphere.controller.request.BaseQueryRequest; import io.metersphere.dto.RelationshipGraphData; import org.apache.ibatis.annotations.Param; @@ -52,7 +53,7 @@ public interface ExtApiScenarioMapper { ApiScenario getNextNum(@Param("projectId") String projectId); - List selectIdsByQuery(@Param("request") ApiScenarioRequest request); + List selectIdsByQuery(@Param("request") BaseQueryRequest request); void updateCustomNumByProjectId(@Param("projectId") String projectId); diff --git a/backend/src/main/java/io/metersphere/controller/request/BaseQueryRequest.java b/backend/src/main/java/io/metersphere/controller/request/BaseQueryRequest.java index 848698c806..c1ad76012e 100644 --- a/backend/src/main/java/io/metersphere/controller/request/BaseQueryRequest.java +++ b/backend/src/main/java/io/metersphere/controller/request/BaseQueryRequest.java @@ -24,6 +24,11 @@ public class BaseQueryRequest { private List nodeIds; + /** + * 排除哪些id + */ + private List notInIds; + /** * selectAll:选择的数据是否是全部数据(全部数据是不受分页影响的数据) * filters: 数据状态 diff --git a/backend/src/main/java/io/metersphere/service/RelationshipEdgeService.java b/backend/src/main/java/io/metersphere/service/RelationshipEdgeService.java index 852cf7fc9a..045f4dfa8a 100644 --- a/backend/src/main/java/io/metersphere/service/RelationshipEdgeService.java +++ b/backend/src/main/java/io/metersphere/service/RelationshipEdgeService.java @@ -7,6 +7,7 @@ import io.metersphere.base.mapper.RelationshipEdgeMapper; import io.metersphere.commons.utils.SessionUtils; import io.metersphere.controller.request.RelationshipEdgeRequest; import org.apache.commons.collections.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; @@ -17,6 +18,7 @@ import javax.annotation.Resource; import java.util.ArrayList; import java.util.List; import java.util.UUID; +import java.util.stream.Collectors; /** * @author jianxingChen @@ -57,6 +59,28 @@ public class RelationshipEdgeService { relationshipEdgeMapper.deleteByExample(example); } + public List getRelationshipEdgeByType(String id, String relationshipType) { + if (StringUtils.equals(relationshipType, "PRE")) { + return getBySourceId(id); + } else if (StringUtils.equals(relationshipType, "POST")) { + return getByTargetId(id); + } + return new ArrayList<>(); + } + + public List getRelationIdsByType(String relationshipType, List relationshipEdges) { + if (StringUtils.equals(relationshipType, "PRE")) { + return relationshipEdges.stream() + .map(RelationshipEdge::getTargetId) + .collect(Collectors.toList()); + } else if (StringUtils.equals(relationshipType, "POST")) { + return relationshipEdges.stream() + .map(RelationshipEdge::getSourceId) + .collect(Collectors.toList()); + } + return new ArrayList<>(); + } + public List getBySourceId(String sourceId) { RelationshipEdgeExample example = new RelationshipEdgeExample(); example.createCriteria() @@ -140,4 +164,18 @@ public class RelationshipEdgeService { return graphId; } + /** + * 给定一个节点获取跟他关联的所有节点的id + * @param nodeId + * @return + */ + public List getRelationshipIds(String nodeId) { + List sourceRelationshipEdges = getBySourceId(nodeId); + List targetRelationshipEdges = getByTargetId(nodeId); + List ids = sourceRelationshipEdges.stream().map(RelationshipEdge::getTargetId).collect(Collectors.toList()); + ids.addAll(targetRelationshipEdges.stream().map(RelationshipEdge::getSourceId).collect(Collectors.toList())); + ids.add(nodeId); + return ids; + } + } diff --git a/backend/src/main/java/io/metersphere/track/service/TestCaseService.java b/backend/src/main/java/io/metersphere/track/service/TestCaseService.java index 4ca7260c3c..150a56ea10 100644 --- a/backend/src/main/java/io/metersphere/track/service/TestCaseService.java +++ b/backend/src/main/java/io/metersphere/track/service/TestCaseService.java @@ -1959,29 +1959,15 @@ public class TestCaseService { public List getRelationshipRelateList(QueryTestCaseRequest request) { setDefaultOrder(request); - List sourceRelationshipEdges = relationshipEdgeService.getBySourceId(request.getId()); - List targetRelationshipEdges = relationshipEdgeService.getByTargetId(request.getId()); - List ids = sourceRelationshipEdges.stream().map(RelationshipEdge::getTargetId).collect(Collectors.toList()); - ids.addAll(targetRelationshipEdges.stream().map(RelationshipEdge::getTargetId).collect(Collectors.toList())); - ids.add(request.getId()); - request.setTestCaseContainIds(ids); + List relationshipIds = relationshipEdgeService.getRelationshipIds(request.getId()); + request.setTestCaseContainIds(relationshipIds); return extTestCaseMapper.getTestCase(request); } public List getRelationshipCase(String id, String relationshipType) { - List relationshipEdges; - List ids; - if (StringUtils.equals(relationshipType, "PRE")) { - relationshipEdges = relationshipEdgeService.getBySourceId(id); - ids = relationshipEdges.stream() - .map(RelationshipEdge::getTargetId) - .collect(Collectors.toList()); - } else { - relationshipEdges = relationshipEdgeService.getByTargetId(id); - ids = relationshipEdges.stream() - .map(RelationshipEdge::getSourceId) - .collect(Collectors.toList()); - } + + List relationshipEdges= relationshipEdgeService.getRelationshipEdgeByType(id, relationshipType); + List ids = relationshipEdgeService.getRelationIdsByType(relationshipType, relationshipEdges); if (CollectionUtils.isNotEmpty(ids)) { TestCaseExample example = new TestCaseExample(); diff --git a/backend/src/main/java/io/metersphere/xpack b/backend/src/main/java/io/metersphere/xpack index 77d0367751..7fa58311f2 160000 --- a/backend/src/main/java/io/metersphere/xpack +++ b/backend/src/main/java/io/metersphere/xpack @@ -1 +1 @@ -Subproject commit 77d0367751b0a996a32f1c4b6b18bee9becfc4e3 +Subproject commit 7fa58311f24417999905d463f17dc142963c3524 diff --git a/backend/src/main/resources/db/migration/V97__v1.14_release.sql b/backend/src/main/resources/db/migration/V97__v1.14_release.sql index 4a20c82c11..cc7c41ebff 100644 --- a/backend/src/main/resources/db/migration/V97__v1.14_release.sql +++ b/backend/src/main/resources/db/migration/V97__v1.14_release.sql @@ -51,6 +51,8 @@ create table if not exists relationship_edge ( DEFAULT CHARSET = utf8mb4 COLLATE utf8mb4_general_ci; +ALTER TABLE api_definition ADD remark TEXT NULL; + ALTER TABLE test_case_review ADD COLUMN follow_people; ALTER TABLE test_plan ADD COLUMN follow_people; diff --git a/frontend/src/business/components/api/automation/scenario/ApiScenarioList.vue b/frontend/src/business/components/api/automation/scenario/ApiScenarioList.vue index 336a480a37..93d9120f17 100644 --- a/frontend/src/business/components/api/automation/scenario/ApiScenarioList.vue +++ b/frontend/src/business/components/api/automation/scenario/ApiScenarioList.vue @@ -446,7 +446,8 @@ export default { { name: "生成依赖关系", handleClick: this.generateGraph, - permissions: ['PROJECT_API_SCENARIO:READ+MOVE_BATCH'] + isXPack: true, + permissions: ['PROJECT_API_SCENARIO:READ+EDIT'] }, { name: this.$t('api_test.automation.batch_add_plan'), @@ -492,9 +493,14 @@ export default { }; }, created() { - if (!hasLicense()) { - this.unTrashButtons.splice(5,1); - } + // if (!hasLicense()) { + // for (let i = 0; i < this.unTrashButtons.length; i++) { + // if (this.unTrashButtons[i].handleClick === this.generateGraph) { + // this.unTrashButtons.splice(i,1); + // break; + // } + // } + // } scenario.$on('hide', id => { this.hideStopBtn(id); }); diff --git a/frontend/src/business/components/api/automation/scenario/api/RelevanceApiList.vue b/frontend/src/business/components/api/automation/scenario/api/RelevanceApiList.vue index b42d9f908f..d97119f4ea 100644 --- a/frontend/src/business/components/api/automation/scenario/api/RelevanceApiList.vue +++ b/frontend/src/business/components/api/automation/scenario/api/RelevanceApiList.vue @@ -4,100 +4,28 @@ :is-api-list-enable="isApiListEnable" @isApiListEnableChange="isApiListEnableChange"> - + - - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -105,88 +33,35 @@ diff --git a/frontend/src/business/components/api/definition/ApiDefinition.vue b/frontend/src/business/components/api/definition/ApiDefinition.vue index 7b6e838da8..aa8d749f64 100644 --- a/frontend/src/business/components/api/definition/ApiDefinition.vue +++ b/frontend/src/business/components/api/definition/ApiDefinition.vue @@ -494,6 +494,7 @@ export default { url: "", protocol: this.currentProtocol, environmentId: "", + remark: "", moduleId: 'default-module', modulePath: "/" + this.$t("commons.module_title") }; diff --git a/frontend/src/business/components/api/definition/api-definition.js b/frontend/src/business/components/api/definition/api-definition.js new file mode 100644 index 0000000000..0434de22ac --- /dev/null +++ b/frontend/src/business/components/api/definition/api-definition.js @@ -0,0 +1,42 @@ +export function getProtocolFilter(protocolType) { + if (protocolType === "HTTP") { + return [ + {text: 'GET', value: 'GET'}, + {text: 'POST', value: 'POST'}, + {text: 'PUT', value: 'PUT'}, + {text: 'PATCH', value: 'PATCH'}, + {text: 'DELETE', value: 'DELETE'}, + {text: 'OPTIONS', value: 'OPTIONS'}, + {text: 'HEAD', value: 'HEAD'}, + {text: 'CONNECT', value: 'CONNECT'}, + ]; + } else if (protocolType === "TCP") { + return [ + {text: 'TCP', value: 'TCP'}, + ]; + } else if (protocolType === "SQL") { + return [ + {text: 'SQL', value: 'SQL'}, + ]; + } else if (protocolType === "DUBBO") { + return [ + {text: 'DUBBO', value: 'DUBBO'}, + {text: 'dubbo://', value: 'dubbo://'}, + ]; + } else { + return [ + {text: 'GET', value: 'GET'}, + {text: 'POST', value: 'POST'}, + {text: 'PUT', value: 'PUT'}, + {text: 'PATCH', value: 'PATCH'}, + {text: 'DELETE', value: 'DELETE'}, + {text: 'OPTIONS', value: 'OPTIONS'}, + {text: 'HEAD', value: 'HEAD'}, + {text: 'CONNECT', value: 'CONNECT'}, + {text: 'DUBBO', value: 'DUBBO'}, + {text: 'dubbo://', value: 'dubbo://'}, + {text: 'SQL', value: 'SQL'}, + {text: 'TCP', value: 'TCP'}, + ]; + } +} diff --git a/frontend/src/business/components/api/definition/components/complete/ApiInfoContainer.vue b/frontend/src/business/components/api/definition/components/complete/ApiInfoContainer.vue new file mode 100644 index 0000000000..2d6791e4f2 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/complete/ApiInfoContainer.vue @@ -0,0 +1,46 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/complete/ApiOtherInfo.vue b/frontend/src/business/components/api/definition/components/complete/ApiOtherInfo.vue new file mode 100644 index 0000000000..f01767e06d --- /dev/null +++ b/frontend/src/business/components/api/definition/components/complete/ApiOtherInfo.vue @@ -0,0 +1,51 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/complete/ApiRelationshipList.vue b/frontend/src/business/components/api/definition/components/complete/ApiRelationshipList.vue new file mode 100644 index 0000000000..5434325916 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/complete/ApiRelationshipList.vue @@ -0,0 +1,90 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/complete/ApiRelationshipRelevance.vue b/frontend/src/business/components/api/definition/components/complete/ApiRelationshipRelevance.vue new file mode 100644 index 0000000000..c940f901d9 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/complete/ApiRelationshipRelevance.vue @@ -0,0 +1,142 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/complete/ApiTableList.vue b/frontend/src/business/components/api/definition/components/complete/ApiTableList.vue new file mode 100644 index 0000000000..46907b7a52 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/complete/ApiTableList.vue @@ -0,0 +1,243 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/complete/EditCompleteHTTPApi.vue b/frontend/src/business/components/api/definition/components/complete/EditCompleteHTTPApi.vue index 9c34a3ebdd..40082624a6 100644 --- a/frontend/src/business/components/api/definition/components/complete/EditCompleteHTTPApi.vue +++ b/frontend/src/business/components/api/definition/components/complete/EditCompleteHTTPApi.vue @@ -11,7 +11,8 @@ {{ $t('commons.save') }}
-

{{ $t('test_track.plan_view.base_info') }}

+ +
@@ -96,7 +97,7 @@
-

{{ $t('test_track.plan_view.mock_info') }}

+
@@ -114,7 +115,7 @@
-

{{ $t('api_test.definition.request.req_param') }}

+
@@ -122,8 +123,10 @@ -

{{ $t('api_test.definition.request.res_param') }}

- + + + + @@ -142,10 +145,15 @@ import MsSelectTree from "../../../../common/select-tree/SelectTree"; import MsChangeHistory from "../../../../history/ChangeHistory"; import {getCurrentProjectID, getUUID} from "@/common/js/utils"; + import MsFormDivider from "@/business/components/common/components/MsFormDivider"; + import ApiOtherInfo from "@/business/components/api/definition/components/complete/ApiOtherInfo"; export default { name: "MsAddCompleteHttpApi", - components: {MsJsr233Processor, MsResponseText, MsApiRequestForm, MsInputTag, MsSelectTree,MsChangeHistory}, + components: { + ApiOtherInfo, + MsFormDivider, + MsJsr233Processor, MsResponseText, MsApiRequestForm, MsInputTag, MsSelectTree,MsChangeHistory}, data() { let validateURL = (rule, value, callback) => { if (!this.httpForm.path.startsWith("/") || this.httpForm.path.match(/\s/) != null) { diff --git a/frontend/src/business/components/api/definition/components/complete/RelationshipRelevanceList.vue b/frontend/src/business/components/api/definition/components/complete/RelationshipRelevanceList.vue new file mode 100644 index 0000000000..1081f1dd52 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/complete/RelationshipRelevanceList.vue @@ -0,0 +1,100 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/list/ApiList.vue b/frontend/src/business/components/api/definition/components/list/ApiList.vue index 504614e673..e19f6ceb25 100644 --- a/frontend/src/business/components/api/definition/components/list/ApiList.vue +++ b/frontend/src/business/components/api/definition/components/list/ApiList.vue @@ -180,7 +180,10 @@ - + + + +
@@ -215,11 +218,15 @@ import { import HeaderLabelOperate from "@/business/components/common/head/HeaderLabelOperate"; import {Body} from "@/business/components/api/definition/model/ApiTestModel"; import {editApiDefinitionOrder} from "@/network/api"; +import {getProtocolFilter} from "@/business/components/api/definition/api-definition"; +import {getGraphByCondition} from "@/network/graph"; +import RelationshipGraphDrawer from "@/business/components/xpack/graph/RelationshipGraphDrawer"; export default { name: "ApiList", components: { + RelationshipGraphDrawer, HeaderLabelOperate, CaseBatchMove, ApiStatus, @@ -253,6 +260,7 @@ export default { moduleId: "", enableOrderDrag: true, selectDataRange: "all", + graphData: [], deletePath: "/test/case/delete", buttons: [ { @@ -269,6 +277,12 @@ export default { name: this.$t('api_test.definition.request.batch_move'), handleClick: this.handleBatchMove, permissions: ['PROJECT_API_DEFINITION:READ+EDIT_API'] + }, + { + name: this.$t('生成依赖关系'), + isXPack: true, + handleClick: this.generateGraph, + permissions: ['PROJECT_API_DEFINITION:READ+EDIT_API'] } ], trashButtons: [ @@ -488,6 +502,12 @@ export default { } }); }, + generateGraph() { + getGraphByCondition('API', buildBatchParam(this, this.$refs.table.selectIds),(data) => { + this.graphData = data; + this.$refs.relationshipGraph.open(); + }); + }, handleBatchMove() { this.$refs.testCaseBatchMove.open(this.moduleTree, [], this.moduleOptions); }, @@ -543,7 +563,7 @@ export default { } if (this.condition.projectId) { this.result = this.$post("/api/definition/list/" + this.currentPage + "/" + this.pageSize, this.condition, response => { - this.genProtocalFilter(this.condition.protocol); + getProtocolFilter(this.condition.protocol); this.total = response.data.itemCount; this.tableData = response.data.listObject; this.tableData.forEach(item => { @@ -557,48 +577,6 @@ export default { this.$emit("refreshTree"); } }, - genProtocalFilter(protocalType) { - if (protocalType === "HTTP") { - this.methodFilters = [ - {text: 'GET', value: 'GET'}, - {text: 'POST', value: 'POST'}, - {text: 'PUT', value: 'PUT'}, - {text: 'PATCH', value: 'PATCH'}, - {text: 'DELETE', value: 'DELETE'}, - {text: 'OPTIONS', value: 'OPTIONS'}, - {text: 'HEAD', value: 'HEAD'}, - {text: 'CONNECT', value: 'CONNECT'}, - ]; - } else if (protocalType === "TCP") { - this.methodFilters = [ - {text: 'TCP', value: 'TCP'}, - ]; - } else if (protocalType === "SQL") { - this.methodFilters = [ - {text: 'SQL', value: 'SQL'}, - ]; - } else if (protocalType === "DUBBO") { - this.methodFilters = [ - {text: 'DUBBO', value: 'DUBBO'}, - {text: 'dubbo://', value: 'dubbo://'}, - ]; - } else { - this.methodFilters = [ - {text: 'GET', value: 'GET'}, - {text: 'POST', value: 'POST'}, - {text: 'PUT', value: 'PUT'}, - {text: 'PATCH', value: 'PATCH'}, - {text: 'DELETE', value: 'DELETE'}, - {text: 'OPTIONS', value: 'OPTIONS'}, - {text: 'HEAD', value: 'HEAD'}, - {text: 'CONNECT', value: 'CONNECT'}, - {text: 'DUBBO', value: 'DUBBO'}, - {text: 'dubbo://', value: 'dubbo://'}, - {text: 'SQL', value: 'SQL'}, - {text: 'TCP', value: 'TCP'}, - ]; - } - }, getMaintainerOptions() { this.$post('/user/project/member/tester/list', {projectId: getCurrentProjectID()}, response => { this.valueArr.userId = response.data; diff --git a/frontend/src/business/components/track/case/components/TestCaseDependencies.vue b/frontend/src/business/components/common/components/graph/DependenciesList.vue similarity index 57% rename from frontend/src/business/components/track/case/components/TestCaseDependencies.vue rename to frontend/src/business/components/common/components/graph/DependenciesList.vue index 60ed5197e2..9b40190c3c 100644 --- a/frontend/src/business/components/track/case/components/TestCaseDependencies.vue +++ b/frontend/src/business/components/common/components/graph/DependenciesList.vue @@ -1,11 +1,11 @@ + + diff --git a/frontend/src/business/components/track/case/TestCase.vue b/frontend/src/business/components/track/case/TestCase.vue index d8b7671d67..71e82c9687 100644 --- a/frontend/src/business/components/track/case/TestCase.vue +++ b/frontend/src/business/components/track/case/TestCase.vue @@ -8,7 +8,6 @@ @setTreeNodes="setTreeNodes" @exportTestCase="exportTestCase" @saveAsEdit="editTestCase" - @openGraph="openGraph" :show-operator="true" @createCase="handleCaseSimpleCreate($event, 'add')" @refreshAll="refreshAll" @@ -324,9 +323,6 @@ export default { } this.$refs.testCaseList.exportTestCase(type); }, - openGraph() { - this.$refs.testCaseList.generateGraph(); - }, addListener() { let index = this.tabs.findIndex(item => item.name === this.activeName); // 找到当前选中tab的index if (index != -1) { // 为当前选中的tab添加监听 diff --git a/frontend/src/business/components/track/case/components/ShowMoreBtn.vue b/frontend/src/business/components/track/case/components/ShowMoreBtn.vue index f211ab299c..361c7fab0b 100644 --- a/frontend/src/business/components/track/case/components/ShowMoreBtn.vue +++ b/frontend/src/business/components/track/case/components/ShowMoreBtn.vue @@ -9,16 +9,18 @@
{{$t('test_track.case.batch_handle', [size])}}
- - {{btn.name}} - + + + {{btn.name}} + +
diff --git a/frontend/src/business/components/track/common/TestCaseNodeTree.vue b/frontend/src/business/components/track/common/TestCaseNodeTree.vue index 43aee7fd7d..7e12edf782 100644 --- a/frontend/src/business/components/track/common/TestCaseNodeTree.vue +++ b/frontend/src/business/components/track/common/TestCaseNodeTree.vue @@ -81,11 +81,6 @@ export default { label: this.$t('api_test.export_config'), callback: this.handleExport, permissions: ['PROJECT_TRACK_CASE:READ+EXPORT'] - }, - { - label: this.$t('查看依赖'), - callback: this.openGraph, - // permissions: ['PROJECT_TRACK_CASE:READ+EXPORT'] } ] }; @@ -114,9 +109,6 @@ export default { }, }, methods: { - openGraph() { - this.$emit('openGraph'); - }, addTestCase(){ if (!this.projectId) { this.$warning(this.$t('commons.check_project_tip')); diff --git a/frontend/src/network/api.js b/frontend/src/network/api.js index 915eceac11..c7c1242f38 100644 --- a/frontend/src/network/api.js +++ b/frontend/src/network/api.js @@ -31,3 +31,7 @@ export function editApiDefinitionOrder(request, callback) { export function editApiTestCaseOrder(request, callback) { return basePost('/api/testcase/edit/order', request, callback); } + +export function getRelationshipApi(id, relationshipType, callback) { + return baseGet('/api/definition/relationship/' + id + '/' + relationshipType, callback); +}