fix(测试跟踪): Jira同步全量缺陷失败

This commit is contained in:
chenjianxing 2022-11-28 19:33:12 +08:00 committed by jianxing
parent a3ced967c4
commit 9449bf8243
27 changed files with 195 additions and 1674 deletions

View File

@ -29,7 +29,7 @@
<platform-account-config <platform-account-config
:config="config" :config="config"
:account-config="currentPlatformInfo" :account-config="currentPlatformInfo"
v-if="config.key === 'Jira'" v-if="showPlatformConfig(config.key)"
/> />
</div> </div>
<tapd-user-info @auth="handleAuth" v-if="hasTapd" :data="currentPlatformInfo"/> <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 {handleAuth as _handleAuth, getUserInfo, getWsAndPj, updateInfo} from "../../api/user";
import PlatformAccountConfig from "./PlatformAccountConfig"; import PlatformAccountConfig from "./PlatformAccountConfig";
import {getPlatformAccountInfo} from "../../api/platform-plugin"; import {getPlatformAccountInfo} from "../../api/platform-plugin";
import {ISSUE_PLATFORM_OPTION} from "../../utils/table-constants";
const userStore = useUserStore(); const userStore = useUserStore();
@ -125,6 +126,9 @@ export default {
currentUser: () => { currentUser: () => {
return getCurrentUser(); return getCurrentUser();
}, },
showPlatformConfig(platform) {
return ISSUE_PLATFORM_OPTION.map(item => item.value).indexOf(platform) < 0;
},
handleAuth(type) { handleAuth(type) {
let param = {...this.currentPlatformInfo}; let param = {...this.currentPlatformInfo};
if (type === 'Zentao') { if (type === 'Zentao') {
@ -246,7 +250,8 @@ export default {
height: 40px; height: 40px;
line-height: 40px; line-height: 40px;
} }
.el-form-item-class { .el-form-item-class {
margin-left:110px; margin-left: 110px;
} }
</style> </style>

View File

@ -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);
}
}

View File

@ -1,4 +1,4 @@
package io.metersphere.request.attachment; package io.metersphere.xpack.track.dto;
import lombok.Data; import lombok.Data;

View File

@ -2,6 +2,10 @@ package io.metersphere.xpack.track.dto;
import lombok.Data; import lombok.Data;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/** /**
* @author songcc * @author songcc
*/ */
@ -22,4 +26,8 @@ public class IssueSyncRequest {
* TRUE: 创建时间之前 * TRUE: 创建时间之前
*/ */
private boolean pre; private boolean pre;
private String defaultCustomFields;
private Map<String, List<io.metersphere.xpack.track.dto.PlatformAttachment>> attachmentMap = new HashMap<>();
} }

View File

@ -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;
}

View File

@ -1,10 +1,11 @@
package io.metersphere.xpack.track.service; package io.metersphere.xpack.track.service;
import io.metersphere.base.domain.Project;
import io.metersphere.xpack.track.dto.IssueSyncRequest; import io.metersphere.xpack.track.dto.IssueSyncRequest;
public interface XpackIssueService { public interface XpackIssueService {
boolean syncThirdPartyIssues(IssueSyncRequest request); boolean syncThirdPartyIssues(Project project, IssueSyncRequest request);
void syncThirdPartyIssues(); void syncThirdPartyIssues();
} }

View File

@ -3,7 +3,7 @@ package io.metersphere.controller;
import io.metersphere.base.domain.FileAttachmentMetadata; import io.metersphere.base.domain.FileAttachmentMetadata;
import io.metersphere.metadata.service.FileMetadataService; import io.metersphere.metadata.service.FileMetadataService;
import io.metersphere.request.attachment.AttachmentDumpRequest; 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 io.metersphere.service.AttachmentService;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;

View File

@ -25,10 +25,7 @@ import io.metersphere.service.BaseCheckPermissionService;
import io.metersphere.service.IssuesService; import io.metersphere.service.IssuesService;
import io.metersphere.service.PlatformPluginService; import io.metersphere.service.PlatformPluginService;
import io.metersphere.service.issue.domain.zentao.ZentaoBuild; import io.metersphere.service.issue.domain.zentao.ZentaoBuild;
import io.metersphere.xpack.track.dto.IssueTemplateDao; import io.metersphere.xpack.track.dto.*;
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.request.IssuesRequest; import io.metersphere.xpack.track.dto.request.IssuesRequest;
import io.metersphere.xpack.track.dto.request.IssuesUpdateRequest; import io.metersphere.xpack.track.dto.request.IssuesUpdateRequest;
import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.CollectionUtils;
@ -164,10 +161,15 @@ public class IssuesController {
} }
@GetMapping("/sync/{projectId}") @GetMapping("/sync/{projectId}")
public boolean getPlatformIssue(@PathVariable String projectId) { public boolean syncThirdPartyIssues(@PathVariable String projectId) {
return issuesService.syncThirdPartyIssues(projectId); return issuesService.syncThirdPartyIssues(projectId);
} }
@PostMapping("/sync/all")
public boolean syncThirdPartyAllIssues(@RequestBody IssueSyncRequest request) {
return issuesService.syncThirdPartyAllIssues(request);
}
@GetMapping("/sync/check/{projectId}") @GetMapping("/sync/check/{projectId}")
public boolean checkSync(@PathVariable String projectId) { public boolean checkSync(@PathVariable String projectId) {
return issuesService.checkSync(projectId); return issuesService.checkSync(projectId);

View File

@ -13,7 +13,7 @@ import io.metersphere.i18n.Translator;
import io.metersphere.metadata.service.FileMetadataService; import io.metersphere.metadata.service.FileMetadataService;
import io.metersphere.metadata.utils.MetadataUtils; import io.metersphere.metadata.utils.MetadataUtils;
import io.metersphere.platform.domain.SyncIssuesAttachmentRequest; 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.service.issue.platform.IssueFactory;
import io.metersphere.xmind.utils.FileUtil; import io.metersphere.xmind.utils.FileUtil;
import io.metersphere.xpack.track.dto.AttachmentSyncType; import io.metersphere.xpack.track.dto.AttachmentSyncType;

View File

@ -6,6 +6,7 @@ import io.metersphere.base.domain.CustomFieldIssues;
import io.metersphere.base.domain.CustomFieldIssuesExample; import io.metersphere.base.domain.CustomFieldIssuesExample;
import io.metersphere.base.mapper.CustomFieldIssuesMapper; import io.metersphere.base.mapper.CustomFieldIssuesMapper;
import io.metersphere.base.mapper.ext.BaseCustomFieldResourceMapper; import io.metersphere.base.mapper.ext.BaseCustomFieldResourceMapper;
import io.metersphere.commons.utils.SubListUtil;
import io.metersphere.constants.SystemCustomField; import io.metersphere.constants.SystemCustomField;
import io.metersphere.dto.CustomFieldDao; import io.metersphere.dto.CustomFieldDao;
import io.metersphere.dto.CustomFieldResourceDTO; 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)); int batchSize = 500;
sqlSession.flushStatements();
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) { if (sqlSession != null && sqlSessionFactory != null) {
SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory); SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory);
} }

View File

@ -38,8 +38,9 @@ import io.metersphere.plan.service.TestPlanTestCaseService;
import io.metersphere.plan.utils.TestPlanStatusCalculator; import io.metersphere.plan.utils.TestPlanStatusCalculator;
import io.metersphere.platform.api.Platform; import io.metersphere.platform.api.Platform;
import io.metersphere.platform.domain.*; import io.metersphere.platform.domain.*;
import io.metersphere.platform.domain.PlatformAttachment;
import io.metersphere.request.IntegrationRequest; 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.IssueExportRequest;
import io.metersphere.request.issues.IssueImportRequest; import io.metersphere.request.issues.IssueImportRequest;
import io.metersphere.request.issues.PlatformIssueTypeRequest; 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.IssuesRequest;
import io.metersphere.xpack.track.dto.request.IssuesUpdateRequest; import io.metersphere.xpack.track.dto.request.IssuesUpdateRequest;
import io.metersphere.xpack.track.issue.IssuesPlatform; 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.CollectionUtils;
import org.apache.commons.collections.MapUtils; import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.BooleanUtils;
@ -1005,7 +1007,7 @@ public class IssuesService {
request.setIssues(platformIssues); request.setIssues(platformIssues);
request.setDefaultCustomFields(defaultCustomFields); request.setDefaultCustomFields(defaultCustomFields);
request.setProjectConfig(PlatformPluginService.getCompatibleProjectConfig(project)); request.setProjectConfig(PlatformPluginService.getCompatibleProjectConfig(project));
Platform platform = platformPluginService.getPlatform(project.getPlatform()); Platform platform = platformPluginService.getPlatform(project.getPlatform(), project.getWorkspaceId());
// 获取需要变更的缺陷 // 获取需要变更的缺陷
SyncIssuesResult syncIssuesResult = platform.syncIssues(request); SyncIssuesResult syncIssuesResult = platform.syncIssues(request);
@ -1047,61 +1049,125 @@ 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(); Map<String, List<PlatformAttachment>> attachmentMap = syncIssuesResult.getAttachmentMap();
if (MapUtils.isNotEmpty(attachmentMap)) { if (MapUtils.isNotEmpty(attachmentMap)) {
for (String issueId : attachmentMap.keySet()) { for (String issueId : attachmentMap.keySet()) {
// 查询我们平台的附件 // 查询我们平台的附件
Set<String> jiraAttachmentSet = new HashSet<>(); Set<String> jiraAttachmentSet = new HashSet<>();
AttachmentRequest attachmentRequest = new AttachmentRequest(); List<FileAttachmentMetadata> allMsAttachments = getIssueFileAttachmentMetadata(issueId);
attachmentRequest.setBelongType(AttachmentType.ISSUE.type());
attachmentRequest.setBelongId(issueId);
List<FileAttachmentMetadata> allMsAttachments = attachmentService.listMetadata(attachmentRequest);
Set<String> attachmentsNameSet = allMsAttachments.stream() Set<String> attachmentsNameSet = allMsAttachments.stream()
.map(FileAttachmentMetadata::getName) .map(FileAttachmentMetadata::getName)
.collect(Collectors.toSet()); .collect(Collectors.toSet());
List<PlatformAttachment> syncAttachments = attachmentMap.get(issueId); List<PlatformAttachment> syncAttachments = attachmentMap.get(issueId);
for (PlatformAttachment syncAttachment : syncAttachments) { for (PlatformAttachment syncAttachment : syncAttachments) {
if (!attachmentsNameSet.contains(syncAttachment.getFileName())) { String fileName = syncAttachment.getFileName();
jiraAttachmentSet.add(syncAttachment.getFileName()); String fileKey = syncAttachment.getFileKey();
try { if (!attachmentsNameSet.contains(fileName)) {
byte[] content = platform.getAttachmentContent(syncAttachment.getFileKey()); jiraAttachmentSet.add(fileName);
if (content == null) { saveAttachmentModuleRelation(platform, issueId, fileName, fileKey, batchAttachmentModuleRelationMapper);
continue;
}
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中不存在的附件 // 删除Jira中不存在的附件
if (CollectionUtils.isNotEmpty(allMsAttachments)) { deleteSyncAttachment(batchAttachmentModuleRelationMapper, jiraAttachmentSet, allMsAttachments);
List<FileAttachmentMetadata> deleteMsAttachments = allMsAttachments.stream()
.filter(msAttachment -> !jiraAttachmentSet.contains(msAttachment.getName()))
.collect(Collectors.toList());
deleteMsAttachments.forEach(fileAttachmentMetadata -> {
List<String> ids = List.of(fileAttachmentMetadata.getId());
AttachmentModuleRelationExample example = new AttachmentModuleRelationExample();
example.createCriteria().andAttachmentIdIn(ids).andRelationTypeEqualTo(AttachmentType.ISSUE.type());
// 删除MS附件及关联数据
attachmentService.deleteAttachmentByIds(ids);
attachmentService.deleteFileAttachmentByIds(ids);
batchAttachmentModuleRelationMapper.deleteByExample(example);
});
}
} }
} }
} }
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()
.filter(msAttachment -> !jiraAttachmentSet.contains(msAttachment.getName()))
.collect(Collectors.toList());
deleteMsAttachments.forEach(fileAttachmentMetadata -> {
List<String> ids = List.of(fileAttachmentMetadata.getId());
AttachmentModuleRelationExample example = new AttachmentModuleRelationExample();
example.createCriteria().andAttachmentIdIn(ids).andRelationTypeEqualTo(AttachmentType.ISSUE.type());
// 删除MS附件及关联数据
attachmentService.deleteAttachmentByIds(ids);
attachmentService.deleteFileAttachmentByIds(ids);
batchAttachmentModuleRelationMapper.deleteByExample(example);
});
}
}
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())) { if (StringUtils.equalsIgnoreCase(project.getPlatform(), IssuesManagePlatform.Tapd.name())) {
TapdPlatform tapd = new TapdPlatform(issuesRequest); TapdPlatform tapd = new TapdPlatform(issuesRequest);
this.doCheckThirdProjectExist(tapd, project.getTapdId()); 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())) { } else if (StringUtils.equalsIgnoreCase(project.getPlatform(), IssuesManagePlatform.Zentao.name())) {
ZentaoPlatform zentao = new ZentaoPlatform(issuesRequest); ZentaoPlatform zentao = new ZentaoPlatform(issuesRequest);
this.doCheckThirdProjectExist(zentao, project.getZentaoId()); this.doCheckThirdProjectExist(zentao, project.getZentaoId());
@ -1755,4 +1818,37 @@ public class IssuesService {
return BooleanUtils.isTrue(project.getThirdPartTemplate()) return BooleanUtils.isTrue(project.getThirdPartTemplate())
&& platformPluginService.isThirdPartTemplateSupport(project.getPlatform()); && 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;
}
} }

View File

@ -35,7 +35,7 @@ import io.metersphere.plan.service.TestPlanTestCaseService;
import io.metersphere.request.OrderRequest; import io.metersphere.request.OrderRequest;
import io.metersphere.request.ProjectVersionRequest; import io.metersphere.request.ProjectVersionRequest;
import io.metersphere.request.ResetOrderRequest; 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.member.QueryMemberRequest;
import io.metersphere.request.testcase.*; import io.metersphere.request.testcase.*;
import io.metersphere.service.issue.platform.IssueFactory; import io.metersphere.service.issue.platform.IssueFactory;

View File

@ -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);
}
}

View File

@ -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";
}
}

View File

@ -1,7 +0,0 @@
package io.metersphere.service.issue.client;
public class JiraClientV3 extends JiraAbstractClient {
{
PREFIX = "/rest/api/3";
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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;
// }
}

View File

@ -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";
}
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -11,7 +11,7 @@ import io.metersphere.commons.utils.LogUtil;
import io.metersphere.xpack.track.dto.AttachmentSyncType; import io.metersphere.xpack.track.dto.AttachmentSyncType;
import io.metersphere.constants.AttachmentType; import io.metersphere.constants.AttachmentType;
import io.metersphere.dto.*; 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.DemandDTO;
import io.metersphere.xpack.track.dto.IssuesDao; import io.metersphere.xpack.track.dto.IssuesDao;
import io.metersphere.xpack.track.dto.request.IssuesRequest; 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 io.metersphere.xpack.track.dto.PlatformStatusDTO;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils; 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.apache.logging.log4j.util.Strings;
import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.FileSystemResource;
import org.springframework.http.HttpEntity; import org.springframework.http.HttpEntity;

View File

@ -148,14 +148,14 @@ export function getRelateIssues(page) {
export function syncAllIssues(param) { export function syncAllIssues(param) {
// 浏览器默认策略请求同一个url可能导致 stalled 时间过长加个uuid防止请求阻塞 // 浏览器默认策略请求同一个url可能导致 stalled 时间过长加个uuid防止请求阻塞
let url = 'xpack/issue/sync' + "?stamp=" + getUUID(); return post(BASE_URL + 'sync/all?stamp=' + getUUID(), param);
return post(url, param);
} }
export function syncIssues() { export function syncIssues() {
// 浏览器默认策略请求同一个url可能导致 stalled 时间过长加个uuid防止请求阻塞 // 浏览器默认策略请求同一个url可能导致 stalled 时间过长加个uuid防止请求阻塞
let url = 'issues/sync/' + getCurrentProjectID() + "?stamp=" + getUUID(); let projectId = getCurrentProjectID();
return get(url); let uuid = getUUID();
return get(BASE_URL + `sync/${projectId}?stamp=${uuid}`);
} }
// 轮询同步状态 // 轮询同步状态