refactor: 开源自动获取Jira模板功能

This commit is contained in:
chenjianxing 2021-12-20 21:15:37 +08:00 committed by jianxing
parent 3ab559ef19
commit 7152640161
9 changed files with 215 additions and 18 deletions

View File

@ -160,7 +160,7 @@ public class ApiTestEnvironmentService {
} }
String apiName = MockConfigStaticData.MOCK_EVN_NAME; String apiName = MockConfigStaticData.MOCK_EVN_NAME;
ApiTestEnvironmentWithBLOBs returnModel = null; ApiTestEnvironmentWithBLOBs returnModel;
ApiTestEnvironmentExample example = new ApiTestEnvironmentExample(); ApiTestEnvironmentExample example = new ApiTestEnvironmentExample();
example.createCriteria().andProjectIdEqualTo(projectId).andNameEqualTo(apiName); example.createCriteria().andProjectIdEqualTo(projectId).andNameEqualTo(apiName);
List<ApiTestEnvironmentWithBLOBs> list = this.selectByExampleWithBLOBs(example); List<ApiTestEnvironmentWithBLOBs> list = this.selectByExampleWithBLOBs(example);

View File

@ -12,6 +12,7 @@ import io.metersphere.base.mapper.*;
import io.metersphere.base.mapper.ext.ExtProjectMapper; import io.metersphere.base.mapper.ext.ExtProjectMapper;
import io.metersphere.base.mapper.ext.ExtUserGroupMapper; import io.metersphere.base.mapper.ext.ExtUserGroupMapper;
import io.metersphere.base.mapper.ext.ExtUserMapper; import io.metersphere.base.mapper.ext.ExtUserMapper;
import io.metersphere.commons.constants.IssuesManagePlatform;
import io.metersphere.commons.constants.UserGroupConstants; import io.metersphere.commons.constants.UserGroupConstants;
import io.metersphere.commons.exception.MSException; import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.CommonBeanFactory; import io.metersphere.commons.utils.CommonBeanFactory;
@ -105,6 +106,9 @@ public class ProjectService {
if (projectMapper.countByExample(example) > 0) { if (projectMapper.countByExample(example) > 0) {
MSException.throwException(Translator.get("project_name_already_exists")); MSException.throwException(Translator.get("project_name_already_exists"));
} }
if (StringUtils.isBlank(project.getPlatform())) {
project.setPlatform(IssuesManagePlatform.Local.name());
}
project.setId(UUID.randomUUID().toString()); project.setId(UUID.randomUUID().toString());
String systemId = this.genSystemId(); String systemId = this.genSystemId();

View File

@ -9,6 +9,7 @@ import io.metersphere.commons.constants.NoticeConstants;
import io.metersphere.commons.constants.OperLogConstants; import io.metersphere.commons.constants.OperLogConstants;
import io.metersphere.commons.utils.PageUtils; import io.metersphere.commons.utils.PageUtils;
import io.metersphere.commons.utils.Pager; import io.metersphere.commons.utils.Pager;
import io.metersphere.dto.IssueTemplateDao;
import io.metersphere.log.annotation.MsAuditLog; import io.metersphere.log.annotation.MsAuditLog;
import io.metersphere.notice.annotation.SendNotice; import io.metersphere.notice.annotation.SendNotice;
import io.metersphere.track.issue.domain.PlatformUser; import io.metersphere.track.issue.domain.PlatformUser;
@ -144,4 +145,9 @@ public class IssuesController {
public void saveFollows(@PathVariable String issueId,@RequestBody List<String> follows) { public void saveFollows(@PathVariable String issueId,@RequestBody List<String> follows) {
issuesService.saveFollows(issueId,follows); issuesService.saveFollows(issueId,follows);
} }
@GetMapping("/thirdpart/template/{projectId}")
public IssueTemplateDao getThirdPartTemplate(@PathVariable String projectId) {
return issuesService.getThirdPartTemplate(projectId);
}
} }

View File

@ -492,7 +492,7 @@ public abstract class AbstractIssuePlatform implements IssuesPlatform {
public boolean isThirdPartTemplate() { public boolean isThirdPartTemplate() {
Project project = projectService.getProjectById(projectId); Project project = projectService.getProjectById(projectId);
if (project.getThirdPartTemplate() != null && project.getThirdPartTemplate() && LicenseUtils.valid()) { if (project.getThirdPartTemplate() != null && project.getThirdPartTemplate()) {
return true; return true;
} }
return false; return false;

View File

@ -5,19 +5,20 @@ import com.alibaba.fastjson.JSONObject;
import io.metersphere.base.domain.IssuesDao; import io.metersphere.base.domain.IssuesDao;
import io.metersphere.base.domain.IssuesWithBLOBs; import io.metersphere.base.domain.IssuesWithBLOBs;
import io.metersphere.base.domain.Project; import io.metersphere.base.domain.Project;
import io.metersphere.commons.constants.CustomFieldType;
import io.metersphere.commons.constants.IssuesManagePlatform; import io.metersphere.commons.constants.IssuesManagePlatform;
import io.metersphere.commons.constants.IssuesStatus; import io.metersphere.commons.constants.IssuesStatus;
import io.metersphere.commons.exception.MSException; import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.LogUtil; import io.metersphere.commons.utils.LogUtil;
import io.metersphere.dto.CustomFieldDao;
import io.metersphere.dto.CustomFieldItemDTO; import io.metersphere.dto.CustomFieldItemDTO;
import io.metersphere.dto.IssueTemplateDao;
import io.metersphere.dto.UserDTO; import io.metersphere.dto.UserDTO;
import io.metersphere.service.CustomFieldService; import io.metersphere.service.CustomFieldService;
import io.metersphere.track.dto.DemandDTO; import io.metersphere.track.dto.DemandDTO;
import io.metersphere.track.issue.client.JiraClientV2; import io.metersphere.track.issue.client.JiraClientV2;
import io.metersphere.track.issue.domain.PlatformUser; import io.metersphere.track.issue.domain.PlatformUser;
import io.metersphere.track.issue.domain.jira.JiraAddIssueResponse; import io.metersphere.track.issue.domain.jira.*;
import io.metersphere.track.issue.domain.jira.JiraConfig;
import io.metersphere.track.issue.domain.jira.JiraIssue;
import io.metersphere.track.request.testcase.IssuesRequest; import io.metersphere.track.request.testcase.IssuesRequest;
import io.metersphere.track.request.testcase.IssuesUpdateRequest; import io.metersphere.track.request.testcase.IssuesUpdateRequest;
import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.CollectionUtils;
@ -28,9 +29,11 @@ import org.commonmark.renderer.html.HtmlRenderer;
import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.HttpClientErrorException;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.time.Instant;
import java.util.List; import java.time.LocalDateTime;
import java.util.UUID; import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;
public class JiraPlatform extends AbstractIssuePlatform { public class JiraPlatform extends AbstractIssuePlatform {
@ -348,4 +351,179 @@ public class JiraPlatform extends AbstractIssuePlatform {
public JiraConfig setUserConfig() { public JiraConfig setUserConfig() {
return setUserConfig(getUserPlatInfo(this.workspaceId)); return setUserConfig(getUserPlatInfo(this.workspaceId));
} }
public IssueTemplateDao getThirdPartTemplate() {
Set<String> ignoreSet = new HashSet() {{
add("timetracking");
add("attachment");
}};
JiraConfig config = getConfig();
String projectKey = getProjectId(this.projectId);
Map<String, JiraCreateMetadataResponse.Field> createMetadata = jiraClientV2.getCreateMetadata(projectKey, config.getIssuetype());
String userOptions = getUserOptions(projectKey);
List<CustomFieldDao> fields = new ArrayList<>();
char filedKey = 'A';
for (String name : createMetadata.keySet()) {
JiraCreateMetadataResponse.Field item = createMetadata.get(name);
if (ignoreSet.contains(name)) continue; // timetracking, attachment todo
JiraCreateMetadataResponse.Schema schema = item.getSchema();
CustomFieldDao customFieldDao = new CustomFieldDao();
customFieldDao.setKey(String.valueOf(filedKey++));
customFieldDao.setId(name);
customFieldDao.setCustomData(name);
customFieldDao.setName(item.getName());
customFieldDao.setRequired(item.isRequired());
setCustomFiledType(schema, customFieldDao, userOptions);
setCustomFiledDefaultValue(customFieldDao, item);
JSONArray options = getAllowedValuesOptions(item.getAllowedValues());
if (options != null)
customFieldDao.setOptions(options.toJSONString());
fields.add(customFieldDao);
}
fields = fields.stream().filter(i -> StringUtils.isNotBlank(i.getType()))
.collect(Collectors.toList());
// 按类型排序富文本排最后input 排最前面summary 排第一个
fields.sort((a, b) -> {
if (a.getType().equals(CustomFieldType.RICH_TEXT.getValue())) return 1;
if (b.getType().equals(CustomFieldType.RICH_TEXT.getValue())) return -1;
if (a.getId().equals("summary")) return -1;
if (b.getId().equals("summary")) return 1;
if (a.getType().equals(CustomFieldType.INPUT.getValue())) return -1;
if (b.getType().equals(CustomFieldType.INPUT.getValue())) return 1;
return a.getType().compareTo(b.getType());
});
IssueTemplateDao issueTemplateDao = new IssueTemplateDao();
issueTemplateDao.setCustomFields(fields);
issueTemplateDao.setPlatform(this.key);
return issueTemplateDao;
}
private void setCustomFiledType(JiraCreateMetadataResponse.Schema schema, CustomFieldDao customFieldDao, String userOptions) {
Map<String, String> fieldTypeMap = new HashMap() {{
put("summary", CustomFieldType.INPUT.getValue());
put("description", CustomFieldType.RICH_TEXT.getValue());
put("components", CustomFieldType.MULTIPLE_SELECT.getValue());
put("fixVersions", CustomFieldType.MULTIPLE_SELECT.getValue());
put("versions", CustomFieldType.MULTIPLE_SELECT.getValue());
put("priority", CustomFieldType.SELECT.getValue());
put("environment", CustomFieldType.RICH_TEXT.getValue());
put("labels", CustomFieldType.MULTIPLE_INPUT.getValue());
}};
String customType = schema.getCustom();
String value = null;
if (StringUtils.isNotBlank(customType)) {
// 自定义字段
if (customType.contains("multiselect")) {
value = CustomFieldType.MULTIPLE_SELECT.getValue();
} else if (customType.contains("cascadingselect")) {
value = "cascadingSelect";
} else if (customType.contains("userpicker")) {
value = CustomFieldType.SELECT.getValue();
customFieldDao.setOptions(userOptions);
} else if (customType.contains("multicheckboxes")) {
value = CustomFieldType.CHECKBOX.getValue();
customFieldDao.setDefaultValue(JSONObject.toJSONString(new ArrayList<>()));
} else if (customType.contains("radiobuttons")) {
value = CustomFieldType.RADIO.getValue();
} else if (customType.contains("textfield")) {
value = CustomFieldType.INPUT.getValue();
} else if (customType.contains("datetime")) {
value = CustomFieldType.DATETIME.getValue();
} else if (customType.contains("datepicker")) {
value = CustomFieldType.DATE.getValue();
} else if (customType.contains("float")) {
value = CustomFieldType.FLOAT.getValue();
} else if (customType.contains("select")) {
value = CustomFieldType.SELECT.getValue();
} else if (customType.contains("url")) {
value = CustomFieldType.INPUT.getValue();
} else if (customType.contains("textarea")) {
value = CustomFieldType.TEXTAREA.getValue();
} else if (customType.contains("labels")) {
value = CustomFieldType.MULTIPLE_INPUT.getValue();
}
} else {
// 系统字段
value = fieldTypeMap.get(customFieldDao.getId());
String type = schema.getType();
if ("user".equals(type)) {
value = CustomFieldType.SELECT.getValue();
customFieldDao.setOptions(userOptions);
} else if ("date".equals(type)) {
value = CustomFieldType.DATE.getValue();
} else if ("datetime".equals(type)) {
value = CustomFieldType.DATETIME.getValue();
}
}
customFieldDao.setType(value);
}
private void setCustomFiledDefaultValue(CustomFieldDao customFieldDao, JiraCreateMetadataResponse.Field item) {
if (item.isHasDefaultValue()) {
Object defaultValue = item.getDefaultValue();
if (defaultValue != null) {
Object msDefaultValue;
if (defaultValue instanceof JSONObject) {
msDefaultValue = ((JSONObject) defaultValue).get("id");
} else if (defaultValue instanceof JSONArray) {
List defaultList = new ArrayList();
((JSONArray) defaultValue).forEach(i -> {
if (i instanceof JSONObject) {
JSONObject obj = (JSONObject) i;
defaultList.add(obj.get("id"));
} else {
defaultList.add(i);
}
});
msDefaultValue = defaultList;
} else {
if (customFieldDao.getType().equals(CustomFieldType.DATE.getValue())) {
msDefaultValue = Instant.ofEpochMilli((Long) defaultValue).atZone(ZoneId.systemDefault()).toLocalDate();
} else if (customFieldDao.getType().equals(CustomFieldType.DATETIME.getValue())) {
msDefaultValue = LocalDateTime.ofInstant(Instant.ofEpochMilli((Long) defaultValue), ZoneId.systemDefault()).toString();
} else {
msDefaultValue = defaultValue;
}
}
customFieldDao.setDefaultValue(JSONObject.toJSONString(msDefaultValue));
}
}
}
private JSONArray getAllowedValuesOptions(List<JiraCreateMetadataResponse.AllowedValues> allowedValues) {
if (allowedValues != null) {
JSONArray options = new JSONArray();
allowedValues.forEach(val -> {
JSONObject jsonObject = new JSONObject();
jsonObject.put("value", val.getId());
if (StringUtils.isNotBlank(val.getName())) {
jsonObject.put("text", val.getName());
} else {
jsonObject.put("text", val.getValue());
}
JSONArray children = getAllowedValuesOptions(val.getChildren());
if (children != null) {
jsonObject.put("children", children);
}
options.add(jsonObject);
});
return options;
}
return null;
}
private String getUserOptions(String projectKey) {
List<JiraUser> userOptions = jiraClientV2.getAssignableUser(projectKey);
JSONArray options = new JSONArray();
userOptions.forEach(val -> {
JSONObject jsonObject = new JSONObject();
jsonObject.put("value", val.getAccountId());
jsonObject.put("text", val.getDisplayName());
options.add(jsonObject);
});
return options.toJSONString();
}
} }

View File

@ -635,4 +635,15 @@ public class IssuesService {
return issuesMapper.selectByExampleWithBLOBs(example); return issuesMapper.selectByExampleWithBLOBs(example);
} }
public IssueTemplateDao getThirdPartTemplate(String projectId) {
if (StringUtils.isNotBlank(projectId)) {
Project project = projectService.getProjectById(projectId);
IssuesRequest issuesRequest = new IssuesRequest();
issuesRequest.setProjectId(projectId);
issuesRequest.setWorkspaceId(project.getWorkspaceId());
return IssueFactory.createPlatform(IssuesManagePlatform.Jira.toString(), issuesRequest)
.getThirdPartTemplate();
}
return new IssueTemplateDao();
}
} }

@ -1 +1 @@
Subproject commit b0c13798836a8bfbc1d415bc5a9610c56df61833 Subproject commit a618ef0fb1ae45790e0c7de9805c8ff17c9df5be

View File

@ -7,7 +7,7 @@
<el-input v-model="form.name" autocomplete="off"></el-input> <el-input v-model="form.name" autocomplete="off"></el-input>
</el-form-item> </el-form-item>
<el-form-item v-if="platformOptions.length > 1" :label-width="labelWidth" :label="$t('集成第三方平台')" <el-form-item v-if="platformOptions.length > 1" :label-width="labelWidth" :label="$t('test_track.issue.third_party_integrated')"
prop="platform"> prop="platform">
<el-select filterable v-model="form.platform"> <el-select filterable v-model="form.platform">
<el-option v-for="item in platformOptions" :key="item.value" :label="item.text" :value="item.value"> <el-option v-for="item in platformOptions" :key="item.value" :label="item.text" :value="item.value">
@ -19,11 +19,11 @@
<template-select :data="form" scene="API_CASE" prop="caseTemplateId" ref="caseTemplate"/> <template-select :data="form" scene="API_CASE" prop="caseTemplateId" ref="caseTemplate"/>
</el-form-item> </el-form-item>
<el-form-item v-if="xpackEable" :label-width="labelWidth" :label="$t('使用第三方平台模板')" prop="scenarioCustomNum"> <el-form-item v-if="form.platform == 'Jira'" :label-width="labelWidth" :label="$t('test_track.issue.use_third_party')" prop="scenarioCustomNum">
<el-switch v-model="form.thirdPartTemplate"></el-switch> <el-switch v-model="form.thirdPartTemplate"></el-switch>
</el-form-item> </el-form-item>
<el-form-item v-if="!xpackEable || !form.thirdPartTemplate" :label-width="labelWidth" <el-form-item v-if="!form.thirdPartTemplate" :label-width="labelWidth"
:label="$t('workspace.issue_template_manage')" prop="issueTemplateId"> :label="$t('workspace.issue_template_manage')" prop="issueTemplateId">
<template-select :platform="form.platform" :data="form" scene="ISSUE" prop="issueTemplateId" <template-select :platform="form.platform" :data="form" scene="ISSUE" prop="issueTemplateId"
ref="issueTemplate"/> ref="issueTemplate"/>
@ -164,8 +164,7 @@ export default {
}, },
screenHeight: 'calc(100vh - 195px)', screenHeight: 'calc(100vh - 195px)',
labelWidth: '150px', labelWidth: '150px',
platformOptions: [], platformOptions: []
xpackEable: false
}; };
}, },
props: { props: {
@ -183,7 +182,6 @@ export default {
this.create(); this.create();
this.$router.replace('/setting/project/all'); this.$router.replace('/setting/project/all');
} }
this.xpackEable = hasLicense();
}, },
computed: { computed: {
currentUser: () => { currentUser: () => {
@ -242,7 +240,7 @@ export default {
if (platforms.indexOf(platform) === -1) { if (platforms.indexOf(platform) === -1) {
for (let i = 0; i < this.platformOptions.length; i++) { for (let i = 0; i < this.platformOptions.length; i++) {
if (this.platformOptions[i].value === platform) { if (this.platformOptions[i].value === platform) {
this.platformOptions.splice(1, i); this.platformOptions.splice(i, 1);
break; break;
} }
} }

View File

@ -95,7 +95,7 @@ export function deleteIssueRelate(param, callback) {
export function getIssueThirdPartTemplate() { export function getIssueThirdPartTemplate() {
return new Promise(resolve => { return new Promise(resolve => {
baseGet('/xpack/issue/template/' + getCurrentProjectID(), (data) => { baseGet('/issues/thirdpart/template/' + getCurrentProjectID(), (data) => {
let template = data; let template = data;
if (template.customFields) { if (template.customFields) {
template.customFields.forEach(item => { template.customFields.forEach(item => {
@ -129,7 +129,7 @@ export function getIssuePartTemplateWithProject(callback) {
} }
export function enableThirdPartTemplate(currentProject) { export function enableThirdPartTemplate(currentProject) {
return hasLicense() && currentProject && currentProject.thirdPartTemplate && currentProject.platform === JIRA; return currentProject && currentProject.thirdPartTemplate && currentProject.platform === JIRA;
} }
export function isThirdPartEnable(callback) { export function isThirdPartEnable(callback) {