Pr@v1.10@tfs (#5244)

* feat:azuredevops add issue

* feat:azuredevops add issue

* feat:azuredevops取消集成

* feat:azuredevops支持自定义字段

* feat:azuredevops add issue

feat:azuredevops add issue

feat:azuredevops取消集成

feat:azuredevops支持自定义字段

feat:azuredevops add issue

feat:azuredevops add issue

feat:azuredevops取消集成

feat:azuredevops支持自定义字段

fix:fix flyway version
This commit is contained in:
shenkaibo 2021-08-04 09:53:59 +08:00 committed by GitHub
parent cf9507da4b
commit 4dd976cd2e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 514 additions and 35 deletions

4
.gitmodules vendored
View File

@ -1,7 +1,3 @@
[submodule "backend/src/main/java/io/metersphere/xpack"]
path = backend/src/main/java/io/metersphere/xpack
url = git@github.com:metersphere/xpack-backend.git
branch = master
[submodule "frontend/src/business/components/xpack"]
path = frontend/src/business/components/xpack
url = git@github.com:metersphere/xpack-frontend.git

View File

@ -23,6 +23,8 @@ public class Project implements Serializable {
private String zentaoId;
private String azureDevopsId;
private Boolean repeatable;
private String caseTemplateId;

View File

@ -714,6 +714,76 @@ public class ProjectExample {
return (Criteria) this;
}
public Criteria andAzureDevopsIdIsNull() {
addCriterion("azure_devops_id is null");
return (Criteria) this;
}
public Criteria andAzureDevopsIdIsNotNull() {
addCriterion("azure_devops_id is not null");
return (Criteria) this;
}
public Criteria andAzureDevopsIdEqualTo(String value) {
addCriterion("azure_devops_id =", value, "azureDevopsId");
return (Criteria) this;
}
public Criteria andAzureDevopsIdNotEqualTo(String value) {
addCriterion("azure_devops_id <>", value, "azureDevopsId");
return (Criteria) this;
}
public Criteria andAzureDevopsIdGreaterThan(String value) {
addCriterion("azure_devops_id >", value, "azureDevopsId");
return (Criteria) this;
}
public Criteria andAzureDevopsIdGreaterThanOrEqualTo(String value) {
addCriterion("azure_devops_id >=", value, "azureDevopsId");
return (Criteria) this;
}
public Criteria andAzureDevopsIdLessThan(String value) {
addCriterion("azure_devops_id <", value, "azureDevopsId");
return (Criteria) this;
}
public Criteria andAzureDevopsIdLessThanOrEqualTo(String value) {
addCriterion("azure_devops_id <=", value, "azureDevopsId");
return (Criteria) this;
}
public Criteria andAzureDevopsIdLike(String value) {
addCriterion("azure_devops_id like", value, "azureDevopsId");
return (Criteria) this;
}
public Criteria andAzureDevopsIdNotLike(String value) {
addCriterion("azure_devops_id not like", value, "azureDevopsId");
return (Criteria) this;
}
public Criteria andAzureDevopsIdIn(List<String> values) {
addCriterion("azure_devops_id in", values, "azureDevopsId");
return (Criteria) this;
}
public Criteria andAzureDevopsIdNotIn(List<String> values) {
addCriterion("azure_devops_id not in", values, "azureDevopsId");
return (Criteria) this;
}
public Criteria andAzureDevopsIdBetween(String value1, String value2) {
addCriterion("azure_devops_id between", value1, value2, "azureDevopsId");
return (Criteria) this;
}
public Criteria andAzureDevopsIdNotBetween(String value1, String value2) {
addCriterion("azure_devops_id not between", value1, value2, "azureDevopsId");
return (Criteria) this;
}
public Criteria andRepeatableIsNull() {
addCriterion("`repeatable` is null");
return (Criteria) this;

View File

@ -11,6 +11,7 @@
<result column="tapd_id" jdbcType="VARCHAR" property="tapdId" />
<result column="jira_key" jdbcType="VARCHAR" property="jiraKey" />
<result column="zentao_id" jdbcType="VARCHAR" property="zentaoId" />
<result column="azure_devops_id" jdbcType="VARCHAR" property="azureDevopsId" />
<result column="repeatable" jdbcType="BIT" property="repeatable" />
<result column="case_template_id" jdbcType="VARCHAR" property="caseTemplateId" />
<result column="issue_template_id" jdbcType="VARCHAR" property="issueTemplateId" />
@ -79,8 +80,8 @@
</sql>
<sql id="Base_Column_List">
id, workspace_id, `name`, description, create_time, update_time, tapd_id, jira_key,
zentao_id, `repeatable`, case_template_id, issue_template_id, custom_num, scenario_custom_num,
create_user, system_id
zentao_id, azure_devops_id, `repeatable`, case_template_id, issue_template_id, custom_num,
scenario_custom_num, create_user, system_id
</sql>
<select id="selectByExample" parameterType="io.metersphere.base.domain.ProjectExample" resultMap="BaseResultMap">
select
@ -116,15 +117,15 @@
insert into project (id, workspace_id, `name`,
description, create_time, update_time,
tapd_id, jira_key, zentao_id,
`repeatable`, case_template_id, issue_template_id,
custom_num, scenario_custom_num, create_user,
system_id)
azure_devops_id, `repeatable`, case_template_id,
issue_template_id, custom_num, scenario_custom_num,
create_user, system_id)
values (#{id,jdbcType=VARCHAR}, #{workspaceId,jdbcType=VARCHAR}, #{name,jdbcType=VARCHAR},
#{description,jdbcType=VARCHAR}, #{createTime,jdbcType=BIGINT}, #{updateTime,jdbcType=BIGINT},
#{tapdId,jdbcType=VARCHAR}, #{jiraKey,jdbcType=VARCHAR}, #{zentaoId,jdbcType=VARCHAR},
#{repeatable,jdbcType=BIT}, #{caseTemplateId,jdbcType=VARCHAR}, #{issueTemplateId,jdbcType=VARCHAR},
#{customNum,jdbcType=BIT}, #{scenarioCustomNum,jdbcType=BIT}, #{createUser,jdbcType=VARCHAR},
#{systemId,jdbcType=VARCHAR})
#{azureDevopsId,jdbcType=VARCHAR}, #{repeatable,jdbcType=BIT}, #{caseTemplateId,jdbcType=VARCHAR},
#{issueTemplateId,jdbcType=VARCHAR}, #{customNum,jdbcType=BIT}, #{scenarioCustomNum,jdbcType=BIT},
#{createUser,jdbcType=VARCHAR}, #{systemId,jdbcType=VARCHAR})
</insert>
<insert id="insertSelective" parameterType="io.metersphere.base.domain.Project">
insert into project
@ -156,6 +157,9 @@
<if test="zentaoId != null">
zentao_id,
</if>
<if test="azureDevopsId != null">
azure_devops_id,
</if>
<if test="repeatable != null">
`repeatable`,
</if>
@ -206,6 +210,9 @@
<if test="zentaoId != null">
#{zentaoId,jdbcType=VARCHAR},
</if>
<if test="azureDevopsId != null">
#{azureDevopsId,jdbcType=VARCHAR},
</if>
<if test="repeatable != null">
#{repeatable,jdbcType=BIT},
</if>
@ -265,6 +272,9 @@
<if test="record.zentaoId != null">
zentao_id = #{record.zentaoId,jdbcType=VARCHAR},
</if>
<if test="record.azureDevopsId != null">
azure_devops_id = #{record.azureDevopsId,jdbcType=VARCHAR},
</if>
<if test="record.repeatable != null">
`repeatable` = #{record.repeatable,jdbcType=BIT},
</if>
@ -302,6 +312,7 @@
tapd_id = #{record.tapdId,jdbcType=VARCHAR},
jira_key = #{record.jiraKey,jdbcType=VARCHAR},
zentao_id = #{record.zentaoId,jdbcType=VARCHAR},
azure_devops_id = #{record.azureDevopsId,jdbcType=VARCHAR},
`repeatable` = #{record.repeatable,jdbcType=BIT},
case_template_id = #{record.caseTemplateId,jdbcType=VARCHAR},
issue_template_id = #{record.issueTemplateId,jdbcType=VARCHAR},
@ -340,6 +351,9 @@
<if test="zentaoId != null">
zentao_id = #{zentaoId,jdbcType=VARCHAR},
</if>
<if test="azureDevopsId != null">
azure_devops_id = #{azureDevopsId,jdbcType=VARCHAR},
</if>
<if test="repeatable != null">
`repeatable` = #{repeatable,jdbcType=BIT},
</if>
@ -374,6 +388,7 @@
tapd_id = #{tapdId,jdbcType=VARCHAR},
jira_key = #{jiraKey,jdbcType=VARCHAR},
zentao_id = #{zentaoId,jdbcType=VARCHAR},
azure_devops_id = #{azureDevopsId,jdbcType=VARCHAR},
`repeatable` = #{repeatable,jdbcType=BIT},
case_template_id = #{caseTemplateId,jdbcType=VARCHAR},
issue_template_id = #{issueTemplateId,jdbcType=VARCHAR},

View File

@ -33,7 +33,7 @@
<select id="getProjectWithWorkspace" resultType="io.metersphere.dto.ProjectDTO">
select p.id, p.workspace_id, p.name, p.description, p.update_time, p.issue_template_id, p.case_template_id,
p.create_time, w.id as workspaceId, w.name as workspaceName, p.tapd_id, p.jira_key, p.zentao_id,p.repeatable, p.custom_num,
p.create_time, w.id as workspaceId, w.name as workspaceName, p.tapd_id, p.jira_key, p.zentao_id,p.azure_devops_id,p.repeatable, p.custom_num,
user.name as createUserName,
p.scenario_custom_num
from project p
@ -131,6 +131,9 @@
<if test="platform == 'Zentao'">
zentao_id = null
</if>
<if test="platform == 'AzureDevops'">
azure_devops_id = null
</if>
</set>
where project.id in (select id from (select id
from project

View File

@ -1,5 +1,5 @@
package io.metersphere.commons.constants;
public enum IssuesManagePlatform {
Tapd, Jira, Local, Zentao
Tapd, Jira, Local, Zentao, AzureDevops
}

View File

@ -29,6 +29,7 @@ public class UserDTO extends User {
private String tapdUserName;
private String zentaoUserName;
private String zentaoPassword;
private String azureDevopsPat;
}
}

View File

@ -130,7 +130,7 @@ public abstract class AbstractIssuePlatform implements IssuesPlatform {
*
* @return 其他平台和本地项目绑定的属性值
*/
abstract String getProjectId(String projectId);
public abstract String getProjectId(String projectId);
protected boolean isIntegratedPlatform(String orgId, String platform) {
IntegrationRequest request = new IntegrationRequest();

View File

@ -4,10 +4,9 @@ import io.metersphere.commons.constants.IssuesManagePlatform;
import io.metersphere.track.request.testcase.IssuesRequest;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
public class IssueFactory {
public static AbstractIssuePlatform createPlatform(String platform, IssuesRequest addIssueRequest) {
@ -17,6 +16,24 @@ public class IssueFactory {
return new JiraPlatform(addIssueRequest);
} else if (StringUtils.equals(IssuesManagePlatform.Zentao.toString(), platform)) {
return new ZentaoPlatform(addIssueRequest);
} else if (StringUtils.equals(IssuesManagePlatform.AzureDevops.toString(), platform)) {
ClassLoader loader = Thread.currentThread().getContextClassLoader();
try {
Class clazz = loader.loadClass("io.metersphere.xpack.issue.azureDevops.AzureDevopsPlatform");
Constructor cons = clazz.getDeclaredConstructor(new Class[] { IssuesRequest.class });
AbstractIssuePlatform azureDevopsPlatform = (AbstractIssuePlatform) cons.newInstance(addIssueRequest);
return azureDevopsPlatform;
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
} else if (StringUtils.equalsIgnoreCase(IssuesManagePlatform.Local.toString(), platform)) {
return new LocalPlatform(addIssueRequest);
}

View File

@ -298,7 +298,7 @@ public class JiraPlatform extends AbstractIssuePlatform {
}
@Override
String getProjectId(String projectId) {
public String getProjectId(String projectId) {
if (StringUtils.isNotBlank(projectId)) {
return projectService.getProjectById(projectId).getJiraKey();
}

View File

@ -25,5 +25,5 @@ public abstract class LocalAbstractPlatform extends AbstractIssuePlatform {
public void syncIssues(Project project, List<IssuesDao> tapdIssues) {}
@Override
String getProjectId(String projectId) { return null; }
public String getProjectId(String projectId) { return null; }
}

View File

@ -236,7 +236,7 @@ public class TapdPlatform extends AbstractIssuePlatform {
}
@Override
String getProjectId(String projectId) {
public String getProjectId(String projectId) {
if (StringUtils.isNotBlank(projectId)) {
return projectService.getProjectById(projectId).getTapdId();
}

View File

@ -70,7 +70,7 @@ public class ZentaoPlatform extends AbstractIssuePlatform {
}
@Override
String getProjectId(String projectId) {
public String getProjectId(String projectId) {
if (StringUtils.isNotBlank(projectId)) {
return projectService.getProjectById(projectId).getZentaoId();
}

View File

@ -34,6 +34,7 @@ public class DemandService {
boolean tapd = issuesService.isIntegratedPlatform(orgId, IssuesManagePlatform.Tapd.toString());
boolean jira = issuesService.isIntegratedPlatform(orgId, IssuesManagePlatform.Jira.toString());
boolean zentao = issuesService.isIntegratedPlatform(orgId, IssuesManagePlatform.Zentao.toString());
boolean azureDevops = issuesService.isIntegratedPlatform(orgId, IssuesManagePlatform.AzureDevops.toString());
List<DemandDTO> list = new ArrayList<>();
List<String> platforms = new ArrayList<>();
IssuesRequest issueRequest = new IssuesRequest();
@ -59,6 +60,13 @@ public class DemandService {
}
}
if (azureDevops) {
String azureDevopsId = project.getAzureDevopsId();
if (StringUtils.isNotBlank(azureDevopsId)) {
platforms.add(IssuesManagePlatform.AzureDevops.name());
}
}
issueRequest.setOrganizationId(orgId);
List<AbstractIssuePlatform> platformList = IssueFactory.createPlatforms(platforms, issueRequest);
platformList.forEach(platform -> {

View File

@ -40,6 +40,8 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@ -88,6 +90,8 @@ public class IssuesService {
abstractPlatform.testAuth();
}
public void addIssues(IssuesUpdateRequest issuesRequest) {
List<AbstractIssuePlatform> platformList = getUpdatePlatforms(issuesRequest);
platformList.forEach(platform -> {
@ -453,6 +457,9 @@ public class IssuesService {
List<IssuesDao> zentaoIssues = issues.stream()
.filter(item -> item.getPlatform().equals(IssuesManagePlatform.Zentao.name()))
.collect(Collectors.toList());
List<IssuesDao> azureDevopsIssues = issues.stream()
.filter(item -> item.getPlatform().equals(IssuesManagePlatform.AzureDevops.name()))
.collect(Collectors.toList());
IssuesRequest issuesRequest = new IssuesRequest();
issuesRequest.setProjectId(projectId);
@ -469,6 +476,25 @@ public class IssuesService {
ZentaoPlatform zentaoPlatform = new ZentaoPlatform(issuesRequest);
syncThirdPartyIssues(zentaoPlatform::syncIssues, project, zentaoIssues);
}
if (CollectionUtils.isNotEmpty(azureDevopsIssues)) {
ClassLoader loader = Thread.currentThread().getContextClassLoader();
try {
Class clazz = loader.loadClass("io.metersphere.xpack.issue.azureDevops.AzureDevopsPlatform");
Constructor cons = clazz.getDeclaredConstructor(new Class[] { IssuesRequest.class });
AbstractIssuePlatform azureDevopsPlatform = (AbstractIssuePlatform) cons.newInstance(issuesRequest);
syncThirdPartyIssues(azureDevopsPlatform::syncIssues, project, azureDevopsIssues);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}

@ -1 +0,0 @@
Subproject commit 52e757ba127c9b33e2715c02bc618aa315192ceb

View File

@ -0,0 +1,2 @@
alter table project add azure_devops_id varchar(50) null after zentao_id;
alter table custom_field_template modify default_value varchar(100) null ;

View File

@ -64,7 +64,7 @@
<!--要生成的数据库表 -->
<table tableName="test_plan_test_case"/>
<!--<table tableName="test_plan_test_case"/>-->
<!--<table tableName="swagger_url_project"/>
<table tableName="user_header"/>-->
<!--<table tableName="test_plan_api_scenario"/>-->
@ -78,16 +78,21 @@
<table tableName="test_plan"/>
<table tableName="test_case_test"/>-->
<!-- <table tableName="api_test_environment"></table>-->
<!--
<table tableName="project"></table>
-->
<!-- <table tableName="custom_field"></table>-->
<!-- <table tableName="test_case"></table>-->
<!-- <table tableName="test_case"></table>-->
<<<<<<< HEAD
<table tableName="test_plan_report_resource"></table>
<!-- <table tableName="test_case"></table>-->
<!-- <table tableName="api_test_case"></table>-->
<!-- <table tableName="api_definition"></table>-->
=======
<!-- <table tableName="api_scenario"></table>
<table tableName="test_case"></table>
<table tableName="api_test_case"></table>
<table tableName="api_definition"></table>-->
>>>>>>> feat:azuredevops add issue

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

View File

@ -12,13 +12,16 @@
<el-radio label="Zentao">
<img class="zentao_platform" src="../../../../assets/zentao.jpg" alt="Zentao"/>
</el-radio>
<el-radio label="AzureDevops" v-xpack>
<img class="platform" src="../../../../assets/AzureDevops.png" alt="AzureDevops"/>
</el-radio>
</el-radio-group>
</div>
<tapd-setting v-if="tapdEnable" ref="tapdSetting"/>
<jira-setting v-if="jiraEnable" ref="jiraSetting"/>
<zentao-setting v-if="zentaoEnable" ref="zentaoSetting"/>
<azuredevops-setting v-if="azuredevopsEnable" ref="azureDevopsSetting"/>
</div>
</template>
@ -26,16 +29,18 @@
import TapdSetting from "@/business/components/settings/organization/components/TapdSetting";
import JiraSetting from "@/business/components/settings/organization/components/JiraSetting";
import ZentaoSetting from "@/business/components/settings/organization/components/ZentaoSetting";
import {JIRA, TAPD, ZEN_TAO} from "@/common/js/constants";
import AzuredevopsSetting from "@/business/components/settings/organization/components/AzureDevopsSetting";
import {JIRA, TAPD, ZEN_TAO, AZURE_DEVOPS} from "@/common/js/constants";
export default {
name: "BugManagement",
components: {TapdSetting, JiraSetting, ZentaoSetting},
components: {TapdSetting, JiraSetting, ZentaoSetting, AzuredevopsSetting},
data() {
return {
tapdEnable: true,
jiraEnable: false,
zentaoEnable: false,
azuredevopsEnable:false,
result: {},
platform: TAPD
}
@ -46,14 +51,22 @@ export default {
this.tapdEnable = true;
this.jiraEnable = false;
this.zentaoEnable = false;
this.azuredevopsEnable = false;
} else if (platform === JIRA) {
this.tapdEnable = false;
this.jiraEnable = true;
this.zentaoEnable = false;
this.azuredevopsEnable = false;
} else if (platform === ZEN_TAO) {
this.tapdEnable = false;
this.jiraEnable = false;
this.zentaoEnable = true;
this.azuredevopsEnable = false;
} else if (platform === AZURE_DEVOPS) {
this.tapdEnable = false;
this.jiraEnable = false;
this.zentaoEnable = false;
this.azuredevopsEnable = true;
}
}
}

View File

@ -0,0 +1,230 @@
<template>
<div>
<div style="width: 500px">
<div style="margin-top: 20px;margin-bottom: 10px">{{ $t('organization.integration.basic_auth_info') }}</div>
<el-form :model="form" ref="form" label-width="155px" size="small" :disabled="show" :rules="rules">
<el-form-item :label="$t('organization.integration.azure_pat')" prop="pat">
<el-input v-model="form.pat" auto-complete="new-password" v-if="showInput"
:placeholder="$t('organization.integration.input_azure_pat')" show-password/>
</el-form-item>
<el-form-item :label="$t('organization.integration.azure_devops_url')" prop="url">
<el-input v-model="form.url" :placeholder="$t('organization.integration.input_azure_url')"/>
</el-form-item>
<el-form-item :label="$t('organization.integration.azure_organization_id')" prop="organization">
<el-input v-model="form.organization" :placeholder="$t('organization.integration.input_azure_organization_id')"/>
</el-form-item>
<el-form-item :label="$t('organization.integration.azure_issuetype')" prop="issuetype">
<el-input v-model="form.issuetype" :placeholder="$t('organization.integration.input_azure_issuetype')"/>
<ms-instructions-icon effect="light">
<template>
<img class="jira-image" src="../../../../../assets/azureDevops-type.png"/>
</template>
</ms-instructions-icon>
</el-form-item>
<el-form-item :label="$t('organization.integration.azure_storytype')" prop="storytype">
<el-input v-model="form.storytype" :placeholder="$t('organization.integration.input_azure_storytype')"/>
<ms-instructions-icon effect="light">
<template>
<img class="jira-image" src="../../../../../assets/azureDevops-type.png"/>
</template>
</ms-instructions-icon>
</el-form-item>
</el-form>
</div>
<bug-manage-btn @save="save"
@init="init"
:edit-permission="['ORGANIZATION_SERVICE:READ+EDIT']"
@testConnection="testConnection"
@cancelIntegration="cancelIntegration"
@reloadPassInput="reloadPassInput"
:form="form"
:show.sync="show"
ref="bugBtn"/>
<div class="defect-tip">
<div>{{ $t('organization.integration.use_tip') }}</div>
<div>
1. {{ $t('organization.integration.use_tip_azure') }}
</div>
<div>
2. {{ $t('organization.integration.use_tip_two') }}
<router-link to="/setting/project/all" style="margin-left: 5px">
{{ $t('organization.integration.link_the_project_now') }}
</router-link>
</div>
<div>
3. {{ $t('organization.integration.use_tip_three') }}
<router-link :to="{name: 'PersonSetting', params: { open: true }}" style="margin-left: 5px">
{{ $t('organization.integration.link_the_info_now') }}
</router-link>
</div>
</div>
</div>
</template>
<script>
import BugManageBtn from "@/business/components/settings/organization/components/BugManageBtn";
import {getCurrentOrganizationId, getCurrentUser} from "@/common/js/utils";
import {AZURE_DEVOPS} from "@/common/js/constants";
import MsInstructionsIcon from "@/business/components/common/components/MsInstructionsIcon";
export default {
name: "AzureDevopsSetting",
components: {MsInstructionsIcon, BugManageBtn},
created() {
this.init();
},
data() {
return {
show: true,
showInput: true,
form: {},
rules: {
pat: {
required: true,
message: this.$t('organization.integration.input_azure_pat'),
trigger: ['change', 'blur']
},
url: {
required: true,
message: this.$t('organization.integration.input_azure_url'),
trigger: ['change', 'blur']
},
organization: {
required: true,
message: this.$t('organization.integration.input_azure_organization_id'),
trigger: ['change', 'blur']
},
issuetype: {
required: true,
message: this.$t('organization.integration.input_azure_issuetype'),
trigger: ['change', 'blur']
},
storytype: {
required: true,
message: this.$t('organization.integration.input_azure_storytype'),
trigger: ['change', 'blur']
}
},
}
},
methods: {
init() {
const {lastOrganizationId} = getCurrentUser();
let param = {};
param.platform = AZURE_DEVOPS;
param.orgId = lastOrganizationId;
this.$parent.result = this.$post("service/integration/type", param, response => {
let data = response.data;
if (data.configuration) {
let config = JSON.parse(data.configuration);
this.$set(this.form, 'pat', config.pat);
this.$set(this.form, 'url', config.url);
this.$set(this.form, 'organization', config.organization);
this.$set(this.form, 'issuetype', config.issuetype);
this.$set(this.form, 'storytype', config.storytype);
} else {
this.clear();
}
})
},
save() {
this.$refs['form'].validate(valid => {
if (valid) {
let formatUrl = this.form.url.trim();
if (!formatUrl.endsWith('/')) {
formatUrl = formatUrl + '/';
}
let param = {};
let auth = {
pat: this.form.pat,
url: formatUrl,
organization:this.form.organization,
issuetype: this.form.issuetype,
storytype: this.form.storytype
};
const {lastOrganizationId} = getCurrentUser();
param.organizationId = lastOrganizationId;
param.platform = AZURE_DEVOPS;
param.configuration = JSON.stringify(auth);
this.$parent.result = this.$post("service/integration/save", param, () => {
this.show = true;
this.$refs.bugBtn.showEdit = true;
this.$refs.bugBtn.showSave = false;
this.$refs.bugBtn.showCancel = false;
this.reloadPassInput();
this.init();
this.$success(this.$t('commons.save_success'));
});
} else {
return false;
}
})
},
clear() {
this.$set(this.form, 'pat', '');
this.$set(this.form, 'url', '');
this.$set(this.form, 'organization', '');
this.$set(this.form, 'issuetype', '');
this.$set(this.form, 'storytype', '');
this.$nextTick(() => {
if (this.$refs.form) {
this.$refs.form.clearValidate();
}
});
},
testConnection() {
if (this.form.pat) {
this.$parent.result = this.$get("issues/auth/" + getCurrentOrganizationId() + '/' + AZURE_DEVOPS, () => {
this.$success(this.$t('organization.integration.verified'));
});
} else {
this.$warning(this.$t('organization.integration.not_integrated'));
return false;
}
},
cancelIntegration() {
if (this.form.url && this.form.pat) {
this.$alert(this.$t('organization.integration.cancel_confirm') + AZURE_DEVOPS + "", '', {
confirmButtonText: this.$t('commons.confirm'),
callback: (action) => {
if (action === 'confirm') {
const {lastOrganizationId} = getCurrentUser();
let param = {};
param.orgId = lastOrganizationId;
param.platform = AZURE_DEVOPS;
this.$parent.result = this.$post("service/integration/delete", param, () => {
this.$success(this.$t('organization.integration.successful_operation'));
this.init('');
});
}
}
});
} else {
this.$warning(this.$t('organization.integration.not_integrated'));
}
},
reloadPassInput() {
this.showInput = false;
this.$nextTick(function () {
this.showInput = true;
});
}
}
}
</script>
<style scoped>
.defect-tip {
background: #EDEDED;
border: solid #E1E1E1 1px;
margin: 10px 0;
padding: 10px;
border-radius: 3px;
}
.el-input {
width: 80%;
}
</style>

View File

@ -0,0 +1,30 @@
<template>
<el-form label-position="right" label-width="160px" size="small">
<el-form-item :label="'AzureDevops 信息'">
<ms-instructions-icon size="10" :content="'该信息为通过Azure Devops提交缺陷的用户令牌信息若未填写则使用组织中配置的默认信息'"/>
</el-form-item>
<el-form-item :label="$t('organization.integration.azure_pat')" prop="pat">
<el-input v-model="data.azureDevopsPat" :placeholder="$t('organization.integration.input_azure_pat')"/>
</el-form-item>
<el-form-item >
<el-button type="primary" style="float: right" @click="$emit('auth', 'AzureDevops')" size="mini">
{{$t('commons.validate')}}
</el-button>
</el-form-item>
</el-form>
</template>
<script>
import MsInstructionsIcon from "@/business/components/common/components/MsInstructionsIcon";
export default {
name: "AzureDevopsUserInfo",
components: {MsInstructionsIcon},
props: ['data'],
}
</script>
<style scoped>
.instructions-icon {
margin-left: -5px;
}
</style>

View File

@ -54,6 +54,7 @@
<jira-user-info @auth="handleAuth" v-if="hasJira" :data="currentPlatformInfo"/>
<tapd-user-info @auth="handleAuth" v-if="hasTapd" :data="currentPlatformInfo"/>
<zentao-user-info @auth="handleAuth" v-if="hasZentao" :data="currentPlatformInfo"/>
<azure-devops-user-info @auth="handleAuth" v-if="hasAzure" :data="currentPlatformInfo"/>
<template v-slot:footer>
<ms-dialog-footer
@cancel="updateVisible = false"
@ -100,10 +101,11 @@ import JiraUserInfo from "@/business/components/settings/personal/JiraUserInfo";
import TapdUserInfo from "@/business/components/settings/personal/TapdUserInfo";
import {getIntegrationService} from "@/network/organization";
import ZentaoUserInfo from "@/business/components/settings/personal/ZentaoUserInfo";
import AzureDevopsUserInfo from "@/business/components/settings/personal/AzureDevopsUserInfo";
export default {
name: "MsPersonSetting",
components: {ZentaoUserInfo, TapdUserInfo, JiraUserInfo, MsDialogFooter, MsTableOperatorButton},
components: {ZentaoUserInfo, TapdUserInfo, JiraUserInfo,AzureDevopsUserInfo, MsDialogFooter, MsTableOperatorButton},
inject: [
'reload'
],
@ -122,12 +124,14 @@ export default {
jiraPassword: '',
tapdUserName: '',
zentaoUserName: '',
zentaoPassword: ''
zentaoPassword: '',
azureDevopsPat:''
},
ruleForm: {},
hasJira: false,
hasTapd: false,
hasZentao: false,
hasAzure:false,
rule: {
name: [
{required: true, message: this.$t('member.input_name'), trigger: 'blur'},
@ -217,6 +221,9 @@ export default {
if (platforms.indexOf("Zentao") !== -1) {
this.hasZentao = true;
}
if (platforms.indexOf("AzureDevops") !== -1) {
this.hasAzure = true;
}
});
},
editPassword(row) {

View File

@ -122,6 +122,9 @@
</template>
</ms-instructions-icon>
</el-form-item>
<el-form-item :label-width="labelWidth" :label="$t('project.azureDevops_id')" v-if="azuredevops">
<el-input v-model="form.azureDevopsId" autocomplete="off"></el-input>
</el-form-item>
<el-form-item :label-width="labelWidth" :label="$t('project.repeatable')" prop="repeatable">
<el-switch v-model="form.repeatable"></el-switch>
</el-form-item>
@ -206,6 +209,7 @@ export default {
tapd: false,
jira: false,
zentao: false,
azuredevops: false,
form: {},
currentPage: 1,
pageSize: 10,
@ -299,6 +303,9 @@ export default {
if (platforms.indexOf("Zentao") !== -1) {
this.zentao = true;
}
if (platforms.indexOf("AzureDevops") !== -1) {
this.azuredevops = true;
}
});
},
submit(formName) {
@ -359,6 +366,7 @@ export default {
this.tapd = false;
this.jira = false;
this.zentao = false;
this.azuredevops = false;
},
search() {
this.list();

View File

@ -134,6 +134,9 @@
</template>
</ms-instructions-icon>
</el-form-item>
<el-form-item :label-width="labelWidth" :label="$t('project.azureDevops_id')" v-if="azuredevops">
<el-input v-model="form.azureDevopsId" autocomplete="off"></el-input>
</el-form-item>
<el-form-item :label-width="labelWidth" :label="$t('project.repeatable')" prop="repeatable">
<el-switch v-model="form.repeatable"></el-switch>
</el-form-item>
@ -300,6 +303,7 @@ export default {
tapd: false,
jira: false,
zentao: false,
azuredevops: false,
form: {},
currentPage: 1,
pageSize: 10,
@ -430,6 +434,9 @@ export default {
if (platforms.indexOf("Zentao") !== -1) {
this.zentao = true;
}
if (platforms.indexOf("AzureDevops") !== -1) {
this.azuredevops = true;
}
});
},
submit(formName) {
@ -494,6 +501,7 @@ export default {
this.tapd = false;
this.jira = false;
this.zentao = false;
this.azuredevops = false;
},
search() {
this.list();

View File

@ -109,6 +109,7 @@ export default {
Jira: 'JIRA',
Tapd: 'Tapd',
Zentao: '禅道',
AzureDevops: 'Azure Devops',
},
operators: [
{

View File

@ -38,6 +38,7 @@ export const EN_US = 'en_US';
export const TAPD = 'Tapd';
export const JIRA = 'Jira';
export const ZEN_TAO = 'Zentao';
export const AZURE_DEVOPS = 'AzureDevops';
export const GROUP_SYSTEM = 'SYSTEM';
export const GROUP_ORGANIZATION = 'ORGANIZATION';

View File

@ -29,6 +29,7 @@ export const ISSUE_PLATFORM_OPTION = [
{value: 'Jira',text: 'JIRA'},
{value: 'Tapd',text: 'Tapd'},
{value: 'Zentao',text: '禅道'},
{value: 'AzureDevops',text: 'Azure Devops'},
];
export const FIELD_TYPE_MAP = {

View File

@ -424,7 +424,17 @@ export default {
successful_operation: 'Successful operation',
not_integrated: 'The platform is not integrated',
choose_platform: 'Please choose an integrated platform',
verified: 'Verified'
azure_issuetype: 'Azure issue type',
azure_storytype: 'Azure story type',
input_azure_issuetype: 'Please enter the issue type',
input_azure_storytype: 'Please enter the story type',
azure_pat:'PersonalAccessTokens',
azure_devops_url:'Azure Devops url',
azure_organization_id:'Azure Organization ID',
input_azure_pat:'Please enter Personal Access Token',
input_azure_url:'Please enter Azure Devops Url',
input_azure_id:'Please enter Azure Organization ID',
use_tip_azure: 'Azure Devops URL+PersonalAccessTokens(User Settings-Personal Access Tokens-New Token)',
}
},
project: {
@ -445,6 +455,7 @@ export default {
tapd_id: 'TAPD Project ID',
jira_key: 'JIRA Project key',
zentao_id: 'Zentao Project ID',
azureDevops_id: 'AzureDevops Project ID',
manager: 'Manager',
no_data: 'No Data',
select: 'Select',

View File

@ -423,7 +423,18 @@ export default {
successful_operation: '操作成功',
not_integrated: '未集成该平台',
choose_platform: '请选择集成的平台',
verified: '验证通过'
verified: '验证通过',
azure_issuetype: '问题类型',
azure_storytype: '需求类型',
input_azure_issuetype: '请输入问题类型',
input_azure_storytype: '请输入需求类型',
azure_pat:'PersonalAccessTokens',
azure_devops_url:'Azure Devops 地址',
azure_organization_id:'Azure 组织ID',
input_azure_pat:'请输入 Personal Access Token',
input_azure_url:'请输入 Azure Devops 地址',
input_azure_organization_id:'请输入 Azure 组织ID',
use_tip_azure: 'Azure Devops 地址+令牌(账户设置-个人访问令牌-创建令牌)',
}
},
project: {
@ -444,6 +455,7 @@ export default {
tapd_id: 'TAPD项目ID',
jira_key: 'JIRA项目key',
zentao_id: 'Zentao项目ID',
azureDevops_id: 'AzureDevops项目ID',
manager: '项目管理',
no_data: '无数据',
select: '选择项目',

View File

@ -402,6 +402,7 @@ export default {
jira_storytype: '需求類型',
input_api_account: '請輸入賬號',
input_api_password: '請輸入密碼',
input_api_pat:'請输入 Personal Access Token',
input_jira_url: '請輸入Jira地址https://metersphere.atlassian.net/',
input_jira_issuetype: '請輸入問題類型',
input_jira_storytype: '請輸入需求類型',
@ -423,7 +424,18 @@ export default {
successful_operation: '操作成功',
not_integrated: '未集成該平臺',
choose_platform: '請選擇集成的平臺',
verified: '驗證通過'
verified: '驗證通過',
azure_issuetype: '問題類型',
azure_storytype: '需求類型',
input_azure_issuetype: '請輸入問題類型',
input_azure_storytype: '請輸入需求類型',
azure_pat:'PersonalAccessTokens',
azure_devops_url:'Azure Devops 地址',
azure_organization_id:'Azure 組織ID',
input_azure_pat:'請輸入 Personal Access Token',
input_azure_url:'請輸入 Azure Devops 地址',
input_azure_id:'請輸入 Azure 組織ID',
use_tip_azure: 'Azure Devops 地址+令牌(賬戶設置-個人訪問令牌-創建令牌)',
}
},
project: {
@ -444,6 +456,7 @@ export default {
tapd_id: 'TAPD項目ID',
jira_key: 'JIRA項目key',
zentao_id: 'Zentao項目ID',
azureDevops_id: 'AzureDevops項目ID',
manager: '項目管理',
no_data: '無數據',
select: '選擇項目',