This commit is contained in:
fit2-zhao 2020-10-14 11:16:30 +08:00
commit b2f80fea78
13 changed files with 758 additions and 479 deletions

View File

@ -75,6 +75,11 @@ public class APITestController {
apiTestService.create(request, file, bodyFiles);
}
@PostMapping(value = "/create/merge", consumes = {"multipart/form-data"})
public void mergeCreate(@RequestPart("request") SaveAPITestRequest request, @RequestPart(value = "file") MultipartFile file, @RequestPart(value = "selectIds") List<String> selectIds) {
apiTestService.mergeCreate(request, file, selectIds);
}
@PostMapping(value = "/update", consumes = {"multipart/form-data"})
public void update(@RequestPart("request") SaveAPITestRequest request, @RequestPart(value = "file") MultipartFile file, @RequestPart(value = "files") List<MultipartFile> bodyFiles) {
checkownerService.checkApiTestOwner(request.getId());

View File

@ -83,15 +83,19 @@ public class APITestService {
}
public void create(SaveAPITestRequest request, MultipartFile file, List<MultipartFile> bodyFiles) {
List<String> bodyUploadIds = new ArrayList<>(request.getBodyUploadIds());
ApiTest test = createTest(request, file);
createBodyFiles(test, bodyUploadIds, bodyFiles);
}
private ApiTest createTest(SaveAPITestRequest request, MultipartFile file) {
if (file == null) {
throw new IllegalArgumentException(Translator.get("file_cannot_be_null"));
}
checkQuota();
List<String> bodyUploadIds = new ArrayList<>(request.getBodyUploadIds());
request.setBodyUploadIds(null);
ApiTest test = createTest(request);
createBodyFiles(test, bodyUploadIds, bodyFiles);
saveFile(test.getId(), file);
return test;
}
public void update(SaveAPITestRequest request, MultipartFile file, List<MultipartFile> bodyFiles) {
@ -108,6 +112,9 @@ public class APITestService {
}
private void createBodyFiles(ApiTest test, List<String> bodyUploadIds, List<MultipartFile> bodyFiles) {
if (bodyUploadIds.size() <= 0) {
return;
}
String dir = BODY_FILE_DIR + "/" + test.getId();
File testDir = new File(dir);
if (!testDir.exists()) {
@ -436,4 +443,11 @@ public class APITestService {
quotaService.checkAPITestQuota();
}
}
public void mergeCreate(SaveAPITestRequest request, MultipartFile file, List<String> selectIds) {
ApiTest test = createTest(request, file);
selectIds.forEach(sourceId -> {
copyBodyFiles(test.getId(), sourceId);
});
}
}

View File

@ -1,7 +1,7 @@
package io.metersphere.track.controller;
import io.metersphere.base.domain.Issues;
import io.metersphere.track.domain.TapdUser;
import io.metersphere.track.issue.PlatformUser;
import io.metersphere.track.service.IssuesService;
import io.metersphere.track.request.testcase.IssuesRequest;
import org.springframework.web.bind.annotation.*;
@ -42,7 +42,7 @@ public class TestCaseIssuesController {
}
@GetMapping("/tapd/user/{caseId}")
public List<TapdUser> getTapdUsers(@PathVariable String caseId) {
public List<PlatformUser> getPlatformUsers(@PathVariable String caseId) {
return issuesService.getTapdProjectUsers(caseId);
}

View File

@ -0,0 +1,77 @@
package io.metersphere.track.issue;
import io.metersphere.base.domain.ServiceIntegration;
import io.metersphere.base.mapper.IssuesMapper;
import io.metersphere.base.mapper.TestCaseIssuesMapper;
import io.metersphere.base.mapper.ext.ExtIssuesMapper;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.user.SessionUser;
import io.metersphere.commons.utils.CommonBeanFactory;
import io.metersphere.commons.utils.EncryptUtils;
import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.controller.request.IntegrationRequest;
import io.metersphere.service.IntegrationService;
import io.metersphere.service.ProjectService;
import io.metersphere.track.request.testcase.IssuesRequest;
import io.metersphere.track.service.TestCaseService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpHeaders;
public abstract class AbstractIssuePlatform implements IssuesPlatform {
protected IntegrationService integrationService;
protected TestCaseIssuesMapper testCaseIssuesMapper;
protected ProjectService projectService;
protected TestCaseService testCaseService;
protected IssuesMapper issuesMapper;
protected ExtIssuesMapper extIssuesMapper;
protected String testCaseId;
public AbstractIssuePlatform(IssuesRequest issuesRequest) {
this.integrationService = CommonBeanFactory.getBean(IntegrationService.class);
this.testCaseIssuesMapper = CommonBeanFactory.getBean(TestCaseIssuesMapper.class);
this.projectService = CommonBeanFactory.getBean(ProjectService.class);
this.testCaseService = CommonBeanFactory.getBean(TestCaseService.class);
this.issuesMapper = CommonBeanFactory.getBean(IssuesMapper.class);
this.extIssuesMapper = CommonBeanFactory.getBean(ExtIssuesMapper.class);
this.testCaseId = issuesRequest.getTestCaseId();
}
protected String getPlatformConfig(String platform) {
SessionUser user = SessionUtils.getUser();
String orgId = user.getLastOrganizationId();
IntegrationRequest request = new IntegrationRequest();
if (StringUtils.isBlank(orgId)) {
MSException.throwException("organization id is null");
}
request.setOrgId(orgId);
request.setPlatform(platform);
ServiceIntegration integration = integrationService.get(request);
return integration.getConfiguration();
}
protected HttpHeaders auth(String apiUser, String password) {
String authKey = EncryptUtils.base64Encoding(apiUser + ":" + password);
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Basic " + authKey);
return headers;
}
/**
* 获取平台与项目相关的属性
* @return
*/
abstract String getProjectId();
protected boolean isIntegratedPlatform(String orgId, String platform) {
IntegrationRequest request = new IntegrationRequest();
request.setPlatform(platform);
request.setOrgId(orgId);
ServiceIntegration integration = integrationService.get(request);
return StringUtils.isNotBlank(integration.getId());
}
}

View File

@ -0,0 +1,32 @@
package io.metersphere.track.issue;
import io.metersphere.commons.constants.IssuesManagePlatform;
import io.metersphere.track.request.testcase.IssuesRequest;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.List;
public class IssueFactory {
public static AbstractIssuePlatform createPlatform(String platform, IssuesRequest addIssueRequest) {
if (StringUtils.equals(IssuesManagePlatform.Tapd.toString(), platform)) {
return new TapdIssue(addIssueRequest);
} else if (StringUtils.equals(IssuesManagePlatform.Jira.toString(), platform)) {
return new JiraIssue(addIssueRequest);
} else if (StringUtils.equals("LOCAL", platform)) {
return new LocalIssue(addIssueRequest);
}
return null;
}
public static List<AbstractIssuePlatform> createPlatforms(List<String> types, IssuesRequest addIssueRequest) {
List<AbstractIssuePlatform> platforms = new ArrayList<>();
types.forEach(type -> {
AbstractIssuePlatform abstractIssuePlatform = createPlatform(type, addIssueRequest);
if (abstractIssuePlatform != null) {
platforms.add(abstractIssuePlatform);
}
});
return platforms;
}
}

View File

@ -0,0 +1,39 @@
package io.metersphere.track.issue;
import io.metersphere.base.domain.Issues;
import io.metersphere.track.request.testcase.IssuesRequest;
import java.util.List;
public interface IssuesPlatform {
/**
* 获取平台相关联的缺陷
* @return
*/
List<Issues> getIssue();
/**
* 添加缺陷到缺陷平台
* @param issuesRequest
*/
void addIssue(IssuesRequest issuesRequest);
/**
* 删除缺陷平台缺陷
* @param id
*/
void deleteIssue(String id);
/**
* 测试缺陷平台连通性
* @param
*/
void testAuth();
/**
* 获取缺陷平台项目下的相关人员
* @return
*/
List<PlatformUser> getPlatformUser();
}

View File

@ -0,0 +1,264 @@
package io.metersphere.track.issue;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import io.metersphere.base.domain.*;
import io.metersphere.commons.constants.IssuesManagePlatform;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.EncryptUtils;
import io.metersphere.commons.utils.LogUtil;
import io.metersphere.track.request.testcase.IssuesRequest;
import org.apache.commons.lang3.StringUtils;
import org.commonmark.node.Node;
import org.commonmark.parser.Parser;
import org.commonmark.renderer.html.HtmlRenderer;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.safety.Whitelist;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestTemplate;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
public class JiraIssue extends AbstractIssuePlatform {
public JiraIssue(IssuesRequest issuesRequest) {
super(issuesRequest);
}
@Override
public List<Issues> getIssue() {
List<Issues> list = new ArrayList<>();
String config = getPlatformConfig(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(testCaseId);
List<Issues> issues = extIssuesMapper.getIssues(testCaseId, IssuesManagePlatform.Jira.toString());
List<String> issuesIds = issues.stream().map(Issues::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(testCaseId)
.andIssuesIdEqualTo(issuesId);
testCaseIssuesMapper.deleteByExample(issuesExample);
issuesMapper.deleteByPrimaryKey(issuesId);
} else {
// 缺陷状态为 完成则不显示
if (!StringUtils.equals("done", dto.getStatus())) {
list.add(dto);
}
}
});
return list;
}
@Override
public void addIssue(IssuesRequest issuesRequest) {
String config = getPlatformConfig(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 issuetype = object.getString("issuetype");
if (StringUtils.isBlank(issuetype)) {
MSException.throwException("Jira 问题类型为空");
}
String auth = EncryptUtils.base64Encoding(account + ":" + password);
String testCaseId = issuesRequest.getTestCaseId();
String jiraKey = getProjectId();
if (StringUtils.isBlank(jiraKey)) {
MSException.throwException("未关联Jira 项目Key");
}
String content = issuesRequest.getContent();
Document document = Jsoup.parse(content);
document.outputSettings(new Document.OutputSettings().prettyPrint(false));
document.select("br").append("\\n");
document.select("p").prepend("\\n\\n");
String s = document.html().replaceAll("\\\\n", "\n");
String desc = Jsoup.clean(s, "", Whitelist.none(), new Document.OutputSettings().prettyPrint(false));
desc = desc.replace("&nbsp;", "");
String json = "{\n" +
" \"fields\":{\n" +
" \"project\":{\n" +
" \"key\":\"" + jiraKey + "\"\n" +
" },\n" +
" \"summary\":\"" + issuesRequest.getTitle() + "\",\n" +
" \"description\": " + JSON.toJSONString(desc) + ",\n" +
" \"issuetype\":{\n" +
" \"name\":\"" + issuetype + "\"\n" +
" }\n" +
" }\n" +
"}";
String result = addJiraIssue(url, auth, json);
JSONObject jsonObject = JSON.parseObject(result);
String id = jsonObject.getString("key");
// 用例与第三方缺陷平台中的缺陷关联
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<String> requestEntity = new HttpEntity<>(json, requestHeaders);
RestTemplate restTemplate = new RestTemplate();
//post
ResponseEntity<String> responseEntity = null;
try {
responseEntity = restTemplate.exchange(url + "/rest/api/2/issue", HttpMethod.POST, requestEntity, String.class);
} catch (Exception e) {
LogUtil.error(e.getMessage(), e);
MSException.throwException("调用Jira接口创建缺陷失败");
}
return responseEntity.getBody();
}
@Override
public void deleteIssue(String id) {
}
@Override
public void testAuth() {
try {
String config = getPlatformConfig(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<MultiValueMap> 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("验证失败!");
}
}
@Override
public List<PlatformUser> getPlatformUser() {
return null;
}
@Override
String getProjectId() {
TestCaseWithBLOBs testCase = testCaseService.getTestCase(testCaseId);
Project project = projectService.getProjectById(testCase.getProjectId());
return project.getJiraKey();
}
private Issues getJiraIssues(HttpHeaders headers, String url, String issuesId) {
HttpEntity<MultiValueMap> requestEntity = new HttpEntity<>(headers);
RestTemplate restTemplate = new RestTemplate();
//post
ResponseEntity<String> responseEntity;
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);
LogUtil.info(obj);
String lastmodify = "";
String status = "";
JSONObject fields = (JSONObject) obj.get("fields");
JSONObject statusObj = (JSONObject) fields.get("status");
JSONObject assignee = (JSONObject) fields.get("assignee");
if (statusObj != null) {
JSONObject statusCategory = (JSONObject) statusObj.get("statusCategory");
status = statusCategory.getString("key");
}
String id = obj.getString("key");
String title = fields.getString("summary");
String description = fields.getString("description");
Parser parser = Parser.builder().build();
Node document = parser.parse(description);
HtmlRenderer renderer = HtmlRenderer.builder().build();
description = renderer.render(document);
Long createTime = fields.getLong("created");
if (assignee != null) {
lastmodify = assignee.getString("displayName");
}
issues.setId(id);
issues.setTitle(title);
issues.setCreateTime(createTime);
issues.setLastmodify(lastmodify);
issues.setDescription(description);
issues.setStatus(status);
issues.setPlatform(IssuesManagePlatform.Jira.toString());
} catch (HttpClientErrorException.NotFound e) {
LogUtil.error(e.getStackTrace(), e);
return new Issues();
} catch (HttpClientErrorException.Unauthorized e) {
LogUtil.error(e.getStackTrace(), e);
MSException.throwException("获取Jira缺陷失败检查Jira配置信息");
} catch (Exception e) {
LogUtil.error(e.getMessage(), e);
MSException.throwException("调用Jira接口获取缺陷失败");
}
return issues;
}
}

View File

@ -0,0 +1,72 @@
package io.metersphere.track.issue;
import io.metersphere.base.domain.Issues;
import io.metersphere.base.domain.TestCaseIssues;
import io.metersphere.commons.constants.IssuesManagePlatform;
import io.metersphere.commons.user.SessionUser;
import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.track.request.testcase.IssuesRequest;
import java.util.List;
import java.util.UUID;
public class LocalIssue extends AbstractIssuePlatform {
public LocalIssue(IssuesRequest issuesRequest) {
super(issuesRequest);
}
@Override
public List<Issues> getIssue() {
return extIssuesMapper.getIssues(testCaseId, IssuesManagePlatform.Local.toString());
}
@Override
public void addIssue(IssuesRequest issuesRequest) {
SessionUser user = SessionUtils.getUser();
String id = UUID.randomUUID().toString();
Issues issues = new Issues();
issues.setId(id);
issues.setStatus("new");
issues.setReporter(user.getId());
issues.setTitle(issuesRequest.getTitle());
issues.setDescription(issuesRequest.getContent());
issues.setCreateTime(System.currentTimeMillis());
issues.setUpdateTime(System.currentTimeMillis());
issues.setPlatform(IssuesManagePlatform.Local.toString());
issuesMapper.insert(issues);
TestCaseIssues testCaseIssues = new TestCaseIssues();
testCaseIssues.setId(UUID.randomUUID().toString());
testCaseIssues.setIssuesId(id);
testCaseIssues.setTestCaseId(issuesRequest.getTestCaseId());
testCaseIssuesMapper.insert(testCaseIssues);
}
@Override
public void deleteIssue(String id) {
issuesMapper.deleteByPrimaryKey(id);
}
@Override
public void testAuth() {
}
@Override
public List<PlatformUser> getPlatformUser() {
return null;
}
@Override
String getProjectId() {
return null;
}
public void closeIssue(String issueId) {
Issues issues = new Issues();
issues.setId(issueId);
issues.setStatus("closed");
issuesMapper.updateByPrimaryKeySelective(issues);
}
}

View File

@ -0,0 +1,12 @@
package io.metersphere.track.issue;
import lombok.Data;
import java.util.List;
@Data
public class PlatformUser {
private List<String> roleId;
private String name;
private String user;
}

View File

@ -0,0 +1,200 @@
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.commons.constants.IssuesManagePlatform;
import io.metersphere.commons.exception.MSException;
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.track.request.testcase.IssuesRequest;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
public class TapdIssue extends AbstractIssuePlatform {
public TapdIssue(IssuesRequest issueRequest) {
super(issueRequest);
}
@Override
public List<Issues> getIssue() {
List<Issues> list = new ArrayList<>();
String tapdId = getProjectId();
TestCaseIssuesExample example = new TestCaseIssuesExample();
example.createCriteria().andTestCaseIdEqualTo(testCaseId);
List<Issues> issues = extIssuesMapper.getIssues(testCaseId, IssuesManagePlatform.Tapd.toString());
List<String> issuesIds = issues.stream().map(Issues::getId).collect(Collectors.toList());
issuesIds.forEach(issuesId -> {
Issues dto = getTapdIssues(tapdId, issuesId);
if (StringUtils.isBlank(dto.getId())) {
// 缺陷不存在解除用例和缺陷的关联
TestCaseIssuesExample issuesExample = new TestCaseIssuesExample();
issuesExample.createCriteria()
.andTestCaseIdEqualTo(testCaseId)
.andIssuesIdEqualTo(issuesId);
testCaseIssuesMapper.deleteByExample(issuesExample);
issuesMapper.deleteByPrimaryKey(issuesId);
} else {
dto.setPlatform(IssuesManagePlatform.Tapd.toString());
// 缺陷状态为 关闭则不显示
if (!StringUtils.equals("closed", dto.getStatus())) {
list.add(dto);
}
}
});
return list;
}
private Issues getTapdIssues(String projectId, String issuesId) {
String url = "https://api.tapd.cn/bugs?workspace_id=" + projectId + "&id=" + issuesId;
ResultHolder call = call(url);
String listJson = JSON.toJSONString(call.getData());
if (StringUtils.equals(Boolean.FALSE.toString(), listJson)) {
return new Issues();
}
JSONObject jsonObject = JSONObject.parseObject(listJson);
JSONObject bug = jsonObject.getJSONObject("Bug");
Long created = bug.getLong("created");
Issues issues = jsonObject.getObject("Bug", Issues.class);
issues.setCreateTime(created);
return issues;
}
@Override
public void addIssue(IssuesRequest issuesRequest) {
String url = "https://api.tapd.cn/bugs";
String testCaseId = issuesRequest.getTestCaseId();
String tapdId = getProjectId();
if (StringUtils.isBlank(tapdId)) {
MSException.throwException("未关联Tapd 项目ID");
}
List<String> PlatformUsers = issuesRequest.getTapdUsers();
String usersStr = String.join(";", PlatformUsers);
String username = SessionUtils.getUser().getName();
MultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<>();
paramMap.add("title", issuesRequest.getTitle());
paramMap.add("workspace_id", tapdId);
paramMap.add("description", issuesRequest.getContent());
paramMap.add("reporter", username);
paramMap.add("current_owner", usersStr);
ResultHolder result = call(url, HttpMethod.POST, paramMap);
String listJson = JSON.toJSONString(result.getData());
JSONObject jsonObject = JSONObject.parseObject(listJson);
String issuesId = jsonObject.getObject("Bug", Issues.class).getId();
// 用例与第三方缺陷平台中的缺陷关联
TestCaseIssues testCaseIssues = new TestCaseIssues();
testCaseIssues.setId(UUID.randomUUID().toString());
testCaseIssues.setIssuesId(issuesId);
testCaseIssues.setTestCaseId(testCaseId);
testCaseIssuesMapper.insert(testCaseIssues);
// 插入缺陷表
Issues issues = new Issues();
issues.setId(issuesId);
issues.setPlatform(IssuesManagePlatform.Tapd.toString());
issuesMapper.insert(issues);
}
@Override
public void deleteIssue(String id) {}
@Override
public void testAuth() {
try {
String tapdConfig = getPlatformConfig(IssuesManagePlatform.Tapd.toString());
JSONObject object = JSON.parseObject(tapdConfig);
String account = object.getString("account");
String password = object.getString("password");
HttpHeaders headers = auth(account, password);
HttpEntity<MultiValueMap> requestEntity = new HttpEntity<>(headers);
RestTemplate restTemplate = new RestTemplate();
restTemplate.exchange("https://api.tapd.cn/quickstart/testauth", HttpMethod.GET, requestEntity, String.class);
} catch (Exception e) {
LogUtil.error(e.getMessage(), e);
MSException.throwException("验证失败!");
}
}
@Override
public List<PlatformUser> getPlatformUser() {
List<PlatformUser> users = new ArrayList<>();
String projectId = getProjectId();
String url = "https://api.tapd.cn/workspaces/users?workspace_id=" + projectId;
ResultHolder call = call(url);
String listJson = JSON.toJSONString(call.getData());
JSONArray jsonArray = JSON.parseArray(listJson);
for (int i = 0; i < jsonArray.size(); i++) {
JSONObject o = jsonArray.getJSONObject(i);
PlatformUser user = o.getObject("UserWorkspace", PlatformUser.class);
users.add(user);
}
return users;
}
@Override
String getProjectId() {
TestCaseWithBLOBs testCase = testCaseService.getTestCase(testCaseId);
Project project = projectService.getProjectById(testCase.getProjectId());
return project.getTapdId();
}
private ResultHolder call(String url) {
return call(url, HttpMethod.GET, null);
}
private ResultHolder call(String url, HttpMethod httpMethod, Object params) {
String responseJson;
String config = getPlatformConfig(IssuesManagePlatform.Tapd.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");
HttpHeaders header = auth(account, password);
if (httpMethod.equals(HttpMethod.GET)) {
responseJson = RestTemplateUtils.get(url, header);
} else {
responseJson = RestTemplateUtils.post(url, params, header);
}
ResultHolder result = JSON.parseObject(responseJson, ResultHolder.class);
if (!result.isSuccess()) {
MSException.throwException(result.getMessage());
}
return JSON.parseObject(responseJson, ResultHolder.class);
}
}

View File

@ -1,45 +1,23 @@
package io.metersphere.track.service;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import io.metersphere.base.domain.*;
import io.metersphere.base.mapper.IssuesMapper;
import io.metersphere.base.mapper.TestCaseIssuesMapper;
import io.metersphere.base.mapper.ext.ExtIssuesMapper;
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.service.IntegrationService;
import io.metersphere.service.ProjectService;
import io.metersphere.track.domain.TapdUser;
import io.metersphere.track.issue.AbstractIssuePlatform;
import io.metersphere.track.issue.IssueFactory;
import io.metersphere.track.issue.PlatformUser;
import io.metersphere.track.request.testcase.IssuesRequest;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.safety.Whitelist;
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.HttpClientErrorException;
import org.springframework.web.client.RestTemplate;
import org.commonmark.node.*;
import org.commonmark.parser.Parser;
import org.commonmark.renderer.html.HtmlRenderer;
import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;
@Service
@Transactional(rollbackFor = Exception.class)
@ -48,108 +26,16 @@ public class IssuesService {
@Resource
private IntegrationService integrationService;
@Resource
private TestCaseIssuesMapper testCaseIssuesMapper;
@Resource
private ProjectService projectService;
@Resource
private TestCaseService testCaseService;
@Resource
private IssuesMapper issuesMapper;
@Resource
private ExtIssuesMapper extIssuesMapper;
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<MultiValueMap> requestEntity = new HttpEntity<>(headers);
RestTemplate restTemplate = new RestTemplate();
restTemplate.exchange("https://api.tapd.cn/quickstart/testauth", HttpMethod.GET, requestEntity, String.class);
} catch (Exception 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<MultiValueMap> 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) {
return call(url, HttpMethod.GET, null);
}
private ResultHolder call(String url, HttpMethod httpMethod, Object params) {
String responseJson;
String config = platformConfig(IssuesManagePlatform.Tapd.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");
HttpHeaders header = auth(account, password);
if (httpMethod.equals(HttpMethod.GET)) {
responseJson = RestTemplateUtils.get(url, header);
} else {
responseJson = RestTemplateUtils.post(url, params, header);
}
ResultHolder result = JSON.parseObject(responseJson, ResultHolder.class);
if (!result.isSuccess()) {
MSException.throwException(result.getMessage());
}
return JSON.parseObject(responseJson, ResultHolder.class);
}
private String platformConfig(String platform) {
SessionUser user = SessionUtils.getUser();
String orgId = user.getLastOrganizationId();
IntegrationRequest request = new IntegrationRequest();
if (StringUtils.isBlank(orgId)) {
MSException.throwException("organization id is null");
}
request.setOrgId(orgId);
request.setPlatform(platform);
ServiceIntegration integration = integrationService.get(request);
return integration.getConfiguration();
}
private HttpHeaders auth(String apiUser, String password) {
String authKey = EncryptUtils.base64Encoding(apiUser + ":" + password);
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Basic " + authKey);
return headers;
AbstractIssuePlatform abstractPlatform = IssueFactory.createPlatform(platform, new IssuesRequest());
abstractPlatform.testAuth();
}
public void addIssues(IssuesRequest issuesRequest) {
@ -162,247 +48,29 @@ public class IssuesService {
String tapdId = getTapdProjectId(issuesRequest.getTestCaseId());
String jiraKey = getJiraProjectKey(issuesRequest.getTestCaseId());
List<String> platforms = new ArrayList<>();
if (tapd) {
// 是否关联了项目
if (StringUtils.isNotBlank(tapdId)) {
addTapdIssues(issuesRequest);
platforms.add(IssuesManagePlatform.Tapd.name());
}
}
if (jira) {
if (StringUtils.isNotBlank(jiraKey)) {
addJiraIssues(issuesRequest);
platforms.add(IssuesManagePlatform.Jira.name());
}
}
if (StringUtils.isBlank(tapdId) && StringUtils.isBlank(jiraKey)) {
addLocalIssues(issuesRequest);
platforms.add("LOCAL");
}
}
public void addTapdIssues(IssuesRequest issuesRequest) {
String url = "https://api.tapd.cn/bugs";
String testCaseId = issuesRequest.getTestCaseId();
String tapdId = getTapdProjectId(testCaseId);
if (StringUtils.isBlank(tapdId)) {
MSException.throwException("未关联Tapd 项目ID");
}
List<String> tapdUsers = issuesRequest.getTapdUsers();
String usersStr = String.join(";", tapdUsers);
String username = SessionUtils.getUser().getName();
MultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<>();
paramMap.add("title", issuesRequest.getTitle());
paramMap.add("workspace_id", tapdId);
paramMap.add("description", issuesRequest.getContent());
paramMap.add("reporter", username);
paramMap.add("current_owner", usersStr);
ResultHolder result = call(url, HttpMethod.POST, paramMap);
String listJson = JSON.toJSONString(result.getData());
JSONObject jsonObject = JSONObject.parseObject(listJson);
String issuesId = jsonObject.getObject("Bug", Issues.class).getId();
// 用例与第三方缺陷平台中的缺陷关联
TestCaseIssues testCaseIssues = new TestCaseIssues();
testCaseIssues.setId(UUID.randomUUID().toString());
testCaseIssues.setIssuesId(issuesId);
testCaseIssues.setTestCaseId(testCaseId);
testCaseIssuesMapper.insert(testCaseIssues);
// 插入缺陷表
Issues issues = new Issues();
issues.setId(issuesId);
issues.setPlatform(IssuesManagePlatform.Tapd.toString());
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 issuetype = object.getString("issuetype");
if (StringUtils.isBlank(issuetype)) {
MSException.throwException("Jira 问题类型为空");
}
String auth = EncryptUtils.base64Encoding(account + ":" + password);
String testCaseId = issuesRequest.getTestCaseId();
String jiraKey = getJiraProjectKey(testCaseId);
if (StringUtils.isBlank(jiraKey)) {
MSException.throwException("未关联Jira 项目Key");
}
String content = issuesRequest.getContent();
Document document = Jsoup.parse(content);
document.outputSettings(new Document.OutputSettings().prettyPrint(false));
document.select("br").append("\\n");
document.select("p").prepend("\\n\\n");
String s = document.html().replaceAll("\\\\n", "\n");
String desc = Jsoup.clean(s, "", Whitelist.none(), new Document.OutputSettings().prettyPrint(false));
desc = desc.replace("&nbsp;", "");
String json = "{\n" +
" \"fields\":{\n" +
" \"project\":{\n" +
" \"key\":\"" + jiraKey + "\"\n" +
" },\n" +
" \"summary\":\"" + issuesRequest.getTitle() + "\",\n" +
" \"description\": " + JSON.toJSONString(desc) + ",\n" +
" \"issuetype\":{\n" +
" \"name\":\"" + issuetype + "\"\n" +
" }\n" +
" }\n" +
"}";
String result = addJiraIssue(url, auth, json);
JSONObject jsonObject = JSON.parseObject(result);
String id = jsonObject.getString("key");
// 用例与第三方缺陷平台中的缺陷关联
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<String> requestEntity = new HttpEntity<>(json, requestHeaders);
RestTemplate restTemplate = new RestTemplate();
//post
ResponseEntity<String> responseEntity = null;
try {
responseEntity = restTemplate.exchange(url + "/rest/api/2/issue", HttpMethod.POST, requestEntity, String.class);
} catch (Exception e) {
LogUtil.error(e.getMessage(), e);
MSException.throwException("调用Jira接口创建缺陷失败");
}
return responseEntity.getBody();
}
public void addLocalIssues(IssuesRequest request) {
SessionUser user = SessionUtils.getUser();
String id = UUID.randomUUID().toString();
Issues issues = new Issues();
issues.setId(id);
issues.setStatus("new");
issues.setReporter(user.getId());
issues.setTitle(request.getTitle());
issues.setDescription(request.getContent());
issues.setCreateTime(System.currentTimeMillis());
issues.setUpdateTime(System.currentTimeMillis());
issues.setPlatform(IssuesManagePlatform.Local.toString());
issuesMapper.insert(issues);
TestCaseIssues testCaseIssues = new TestCaseIssues();
testCaseIssues.setId(UUID.randomUUID().toString());
testCaseIssues.setIssuesId(id);
testCaseIssues.setTestCaseId(request.getTestCaseId());
testCaseIssuesMapper.insert(testCaseIssues);
}
public Issues getTapdIssues(String projectId, String issuesId) {
String url = "https://api.tapd.cn/bugs?workspace_id=" + projectId + "&id=" + issuesId;
ResultHolder call = call(url);
String listJson = JSON.toJSONString(call.getData());
if (StringUtils.equals(Boolean.FALSE.toString(), listJson)) {
return new Issues();
}
JSONObject jsonObject = JSONObject.parseObject(listJson);
JSONObject bug = jsonObject.getJSONObject("Bug");
Long created = bug.getLong("created");
Issues issues = jsonObject.getObject("Bug", Issues.class);
issues.setCreateTime(created);
return issues;
}
public Issues getJiraIssues(HttpHeaders headers, String url, String issuesId) {
HttpEntity<MultiValueMap> requestEntity = new HttpEntity<>(headers);
RestTemplate restTemplate = new RestTemplate();
//post
ResponseEntity<String> responseEntity;
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);
LogUtil.info(obj);
String lastmodify = "";
String status = "";
JSONObject fields = (JSONObject) obj.get("fields");
JSONObject statusObj = (JSONObject) fields.get("status");
JSONObject assignee = (JSONObject) fields.get("assignee");
if (statusObj != null) {
JSONObject statusCategory = (JSONObject) statusObj.get("statusCategory");
status = statusCategory.getString("key");
}
String id = obj.getString("key");
String title = fields.getString("summary");
String description = fields.getString("description");
Parser parser = Parser.builder().build();
Node document = parser.parse(description);
HtmlRenderer renderer = HtmlRenderer.builder().build();
description = renderer.render(document);
Long createTime = fields.getLong("created");
if (assignee != null) {
lastmodify = assignee.getString("displayName");
}
issues.setId(id);
issues.setTitle(title);
issues.setCreateTime(createTime);
issues.setLastmodify(lastmodify);
issues.setDescription(description);
issues.setStatus(status);
issues.setPlatform(IssuesManagePlatform.Jira.toString());
} catch (HttpClientErrorException.NotFound e) {
LogUtil.error(e.getStackTrace(), e);
return new Issues();
} catch (HttpClientErrorException.Unauthorized e) {
LogUtil.error(e.getStackTrace(), e);
MSException.throwException("获取Jira缺陷失败检查Jira配置信息");
} catch (Exception e) {
LogUtil.error(e.getMessage(), e);
MSException.throwException("调用Jira接口获取缺陷失败");
}
return issues;
List<AbstractIssuePlatform> platformList = IssueFactory.createPlatforms(platforms, issuesRequest);
platformList.forEach(platform -> {
platform.addIssue(issuesRequest);
});
}
@ -414,11 +82,12 @@ public class IssuesService {
boolean tapd = isIntegratedPlatform(orgId, IssuesManagePlatform.Tapd.toString());
boolean jira = isIntegratedPlatform(orgId, IssuesManagePlatform.Jira.toString());
List<String> platforms = new ArrayList<>();
if (tapd) {
// 是否关联了项目
String tapdId = getTapdProjectId(caseId);
if (StringUtils.isNotBlank(tapdId)) {
list.addAll(getTapdIssues(caseId));
platforms.add(IssuesManagePlatform.Tapd.name());
}
}
@ -426,90 +95,22 @@ public class IssuesService {
if (jira) {
String jiraKey = getJiraProjectKey(caseId);
if (StringUtils.isNotBlank(jiraKey)) {
list.addAll(getJiraIssues(caseId));
platforms.add(IssuesManagePlatform.Jira.name());
}
}
list.addAll(getLocalIssues(caseId));
return list;
}
public List<Issues> getTapdIssues(String caseId) {
List<Issues> list = new ArrayList<>();
String tapdId = getTapdProjectId(caseId);
TestCaseIssuesExample example = new TestCaseIssuesExample();
example.createCriteria().andTestCaseIdEqualTo(caseId);
List<Issues> issues = extIssuesMapper.getIssues(caseId, IssuesManagePlatform.Tapd.toString());
List<String> issuesIds = issues.stream().map(Issues::getId).collect(Collectors.toList());
issuesIds.forEach(issuesId -> {
Issues dto = getTapdIssues(tapdId, issuesId);
if (StringUtils.isBlank(dto.getId())) {
// 缺陷不存在解除用例和缺陷的关联
TestCaseIssuesExample issuesExample = new TestCaseIssuesExample();
issuesExample.createCriteria()
.andTestCaseIdEqualTo(caseId)
.andIssuesIdEqualTo(issuesId);
testCaseIssuesMapper.deleteByExample(issuesExample);
issuesMapper.deleteByPrimaryKey(issuesId);
} else {
dto.setPlatform(IssuesManagePlatform.Tapd.toString());
// 缺陷状态为 关闭则不显示
if (!StringUtils.equals("closed", dto.getStatus())) {
list.add(dto);
}
}
platforms.add("LOCAL");
IssuesRequest issueRequest = new IssuesRequest();
issueRequest.setTestCaseId(caseId);
List<AbstractIssuePlatform> platformList = IssueFactory.createPlatforms(platforms, issueRequest);
platformList.forEach(platform -> {
List<Issues> issue = platform.getIssue();
list.addAll(issue);
});
return list;
}
public List<Issues> getJiraIssues(String caseId) {
List<Issues> 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> issues = extIssuesMapper.getIssues(caseId, IssuesManagePlatform.Jira.toString());
List<String> issuesIds = issues.stream().map(Issues::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<Issues> getLocalIssues(String caseId) {
return extIssuesMapper.getIssues(caseId, IssuesManagePlatform.Local.toString());
}
public String getTapdProjectId(String testCaseId) {
TestCaseWithBLOBs testCase = testCaseService.getTestCase(testCaseId);
Project project = projectService.getProjectById(testCase.getProjectId());
@ -540,19 +141,11 @@ public class IssuesService {
issuesMapper.updateByPrimaryKeySelective(issues);
}
public List<TapdUser> getTapdProjectUsers(String caseId) {
List<TapdUser> users = new ArrayList<>();
String projectId = getTapdProjectId(caseId);
String url = "https://api.tapd.cn/workspaces/users?workspace_id=" + projectId;
ResultHolder call = call(url);
String listJson = JSON.toJSONString(call.getData());
JSONArray jsonArray = JSON.parseArray(listJson);
for (int i = 0; i < jsonArray.size(); i++) {
JSONObject o = jsonArray.getJSONObject(i);
TapdUser user = o.getObject("UserWorkspace", TapdUser.class);
users.add(user);
}
return users;
public List<PlatformUser> getTapdProjectUsers(String caseId) {
IssuesRequest issueRequest = new IssuesRequest();
issueRequest.setTestCaseId(caseId);
AbstractIssuePlatform platform = IssueFactory.createPlatform(IssuesManagePlatform.Tapd.name(), issueRequest);
return platform.getPlatformUser();
}
public void deleteIssue(String id) {

View File

@ -156,37 +156,11 @@
},
save(callback) {
this.change = false;
let url = "/api/create";
let bodyFiles = this.getBodyUploadFiles();
this.result = this.$request(this.getOptions(url, bodyFiles), () => {
let url = "/api/create/merge";
this.result = this.$request(this.getOptions(url, this.selectIds), () => {
if (callback) callback();
});
},
getBodyUploadFiles() {
let bodyUploadFiles = [];
this.test.bodyUploadIds = [];
this.test.scenarioDefinition.forEach(scenario => {
scenario.requests.forEach(request => {
if (request.body) {
request.body.kvs.forEach(param => {
if (param.files) {
param.files.forEach(item => {
if (item.file) {
let fileId = getUUID().substring(0, 8);
item.name = item.file.name;
item.id = fileId;
this.test.bodyUploadIds.push(fileId);
bodyUploadFiles.push(item.file);
// item.file = undefined;
}
});
}
});
}
});
});
return bodyUploadFiles;
},
runTest() {
this.result = this.$post("/api/run", {id: this.test.id, triggerMode: 'MANUAL'}, (response) => {
this.$success(this.$t('api_test.running'));
@ -196,16 +170,14 @@
this.test = ""
});
},
getOptions(url, bodyFiles) {
getOptions(url, selectIds) {
let formData = new FormData();
if (bodyFiles) {
bodyFiles.forEach(f => {
formData.append("files", f);
})
}
let requestJson = JSON.stringify(this.test);
formData.append('request', new Blob([requestJson], {
formData.append('request', new Blob([JSON.stringify(this.test)], {
type: "application/json"
}));
formData.append('selectIds', new Blob([JSON.stringify(Array.from(selectIds))], {
type: "application/json"
}));

View File

@ -45,7 +45,6 @@
width: 20px;
height: 25px;
line-height: 25px;
background-color: #FFF;
}
.show-more-btn-title {