J Tricks - Little JIRA Tricks
 
Let us face it! REST is the order of the day. And Atlassian just underlined that statement with the release of JIRA5.

With JIRA5, a lot of the operations can be done using the REST API and here is the full list of operations supported.
There are so many ways to invoke the rest APIs. The Atlassian developer documentation is a good start. It gives you a good explanation on the input parameters needed for the various operations, if not fully clear from the API documentation.

But if you are yet unsure on how to invoke these REST operations from a Java client, read on...

Before boring you further, I must say that the best Java client for JIRA REST APIs is JRJC - The JIRA Rest Java Client supported by Atlassian. But at least for now, it doesn't support all the Issue CRUD methods. I must add that it won't be long before they are added (or is available when you re reading it!).

Still, what if you want to create another client? Without the dependency on something like JRJC? Let us look at a rather simple option - Using Jersey to write a REST Client.

I have mentioned about JRJC in my book but the following part is probably missing from the book. Considering the JIRA5 REST capabilities, it is never too late to write something about it, is it?

As mentioned before, I am just going to look at Jersey and see how we can write a simple client. Let us start with creating a simple maven project. Following is the dependency that needs to be added for using the jersey client libraries.

<dependency>
        <groupId>com.sun.jersey</groupId>
        <artifactId>jersey-client</artifactId>
        <version>1.8</version>
</dependency>

Change the version as appropriate!

Consider a simple scenario. We need to get a list of all projects in a JIRA instance. As per the API docs, the REST url is /rest/api/2/project and it is a GET operation. Following are the steps:

  1. Choose the authentication style. We are going to use the basic authentication as that is the simplest to explain. But if you are interested, there is an explanation of OAuth dance here.
  2. Create the basic authentication String. We will be using it in the REST call.

    String auth = new String(Base64.encode("username:password"));

  3. Instantiate a Jersey Client and create a Web resource that is capable of building requests to send to the REST resource and process responses returned from the resource.

    Client client = Client.create();
    WebResource webResource = client.resource("http://localhost:8080/rest/api/2/project");

    Here the url used is the base url of JIRA + the rest resource url.

  4. Obtain the response using the webResource after setting the required parameters like authentication type, media type etc.

    ClientResponse response = webResource.header("Authorization", "Basic " + auth).type("application/json").accept("application/json").get(ClientResponse.class);

                
As you can see, we have set the basic authentication parameters in the header which includes the authentication String we created in Step 2. You can add even custom values in the header as long as the server can use it but in our case that is not required.

The media type we used is application/json as that is the one supported by JIRA REST methods.

We are also using the get method here and accepts the response into the ClientResponse class.

That's it! We have sent the request and got the response. It is now only a matter of making sure the response is what is expected. The easiest thing to do is to check the status code in the response. For example, a response code in the 200 range is normally successful where as 401, for example, indicates invalid authentication error!

A good list of status codes can be found here.

You can get the status code and check it like this:

int statusCode = response.getStatus();
if (statusCode == 401) {
    throw new AuthenticationException("Invalid Username or Password");
}

And what is the status code is fine? If you are expecting a response, like in our case - list of projects, we can get the result as shown:

String response = response.getEntity(String.class);

Here the response will be in json format since that is what is given back by JIRA. Remember, the media type we set was application/json!

A sample output is here:

[
    {
        "self": "http://localhost:8080/rest/api/2/project/DEMO",
        "id": "10000",
        "key": "DEMO",
        "name": "DEMO",
        "avatarUrls": {
            "16x16": "http://localhost:8080/secure/projectavatar?size=small&pid=10000&avatarId=10011",
            "48x48": "http://localhost:8080/secure/projectavatar?pid=10000&avatarId=10011"
        }
    },
    {
        "self": "http://localhost:8080/rest/api/2/project/TEST",
        "id": "10001",
        "key": "TEST",
        "name": "TEST",
        "avatarUrls": {
            "16x16": "http://localhost:8080/secure/projectavatar?size=small&pid=10001&avatarId=10011",
            "48x48": "http://localhost:8080/secure/projectavatar?pid=10001&avatarId=10011"
        }
    }
]

As you can see, the response here is a JSON array and you can now extract the details from here as you wish! Here is small example of extracting the key and name.

We will use JSON in java for this. The dependency to be added is:

<dependency>
        <groupId>org.json</groupId>
        <artifactId>json</artifactId>
        <version>20090211</version>
</dependency>

You can simply extract the key and name as follows:

JSONArray projectArray = new JSONArray(jsonResponse);
for (int i = 0; i < projectArray.length(); i++) {
    JSONObject proj = projectArray.getJSONObject(i);
    System.out.println("Key:"+proj.getString("key")+", Name:"+proj.getString("name"));
}

So, that was a simple GET example. How about creating/updating/deleting an issue? I have taken these examples because they use POST/PUT/DELETE operations.

Creating an issue

As per the docs, we need to POST the fields required to create the issue to the REST resource. The url is http://localhost:8080/rest/api/2/issue and the simple json input with just summary, project and issuetype details will be something like this:

{"fields":{"project":{"key":"DEMO"},"summary":"REST Test","issuetype":{"name":"Bug"}}}

Everything else remains the same expect that we will be using post method with the above data.

private static String invokePostMethod(String auth, String url, String data) throws AuthenticationException, ClientHandlerException {
    Client client = Client.create();
    WebResource webResource = client.resource(url);
    ClientResponse response = webResource.header("Authorization", "Basic " + auth).type("application/json")
                .accept("application/json").post(ClientResponse.class, data);
    int statusCode = response.getStatus();
    if (statusCode == 401) {
        throw new AuthenticationException("Invalid Username or Password");
    }
    return response.getEntity(String.class);
}

Updating an issue

The url will have the issue key we need to edit: http://localhost:8080/rest/api/2/issue/DEMO-13. A simple example of json data to change the assignee will be:

{"fields":{"assignee":{"name":"test"}}}

Everything else remains same except that we use the PUT method:

private static void invokePutMethod(String auth, String url, String data) throws AuthenticationException, ClientHandlerException {
    Client client = Client.create();
    WebResource webResource = client.resource(url);
    ClientResponse response = webResource.header("Authorization", "Basic " + auth).type("application/json")
                .accept("application/json").put(ClientResponse.class, data);
    int statusCode = response.getStatus();
    if (statusCode == 401) {
        throw new AuthenticationException("Invalid Username or Password");
    }
}

Note that we are not returning any response here since the REST method doesn't return anything. The status code of 204 indicates 'No Content'.

Deleting an issue

As you would expect, this doesn't need any additional data as the url has the issue key in it: http://localhost:8080/rest/api/2/issue/DEMO-13. The operation is DELETE as opposed to PUT.

private static void invokeDeleteMethod(String auth, String url) throws AuthenticationException, ClientHandlerException {
    Client client = Client.create();
    WebResource webResource = client.resource(url);
    ClientResponse response = webResource.header("Authorization", "Basic " + auth).type("application/json")
                .accept("application/json").delete(ClientResponse.class);
    int statusCode = response.getStatus();
    if (statusCode == 401) {
        throw new AuthenticationException("Invalid Username or Password");
    }
}


Well, that wraps up the basics. Everything else is just an extension of this. https://developer.atlassian.com/display/JIRADEV/JIRA+REST+API+Tutorials has a whole lot of examples with data for each operation. Also attached to this post is the Java project we used for this post.

Don't forget to have a look at the JIRA Development Cookbook.
rest-client.zip
File Size: 5 kb
File Type: zip
Download File

 


Comments

parthi
05/10/2012 2:42am

Nice 1 Jobin

keep them coming please

Reply
J-Tricks
05/10/2012 5:11am

Thanks Parthi. Glad you liked it :)

Reply
Fcornejo
06/26/2012 1:48am

Great job, it helped me a lot. Thanks!

Reply
Daniel
07/11/2012 6:22pm

How would you go about attaching a file to an issue using jersey?

Reply
frank
07/24/2012 3:46am

Very nice examples, thanks a lot!

Reply
mike
08/03/2012 9:58am

Thanks for the nice article. I am able to use rest api to get project information however stuck while trying to update an issue :(
my url:
http://jira.xxx.intra:8080/rest/api/latest/issue/ABC-111
my data:
{"fields":{"assignee":{"name":"MG Reader"}}}
but when I want to update the issue with this data, I get a response status of 405 Method Not Allowed.. what can be issue..

Reply
J-Tricks
08/06/2012 1:52pm

Do you have "Assign Issue" permission for the issue? Are you able to do this operation from UI when logged in as the user you are trying from REST?

Reply
mike
08/06/2012 3:10pm

yea.. everything is good but now I know the reason.. many of the put/post methods are not supported before jira 5.0.x and I have jira old version.. so now I am using Soap client..

thanks :)

Reply
Chandramohan M
10/03/2012 12:08pm

Hi,

I was able to create an issue through REST JSON but I would like to create an issue with attachment. Which api should I use? and how should I mention the attachment file in the code? I have mentioned my code below.

Code:

String auth = new String(Base64.encode("admin:admin"));

Client client = Client.create();
WebResource webResource = client.resource("http://localhost:8090/rest/api/2/issue/createmeta");
ClientResponse response1 = webResource.header("Authorization", "Basic " + auth).type("application/json").accept("application/json").get(ClientResponse.class);
JSONObject json = new JSONObject();
JSONObject objProject =new JSONObject();
JSONObject objTemp =new JSONObject();
objTemp.put("key", "TES");
objProject.put("project", objTemp.toString());
objProject.put("summary", "Testing Rest ");
objProject.put("description", "Descibing what REst can do ");
//json.put("fields", objProject.toString());
//json.put("summary", "Testing Rest ");
objTemp =new JSONObject();
objTemp.put("name", "Bug");
//JSONObject objIssuetype =new JSONObject();
objProject.put("issuetype", objTemp.toString());
//data = "{"fields":{"project":{"key":"DEMO" }, "summary":"REST Test ","issuetype": {"name":"Bug" }}}";
// {"fields":{"project":{"TP" :"TestProj"}, "summary":"Testing Rest ","issuetype": {"name":"Bugwa"}}}
json.put("fields", objProject.toString());
String data =json.toString();

private static String invokePostMethod(String auth, String url, String data) throws ClientHandlerException {
Client client = Client.create();
WebResource webResource = client.resource(url);

ClientResponse response = webResource.header("Authorization", "Basic " + auth).type("application/json")
.accept("application/json").post(ClientResponse.class, data);
int statusCode = response.getStatus();

Reply
daeyong.park
10/29/2012 11:45pm

Cool!!

Reply
sridhar
11/02/2012 10:14am

I working on Rest api to get the issue details based on jira cook book and i am getting this error when trying to access jira from a standalone java application.

Exception in thread "main" com.atlassian.jira.rest.client.RestClientException: org.codehaus.jettison.json.JSONException: JSONObject["summary"] is not a JSONObject.
at com.atlassian.jira.rest.client.internal.jersey.AbstractJerseyRestClient.invoke(AbstractJerseyRestClient.java:75)
at com.atlassian.jira.rest.client.internal.jersey.AbstractJerseyRestClient.getAndParse(AbstractJerseyRestClient.java:80)
at com.atlassian.jira.rest.client.internal.jersey.JerseyIssueRestClient.getIssue(JerseyIssueRestClient.java:109)
at dst.App.main(App.java:34)
Caused by: org.codehaus.jettison.json.JSONException: JSONObject["summary"] is not a JSONObject.
at org.codehaus.jettison.json.JSONObject.getJSONObject(JSONObject.java:458)
at com.atlassian.jira.rest.client.internal.json.JsonParseUtil.getNestedString(JsonParseUtil.java:116)
at com.atlassian.jira.rest.client.internal.json.IssueJsonParser.parse(IssueJsonParser.java:141)
at com.atlassian.jira.rest.client.internal.json.IssueJsonParser.parse(IssueJsonParser.java:54)
at com.atlassian.jira.rest.client.internal.jersey.AbstractJerseyRestClient$1.call(AbstractJerseyRestClient.java:85)
at com.atlassian.jira.rest.client.internal.jersey.AbstractJerseyRestClient.invoke(AbstractJerseyRestClient.java:54)
... 3 more

Reply
J-Tricks
11/02/2012 10:23am

How is your json object constructed? Maybe you can post the code here?

Reply
Sivanandam
02/01/2013 6:09am

Hi,
I am getting the following error while running the code.

In the Line : ClientResponse response = webResource.header("Authorization", "Basic " + auth).type("application/json")
.accept("application/json").get(ClientResponse.class);

Error:
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
The method accept(MediaType[]) in the type RequestBuilder is not applicable for the arguments (String)

Reply
J-Tricks
02/04/2013 4:11pm

What version of Jersey client is it?

Reply
Mizan
02/04/2013 1:21am

Hi JTricks ,
Thanks for providing this tutorial , I need to know whether I can create a customfield which will display value from the JSON array return by a Rest call ?
Thank you

Reply
J-Tricks
02/04/2013 4:12pm

Sorry, I didn't understand. You can get any custom fields value via REST. What is different here?

Reply
Mizan
02/04/2013 10:17pm

I have Vertigo SLA plugin installed , they provide some REST api ( https://confluence.valiantys.com/display/VSLA/REST+Access+Guide#RESTAccessGuide-Authorizations ) using this I can get Remaining value of the SLA customfield .
I need this value in a separate customfield so that It appears in the issue navigator/gadgets , At present this value is not shown in any report/gadget .
Can i create a new customfield type which can get this value OR any other way i can get it in the customfield ?
Thanks :)

J-Tricks
02/05/2013 7:13pm

You should probably create a Scripted field. Script runner plugin provides one.

Mizan
02/05/2013 9:15pm

Thank you J-Tricks , I will try this .


Your comment will be posted after it is approved.


Leave a Reply


J tricks - Little JIRA Tricks