Recently I had to transition a Rails app from Google's old provisioning API to the new-ish Directory API. There were a few bits that I had trouble with, and some of the Stack Overflow threads reflected similar confusion, so here's a writeup in hopes of helping others.
The Directory API is a pretty standard REST setup, so you could just hit it with httparty or faraday or some such HTTP client. But I think most people will want to use the google-api-client gem since that's what Google provides. This gem uses an interesting technique; it doesn't define a bunch of methods like Group#update
and whatnot. Instead it downloads an API definition file and at runtime uses that to generate classes and methods. This means that Google doesn't have to release a new gem version every time they add an API endpoint, which is pretty cool. But it does mean that you can't just bundle open
the gem and see what the method names are.
You don't want to download that API file every time you hit the API, so you'll want to do something to cache it locally. I created a GoogleAdminDirectoryWrapper
class in lib/
for easier testing and command line usage; here's the part of that class that caches the API definition file:
Then you can jump in a console, run GoogleAdminDirectoryWrapper.new.dump_directory_to_file
, and you've got the file locally. In order to use the API definition you need to register it with an instance of Google::APIClient
, so you'll want something like:
The client
method is another helper I wrote that returns a properly initialized Google::APIClient
, i.e., one that's been authorized with OAuth using an instance of Signet::OAuth2::Client
. Getting a service (i.e., a web app) to authenticate is a whole discussion in itself. The one hint (and the documentation calls this out, but still) that I'd give is that when authorizing you need to use a :person
entry in the hash where the value is the email of the administrative account that you're "impersonating", that is, the account with which you created the application that's making these calls.
So, back to API usage. When I was writing the code that calls the endpoints it wasn't always clear to me how to actually compose those method invocations. The invocations vary based on the HTTP method and the content of the request; they depend on what's in the URL itself and what's in the request body. It's a sort of mapping technique; for example, here's more or less my function for deleting a group:
Seems pretty straightforward. But adding a member to a group looks a little different since it's a HTTP POST that requires information in the request body. You have to supply an api_method
parameter, a parameters
containing the URL parameters, and a body_object
containing the POST request body:
Here's another example. To fetch all members of a group you're doing a GET and so everything is contained in the URL and in the parameters. So we need api_method
and parameters
but no body_object
. The method below also takes care of unpacking the relevant (for my purposes) bits from the data structures that the gem returns:
After doing a couple of these a pattern becomes clear and you can just bang them out. I found it really helpful to sit in a console and do load 'lib/google_admin_directory_wrapper.rb'
while I experimented with the method definitions and only after I nailed it down would I write client code within my application.
I thought some of the error handling was a little surprising. For example, the API responds with a permissions error if the group does not exist, so you can do something like this to check for a group's existence:
For testing, I used mocha
to mock out method invocations on GoogleAdminDirectoryWrapper
and supply the expected results. I feel a little bad about not mocking things at a lower level - i.e., using vcr
. I didn't actually change anything, but I do feel bad, so that's got to count for something.
I think the lessons learned are pretty much the usual ones. Get started on API migrations early (which I didn't). Do yourself a favor and put together a rapid development cycle. Get simple things working and build from there. RTFM.