fix(测试跟踪): Jira同步全量缺陷失败
This commit is contained in:
parent
a3ced967c4
commit
9449bf8243
|
@ -29,7 +29,7 @@
|
|||
<platform-account-config
|
||||
:config="config"
|
||||
:account-config="currentPlatformInfo"
|
||||
v-if="config.key === 'Jira'"
|
||||
v-if="showPlatformConfig(config.key)"
|
||||
/>
|
||||
</div>
|
||||
<tapd-user-info @auth="handleAuth" v-if="hasTapd" :data="currentPlatformInfo"/>
|
||||
|
@ -64,6 +64,7 @@ import {useUserStore} from "@/store";
|
|||
import {handleAuth as _handleAuth, getUserInfo, getWsAndPj, updateInfo} from "../../api/user";
|
||||
import PlatformAccountConfig from "./PlatformAccountConfig";
|
||||
import {getPlatformAccountInfo} from "../../api/platform-plugin";
|
||||
import {ISSUE_PLATFORM_OPTION} from "../../utils/table-constants";
|
||||
|
||||
|
||||
const userStore = useUserStore();
|
||||
|
@ -125,6 +126,9 @@ export default {
|
|||
currentUser: () => {
|
||||
return getCurrentUser();
|
||||
},
|
||||
showPlatformConfig(platform) {
|
||||
return ISSUE_PLATFORM_OPTION.map(item => item.value).indexOf(platform) < 0;
|
||||
},
|
||||
handleAuth(type) {
|
||||
let param = {...this.currentPlatformInfo};
|
||||
if (type === 'Zentao') {
|
||||
|
@ -246,6 +250,7 @@ export default {
|
|||
height: 40px;
|
||||
line-height: 40px;
|
||||
}
|
||||
|
||||
.el-form-item-class {
|
||||
margin-left: 110px;
|
||||
}
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
package io.metersphere.xpack.track.controller;
|
||||
|
||||
import io.metersphere.commons.utils.CommonBeanFactory;
|
||||
import io.metersphere.xpack.track.dto.IssueSyncRequest;
|
||||
import io.metersphere.xpack.track.service.XpackIssueService;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@RestController
|
||||
@RequestMapping(value = "/xpack/issue")
|
||||
public class XpackIssueController {
|
||||
|
||||
@PostMapping("/sync")
|
||||
public boolean getPlatformIssue(@RequestBody IssueSyncRequest request) {
|
||||
XpackIssueService xpackIssueService = CommonBeanFactory.getBean(XpackIssueService.class);
|
||||
return xpackIssueService.syncThirdPartyIssues(request);
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.request.attachment;
|
||||
package io.metersphere.xpack.track.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
|
@ -2,6 +2,10 @@ package io.metersphere.xpack.track.dto;
|
|||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author songcc
|
||||
*/
|
||||
|
@ -22,4 +26,8 @@ public class IssueSyncRequest {
|
|||
* TRUE: 创建时间之前
|
||||
*/
|
||||
private boolean pre;
|
||||
|
||||
private String defaultCustomFields;
|
||||
|
||||
private Map<String, List<io.metersphere.xpack.track.dto.PlatformAttachment>> attachmentMap = new HashMap<>();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
package io.metersphere.xpack.track.dto;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class PlatformAttachment {
|
||||
private String fileName;
|
||||
private String fileKey;
|
||||
}
|
|
@ -1,10 +1,11 @@
|
|||
package io.metersphere.xpack.track.service;
|
||||
|
||||
import io.metersphere.base.domain.Project;
|
||||
import io.metersphere.xpack.track.dto.IssueSyncRequest;
|
||||
|
||||
public interface XpackIssueService {
|
||||
|
||||
boolean syncThirdPartyIssues(IssueSyncRequest request);
|
||||
boolean syncThirdPartyIssues(Project project, IssueSyncRequest request);
|
||||
|
||||
void syncThirdPartyIssues();
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ package io.metersphere.controller;
|
|||
import io.metersphere.base.domain.FileAttachmentMetadata;
|
||||
import io.metersphere.metadata.service.FileMetadataService;
|
||||
import io.metersphere.request.attachment.AttachmentDumpRequest;
|
||||
import io.metersphere.request.attachment.AttachmentRequest;
|
||||
import io.metersphere.xpack.track.dto.AttachmentRequest;
|
||||
import io.metersphere.service.AttachmentService;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
|
|
|
@ -25,10 +25,7 @@ import io.metersphere.service.BaseCheckPermissionService;
|
|||
import io.metersphere.service.IssuesService;
|
||||
import io.metersphere.service.PlatformPluginService;
|
||||
import io.metersphere.service.issue.domain.zentao.ZentaoBuild;
|
||||
import io.metersphere.xpack.track.dto.IssueTemplateDao;
|
||||
import io.metersphere.xpack.track.dto.IssuesDao;
|
||||
import io.metersphere.xpack.track.dto.PlatformStatusDTO;
|
||||
import io.metersphere.xpack.track.dto.PlatformUser;
|
||||
import io.metersphere.xpack.track.dto.*;
|
||||
import io.metersphere.xpack.track.dto.request.IssuesRequest;
|
||||
import io.metersphere.xpack.track.dto.request.IssuesUpdateRequest;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
|
@ -164,10 +161,15 @@ public class IssuesController {
|
|||
}
|
||||
|
||||
@GetMapping("/sync/{projectId}")
|
||||
public boolean getPlatformIssue(@PathVariable String projectId) {
|
||||
public boolean syncThirdPartyIssues(@PathVariable String projectId) {
|
||||
return issuesService.syncThirdPartyIssues(projectId);
|
||||
}
|
||||
|
||||
@PostMapping("/sync/all")
|
||||
public boolean syncThirdPartyAllIssues(@RequestBody IssueSyncRequest request) {
|
||||
return issuesService.syncThirdPartyAllIssues(request);
|
||||
}
|
||||
|
||||
@GetMapping("/sync/check/{projectId}")
|
||||
public boolean checkSync(@PathVariable String projectId) {
|
||||
return issuesService.checkSync(projectId);
|
||||
|
|
|
@ -13,7 +13,7 @@ import io.metersphere.i18n.Translator;
|
|||
import io.metersphere.metadata.service.FileMetadataService;
|
||||
import io.metersphere.metadata.utils.MetadataUtils;
|
||||
import io.metersphere.platform.domain.SyncIssuesAttachmentRequest;
|
||||
import io.metersphere.request.attachment.AttachmentRequest;
|
||||
import io.metersphere.xpack.track.dto.AttachmentRequest;
|
||||
import io.metersphere.service.issue.platform.IssueFactory;
|
||||
import io.metersphere.xmind.utils.FileUtil;
|
||||
import io.metersphere.xpack.track.dto.AttachmentSyncType;
|
||||
|
|
|
@ -6,6 +6,7 @@ import io.metersphere.base.domain.CustomFieldIssues;
|
|||
import io.metersphere.base.domain.CustomFieldIssuesExample;
|
||||
import io.metersphere.base.mapper.CustomFieldIssuesMapper;
|
||||
import io.metersphere.base.mapper.ext.BaseCustomFieldResourceMapper;
|
||||
import io.metersphere.commons.utils.SubListUtil;
|
||||
import io.metersphere.constants.SystemCustomField;
|
||||
import io.metersphere.dto.CustomFieldDao;
|
||||
import io.metersphere.dto.CustomFieldResourceDTO;
|
||||
|
@ -102,9 +103,19 @@ public class CustomFieldIssuesService extends CustomFieldResourceService {
|
|||
}
|
||||
}
|
||||
}
|
||||
addList.forEach(l -> batchMapper.insert(TABLE_NAME, l));
|
||||
updateList.forEach(l -> batchMapper.updateByPrimaryKeySelective(TABLE_NAME, l));
|
||||
sqlSession.flushStatements();
|
||||
|
||||
int batchSize = 500;
|
||||
|
||||
SubListUtil.dealForSubList(addList, batchSize, (subList) -> {
|
||||
subList.forEach(l -> batchMapper.insert(TABLE_NAME, (CustomFieldResourceDTO) l));
|
||||
sqlSession.commit();
|
||||
});
|
||||
|
||||
SubListUtil.dealForSubList(updateList, batchSize, (subList) -> {
|
||||
subList.forEach(l -> batchMapper.updateByPrimaryKeySelective(TABLE_NAME, (CustomFieldResourceDTO) l));
|
||||
sqlSession.commit();
|
||||
});
|
||||
|
||||
if (sqlSession != null && sqlSessionFactory != null) {
|
||||
SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory);
|
||||
}
|
||||
|
|
|
@ -38,8 +38,9 @@ import io.metersphere.plan.service.TestPlanTestCaseService;
|
|||
import io.metersphere.plan.utils.TestPlanStatusCalculator;
|
||||
import io.metersphere.platform.api.Platform;
|
||||
import io.metersphere.platform.domain.*;
|
||||
import io.metersphere.platform.domain.PlatformAttachment;
|
||||
import io.metersphere.request.IntegrationRequest;
|
||||
import io.metersphere.request.attachment.AttachmentRequest;
|
||||
import io.metersphere.xpack.track.dto.AttachmentRequest;
|
||||
import io.metersphere.request.issues.IssueExportRequest;
|
||||
import io.metersphere.request.issues.IssueImportRequest;
|
||||
import io.metersphere.request.issues.PlatformIssueTypeRequest;
|
||||
|
@ -58,6 +59,7 @@ import io.metersphere.xpack.track.dto.*;
|
|||
import io.metersphere.xpack.track.dto.request.IssuesRequest;
|
||||
import io.metersphere.xpack.track.dto.request.IssuesUpdateRequest;
|
||||
import io.metersphere.xpack.track.issue.IssuesPlatform;
|
||||
import io.metersphere.xpack.track.service.XpackIssueService;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
|
@ -1005,7 +1007,7 @@ public class IssuesService {
|
|||
request.setIssues(platformIssues);
|
||||
request.setDefaultCustomFields(defaultCustomFields);
|
||||
request.setProjectConfig(PlatformPluginService.getCompatibleProjectConfig(project));
|
||||
Platform platform = platformPluginService.getPlatform(project.getPlatform());
|
||||
Platform platform = platformPluginService.getPlatform(project.getPlatform(), project.getWorkspaceId());
|
||||
|
||||
// 获取需要变更的缺陷
|
||||
SyncIssuesResult syncIssuesResult = platform.syncIssues(request);
|
||||
|
@ -1047,42 +1049,79 @@ public class IssuesService {
|
|||
}
|
||||
}
|
||||
|
||||
private void syncPluginIssueAttachment(Platform platform, SyncIssuesResult syncIssuesResult, AttachmentModuleRelationMapper batchAttachmentModuleRelationMapper) {
|
||||
private void syncPluginIssueAttachment(Platform platform, SyncIssuesResult syncIssuesResult,
|
||||
AttachmentModuleRelationMapper batchAttachmentModuleRelationMapper) {
|
||||
Map<String, List<PlatformAttachment>> attachmentMap = syncIssuesResult.getAttachmentMap();
|
||||
if (MapUtils.isNotEmpty(attachmentMap)) {
|
||||
for (String issueId : attachmentMap.keySet()) {
|
||||
// 查询我们平台的附件
|
||||
Set<String> jiraAttachmentSet = new HashSet<>();
|
||||
AttachmentRequest attachmentRequest = new AttachmentRequest();
|
||||
attachmentRequest.setBelongType(AttachmentType.ISSUE.type());
|
||||
attachmentRequest.setBelongId(issueId);
|
||||
List<FileAttachmentMetadata> allMsAttachments = attachmentService.listMetadata(attachmentRequest);
|
||||
List<FileAttachmentMetadata> allMsAttachments = getIssueFileAttachmentMetadata(issueId);
|
||||
Set<String> attachmentsNameSet = allMsAttachments.stream()
|
||||
.map(FileAttachmentMetadata::getName)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
List<PlatformAttachment> syncAttachments = attachmentMap.get(issueId);
|
||||
for (PlatformAttachment syncAttachment : syncAttachments) {
|
||||
if (!attachmentsNameSet.contains(syncAttachment.getFileName())) {
|
||||
jiraAttachmentSet.add(syncAttachment.getFileName());
|
||||
try {
|
||||
byte[] content = platform.getAttachmentContent(syncAttachment.getFileKey());
|
||||
if (content == null) {
|
||||
continue;
|
||||
String fileName = syncAttachment.getFileName();
|
||||
String fileKey = syncAttachment.getFileKey();
|
||||
if (!attachmentsNameSet.contains(fileName)) {
|
||||
jiraAttachmentSet.add(fileName);
|
||||
saveAttachmentModuleRelation(platform, issueId, fileName, fileKey, batchAttachmentModuleRelationMapper);
|
||||
}
|
||||
FileAttachmentMetadata fileAttachmentMetadata = attachmentService
|
||||
.saveAttachmentByBytes(content, AttachmentType.ISSUE.type(), issueId, syncAttachment.getFileName());
|
||||
AttachmentModuleRelation attachmentModuleRelation = new AttachmentModuleRelation();
|
||||
attachmentModuleRelation.setAttachmentId(fileAttachmentMetadata.getId());
|
||||
attachmentModuleRelation.setRelationId(issueId);
|
||||
attachmentModuleRelation.setRelationType(AttachmentType.ISSUE.type());
|
||||
batchAttachmentModuleRelationMapper.insert(attachmentModuleRelation);
|
||||
} catch (Exception e) {
|
||||
LogUtil.error(e);
|
||||
|
||||
}
|
||||
|
||||
// 删除Jira中不存在的附件
|
||||
deleteSyncAttachment(batchAttachmentModuleRelationMapper, jiraAttachmentSet, allMsAttachments);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void syncAllPluginIssueAttachment(Project project, IssueSyncRequest syncIssuesResult) {
|
||||
// todo 所有平台改造完之后删除
|
||||
if (!StringUtils.equals(project.getPlatform(), IssuesManagePlatform.Jira.name())) {
|
||||
return;
|
||||
}
|
||||
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
|
||||
try {
|
||||
AttachmentModuleRelationMapper batchAttachmentModuleRelationMapper = sqlSession.getMapper(AttachmentModuleRelationMapper.class);
|
||||
Platform platform = platformPluginService.getPlatform(project.getPlatform(), project.getWorkspaceId());
|
||||
Map<String, List<io.metersphere.xpack.track.dto.PlatformAttachment>> attachmentMap = syncIssuesResult.getAttachmentMap();
|
||||
if (MapUtils.isNotEmpty(attachmentMap)) {
|
||||
for (String issueId : attachmentMap.keySet()) {
|
||||
// 查询我们平台的附件
|
||||
Set<String> jiraAttachmentSet = new HashSet<>();
|
||||
List<FileAttachmentMetadata> allMsAttachments = getIssueFileAttachmentMetadata(issueId);
|
||||
Set<String> attachmentsNameSet = allMsAttachments.stream()
|
||||
.map(FileAttachmentMetadata::getName)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
List<io.metersphere.xpack.track.dto.PlatformAttachment> syncAttachments = attachmentMap.get(issueId);
|
||||
for (io.metersphere.xpack.track.dto.PlatformAttachment syncAttachment : syncAttachments) {
|
||||
String fileName = syncAttachment.getFileName();
|
||||
String fileKey = syncAttachment.getFileKey();
|
||||
if (!attachmentsNameSet.contains(fileName)) {
|
||||
jiraAttachmentSet.add(fileName);
|
||||
saveAttachmentModuleRelation(platform, issueId, fileName, fileKey, batchAttachmentModuleRelationMapper);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 删除Jira中不存在的附件
|
||||
deleteSyncAttachment(batchAttachmentModuleRelationMapper, jiraAttachmentSet, allMsAttachments);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LogUtil.error(e);
|
||||
} finally {
|
||||
SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory);
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteSyncAttachment(AttachmentModuleRelationMapper batchAttachmentModuleRelationMapper,
|
||||
Set<String> jiraAttachmentSet,
|
||||
List<FileAttachmentMetadata> allMsAttachments) {
|
||||
// 删除Jira中不存在的附件
|
||||
if (CollectionUtils.isNotEmpty(allMsAttachments)) {
|
||||
List<FileAttachmentMetadata> deleteMsAttachments = allMsAttachments.stream()
|
||||
|
@ -1099,7 +1138,34 @@ public class IssuesService {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void saveAttachmentModuleRelation(Platform platform, String issueId,
|
||||
String fileName, String fileKey,
|
||||
AttachmentModuleRelationMapper batchAttachmentModuleRelationMapper) {
|
||||
try {
|
||||
byte[] content = platform.getAttachmentContent(fileKey);
|
||||
if (content == null) {
|
||||
return;
|
||||
}
|
||||
FileAttachmentMetadata fileAttachmentMetadata = attachmentService
|
||||
.saveAttachmentByBytes(content, AttachmentType.ISSUE.type(), issueId, fileName);
|
||||
AttachmentModuleRelation attachmentModuleRelation = new AttachmentModuleRelation();
|
||||
attachmentModuleRelation.setAttachmentId(fileAttachmentMetadata.getId());
|
||||
attachmentModuleRelation.setRelationId(issueId);
|
||||
attachmentModuleRelation.setRelationType(AttachmentType.ISSUE.type());
|
||||
batchAttachmentModuleRelationMapper.insert(attachmentModuleRelation);
|
||||
} catch (Exception e) {
|
||||
LogUtil.error(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private List<FileAttachmentMetadata> getIssueFileAttachmentMetadata(String issueId) {
|
||||
AttachmentRequest attachmentRequest = new AttachmentRequest();
|
||||
attachmentRequest.setBelongType(AttachmentType.ISSUE.type());
|
||||
attachmentRequest.setBelongId(issueId);
|
||||
List<FileAttachmentMetadata> allMsAttachments = attachmentService.listMetadata(attachmentRequest);
|
||||
return allMsAttachments;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1407,9 +1473,6 @@ public class IssuesService {
|
|||
if (StringUtils.equalsIgnoreCase(project.getPlatform(), IssuesManagePlatform.Tapd.name())) {
|
||||
TapdPlatform tapd = new TapdPlatform(issuesRequest);
|
||||
this.doCheckThirdProjectExist(tapd, project.getTapdId());
|
||||
} else if (StringUtils.equalsIgnoreCase(project.getPlatform(), IssuesManagePlatform.Jira.name())) {
|
||||
JiraPlatform jira = new JiraPlatform(issuesRequest);
|
||||
this.doCheckThirdProjectExist(jira, project.getJiraKey());
|
||||
} else if (StringUtils.equalsIgnoreCase(project.getPlatform(), IssuesManagePlatform.Zentao.name())) {
|
||||
ZentaoPlatform zentao = new ZentaoPlatform(issuesRequest);
|
||||
this.doCheckThirdProjectExist(zentao, project.getZentaoId());
|
||||
|
@ -1755,4 +1818,37 @@ public class IssuesService {
|
|||
return BooleanUtils.isTrue(project.getThirdPartTemplate())
|
||||
&& platformPluginService.isThirdPartTemplateSupport(project.getPlatform());
|
||||
}
|
||||
|
||||
public boolean syncThirdPartyAllIssues(IssueSyncRequest syncRequest) {
|
||||
syncRequest.setProjectId(syncRequest.getProjectId());
|
||||
XpackIssueService xpackIssueService = CommonBeanFactory.getBean(XpackIssueService.class);
|
||||
if (StringUtils.isNotBlank(syncRequest.getProjectId())) {
|
||||
// 获取当前项目执行同步缺陷Key
|
||||
String syncValue = getSyncKey(syncRequest.getProjectId());
|
||||
// 存在即正在同步中
|
||||
if (StringUtils.isNotEmpty(syncValue)) {
|
||||
return false;
|
||||
}
|
||||
// 不存在则设置Key, 设置过期时间, 执行完成后delete掉
|
||||
setSyncKey(syncRequest.getProjectId());
|
||||
|
||||
try {
|
||||
Project project = baseProjectService.getProjectById(syncRequest.getProjectId());
|
||||
|
||||
if (!isThirdPartTemplate(project)) {
|
||||
syncRequest.setDefaultCustomFields(getDefaultCustomFields(syncRequest.getProjectId()));
|
||||
}
|
||||
|
||||
xpackIssueService.syncThirdPartyIssues(project, syncRequest);
|
||||
|
||||
syncAllPluginIssueAttachment(project, syncRequest);
|
||||
} catch (Exception e) {
|
||||
LogUtil.error(e);
|
||||
MSException.throwException(e);
|
||||
} finally {
|
||||
deleteSyncKey(syncRequest.getProjectId());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ import io.metersphere.plan.service.TestPlanTestCaseService;
|
|||
import io.metersphere.request.OrderRequest;
|
||||
import io.metersphere.request.ProjectVersionRequest;
|
||||
import io.metersphere.request.ResetOrderRequest;
|
||||
import io.metersphere.request.attachment.AttachmentRequest;
|
||||
import io.metersphere.xpack.track.dto.AttachmentRequest;
|
||||
import io.metersphere.request.member.QueryMemberRequest;
|
||||
import io.metersphere.request.testcase.*;
|
||||
import io.metersphere.service.issue.platform.IssueFactory;
|
||||
|
|
|
@ -1,290 +0,0 @@
|
|||
package io.metersphere.service.issue.client;
|
||||
|
||||
import io.metersphere.commons.exception.MSException;
|
||||
import io.metersphere.commons.utils.JSON;
|
||||
import io.metersphere.commons.utils.LogUtil;
|
||||
import io.metersphere.i18n.Translator;
|
||||
import io.metersphere.service.issue.domain.jira.*;
|
||||
import io.metersphere.xpack.track.dto.JiraIssue;
|
||||
import io.metersphere.xpack.track.dto.JiraIssueListResponse;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.http.*;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.client.HttpClientErrorException;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public abstract class JiraAbstractClient extends BaseClient {
|
||||
|
||||
protected String ENDPOINT;
|
||||
|
||||
protected String PREFIX;
|
||||
|
||||
protected String USER_NAME;
|
||||
|
||||
protected String PASSWD;
|
||||
|
||||
public JiraIssue getIssues(String issuesId) {
|
||||
LogUtil.info("getIssues: " + issuesId);
|
||||
ResponseEntity<String> responseEntity;
|
||||
responseEntity = restTemplate.exchange(getBaseUrl() + "/issue/" + issuesId, HttpMethod.GET, getAuthHttpEntity(), String.class);
|
||||
return (JiraIssue) getResultForObject(JiraIssue.class, responseEntity);
|
||||
}
|
||||
|
||||
public Map<String, JiraCreateMetadataResponse.Field> getCreateMetadata(String projectKey, String issueType) {
|
||||
String url = getBaseUrl() + "/issue/createmeta?projectKeys={1}&issuetypeIds={2}&expand=projects.issuetypes.fields";
|
||||
ResponseEntity<String> response = null;
|
||||
Map<String, JiraCreateMetadataResponse.Field> fields = null;
|
||||
try {
|
||||
response = restTemplate.exchange(url, HttpMethod.GET, getAuthHttpEntity(), String.class, projectKey, issueType);
|
||||
} catch (Exception e) {
|
||||
LogUtil.error(e.getMessage(), e);
|
||||
MSException.throwException(e.getMessage());
|
||||
}
|
||||
try {
|
||||
fields = ((JiraCreateMetadataResponse) getResultForObject(JiraCreateMetadataResponse.class, response))
|
||||
.getProjects().get(0).getIssuetypes().get(0).getFields();
|
||||
} catch (Exception e) {
|
||||
MSException.throwException(Translator.get("issue_jira_info_error"));
|
||||
}
|
||||
fields.remove("project");
|
||||
fields.remove("issuetype");
|
||||
return fields;
|
||||
}
|
||||
|
||||
public List<JiraIssueType> getIssueType(String projectKey) {
|
||||
JiraIssueProject project = getProject(projectKey);
|
||||
String url = getUrl("/issuetype/project?projectId={0}");
|
||||
ResponseEntity<String> response = null;
|
||||
try {
|
||||
response = restTemplate.exchange(url, HttpMethod.GET, getAuthHttpEntity(), String.class, project.getId());
|
||||
} catch (HttpClientErrorException e) {
|
||||
if (e.getRawStatusCode() == 404) { // Sass 的jira才有这个接口,报错则调用其他接口
|
||||
return this.getProject(projectKey).getIssueTypes();
|
||||
}
|
||||
LogUtil.error(e.getMessage(), e);
|
||||
MSException.throwException(e.getMessage());
|
||||
} catch (Exception e) {
|
||||
LogUtil.error(e.getMessage(), e);
|
||||
MSException.throwException(e.getMessage());
|
||||
}
|
||||
return (List<JiraIssueType>) getResultForList(JiraIssueType.class, response);
|
||||
}
|
||||
|
||||
public JiraIssueProject getProject(String projectKey) {
|
||||
String url = getUrl("/project/" + projectKey);
|
||||
ResponseEntity<String> response = null;
|
||||
try {
|
||||
response = restTemplate.exchange(url, HttpMethod.GET, getAuthHttpEntity(), String.class);
|
||||
} catch (Exception e) {
|
||||
LogUtil.error(e.getMessage(), e);
|
||||
MSException.throwException(e.getMessage());
|
||||
}
|
||||
return (JiraIssueProject) getResultForObject(JiraIssueProject.class, response);
|
||||
}
|
||||
|
||||
public List<JiraUser> getAssignableUser(String projectKey) {
|
||||
String url = getBaseUrl() + "/user/assignable/search?project={1}&maxResults=" + 1000 + "&startAt=" + 0;
|
||||
ResponseEntity<String> response = null;
|
||||
try {
|
||||
response = restTemplate.exchange(url, HttpMethod.GET, getAuthHttpEntity(), String.class, projectKey);
|
||||
} catch (Exception e) {
|
||||
LogUtil.error(e.getMessage(), e);
|
||||
MSException.throwException(e.getMessage());
|
||||
}
|
||||
return (List<JiraUser>) getResultForList(JiraUser.class, response);
|
||||
}
|
||||
|
||||
public List getDemands(String projectKey, String issueType, int startAt, int maxResults) {
|
||||
String jql = getBaseUrl() + "/search?jql=project=" + projectKey + "+AND+issuetype=" + issueType
|
||||
+ "&maxResults=" + maxResults + "&startAt=" + startAt + "&fields=summary,issuetype";
|
||||
ResponseEntity<String> responseEntity = restTemplate.exchange(jql,
|
||||
HttpMethod.GET, getAuthHttpEntity(), String.class);
|
||||
Map jsonObject = JSON.parseMap(responseEntity.getBody());
|
||||
return (List) jsonObject.get("issues");
|
||||
}
|
||||
|
||||
public List<JiraField> getFields() {
|
||||
ResponseEntity<String> response = restTemplate.exchange(getBaseUrl() + "/field", HttpMethod.GET, getAuthHttpEntity(), String.class);
|
||||
return (List<JiraField>) getResultForList(JiraField.class, response);
|
||||
}
|
||||
|
||||
public JiraAddIssueResponse addIssue(String body) {
|
||||
LogUtil.info("addIssue: " + body);
|
||||
HttpHeaders headers = getAuthHeader();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
HttpEntity<String> requestEntity = new HttpEntity<>(body, headers);
|
||||
ResponseEntity<String> response = null;
|
||||
try {
|
||||
response = restTemplate.exchange(getBaseUrl() + "/issue", HttpMethod.POST, requestEntity, String.class);
|
||||
} catch (Exception e) {
|
||||
LogUtil.error(e.getMessage(), e);
|
||||
MSException.throwException(e.getMessage());
|
||||
}
|
||||
return (JiraAddIssueResponse) getResultForObject(JiraAddIssueResponse.class, response);
|
||||
}
|
||||
|
||||
public List<JiraTransitionsResponse.Transitions> getTransitions(String issueKey) {
|
||||
ResponseEntity<String> response = restTemplate.exchange(getBaseUrl() + "/issue/{1}/transitions", HttpMethod.GET, getAuthHttpEntity(), String.class, issueKey);
|
||||
return ((JiraTransitionsResponse) getResultForObject(JiraTransitionsResponse.class, response)).getTransitions();
|
||||
}
|
||||
|
||||
public void updateIssue(String id, String body) {
|
||||
LogUtil.info("addIssue: " + body);
|
||||
HttpHeaders headers = getAuthHeader();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
HttpEntity<String> requestEntity = new HttpEntity<>(body, headers);
|
||||
try {
|
||||
restTemplate.exchange(getBaseUrl() + "/issue/" + id, HttpMethod.PUT, requestEntity, String.class);
|
||||
} catch (Exception e) {
|
||||
LogUtil.error(e.getMessage(), e);
|
||||
MSException.throwException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void deleteIssue(String id) {
|
||||
LogUtil.info("deleteIssue: " + id);
|
||||
try {
|
||||
restTemplate.exchange(getBaseUrl() + "/issue/" + id, HttpMethod.DELETE, getAuthHttpEntity(), String.class);
|
||||
} catch (HttpClientErrorException e) {
|
||||
if (e.getRawStatusCode() != 404) {// 404说明jira没有,可以直接删
|
||||
MSException.throwException(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void deleteAttachment(String id) {
|
||||
LogUtil.info("deleteAttachment: " + id);
|
||||
try {
|
||||
restTemplate.exchange(getBaseUrl() + "/attachment/" + id, HttpMethod.DELETE, getAuthHttpEntity(), String.class);
|
||||
} catch (HttpClientErrorException e) {
|
||||
if (e.getRawStatusCode() != 404) {// 404说明jira没有,可以直接删
|
||||
MSException.throwException(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void uploadAttachment(String issueKey, File file) {
|
||||
HttpHeaders authHeader = getAuthHeader();
|
||||
authHeader.add("X-Atlassian-Token", "no-check");
|
||||
authHeader.setContentType(MediaType.parseMediaType("multipart/form-data; charset=UTF-8"));
|
||||
|
||||
MultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<>();
|
||||
FileSystemResource fileResource = new FileSystemResource(file);
|
||||
paramMap.add("file", fileResource);
|
||||
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(paramMap, authHeader);
|
||||
ResponseEntity<String> response = null;
|
||||
try {
|
||||
response = restTemplate.exchange(getBaseUrl() + "/issue/" + issueKey + "/attachments", HttpMethod.POST, requestEntity, String.class);
|
||||
} catch (Exception e) {
|
||||
LogUtil.error(e.getMessage(), e);
|
||||
}
|
||||
System.out.println(response);
|
||||
}
|
||||
|
||||
public void auth() {
|
||||
ResponseEntity<String> response = null;
|
||||
try {
|
||||
response = restTemplate.exchange(getBaseUrl() + "/myself", HttpMethod.GET, getAuthHttpEntity(), String.class);
|
||||
if (StringUtils.isBlank(response.getBody()) || (StringUtils.isNotBlank(response.getBody()) && !response.getBody().startsWith("{\"self\""))) {
|
||||
MSException.throwException(Translator.get("jira_auth_url_error"));
|
||||
}
|
||||
} catch (HttpClientErrorException e) {
|
||||
if (e.getRawStatusCode() == 401) {
|
||||
MSException.throwException(Translator.get("jira_auth_error"));
|
||||
} else {
|
||||
LogUtil.error(e.getMessage(), e);
|
||||
MSException.throwException(e.getMessage());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LogUtil.error(e.getMessage(), e);
|
||||
MSException.throwException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
protected HttpEntity<MultiValueMap> getAuthHttpEntity() {
|
||||
return new HttpEntity<>(getAuthHeader());
|
||||
}
|
||||
|
||||
protected HttpHeaders getAuthHeader() {
|
||||
return getBasicHttpHeaders(USER_NAME, PASSWD);
|
||||
}
|
||||
|
||||
protected HttpHeaders getAuthJsonHeader() {
|
||||
HttpHeaders headers = getAuthHeader();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
return headers;
|
||||
}
|
||||
|
||||
protected String getBaseUrl() {
|
||||
return ENDPOINT + PREFIX;
|
||||
}
|
||||
|
||||
protected String getUrl(String path) {
|
||||
return getBaseUrl() + path;
|
||||
}
|
||||
|
||||
public void setConfig(JiraConfig config) {
|
||||
if (config == null) {
|
||||
MSException.throwException("config is null");
|
||||
}
|
||||
String url = config.getUrl();
|
||||
|
||||
if (StringUtils.isNotBlank(url) && url.endsWith("/")) {
|
||||
url = url.substring(0, url.length() - 1);
|
||||
}
|
||||
ENDPOINT = url;
|
||||
USER_NAME = config.getAccount();
|
||||
PASSWD = config.getPassword();
|
||||
}
|
||||
|
||||
public JiraIssueListResponse getProjectIssues(Integer startAt, Integer maxResults, String projectKey, String issueType) {
|
||||
return getProjectIssues(startAt, maxResults, projectKey, issueType, null);
|
||||
}
|
||||
|
||||
public JiraIssueListResponse getProjectIssues(Integer startAt, Integer maxResults, String projectKey, String issueType, String fields) {
|
||||
ResponseEntity<String> responseEntity;
|
||||
String url = getBaseUrl() + "/search?startAt={1}&maxResults={2}&jql=project={3}+AND+issuetype={4}";
|
||||
if (StringUtils.isNotBlank(fields)) {
|
||||
url = url + "&fields=" + fields;
|
||||
}
|
||||
responseEntity = restTemplate.exchange(url,
|
||||
HttpMethod.GET, getAuthHttpEntity(), String.class, startAt, maxResults, projectKey, issueType);
|
||||
return (JiraIssueListResponse)getResultForObject(JiraIssueListResponse.class, responseEntity);
|
||||
}
|
||||
|
||||
public byte[] getAttachmentContent(String url) {
|
||||
ResponseEntity<byte[]> responseEntity;
|
||||
responseEntity = restTemplate.exchange(url,
|
||||
HttpMethod.GET, getAuthHttpEntity(), byte[].class);
|
||||
return responseEntity.getBody();
|
||||
}
|
||||
|
||||
public JiraIssueListResponse getProjectIssuesAttachment(Integer startAt, Integer maxResults, String projectKey, String issueType) {
|
||||
return getProjectIssues(startAt, maxResults, projectKey, issueType, "attachment");
|
||||
|
||||
}
|
||||
public void setTransitions(String jiraKey, JiraTransitionsResponse.Transitions transitions) {
|
||||
LogUtil.info("setTransitions: " + transitions);
|
||||
Map jsonObject = new LinkedHashMap();
|
||||
jsonObject.put("transition", transitions);
|
||||
HttpEntity<String> requestEntity = new HttpEntity<>(JSON.toJSONString(jsonObject), getAuthJsonHeader());
|
||||
try {
|
||||
restTemplate.exchange(getBaseUrl() + "/issue/{1}/transitions", HttpMethod.POST, requestEntity, String.class, jiraKey);
|
||||
} catch (Exception e) {
|
||||
LogUtil.error(e.getMessage(), e);
|
||||
MSException.throwException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public ResponseEntity proxyForGet(String url, Class responseEntityClazz) {
|
||||
LogUtil.info("jira proxyForGet: " + url);
|
||||
return restTemplate.exchange(url, HttpMethod.GET, getAuthHttpEntity(), responseEntityClazz);
|
||||
}
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
package io.metersphere.service.issue.client;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class JiraClientV2 extends JiraAbstractClient {
|
||||
{
|
||||
PREFIX = "/rest/api/2";
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
package io.metersphere.service.issue.client;
|
||||
|
||||
public class JiraClientV3 extends JiraAbstractClient {
|
||||
{
|
||||
PREFIX = "/rest/api/3";
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
package io.metersphere.service.issue.domain.jira;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class JiraAddIssueResponse {
|
||||
private String id;
|
||||
private String key;
|
||||
private String self;
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
package io.metersphere.service.issue.domain.jira;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class JiraConfig {
|
||||
private String account;
|
||||
private String password;
|
||||
private String url;
|
||||
private String issuetype;
|
||||
private String storytype;
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
package io.metersphere.service.issue.domain.jira;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
public class JiraCreateMetadataResponse {
|
||||
private List<Projects> projects;
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
public static class Projects {
|
||||
private List<Issuetypes> issuetypes;
|
||||
}
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
public static class Issuetypes {
|
||||
private Map<String, Field> fields;
|
||||
}
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
public static class Field {
|
||||
private boolean required;
|
||||
private Schema schema;
|
||||
private String name;
|
||||
private String key;
|
||||
private String autoCompleteUrl;
|
||||
private boolean hasDefaultValue;
|
||||
private Object defaultValue;
|
||||
private List<AllowedValues> allowedValues;
|
||||
}
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
public static class Schema {
|
||||
private String type;
|
||||
private String items;
|
||||
private String custom;
|
||||
private int customId;
|
||||
}
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
public static class AllowedValues {
|
||||
private String self;
|
||||
private String id;
|
||||
private String description;
|
||||
private String name;
|
||||
private String value;
|
||||
private boolean subtask;
|
||||
private int avatarId;
|
||||
private int hierarchyLevel;
|
||||
private List<AllowedValues> children;
|
||||
}
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
package io.metersphere.service.issue.domain.jira;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class JiraField {
|
||||
|
||||
private String id;
|
||||
private String key;
|
||||
private String name;
|
||||
private boolean custom;
|
||||
// private boolean orderable;
|
||||
// private boolean navigable;
|
||||
// private boolean searchable;
|
||||
// private List<String> clauseNames;
|
||||
// private Schema schema;
|
||||
//
|
||||
// @Getter
|
||||
// @Setter
|
||||
// public class Schema {
|
||||
// private String type;
|
||||
// private String system;
|
||||
// }
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
package io.metersphere.service.issue.domain.jira;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class JiraIssueDescriptionV3 {
|
||||
private String type;
|
||||
private int version;
|
||||
private List<Content> content;
|
||||
|
||||
public JiraIssueDescriptionV3(String text) {
|
||||
List<Content> list = new ArrayList<>();
|
||||
Content content = new Content(text);
|
||||
list.add(content);
|
||||
this.type = "doc";
|
||||
this.version = 1;
|
||||
this.content = list;
|
||||
}
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class Content {
|
||||
private String type;
|
||||
private List<ContentInfo> content;
|
||||
|
||||
public Content(String text) {
|
||||
List<ContentInfo> list = new ArrayList<>();
|
||||
ContentInfo content = new ContentInfo(text);
|
||||
list.add(content);
|
||||
this.type = "paragraph";
|
||||
this.content = list;
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class ContentInfo {
|
||||
private String text;
|
||||
private String type;
|
||||
|
||||
public ContentInfo(String text) {
|
||||
this.text = text;
|
||||
this.type = "text";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
package io.metersphere.service.issue.domain.jira;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class JiraIssueProject {
|
||||
private String id;
|
||||
private String name;
|
||||
private String key;
|
||||
private List<JiraIssueType> issueTypes;
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
package io.metersphere.service.issue.domain.jira;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class JiraIssueType {
|
||||
private String id;
|
||||
private String name;
|
||||
private String untranslatedName;
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
package io.metersphere.service.issue.domain.jira;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class JiraTransitionsResponse {
|
||||
|
||||
private String expand;
|
||||
private List<Transitions> transitions;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public static class Transitions {
|
||||
private String id;
|
||||
private String name;
|
||||
private To to;
|
||||
private Boolean hasScreen;
|
||||
private Boolean isGlobal;
|
||||
private Boolean isInitial;
|
||||
private Boolean isAvailable;
|
||||
private Boolean isConditional;
|
||||
private Boolean isLooped;
|
||||
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public static class To {
|
||||
private String self;
|
||||
private String description;
|
||||
private String iconUrl;
|
||||
private String name;
|
||||
private String id;
|
||||
private StatusCategory statusCategory;
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public static class StatusCategory {
|
||||
private String self;
|
||||
private int id;
|
||||
private String key;
|
||||
private String colorName;
|
||||
private String name;
|
||||
}
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
package io.metersphere.service.issue.domain.jira;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
public class JiraUser {
|
||||
private String accountId;
|
||||
private String key;
|
||||
private String name;
|
||||
private String displayName;
|
||||
private String emailAddress;
|
||||
private Boolean active;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -11,7 +11,7 @@ import io.metersphere.commons.utils.LogUtil;
|
|||
import io.metersphere.xpack.track.dto.AttachmentSyncType;
|
||||
import io.metersphere.constants.AttachmentType;
|
||||
import io.metersphere.dto.*;
|
||||
import io.metersphere.request.attachment.AttachmentRequest;
|
||||
import io.metersphere.xpack.track.dto.AttachmentRequest;
|
||||
import io.metersphere.xpack.track.dto.DemandDTO;
|
||||
import io.metersphere.xpack.track.dto.IssuesDao;
|
||||
import io.metersphere.xpack.track.dto.request.IssuesRequest;
|
||||
|
@ -27,8 +27,6 @@ import io.metersphere.service.issue.domain.zentao.ZentaoConfig;
|
|||
import io.metersphere.xpack.track.dto.PlatformStatusDTO;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.http.entity.mime.MultipartEntity;
|
||||
import org.apache.http.entity.mime.content.FileBody;
|
||||
import org.apache.logging.log4j.util.Strings;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.http.HttpEntity;
|
||||
|
|
|
@ -148,14 +148,14 @@ export function getRelateIssues(page) {
|
|||
|
||||
export function syncAllIssues(param) {
|
||||
// 浏览器默认策略,请求同一个url,可能导致 stalled 时间过长,加个uuid防止请求阻塞
|
||||
let url = 'xpack/issue/sync' + "?stamp=" + getUUID();
|
||||
return post(url, param);
|
||||
return post(BASE_URL + 'sync/all?stamp=' + getUUID(), param);
|
||||
}
|
||||
|
||||
export function syncIssues() {
|
||||
// 浏览器默认策略,请求同一个url,可能导致 stalled 时间过长,加个uuid防止请求阻塞
|
||||
let url = 'issues/sync/' + getCurrentProjectID() + "?stamp=" + getUUID();
|
||||
return get(url);
|
||||
let projectId = getCurrentProjectID();
|
||||
let uuid = getUUID();
|
||||
return get(BASE_URL + `sync/${projectId}?stamp=${uuid}`);
|
||||
}
|
||||
|
||||
// 轮询同步状态
|
||||
|
|
Loading…
Reference in New Issue