[rt-users] API from Python?

Mark Eichin eichin at metacarta.com
Thu Aug 14 14:05:04 EDT 2008


Yes, one can (and should :-) use the REST interface from python.  It
takes a fair bit of hacking around, and reading bin/rt.in and
html/REST/1.0/dhandler (which do a lot of things implicitly, making
them poor-to-wretched as "documentation" :-) Hopefully this will save
you some reverse engineering and let you get some simple tools off the
ground.

I use pycurl, mostly because our RT instance uses kerberos auth, and
pycurl actually handles that (otherwise I tend to prefer pure-python
approaches.)

Note also that all of this is based on 3.6.1, which is I think the
first release where the REST interface worked *at all* with Custom
Fields; I seem to recall the later 3.6's didn't improve on this, but
3.8.0 might (before you harp on how old that makes 3.6, recall that
there *wasn't* a 3.7...)

Searching is about as much of a pain as it is with searchbuilder; at
least in 3.6.1 (which we're still running), it fails in much the same
ways as well (I just discovered that we have a custom field with a
value of "Won't fix" and the REST interface can't match it - but
neither can the web interface :-}

A simple search is just a matter of doing a GET on:

> query = urllib.quote(rawquery)
> query_url = RT_RESTBASE + "/search/ticket?" + ("format=%s" % format) + ("&query=%s" % query)

format is "l" or "s" (depending on the output you want.)  RT_RESTBASE
is "your-site/rt/REST/1.0".

This particular code is in a tool called "rt_list_mine", so:

> rawquery = "owner='%s at metacarta.com'" % os.getenv("USER")

(if you aren't us, you need to change that, of course :-)

If I've given the optional --status argument,

> rawquery = rawquery + "AND 'CF.{Ticket Status}'='" + status + "'"

Note that that's a CF ("custom field") - you can get sample syntax for
these strings by building a search in the webui and looking at the SQL
box (or just going to "Advanced" and cut&pasting it from there.)  The
important thing is that this goes through the urllib.quote above.

Just this past week I took another shot at this, which is why it's
fresh in mind: actually *updating* a ticket from python code is an
entirely different thing.  First step is to clear your mind of the
idea that the ticket is a REST object.  It's simply a url with an edit
method that lets you stuff a text representation of your fields into
the ticket.  This is what the CLI does - as far as I can tell (since
it doesn't actually *work* with kerberos) it fetches the fields of the
ticket, lets you edit them, then posts them back to cause an update.
(At least as of 3.6.1, I don't see how this works, because with the
fields I'm using, it *appends* the posted data...)

Anyway, we have a simple svn-linkage where we have a custom field
which has a renderer that posts off to another service and gets back a
given revisions changed-files, log message, and link to svnweb.  In
order to update that from the command line, I use this code:

> def update_ticket(ticket, revision):
>     """add a revision to a ticket"""
>     assert ticket, "please supply a ticket number"
>     assert revision, "please supply a revision"
>     # we don't actually need to *edit* the ticket like bin/rt does...
>     content = { 
>         "id": "ticket/%s" % ticket,
>         "CF-Revisions": "r%s" % revision,
>         }
>     postcontent = "\n".join(["%s: %s" % (k, v) for k, v in content.items()]) + "\n"
>     status, res = basepost(make_rest_url("edit"), [("content", postcontent)])
>     print status, res
>     if status != 200:
>         return res

basepost just posts to the url; it's using "c.setopt(c.HTTPPOST, postdata)" 
underneath, which is a relatively new pycurl feature, thus the
list-of-fields structure of the argument (saves dealing with mime
encoding altogether.)  make_rest_url just adds the given string to
RT_RESTBASE above, so that's posting to /rt/REST/1.0/edit.  (In
theory, you can encode the ticket in the URL; in practice, using the
id field in the content made more sense.)

To get an idea what the fields are actually called (which isn't the
same as what they're displayed as in the UI), you can fetch a sample
ticket using the REST show interface (instead of the search interface
above) with a similar POST:

> basepost(RT_RESTBASE + "/ticket/show", [("id", "17747")])

In *theory* the output you get back could *all* be posted up using the
technique used in update_ticket.  In practice, you'll get failures
like
  * a field named "CF-Release notes?" will give you a Syntax Error
  * you'll get complaints about "LastUpdated: Immutable field" which
    you can ignore, since they don't prevent the update from going
    through :-)
  * there's some confusion about updating list fields; they'll come
    back from a /ticket/show as having just commas in them, but that
    will go away if you do any update through the web UI.

Hope this saves you some reverse engineering :-) I'd like to see a
more solid REST interface - ok, what I really want is an emacs mode,
like I had for PRMS/GNATS! - but my renewed patience for the perl
expired about halfway through implementing update_ticket above :-)

			_Mark_ <eichin at metacarta.com>



More information about the rt-users mailing list