1、JIRA介绍
JIRA平台是国际上比较流行的项目缺陷追踪管理平台,与阿里云RDC系统类似,均为缺陷跟踪平台。
2、JIRA REST API调用方式
2.1、权限验证
权限验证采用basic authentication这种验证方式,主要考虑到简单易行、操作方便、内部系统。具体的操作方法为在请求的http header中添加Authorization参数,取值为Basic base64(username:password),如下图:
通过上述方式即可完成权限验证,就上图中的项目详情接口查询的http请求来举例,下图中为请求结果:
*权限验证说明文档:https://developer.atlassian.com/cloud/jira/platform/jira-rest-api-basic-authentication/
2.2、数据请求
2.2.1、数据接口文档
*数据接口说明文档:https://developer.atlassian.com/cloud/jira/platform/rest/v2/ 目前开放的接口正式版本为v2,接口文档中的v3为公共测试版本,不要使用。
2.2.2、问题列表查询
请求URL:https://XXX/rest/api/2/search?jql=project=10223+and+assignee=abc+order+by+updated&startAt=0&maxResults=10
请求方式:GET
返回问题详情结果:
{
"expand":"names,schema",
"startAt":0,
"maxResults":1,
"total":33,
"issues":
[
{详细问题结构参考},
{详细问题结构参考}
]
}
2.2.3、问题详情查询
请求URL:https://XXX/rest/api/2/issue/{issueIdOrKey}
请求方式:GET
返回问题详情结果:
{
"id": "10002",
"self": "http://your-domain.atlassian.net/rest/api/2/issue/10002",
"key": "EX-1",
"fields": {
"watcher": {
"self": "http://your-domain.atlassian.net/rest/api/2/issue/EX-1/watchers",
"isWatching": false,
"watchCount": 1,
"watchers": [
{
"self": "http://your-domain.atlassian.net/rest/api/2/user?accoundId=99:27935d01-92a7-4687-8272-a9b8d3b2ae2e",
"name": "mia",
"displayName": "Mia Krystof",
"active": false
}
]
},
"attachment": [
{
"id": 10001,
"self": "https://your-domain.atlassian.net/rest/api/2/attachments/10001",
"filename": "debuglog.txt",
"author": {
"self": "http://your-domain.atlassian.net/rest/api/2/user?username=mia",
"key": "mia",
"accountId": "99:27935d01-92a7-4687-8272-a9b8d3b2ae2e",
"name": "mia",
"avatarUrls": {
"48x48": "http://your-domain.atlassian.net/secure/useravatar?size=large&ownerId=mia",
"24x24": "http://your-domain.atlassian.net/secure/useravatar?size=small&ownerId=mia",
"16x16": "http://your-domain.atlassian.net/secure/useravatar?size=xsmall&ownerId=mia",
"32x32": "http://your-domain.atlassian.net/secure/useravatar?size=medium&ownerId=mia"
},
"displayName": "Mia Krystof",
"active": false
},
"created": "2018-10-10T23:10:51.169+0000",
"size": 2460,
"mimeType": "text/plain",
"content": "https://your-domain.atlassian.net/jira/attachments/10001",
"thumbnail": "https://your-domain.atlassian.net/jira/secure/thumbnail/10002"
}
],
"sub-tasks": [
{
"id": "10000",
"type": {
"id": "10000",
"name": "",
"inward": "Parent",
"outward": "Sub-task"
},
"outwardIssue": {
"id": "10003",
"key": "EX-2",
"self": "http://your-domain.atlassian.net/rest/api/2/issue/EX-2",
"fields": {
"status": {
"iconUrl": "http://your-domain.atlassian.net/images/icons/statuses/open.png",
"name": "Open"
}
}
}
}
],
"description": "example bug report",
"project": {
"self": "http://your-domain.atlassian.net/rest/api/2/project/EX",
"id": "10000",
"key": "EX",
"name": "Example",
"avatarUrls": {
"48x48": "http://your-domain.atlassian.net/secure/projectavatar?size=large&pid=10000",
"24x24": "http://your-domain.atlassian.net/secure/projectavatar?size=small&pid=10000",
"16x16": "http://your-domain.atlassian.net/secure/projectavatar?size=xsmall&pid=10000",
"32x32": "http://your-domain.atlassian.net/secure/projectavatar?size=medium&pid=10000"
},
"projectCategory": {
"self": "http://your-domain.atlassian.net/rest/api/2/projectCategory/10000",
"id": "10000",
"name": "FIRST",
"description": "First Project Category"
},
"simplified": false,
"style": "classic"
},
"comment": [
{
"self": "http://your-domain.atlassian.net/rest/api/2/issue/10010/comment/10000",
"id": "10000",
"author": {
"self": "http://your-domain.atlassian.net/rest/api/2/user?accoundId=99:27935d01-92a7-4687-8272-a9b8d3b2ae2e",
"name": "mia",
"displayName": "Mia Krystof",
"active": false
},
"body": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque eget venenatis elit. Duis eu justo eget augue iaculis fermentum. Sed semper quam laoreet nisi egestas at posuere augue semper.",
"updateAuthor": {
"self": "http://your-domain.atlassian.net/rest/api/2/user?accoundId=99:27935d01-92a7-4687-8272-a9b8d3b2ae2e",
"name": "mia",
"displayName": "Mia Krystof",
"active": false
},
"created": "2018-10-10T23:10:51.172+0000",
"updated": "2018-10-10T23:10:51.198+0000",
"visibility": {
"type": "role",
"value": "Administrators"
}
}
],
"issuelinks": [
{
"id": "10001",
"type": {
"id": "10000",
"name": "Dependent",
"inward": "depends on",
"outward": "is depended by"
},
"outwardIssue": {
"id": "10004L",
"key": "PRJ-2",
"self": "http://your-domain.atlassian.net/rest/api/2/issue/PRJ-2",
"fields": {
"status": {
"iconUrl": "http://your-domain.atlassian.net/images/icons/statuses/open.png",
"name": "Open"
}
}
}
},
{
"id": "10002",
"type": {
"id": "10000",
"name": "Dependent",
"inward": "depends on",
"outward": "is depended by"
},
"inwardIssue": {
"id": "10004",
"key": "PRJ-3",
"self": "http://your-domain.atlassian.net/rest/api/2/issue/PRJ-3",
"fields": {
"status": {
"iconUrl": "http://your-domain.atlassian.net/images/icons/statuses/open.png",
"name": "Open"
}
}
}
}
],
"worklog": [
{
"self": "http://your-domain.atlassian.net/rest/api/2/issue/10010/worklog/10000",
"author": {
"self": "http://your-domain.atlassian.net/rest/api/2/user?accoundId=99:27935d01-92a7-4687-8272-a9b8d3b2ae2e",
"name": "mia",
"displayName": "Mia Krystof",
"active": false
},
"updateAuthor": {
"self": "http://your-domain.atlassian.net/rest/api/2/user?accoundId=99:27935d01-92a7-4687-8272-a9b8d3b2ae2e",
"name": "mia",
"displayName": "Mia Krystof",
"active": false
},
"comment": "I did some work here.",
"updated": "2018-10-10T23:10:51.220+0000",
"visibility": {
"type": "group",
"value": "jira-developers"
},
"started": "2018-10-10T23:10:51.220+0000",
"timeSpent": "3h 20m",
"timeSpentSeconds": 12000,
"id": "100028",
"issueId": "10002"
}
],
"updated": 1,
"timetracking": {
"originalEstimate": "10m",
"remainingEstimate": "3m",
"timeSpent": "6m",
"originalEstimateSeconds": 600,
"remainingEstimateSeconds": 200,
"timeSpentSeconds": 400
}
}
}
2.2.4、项目问题更新
请求URL:https://XXX/rest/api/2/issue/{issueIdOrKey}
请求方式:PUT
待测试(需要在JIRA上创建测试票)
2.2.5、项目问题评论创建
请求URL:https://XXX/rest/api/2/issue/{issueIdOrKey}/comment
请求方式:POST
待测试(需要在JIRA上创建测试票)
2.2.6、项目问题附件添加
请求URL:https://XXX/rest/api/2/issue/{issueIdOrKey}/attachments
请求方式:POST
待测试(需要在JIRA上创建测试票)
3、JAVA测试
http请求方法
@Override
public JiraHttpResponse doGetMethod(String url) {
JiraHttpResponse jiraHttpResponse = new JiraHttpResponse();
boolean success = false;
String exception = "";
String body = "";
logger.info("request url:" + url);
logger.info("request method: get");
if (!Constant.emptyString.equals(url)) {
CloseableHttpClient closeableHttpClient = createCloseableHttpClient();
RequestConfig defaultConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD).build();
HttpGet httpGet;
HttpResponse httpResponse;
httpGet = new HttpGet(url);
// 设置CookieSpecs策略
httpGet.setConfig(defaultConfig);
// 定义请求header和鉴权权限参数
httpGet.setHeader("Accept","application/json");
httpGet.setHeader("Content-Type","application/json");
httpGet.setHeader("Authorization","Basic " + bash64Util.getBase64(configBean.getJiraAccount() + ":" + configBean.getJiraPassword()));
try {
httpResponse = closeableHttpClient.execute(httpGet);
logger.info("httpStatus=" + httpResponse.getStatusLine().getStatusCode());
if (HttpStatus.SC_OK == httpResponse.getStatusLine().getStatusCode()) {
HttpEntity httpEntity = httpResponse.getEntity();
if (httpEntity != null) {
body = EntityUtils.toString(httpEntity,"UTF-8");
logger.info("body length:" + body.length());
success = true;
} else {
exception = "JIRA请求返回实体为空!";
}
}
} catch (IOException e) {
e.printStackTrace();
exception = "JIRA数据请求失败!";
} finally {
closeHttpClient(closeableHttpClient);
}
} else {
exception = "JIRA请求地址为空!";
}
jiraHttpResponse.setSuccess(success);
jiraHttpResponse.setErrorMessage(exception);
jiraHttpResponse.setBody(body);
logger.info(jiraHttpResponse.toString());
return jiraHttpResponse;
}
请求结果:
2018-10-12 15:03:46.671 INFO 107496 --- [959D39DD333BC-0] c.a.c.j.s.impl.HttpClientServiceImpl : request url:https://XXX/rest/api/2/search?jql=project=10223+and+assignee=abc+order+by+updated&startAt=0&maxResults=10
2018-10-12 15:03:46.672 INFO 107496 --- [959D39DD333BC-0] c.a.c.j.s.impl.HttpClientServiceImpl : request method: get
2018-10-12 15:03:47.298 INFO 107496 --- [959D39DD333BC-0] com.amap.chexian.jira.util.Bash64Util : Bash64Utils.getBash64FromUrl bash64 size:28
2018-10-12 15:03:52.416 INFO 107496 --- [959D39DD333BC-0] c.a.c.j.s.impl.HttpClientServiceImpl : httpStatus=200
2018-10-12 15:03:52.681 INFO 107496 --- [959D39DD333BC-0] c.a.c.j.s.impl.HttpClientServiceImpl : body length:235270
2018-10-12 15:03:52.683 INFO 107496 --- [959D39DD333BC-0] c.a.c.j.s.impl.HttpClientServiceImpl : JiraHttpResponse{success=true, errorMessage='', body='{"expand":"schema,names","startAt":0,"maxResults":10,"total":34,"issues":[]}'}
4、参考资料
1、JIRA REST API测试样例 https://developer.atlassian.com/server/jira/platform/jira-rest-api-examples/
2、JIRA API说明文档 https://developer.atlassian.com/cloud/jira/platform/rest/v2/
3、JIRA JQL语法说明 https://www.atlassian.com/blog/jira-software/jql-the-most-flexible-way-to-search-jira-14
4、JIRA 实现基础 https://blog.csdn.net/liumiaocn/article/details/81301550