Java library for accessing the Canvas API

Community Member

The Java development team here at Kansas State University started our Canvas adventure off with some questionable decisions. We were originally just going to write a couple of simple LTI applications. Of course the scope and complexity of the applications grew but things were structured as a single monolithic code base. This quickly became unacceptable and we began working on splitting things into more reasonable chunks.

One of the chunks is a stand-alone API library to handle all of our interactions with the Canvas API. We wanted a central place to deal with things like pagination, masquerading and OAuth access tokens. After we started pulling this code out of our initial mess we thought the wider Canvas developer community might be able to benefit from it and decided to work towards releasing it as open source software. It is by no means a finished work but I would like to put it out there and see what others think about what we have so far.

The code is up on GitHub and available under the LGPL v3 license: kstateome/canvas-api: Java library for interacting with the Canvas LMS API

We are also publishing releases to the Sonatype Maven central repository so it can be easily used in maven based projects. The maven coordinate is edu.ksu.canvas:canvas-api

The readme file contains details on how to use the library. Following are some notes about the state and direction of development. We are also soliciting input from any other developers out there.

We do not have every API call implemented. Not even close. We are implementing them as we have time and need them for our own applications. The good news is that most of the heavy lifting of performing the HTTP requests and parsing the responses is sufficiently abstracted in common code so implementing a new call for an existing object type is usually pretty easy, especially for read operations. Creating new objects can be annoying because the Canvas documentation cannot be relied on to be accurate. You have to actually execute API calls and examine the returned data. Pushing data into the API can be tricky at times because of some API quirks that we are still discovering.

As mentioned above, we would be happy for some input from other developers. I think there are a few things we still need to change before it is really ready for more widespread adoption by others since they might be breaking changes as far as method/class signatures.

There are two specific related areas where I would love input, and they need to be set in stone quickly: Return types and exceptions. Right now every method for an API call that returns a single item has a return type of Optional<SomeCanvasObject>. As an example, if you request a course with an ID that doesn't exist, the Canvas API will return an HTTP 404 error but our library will return an empty Optional<Course>. Does it make sense to do this?

This is one of the first projects where we have used Java 8 from the ground up so we're still learning how to appropriately use some of the new features like Optional. I think this design grew out of some code where we had some shady instances of "catch(Exception e) { return null; }" going on which, of course, spawned NullPointerExceptions in other places. This got translated to Optional when we rewrote it because we wanted to avoid NPEs. It might make more sense for the methods to return the bare Course object and throw a descriptive runtime exception if there is a problem executing the request. Like throwing a custom ObjectNotFound exception if the API returns a 404 instead of returning an empty Optional. Returning null should definitely not happen. On the other hand, using Optional can be a little cleaner than having to wrap things in try/catch blocks.

That being said, you already need try/catch blocks in a lot of cases because right now every method declares throws IOException. If you pass in the wrong Canvas URL or a malformed URL, the Apache httpclient code will end up throwing an UnkownHostException or URISyntaxException or ConnectTimeoutException or something like that, all of which extend IOException. This exception will be passed all the way up to the calling code. This is kind of inconsistent with some error conditions returning an empty Optional but others letting IOExceptions to be thrown.

Another thing that might look a little odd at first glance is the requestOptions package. These are objects to encapsulate all of the (often optional) parameters you can pass to a given API call. Taking options via individual method arguments would be a bit ridiculous for some API calls that take 6+ parameters. And, if Canvas adds a new option, it would break existing code by changing the method signature. So far, so good. However you will notice that these request options classes have enums inside of them, some of which are repeated in multiple options classes. I initially tried to share the enums but ran into Canvas accepting different values on different API calls. I also found one instance of two API calls accepting the same logical options, but using different string literals to represent them. I actually opened up a GitHub issue about that one.

We still haven't ported all of our applications over to use this stand-alone library. However we have already used it to do a few things. One was to iterate through every course page in our account to do a search/replace on page content when we moved our Mediasite installation to a new URL. That process executed over 140,000 API calls over the course of a day without incident. Right now I am working on switching our Scantron LTI application over so there will probably be some missing API calls that I have to implement within the next couple of weeks for that.

We already have a pull request open on our code from someone at another institution so I'm hoping that is an indication that there is demand for a library like this and we can get some collaboration going. But first, please roast our code! :smileycool: