通过java程序,实现在jira上增删改查项目、issue等操作。
参考资料:https://docs.atlassian.com/software/jira/docs/api/REST/7.7.1/#api/2
上面记录了各种操作的需要调用的url 以及请求类型。
使用方式就像这样:
public static String getIssue(String issueKey) throws IOException {
String command = "curl -D- -u " + user + ":" + pwd
+ " -X GET -H \"Content-Type: application/json\" \"" + uri
+ "/rest/api/2/issue/" + issueKey + "\"";
String issueSt = executeShell(command);
return issueSt;
}
public static String getProject(String projectKey) throws IOException{
String command = "curl -D- -u " + user + ":" + pwd
+ " -X GET -H \"Content-Type: application/json\" \"" + uri
+ "/rest/api/2/project/" + projectKey + "\"";
String projectSt = executeShell(command);
return projectSt;
}
相当于直接在命令执行命令:
curl --request GET \
--url '/rest/api/2/project/{projectIdOrKey}' \
--user '[email protected]:' \
--header 'Accept: application/json'
这种方式网上有类似的工具类,但是使用起来感觉不是很方便,尤其是一些创建更新操作,需要很多参数,这种拼装的方式很容易出错,好在jira提供了java版的客户端。
<properties>
<jira-client.version>5.1.6</jira-client.version>
<alibaba-fastjson.version>1.2.68</alibaba-fastjson.version>
<atlassian-fugue.version>4.7.2</atlassian-fugue.version>
<guava.version>26.0-jre</guava.version>
</properties>
<!--jira java-->
<dependency>
<groupId>com.atlassian.jira</groupId>
<artifactId>jira-rest-java-client-core</artifactId>
<version>${jira-client.version}</version>
</dependency>
<dependency>
<groupId>io.atlassian.fugue</groupId>
<artifactId>fugue</artifactId>
<version>${atlassian-fugue.version}</version>
</dependency>
<!-- http client-->
<dependency>
<groupId>com.mashape.unirest</groupId>
<artifactId>unirest-java</artifactId>
<version>1.4.9</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${alibaba-fastjson.version}</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
</dependency>
通过java client使用起来也很简单
先通过jira地址,用户名,密码创建要给JiraRestClient。
private static JiraRestClient loginJira(String url,String userName,String pwd){
AsynchronousJiraRestClientFactory asynchronousJiraRestClientFactory = new AsynchronousJiraRestClientFactory();
JiraRestClient jiraRestClient = asynchronousJiraRestClientFactory.createWithBasicHttpAuthentication(URI.create(url), userName,pwd);
return jiraRestClient;
}
这个client里面有操作jira的各种客户端
public interface JiraRestClient extends Closeable {
/**
* @return com.atlassian.jira.rest.client.api for performing operations on selected issue
*/
IssueRestClient getIssueClient();
/**
* @return the com.atlassian.jira.rest.client.api handling session information
*/
SessionRestClient getSessionClient();
//后面的省略了
/**
* Destroys this instance of JIRA Rest Client.
*
* @throws IOException if there is a problem closing this client.
*/
void close() throws IOException;
}
上代码(简单的自测代码,还没有做到当工具类适用),部分注意点都写了注释
官方参考文档:
https://developer.atlassian.com/cloud/jira/platform/rest/v2/
package com.stwl.jira.util;
import com.alibaba.fastjson.JSON;
import com.atlassian.jira.rest.client.api.JiraRestClient;
import com.atlassian.jira.rest.client.api.domain.*;
import com.atlassian.jira.rest.client.api.domain.input.*;
import com.atlassian.jira.rest.client.internal.async.AsynchronousJiraRestClientFactory;
import com.google.common.collect.Lists;
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.JsonNode;
import com.mashape.unirest.http.Unirest;
import com.mashape.unirest.http.exceptions.UnirestException;
import com.stwl.jira.model.TransitionStatusEnum;
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author
* @date 2020/4/26 9:19
*/
public class JiraUtils2 {
private static final Logger logger = LoggerFactory.getLogger(JiraUtils2.class);
private static final String url = "http://my.jira.com:8080";
static String user = "user" ;
static String pwd ="pwd.";
private static final String PROJECT_KEY = "ADTEST";
public static void main(String[] args) throws UnirestException, FileNotFoundException {
getIssue("ADTEST-3");
// getIssue("OAGJXT-1");
// createIssue( "新创建issue描述", "新创建issue主题");
// updateIssue("ADTEST-2");
// deleteIssue("ADTEST-1");
// changeStatus("ADTEST-1");
// addCommon("ADTEST-1");
// getProject(PROJECT_KEY);
// createSubTask("创建子任务描述", "创建子任务主题","ADTEST-2");
// getIssueAllTypes();
// addAttachment("ADTEST-2", "D:/项目文件/test.txt");
// ProjectInfo projectInfo = ProjectInfo.Builder.projectInfo().setName("").build();
}
/**
* 通过issueKey获取issue
* @param issueKey
* @return
*/
public static Issue getIssue(String issueKey) {
JiraRestClient restClient = loginJira(url, user, pwd);
Issue issue = restClient.getIssueClient().getIssue(issueKey).claim();
logger.info(JSON.toJSONString(issue));
return issue;
}
/**
* 创建issue:description ,summary,projectKey,issueType,是必须要有的
* @param map
*/
public static void createIssue(Map<String,Object> map) throws IOException {
JiraRestClient restClient = loginJira(url, user, pwd);
IssueInputBuilder builder = new IssueInputBuilder();
builder.setDescription(map.get(IssueFieldId.DESCRIPTION_FIELD.id).toString());
builder.setSummary(map.get(IssueFieldId.SUMMARY_FIELD.id).toString());
builder.setProjectKey(PROJECT_KEY);
builder.setFieldInput(new FieldInput(IssueFieldId.ISSUE_TYPE_FIELD, ComplexIssueInputFieldValue.with("name", "任务")));
builder.setFieldInput(new FieldInput(IssueFieldId.PRIORITY_FIELD, ComplexIssueInputFieldValue.with("name", "不重要不紧急")));
//如果还需要一些其他信息,可以按照上面的build继续构造,想要知道IssueFieldId对应哪些数据,
//我们可以通过getIssue()方法获取一个issue有哪些数据,然后对照着构建。
IssueInput issueInput = builder.build();
try {
BasicIssue issue = restClient.getIssueClient().createIssue(issueInput).claim();
logger.info(JSON.toJSONString(issue));
} catch (Exception e) {
e.printStackTrace();
}finally {
restClient.close();
}
}
/**
* 创建子任务
* @param description
* @param summary
* @param parentIssueKey
*/
public static void createSubTask(String description,String summary,String parentIssueKey){
JiraRestClient restClient = loginJira(url, user, pwd);
IssueInputBuilder builder = new IssueInputBuilder();
builder.setDescription(description);
builder.setSummary(summary);
builder.setProjectKey(PROJECT_KEY);
builder.setFieldInput(new FieldInput(IssueFieldId.ISSUE_TYPE_FIELD, ComplexIssueInputFieldValue.with("name", "子任务")));
builder.setFieldInput(new FieldInput(IssueFieldId.PRIORITY_FIELD, ComplexIssueInputFieldValue.with("name", "不重要不紧急")));
//创建子任务需要指定父任务的issuekey
builder.setFieldInput(new FieldInput("parent", ComplexIssueInputFieldValue.with("key", parentIssueKey)));
IssueInput issueInput = builder.build();
BasicIssue issue = restClient.getIssueClient().createIssue(issueInput).claim();
logger.info(JSON.toJSONString(issue));
}
/**
* 更新issue
* @param issueKey,下面需要更新的数据现在代码里是写死了,真正用的时候以参数的形式传进去
*/
public static void updateIssue(String issueKey) {
JiraRestClient restClient = loginJira(url, user, pwd);
IssueInputBuilder builder = new IssueInputBuilder();
//设置项目key然后才能修改
builder.setProjectKey(PROJECT_KEY);
// builder.setDescription("通过java更新issue描述");
//优先级
builder.setFieldInput(new FieldInput("priority", ComplexIssueInputFieldValue.with("name", "不重要不紧急")));
//经办人
// builder.setFieldInput(new FieldInput("assignee", ComplexIssueInputFieldValue.with("name", "liuwei01")));
restClient.getIssueClient().updateIssue(issueKey, builder.build()).claim();
}
/**
* 改变工作状态:如果接受任务--开始任务--完成任务
* 接受任务必须要哪些参数都可以通过jira页面查看,他要你必填哪些信息。和我们通过接口调用是一致的
* @param issueKey
*/
public static void changeStatus(String issueKey) {
JiraRestClient restClient = loginJira(url, user, pwd);
Issue issue = getIssue(issueKey);
//查询进度转变
Iterable<Transition> transitions = restClient.getIssueClient().getTransitions(issue).claim();
logger.info(JSON.toJSONString(transitions));
List<FieldInput> fieldInputList = new ArrayList<>();
//接受任务
// fieldInputList.add(new FieldInput("customfield_10200", "2020-04-26"));
// fieldInputList.add(new FieldInput("customfield_10201", 4));
//实际工作量
fieldInputList.add(new FieldInput("customfield_10202", 4));
//完成度
// fieldInputList.add(new FieldInput("customfield_10203", "-1"));
//这里需要注意的是issue返回的信息里面有很多customfield_10203这样类型的字段,
//具体含义需要我们去尝试、猜测,我总结了部分字段的含义,搞了个枚举,在后面贴出
TransitionInput transitionInput = new TransitionInput(TransitionStatusEnum.SOLUTION_TASK.getCode(), fieldInputList);
restClient.getIssueClient().transition(issue, transitionInput).claim();
}
/**
* 增加注释 评论
* @param issueKey
*/
public static void addCommon(String issueKey) {
JiraRestClient restClient = loginJira(url, user, pwd);
Comment comment = Comment.valueOf("one more time");
Issue issue = getIssue(issueKey);
URI uri = issue.getCommentsUri();
logger.info(uri.toString());
restClient.getIssueClient().addComment(uri,comment);
try {
restClient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 获取项目信息
* @param projectKey
*/
public static void getProject(String projectKey) {
JiraRestClient restClient = loginJira(url, user, pwd);
Project project = restClient.getProjectClient().getProject(projectKey).claim();
logger.info(JSON.toJSONString(project));
}
/**
* 创建项目 这个在 getProjectClient()里面居然没有创建方法。就只能使用发送http请求的形式了。
* @param description 描述
* @param name 项目名
* @param key 项目的key。自己设置
* @param user 用户名 搞一个有创建项目权限的账号
* @param pwd 密码
* @param projectTypeKey 有效值:software, service_desk, business
*/
public static void createProject(String description,String name,String key,
String user,String pwd,String projectTypeKey) throws IOException{
Map<String, Object> map = new HashMap<>();
map.put("description",description);
map.put("name",name);
map.put("key", key);
map.put("projectTypeKey",projectTypeKey);
map.put("lead","chenjing");
try {
//下面这个Unirest也是参考官方文档使用的,其实内部封装是apache httpclient,简化了使用
HttpResponse<JsonNode> response = Unirest.post(url + "/rest/api/2/project")
.basicAuth(user, pwd)
.header("Accept", "application/json")
.header("Content-Type", "application/json")
.body(JSON.toJSONString(map))
.asJson();
System.out.println(response.getBody());
} catch (UnirestException e) {
e.printStackTrace();
}
}
/**
* 通过projectKey删除 项目
* @param projectKey
* @param user
* @param pwd
* @throws UnirestException
*/
public static void deleteProject(String projectKey, String user, String pwd) throws UnirestException {
HttpResponse<String> response = Unirest.delete(url + "/rest/api/2/project/" + projectKey)
.basicAuth(user, pwd)
.asString();
logger.info(response.getBody());
}
/**
* 上传附件
*/
/* public static void addAttachment(String issueKey,String filePath) throws FileNotFoundException {
Issue issue = getIssue(issueKey);
JiraRestClient restClient = loginJira(url, user, pwd);
File file = new File(filePath);
FileInputStream inputStream = new FileInputStream(file);
restClient.getIssueClient().addAttachment(issue.getAttachmentsUri(),inputStream, file.getName());
}*/
public static String getIssueAllTypes() throws UnirestException {
HttpResponse<JsonNode> response = Unirest.get(url+"/rest/api/2/issuetype")
.basicAuth(user, pwd)
.header("Accept", "application/json")
.asJson();
System.out.println(response.getBody());
return response.getBody().toString();
}
/**
* 删除issue,需要权限
* @param issueKey
*/
public static void deleteIssue(String issueKey) {
JiraRestClient restClient = loginJira(url, user, pwd);
restClient.getIssueClient().deleteIssue(issueKey,true);
}
private static JiraRestClient loginJira(String url,String userName,String pwd){
AsynchronousJiraRestClientFactory asynchronousJiraRestClientFactory = new AsynchronousJiraRestClientFactory();
JiraRestClient jiraRestClient = asynchronousJiraRestClientFactory.createWithBasicHttpAuthentication(URI.create(url), userName,pwd);
return jiraRestClient;
}
}
附上一个枚举
package com.stwl.jira.model;
/**
* @author
* @date 2020/4/26 15:38
*/
public enum TransitionStatusEnum {
ACCEPT_TASK(11, "接受任务"),
REFUSE_TASK(51, "拒绝任务"),
START_TASK(21, "开始任务"),
SOLUTION_TASK(31, "解决任务"),
AFTER_ACCEPT_HANG_UP_TASK(71, "接受任务后挂起任务"),
AFTER_START_HANG_UP_TASK(91, "开始任务后挂起任务"),
RESTART_TASK(81, "重新开始任务"),
;
private Integer code;
private String desc;
TransitionStatusEnum(Integer code, String desc) {
this.code = code;
this.desc = desc;
}
public Integer getCode() {
return code;
}
public String getDesc() {
return desc;
}
}
附上一个issueinfo类:
package com.stwl.jira.model;
/**
* @author
* @date 2020/4/26 13:25
*/
public class IssueInfo {
/**
* 任务标题
*/
private String summary;
/**
* 描述
*/
private String description;
/**
* issue key
*/
private String key;
/**
* 优先级 紧急重要、紧急不重要、重要不紧急、不重要不紧急 name属性
*/
private String priority;
/**
* 状态 新建,处理中,已完成
*/
private String status;
/**
* 解决结果
*/
private String resolution;
/**
* 经办人 通过name属性设置经办人
*/
private String assignee;
/**
* 报告人
*/
private String reporter;
/**
* 开始日期
*/
private String customfield_10200;
/**
* 预计工时 整形
*/
private String customfield_10201;
/**
* 完成度
*/
private String customfield_10203;
/**
* 实际工作量
*/
private String customfield_10202;
}