可方便扩展的JIRA Rest Web API的封装调用
JIRA是一个缺陷跟踪管理系统,被广泛应用于缺陷跟踪、客户服务、需求收集、流程审批、任务跟踪、项目跟踪和敏捷管理等工作领域,当我们需要把第三方业务系统集成进来时,可以调用他的API。
JIRA本身的API非常强大,但它是一个底层的API体系,并不是一个易用的接口,如果要开发和拓展,所以需要我们二次包装。
jira官方为解决这个问题,推出了方便强大的java client library(目前只有java客户端库,没有.Net类库)
jira的Rest API 最新文档官网.
JIRA 6.4.12 REST API documentation
https://docs.atlassian.com/jira/REST/latest/
JIRA REST API Tutorials:
https://developer.atlassian.com/jiradev/jira-apis/jira-rest-apis/jira-rest-api-tutorials
如果是编写java桌面或web应用,jira提供了更方便的方式(Client类库),JIRA REST Java Client is a Java library (usable from any JVM language) which allows to easily talk to any JIRA 4.2+ instance using new (and still evolving) REST API.
JIRA Java Client library
https://ecosystem.atlassian.net/wiki/display/JRJC/Home
如果使用Client类库,可以方便应用各种现成的jira实体类(如项目、问题、备注、自定义字段......),不需要再重复造轮子,大幅提升效率。
首先,必须要了解JIRA api的接口结构,其中<resource-name>可以理解成api的方法,比如project,就是项目信息,user就是用户信息,issue就是问题信息....
http://hostname/rest/<api-name>/<api-version>/<resource-name>
/rest/
. Hence, if your JIRA site is running at:还先要搞清楚jira api的 认证体系,摘自官网:
the first step in using the JIRA REST API is to authenticate a user account with your JIRA site. For the purposes of this tutorial we will use HTTP BASIC Authentication, but any authentication that works against JIRA will work against the REST API. This includes:
- OAuth
- HTTP Cookies
- Trusted Applications
- os_username/os_password query parameters
为方便使用,我们采用Basic Auth
Basic Auth headers
If you need to you may construct and send basic auth headers yourself. To do this you need to perform the following steps:
- Build a string of the form username:password
- Base64 encode the string
- Supply an "Authorization" header with content "Basic " followed by the encoded string. For example, the string "fred:fred" encodes to "ZnJlZDpmcmVk" in base64, so you would make the request as follows.
一个curl的例子,注意红色字符串是对“username:password”的Base64编码
curl -D- -X GET -H "Authorization: Basic ZnJlZDpmcmVk" -H "Content-Type: application/json" "http://kelpie9:8081/rest/api/2/issue/QA-31"
搞清楚了Basic Auth,用C#进行代码实现,写了一个用于ASP.NET MVC的包装类,方便重复调用。
1、C#实现的API包装类
public class JiraApi
{
private string m_Username;
private string m_Password;
public JiraApi(string username, string password)
{
m_Username = username;
m_Password = password;
}
/// <summary>
/// 处理post请求,执行新建、编辑、删除等操作
/// </summary>
/// <param name="sData">json输入字符</param>
/// <param name="uri">api的具体地址,一般是baseurl + 业务处理资源关键字</param>
/// <returns>Jira返回的WebResponse输出</returns>
public string DoPost(string sData, string uri)
{
Uri address = new Uri(uri);
HttpWebRequest request;
//HttpWebResponse response1 = null;
StreamReader sr;
string returnXML = string.Empty;
if (address == null) { throw new ArgumentNullException("address"); }
try
{
request = WebRequest.Create(address) as HttpWebRequest;
request.Method = "POST";
request.ContentType = "application/json";
string base64Credentials = GetEncodedCredentials();
request.Headers.Add("Authorization", "Basic " + base64Credentials);
//request.Credentials = new NetworkCredential(sUsername, sPassword);
if (sData != null)
{
byte[] byteData = UTF8Encoding.UTF8.GetBytes(sData);
request.ContentLength = byteData.Length;
using (Stream postStream = request.GetRequestStream())
{
postStream.Write(byteData, 0, byteData.Length);
}
using (HttpWebResponse response1 = request.GetResponse() as HttpWebResponse)
{
StreamReader reader = new StreamReader(response1.GetResponseStream());
string str = reader.ReadToEnd();
return str;
}
}
return "error";
}
catch (WebException wex)
{
if (wex.Response != null)
{
using (HttpWebResponse errorResponse = (HttpWebResponse)wex.Response)
{
try
{
string sError = string.Format("The server returned '{0}' with the status code {1} ({2:d}).",
errorResponse.StatusDescription, errorResponse.StatusCode,
errorResponse.StatusCode);
sr = new StreamReader(errorResponse.GetResponseStream(), Encoding.UTF8);
returnXML = sr.ReadToEnd();
return returnXML;
}
finally
{
if (errorResponse != null) errorResponse.Close();
}
}
}
else
{
//throw new Exception(wex.Message);
return wex.Message;
}
}
}
/// <summary>
/// 处理get请求,执行查询操作
/// </summary>
/// <param name="resource">输入的业务处理资源关键字,必填项</param>
/// <param name="argument">参数,用于获取具体查询操作,非必填项</param>
/// <param name="data">暂时没用处,非必填项</param>
/// <param name="method">默认为GET,非必填项</param>
/// <returns></returns>
public string DoQuery(
string resource,
string argument = null,
string data = null,
string method = "GET")
{
string url = string.Format("{0}{1}/", Config.BaseURL, resource.ToString());
if (argument != null)
{
url = string.Format("{0}{1}/", url, argument);
}
HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
request.ContentType = "application/json";
request.Method = method;
if (data != null)
{
using (StreamWriter writer = new StreamWriter(request.GetRequestStream()))
{
writer.Write(data);
}
}
string base64Credentials = GetEncodedCredentials();
request.Headers.Add("Authorization", "Basic " + base64Credentials);
HttpWebResponse response = request.GetResponse() as HttpWebResponse;
string result = string.Empty;
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
result = reader.ReadToEnd();
}
return result;
}
private string GetEncodedCredentials()
{
string mergedCredentials = string.Format("{0}:{1}", m_Username, m_Password);
byte[] byteCredentials = UTF8Encoding.UTF8.GetBytes(mergedCredentials);
return Convert.ToBase64String(byteCredentials);
}
}
调用方法:查询Get
/// <summary>
/// 查询jira的项目情况
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void Page_Load(object sender, EventArgs e)
{
JiraApi manager = new JiraApi(Config.User, Config.Password);
Response.Write(manager.DoQuery("project"));
}
调用方法:提交Post
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
JiraApi api = new JiraApi(Config.User, Config.Password);
//对问题添加备注
SubmitComment(api, Request.QueryString["issue"], Request.QueryString["comment"]);
}
}
/// <summary>
/// 添加问题的备注
/// </summary>
/// <param name="jm">JiraApi实例</param>
/// <param name="issue">问题的关键字,比如SomeIssue-18</param>
/// <param name="comment">备注的内容</param>
private void SubmitComment(JiraApi api, string issue, string comment)
{
string sData = string.Format("{\"body\": \"{0}\"}", comment);
string uri = Config.BaseURL + string.Format("issue/{0}/comment", issue);
Response.Write(api.DoPost(sData, uri));
}
2、JAVA Client类库实现的API DEMO
package jiraTEST; import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException; import org.apache.commons.codec.binary.Base32;
import org.joda.time.DateTime; import com.atlassian.jira.rest.client.IssueRestClient;
import com.atlassian.jira.rest.client.JiraRestClient;
import com.atlassian.jira.rest.client.SearchRestClient;
import com.atlassian.jira.rest.client.domain.BasicIssue;
import com.atlassian.jira.rest.client.domain.BasicProject;
import com.atlassian.jira.rest.client.domain.BasicUser;
import com.atlassian.jira.rest.client.domain.Comment;
import com.atlassian.jira.rest.client.domain.Field;
import com.atlassian.jira.rest.client.domain.Issue;
import com.atlassian.jira.rest.client.domain.Project;
import com.atlassian.jira.rest.client.domain.SearchResult;
import com.atlassian.jira.rest.client.domain.Transition;
import com.atlassian.jira.rest.client.domain.input.ComplexIssueInputFieldValue;
import com.atlassian.jira.rest.client.domain.input.FieldInput;
import com.atlassian.jira.rest.client.domain.input.IssueInput;
import com.atlassian.jira.rest.client.domain.input.IssueInputBuilder;
import com.atlassian.jira.rest.client.internal.async.AsynchronousJiraRestClientFactory;
import com.atlassian.util.concurrent.Promise;
import com.google.common.collect.Lists; public class CvteJiraDemo { public static String BaseURL = "http://jira-test:8080/";
public static String User = "admin";
public static String Password = "admin";
private static URI jiraServerUri = URI
.create("http://jira-test/rest/api/2/");
private static boolean quiet = false;
private static final long BUG_TYPE_ID = 1L; // JIRA magic value
private static final long TASK_TYPE_ID = 3L; // JIRA magic value
private static final DateTime DUE_DATE = new DateTime();
private static final String PRIORITY = "Trivial";
private static final String DESCRIPTION = "description"; public static void main(String[] args) throws InterruptedException,
ExecutionException { final AsynchronousJiraRestClientFactory factory = new AsynchronousJiraRestClientFactory();
URI jiraServerUri;
try {
jiraServerUri = new URI(BaseURL);
final JiraRestClient restClient = (JiraRestClient) factory
.createWithBasicHttpAuthentication(jiraServerUri, User,
Password);
getAllProjects(restClient);
getProject(restClient, "DEMO");
getIssue(restClient, "FEEDBACK-14");
getIssueFields(restClient, "FEEDBACK-27");
addIssue(restClient, "FEEDBACK", "AAAAB");
addIssueComplex(restClient, "FEEDBACK",DUE_DATE.toString()); } catch (URISyntaxException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally { }
} private static void println(Object o) {
if (!quiet) {
System.out.println(o);
}
} private static void parseArgs(String[] argsArray) throws URISyntaxException {
final List<String> args = Lists.newArrayList(argsArray);
if (args.contains("-q")) {
quiet = true;
args.remove(args.indexOf("-q"));
} if (!args.isEmpty()) {
jiraServerUri = new URI(args.get());
}
} private static Transition getTransitionByName(
Iterable<Transition> transitions, String transitionName) {
for (Transition transition : transitions) {
if (transition.getName().equals(transitionName)) {
return transition;
}
}
return null;
} // 得到所有项目信息
private static void getAllProjects(final JiraRestClient restClient)
throws InterruptedException, ExecutionException {
try { Promise<Iterable<BasicProject>> list = restClient
.getProjectClient().getAllProjects();
Iterable<BasicProject> a = list.get();
Iterator<BasicProject> it = a.iterator();
while (it.hasNext()) {
System.out.println(it.next());
} } finally {
}
} // 得到单一项目信息
private static void getProject(final JiraRestClient restClient,
String porjectKEY) throws InterruptedException, ExecutionException {
try { Project project = restClient.getProjectClient()
.getProject(porjectKEY).get();
System.out.println(project); } finally {
}
} // 得到单一问题信息
private static void getIssue(final JiraRestClient restClient,
String issueKEY) throws InterruptedException, ExecutionException {
try { Promise<Issue> list = restClient.getIssueClient()
.getIssue(issueKEY);
Issue issue = list.get();
System.out.println(issue); } finally {
}
} // 创建问题
public static BasicIssue createIssue(final JiraRestClient jiraRestClient,
IssueInput newIssue) {
BasicIssue basicIssue = jiraRestClient.getIssueClient()
.createIssue(newIssue).claim();
return basicIssue;
} //添加备注到问题
public static void addCommentToIssue(final JiraRestClient jiraRestClient,Issue issue, String comment) {
IssueRestClient issueClient = jiraRestClient.getIssueClient();
issueClient.addComment(issue.getCommentsUri(), Comment.valueOf(comment)).claim();
} //删除问题,目前找不到对应API
public static void deleteIssue(final JiraRestClient jiraRestClient, Issue issue) {
IssueRestClient issueClient = jiraRestClient.getIssueClient();
//issueClient.deleteIssue(issue.getKey(), false).claim();
} //通过标题获取问题
public static Iterable findIssuesByLabel(final JiraRestClient jiraRestClient, String label) {
SearchRestClient searchClient = jiraRestClient.getSearchClient();
String jql = "labels%3D"+label;
com.atlassian.jira.rest.client.domain.SearchResult results = ((SearchRestClient) jiraRestClient).searchJql(jql).claim();
return results.getIssues();
} //通过KEY获取问题
public static Issue findIssueByIssueKey(final JiraRestClient jiraRestClient, String issueKey) {
SearchRestClient searchClient = jiraRestClient.getSearchClient();
String jql = "issuekey = \"" + issueKey + "\"";
SearchResult results = searchClient.searchJql(jql).claim();
return (Issue) results.getIssues().iterator().next();
} // 创建问题 :仅有简单问题名称
private static void addIssue(final JiraRestClient restClient,
String porjectKEY, String issueName) throws InterruptedException,
ExecutionException {
try {
IssueInputBuilder builder = new IssueInputBuilder(porjectKEY,
TASK_TYPE_ID, issueName);
builder.setDescription("issue description");
final IssueInput input = builder.build(); try {
// create issue
final IssueRestClient client = restClient.getIssueClient();
final BasicIssue issue = client.createIssue(input).claim();
final Issue actual = client.getIssue(issue.getKey()).claim();
System.out.println(actual);
} finally {
if (restClient != null) {
// restClient.close();
}
} } finally {
}
} // 创建问题 :包含自定义字段
private static void addIssueComplex(final JiraRestClient restClient,
String porjectKEY, String issueName) throws InterruptedException,
ExecutionException {
try {
IssueInputBuilder builder = new IssueInputBuilder(porjectKEY,
TASK_TYPE_ID, issueName);
builder.setDescription("issue description");
// builder.setFieldValue("priority", ComplexIssueInputFieldValue.with("name", PRIORITY));
//单行文本
builder.setFieldValue("customfield_10042", "单行文本测试"); //单选字段
builder.setFieldValue("customfield_10043", ComplexIssueInputFieldValue.with("value", "一般")); //数值自定义字段
builder.setFieldValue("customfield_10044", 100.08); //用户选择自定义字段
builder.setFieldValue("customfield_10045", ComplexIssueInputFieldValue.with("name", "admin"));
//用户选择自定义字段(多选)
Map<String, Object> user1 = new HashMap<String, Object>();
user1.put("name", "admin");
Map<String, Object> user2 = new HashMap<String, Object>();
user2.put("name", "wangxn");
ArrayList peoples = new ArrayList();
peoples.add(user1);
peoples.add(user2);
builder.setFieldValue("customfield_10047", peoples); //设定父问题
Map<String, Object> parent = new HashMap<String, Object>();
parent.put("key", "FEEDBACK-25");
FieldInput parentField = new FieldInput("parent", new ComplexIssueInputFieldValue(parent));
builder.setFieldInput(parentField); final IssueInput input = builder.build();
try {
final IssueRestClient client = restClient.getIssueClient();
final BasicIssue issue = client.createIssue(input).claim();
final Issue actual = client.getIssue(issue.getKey()).claim();
System.out.println(actual);
} finally {
if (restClient != null) {
// restClient.close();
}
} } finally {
}
} //获取问题的所有字段
private static void getIssueFields(final JiraRestClient restClient,
String issueKEY) throws InterruptedException, ExecutionException {
try { Promise<Issue> list = restClient.getIssueClient()
.getIssue(issueKEY);
Issue issue = list.get();
Iterable<Field> fields = issue.getFields();
Iterator<Field> it = fields.iterator();
while (it.hasNext()) {
System.out.println(it.next());
} } finally {
}
} }
可方便扩展的JIRA Rest Web API的封装调用的更多相关文章
- 通过扩展让ASP.NET Web API支持JSONP
同源策略(Same Origin Policy)的存在导致了"源"自A的脚本只能操作"同源"页面的DOM,"跨源"操作来源于B的页面将会被拒 ...
- 通过扩展让ASP.NET Web API支持W3C的CORS规范
让ASP.NET Web API支持JSONP和W3C的CORS规范是解决"跨域资源共享"的两种途径,在<通过扩展让ASP.NET Web API支持JSONP>中我们 ...
- 通过扩展让ASP.NET Web API支持W3C的CORS规范(转载)
转载地址:http://www.cnblogs.com/artech/p/cors-4-asp-net-web-api-04.html CORS(Cross-Origin Resource Shari ...
- (转)通过扩展让ASP.NET Web API支持JSONP
原文地址:http://www.cnblogs.com/artech/p/3460544.html 同源策略(Same Origin Policy)的存在导致了“源”自A的脚本只能操作“同源”页面的D ...
- 在混合开发框架模式中,简化客户端对Web API的频繁调用
在混合开发框架模式中,有时候我们在处理树形节点的时候,需要很多关联的处理,可能需要结合用户配置信息,属性字典,以及表的字段分类等信息来展示一个结构树,那么在处理的时候就可能会频繁的对这些接口API进行 ...
- 通过扩展让ASP.NET Web API支持JSONP -摘自网络
同源策略(Same Origin Policy)的存在导致了“源”自A的脚本只能操作“同源”页面的DOM,“跨源”操作来源于B的页面将会被拒绝.同源策略以及跨域资源共享在大部分情况下针对的是Ajax请 ...
- ASP.NET Web API编程——客户端调用
可以使用HttpClient这个调用Web API,下面是HttpClient的定义,列举了一些常用的方法,其中还有一些没有列举,包括重载的方法. public class HttpClient : ...
- Web API应用架构在Winform混合框架中的应用(5)--系统级别字典和公司级别字典并存的处理方式
在我这个系列中,我主要以我正在开发的云会员管理系统为例进行介绍Web API的应用,由于云会员的数据设计是支持多个商家公司,而每个公司又可以包含多个店铺的,因此一些字典型的数据需要考虑这方面的不同.如 ...
- 在一个空ASP.NET Web项目上创建一个ASP.NET Web API 2.0应用
由于ASP.NET Web API具有与ASP.NET MVC类似的编程方式,再加上目前市面上专门介绍ASP.NET Web API 的书籍少之又少(我们看到的相关内容往往是某本介绍ASP.NET M ...
随机推荐
- MyIocp 测试截图
根据 小猪的网络教程 学习了 IOCP 并且自己写了一个命令行版本的 客户端测试还是使用的小猪的代码 有兴趣学习IOCP的 建议去小猪的空间看看 代码思路都讲解的很清楚 推荐 http://blog. ...
- Spring Boot与Logback的运用(自定义异常+AOP)
在开发以及调试过程中,程序员对日志的需求是非常大的,出了什么问题,都要通过日志去进行排查,但是如果日志不清或者杂乱无章,则不利于维护 这边就比较详细的列举几种类型的日志,供大家参考 首先明白logba ...
- BZOJ 1014 [JSOI2008]火星人prefix (Splay + Hash + 二分)
1014: [JSOI2008]火星人prefix Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 8112 Solved: 2569[Submit] ...
- SpringMVC上传图片总结(2)--- 使用百度webuploader上传组件进行上传图片
SpringMVC上传图片总结(2)--- 使用百度webuploader上传组件进行上传图片 在上一篇文章中,我们介绍了< SpringMVC上传图片的常规上传方法 >.本文接着第一 ...
- maven项目诡异的问题
install 的 war包中 dao 文件夹下没有xml文件 解决:重新注册为maven项目
- MFC中和定时器使用
在MFC中和定时器相关的有三个函数: 1.设置定时器(定义一个定时器的属性): SetTimer( UINT nIDEvent, UINT nElapse, void (CALLBAC ...
- CodeForces - 589J —(DFS)
Masha has recently bought a cleaner robot, it can clean a floor without anybody's assistance. Schema ...
- POJ1066线段交点
POJ1066 题意:给出一个100*100的正方形区域,通过若干连接区域边界的线段将正方形区域分割为多个不规则多边形小区域,然后给出宝藏位置,要求从区域外部开辟到宝藏所在位置的一条路径,使得开辟路径 ...
- Ansible配置文件
官方配置文件文档 Ansible安装完成之后默认配置文件为:/etc/asnible/ansible.cfg Ansible配置文件内容: cat ansible.cfg # config file ...
- Using Integrated SOA Gateway in Oracle EBS(websevice)
http://blog.csdn.net/pan_tian/article/details/10159935 Oracle EBS如何与第三方系统相集成?比如这样的需求,X系统知道物料编码,需要从EB ...