JIRA REST API调用方式

时间:2024-03-24 15:50:34

1、JIRA介绍

JIRA平台是国际上比较流行的项目缺陷追踪管理平台,与阿里云RDC系统类似,均为缺陷跟踪平台。

2、JIRA REST API调用方式

2.1、权限验证

权限验证采用basic authentication这种验证方式,主要考虑到简单易行、操作方便、内部系统。具体的操作方法为在请求的http header中添加Authorization参数,取值为Basic base64(username:password),如下图:
JIRA REST API调用方式
通过上述方式即可完成权限验证,就上图中的项目详情接口查询的http请求来举例,下图中为请求结果:
JIRA REST API调用方式
*权限验证说明文档: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