Welcome to Part 9 of this series of guides on how to integrate Gmail with Angular and Spring Boot.

You'll be happy to learn that in this guide you'll write code that actually uses the Gmail API.

You haven't done that yet.

That's because I've spent the lion's share of these guides covering OAuth2 authentication with Google. But now that you've put that notch in your belt, it's time to move on to doing the real work: Gmail integration.

And in this part of the guide, I'll help you get started with that.

If you'd rather just copy and paste code, though, you can do that from GitHub.

Otherwise, read on.

At This Milestone

Thus far in the series, you have:

  • Created an OAuth2 client ID and secret on Google Cloud Console
  • Enabled the Gmail API via Google Cloud Console
  • Set the necessary properties in your Spring Boot application.properties file 
  • Set up a DataStore implementation with its associated factory
  • Set up a refresh listener
  • Created a utility class that handles the Google authorization code flow
  • Set up proper Gmail consent with the Google Cloud Platform
  • Created a controller that handles a request to get the Google authorization code flow URL
  • Used Postman to get the Google authorization code URL
  • Used that URL to authorize your application to access your Gmail inbox
  • Updated the controller so it handles a token request
  • Updated the utility class so it creates the credential from a token
  • Persisted that credential
  • Used Postman to retrieve the token
  • Learned why you didn't actually need to retrieve the token
  • Learned about the structure of the Message object
  • Learned about MIME
  • Created an Email type that gets sent back to the client
  • Wrote code that converts a Message object to an Email object

And if you haven't done those things, then you should take a look at Part 1 and work forward from there.

The Next Milestone

In this guide, you will:

  • Instantiate the Gmail service
  • Use it to retrieve just the email messages you want to retrieve

Then you can back to drinking your Grape Nehi.

Gmail at Your Service

At long last, it's time to access the user's inbox. But if you want to do that, you'll need to instantiate a Gmail object.

Even though that class doesn't include the word "service" in the name, it's a service.

It's the point of contact between your Spring Boot microservice and the downstream Gmail service.

So it's kind of a big deal. 

Anyhoo, let's revisit GmailUtil and take a look at how you instantiate that beast:

    public List<Email> getInbox(Credential credential) throws IOException, GeneralSecurityException {
        Gmail service = new Gmail.Builder(GoogleNetHttpTransport.newTrustedTransport(), new GsonFactory(), credential)
                .setApplicationName(Constants.GMAIL_APPLICATION_NAME)
                .build();           
        
        List<Message> list = getMessageList(service);
        List<Email> emails = new ArrayList<Email>();
        
        if (list != null) {
            list.forEach(message -> {
                Email email = getSingleEmailMessageById(message.getId(), service);
                if (email != null) emails.add(email);
            });
        }
        
        return emails;
    }

There's a lot going on in that whole method so for now just key in on the Gmail instantiation at the top. 

It starts off the object creation process with a familiar pattern. It uses a builder.

It's Gmail.Builder. And the constructor uses just three parameters:

  • The HTTP library implementation used by the service
  • The JSON parser used by the service
  • The user's credential as retrieved from the MongoDB document

It also sets the application name with a String constant defined in Constants.

Finally, the builder invokes the build() method that produces an instance of Gmail.

Once you get through that process with no exceptions, you're gold. All you need to do is look at the Gmail API reference and map each resource to its appropriate Java class and method. Then, you can do whatever the security scope will allow you to do with that user's Gmail account.

The Innards

Take another look at that method up above. As you can tell from the name (getInbox()), it gets the user's Gmail inbox.

Well, that's a bit of a misnomer.

After all, you don't necessarily want all the messages from the user's inbox. There could be thousands of them.

So the code ultimately just grabs the top 20 messages.

Where does it do that? In the getMessageList() method. Here's what it looks like:

    private List<Message> getMessageList(Gmail service) {
        try {
            Messages messages = service.users().messages();
            Gmail.Users.Messages.List messageList = messages
                                                    .list("me")
                                                    .setQ("in:inbox -category:{social promotions forums}")
                                                    .setMaxResults(20l);
            
            ListMessagesResponse rsp = messageList.execute();
            List<Message> list = rsp.getMessages();
    
            return list;
        } catch (IOException ie) {
            LOG.error("Problem retrieving Gmail messages!", ie);
        }
        
        return null;
    }

That method return a List of Message objects. You won't be shocked to learn that each Message object represents an email message in the user's Gmail inbox.

So in a nutshell: the method uses the Gmail object provided as a parameter to get the top 20 messages from the user's inbox.

I'll walk you through how it does that step by step.

First, the code invokes the users() method to get the Users object.

Bookmark that one. You'll use that object for everything.

Next, the code invokes the messages() method on the Users object to get back a Messages object.

That object gives you a variety of methods you can use to get the user's email messages.

One of those methods is list(). That method takes a single parameter: the user's ID. Then, it gets a List object that lets you retrieve emails based on criteria.

But let's back up a minute. What's with the word "me" in that list() method? That doesn't look like a user ID.

It's not. That string is just a shorthand way of referring to the already credentialed user. In this case, that's whoever is using your Angular app.

A Quick Query

Now let's get back to what you can do with that List object. You can set a query using the setQ() method. That query is a simple string with different parts of the query separated by a space.

If you look at the code above, you'll see it's using this query:

in:inbox -category:{social promotions forums}

That's saying: "I want all emails in the inbox that aren't in the 'social,' 'promotions,' or 'forums' categories."

I think you'll find that's what you'll see in your inbox if you use your Gmail app or visit the site on the web.

The "in:inbox" part of the query means all emails "in the inbox."

That minus sign in front of category means "I don't want these." The categories that it doesn't want are listed in brackets.

Keep in mind: that's just scratching the surface of what you can do with a Gmail query. Feel free to browse through the whole list of Gmail search operators and select the ones that work best for your requirements.

That setMaxResults() method invocation will limit results to 20 email messages. It's hardcoded so it's clearly a candidate for refactoring later.

But even after all of those method invocations and query-settings, the code still hasn't returned a list of messages.

Nope. That's why it needs to use the execute() method on the List object. Only then does it finally get back something.

And that something is a ListMessagesResponse object. The code uses that to get a List of Message objects.

Wrapping It Up

Whew. We've covered a lot of territory in a fairly brief timespan.

Hope you enjoyed it. In the next guide, you'll take what you've learned here to the next level.

In the meantime, have fun!

Photo by Edward Jenner from Pexels