diff --git a/backend/src/main/java/io/metersphere/api/service/ApiScenarioModuleService.java b/backend/src/main/java/io/metersphere/api/service/ApiScenarioModuleService.java index 772827551c..1be7cadebc 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiScenarioModuleService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiScenarioModuleService.java @@ -272,7 +272,8 @@ public class ApiScenarioModuleService extends NodeTreeService apiScenarios = queryByModuleIds(request); apiScenarios.forEach(apiScenario -> { - StringBuilder path = new StringBuilder(apiScenario.getModulePath()); + String modulePath = apiScenario.getModulePath(); + StringBuilder path = new StringBuilder(modulePath == null ? "" : modulePath); List pathLists = Arrays.asList(path.toString().split("/")); if (pathLists.size() > request.getLevel()) { pathLists.set(request.getLevel(), request.getName()); diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtIssuesMapper.java b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtIssuesMapper.java index 1e27227046..83265a5522 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtIssuesMapper.java +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtIssuesMapper.java @@ -16,4 +16,6 @@ public interface ExtIssuesMapper { List getRelateIssues(@Param("request") IssuesRequest request); Issues getNextNum(String projectId); + + List getIssueForSync(String projectId); } diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtIssuesMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtIssuesMapper.xml index a1264b7f40..f9c320e17f 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtIssuesMapper.xml +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtIssuesMapper.xml @@ -34,12 +34,20 @@ and test_case_issues.issues_id is null + - and issues.title LIKE CONCAT('%', #{request.name}, '%') + and ( + issues.title LIKE CONCAT('%', #{request.name}, '%') + or issues.id LIKE CONCAT('%', #{request.name}, '%') + ) diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtProjectMapper.java b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtProjectMapper.java index efc11cd725..4e1b1e393c 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtProjectMapper.java +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtProjectMapper.java @@ -18,4 +18,5 @@ public interface ExtProjectMapper { String getSystemIdByProjectId(String projectId); + List getProjectIds(); } diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtProjectMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtProjectMapper.xml index 3d7e40d27e..c813fdb500 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtProjectMapper.xml +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtProjectMapper.xml @@ -110,6 +110,9 @@ + diff --git a/backend/src/main/java/io/metersphere/commons/constants/ScheduleGroup.java b/backend/src/main/java/io/metersphere/commons/constants/ScheduleGroup.java index 1d56f1abec..5034727411 100644 --- a/backend/src/main/java/io/metersphere/commons/constants/ScheduleGroup.java +++ b/backend/src/main/java/io/metersphere/commons/constants/ScheduleGroup.java @@ -1,5 +1,5 @@ package io.metersphere.commons.constants; public enum ScheduleGroup { - API_TEST, PERFORMANCE_TEST, API_SCENARIO_TEST, TEST_PLAN_TEST, SWAGGER_IMPORT + API_TEST, PERFORMANCE_TEST, API_SCENARIO_TEST, TEST_PLAN_TEST, SWAGGER_IMPORT, ISSUE_SYNC } diff --git a/backend/src/main/java/io/metersphere/job/sechedule/IssueSyncJob.java b/backend/src/main/java/io/metersphere/job/sechedule/IssueSyncJob.java new file mode 100644 index 0000000000..3bd08a43c3 --- /dev/null +++ b/backend/src/main/java/io/metersphere/job/sechedule/IssueSyncJob.java @@ -0,0 +1,31 @@ +package io.metersphere.job.sechedule; + +import io.metersphere.commons.constants.ScheduleGroup; +import io.metersphere.commons.utils.CommonBeanFactory; +import io.metersphere.track.service.IssuesService; +import org.quartz.JobExecutionContext; +import org.quartz.JobKey; +import org.quartz.TriggerKey; + +public class IssueSyncJob extends MsScheduleJob { + + private IssuesService issuesService; + + public IssueSyncJob() { + issuesService = CommonBeanFactory.getBean(IssuesService.class); + } + + @Override + void businessExecute(JobExecutionContext context) { + issuesService.syncThirdPartyIssues(); + } + + public static JobKey getJobKey(String projectId) { + return new JobKey(projectId, ScheduleGroup.ISSUE_SYNC.name()); + } + + public static TriggerKey getTriggerKey(String projectId) { + return new TriggerKey(projectId, ScheduleGroup.ISSUE_SYNC.name()); + } +} + diff --git a/backend/src/main/java/io/metersphere/service/ProjectService.java b/backend/src/main/java/io/metersphere/service/ProjectService.java index 141ae1d1e7..54bb3e5715 100644 --- a/backend/src/main/java/io/metersphere/service/ProjectService.java +++ b/backend/src/main/java/io/metersphere/service/ProjectService.java @@ -451,4 +451,8 @@ public class ProjectService { return returnList.get(0); } } + + public List getProjectIds() { + return extProjectMapper.getProjectIds(); + } } diff --git a/backend/src/main/java/io/metersphere/track/controller/IssuesController.java b/backend/src/main/java/io/metersphere/track/controller/IssuesController.java index e75ccfd902..ea8b8c3c5c 100644 --- a/backend/src/main/java/io/metersphere/track/controller/IssuesController.java +++ b/backend/src/main/java/io/metersphere/track/controller/IssuesController.java @@ -92,10 +92,13 @@ public class IssuesController { return issuesService.getZentaoBuilds(request); } - @PostMapping("/get/platform/issue") - public IssuesWithBLOBs getPlatformIssue(@RequestBody IssuesWithBLOBs issue) { - return issuesService.getPlatformIssue(issue); +// @PostMapping("/get/platform/issue") +// public IssuesWithBLOBs getPlatformIssue(@RequestBody IssuesWithBLOBs issue) { +// return issuesService.getPlatformIssue(issue); +// } + + @GetMapping("/sync/{projectId}") + public void getPlatformIssue(@PathVariable String projectId) { + issuesService.syncThirdPartyIssues(projectId); } - - } diff --git a/backend/src/main/java/io/metersphere/track/issue/IssuesPlatform.java b/backend/src/main/java/io/metersphere/track/issue/IssuesPlatform.java index 4a32253952..b8a0790df0 100644 --- a/backend/src/main/java/io/metersphere/track/issue/IssuesPlatform.java +++ b/backend/src/main/java/io/metersphere/track/issue/IssuesPlatform.java @@ -1,6 +1,7 @@ package io.metersphere.track.issue; import io.metersphere.base.domain.IssuesDao; +import io.metersphere.base.domain.Project; import io.metersphere.track.dto.DemandDTO; import io.metersphere.track.issue.domain.PlatformUser; import io.metersphere.track.request.testcase.IssuesRequest; @@ -56,4 +57,11 @@ public interface IssuesPlatform { * @return platform user list */ List getPlatformUser(); + + /** + * 同步缺陷最新变更 + * @param project + * @param tapdIssues + */ + void syncIssues(Project project, List tapdIssues); } diff --git a/backend/src/main/java/io/metersphere/track/issue/JiraPlatform.java b/backend/src/main/java/io/metersphere/track/issue/JiraPlatform.java index 91ee591e56..44094f7ae0 100644 --- a/backend/src/main/java/io/metersphere/track/issue/JiraPlatform.java +++ b/backend/src/main/java/io/metersphere/track/issue/JiraPlatform.java @@ -31,6 +31,7 @@ import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; +import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.RestTemplate; import java.util.ArrayList; @@ -64,8 +65,6 @@ public class JiraPlatform extends AbstractIssuePlatform { @Override public List getIssue(IssuesRequest issuesRequest) { - List list = new ArrayList<>(); - issuesRequest.setPlatform(IssuesManagePlatform.Jira.toString()); List issues; if (StringUtils.isNotBlank(issuesRequest.getProjectId())) { @@ -73,26 +72,26 @@ public class JiraPlatform extends AbstractIssuePlatform { } else { issues = extIssuesMapper.getIssuesByCaseId(issuesRequest); } - setConfig(issuesRequest.getOrganizationId()); - issues.forEach(item -> { - String issuesId = item.getId(); - parseIssue(item, jiraClientV2.getIssues(issuesId)); - if (StringUtils.isBlank(item.getId())) { - // 缺陷不存在,解除用例和缺陷的关联 - TestCaseIssuesExample issuesExample = new TestCaseIssuesExample(); - issuesExample.createCriteria() - .andTestCaseIdEqualTo(testCaseId) - .andIssuesIdEqualTo(issuesId); - testCaseIssuesMapper.deleteByExample(issuesExample); - issuesMapper.deleteByPrimaryKey(issuesId); - } else { - // 缺陷状态为 完成,则不显示 - if (!StringUtils.equals("done", item.getStatus())) { - list.add(item); - } - } - }); - return list; +// setConfig(issuesRequest.getOrganizationId()); +// issues.forEach(item -> { +// String issuesId = item.getId(); +// parseIssue(item, jiraClientV2.getIssues(issuesId)); +// if (StringUtils.isBlank(item.getId())) { +// // 缺陷不存在,解除用例和缺陷的关联 +// TestCaseIssuesExample issuesExample = new TestCaseIssuesExample(); +// issuesExample.createCriteria() +// .andTestCaseIdEqualTo(testCaseId) +// .andIssuesIdEqualTo(issuesId); +// testCaseIssuesMapper.deleteByExample(issuesExample); +// issuesMapper.deleteByPrimaryKey(issuesId); +// } else { +// // 缺陷状态为 完成,则不显示 +// if (!StringUtils.equals("done", item.getStatus())) { +// list.add(item); +// } +// } +// }); + return issues; } public void parseIssue(IssuesWithBLOBs item, JiraIssue jiraIssue) { @@ -296,6 +295,27 @@ public class JiraPlatform extends AbstractIssuePlatform { return null; } + @Override + public void syncIssues(Project project, List tapdIssues) { + tapdIssues.forEach(item -> { + setConfig(null); + try { + parseIssue(item, jiraClientV2.getIssues(item.getId())); + // 缺陷状态为 完成,则不显示 + if (StringUtils.equals("done", item.getStatus())) { + item.setStatus(IssuesStatus.RESOLVED.toString()); + } + issuesMapper.updateByPrimaryKeySelective(item); + } catch (HttpClientErrorException e) { + if (e.getRawStatusCode() == 404) { + // 标记成删除 + item.setStatus(IssuesStatus.DELETE.toString()); + issuesMapper.deleteByPrimaryKey(item.getId()); + } + } + }); + } + @Override String getProjectId(String projectId) { if (StringUtils.isNotBlank(projectId)) { diff --git a/backend/src/main/java/io/metersphere/track/issue/LocalPlatform.java b/backend/src/main/java/io/metersphere/track/issue/LocalPlatform.java index 6ef476ce8a..faa31b627f 100644 --- a/backend/src/main/java/io/metersphere/track/issue/LocalPlatform.java +++ b/backend/src/main/java/io/metersphere/track/issue/LocalPlatform.java @@ -2,6 +2,7 @@ package io.metersphere.track.issue; import io.metersphere.base.domain.IssuesDao; import io.metersphere.base.domain.IssuesWithBLOBs; +import io.metersphere.base.domain.Project; import io.metersphere.commons.constants.IssuesManagePlatform; import io.metersphere.commons.user.SessionUser; import io.metersphere.commons.utils.BeanUtils; @@ -81,6 +82,11 @@ public class LocalPlatform extends AbstractIssuePlatform { return null; } + @Override + public void syncIssues(Project project, List tapdIssues) { + + } + @Override String getProjectId(String projectId) { return null; diff --git a/backend/src/main/java/io/metersphere/track/issue/TapdPlatform.java b/backend/src/main/java/io/metersphere/track/issue/TapdPlatform.java index 9568c61372..e986e49b98 100644 --- a/backend/src/main/java/io/metersphere/track/issue/TapdPlatform.java +++ b/backend/src/main/java/io/metersphere/track/issue/TapdPlatform.java @@ -3,16 +3,24 @@ package io.metersphere.track.issue; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; -import io.metersphere.base.domain.*; +import io.metersphere.base.domain.Issues; +import io.metersphere.base.domain.IssuesDao; +import io.metersphere.base.domain.Project; +import io.metersphere.base.domain.TestCaseWithBLOBs; import io.metersphere.commons.constants.IssuesManagePlatform; import io.metersphere.commons.constants.IssuesStatus; import io.metersphere.commons.exception.MSException; +import io.metersphere.commons.utils.BeanUtils; import io.metersphere.commons.utils.LogUtil; import io.metersphere.commons.utils.SessionUtils; import io.metersphere.controller.ResultHolder; import io.metersphere.dto.CustomFieldItemDTO; +import io.metersphere.dto.UserDTO; import io.metersphere.track.dto.DemandDTO; +import io.metersphere.track.issue.client.TapdClient; import io.metersphere.track.issue.domain.PlatformUser; +import io.metersphere.track.issue.domain.TapdConfig; +import io.metersphere.track.issue.domain.TapdGetIssueResponse; import io.metersphere.track.request.testcase.IssuesRequest; import io.metersphere.track.request.testcase.IssuesUpdateRequest; import org.apache.commons.collections.CollectionUtils; @@ -33,15 +41,17 @@ public class TapdPlatform extends AbstractIssuePlatform { protected String key = IssuesManagePlatform.Tapd.toString(); + private TapdClient tapdClient = new TapdClient(); + + public TapdPlatform(IssuesRequest issueRequest) { super(issueRequest); } @Override public List getIssue(IssuesRequest issuesRequest) { - List list = new ArrayList<>(); - String tapdId = getProjectId(issuesRequest.getProjectId()); - +// List list = new ArrayList<>(); +// String tapdId = getProjectId(issuesRequest.getProjectId()); issuesRequest.setPlatform(IssuesManagePlatform.Tapd.toString()); List issues; if (StringUtils.isNotBlank(issuesRequest.getProjectId())) { @@ -49,30 +59,29 @@ public class TapdPlatform extends AbstractIssuePlatform { } else { issues = extIssuesMapper.getIssuesByCaseId(issuesRequest); } - - issues.forEach(item -> { - String issuesId = item.getId(); - IssuesDao dto = getTapdIssues(tapdId, issuesId); - dto.setNum(item.getNum()); - if (StringUtils.isBlank(dto.getId())) { - // 缺陷不存在,解除用例和缺陷的关联 - TestCaseIssuesExample issuesExample = new TestCaseIssuesExample(); - TestCaseIssuesExample.Criteria criteria = issuesExample.createCriteria(); - if (StringUtils.isNotBlank(testCaseId)) { - criteria.andTestCaseIdEqualTo(testCaseId); - } - criteria.andIssuesIdEqualTo(issuesId); - testCaseIssuesMapper.deleteByExample(issuesExample); - issuesMapper.deleteByPrimaryKey(issuesId); - } else { - dto.setPlatform(IssuesManagePlatform.Tapd.toString()); - // 缺陷状态为 关闭,则不显示 - if (!StringUtils.equals(IssuesStatus.CLOSED.toString(), dto.getStatus())) { - list.add(dto); - } - } - }); - return list; +// issues.forEach(item -> { +// String issuesId = item.getId(); +// IssuesDao dto = getTapdIssues(tapdId, issuesId); +// dto.setNum(item.getNum()); +// if (StringUtils.isBlank(dto.getId())) { +// // 缺陷不存在,解除用例和缺陷的关联 +// TestCaseIssuesExample issuesExample = new TestCaseIssuesExample(); +// TestCaseIssuesExample.Criteria criteria = issuesExample.createCriteria(); +// if (StringUtils.isNotBlank(testCaseId)) { +// criteria.andTestCaseIdEqualTo(testCaseId); +// } +// criteria.andIssuesIdEqualTo(issuesId); +// testCaseIssuesMapper.deleteByExample(issuesExample); +// issuesMapper.deleteByPrimaryKey(issuesId); +// } else { +// dto.setPlatform(IssuesManagePlatform.Tapd.toString()); +// // 缺陷状态为 关闭,则不显示 +// if (!StringUtils.equals(IssuesStatus.CLOSED.toString(), dto.getStatus())) { +// list.add(dto); +// } +// } +// }); + return issues; } @Override @@ -152,7 +161,6 @@ public class TapdPlatform extends AbstractIssuePlatform { List customFields = getCustomFields(issuesRequest.getCustomFields()); String url = "https://api.tapd.cn/bugs"; - String testCaseId = issuesRequest.getTestCaseId(); String tapdId = getProjectId(issuesRequest.getProjectId()); if (StringUtils.isBlank(tapdId)) { @@ -240,6 +248,33 @@ public class TapdPlatform extends AbstractIssuePlatform { return users; } + @Override + public void syncIssues(Project project, List tapdIssues) { + int pageNum = 1; + int limit = 200; + int count = 200; + List ids = tapdIssues.stream() + .map(Issues::getId) + .collect(Collectors.toList()); + + setConfig(null); + + while (count == limit) { + count = 0; + TapdGetIssueResponse result = tapdClient.getIssueForPageByIds(project.getTapdId(), pageNum, limit, ids); + List data = result.getData(); + count = data.size(); + pageNum++; + data.forEach(issue -> { + TapdGetIssueResponse.Bug bug = issue.getBug(); + IssuesDao issuesDao = new IssuesDao(); + BeanUtils.copyBean(issuesDao, bug); +// tapdClient.getStatusMap(projectId); + issuesMapper.updateByPrimaryKeySelective(issuesDao); + }); + } + } + @Override String getProjectId(String projectId) { if (StringUtils.isNotBlank(projectId)) { @@ -250,6 +285,26 @@ public class TapdPlatform extends AbstractIssuePlatform { return project.getTapdId(); } + public TapdConfig getConfig(String orgId) { + TapdConfig tapdConfig = null; + String config = getPlatformConfig(IssuesManagePlatform.Tapd.toString()); + if (StringUtils.isNotBlank(config)) { + tapdConfig = JSONObject.parseObject(config, TapdConfig.class); + UserDTO.PlatformInfo userPlatInfo = getUserPlatInfo(orgId, SessionUtils.getUserId()); + if (userPlatInfo != null && StringUtils.isNotBlank(userPlatInfo.getTapdUserName())) { +// tapdConfig.setAccount(userPlatInfo.getTapdUserName()); + } + } +// validateConfig(tapdConfig); + return tapdConfig; + } + + public TapdConfig setConfig(String orgId) { + TapdConfig config = getConfig(orgId); + tapdClient.setConfig(config); + return config; + } + private ResultHolder call(String url) { return call(url, HttpMethod.GET, null); } diff --git a/backend/src/main/java/io/metersphere/track/issue/ZentaoPlatform.java b/backend/src/main/java/io/metersphere/track/issue/ZentaoPlatform.java index 577f9b551d..dac0982871 100644 --- a/backend/src/main/java/io/metersphere/track/issue/ZentaoPlatform.java +++ b/backend/src/main/java/io/metersphere/track/issue/ZentaoPlatform.java @@ -371,6 +371,11 @@ public class ZentaoPlatform extends AbstractIssuePlatform { return users; } + @Override + public void syncIssues(Project project, List tapdIssues) { + + } + public List getBuilds() { String session = login(); String projectId1 = getProjectId(projectId); diff --git a/backend/src/main/java/io/metersphere/track/issue/client/TapdClient.java b/backend/src/main/java/io/metersphere/track/issue/client/TapdClient.java new file mode 100644 index 0000000000..e1ff4dd672 --- /dev/null +++ b/backend/src/main/java/io/metersphere/track/issue/client/TapdClient.java @@ -0,0 +1,79 @@ +package io.metersphere.track.issue.client; + +import io.metersphere.commons.exception.MSException; +import io.metersphere.track.issue.domain.TapdConfig; +import io.metersphere.track.issue.domain.TapdGetIssueResponse; +import io.metersphere.track.issue.domain.TapdStatusMapResponse; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; +import org.springframework.util.MultiValueMap; + +import java.util.List; + +@Component +public class TapdClient extends BaseClient { + + protected String ENDPOINT = "https://api.tapd.cn"; + + protected String USER_NAME; + + protected String PASSWD; + +// public JiraAddIssueResponse addIssue(String body) { +// LogUtil.debug("addIssue: " + body); +// HttpHeaders headers = getAuthHeader(); +// headers.setContentType(MediaType.APPLICATION_JSON); +// HttpEntity requestEntity = new HttpEntity<>(body, headers); +// ResponseEntity response = restTemplate.exchange(getBaseUrl() + "/issue", HttpMethod.POST, requestEntity, String.class); +// return (JiraAddIssueResponse) getResultForObject(JiraAddIssueResponse.class, response); +// } + + public TapdGetIssueResponse getIssueForPage(String projectId, int pageNum, int limit) { + return getIssueForPageByIds(projectId, pageNum, limit, null); + } + + public TapdStatusMapResponse getStatusMap(String projectId) { + String url = getBaseUrl() + "/workflows/status_map?workspace_id={1}&system=bug"; + ResponseEntity response = restTemplate.exchange(url, HttpMethod.GET, getAuthHttpEntity(), String.class, projectId); + return (TapdStatusMapResponse) getResultForObject(TapdStatusMapResponse.class, response); + } + + public TapdGetIssueResponse getIssueForPageByIds(String projectId, int pageNum, int limit, List ids) { + String url = getBaseUrl() + "/bugs?workspace_id={1}&page={2}&limit={3}&fields={4}"; + StringBuilder idStr = new StringBuilder(); + ids.forEach(item -> { + idStr.append(item + ","); + }); + if (!CollectionUtils.isEmpty(ids)) { + url += "&id={5}"; + } + String fields = "id,title,description,priority,severity,reporter,status"; + ResponseEntity response = restTemplate.exchange(url, HttpMethod.GET, getAuthHttpEntity(), String.class, + projectId, pageNum, limit, fields, idStr); + return (TapdGetIssueResponse) getResultForObject(TapdGetIssueResponse.class, response); + } + + protected HttpEntity getAuthHttpEntity() { + return new HttpEntity<>(getAuthHeader()); + } + + protected HttpHeaders getAuthHeader() { + return getBasicHttpHeaders(USER_NAME, PASSWD); + } + + protected String getBaseUrl() { + return ENDPOINT; + } + + public void setConfig(TapdConfig config) { + if (config == null) { + MSException.throwException("config is null"); + } + USER_NAME = config.getAccount(); + PASSWD = config.getPassword(); + } +} diff --git a/backend/src/main/java/io/metersphere/track/issue/domain/TapdConfig.java b/backend/src/main/java/io/metersphere/track/issue/domain/TapdConfig.java new file mode 100644 index 0000000000..092c5c331f --- /dev/null +++ b/backend/src/main/java/io/metersphere/track/issue/domain/TapdConfig.java @@ -0,0 +1,11 @@ +package io.metersphere.track.issue.domain; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class TapdConfig { + private String account; + private String password; +} diff --git a/backend/src/main/java/io/metersphere/track/issue/domain/TapdGetIssueResponse.java b/backend/src/main/java/io/metersphere/track/issue/domain/TapdGetIssueResponse.java new file mode 100644 index 0000000000..903e301cd1 --- /dev/null +++ b/backend/src/main/java/io/metersphere/track/issue/domain/TapdGetIssueResponse.java @@ -0,0 +1,33 @@ +package io.metersphere.track.issue.domain; + +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +public class TapdGetIssueResponse { + + private int status; + private List data; + private String info; + + @Getter + @Setter + public static class Data { + private Bug bug; + } + + @Getter + @Setter + public static class Bug { + private String id; + private String title; + private String description; +// private String priority; +// private String severity; +// private String reporter; +// private String status; + } +} diff --git a/backend/src/main/java/io/metersphere/track/issue/domain/TapdStatusMapResponse.java b/backend/src/main/java/io/metersphere/track/issue/domain/TapdStatusMapResponse.java new file mode 100644 index 0000000000..f7acf2660f --- /dev/null +++ b/backend/src/main/java/io/metersphere/track/issue/domain/TapdStatusMapResponse.java @@ -0,0 +1,29 @@ +package io.metersphere.track.issue.domain; + +import com.alibaba.fastjson.annotation.JSONField; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class TapdStatusMapResponse { + + private int status; + private Data data; + private String info; + + @Getter + @Setter + public class Data { + @JSONField(name = "new") + private String create; + @JSONField(name = "in_progress") + private String inProgress; + private String resolved; + private String verified; + private String reopened; + private String rejected; + private String closed; + } + +} diff --git a/backend/src/main/java/io/metersphere/track/service/IssuesService.java b/backend/src/main/java/io/metersphere/track/service/IssuesService.java index 52960b630d..3ebc42cf62 100644 --- a/backend/src/main/java/io/metersphere/track/service/IssuesService.java +++ b/backend/src/main/java/io/metersphere/track/service/IssuesService.java @@ -1,7 +1,6 @@ package io.metersphere.track.service; import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONObject; import io.metersphere.base.domain.*; import io.metersphere.base.mapper.IssueTemplateMapper; import io.metersphere.base.mapper.IssuesMapper; @@ -42,6 +41,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.function.BiConsumer; import java.util.stream.Collectors; @Service @@ -299,18 +299,6 @@ public class IssuesService { public List list(IssuesRequest request) { request.setOrders(ServiceUtils.getDefaultOrder(request.getOrders())); - - -// List list = new ArrayList<>(); -// String projectId = request.getProjectId(); -// Project project = projectService.getProjectById(projectId); -// List platforms = getPlatforms(project); -// platforms.add(IssuesManagePlatform.Local.toString()); -// List platformList = IssueFactory.createPlatforms(platforms, request); -// platformList.forEach(platform -> { -// List issue = platform.getIssue(request); -// list.addAll(issue); -// }); List issues = extIssuesMapper.getIssuesByProjectId(request); List ids = issues.stream() @@ -334,17 +322,7 @@ public class IssuesService { item.setResourceName(planMap.get(item.getResourceId())); } }); - -// Map> issueMap = getIssueMap(issues); -// Map platformMap = getPlatformMap(request); -// issueMap.forEach((platformName, data) -> { -// AbstractIssuePlatform platform = platformMap.get(platformName); -// if (platform != null) { -// platform.filter(data); -// } -// }); return issues; -// return list; } public Map> getIssueMap(List issues) { @@ -372,40 +350,100 @@ public class IssuesService { return IssueFactory.createPlatformsForMap(platforms, request); } - public IssuesWithBLOBs getPlatformIssue(IssuesWithBLOBs issue) { - String platform = issue.getPlatform(); - if (StringUtils.isNotBlank(issue.getProjectId())) { - Project project = projectService.getProjectById(issue.getProjectId()); - Workspace workspace = workspaceMapper.selectByPrimaryKey(project.getWorkspaceId()); - String orgId = workspace.getOrganizationId(); - try { - if (StringUtils.equals(platform, IssuesManagePlatform.Tapd.name())) { - TapdPlatform tapdPlatform = new TapdPlatform(new IssuesRequest()); - String tapdId = projectService.getProjectById(issue.getProjectId()).getTapdId(); - IssuesDao tapdIssues = tapdPlatform.getTapdIssues(tapdId, issue.getId()); - issue.setTitle(tapdIssues.getTitle()); - issue.setDescription(tapdIssues.getDescription()); - issue.setStatus(tapdIssues.getStatus()); - } else if (StringUtils.equals(platform, IssuesManagePlatform.Jira.name())) { - JiraPlatform jiraPlatform = new JiraPlatform(new IssuesRequest()); - jiraPlatform.getJiraIssues(issue, issue.getId()); - } else if (StringUtils.equals(platform, IssuesManagePlatform.Zentao.name())) { - String config = getConfig(orgId, IssuesManagePlatform.Zentao.toString()); - JSONObject object = JSON.parseObject(config); - String account = object.getString("account"); - String password = object.getString("password"); - String url = object.getString("url"); - ZentaoPlatform zentaoPlatform = new ZentaoPlatform(account, password, url); - IssuesDao zentaoIssues = zentaoPlatform.getZentaoIssues(issue.getId()); - issue.setTitle(zentaoIssues.getTitle()); - issue.setDescription(zentaoIssues.getDescription()); - issue.setStatus(zentaoIssues.getStatus()); - } - } catch (Exception e) { - LogUtil.error(e.getMessage(), e); +// public IssuesWithBLOBs getPlatformIssue(IssuesWithBLOBs issue) { +// String platform = issue.getPlatform(); +// if (StringUtils.isNotBlank(issue.getProjectId())) { +// Project project = projectService.getProjectById(issue.getProjectId()); +// Workspace workspace = workspaceMapper.selectByPrimaryKey(project.getWorkspaceId()); +// String orgId = workspace.getOrganizationId(); +// try { +// if (StringUtils.equals(platform, IssuesManagePlatform.Tapd.name())) { +// TapdPlatform tapdPlatform = new TapdPlatform(new IssuesRequest()); +// String tapdId = projectService.getProjectById(issue.getProjectId()).getTapdId(); +// IssuesDao tapdIssues = tapdPlatform.getTapdIssues(tapdId, issue.getId()); +// issue.setTitle(tapdIssues.getTitle()); +// issue.setDescription(tapdIssues.getDescription()); +// issue.setStatus(tapdIssues.getStatus()); +// } else if (StringUtils.equals(platform, IssuesManagePlatform.Jira.name())) { +// JiraPlatform jiraPlatform = new JiraPlatform(new IssuesRequest()); +// jiraPlatform.getJiraIssues(issue, issue.getId()); +// } else if (StringUtils.equals(platform, IssuesManagePlatform.Zentao.name())) { +// String config = getConfig(orgId, IssuesManagePlatform.Zentao.toString()); +// JSONObject object = JSON.parseObject(config); +// String account = object.getString("account"); +// String password = object.getString("password"); +// String url = object.getString("url"); +// ZentaoPlatform zentaoPlatform = new ZentaoPlatform(account, password, url); +// IssuesDao zentaoIssues = zentaoPlatform.getZentaoIssues(issue.getId()); +// issue.setTitle(zentaoIssues.getTitle()); +// issue.setDescription(zentaoIssues.getDescription()); +// issue.setStatus(zentaoIssues.getStatus()); +// } +// } catch (Exception e) { +// LogUtil.error(e.getMessage(), e); +// } +// } +// return issue; +// } + + public void syncThirdPartyIssues() { + List projectIds = projectService.getProjectIds(); + projectIds.forEach(id -> { + syncThirdPartyIssues(id); + }); + } + + public void syncThirdPartyIssues(String projectId) { + if (StringUtils.isNotBlank(projectId)) { + Project project = projectService.getProjectById(projectId); + + List issues = extIssuesMapper.getIssueForSync(projectId); + + if (CollectionUtils.isEmpty(issues)) { + return; } + + List tapdIssues = issues.stream() + .filter(item -> item.getPlatform().equals(IssuesManagePlatform.Tapd.name())) + .collect(Collectors.toList()); + List jiraIssues = issues.stream() + .filter(item -> item.getPlatform().equals(IssuesManagePlatform.Jira.name())) + .collect(Collectors.toList()); +// List zentaoIssues = issues.stream() +// .filter(item -> item.getPlatform().equals(IssuesManagePlatform.Zentao.name())) +// .collect(Collectors.toList()); + + IssuesRequest issuesRequest = new IssuesRequest(); + issuesRequest.setProjectId(projectId); + if (CollectionUtils.isNotEmpty(tapdIssues)) { + TapdPlatform tapdPlatform = new TapdPlatform(issuesRequest); + syncThirdPartyIssues(tapdPlatform::syncIssues, project, tapdIssues); + } + if (CollectionUtils.isNotEmpty(jiraIssues)) { + JiraPlatform jiraPlatform = new JiraPlatform(new IssuesRequest()); + syncThirdPartyIssues(jiraPlatform::syncIssues, project, tapdIssues); + } +// if (CollectionUtils.isNotEmpty(zentaoIssues)) { +// String config = getConfig(orgId, IssuesManagePlatform.Zentao.toString()); +// JSONObject object = JSON.parseObject(config); +// String account = object.getString("account"); +// String password = object.getString("password"); +// String url = object.getString("url"); +// ZentaoPlatform zentaoPlatform = new ZentaoPlatform(account, password, url); +// IssuesDao zentaoIssues = zentaoPlatform.getZentaoIssues(issue.getId()); +// issue.setTitle(zentaoIssues.getTitle()); +// issue.setDescription(zentaoIssues.getDescription()); +// issue.setStatus(zentaoIssues.getStatus()); +// } + } + } + + public void syncThirdPartyIssues(BiConsumer> syncFuc, Project project, List issues) { + try { + syncFuc.accept(project, issues); + } catch (Exception e) { + LogUtil.error(e.getMessage(), e); } - return issue; } private String getConfig(String orgId, String platform) { diff --git a/frontend/src/business/components/api/automation/scenario/ApiScenarioList.vue b/frontend/src/business/components/api/automation/scenario/ApiScenarioList.vue index d2bae117f0..28d9f81c49 100644 --- a/frontend/src/business/components/api/automation/scenario/ApiScenarioList.vue +++ b/frontend/src/business/components/api/automation/scenario/ApiScenarioList.vue @@ -930,11 +930,6 @@ export default { margin-bottom: 0px; } -/deep/ .run-button { - background-color: #409EFF; - border-color: #409EFF; -} - /deep/ .el-table__fixed-body-wrapper { z-index: auto !important; } diff --git a/frontend/src/business/components/settings/workspace/MsProject.vue b/frontend/src/business/components/settings/workspace/MsProject.vue index 62dd9e39c9..8c5a0a30a5 100644 --- a/frontend/src/business/components/settings/workspace/MsProject.vue +++ b/frontend/src/business/components/settings/workspace/MsProject.vue @@ -375,7 +375,7 @@ export default { this.createVisible = true; listenGoBack(this.handleClose); this.form = Object.assign({}, row); - this.$get("/service/integration/all/" + getCurrentUser().lastOrganizationId, response => { + this.$get("/service/integration/all/" + getCurrentOrganizationId(), response => { let data = response.data; let platforms = data.map(d => d.platform); if (platforms.indexOf("Tapd") !== -1) { diff --git a/frontend/src/business/components/track/issue/IssueList.vue b/frontend/src/business/components/track/issue/IssueList.vue index a8ad3ccfba..b80b102405 100644 --- a/frontend/src/business/components/track/issue/IssueList.vue +++ b/frontend/src/business/components/track/issue/IssueList.vue @@ -5,7 +5,14 @@ @@ -47,6 +55,7 @@ :field="item" :fields-width="fieldsWidth" :label="$t('test_track.issue.title')" + sortable prop="title"> @@ -136,7 +145,7 @@ import { import MsTableHeader from "@/business/components/common/components/MsTableHeader"; import IssueDescriptionTableItem from "@/business/components/track/issue/IssueDescriptionTableItem"; import IssueEdit from "@/business/components/track/issue/IssueEdit"; -import {getIssues} from "@/network/Issue"; +import {getIssues, syncIssues} from "@/network/Issue"; import { getCustomFieldValue, getCustomTableWidth, @@ -147,6 +156,7 @@ import MsMainContainer from "@/business/components/common/components/MsMainConta import {getCurrentProjectID} from "@/common/js/utils"; import {getIssueTemplate} from "@/network/custom-field-template"; import {getProjectMember} from "@/network/user"; +import {getIntegrationService} from "@/network/organization"; export default { name: "IssueList", @@ -183,13 +193,17 @@ export default { } ], issueTemplate: {}, - members: [] + members: [], + platforms: [] }; }, activated() { getProjectMember((data) => { this.members = data; }); + getIntegrationService((data) => { + this.platforms = data.map(d => d.platform); + }); getIssueTemplate() .then((template) => { this.issueTemplate = template; @@ -216,6 +230,14 @@ export default { }, projectId() { return getCurrentProjectID(); + }, + hasThirdPart() { + if (this.platforms.indexOf("Tapd") !== -1 + || this.platforms.indexOf("Jira") !== -1 + || this.platforms.indexOf("Zentao") !== -1) { + return true; + } + return false; } }, methods: { @@ -258,6 +280,10 @@ export default { saveSortField(key,orders){ saveLastTableSortField(key,JSON.stringify(orders)); }, + syncIssues() { + this.page.result = syncIssues(); + this.getIssues(); + }, getSortField(){ let orderJsonStr = getLastTableSortField(this.tableHeaderKey); let returnObj = null; diff --git a/frontend/src/business/components/track/plan/view/comonents/api/RelevanceScenarioList.vue b/frontend/src/business/components/track/plan/view/comonents/api/RelevanceScenarioList.vue index 6e72b8b534..4a53190e82 100644 --- a/frontend/src/business/components/track/plan/view/comonents/api/RelevanceScenarioList.vue +++ b/frontend/src/business/components/track/plan/view/comonents/api/RelevanceScenarioList.vue @@ -64,7 +64,6 @@ import MsTablePagination from "@/business/components/common/pagination/TablePagination"; import ShowMoreBtn from "@/business/components/track/case/components/ShowMoreBtn"; import MsTag from "../../../../../common/components/MsTag"; - import {getUUID, getCurrentProjectID} from "@/common/js/utils"; import MsApiReportDetail from "../../../../../api/automation/report/ApiReportDetail"; import MsTableMoreBtn from "../../../../../api/automation/scenario/TableMoreBtn"; import MsTestPlanList from "../../../../../api/automation/scenario/testplan/TestPlanList"; diff --git a/frontend/src/business/components/track/plan/view/comonents/api/TestPlanApi.vue b/frontend/src/business/components/track/plan/view/comonents/api/TestPlanApi.vue index f7977b0366..d3cc429375 100644 --- a/frontend/src/business/components/track/plan/view/comonents/api/TestPlanApi.vue +++ b/frontend/src/business/components/track/plan/view/comonents/api/TestPlanApi.vue @@ -201,9 +201,4 @@ margin: 5px 10px; } - /deep/ .run-button { - background-color: #409EFF; - border-color: #409EFF; - } - diff --git a/frontend/src/business/components/track/plan/view/comonents/api/TestPlanApiScenarioList.vue b/frontend/src/business/components/track/plan/view/comonents/api/TestPlanApiScenarioList.vue index d026f4245d..4ede87a904 100644 --- a/frontend/src/business/components/track/plan/view/comonents/api/TestPlanApiScenarioList.vue +++ b/frontend/src/business/components/track/plan/view/comonents/api/TestPlanApiScenarioList.vue @@ -8,7 +8,8 @@ @relevanceCase="$emit('relevanceCase', 'scenario')"/> - diff --git a/frontend/src/common/css/main.css b/frontend/src/common/css/main.css index 87160f571a..61d0b6a3e1 100644 --- a/frontend/src/common/css/main.css +++ b/frontend/src/common/css/main.css @@ -239,3 +239,8 @@ textarea { .el-button + .tooltip-btn, .tooltip-btn + .el-button, .tooltip-btn + .tooltip-btn { margin-left: 10px; } + +.run-button .el-button { + background-color: #409EFF; + border-color: #409EFF; +} diff --git a/frontend/src/network/Issue.js b/frontend/src/network/Issue.js index 4699d25337..4e0d1ef898 100644 --- a/frontend/src/network/Issue.js +++ b/frontend/src/network/Issue.js @@ -1,5 +1,6 @@ import {post, get} from "@/common/js/ajax"; import {getPageDate} from "@/common/js/tableUtils"; +import {getCurrentProjectID} from "@/common/js/utils"; export function buildIssues(page) { let data = page.data; @@ -8,9 +9,9 @@ export function buildIssues(page) { if (data[i].customFields) { data[i].customFields = JSON.parse(data[i].customFields); } - if (data[i].platform !== 'Local') { - page.result = buildPlatformIssue(data[i]); - } + // if (data[i].platform !== 'Local') { + // page.result = buildPlatformIssue(data[i]); + // } } } } @@ -63,4 +64,10 @@ export function getRelateIssues(page) { }); } +export function syncIssues() { + return get('issues/sync/' + getCurrentProjectID(), (response) => { + console.log('ok'); + }); +} + diff --git a/frontend/src/network/organization.js b/frontend/src/network/organization.js index bc79527ae9..d4865fbca0 100644 --- a/frontend/src/network/organization.js +++ b/frontend/src/network/organization.js @@ -3,7 +3,6 @@ import {get} from "@/common/js/ajax"; export function getIntegrationService(success) { return get("/service/integration/all/" + getCurrentOrganizationId(), response => { - let data = response.data; if (success) { success(response.data); }