Thursday, April 26, 2012

Greenhopper, Jira, and REST

One of the somewhat frustrating problems I'm dealing with in Greenhopper is that I want the ability to treat a linked issue like a subtask, but without all the restrictions of a subtask.  Subtasks have at least three limitations that get in my way:
  1. They must be in the same project as their parent
  2. They must have the same permissions (issue-level security) as their parent
  3. They must be of an issue type that is flagged as a "subtask" type, so for example, a "Feature" cannot be a subtask of a "Story" unless you create a separate "Feature (subtask)" issues type.
Issue #1 is probably the most frustrating, because product management and the exec team at a software company think in terms of high-level features or use cases for which the implementation will often cross project boundaries.  (An example at Eucalyptus is that a new use case may require changes to both Eucalyptus and Euca2ools.)

The Greenhopper UI operates mostly via a REST API, and so far this API is not well documented.  Last night I got around this lack of documentation by using mitmproxy to monitor calls while moving issues up and down the planning page in Greenhopper's Rapid Board.  Then I added a simple rest client class to jiranemo based on restkit.  I made two helper functions: one to get the rest representation of an issue, and another to change the rank of an issue in Greenhopper.   My script looks like this:


import sys
import pyjira
from jiranemo import jiracfg

# Set the exception hook to enter a debugger on
# uncaught exceptions
from jiranemo.lib import util
sys.excepthook = util.genExcepthook(debug=True,

# Read ${HOME}/.jirarc, and set up clients and auth caches.
cfg = jiracfg.JiraConfiguration(readConfigFiles=True)
authorizer = pyjira.auth.CachingInteractiveAuthorizer(cfg.authCache)
ccAuthorizer = pyjira.auth.CookieCachingInteractiveAuthorizer(cfg.cookieCache)
client = pyjira.JiraClient(cfg.wsdl, 
                           (cfg.user, cfg.password), 

# Do a simple JQL query via the SOAP client, return 20 results
issues = client.client.getIssuesFromJqlSearch(
    '''project = "system testing 2" order by Rank DESC''', 20)
for x in issues:
    # Get the REST representation of each issue, because links
    # aren't shown in the SOAP representation
    rest_issue = client.restclient.get_issue(x.key)

    for link in rest_issue['fields']['issuelinks']:
      if link['type'].has_key('inward') and \
         link['type']['inward'] == "is blocked by":
        # Rank the linked issue above this one in Greenhopper
        result = client.restclient.gh_rank(link['inwardIssue']['key'], 
The code could use some error checking, but this is a pretty simple starting point for doing something that Jira and Greenhopper can't do on their own.


  1. Hi Andy,

    Awesome to see that you are using the REST API for GreenHopper. Main reason it hasn't been documented on is that folks haven't been using it in anger yet.

    One thing that may make your life easier is the API browser:
    More details here:

    Please keep sharing how you are using the API. And raise issues on our backlog if there are actions you can't accomplish today that you need to:

    Thanks Andy,
    Nicholas Muldoon

  2. Hi Andy,
    I recently came across a plug-in name Structure developed by ALMWORKS, this Plug in can display structures based on dedicated link types.
    We defined a Parent-Child relationship link type.
    This enabled us to create a multi-level issue hirarchy (another limitation of Jira) ignoring the boundaries of projects and issue type.
    Having said that the lack of REST documentation for Green Hopper is truly painful.
    Good luck,
    Alex Flekshtien