From 240ac7ab264feda2cd439183070f197b577d80c0 Mon Sep 17 00:00:00 2001 From: shiziyuan9527 Date: Mon, 17 Aug 2020 22:01:15 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=9B=86=E6=88=90Jira?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/metersphere/service/IssuesService.java | 218 ++++++++++++++++-- .../controller/TestCaseIssuesController.java | 6 + .../organization/DefectManagement.vue | 19 +- 3 files changed, 225 insertions(+), 18 deletions(-) diff --git a/backend/src/main/java/io/metersphere/service/IssuesService.java b/backend/src/main/java/io/metersphere/service/IssuesService.java index e1302b103c..73a9e4e7b3 100644 --- a/backend/src/main/java/io/metersphere/service/IssuesService.java +++ b/backend/src/main/java/io/metersphere/service/IssuesService.java @@ -10,24 +10,26 @@ import io.metersphere.commons.constants.IssuesManagePlatform; import io.metersphere.commons.exception.MSException; import io.metersphere.commons.user.SessionUser; import io.metersphere.commons.utils.EncryptUtils; +import io.metersphere.commons.utils.LogUtil; import io.metersphere.commons.utils.RestTemplateUtils; import io.metersphere.commons.utils.SessionUtils; import io.metersphere.controller.ResultHolder; import io.metersphere.controller.request.IntegrationRequest; import io.metersphere.track.request.testcase.IssuesRequest; import io.metersphere.track.service.TestCaseService; +import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.apache.commons.lang3.StringUtils; +import org.springframework.web.client.RestTemplate; import javax.annotation.Resource; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; +import java.util.*; import java.util.stream.Collectors; @Service @@ -48,10 +50,42 @@ public class IssuesService { private ExtIssuesMapper extIssuesMapper; - public void testAuth() { - String url = "https://api.tapd.cn/quickstart/testauth"; - ResultHolder call = call(url); - System.out.println(call.getData()); + public void testAuth(String platform) { + if (StringUtils.equals(platform, IssuesManagePlatform.Tapd.toString())) { + + try { + String tapdConfig = platformConfig(IssuesManagePlatform.Tapd.toString()); + JSONObject object = JSON.parseObject(tapdConfig); + String account = object.getString("account"); + String password = object.getString("password"); + HttpHeaders headers = auth(account, password); + HttpEntity requestEntity = new HttpEntity<>(headers); + RestTemplate restTemplate = new RestTemplate(); + restTemplate.exchange("https://api.tapd.cn/quickstart/testauth", HttpMethod.GET, requestEntity, String.class); + } catch (Exception e) { + System.out.println(e); + LogUtil.error(e.getMessage(), e); + MSException.throwException("验证失败!"); + } + + } else { + + try { + String config = platformConfig(IssuesManagePlatform.Jira.toString()); + JSONObject object = JSON.parseObject(config); + String account = object.getString("account"); + String password = object.getString("password"); + String url = object.getString("url"); + HttpHeaders headers = auth(account, password); + HttpEntity requestEntity = new HttpEntity<>(headers); + RestTemplate restTemplate = new RestTemplate(); + restTemplate.exchange(url + "rest/api/2/issue/createmeta", HttpMethod.GET, requestEntity, String.class); + } catch (Exception e) { + LogUtil.error(e.getMessage(), e); + MSException.throwException("验证失败!"); + } + } + } private ResultHolder call(String url) { @@ -61,7 +95,7 @@ public class IssuesService { private ResultHolder call(String url, HttpMethod httpMethod, Object params) { String responseJson; - String config = tapdConfig(); + String config = platformConfig(IssuesManagePlatform.Tapd.toString()); JSONObject object = JSON.parseObject(config); if (object == null) { @@ -71,7 +105,7 @@ public class IssuesService { String account = object.getString("account"); String password = object.getString("password"); - HttpHeaders header = tapdAuth(account, password); + HttpHeaders header = auth(account, password); if (httpMethod.equals(HttpMethod.GET)) { responseJson = RestTemplateUtils.get(url, header); @@ -88,7 +122,7 @@ public class IssuesService { } - private String tapdConfig() { + private String platformConfig(String platform) { SessionUser user = SessionUtils.getUser(); String orgId = user.getLastOrganizationId(); @@ -97,13 +131,13 @@ public class IssuesService { MSException.throwException("organization id is null"); } request.setOrgId(orgId); - request.setPlatform(IssuesManagePlatform.Tapd.toString()); + request.setPlatform(platform); ServiceIntegration integration = integrationService.get(request); return integration.getConfiguration(); } - private HttpHeaders tapdAuth(String apiUser, String password) { + private HttpHeaders auth(String apiUser, String password) { String authKey = EncryptUtils.base64Encoding(apiUser + ":" + password); HttpHeaders headers = new HttpHeaders(); headers.add("Authorization", "Basic " + authKey); @@ -118,10 +152,10 @@ public class IssuesService { boolean jira = isIntegratedPlatform(orgId, IssuesManagePlatform.Jira.toString()); String tapdId = getTapdProjectId(issuesRequest.getTestCaseId()); - String jiraId = ""; + String jiraKey = getJiraProjectKey(issuesRequest.getTestCaseId()); if (tapd || jira) { - if (StringUtils.isBlank(tapdId) && StringUtils.isBlank(jiraId)) { + if (StringUtils.isBlank(tapdId) && StringUtils.isBlank(jiraKey)) { MSException.throwException("集成了缺陷管理平台,但未进行项目关联!"); } } @@ -134,7 +168,9 @@ public class IssuesService { } if (jira) { - // addJiraIssues(issuesRequest); + if (StringUtils.isNotBlank(jiraKey)) { + addJiraIssues(issuesRequest); + } } if (!tapd && !jira) { @@ -177,6 +213,72 @@ public class IssuesService { issuesMapper.insert(issues); } + public void addJiraIssues(IssuesRequest issuesRequest) { + String config = platformConfig(IssuesManagePlatform.Jira.toString()); + JSONObject object = JSON.parseObject(config); + + if (object == null) { + MSException.throwException("jira config is null"); + } + + String account = object.getString("account"); + String password = object.getString("password"); + String url = object.getString("url"); + String auth = EncryptUtils.base64Encoding(account + ":" + password); + + String testCaseId = issuesRequest.getTestCaseId(); + String jiraKey = getJiraProjectKey(testCaseId); + + + if (StringUtils.isBlank(jiraKey)) { + MSException.throwException("未关联Jira 项目Key"); + } + + String json = "{\n" + + " \"fields\":{\n" + + " \"project\":{\n" + + " \"key\":\"" + jiraKey + "\"\n" + + " },\n" + + " \"summary\":\"" + issuesRequest.getTitle() + "\",\n" + + " \"description\":\"" + issuesRequest.getContent() + "\",\n" + + " \"issuetype\":{\n" + + " \"id\":\"10009\",\n" + + " \"name\":\"Defect\"\n" + + " }\n" + + " }\n" + + "}"; + + String result = addJiraIssue(url, auth, json); + + JSONObject jsonObject = JSON.parseObject(result); + String id = jsonObject.getString("id"); + + // 用例与第三方缺陷平台中的缺陷关联 + TestCaseIssues testCaseIssues = new TestCaseIssues(); + testCaseIssues.setId(UUID.randomUUID().toString()); + testCaseIssues.setIssuesId(id); + testCaseIssues.setTestCaseId(testCaseId); + testCaseIssuesMapper.insert(testCaseIssues); + + // 插入缺陷表 + Issues issues = new Issues(); + issues.setId(id); + issues.setPlatform(IssuesManagePlatform.Jira.toString()); + issuesMapper.insert(issues); + } + + private String addJiraIssue(String url, String auth, String json) { + HttpHeaders requestHeaders = new HttpHeaders(); + requestHeaders.add("Authorization", "Basic " + auth); + requestHeaders.setContentType(org.springframework.http.MediaType.APPLICATION_JSON); + //HttpEntity + HttpEntity requestEntity = new HttpEntity<>(json, requestHeaders); + RestTemplate restTemplate = new RestTemplate(); + //post + ResponseEntity responseEntity = restTemplate.exchange(url + "/rest/api/2/issue", HttpMethod.POST, requestEntity, String.class); + return responseEntity.getBody(); + } + public void addLocalIssues(IssuesRequest request) { SessionUser user = SessionUtils.getUser(); String id = UUID.randomUUID().toString(); @@ -209,6 +311,39 @@ public class IssuesService { return jsonObject.getObject("Bug", Issues.class); } + public Issues getJiraIssues(HttpHeaders headers, String url, String issuesId) { + HttpEntity requestEntity = new HttpEntity<>(headers); + RestTemplate restTemplate = new RestTemplate(); + //post + ResponseEntity responseEntity = null; + Issues issues = new Issues(); + try { + responseEntity = restTemplate.exchange(url + "/rest/api/2/issue/" + issuesId, HttpMethod.GET, requestEntity, String.class); + String body = responseEntity.getBody(); + + JSONObject obj = JSONObject.parseObject(body); + JSONObject fields = (JSONObject) obj.get("fields"); + JSONObject statusObj = (JSONObject) fields.get("status"); + JSONObject statusCategory = (JSONObject) statusObj.get("statusCategory"); + + String id = obj.getString("id"); + String title = fields.getString("summary"); + String description = fields.getString("description"); + String status = statusCategory.getString("key"); + + issues.setId(id); + issues.setTitle(title); + issues.setDescription(description); + issues.setStatus(status); + issues.setPlatform(IssuesManagePlatform.Jira.toString()); + } catch (Exception e) { + return new Issues(); + } + + return issues; + + } + public List getIssues(String caseId) { List list = new ArrayList<>(); SessionUser user = SessionUtils.getUser(); @@ -227,7 +362,10 @@ public class IssuesService { } if (jira) { - // getJiraIssues(caseId); + String jiraKey = getJiraProjectKey(caseId); + if (StringUtils.isNotBlank(jiraKey)) { + list.addAll(getJiraIssues(caseId)); + } } list.addAll(getLocalIssues(caseId)); @@ -253,6 +391,7 @@ public class IssuesService { .andTestCaseIdEqualTo(caseId) .andIssuesIdEqualTo(issuesId); testCaseIssuesMapper.deleteByExample(issuesExample); + issuesMapper.deleteByPrimaryKey(issuesId); } else { dto.setPlatform(IssuesManagePlatform.Tapd.toString()); // 缺陷状态为 关闭,则不显示 @@ -264,6 +403,47 @@ public class IssuesService { return list; } + public List getJiraIssues(String caseId) { + List list = new ArrayList<>(); + + String config = platformConfig(IssuesManagePlatform.Jira.toString()); + JSONObject object = JSON.parseObject(config); + + if (object == null) { + MSException.throwException("tapd config is null"); + } + + String account = object.getString("account"); + String password = object.getString("password"); + String url = object.getString("url"); + HttpHeaders headers = auth(account, password); + + TestCaseIssuesExample example = new TestCaseIssuesExample(); + example.createCriteria().andTestCaseIdEqualTo(caseId); + + List issues = extIssuesMapper.getIssues(caseId, IssuesManagePlatform.Jira.toString()); + + List issuesIds = issues.stream().map(issue -> issue.getId()).collect(Collectors.toList()); + issuesIds.forEach(issuesId -> { + Issues dto = getJiraIssues(headers, url, issuesId); + if (StringUtils.isBlank(dto.getId())) { + // 缺陷不存在,解除用例和缺陷的关联 + TestCaseIssuesExample issuesExample = new TestCaseIssuesExample(); + issuesExample.createCriteria() + .andTestCaseIdEqualTo(caseId) + .andIssuesIdEqualTo(issuesId); + testCaseIssuesMapper.deleteByExample(issuesExample); + issuesMapper.deleteByPrimaryKey(issuesId); + } else { + // 缺陷状态为 完成,则不显示 + if (!StringUtils.equals("done", dto.getStatus())) { + list.add(dto); + } + } + }); + return list; + } + public List getLocalIssues(String caseId) { return extIssuesMapper.getIssues(caseId, IssuesManagePlatform.Local.toString()); } @@ -274,6 +454,12 @@ public class IssuesService { return project.getTapdId(); } + public String getJiraProjectKey(String testCaseId) { + TestCaseWithBLOBs testCase = testCaseService.getTestCase(testCaseId); + Project project = projectService.getProjectById(testCase.getProjectId()); + return project.getJiraKey(); + } + /** * 是否关联平台 */ diff --git a/backend/src/main/java/io/metersphere/track/controller/TestCaseIssuesController.java b/backend/src/main/java/io/metersphere/track/controller/TestCaseIssuesController.java index b0c28a0646..a977736aa3 100644 --- a/backend/src/main/java/io/metersphere/track/controller/TestCaseIssuesController.java +++ b/backend/src/main/java/io/metersphere/track/controller/TestCaseIssuesController.java @@ -24,4 +24,10 @@ public class TestCaseIssuesController { public List getIssues(@PathVariable String id) { return issuesService.getIssues(id); } + + @GetMapping("/auth/{platform}") + public void testAuth(@PathVariable String platform) { + issuesService.testAuth(platform); + } + } diff --git a/frontend/src/business/components/settings/organization/DefectManagement.vue b/frontend/src/business/components/settings/organization/DefectManagement.vue index b0a7842ea3..8b5b25ebac 100644 --- a/frontend/src/business/components/settings/organization/DefectManagement.vue +++ b/frontend/src/business/components/settings/organization/DefectManagement.vue @@ -19,10 +19,15 @@ + + +
+ {{$t('ldap.test_connect')}} + {{$t('commons.edit')}} {{$t('commons.save')}} 取消编辑 @@ -72,7 +77,8 @@ ], rules: { account: {required: true, message: this.$t('organization.input_api_account'), trigger: ['change', 'blur']}, - password: {required: true, message: this.$t('organization.input_api_password'), trigger: ['change', 'blur']} + password: {required: true, message: this.$t('organization.input_api_password'), trigger: ['change', 'blur']}, + url: {required: true, message: '请输入url', trigger: ['change', 'blur']} }, } }, @@ -91,6 +97,7 @@ let config = JSON.parse(data.configuration); this.$set(this.form, 'account', config.account); this.$set(this.form, 'password', config.password); + this.$set(this.form, 'url', config.url); } else { this.clear(); } @@ -138,7 +145,8 @@ let param = {}; let auth = { account: this.form.account, - password: this.form.password + password: this.form.password, + url: this.form.url }; param.organizationId = getCurrentUser().lastOrganizationId; param.platform = this.platform; @@ -172,6 +180,7 @@ let config = JSON.parse(data.configuration); this.$set(this.form, 'account', config.account); this.$set(this.form, 'password', config.password); + this.$set(this.form, 'url', config.url); } else { this.clear(); } @@ -180,9 +189,15 @@ clear() { this.$set(this.form, 'account', ''); this.$set(this.form, 'password', ''); + this.$set(this.form, 'url', ''); this.$nextTick(() => { this.$refs.form.clearValidate(); }); + }, + testConnection() { + this.$get("issues/auth/" + this.platform, () => { + this.$success("验证通过!"); + }); } } }