Last updated Feb 19, 2024

Writing a REST client in Python

This tutorial teaches you how to interact with Fisheye/Crucible's REST interface from a Python program.

We'll write a Python script which lists the users who are uncompleted reviewers of at least one open review.

This tutorial assumes that you have Python 2.6.3 installed. Note that the default version on Mac OS X 10.6 is 2.5.

Create a new directory to write your client in and cd into it.

First you'll need to install the python-rest-client package. Download the 0.2 distribution and unpack it in your current directory.

Set your PYTHONPATH environment variable to include the python-rest-client:

1
2
export PYTHONPATH=./python-rest-client

To get all open reviews we use the URL /reviews-v1/filter/{filter}, setting {filter} to allOpenReviews, and to get the incomplete reviewers for each of these reviews we use the URL /reviews-v1/{id}/reviewers/uncompleted

When JSON produces lists of objects, the structures produced depend on the number of items in the list. If the /reviews-v1/filter URL returns a single review, the JSON will look like this:

1
2
 
{"reviews": 
    {"reviewData": {...attributes of review...}} 
} 

but if several reviews are returned the JSON will be:

1
2
 
{"reviews": 
    {"reviewData": [{...first review...},{...second review},...]} 
} 

and if there are no reviews it will simply be:

1
2
 
{"reviews": ""} 
The `toList` function in the code below normalises each of the responses shown above to a list:
1
2
 
reviews = ...one of the dictionaries above... 
reviewList = toList(reviews["reviews"], "reviewData") 
1
2
# import the standard JSON parser
import json
# import the REST library
from restful_lib import Connection

base_url = "http://localhost:6060/foo/rest-service/reviews-v1"

conn = Connection(base_url, username="admin", password="admin")

# the rest library can't distinguish between a property and a list of properties with one element.
# this function converts a json object into a list with many, one, or no elements
# o is the dictionary containing the list
# key is the key containing the list (if any)
def toList(o, key):
    if isinstance(o,dict):
        elements = o[key]
        if not isinstance(elements,list):
            return [elements]
        else:
            return elements
    else:
        return []

# a function to get the uncompleted reviwers for a single review
def uncompletedReviewers(review):
    id = review[u'permaId'][u'id']
    resp = conn.request_get("/" + id + "/reviewers/uncompleted", args={}, headers={'content-type':'application/json', 'accept':'application/json'})
    status = resp[u'headers']['status']
    if status == '200' or status == '304':
        reviewers = toList(json.loads(resp[u'body'])[u'reviewers'],u'reviewer')
        return map(lambda r: r[u'displayName'], reviewers)
    else:
        return []

# get a dictionary containing the response to the GET request
# we specify JSON as the format as that is easy to parse in Python
resp = conn.request_get("/filter/allOpenReviews", args={}, headers={'content-type':'application/json', 'accept':'application/json'})

status = resp[u'headers']['status']
# check that we either got a successful response (200) or a previously retrieved, but still valid response (304)
if status == '200' or status == '304':
    reviews = toList(json.loads(resp[u'body'])[u'reviews'],u'reviewData')
    reviewerLists = map(uncompletedReviewers,reviews)
    reviewers = reduce(lambda a, b: set(a).union(set(b)), reviewerLists, set())
    print 'Incomplete Reviewers: '
    for r in reviewers:
        print '    ',r
else:
    print 'Error status code: ', status

Rate this page: