Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
MeterSphere
metersphere
提交
240ac7ab
M
metersphere
项目概览
MeterSphere
/
metersphere
上一次同步 大约 3 年
通知
25
Star
1
Fork
1
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
M
metersphere
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
240ac7ab
编写于
8月 17, 2020
作者:
S
shiziyuan9527
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
feat: 集成Jira
上级
60ed8a05
变更
3
隐藏空白更改
内联
并排
Showing
3 changed file
with
225 addition
and
18 deletion
+225
-18
backend/src/main/java/io/metersphere/service/IssuesService.java
...d/src/main/java/io/metersphere/service/IssuesService.java
+202
-16
backend/src/main/java/io/metersphere/track/controller/TestCaseIssuesController.java
...etersphere/track/controller/TestCaseIssuesController.java
+6
-0
frontend/src/business/components/settings/organization/DefectManagement.vue
...ess/components/settings/organization/DefectManagement.vue
+17
-2
未找到文件。
backend/src/main/java/io/metersphere/service/IssuesService.java
浏览文件 @
240ac7ab
...
...
@@ -10,24 +10,26 @@ import io.metersphere.commons.constants.IssuesManagePlatform;
import
io.metersphere.commons.exception.MSException
;
import
io.metersphere.commons.user.SessionUser
;
import
io.metersphere.commons.utils.EncryptUtils
;
import
io.metersphere.commons.utils.LogUtil
;
import
io.metersphere.commons.utils.RestTemplateUtils
;
import
io.metersphere.commons.utils.SessionUtils
;
import
io.metersphere.controller.ResultHolder
;
import
io.metersphere.controller.request.IntegrationRequest
;
import
io.metersphere.track.request.testcase.IssuesRequest
;
import
io.metersphere.track.service.TestCaseService
;
import
org.springframework.http.HttpEntity
;
import
org.springframework.http.HttpHeaders
;
import
org.springframework.http.HttpMethod
;
import
org.springframework.http.ResponseEntity
;
import
org.springframework.stereotype.Service
;
import
org.springframework.transaction.annotation.Transactional
;
import
org.springframework.util.LinkedMultiValueMap
;
import
org.springframework.util.MultiValueMap
;
import
org.apache.commons.lang3.StringUtils
;
import
org.springframework.web.client.RestTemplate
;
import
javax.annotation.Resource
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.UUID
;
import
java.util.*
;
import
java.util.stream.Collectors
;
@Service
...
...
@@ -48,10 +50,42 @@ public class IssuesService {
private
ExtIssuesMapper
extIssuesMapper
;
public
void
testAuth
()
{
String
url
=
"https://api.tapd.cn/quickstart/testauth"
;
ResultHolder
call
=
call
(
url
);
System
.
out
.
println
(
call
.
getData
());
public
void
testAuth
(
String
platform
)
{
if
(
StringUtils
.
equals
(
platform
,
IssuesManagePlatform
.
Tapd
.
toString
()))
{
try
{
String
tapdConfig
=
platformConfig
(
IssuesManagePlatform
.
Tapd
.
toString
());
JSONObject
object
=
JSON
.
parseObject
(
tapdConfig
);
String
account
=
object
.
getString
(
"account"
);
String
password
=
object
.
getString
(
"password"
);
HttpHeaders
headers
=
auth
(
account
,
password
);
HttpEntity
<
MultiValueMap
>
requestEntity
=
new
HttpEntity
<>(
headers
);
RestTemplate
restTemplate
=
new
RestTemplate
();
restTemplate
.
exchange
(
"https://api.tapd.cn/quickstart/testauth"
,
HttpMethod
.
GET
,
requestEntity
,
String
.
class
);
}
catch
(
Exception
e
)
{
System
.
out
.
println
(
e
);
LogUtil
.
error
(
e
.
getMessage
(),
e
);
MSException
.
throwException
(
"验证失败!"
);
}
}
else
{
try
{
String
config
=
platformConfig
(
IssuesManagePlatform
.
Jira
.
toString
());
JSONObject
object
=
JSON
.
parseObject
(
config
);
String
account
=
object
.
getString
(
"account"
);
String
password
=
object
.
getString
(
"password"
);
String
url
=
object
.
getString
(
"url"
);
HttpHeaders
headers
=
auth
(
account
,
password
);
HttpEntity
<
MultiValueMap
>
requestEntity
=
new
HttpEntity
<>(
headers
);
RestTemplate
restTemplate
=
new
RestTemplate
();
restTemplate
.
exchange
(
url
+
"rest/api/2/issue/createmeta"
,
HttpMethod
.
GET
,
requestEntity
,
String
.
class
);
}
catch
(
Exception
e
)
{
LogUtil
.
error
(
e
.
getMessage
(),
e
);
MSException
.
throwException
(
"验证失败!"
);
}
}
}
private
ResultHolder
call
(
String
url
)
{
...
...
@@ -61,7 +95,7 @@ public class IssuesService {
private
ResultHolder
call
(
String
url
,
HttpMethod
httpMethod
,
Object
params
)
{
String
responseJson
;
String
config
=
tapdConfig
(
);
String
config
=
platformConfig
(
IssuesManagePlatform
.
Tapd
.
toString
()
);
JSONObject
object
=
JSON
.
parseObject
(
config
);
if
(
object
==
null
)
{
...
...
@@ -71,7 +105,7 @@ public class IssuesService {
String
account
=
object
.
getString
(
"account"
);
String
password
=
object
.
getString
(
"password"
);
HttpHeaders
header
=
tapdA
uth
(
account
,
password
);
HttpHeaders
header
=
a
uth
(
account
,
password
);
if
(
httpMethod
.
equals
(
HttpMethod
.
GET
))
{
responseJson
=
RestTemplateUtils
.
get
(
url
,
header
);
...
...
@@ -88,7 +122,7 @@ public class IssuesService {
}
private
String
tapdConfig
(
)
{
private
String
platformConfig
(
String
platform
)
{
SessionUser
user
=
SessionUtils
.
getUser
();
String
orgId
=
user
.
getLastOrganizationId
();
...
...
@@ -97,13 +131,13 @@ public class IssuesService {
MSException
.
throwException
(
"organization id is null"
);
}
request
.
setOrgId
(
orgId
);
request
.
setPlatform
(
IssuesManagePlatform
.
Tapd
.
toString
()
);
request
.
setPlatform
(
platform
);
ServiceIntegration
integration
=
integrationService
.
get
(
request
);
return
integration
.
getConfiguration
();
}
private
HttpHeaders
tapdA
uth
(
String
apiUser
,
String
password
)
{
private
HttpHeaders
a
uth
(
String
apiUser
,
String
password
)
{
String
authKey
=
EncryptUtils
.
base64Encoding
(
apiUser
+
":"
+
password
);
HttpHeaders
headers
=
new
HttpHeaders
();
headers
.
add
(
"Authorization"
,
"Basic "
+
authKey
);
...
...
@@ -118,10 +152,10 @@ public class IssuesService {
boolean
jira
=
isIntegratedPlatform
(
orgId
,
IssuesManagePlatform
.
Jira
.
toString
());
String
tapdId
=
getTapdProjectId
(
issuesRequest
.
getTestCaseId
());
String
jira
Id
=
""
;
String
jira
Key
=
getJiraProjectKey
(
issuesRequest
.
getTestCaseId
())
;
if
(
tapd
||
jira
)
{
if
(
StringUtils
.
isBlank
(
tapdId
)
&&
StringUtils
.
isBlank
(
jira
Id
))
{
if
(
StringUtils
.
isBlank
(
tapdId
)
&&
StringUtils
.
isBlank
(
jira
Key
))
{
MSException
.
throwException
(
"集成了缺陷管理平台,但未进行项目关联!"
);
}
}
...
...
@@ -134,7 +168,9 @@ public class IssuesService {
}
if
(
jira
)
{
// addJiraIssues(issuesRequest);
if
(
StringUtils
.
isNotBlank
(
jiraKey
))
{
addJiraIssues
(
issuesRequest
);
}
}
if
(!
tapd
&&
!
jira
)
{
...
...
@@ -177,6 +213,72 @@ public class IssuesService {
issuesMapper
.
insert
(
issues
);
}
public
void
addJiraIssues
(
IssuesRequest
issuesRequest
)
{
String
config
=
platformConfig
(
IssuesManagePlatform
.
Jira
.
toString
());
JSONObject
object
=
JSON
.
parseObject
(
config
);
if
(
object
==
null
)
{
MSException
.
throwException
(
"jira config is null"
);
}
String
account
=
object
.
getString
(
"account"
);
String
password
=
object
.
getString
(
"password"
);
String
url
=
object
.
getString
(
"url"
);
String
auth
=
EncryptUtils
.
base64Encoding
(
account
+
":"
+
password
);
String
testCaseId
=
issuesRequest
.
getTestCaseId
();
String
jiraKey
=
getJiraProjectKey
(
testCaseId
);
if
(
StringUtils
.
isBlank
(
jiraKey
))
{
MSException
.
throwException
(
"未关联Jira 项目Key"
);
}
String
json
=
"{\n"
+
" \"fields\":{\n"
+
" \"project\":{\n"
+
" \"key\":\""
+
jiraKey
+
"\"\n"
+
" },\n"
+
" \"summary\":\""
+
issuesRequest
.
getTitle
()
+
"\",\n"
+
" \"description\":\""
+
issuesRequest
.
getContent
()
+
"\",\n"
+
" \"issuetype\":{\n"
+
" \"id\":\"10009\",\n"
+
" \"name\":\"Defect\"\n"
+
" }\n"
+
" }\n"
+
"}"
;
String
result
=
addJiraIssue
(
url
,
auth
,
json
);
JSONObject
jsonObject
=
JSON
.
parseObject
(
result
);
String
id
=
jsonObject
.
getString
(
"id"
);
// 用例与第三方缺陷平台中的缺陷关联
TestCaseIssues
testCaseIssues
=
new
TestCaseIssues
();
testCaseIssues
.
setId
(
UUID
.
randomUUID
().
toString
());
testCaseIssues
.
setIssuesId
(
id
);
testCaseIssues
.
setTestCaseId
(
testCaseId
);
testCaseIssuesMapper
.
insert
(
testCaseIssues
);
// 插入缺陷表
Issues
issues
=
new
Issues
();
issues
.
setId
(
id
);
issues
.
setPlatform
(
IssuesManagePlatform
.
Jira
.
toString
());
issuesMapper
.
insert
(
issues
);
}
private
String
addJiraIssue
(
String
url
,
String
auth
,
String
json
)
{
HttpHeaders
requestHeaders
=
new
HttpHeaders
();
requestHeaders
.
add
(
"Authorization"
,
"Basic "
+
auth
);
requestHeaders
.
setContentType
(
org
.
springframework
.
http
.
MediaType
.
APPLICATION_JSON
);
//HttpEntity
HttpEntity
<
String
>
requestEntity
=
new
HttpEntity
<>(
json
,
requestHeaders
);
RestTemplate
restTemplate
=
new
RestTemplate
();
//post
ResponseEntity
<
String
>
responseEntity
=
restTemplate
.
exchange
(
url
+
"/rest/api/2/issue"
,
HttpMethod
.
POST
,
requestEntity
,
String
.
class
);
return
responseEntity
.
getBody
();
}
public
void
addLocalIssues
(
IssuesRequest
request
)
{
SessionUser
user
=
SessionUtils
.
getUser
();
String
id
=
UUID
.
randomUUID
().
toString
();
...
...
@@ -209,6 +311,39 @@ public class IssuesService {
return
jsonObject
.
getObject
(
"Bug"
,
Issues
.
class
);
}
public
Issues
getJiraIssues
(
HttpHeaders
headers
,
String
url
,
String
issuesId
)
{
HttpEntity
<
MultiValueMap
>
requestEntity
=
new
HttpEntity
<>(
headers
);
RestTemplate
restTemplate
=
new
RestTemplate
();
//post
ResponseEntity
<
String
>
responseEntity
=
null
;
Issues
issues
=
new
Issues
();
try
{
responseEntity
=
restTemplate
.
exchange
(
url
+
"/rest/api/2/issue/"
+
issuesId
,
HttpMethod
.
GET
,
requestEntity
,
String
.
class
);
String
body
=
responseEntity
.
getBody
();
JSONObject
obj
=
JSONObject
.
parseObject
(
body
);
JSONObject
fields
=
(
JSONObject
)
obj
.
get
(
"fields"
);
JSONObject
statusObj
=
(
JSONObject
)
fields
.
get
(
"status"
);
JSONObject
statusCategory
=
(
JSONObject
)
statusObj
.
get
(
"statusCategory"
);
String
id
=
obj
.
getString
(
"id"
);
String
title
=
fields
.
getString
(
"summary"
);
String
description
=
fields
.
getString
(
"description"
);
String
status
=
statusCategory
.
getString
(
"key"
);
issues
.
setId
(
id
);
issues
.
setTitle
(
title
);
issues
.
setDescription
(
description
);
issues
.
setStatus
(
status
);
issues
.
setPlatform
(
IssuesManagePlatform
.
Jira
.
toString
());
}
catch
(
Exception
e
)
{
return
new
Issues
();
}
return
issues
;
}
public
List
<
Issues
>
getIssues
(
String
caseId
)
{
List
<
Issues
>
list
=
new
ArrayList
<>();
SessionUser
user
=
SessionUtils
.
getUser
();
...
...
@@ -227,7 +362,10 @@ public class IssuesService {
}
if
(
jira
)
{
// getJiraIssues(caseId);
String
jiraKey
=
getJiraProjectKey
(
caseId
);
if
(
StringUtils
.
isNotBlank
(
jiraKey
))
{
list
.
addAll
(
getJiraIssues
(
caseId
));
}
}
list
.
addAll
(
getLocalIssues
(
caseId
));
...
...
@@ -253,6 +391,7 @@ public class IssuesService {
.
andTestCaseIdEqualTo
(
caseId
)
.
andIssuesIdEqualTo
(
issuesId
);
testCaseIssuesMapper
.
deleteByExample
(
issuesExample
);
issuesMapper
.
deleteByPrimaryKey
(
issuesId
);
}
else
{
dto
.
setPlatform
(
IssuesManagePlatform
.
Tapd
.
toString
());
// 缺陷状态为 关闭,则不显示
...
...
@@ -264,6 +403,47 @@ public class IssuesService {
return
list
;
}
public
List
<
Issues
>
getJiraIssues
(
String
caseId
)
{
List
<
Issues
>
list
=
new
ArrayList
<>();
String
config
=
platformConfig
(
IssuesManagePlatform
.
Jira
.
toString
());
JSONObject
object
=
JSON
.
parseObject
(
config
);
if
(
object
==
null
)
{
MSException
.
throwException
(
"tapd config is null"
);
}
String
account
=
object
.
getString
(
"account"
);
String
password
=
object
.
getString
(
"password"
);
String
url
=
object
.
getString
(
"url"
);
HttpHeaders
headers
=
auth
(
account
,
password
);
TestCaseIssuesExample
example
=
new
TestCaseIssuesExample
();
example
.
createCriteria
().
andTestCaseIdEqualTo
(
caseId
);
List
<
Issues
>
issues
=
extIssuesMapper
.
getIssues
(
caseId
,
IssuesManagePlatform
.
Jira
.
toString
());
List
<
String
>
issuesIds
=
issues
.
stream
().
map
(
issue
->
issue
.
getId
()).
collect
(
Collectors
.
toList
());
issuesIds
.
forEach
(
issuesId
->
{
Issues
dto
=
getJiraIssues
(
headers
,
url
,
issuesId
);
if
(
StringUtils
.
isBlank
(
dto
.
getId
()))
{
// 缺陷不存在,解除用例和缺陷的关联
TestCaseIssuesExample
issuesExample
=
new
TestCaseIssuesExample
();
issuesExample
.
createCriteria
()
.
andTestCaseIdEqualTo
(
caseId
)
.
andIssuesIdEqualTo
(
issuesId
);
testCaseIssuesMapper
.
deleteByExample
(
issuesExample
);
issuesMapper
.
deleteByPrimaryKey
(
issuesId
);
}
else
{
// 缺陷状态为 完成,则不显示
if
(!
StringUtils
.
equals
(
"done"
,
dto
.
getStatus
()))
{
list
.
add
(
dto
);
}
}
});
return
list
;
}
public
List
<
Issues
>
getLocalIssues
(
String
caseId
)
{
return
extIssuesMapper
.
getIssues
(
caseId
,
IssuesManagePlatform
.
Local
.
toString
());
}
...
...
@@ -274,6 +454,12 @@ public class IssuesService {
return
project
.
getTapdId
();
}
public
String
getJiraProjectKey
(
String
testCaseId
)
{
TestCaseWithBLOBs
testCase
=
testCaseService
.
getTestCase
(
testCaseId
);
Project
project
=
projectService
.
getProjectById
(
testCase
.
getProjectId
());
return
project
.
getJiraKey
();
}
/**
* 是否关联平台
*/
...
...
backend/src/main/java/io/metersphere/track/controller/TestCaseIssuesController.java
浏览文件 @
240ac7ab
...
...
@@ -24,4 +24,10 @@ public class TestCaseIssuesController {
public
List
<
Issues
>
getIssues
(
@PathVariable
String
id
)
{
return
issuesService
.
getIssues
(
id
);
}
@GetMapping
(
"/auth/{platform}"
)
public
void
testAuth
(
@PathVariable
String
platform
)
{
issuesService
.
testAuth
(
platform
);
}
}
frontend/src/business/components/settings/organization/DefectManagement.vue
浏览文件 @
240ac7ab
...
...
@@ -19,10 +19,15 @@
<el-input
v-model=
"form.password"
auto-complete=
"new-password"
:placeholder=
"$t('organization.input_api_password')"
show-password
/>
</el-form-item>
<el-form-item
label=
"JIRA 地址"
prop=
"url"
v-if=
"platform === 'Jira'"
>
<el-input
v-model=
"form.url"
placeholder=
"请输入Jira地址"
/>
</el-form-item>
</el-form>
</div>
<div
style=
"margin-left: 100px"
>
<el-button
type=
"primary"
size=
"small"
:disabled=
"!show"
@
click=
"testConnection"
>
{{
$t
(
'
ldap.test_connect
'
)
}}
</el-button>
<el-button
v-if=
"showEdit"
size=
"small"
@
click=
"edit"
>
{{
$t
(
'
commons.edit
'
)
}}
</el-button>
<el-button
type=
"primary"
v-if=
"showSave"
size=
"small"
@
click=
"save('form')"
>
{{
$t
(
'
commons.save
'
)
}}
</el-button>
<el-button
v-if=
"showCancel"
size=
"small"
@
click=
"cancelEdit"
>
取消编辑
</el-button>
...
...
@@ -72,7 +77,8 @@
],
rules
:
{
account
:
{
required
:
true
,
message
:
this
.
$t
(
'
organization.input_api_account
'
),
trigger
:
[
'
change
'
,
'
blur
'
]},
password
:
{
required
:
true
,
message
:
this
.
$t
(
'
organization.input_api_password
'
),
trigger
:
[
'
change
'
,
'
blur
'
]}
password
:
{
required
:
true
,
message
:
this
.
$t
(
'
organization.input_api_password
'
),
trigger
:
[
'
change
'
,
'
blur
'
]},
url
:
{
required
:
true
,
message
:
'
请输入url
'
,
trigger
:
[
'
change
'
,
'
blur
'
]}
},
}
},
...
...
@@ -91,6 +97,7 @@
let
config
=
JSON
.
parse
(
data
.
configuration
);
this
.
$set
(
this
.
form
,
'
account
'
,
config
.
account
);
this
.
$set
(
this
.
form
,
'
password
'
,
config
.
password
);
this
.
$set
(
this
.
form
,
'
url
'
,
config
.
url
);
}
else
{
this
.
clear
();
}
...
...
@@ -138,7 +145,8 @@
let
param
=
{};
let
auth
=
{
account
:
this
.
form
.
account
,
password
:
this
.
form
.
password
password
:
this
.
form
.
password
,
url
:
this
.
form
.
url
};
param
.
organizationId
=
getCurrentUser
().
lastOrganizationId
;
param
.
platform
=
this
.
platform
;
...
...
@@ -172,6 +180,7 @@
let
config
=
JSON
.
parse
(
data
.
configuration
);
this
.
$set
(
this
.
form
,
'
account
'
,
config
.
account
);
this
.
$set
(
this
.
form
,
'
password
'
,
config
.
password
);
this
.
$set
(
this
.
form
,
'
url
'
,
config
.
url
);
}
else
{
this
.
clear
();
}
...
...
@@ -180,9 +189,15 @@
clear
()
{
this
.
$set
(
this
.
form
,
'
account
'
,
''
);
this
.
$set
(
this
.
form
,
'
password
'
,
''
);
this
.
$set
(
this
.
form
,
'
url
'
,
''
);
this
.
$nextTick
(()
=>
{
this
.
$refs
.
form
.
clearValidate
();
});
},
testConnection
()
{
this
.
$get
(
"
issues/auth/
"
+
this
.
platform
,
()
=>
{
this
.
$success
(
"
验证通过!
"
);
});
}
}
}
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录