We recently updated the Google Apps Marketplace with several new features to help developers better engage with their customers and improve discoverability of apps in the marketplace.
It’s no secret that engaging your customers and responding to their feedback is critical to success. It’s now possible to engage in conversations with customers based on comments & reviews for your app in the marketplace.
Google Search recently introduced rich snippets for applications several months ago with enhanced search results for applications from marketplaces like Android Market and others. Marketplace apps will soon be appearing as rich snippets with ratings and pricing details.
Lastly, we introduced new home pages for each category in the Marketplace to feature the top installed and newest apps for that category.
We hope that you find value in these changes and wish everyone a happy new year!
2011 was the year of momentum for Google Apps Script. As 2012 dawns upon us, let us take a moment to reflect on the past year.
We started 2011 with a bang! In January we released a cloud-based Debugger into Apps Script’s IDE that proved to be very useful for developers. The Script Editor was upgraded bringing about many features and bug fixes. In March we implemented a very powerful feature of embedding Apps Script in Google Sites pages as Gadgets, making it easy to enhance Sites in amazing ways. We also improved Contacts Services, making it more stable with an improved API.
At Google I/O in May we launched Document Services, Gmail Services and the drag ‘n’ drop GUI Builder. These were major steps forward in making sure that Apps Script provides a full set of APIs to allow developers to build rich workflow and automation solutions.
We were very busy during the summer months preparing for a series of launch for later part of 2011. In September, we launched Charts Services. It allows users to dynamically create Charts and embed them in emails, UiApp or export as images. We also released three Google API services for Prediction, UrlShortener and Tasks APIs.
Lock and Cache Services launched in October. These services are important for building performant and scalable applications. We also improved the Script Editor by adding Project Support and made other UI improvements. November brought about the launch of Client Handlers and Validators. This is only the beginning of our commitment to allow developers to build more advanced UI using Apps Script.
In December we continued to improve the reliability and stability of Apps Script runtime. We capped the year by releasing Groups and Domain Services. And who can forget the very useful AdSense Services for AdSense advertisers!
Throughout the year we expanded our outreach channels. There were Apps Script sessions at Google I/O and Bootcamp, and several attendees got their last minute tickets through the Apps Script I/O challenge. We were at Google Developer Day and DevFest events, met with GTUGs, and hosted hackathons throughout the world. Our blog also featured scripts like Revevol’s Trainer Finder, Corey’s Gmail Snooze, Dave’s Flubaroo, Top Contributor’s Mail Merge, Saqib’s Idea Bank, and Drew’s Calorie Counting that showed the power of Apps Script.
Recently we started Office Hours in G+ hangout. These hangouts proved to be very popular, personal and effective means to share ideas with Apps Script community. Join us some day!
In our efforts to help educators, we worked with a New York city school to help them make most out of Google Apps. We also hosted many EDU focused webinars and workshops. In great Google tradition, Apps Script team participated in CAPE and Google Serve.
2012 is going to be an even more exciting and promising year. Tighten your seat belts because we intend to keep firing on all cylinders!
The recently launched Groups Settings API allows Google Apps domain administrators to write client applications for managing groups in their domain. Once you have created the groups and added members using the Provisioning API, you can use the Groups Settings API to perform actions like:
Let’s have a look at how you can make authorized calls to the Groups Settings API from your client application.
You must enable the Provisioning API to make requests to the Groups Settings API. You can do so by enabling the Provisioning API checkbox in the Domain settings tab of your Google Apps control panel.
Next, ensure that the Google Groups for Business and Email services are added to your domain by going to the Dashboard. If these services are not listed, add them by going to Add more services link next to the Service settings heading.
Now you are set to write client applications. Let's discuss the steps to write an application using the Python client library. You need to install the google-api-python-client library first.
You can register a new project or use an existing one from the APIs console to obtain credentials to use in your application. The credentials (client_id, client_secret) are used to obtain OAuth tokens for authorization of the API requests.
client_id
client_secret
The Groups Settings API supports various authorization mechanisms, including OAuth 2.0. Please see the wiki for more information on using the library’s support for OAuth 2.0 to create a httplib2.Http object. This object is used by the Groups Settings service to make authorized requests.
httplib2.Http
The Python client library uses the Discovery API to build the Groups Settings service from discovery. The method build is defined in the library and can be imported to build the service. The service can then access resources (‘groups’ in this case) and perform actions on them using methods defined in the discovery metadata.
build
The following example shows how to retrieve the properties for the group staff@example.com.
staff@example.com
service = build(“groups”, “v1”, http=http) group = service.groups() g = group.get(groupUniqueId=”staff@example.com”).execute()
This method returns a dictionary of property pairs (name/value):
group_name = g['name'] group_isArchived = g['isArchived'] group_whoCanViewGroup = g['whoCanViewGroup']
The update method can be used to set the properties of a group. Let’s have a look at how you can set the access permissions for a group:
update
body = {'whoCanInvite': ALL_MANAGERS_CAN_INVITE, 'whoCanJoin': ‘INVITED_CAN_JOIN’, 'whoCanPostMessage': ‘ALL_MEMBERS_CAN_POST’, 'whoCanViewGroup': ‘ALL_IN_DOMAIN_CAN_VIEW’ } # Update the properties of group g1 = group.update(groupUniqueId=groupId, body=body).execute()
Additional valid values for these properties, as well as the complete list of properties, are documented in the reference guide. We have recently added a sample in the Python client library that you can refer to when developing your own application. We would be glad to hear your feedback or any queries you have on the forum.
Though developers are quite comfortable thinking in abstractions, we still find a lot of value in code examples and fully developed sample applications. Judging by the volume of comments, tweets, and git checkouts of the Au-to-do sample code we released a few weeks ago, our readers agree.
For Google Apps API developers who want to get started writing mobile apps, here’s a great new resource: a sample application that integrates Google Calendar API v3 with Android and illustrates best practices for OAuth 2.0. This meeting scheduler app built by Alain Vongsouvanh gets the user’s contact list, lets users select attendees, and matches free/busy times to suggest available meeting times. It provides a simple Android UI for user actions such as attendee selection:
The sample code for Meeting Scheduler demonstrates many key aspects of developing with Google Apps APIs. After authorizing all required access using OAuth 2.0, the Meeting Scheduler makes requests to the Calendar API. For example, the class FreeBusyTimesRetriever queries the free/busy endpoint to find available meeting times:
FreeBusyTimesRetriever
FreeBusyRequest request = new FreeBusyRequest(); request.setTimeMin(getDateTime(startDate, 0)); request.setTimeMax(getDateTime(startDate, timeSpan)); for (String attendee : attendees) { requestItems.add(new FreeBusyRequestItem().setId(attendee)); } request.setItems(requestItems); FreeBusyResponse busyTimes; try { Freebusy.Query query = service.freebusy().query(request); // Use partial GET to retrieve only needed fields. query.setFields("calendars"); busyTimes = query.execute(); // ... } catch (IOException e) { // ... }
In this snippet above, note the use of a partial GET request. Calendar API v3, along with many other new Google APIs, provides partial response with GET and PATCH to retrieve or update only the data fields you need for a given request. Using these methods can help you streamline your code and conserve system resources.
For the full context of all calls that Meeting Scheduler makes, including OAuth 2.0 flow and the handling of expired access tokens, see Getting Started with the Calendar API v3 and OAuth 2.0 on Android. The project site provides all the source code and other resources, and there’s a related wiki page with important configuration details.
We hope you’ll have a look at the sample and let us know what you think in the Google Apps Calendar API forum. You’re welcome to create a clone of the source code and do some Meeting Scheduler development of your own. If you find a bug or have an idea for a feature, don’t hesitate to file it for evaluation.
All developers agree that saving bandwidth is a critical factor for the success of a mobile application. Less data usage means faster response times and also lower costs for the users as the vast majority of mobile data plans are limited or billed on a per-usage basis.
When using any of the Google Data APIs in your application, you can reduce the amount of data to be transferred by requesting gzip-encoded responses. On average, the size of a page of users returned by the Provisioning API (100 accounts) is about 100 Kb, while the same data, gzip-encoded, is about 5 Kb -- 20 times smaller! When you can reduce data sizes at this dramatic scale, the benefits of compression will often outweigh any costs in client-side processing for decompression.
Enabling gzip-encoding in your application requires two steps. You have to edit the User-Agent string to contain the value gzip and you must also include an Accept-Encoding header with the same value. For example:
User-Agent
gzip
Accept-Encoding
User-Agent: my application - gzip Accept-Encoding: gzip
Client libraries for the various supported programming languages make enabling gzip-encoded responses even easier. For instance, in the .NET client library it is as easy as setting the boolean UseGZip flag of the RequestFactory object:
service.RequestFactory.UseGZip = true;
For any questions, please get in touch with us in the respective forum for the API you’re using.
Users of the Google Documents List API have traditionally had to perform individual export operations in order to export their documents. This is quite inefficient, both in terms of time and bandwidth for these operations.
To improve latency for these operations, we have added the Archive Feed to the API. Archives allow users to export a large number of items at once, in single ZIP archives. This feature provides a useful optimization for users, greatly increasing the efficiency of export operations. Additionally, users can receive emails about archives, and choose to download them from a link provided in the email.
This feature had a soft release earlier this year, and we think it’s now ready for prime time. For more information, please see the Archive Feed documentation.
Since 1998, when the first doodle was released, they have been one of the most loved features of the Google home page. There have been doodles to celebrate all kinds of events, including national holidays, birthdays of artists and scientists, sports competitions, scientific discoveries and even video games! Also, doodles have evolved from simple static images to complex applications, such as the interactive electric guitar used to celebrate the birthday of Les Paul.
Want your company logo to change for selected events or holidays, just like doodles? The Admin Settings API allows domain administrators to write scripts to programmatically change the logo of their Google Apps domain, and Google App Engine offers the ability to configure regularly scheduled tasks, so that those scripts can run automatically every day.
With these two pieces combined, it is pretty easy to implement a complete solution to change the domain logo on a daily basis (assuming the graphic designers have prepared a doodle for each day), as in the following screenshot:
Let’s start with a Python App Engine script called doodleapps.py:
doodleapps.py
import gdata.apps.adminsettings.service from google.appengine.ext import webapp from google.appengine.ext.webapp import util from datetime import date class DoodleHandler(webapp.RequestHandler): # list of available doodles DOODLES = { '1-1': 'images/newyearsday.jpg', '2-14': 'images/valentinesday.jpg', '10-31': 'images/halloween.jpg', '12-25': 'images/christmas.jpg' } # returns the path to the doodle corresponding to the date # or None if no doodle is available def getHolidayDoodle(self, date): key = '%s-%s' % (date.month, date.day) if key not in self.DOODLES: return None return self.DOODLES[key] # handles HTTP requests by setting today’s doodle def get(self): doodle = self.getHolidayDoodle(date.today()) self.response.out.write(doodle) if doodle: service = gdata.apps.adminsettings.service.AdminSettingsService() // replace domain, email and password with your credentials // or change the authorization mechanism to use OAuth service.domain = 'MYDOMAIN.COM' service.email = 'ADMIN@MYDOMAIN.COM' service.password = 'MYPASSWORD' service.source = 'DoodleApps' service.ProgrammaticLogin() # reads the doodle image and update the domain logo doodle_bytes = open(doodle, "rb").read() service.UpdateDomainLogo(doodle_bytes) # webapp initialization def main(): application = webapp.WSGIApplication([('/', DoodleHandler)], debug=True) util.run_wsgi_app(application) if __name__ == '__main__': main()
The script uses a set of predefined doodles which can be edited to match your list of images or replaced with more sophisticated logic, such as using the Google Calendar API to get the list of holidays in your country.
Every time the script is triggered by an incoming HTTP request, it will check whether a doodle for the date is available and, if there is one, update the domain logo using the Admin Settings API.
In order for this script to be deployed on App Engine, you need to to configure the application by defining a app.yaml file with the following content:
app.yaml
application: doodleapps version: 1 runtime: python api_version: 1 handlers: - url: .* script: doodleapps.py
We want the script to run automatically every 24 hours, without the need for the administrator to send a request, so we also have to define another configuration file called cron.yaml:
cron.yaml
cron: - description: daily doodle update url: / schedule: every 24 hours
Once the application is deployed on App Engine, it will run the script on a daily basis and update the logo.
The holiday season is upon us. Could there be a better time for your company to start using doodles?
Happy Holidays!
Managing the user accounts in a Google Apps domain can be a daunting task when you have hundreds or thousands of them and you have no tools to automate the process. The Google Apps Provisioning API allows developers to write user management applications in the programming language of their choice, but many system administrators prefer a script-based solution instead. The recently launched UserManager Apps Script service fills the gap, providing Google Apps domain administrators an easy way to automate tasks such as batch user creation or update.
With the new Apps Script service, creating a user will be as easy as writing a single line of code:
var user = UserManager.createUser("newuser", "John", "Smith", "mypassword");
The UserManager service also makes it easy to perform the same task on each account in the domain. The following sample shows how you can force all users to change their passwords at the next login:
var users = UserManager.getAllUsers(); for (var i in users) { users[i].setChangePasswordAtNextLogin(true); }
Calls to the UserManager service can also be scheduled to run hourly or daily, or in response to certain events thanks to Apps Script Triggers.
Interested in what else you can do with the UserManager service? Please check the documentation and get in touch with us on the the forum for any questions about its usage or to share more info about your project with the community.
Google Groups is a great way to foster communication over email and on the web, connecting people and allowing them to participate in and read archived discussions. Today, we are introducing the Google Groups Service in Google Apps Script. Groups Service will allow a script to check if a user belongs to a certain group, or to enumerate the members of a particular group. The Google Groups Service works with groups created through the Google Groups web interface as well as groups created by enterprise customers with their own domain using the control panel and the Google Apps Provisioning API.
This opens a wide range of possibilities, such as allowing a script with Ui Services to show additional buttons to the members of a particular group - for example teachers or managers - and sending customized emails to all the members of a group.
Here are a few sample scripts to help you get started with the new API. To try out these samples, select Create > New Spreadsheet and then Tools > Script Editor from the menu. You can then copy the code into the script editor. The scripts’ output will appear back in the spreadsheet.
The Groups Services can be used to fetch a list of the Google Groups of which you’re a member.
Below is a function which returns all the groups of which you’re a member. Copy and paste it into the script editor and run it. The editor will prompt you to grant READ access to the Google Groups Service before the script can run successfully.
If you receive a message stating that you’re not a member of any group, open up Google Groups and join any of the thousands of groups there.
function showMyGroups() { var groups = GroupsApp.getGroups(); var s; if (groups.length > 0) { s = "You belong to " + groups.length + " groups: "; for (var i = 0; i < groups.length; i++) { var group = groups[i]; if (i > 0) { s += ", "; } s += group.getEmail(); } } else { s = "You are not a member of any group!"; } Browser.msgBox(s); }
Brendan plays trumpet in a band. He also runs the band’s website and updates its Google+ page. He’s created a web application with Google Apps Script and now he wants to add to it some additional features for members of the band. Being a model Google user, he’s already subscribed each band member to a Google Group. Although building a complete UI with Google Apps Script is beyond the scope of this article, Brendan could adapt the following function to help make additional features available only to members of that Google Group.
Of course, this is not just useful for tech-savvy trumpet players: schools may wish to make certain features available just to teachers or others just to students; businesses may need to offer certain functionality to people managers or simply to show on a page or in a UI operations of interest to those in a particular department. Before running this example yourself, replace test@example.com with the email address of any group of which you’re a member.
test@example.com
Note: the group’s member list must be visible to the user running the script. Generally, this means you must yourself be a member of a group to successfully test if another user is a member of that same group. Additionally, group owners and managers can restrict member list access to group owners and managers. For such groups, you must be an owner or manager of the group to query membership.
function testGroupMembership() { var groupEmail = "test@example.com"; var group = GroupsApp.getGroupByName(groupEmail); if (group.hasUser(Session.getActiveUser().getEmail())) { Browser.msgBox("You are a member of " + groupEmail); } else { Browser.msgBox("You are not a member of " + groupEmail); } }
Sending an email to the group’s email address forwards that message to all the members of the group. Specifically, that message is forwarded to all those members who subscribe by email. Indeed, for many users, discussion over email is the principal feature of Google Groups.
Suppose, however, that you want to send a customised message to those same people. Provided you have permission to view a group’s member list, the Google Groups Service can be used to fetch the usernames of all the members of a group. The following script demonstrates how to fetch this list and then send an email to each member.
Before running this script, consider if you actually want to send a very silly message to all the members of the group. It may be advisable just to examine how the script works!
function sendCustomizedEmail() { var groupEmail = "test@example.com"; var group = GroupsApp.getGroupByEmail(groupEmail); var users = group.getUsers(); for (var i = 0; i < users.length; i++) { var user = users[i]; MailApp.sendEmail(user.getEmail(), "Thank you!", "Hello " + user.getEmail() + ", thank you for joining this group!"); } }
The Google Groups Service lets you query a user’s role within a group. One possible role is MANAGER (the other roles are described in detail in the Google Groups Service’s documentation): these users can perform administrative tasks on the group, such as renaming the group and accepting membership requests. Any user’s Role can be queried with the help of the Group class’ getRole() method.
This sample function may be used to fetch a list of a group’s managers. Once again, you must have access to the group’s member list for this function to run successfully:
function getGroupOwners(group) { var users = group.getUsers(); var managers = []; for (var i = 0; i < users.length; i++) { var user = users[i]; if (group.getRole(user) == GroupsApp.Role.MANAGER) { managers.push(user); } } return managers; }
Let us know what you end up building, or if you have any questions about this new functionality, by posting in the Apps Script forum.
Editor's note: this announcement is cross-posted from the Google Ads Developer Blog, which caters to AdWords, AdSense, DoubleClick and AdMob developers. We hope you enjoy this latest addition to Google Apps Script — Ryan Boyd
Starting today, the AdSense Management API is available as part of AdSense Services in Google Apps Script. This means that you’ll be able to do things like:
Accessing the API from Google Apps Scripts is very easy. The following snippet of code shows how to generate a report and populate columns of a spreadsheet with the data retrieved:
function generateReport() { var ss = SpreadsheetApp.getActiveSpreadsheet(); var sheet = ss.getSheetByName('Reports'); var startDate = Browser.inputBox( "Enter a start date (format: 'yyyy-mm-dd')"); var endDate = Browser.inputBox( "Enter an end date (format: 'yyyy-mm-dd')"); var args = { 'metric': ['PAGE_VIEWS', 'AD_REQUESTS', 'MATCHED_AD_REQUESTS', 'INDIVIDUAL_AD_IMPRESSIONS'], 'dimension': ['MONTH']}; var report = AdSense.Reports.generate(startDate, endDate, args).getRows(); for (var i=0; i<report.length; i++) { var row = report[i]; sheet.getRange('A' + String(i+2)).setValue(row[0]); sheet.getRange('B' + String(i+2)).setValue(row[1]); sheet.getRange('C' + String(i+2)).setValue(row[2]); sheet.getRange('D' + String(i+2)).setValue(row[3]); sheet.getRange('E' + String(i+2)).setValue(row[4]); } }
function generateLineChart() { var doc = SpreadsheetApp.getActiveSpreadsheet(); var startDate = Browser.inputBox( "Enter a start date (format: 'yyyy-mm-dd')"); var endDate = Browser.inputBox( "Enter an end date (format: 'yyyy-mm-dd')"); var adClientId = Browser.inputBox("Enter an ad client id"); var args = { 'filter': ['AD_CLIENT_ID==' + adClientId], 'metric': ['PAGE_VIEWS', 'AD_REQUESTS', 'MATCHED_AD_REQUESTS', 'INDIVIDUAL_AD_IMPRESSIONS'], 'dimension': ['MONTH']}; var report = AdSense.Reports.generate(startDate, endDate, args).getRows(); var data = Charts.newDataTable() .addColumn(Charts.ColumnType.STRING, "Month") .addColumn(Charts.ColumnType.NUMBER, "Page views") .addColumn(Charts.ColumnType.NUMBER, "Ad requests") .addColumn(Charts.ColumnType.NUMBER, "Matched ad requests") .addColumn(Charts.ColumnType.NUMBER, "Individual ad impressions"); // Convert the metrics to numeric values. for (var i=0; i<report.length; i++) { var row = report[i]; data.addRow([row[0],parseInt(row[1]),parseInt(row[2]), parseInt(row[3]),parseInt(row[4])]); } data.build(); var chart = Charts.newLineChart() .setDataTable(data) .setTitle("Performances per Month") .build(); var app = UiApp.createApplication().setTitle("Performances"); var panel = app.createVerticalPanel() .setHeight('350') .setWidth('700'); panel.add(chart); app.add(panel); doc.show(app); }
Google Apps domain administrators have access to a number of APIs to automate their management activities or to integrate with third-party applications, including the Provisioning API to manage user accounts and groups, the Admin Audit API, Admin Settings API, and the Reporting API.
These APIs were only available in Google Apps for Business, Education and ISP editions but many administrators of the free version of Google Apps also requested access to them. I’m glad to say that we listened to your feedback and, starting today, we made the these APIs available to all Google Apps editions.
Please check the documentation as the starting point to explore the possibilities of the APIs and post on our forum if you have questions or comments.
When we launched the Google Apps Marketplace in March last year, one of our goals was to make it easy for developers to build, integrate, and sell their apps to Google Apps users. Since then, each vendor in the Google Apps Marketplace has handled their own billing and keeps the revenue they generate. Today, we are making that the official Marketplace policy moving forward: Google will not require vendors to adopt a Google billing API or share any portion of their revenue with us. This will keep more revenue in developers’ pockets, and brings the Google Apps Marketplace policy in line with the Chrome Web Store.
So it’s business as usual -- developers can continue to “bring their own billing” to the Marketplace. Developers retain full control over application pricing and billing, and continue to keep all revenue from Google Apps Marketplace customers.
If you’re in need of a billing solution, we encourage you to try Google Checkout and In-App Payments. Or use one of the many other commercial billing and subscription services available online.
Please join us at our next Google Apps Developer Office Hours on Google+ Hangouts on December 19th at 12 PM PST time to discuss the Google Apps Marketplace and any questions you may have about these changes.
2-Legged OAuth is a useful authorization mechanism for apps that need to manipulate calendars on behalf of users in an organization. Both developers building apps for the Google Apps Marketplace and domain administrators writing tools for their own domains can benefit. Let’s take a look at how to do this with the new Calendar API v3 and Java client library 1.6.0 beta.
To get started as a domain administrator, you need to explicitly enable the new Google Calendar API scopes under Google Apps cPanel > Advanced tools > Manage your OAuth access > Manage third party OAuth Client access:
The scope to include is:
https://www.googleapis.com/auth/calendar
To do the same for a Marketplace app, include the scope in your application's manifest.
Calendar API v3 also needs an API access key that can be retrieved in the APIs Console. Once these requirements are taken care of, a new Calendar service object can be initialized:
public Calendar buildService() { HttpTransport transport = AndroidHttp.newCompatibleTransport(); JacksonFactory jsonFactory = new JacksonFactory(); // The 2-LO authorization section OAuthHmacSigner signer = new OAuthHmacSigner(); signer.clientSharedSecret = "<CONSUMER_SECRET>"; final OAuthParameters oauthParameters = new OAuthParameters(); oauthParameters.version = "1"; oauthParameters.consumerKey = "<CONSUMER_KEY>"; oauthParameters.signer = signer; Calendar service = Calendar.builder(transport, jsonFactory) .setApplicationName("<YOUR_APPLICATION_NAME>") .setJsonHttpRequestInitializer(new JsonHttpRequestInitializer() { @Override public void initialize(JsonHttpRequest request) { CalendarRequest calendarRequest = (CalendarRequest) request; calendarRequest.setKey("<YOUR_API_KEY>"); } }).setHttpRequestInitializer(oauthParameters).build(); return service; }
Once the Calendar service object is properly initialized, it can be used to send authorized requests to the API. To access calendar data for a particular user, set the query parameter xoauth_requestor_id to a user’s email address:
xoauth_requestor_id
public void printEvents() { Calendar service = buildService(); // Add the xoauth_requestor_id query parameter to let the API know // on behalf of which user the request is being made. ArrayMap customKeys = new ArrayMap(); customKeys.add("xoauth_requestor_id", "<USER_EMAIL_ADDRESS>"); List listEventsOperation = service.events().list("primary"); listEventsOperation.setUnknownKeys(customKeys); Events events = listEventsOperation.execute(); for (Event event : events.getItems()) { System.out.println("Event: " + event.getSummary()); } }
Additionally, if the same service will be used to send requests on behalf of the same user, the xoauth_requestor_id query parameter can be set in the initializer:
// … Calendar service = Calendar.builder(transport, jsonFactory) .setApplicationName("<YOUR_APPLICATION_NAME>") .setJsonHttpRequestInitializer(new JsonHttpRequestInitializer() { @Override public void initialize(JsonHttpRequest request) { ArrayMap customKeys = new ArrayMap(); customKeys.add("xoauth_requestor_id", "<USER_EMAIL_ADDRESS>"); calendarRequest.setKey("<YOUR_API_KEY>"); calendarRequest.setUnknownKeys(customKeys); } }).setHttpRequestInitializer(oauthParameters).build(); // ...
We hope you’ll try out 2-Legged OAuth and let us know what you think in the Google Calendar API forum.
With the Google Documents List API, there are two ways to identify resources: typed and untyped resource identifiers. Typed resource identifiers prefix a string of characters with the resource type. Untyped resource identifiers are similar, but do not have a type prefix. For example:
drawing:0Aj01z0xcb9
0Aj01z0xcb9
Client applications often need one type of identifier or the other. For instance, some applications use untyped resource IDs to access spreadsheets using the Google Spreadsheets API. Automatically generated API URLs in the Documents List API use typed or untyped resource IDs in certain situations.
Having two types of resource IDs is something that we will resolve in a future version of the API. Meanwhile, we strongly recommend that instead of using resource identifiers, clients always use URLs provided in feeds and entries of the Google Documents List API. The only time that manual URL modification is required is to add special parameters to a URL given by the API, for instance to search for a resource by title.
For example, the API issues self links along with each entry. To request an entry again, simply GET the self link of the entry. We recommend against constructing the link manually, by inserting the entry’s resource ID into the link.
Common links on entries include:
Accessing these links from a client library is simple. For instance, to retrieve the alternate link in Python, one uses:
resource = client.GetAllResources()[0] print resource.GetHtmlLink()
More information on these links is available in the documentation. For any questions, please post in the forum.
$apiClient = new apiClient(); $apiClient->setUseObjects(true);
$event = $service->events->get("primary", "eventId"); echo $event->getSummary();
result = client.execute( :api_method => service.events.get, :parameters => {'calendarId' => 'primary', 'eventId' => 'eventId'}) print result.data.summary
import randomimport timedef GetResourcesWithExponentialBackoff(client): """Gets all of the resources for the authorized user Args: client: gdata.docs.client.DocsClient authorized for a user. Returns: gdata.docs.data.ResourceFeed representing Resources found in request. """ for n in range(0, 5): try: response = client.GetResources() return response except: time.sleep((2 ** n) + (random.randint(0, 1000) / 1000)) print "There has been an error, the request never succeeded." return None
Developers using the Google Apps APIs owe it to themselves to stay informed. To make it as easy as possible, we’ve adopted a standard process for making important announcements about the APIs.
All major outages or issues, although infrequent, are announced on the Google Apps APIs Downtime Notify list as quickly as possible. We also post updates and resolutions. All developers using the Google Apps APIs should subscribe.
Announcements about new APIs and features as well as best practices about the Google Apps APIs occur on the this blog. You can subscribe via e-mail (see right sidebar), using the feed, by following @GoogleAppsDev on Twitter, or by following members of the team on Google+.
If you want to hear about just the most important announcements, follow the Google Apps APIs Announcements forum. This is a low-volume list and you can subscribe via e-mail.
Lastly, you should subscribe to the individual forums for the Google Apps APIs you care about the most. A lot of technical discussion occurs in the forums and many advanced API users participate in the conversation along with Google engineers.
We recently launched a new version of the Google Calendar API. In addition to the advantages it gains from Google's new infrastructure for APIs, Google Calendar API v3 has a number of improvements that are specific to Google Calendar. In this blog post we’ll highlight a topic that often causes confusion for developers using the Google Calendar API: recurring events.
A recurring event is a 'template' for a series of events that usually happen with some regularity, for example daily or bi-weekly. To create a recurring event, the client specifies the first instance of the event and includes one or more rules that describe when future events should occur. Google Calendar will then 'expand' the event into the specified occurrences. Individual events in a series may be changed, or even deleted. Such events become exceptions: they are still part of the series, but changes are preserved even if the recurring event itself is updated.
Let's create a daily recurring event that will occur every weekday of the current week (as specified by the recurrence rule on the last line):
POST https://www.googleapis.com/calendar/v3/calendars/primary/events { "summary": "Daily project sync", "start": { "dateTime": "2011-12-12T10:00:00", "timeZone": "Europe/Zurich" }, "end": { "dateTime": "2011-12-12T10:15:00", "timeZone": "Europe/Zurich" }, "recurrence": [ "RRULE:FREQ=DAILY;COUNT=5" ] }
When added to a calendar, this will turn into five different events. The recurrence rule is specified according to the iCalendar format (see RFC 5545). Note, however, that, in contrast to the previous versions of the Google Calendar API, the start and end times are specified the same way as for single instance events, and not with iCalendar syntax. Further, note that a timezone identifier for both the start and end time is always required for recurring events, so that expansion happens correctly if part of a series occurs during daylight savings time.
By default, when listing events on a calendar, recurring events and all exceptions (including canceled events) are returned. To avoid having to expand recurring events, a client can set the singleEvents query parameter to true, like in the previous versions of the API. Doing so excludes the recurring events, but includes all expanded instances.
Another way to get instances of a recurring event is to use the 'instances' collection, which is a new feature of this API version. To list all instances of the daily event that we just created, we can use a query like this:
GET https://www.googleapis.com/calendar/v3/calendars/primary/events/7n6f7a9g8a483r95t8en23rfs4/instances
which returns something like this:
{ ... "items": [ { "kind": "calendar#event", "id": "7n6f7a9g8a483r95t8en23rfs4_20111212T090000Z", "summary": "Daily project sync", "start": { "dateTime": "2011-12-12T10:00:00+01:00" }, "end": { "dateTime": "2011-12-12T10:15:00+01:00" }, "recurringEventId": "7n6f7a9g8a483r95t8en23rfs4", "originalStartTime": { "dateTime": "2011-12-12T10:00:00+01:00", "timeZone": "Europe/Zurich" }, ... }, … (4 more instances) ...
Now, we could turn one instance into an exception by updating that event on the server. For example, we could move one meeting in the series to one hour later as usual and change the title. The original start date in the event is kept, and serves as an identifier of the instance within the series.
If you have a client that does its own recurrence rule expansion and knows the original start date of an instance that you want to change, the best way to get the instance is to use the originalStart parameter like so:
originalStart
GET https://www.googleapis.com/calendar/v3/calendars/primary/events/7n6f7a9g8a483r95t8en23rfs4/instances?originalStart=2011-12-16T10:00:00%2B01:00
This would return a collection with either zero or one item, depending on whether the instance with the exact original start date exists. If it does, just update or delete the event as above.
We hope you’ll find value in these changes to recurring events. Keep in mind, too, that these are not the only improvements in Google Calendar API v3. Look for an upcoming post describing best practices for another key area of improvement: reminders.
If you have any questions about handling recurring events or other features of the new Calendar API, post them on the Calendar API forum.
Editor's note:: 2/20/2012 - Removed references to API call which reverted changes made to an individual instance. This feature was deprecated.
We've held many Office Hours on Google+ Hangouts over the last two months, bringing together Google Apps developers from around the world along with Googlers to discuss the Apps APIs. We've heard great feedback about these Office Hours from participants, so we've scheduled a few more in 2011.
General office hours (covering all Google Apps APIs):
Product-specific office hours:
As we add more Office Hours in the new year, we'll post them on the events calendar, and announce them on @GoogleAppsDev and our personal Google+ profiles.
Hope you’ll hang out with us soon!
Editor’s note: If you’re in the SF area, join us tomorrow for our next hackathon. There are a few spots left. See Eric's note at the end of this post.
Last Thursday’s Google Apps Hackathon in New York brought together 70 developers to create cool new applications using Google Apps APIs. The varied field of talent included individual developers, small teams from companies like ShuttleCloud, and a group of technically inclined educators from the non-profit New Visions for Public Schools.
Ten Google engineers were on hand to help participants with tips and information about specific APIs. Among them, Dan Holevoet and Alain Vongsouvanh from Mountain View shared their Calendar and Contacts API expertise, while Shradda Gupta and Gunjan Sharma traveled from India to help field questions about admin and domain management APIs.
At this hackathon, competitors had two categories to choose from: building new applications using Google Apps APIs, and integrating Google Apps APIs with an existing app. The various groups entered and demonstrated a total of sixteen apps across the two categories, including:
For the new apps category, the panel of judges settled first prize on “AlphaSheet,” a simple but streamlined app using a custom spreadsheet function to fill in values from Wolfram Alpha queries. “Fareshare” took top honors in the second category for integrating Google Calendar in a mobile app that helps New Yorkers share cab rides.
It’s great to see developers join forces and have a great time hacking up new apps at events like this one. An impressive amount of knowledge-sharing occurred, and everyone walked away with new ideas and skills to take back to their work in the realms of business or education.
If you missed this event, don’t worry — the future holds more hackathons, including the upcoming event tomorrow (Dec 6th) at Google’s Mountain View, CA campus from 1:00pm - 8:00pm PST. If you’re located in Europe, we have a series of events planned over the next few months.
Yesterday, the Google APIs Client Library for JavaScript was released, unlocking tons of possibilities for fast, dynamic web applications, without requiring developers to run their own backend services to talk to Google APIs. This client library supports all discovery-based APIs, including the Google Tasks, Google Calendar v3 and Groups Settings APIs. To make it easy to get started using the JS client with Google Apps APIs, we’ve provided an example below.
After you’ve configured your APIs console project as described in the client library instructions, grab a copy of your client ID and API key, as well as the scopes you need to access the API of your choice.
var clientId = 'YOUR_CLIENT_ID'; var apiKey = 'YOUR_API_KEY'; var scopes = 'https://www.googleapis.com/auth/calendar';
You’ll also need several boilerplate methods to check that the user is logged in and to handle authorization:
function handleClientLoad() { gapi.client.setApiKey(apiKey); window.setTimeout(checkAuth,1); checkAuth(); } function checkAuth() { gapi.auth.authorize({client_id: clientId, scope: scopes, immediate: true}, handleAuthResult); } function handleAuthResult(authResult) { var authorizeButton = document.getElementById('authorize-button'); if (authResult) { authorizeButton.style.visibility = 'hidden'; makeApiCall(); } else { authorizeButton.style.visibility = ''; authorizeButton.onclick = handleAuthClick; } } function handleAuthClick(event) { gapi.auth.authorize( {client_id: clientId, scope: scopes, immediate: false}, handleAuthResult); return false; }
Once the application is authorized, the makeApiCall function makes a request to the API of your choice. Here we make a request to retrieve a list of events from the user’s primary calendar, and use the results to populate a list on the page:
function makeApiCall() { gapi.client.load('calendar', 'v3', function() { var request = gapi.client.calendar.events.list({ 'calendarId': 'primary' }); request.execute(function(resp) { for (var i = 0; i < resp.items.length; i++) { var li = document.createElement('li'); li.appendChild(document.createTextNode(resp.items[i].summary)); document.getElementById('events').appendChild(li); } }); }); }
To tie all of this together, we use the following HTML, which configures the DOM elements we need to display the list, a login button the user can click to grant authorization, and a script tag to initially load the client library:
<html> <body> <div id='content'> <h1>Events</h1> <ul id='events'></ul> </div> <a href='#' id='authorize-button' onclick='handleAuthClick();'>Login</a> <script> // Insert the JS from above, here. </script> <script src="https://apis.google.com/js/client.js?onload=handleClientLoad"></script> </body> </html>
Making requests to other discovery-based APIs, such as the Tasks API requires only small modifications to the above:
As additional discovery-based APIs are released, they’ll be automatically supported by the library (just like the Python and Ruby clients).
If you have general questions about the JS client, check out the JavaScript client group. For questions on specific Apps APIs come find us in the respective Apps API forum.