My Life Is A House

My daily life with all things Libre and Open

Sunday, July 28, 2013

Report #2 - Google Tasks API support to libgdata/Evolution - how to make things stick together

As promised, here comes second blog post with more details on how I implementing JSON support. It is still ongoing as I improve code while adding Google Tasks support itself. As I already described, libgdata is is the GLib wrapper/glue library to Google GData protocol family, which is using XML. As they're slowly phasing it out, all newest APIs are using JSON as the communication format. To complicate things a little bit, they are not using unified query parameters (at least not in a documented way), and have changed names of these parameters. They also got rid of several key attributes which presumably were phased out because of lack of real applications for them. But first of all, let's talk how parsing of normal Google API response is done in libgdata.

How to parse JSON data for libgdata

As we know, Google has enabled many services to use external APIs. As library libgdata covers most of those that can be used in desktop environment. For each service library has its Service, Query, Feed and multiple Entry (for more, look at this overview page in libgdata documentation). Service get's all action as it ensures connection and querying. Query have all the information about, well, query itself (and little bit more, see below). And Feed and Entry classes (and their sub classes) are where the work of actual parsing of information and storing it takes place.
Requests themselves are done by __gdata_service_query (which is called by gdata_service_query, which in turn is called from any query function from Service), using libsoup library. My current change when receiving an answer is to look for headers - when receiving application/atom+xml follow current code path, and when receiving application/json, parse content using my implemented support. Parsing of any content happens using virtual functions, which are chained together. For example, Tasklist object received from Google has several members which are common with any other objects, and few which are unique to this object type. First, feed will be parsed (as a concept it doesn't appear in Tasks API, but list acts same way, although it's attribute set is minimized). Then when parsing list of items/entries from this feed, libgdata will first turn to GDataTaskList parse_json virtual function, which will read those members which it acknowledges, and then pass parsing to parent class if it can't find any, which in this case is GDataEntry. Feed classes are similarly chained, although it seems I won't need one for Tasks support. In the end both Feed and Entry passes the torch to their parent class Parsable, which then collects any members not parsed by upstream and call g_debug with message about unknown attribute. In the end when everything is parsed (answer into Feed, objects into Entries), object is returned to query function and after that to library user.

Query or not to query

While Query class itself is rather simple (it it just a collection of query parameters as properties and a set/get function for each), it has important advanced feature - pagination support. For that Query stores 'next' and 'previous' tags from the XML feed. For new protocols with JSON, things got a little bit complicated, as feed returns only 'nextPageToken' member. In overall, similar to other classes, Query class is chained trough get_query_uri with it's parent, which stores general search parameters, and together they build a query which is then passed to __gdata_service query for retrieving actual data. For now 'previous' support for JSON protocols will be broken, but in the future it could be implemented by storing history of previous pages of query. I also had to disable chaining for Tasks support because as I said at the begining, uri parameters are named quite differently. Instead of that, I just get value of property from parent class GDataQuery, and build uri parameter at GDataTasksQuery level.

Making it work together

As I wrote previously, it all comes together when user requires a specific query, for example, requesting all task lists available to authorized user (in this case gdata_tasks_query_all_tasklists in service/gdata-tasks-service.c). Nothing much has changed in this logic, as we pass all parameters to gdata_service_query, recieve output from libsoup and hope that library will figure out which code path to take after detecting content type. This and other query functions are which are used mostly by library user - Evolution in my case.

Saturday, July 27, 2013

Late at the party aka first report of Google Tasks support to libgdata/Evolution

This is reasonably but terribly late my first report on my project for this summer - this spring was my last in my dear uni and therefore my schedule got little bit screwed up. However now I'm back on track and crunching code as mid term review closes in.
This time I'm adding Google Tasks support to libgdata and also Evolution. As vivid Google Tasks user myself I always wanted to try my hand in this, and therefore jumped on first possibility to do it as my this years GSoC project. My mentors are superhackers Philip Withnall and Milan Crha of Evolution fame. I have used lot of their work each day (using Evolution and GMail integration for last two years, Evolution as email client for 8 years I think) and it is a little bit strange to delve into code which I have been dependent to for years. For those who don't know libgdata is that library which allows you to get all things from Google services using their API in GNOME desktop (altough libgdata is very platform neutral with small dependency set).  It is written using C and GObject, and currently uses libxml to parse XML responses from Google.
I started with understanding how Google Tasks API looks like and how it will fit in libgdata universe. Biggest change what comes with implementing it is response format change from XML to JSON. There's tons of JSON libaries for C available, however only one works well within GLib and GObject universe - JSON-GLib. By version number it feels not to be finished, however it is universally available installed by default in all major distributions. It also has what looks like complete documentation.
Main work for first two weeks was to figure out how to properly implement JSON support without breaking or changing current API much. I will have to define lot of new virtual functions for base classes, therefore ABI will be broken anyway. Google Tasks JSON API is very simple comparing to rest of newest API family, therefore adding general JSON support to libgdata I also have to check other APIs (for Calendar example), so they would fit in better when added later.
Currently I have started to read and hack Evolution - more concretely evolution-data-server - code so I can have at least visual demo for midterm review and my 4 minutes of fame in GUADEC :) I will do a little bit more detailed blog post about my changes in libgdata code base before submitting my mid term evaluation. My current work on libgdata can be found at my GitHub account