Over a million developers have joined DZone.

MS Office is so last year, Connect to Google Apps (2/3)

DZone's Guide to

MS Office is so last year, Connect to Google Apps (2/3)

Free Resource

Modernize your application architectures with microservices and APIs with best practices from this free virtual summit series. Brought to you in partnership with CA Technologies.

Google Apps offers a cloud alternative to many of the office products.  If you have a Gmail account then you have Google Apps including Spreadsheets, Docs, Presentations, Contacts, Calendars and Tasks.  Of course Google Apps have APIS and of course we have the connectors to make it easy to connect Google Apps and your applications together.  Lets get the connectors and then take a look at what you can do.

Get set

First thing to do is to install the connectors. If  you’re using Mule Studio, then the connectors are available in the Cloud Connectors update site. Go to the Help menu -> install new software and select the Mule Cloud Connectors Update Site. Then select the Google Cloud connectors and follow the wizard:
If you’re not a Studio user, then you can find how to install each connector through Maven using this github repo as the starting point for all Google Apps connectors.

How to create stuff

Let’s start with simple examples of how to create simple entities. Let’s make a flow that receives a query param called “taskName” and then we use it to create a task:

<flow name="createTask">
        <http:inbound-endpoint exchange-pattern="request-response"
                port="${http.port}" path="createTask" />

        <custom-transformer class="org.mule.sample.CreateTaskConnector" />
  	<google-tasks:insert-task config-ref="Google_Tasks_Connector" />

How does the connector get the info of the task to be created? The CreateTaskTransformer instantiates a Task object and the connector takes it from the payload. The transformer’s code:

import org.mule.api.MuleMessage;
import org.mule.api.transformer.TransformerException;
import org.mule.module.google.task.model.Task;
import org.mule.transformer.AbstractMessageTransformer;

import org.mule.modules.google.api.datetime.DateTime;

public class CreateTaskTransformer extends AbstractMessageTransformer {
	public Object transformMessage(MuleMessage message, String outputEncoding) throws TransformerException {
		Task task = new Task();
		task.setDue(new DateTime());

		return task;

And this how and event would be created following the same pattern. This time we receive the calendar id on a query param plus some other query params you’ll see on the transformer.

<flow name="createEvent">
        <http:inbound-endpoint exchange-pattern="request-response" 
        host="localhost" port="${http.port}"
        path="createTask" />

  <custom-transformer class="org.mule.sample.CreateEventTransformer" />
  <google-calendars:insert-event calendarId="#[message.inboundProperties['calendarId']]" 
        config-ref="Google_Calendars_Connector" />


And the transformer’s code:

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;

import org.mule.api.MuleMessage;
import org.mule.api.transformer.TransformerException;
import org.mule.module.google.calendar.model.Event;
import org.mule.module.google.calendar.model.EventAttendee;
import org.mule.module.google.calendar.model.EventDateTime;
import org.mule.modules.google.api.datetime.DateTime;
import org.mule.transformer.AbstractMessageTransformer;

public class CreateEventTransformer extends AbstractMessageTransformer {
	public Object transformMessage(MuleMessage message, String outputEncoding) throws TransformerException {
		Event event = new Event();


		List<EventAttendee> attendees = new ArrayList<EventAttendee>();
		EventAttendee att = new EventAttendee();


		Date startDate = new Date();
		Date endDate = new Date(startDate.getTime() + 3600000);
		EventDateTime startTime = new EventDateTime();
		startTime.setDateTime(new DateTime(startDate, TimeZone.getTimeZone("UTC")));
		EventDateTime endTime = new EventDateTime();
		endTime.setDateTime(new DateTime(endDate, TimeZone.getTimeZone("UTC")));

		return event;	

To create a contact you can do:

<flow name="createContact">
        <http:inbound-endpoint exchange-pattern="request-response"
        host="localhost" port="${http.port}"
        path="createContact" />
        <custom-transformer class="org.mule.samples.CreateContactTransformer" />
        <google-contacts:insert-contact config-ref="contacts" />

In this case the transformer is a little bit more complex, but still the very same idea:

import org.mule.api.MuleMessage;
import org.mule.api.transformer.TransformerException;
import org.mule.transformer.AbstractMessageTransformer;

import com.google.gdata.data.PlainTextConstruct;
import com.google.gdata.data.contacts.ContactEntry;
import com.google.gdata.data.extensions.City;
import com.google.gdata.data.extensions.Country;
import com.google.gdata.data.extensions.Email;
import com.google.gdata.data.extensions.FamilyName;
import com.google.gdata.data.extensions.FormattedAddress;
import com.google.gdata.data.extensions.FullName;
import com.google.gdata.data.extensions.GivenName;
import com.google.gdata.data.extensions.Im;
import com.google.gdata.data.extensions.Name;
import com.google.gdata.data.extensions.PhoneNumber;
import com.google.gdata.data.extensions.PostCode;
import com.google.gdata.data.extensions.Region;
import com.google.gdata.data.extensions.Street;
import com.google.gdata.data.extensions.StructuredPostalAddress;

public class CreateContactTransformer extends AbstractMessageTransformer {
	public Object transformMessage(MuleMessage message, String outputEncoding) throws TransformerException {
		// Create the entry to insert.
		  ContactEntry contact = new ContactEntry();
		  // Set the contact's name.
		  Name name = new Name();
		  final String NO_YOMI = null;
		  name.setFullName(new FullName(message.getInboundProperty("fullname"), NO_YOMI));
		  name.setGivenName(new GivenName(message.getInboundProperty("givenName"), NO_YOMI));
		  name.setFamilyName(new FamilyName(message.getInboundProperty("familyName"), NO_YOMI))
		  contact.setContent(new PlainTextConstruct("Notes"));
		  // Set contact's e-mail addresses.
		  Email primaryMail = new Email();
		  Email secondaryMail = new Email();
		  // Set contact's phone numbers.
		  PhoneNumber primaryPhoneNumber = new PhoneNumber();
		  PhoneNumber secondaryPhoneNumber = new PhoneNumber();
		  return contact;

The Object caveat

We could keep throwing up examples but at this point you probably realized that in one hand is so very useful to have the entities represented as complex objects, but at the same time it takes quite an effort to instantiate them. We know this and have taken it into account . How? DataMapper works smoothly with all these object types, so that you can visually design transformations that will generate them in a much easier and maintainable way. This will come specially handy when we start discussing batch operations in the next section (also remember that next and final release of this series will be all about using this connectors with DataMapper)

How to batch stuff together

One of the typical issues of HTTP based API’s is the overhead of initializing the underlying http connection. Google API is no exception. That’s why they included the concept of batch operations into their API. Pretty cool huh? It has some limitations though:

  • You cannot mix different APIs in the same batch: For example, you cannot make a batch that inserts a contact and and event, or creates and spreadsheet and a task. Each API handles its batches differently
  • No tasks: The tasks API does not support batchs

So, let’s go with some examples:

Google Calendars

The calendars API has some additional limitations to the ones listed above. You have to make individual batches for each operation types. For example, you have to make one batch for all the inserts, another one for the updates and a third one for the deletes. Put in code this is how you would batch insert events:


And this is how you update:

   config-ref="google-calendars" />

And finally a deletion:

   config-ref="google-calendars" />

All the examples above are to create events. You can however also create calendars, which is pretty much the same deal:

<google-calendars:batch-insert-calendar calendars-ref="#[variable:newCalendars]" />

Google Contacts

The batch API in the contacts is a more flexible one, it allows you to group inserts/updates/and deletes together, the only restriction is that you cannot combine contacts and groups.  The connector uses nested processors to fully take advantage of this flexibility and provide the best user experience possible:

<google-contacts:batch-contacts config-ref="google-contacts" accessTokenId="#[variable:tokenId]">
		<when expression="payload.inserts.size() > 0">
                                    accessTokenId="#[variable:tokenId]" />
			<logger message="No new contacts" />
		<when expression="payload.updates.size() > 0">
                                    accessTokenId="#[variable:tokenId]" />
			<logger message="No updates" />

		<when expression="payload.deletes.size() > 0">
                                    accessTokenId="#[variable:tokenId]" />
			<logger message="No deletes" />

As you can see, you can even put a <choice>, a <foreach> and potentially any mule component inside the batch element. Possibilities are endless!

Google Spreadsheets

The spreadsheets API is probably the one that takes the most advantage of batching. That’s because of the very nature of its data model. A spreadsheet has a set of worksheet which has a set of rows which in turn has a set of cells. Multiplicity is all over the place! That’s why the connector batches all modifications by default. We realized that the use case in which you want to modify one single cell is very unlikely but still a batch in itself. Look at the following example to analyze:

<google-spreadsheets:set-row-values config-ref="google-spreadsheets"
                   spreadsheet="My Accounts" worksheet="Accounts">
	<google-spreadsheets:rows ref="#[payload]" />

The connector provides a model class called org.mule.module.google.spreadsheet.model.Row which contains row number and a list of cells. At the same time, the set-row-values processor in the conector takes a list of Row objects containing the values that needs to change (notice that you’re not obligated to represent every single cell in the spreadsheet, only the ones which values you want to change). The connector will then modify all referenced cells into the same batch. Easy stuff!

Last but not least… Search for stuff!

In my mind, searching is all about being able to apply the right filters (and yes, that includes pagination). The connectors fully support this to the extent of the underlying API. Let’s look AT examples:

Search events

	accessTokenId="#[variable:tokenId]" />

As you can see the example is pretty straight forward and uses only a handful of the search criterias available (for a full description of the search method look at this link).  But there’s one item that escapes the obvious though, which is the pagination token.

The way Google works is that whenever a search result needs to be paginated, they will only return the first page and will provide a next page token. So, if in the next search your provide that token, the next page will be returned instead. So, if Google gives you a next page token, the connector will place it in the message as a flow variable under the key “GoogleCalendar_NEXT_PAGE_TOKEN“. At the same time, if you provide a token to the search method it will be used, if you don’t or if you provide null, then we just don’t use it (that’s why the ‘?’ character is added to the expression).

Search for Tasks

Since the Task API provides search capabilities similar to the Calendars API, the search processor in the connector also follows a consistent approach:

	accessTokenId="#[variable:tokenId]" />

Search for people

The google contacts search api follows pretty much the same design as the other ones. Repeating yourself is the worst thing that can happen to an artist, so for contacts I’m going to show a particular capability of this API which is searching people by their group memberships:

	accessTokenId="#[variable:tokenId]Contact" />


This post is only intended as a quick view over what’s a large set of API’s. Please remember that full documentation of the connector’s capabilities are available in the following links:

Also remember to stop by for the third and final part of this series where will discuss how DataMapper can make your life so much easier. As always, thank you for reading and feel free to post your feedback/questions.

The Integration Zone is proudly sponsored by CA Technologies. Learn from expert microservices and API presentations at the Modernizing Application Architectures Virtual Summit Series.


Opinions expressed by DZone contributors are their own.


Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.


{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}