Tag Archives: Google Apps

A One-Two Punch for Getting Even More out of Google Apps APIs

Editor’s Note: Guest author Steve Ziegler is a senior architect at BetterCloudArun Nagarajan

FlashPanel, a leading enterprise-grade security and management application for Google Apps, makes use of many Google Apps APIs. Each API comes with its own set of rate-limiting quotas, network traffic behavior and response conditions. The challenge we face is to provide our customers with a stable, consistent experience despite these API behavior differences. Fortunately, Google has provided two simple, yet powerful tools to greatly increase our success rate and reliability.

Controlling Throttle Rates with Guava’s RateLimiter


Google Apps APIs operate with quota rates per second or per day. As such, the FlashPanel development team’s objective is to minimize total operation time of a parallelized set of API-intensive processes while avoiding running into Google’s API quota limits. To accomplish this, we can very easily set up a thread-safe RateLimiter for a specific rate per second for each rate restricted API:

final RateLimiter driveApiRateLimiter = RateLimiter.create(QUOTA_RATE_PER_SECOND);

The Guava RateLimiter allows us to attempt to acquire a permit based on the configured rate per second, block until available, and then take it when available, allowing execution.

To use it within the context of a method, we simply reference the rate limiter before making the API call:

public Result performDriveApiCall(driveApiRateLimiter, otherParams){
driveApiRateLimiter.acquire(); // blocks according to rate
// make API call...
}
This provides us with a very easy solution for rate limiting parallelized calls to the same APIs.

Reducing Impact of Network Traffic Issues Through Configured Backoffs


Occasionally, we experience additional network traffic issues despite enforcing rate limits. Google suggests that API clients use an exponential backoff strategy to handle this variety of error response. Typically this category of network issue resolves itself on its own after fractions of a second, but occasionally more time is needed. All we need to do to receive a successful API response is to simply retry the call some number of times, with the interval of time between retries growing exponentially.

Again, Google has made this easy through the Google HTTP Client for Java library’s ExponentialBackOff builder. This class allows us to configure the initial retry time interval, the exponential multiplier, the maximum total time to attempt to retry, and the maximum total time between retries. For example, we could configure a retry to span five minutes, growing with a factor of two, starting at a one second interval, and growing up to a maximum of one minute between retries. An API call with this configuration would retry with the following pattern in terms of seconds between retries:

1, 2, 4, 8, 16, 32, 60, 60, 60

If after this last retry, the API call still was not successful, the failed http response is returned. The code to configure such a strategy using the ExponentialBackOff.Builder reads as follows:

ExponentialBackOff backoff = new ExponentialBackOff.Builder()
.setInitialIntervalMillis(ONE_SECOND)
.setMultiplier(2.0)
.setMaxIntervalMillis(ONE_MINUTE)
.setMaxElapsedTimeMillis(FIVE_MINUTES)
.build();

One potential “gotcha” that we’ve seen is if we accidentally slam a particular API with many simultaneous API calls. In this event, not only would each of these API calls fail, but they would also schedule their retry strategy to occur simultaneously. All subsequent retries would end up firing simultaneously, causing the API calls to continue to fail due to excess per second volumes. The ExponentialBackoff class accounts for this by including a randomization factor within our retry logic that allows us to have each simultaneous API call stagger at different intervals.

For example, using our previous backoff but now with randomization, one API call may retry with these intervals:

1.04, 1.9, 4.23, 7.8, etc.

While a second API call would retry with something like these intervals:

.98, 2.04, 4.1, 8.15, etc.

With this randomization, we avoid API call sequencing collision, mitigating our chances of encountering quota related errors. To simply add this type of randomization, we append to our builder:

builder.setRandomizationFactor(RANDOMIZATION_FACTOR);

Once we have our exponential backoff strategy configured, the Google HTTP Client for Java library allows us to instantiate and assign an HttpBackOffUnsuccessfulResponseHandler to an HttpRequest as part of the request’s initialization:

private HttpBackOffUnsuccessfulResponseHandler handler = new HttpBackOffUnsuccessfulResponseHandler(backoff);

public void initialize(HttpRequest request){
request.setUnsuccessfulResponseHandler(handler);
}

Final Thoughts


By restricting our API calls using Google Guava’s easy-to-use RateLimiter, and by employing an exponential backoff strategy using the Google HTTP Client for Java library’s ExponentialBackOff, we are able to significantly reduce the amount of network traffic errors received by our Google Apps API calls, improving FlashPanel’s reliability and the overall user experience.

Steve is Senior Architect at BetterCloud, the makers of FlashPanel, the number one security and management application for Google Apps. Follow BetterCloud on Google+ at plus.google.com/+Bettercloud.

Posted by Greg Knoke, Googler

Find Unanswered Emails with Apps Script

Editor’s Note: Guest author Alex Moore is the CEO of Baydin, an email productivity company. --Arun Nagarajan
As the CEO of an email productivity company, not a day goes by when I don’t learn about a new email pain point. I love solving email problems for our customers, but many of their problems do not lend themselves to a full browser-extension and server solution, like the products we make. Apps Script is perfect for solving some of these problems in a quick, lightweight, customizable way.

The Awaiting Response script is a perfect example of one of these solutions. My friend Matt Galligan, the CEO of Circa, tweeted a few months back that he wanted a way to find all of the messages that he sent that did not receive a reply.

Boomerang, our flagship extension, provides a way to bring a single message back to your attention if it doesn’t get a response. But Boomerang is not designed for this particular issue — to use Boomerang in this way, you’d need to move every message you'd ever sent back to your inbox! Instead, it makes more sense to create a label and use Apps Script to apply it to each of these messages.

The Awaiting Response script searches the Sent folder to identify all messages you sent over the previous week. It then checks each thread to determine if someone else replied to your message. If no one has, the script applies the label AwaitingResponse to the message. You can then easily visit that label to see all those messages in a single glance.

var d = new Date();
d.setDate(d.getDate() - DAYS_TO_SEARCH);
var dateString = d.getFullYe

ar() + "/" + (d.getMonth() + 1) + "/" + d.getDate();
threads = GmailApp.search("in:Sent after:" + dateString);
Apps Script provides access to the full power of Gmail search, right from within your script. This snippet uses Javascript’s Date object to construct a Gmail-formatted search query that finds all of the conversations where you’ve sent a message in the last DAYS_TO_SEARCH days. It then loads the results of that search into an array of Thread objects.

var userEmailAddress = Session.getEffectiveUser().getEmail();
var EMAIL_REGEX = /[a-zA-Z0-9\._\-]+@[a-zA-Z0-9\.\-]+\.[a-z\.A-Z]+/g;

# if the label already exists, createLabel will return the existing label
var label = GmailApp.createLabel("AwaitingResponse");

var threadsToUpdate = [];
for (var i = 0; i < threads.length; i++)
{
var thread = threads[i];
var lastMessage = thread.getMessages()[thread.getMessageCount()-1];
lastMessageSender = lastMessage.getFrom().match(EMAIL_REGEX)[0];
if (lastMessageSender == userEmailAddress)
{
threadsToUpdate.push[thread];
}
}

label.addToThreads(threads)
And this part of the script is where the heavy lifting happens. We iterate through each message in the list of search results, applying a regular expression to the From header in the message to extract the sender’s email address. We compare the sender’s address to the script user’s email address. If they don’t match, we know someone else sent the last message in the conversation. So we apply the AwaitingResponse label to the conversation. If the script user sent the last message, we simply move along to the next message.

Add in a little bit of glue and a couple configuration options, and you have a flexible, simple script that gives you the superpower of always knowing which messages might need you to check back in.

Matt adapted his own version of the script to run automatically each day and to only apply the label to messages sent more recently than the last week.

He has also set up the script to exclude messages that include labels where, for example, he has already used Boomerang to track the messages for later. It would also be a snap to update the script to handle aliases (for example, if you use your Gmail account to send a message using your corporate email address) or to look for messages that require a reply from you.

You can get the script here. To customize it, just create your own copy and edit it right inside the built-in editor. With Awaiting Response, Apps Script helped us solve a customer problem in about fifteen minutes, without having to build an entire product.

Alex Moore is the CEO of Baydin, an email productivity company. Baydin makes software that combines AI and behavioral science to ease the burden on overloaded emailers, including the popular Boomerang email scheduling extension, which has been downloaded over two million times. When taking a break from his email, Alex makes a chicken florentine that tastes like angels singing. He is a rabid Alabama football fan.

Posted by Louis Gray, Googler

Deprecating ScriptDB and Domain Service in Apps Script

In the last few months, we've added a number of new features to Google Apps Script, including add-ons for Sheets and Docs and 7 new advanced services.

We're eager to maintain that momentum — focusing on new features that help you do more with Google Apps. As a result, we're deprecating two Apps Script services for which good replacements exist elsewhere: ScriptDB (a NoSQL database that has been marked as experimental since it was introduced) and the Domain service (which encapsulates the GroupsManager, NicknameManager, and UserManager global objects).

Both ScriptDB and the Domain service will be turned off on November 20, 2014.

Before then, you'll need to port any ScriptDB projects to another data store, like Google Cloud SQL or a third-party NoSQL database. We've created a migration guide that explains how to export your data from ScriptDB and suggests a few alternate data stores. We have also improved the documentation for connecting to external databases through JDBC to make it a little easier for you to set up Cloud SQL with Apps Script.

The Domain service, which only Google Apps domain administrators can use, is replaced by the recently added Admin SDK Directory and Admin SDK Reports advanced services. Those advanced services also provide many new features that the Domain service does not — like managing users' devices, OAuth tokens, and application-specific passwords — so we expect that you'll prefer using them in the future.


Saurabh Gupta   profile | twitter | blog

As the product manager for Google Apps Script, Saurabh is responsible for Apps Script’s overall vision and direction.

Build add-ons for Google Docs and Sheets

We've just announced Google Docs and Sheets add-ons — new tools created by developers like you that give Google users even more features in their documents and spreadsheets. Joining the launch are more than 60 add-ons that partners have built using Apps Script. Now, we're opening up the platform in a developer-preview phase. If you have a cool idea for Docs and Sheets users, we'd love to publish your code in the add-on store and get it in front of millions of users.


To browse through add-ons for Docs and Sheets, select Get add-ons in the Add-ons menu of any document or spreadsheet. (Add-ons for spreadsheets are only available in the new Google Sheets).


Under the hood

Docs and Sheets add-ons are powered by Google Apps Script, a server-side JavaScript platform that requires zero setup. Even though add-ons are in developer preview right now, the tools and APIs are available to everyone. The only restriction is on final publication to the store.

Once you have a great working prototype in Docs or Sheets, please apply to publish. Scripts that are distributed as add-ons gain a host of benefits:

  • Better discovery: Apps Script has long been popular among programmers and other power users, but difficult for non-technical users to find and install. Add-ons let you distribute your code through a polished storefront—as well as direct links and even Google search results.
  • Sharing: When two people collaborate on a document and one of them uses an add-on, it appears in the Add-ons menu for both to see. Similarly, once you get an add-on from the store, it appears in the menu in every document you create or open, although your collaborators will only see it in documents where you use it. For more info on this sharing model, see the guide to the add-on authorization lifecycle.
  • Automatic updates: When you republish an add-on, the update pushes out automatically to all your users. There's no more hounding people to switch to the latest version.
  • Share functionality without sharing code: Unlike regular Apps Script projects, add-ons don't expose your source code for all to see. That's reassuring both to less-technical users and to the keepers of your codebase's secrets.
  • Enterprise features: If your company has its own Google Apps domain, you can publish add-ons restricted just to your employees. This private distribution channel is a great way for organizations that run on Google Apps to solve their own unique problems.

Beautiful, professional appearance

Thanks to hard work from our developer partners, the add-ons in the store look and feel just like native features of Google Docs and Sheets. We're providing a couple of new resources to help all developers achieve the same visual quality: a CSS package that applies standard Google styling to typography, buttons, and other form elements, and a UI style guide that provides great guidance on designing a Googley user experience.


A replacement for the script gallery

Add-ons are available in the new version of Google Sheets as a replacement for the older version's script gallery. If you have a popular script in the old gallery, now's a great time to upgrade it to newer technology.

We can't wait to see the new uses you'll dream up for add-ons, and we're looking forward to your feedback on Google+ and questions on Stack Overflow. Better yet, if you're free at noon Eastern time this Friday, join us live on YouTube for a special add-on-centric episode of Apps Unscripted.


Dan Lazin   profile | twitter

Dan is a technical writer on the Developer Relations team for Google Apps Script. Before joining Google, he worked as video-game designer and newspaper reporter. He has bicycled through 17 countries.

Drive Google Apps Marketplace installs from your site



To increase the discovery of your Google Apps Marketplace listings, you can now embed an Integrate with Google button on your website, marketing pages, blog posts, and other properties. This lets domain administrators install your apps with a single click without leaving your site. Once they click the button, administrators can enable your app for all users (or just some of them) just like they would from the Google Apps Marketplace.

Here’s what the Integrate with Google button looks like on CloudLock.com:



To put the Integrate with Google button on your site, just embed the code below anywhere you want to display the button:
<script src="https://apis.google.com/js/platform.js"></script>
<div class="g-additnow" data-applicationid="your-app-id"></div>
Check out the developer documentation to start integrating today.

Hiranmoy Saha Google+

Hiranmoy Saha is a Software Engineer working on the Google Apps Ecosystem.

More Apps Script APIs and Features

Developers like you have built amazing scripts with Apps Script, and we want to make Apps Script even more useful. We've been working hard to add a variety of new APIs and features in Google Apps Script. Today, we're ready to share a few of them with you.


New in Document service: named ranges, bookmarks, setting cursor or selection, and undo

Named ranges are a popular feature of the Spreadsheet service; and now, you’ll be able to do something similar in the Document service. With named ranges, your scripts can tag a section of a Google Doc for later reference. For example, a bibliography script could set a named range on every citation in a document, then easily update the citations in the future.


You can use a similar API to manage bookmarks, too. Unlike named ranges, bookmarks are visible to the user and allow you to link to a particular place in a document.

Also for Google Docs, we've added the server-side methods setCursor(position) and setSelection(range) to change the user's cursor position or active selection, plus a client-side method, google.script.host.editor.focus(), which switches the browser focus from a sidebar or dialog back to the document.

Oh, and hey: the Undo command in Google Docs can now revert changes made by a script.


New in HTML service: NATIVE mode by default

If you've used HTML service much, you'll know that the Caja security sandbox has two modes. NATIVE mode imposes fewer restrictions than EMULATED mode and generally runs faster. As of today, NATIVE is now the default if you have not specified which mode your script should use. In a few edge cases, this may affect how existing web apps operate; if so, simply append .setSandboxMode(HtmlService.SandboxMode.EMULATED) to your HtmlOutput object to restore the old behavior.


Revised Properties service

In preparation for future improvements to Apps Script, we've revamped script properties and user properties, combining them into a unified Properties service and adding the notion of document properties, which are (surprise!) specific to a particular document, but shared among all collaborators. The biggest change is that user properties are no longer shared between scripts, but our guide to the Properties service provides all the details. As part of the change, the old ScriptProperties and UserProperties services have been deprecated, although they will continue to function in existing scripts until a sunset date is announced.


Deprecation of Finance service

We’re excited to bring you these new tools. With these new additions, we have also deprecated Finance service. It will remain available for the next six months but will be turned off on September 26, 2014.


Saurabh Gupta   profile | twitter | blog

As the product manager for Google Apps Script, Saurabh is responsible for Apps Script’s overall vision and direction.

Building a Rails based app for Google Apps Marketplace

Editor's note: This is a guest post by Benjamin Coe. Benjamin shares tips and best practices on using Ruby on Rails for integrating with Google Apps and launching on the Marketplace. — Arun Nagarajan

Yesware offers an app in the Google Apps Marketplace which allows our users to schedule reminders, from directly within the Gmail UI. Yesware’s app recently relaunched in the updated Google Apps Marketplace. In prep, we revamped our existing Google Apps Integration:
  • Replacing OpenID with OAuth 2.0 for Single-Sign-On.
  • Replacing 2-legged OAuth with OAuth 2.0 Service Accounts, for delegated account access.
  • Releasing a Gmail Contextual Gadget that worked within these new authentication paradigms.
We should like to share some of the decisions we made, and challenges we faced, upgrading our production Ruby on Rails application to support the improved Google Apps Marketplace.

OAuth 2.0 for SSO

In the revamped Google Apps Marketplace, OAuth 2.0 replaces OpenID for facilitating Single-Sign-On. The flow is as follows:

  1. OAuth 2.0 credentials are created in the Cloud Console, within the same project that has the Google Apps Marketplace SDK enabled.
  2. When accessing your application, a user is put through the standard OAuth 2.0 authentication flow using these credentials.
  3. If the user has the Google Apps Marketplace App installed they will be logged directly into your application, skipping the authorization step.
To implement the OAuth 2.0 authentication flow, you can use the OmniAuth Google OAuth2 Strategy gem. Assuming you're already using OmniAuth, you simply add a line to initializers/omniauth.rb that looks something like this:
Rails.application.config.middleware.use OmniAuth::Builder do
provider :google_oauth2, ENV["GAM_OAUTH_KEY"], ENV["GAM_OAUTH_SECRET"]
end

Yesware already had a Google OAuth 2.0 authentication strategy, so we opted to subclass the Google OAuth 2.0 OmniAuth Strategy. This allowed us to continue supporting our existing OAuth 2.0 credentials, while adding support for Google Apps Marketplace SSO. Our subclassed strategy looked like this:
# Subclass the GoogleOauth2 Omniauth strategy for
# Google Apps Marketplace V2 SSO.
module OmniAuth
module Strategies
class GoogleAppsMarketplace < OmniAuth::Strategies::GoogleOauth2
option :name, 'google_apps_marketplace'
end
end
end
Our final initializers/omniauth.rb file was this:
Rails.application.config.middleware.use OmniAuth::Builder do
provider :google_oauth2, ENV["OAUTH_KEY"],
ENV["OAUTH_SECRET"],
{:scope => ENV["OAUTH_SCOPE"]}
provider :google_apps_marketplace, ENV["GAM_OAUTH_KEY"],
ENV["GAM_OAUTH_SECRET"],
{ :scope => ENV["GAM_OAUTH_SCOPE"],
:access_type => 'online' }end

Note that :access_type is set to online. This is necessary to prevent the authorization prompt from being presented to a SSO user. Omniauth defaults to an :access_type of offline.
That's all it takes. With this OmniAuth strategy in place, when a domain administrator installs your application SSO will be available across the domain.

OAuth 2.0 Service Accounts

To support Yesware's reminder functionality, we needed offline access to a user's email account. In the past, this functionality was supported through 2-legged OAuth. In the new Google Apps Marketplace paradigm, OAuth 2.0 Service Accounts are the replacement.
  • In the Cloud Console, generate a private key for the OAuth 2.0 Service Account associated with your Google Apps Marketplace project. 
  • Download the .p12 private key generated. 
  • Place this key somewhere that will be accessible by your production servers, e.g., a certificates folder in your codebase. 
We used the Google API Ruby Client gem to generate an access token from our Service Account's keys.
Using the deprecated 2-Legged OAuth based approach, our authorization logic looked like this:

Gmail.connect!(:xoauth, '[email protected]', {
token: authentication.token,
secret: authentication.secret,
consumer_key: google.key,
consumer_secret: google.secret,
read_only: true
})
Using the new Service Account Based Approach, it was as follows:
key = Google::APIClient::PKCS12.load_key(
google_apps.service.p12path, # this is a constant value Google uses
# to password protect the key.
'notasecret'
)service_account = Google::APIClient::JWTAsserter.new(
google_apps.service.email,
'https://mail.google.com/',
key
)client = Google::APIClient.new(
:application_name => APPLICATION_NAME,
:version => APPLICATION_VERSION
).tap do |client|
client.authorization = service_account.authorize('[email protected]')end
Google.connect!(:xoauth2, '[email protected]', {
:oauth2_token => client.authorization.access_token,
})
With OAuth 2.0 Service Accounts, the underlying libraries we used to interact with Gmail remained the same. There were simply a few extra steps necessary to obtain an access token.

Contextual Gadgets and SSO

Yesware provides a Gmail Contextual Gadget, for scheduling email reminders. To facilitate this, it's necessary that the gadget interact with a user's email account. To make this a reality, we needed to implement SSO through our contextual gadget. Google provides great reading material on this topic. However, the approach outlined concentrates on the deprecated OpenID-based SSO approach. We used a slightly modified approach.
Rather than OpenID, we used OAuth 2.0 for associating the opensocial_viewer_id with a user. To do this, we needed to modify our OmniAuth strategy to store the opensocial_viewer_id during authentication:
# Subclass the GoogleOauth2 Omniauth strategy for
# Google Apps Marketplace V2 SSO.
module OmniAuth
module Strategies
class GoogleAppsMarketplace < OmniAuth::Strategies::GoogleOauth2

option :name, 'google_apps_marketplace'

def request_phase

# Store the opensocial_viewer_id in the session.
# this allows us to bind the Google Apps contextual
# gadget to a user account.
if request.params['opensocial_viewer_id']
session[:opensocial_viewer_id] = request.params['opensocial_viewer_id']
end

super
end

end
end
end
Once an opensocial_viewer_id was connected to a Yesware user, we could securely make API calls from our contextual gadget. To cut down on the ritual surrounding this, we wrote a Devise Google Apps OpenSocial Strategy for authenticating the OpenSocal signed requests.

Now Go Forth

Once we figured out all the moving parts, we were able to use mostly off the shelf mature libraries for building our Google Apps Marketplace Integration. I hope that this retrospective look at our development process helps other Rails developers hit the ground running even faster than we did.
Benjamin Coe profile

Benjamin Coe cofounded the email productivity company Attachments.me, which was acquired by Yesware, Inc., in  2013. Before starting his own company, Ben was an engineer at FreshBooks, the  world’s #1 accounting solution.
Ben’s in his element when writing scalable cloud-based infrastructure, and loves reflecting on the thought-process that goes into this. A rock-climber, amateur musician, and bagel aficionado, Ben can be found roaming the streets of San Francisco.
[email protected]https://github.com/bcoe@benjamincoe

Get coding fast with Code School and the Google Drive API

The most challenging part of learning anything new is often simply getting started. Unfortunately, when it comes to programming, the first few minutes (or more!) are often occupied with cumbersome details such as setting up an environment, which results in very little time spent actually writing code. We were certain there must be a better way.

Code School has been doing exciting things with learning to program online. This is why we decided to team up with them to create a way for developers to learn to use the Google Drive API, with no setup required. In the Discover Drive course, you can learn at your own pace from your web browser. You’ll spend less time fussing with coding environments and more time writing code.

To find out what the course is all about, go check it out at Code School. Happy coding!



Cross-posted on the Google Developers Blog.

Greg Knoke Google+

Greg Knoke is a technical writer in the Google Drive Developer Relations Team. Prior to joining Google, he worked as a scientist developing image and signal processing algorithms. His current interests include new technologies, content management, information architecture, cooking, music, and photography.

Introducing push notifications and structured user search in the Admin SDK

Since we launched the Admin SDK in May 2013, we’ve been constantly improving it based on your feedback and feature requests. Our first API updates of 2014 bring some features at the top of your requests: push notifications for users, push notifications for activities, and structured user search.


Push notifications for users

Tired of polling user resources to detect changes? With push notifications for users, you can watch for changes to user resources and receive notifications from the Directory API whenever a watched user resource changes.

Check out our user notifications developer guide to learn how to improve the performances of your application by eliminating the extra network and compute costs involved with polling user resources.


Push notifications for activities

Do you want to be notified when changes to a certain document occur or specific events such as the change of a user’s password happen? With push notifications for activities you can watch for changes to activities resources, and receive notifications from the Reports API whenever a watched activity resource changes.

Check out our activities notifications developer guide to learn how to receive notifications for the activities of your interest.


Structured user search

Do you want to know which users in your domain are members of a certain organization, or which users have a common manager in their reporting chain? The new query parameter we’ve just added to the users.list method of the Directory API allows you to perform rich queries over most attributes from the user profile.

For example, you can now retrieve all the users in the Human Resources org by using the query:

orgName='Human Resources'
Or you can retrieve all the users with [email protected] in their reporting chain by using the query:
manager='[email protected]'

Check out our user search developer guide for a more comprehensive guide on how to build your queries and more examples of what you can do.

Silvano Luciani  

Silvano is a Developer Programs Engineer on Google's Admin SDK. Before joining Google in 2011, he has worked in Finland, Italy, Spain and the UK, writing web based configuration management tools for ISPs, social networks, web based training materials, e-commerce apps and more.

Building an Enterprise File Server on Google Drive

Editor's note: This is a guest post by Thomas Gerber. Thomas is the CTO of Altirnao, the developer of the AODocs document management app on the Google Apps Marketplace. Thomas tells the story of his application and provides some tips for developers considering integrating with Google Apps and launching on the Marketplace. — Arun Nagarajan


Google Drive is increasingly popular in the enterprise, and many organizations would like to leverage it as a replacement for their existing on-premises file servers. Moving physical file servers to Drive provides many benefits, such as reliability, cost-effectiveness and the ability to access the files from anywhere and any device. However, the storage structure of Google Drive, where files are owned by many different users, is significantly different from the centralized organization of a file server, where everything is under the control of a small number of system administrators.

To address this problem, AODocs uses the Google Drive API to automatically transfer the ownership of files to a system account, and thus create a sort of “managed area” within Google Drive. With the Google Drive API, AODocs has complete control over the folder structure and the permissions of files owned by this system account. AODocs can be deployed in one click from the Google Apps Marketplace, which makes our application visible (and easy to try out!) for every Google Apps administrator in the world.



Companies who want to store their files on Google Drive may be concerned about losing control of their data (e.g. access to files being lost when an employee leaves the company) and controlling sharing permissions.

AODocs uses a single system account (i.e. a Google Apps account belonging to the customer’s domain, but not used by any human person) as a “proxy” to control the files. When a Google Drive files is added to an AODocs library, the ownership of the file is transferred to the AODocs system account and the file’s writersCanShare attribute is set to false, so that only AODocs is able to modify the file’s permissions afterwards.

To change the ownership of the file, we check if the system account can already access the file, and then either insert a new “owner” permission on it or use the Permissions.update method with the transferOwnership flag:

public void changeOwner(String user, String fileId, String newOwner) {
// Find what is the current permission of the new owner on the file
Permission newOwnerPermission = null;
PermissionList permissionList = RetriableTask.execute(new DrivePermissionListTask(drive.permissions().list(fileId)));
newOwnerPermission = findPermission(permissionList, newOwner);

if (newOwnerPermission == null) {
// New owner is not in the list, we need to insert it
newOwnerPermission = new Permission();
newOwnerPermission.setValue(newOwner);
newOwnerPermission.setType("user");
newOwnerPermission.setRole("owner");
Drive.Permissions.Insert insert = drive.permissions().insert(fileId, newOwnerPermission);
RetriableTask.execute(new DrivePermissionInsertTask(insert));
} else {
// New owner is already in the list, update the existing permission
newOwnerPermission.setRole("owner");
Drive.Permissions.Update update = drive.permissions().update(fileId, newOwnerPermission.getId(), newOwnerPermission);
update.setTransferOwnership(true);
RetriableTask.execute(new DrivePermissionUpdateTask(update));
}
}

Since all the files are owned by the system account, AODocs completely controls the lifecycle of the file (how they are created, in which folder they are located, who can change their permissions, who can delete them, etc). AODocs can thus provide higher-level document management features on top of Google Drive, such as configuring the retention time of deleted files, limiting external sharing to a whitelist of “trusted external domains”, or recording an audit log of file modifications.

As illustrated in the code snippet above, AODocs relies on the Google Drive API to perform all the operations on the managed files. The main challenge we had when using the Drive API was to properly handle all the error codes returned by the API calls, and make sure we make the difference between fatal errors that should not be tried again (for example, permission denied on a file) and the temporary errors that should be re-tried later (for example, “rate limit exceeded”). To handle that, we have encapsulated all our Google Drive API calls (we are using the Java client library) into a class named RetriableTask, which is responsible for handling the non-fatal errors and automatically retry the API calls with the proper exponential back-off. Here is a simplified version:

public class RetriableTask implements Callable {
[...]
private final Callable task;

[...]
@Override public T call() {
T result = null;
try {
startTime = System.currentTimeMillis();
result = task.call();
} catch (NonFatalErrorException e) {
if (numberOfTriesLeft > 0) {
// Wait some time, using exponential back-off in case of multiple attempts
Thread.sleep(getWaitTime());

// Try again
result = call();
} else {
// Too many failed attempts: now this is a fatal error
throw new RetryException();
}
} catch (FatalErrorException e) {
// This one should not be retried
Throwables.propagate(e);
}
return result;
}

AODocs is designed to work seamlessly with Google Drive, and our top priority is to leverage all the integration possibilities offered by the Google APIs. We are very excited to see that new features are added very often in the Admin SDK, the Google+ API, the Drive API that will allow AODocs to provide more options to system administrators and improve the experience for our end users.


Thomas Gerber profile

Thomas is the CTO of Altirnao. Before founding Altirnao, Thomas has led a team of senior technologists and architects on High Availability/High Performance implementations of enterprise software.