Bernard Halas
Bernard Halas

Reputation: 1190

Working with Gmail API from Google AppEngine

In GAE standard environment, I'm struggling with registering the watch() call against Gmail API for the Pub/Sub push notification using google-api-python-client.

Here is the relevant excerpt from my code:

import googleapiclient.discovery
from oauth2client import service_account

SCOPES = ['https://www.googleapis.com/auth/gmail.modify']
SERVICE_ACCOUNT_FILE = '<My-project>-<short-ID>.json'

credentials = service_account.ServiceAccountCredentials.from_json_keyfile_name(SERVICE_ACCOUNT_FILE, scopes=SCOPES)
gmail = googleapiclient.discovery.build('gmail', 'v1', credentials=credentials)

watchRequest = {
    'labelIds' : ['INBOX'],
    'topicName' : 'projects/<my-project>/topics/<my-topic>'
}

gmail.users().watch(userId='<email-I-need-to-watch>', body=watchRequest).execute()

After firing-off this part of the code I get:

Traceback (most recent call last):
  File     "/base/alloc/tmpfs/dynamic_runtimes/python27/54c5883f70296ec8_unzipped/python27_lib/versions/1/google/appengine/runtime/wsgi.py", line 240, in Handle
    handler = _config_handle.add_wsgi_middleware(self._LoadHandler())
  File     "/base/alloc/tmpfs/dynamic_runtimes/python27/54c5883f70296ec8_unzipped/python27_lib/versions/1/google/appengine/runtime/wsgi.py", line 299, in     _LoadHandler
    handler, path, err = LoadObject(self._handler)
  File "/base/alloc/tmpfs/dynamic_runtimes/python27/54c5883f70296ec8_unzipped/python27_lib/versions/1/google/appengine/runtime/wsgi.py", line 85, in LoadObject
obj = __import__(path[0])
  File "/base/data/home/apps/e~<my-project>-191008/20180124t154459.407164278206739455/main.py", line 68, in <module>
gmail.users().watch(userId='<email-I-need-to-watch>', body=watchRequest).execute()
  File "/base/data/home/apps/e~<my-project>/20180124t154459.407164278206739455/lib/oauth2client/_helpers.py", line 133, in positional_wrapper
return wrapped(*args, **kwargs)
  File "/base/data/home/apps/e~<my-project>/20180124t154459.407164278206739455/lib/googleapiclient/http.py", line 844, in execute
raise HttpError(resp, content, uri=self.uri)
HttpError: <HttpError 400 when requesting https://www.googleapis.com/gmail/v1/users/<email-I-need-to-watch>/watch?alt=json returned "Bad Request">

In regards to the authentication and authorization, here is what I have done so far:

  1. I've created a Pub/Sub topic and this is the one I'm passing into the watch() request
  2. I use G-Suite and the email inbox I intend to watch is part of my G-Suite business domain.
  3. For this task I use a service account with enabled G-Suite Domain-wide Delegation. I've downloaded the .json service account file which I'm supplying in order to acquire the oauth2client.service_account.Credentials object (I see the access and refresh tokens being exchanged successfully in the logs). The json service file is placed in the same folder as my main.py script (root of my project).
  4. In my G-Suite administration panel I've enabled the api access to the service account from 2. with the scope of https://www.googleapis.com/auth/gmail.modify. I'm using the gmail.modify access level as I intend to read, write and send both emails and drafts.

Is there something I'm missing out in my code or in the authentication and authorization steps?

Upvotes: 2

Views: 683

Answers (1)

Bernard Halas
Bernard Halas

Reputation: 1190

Problem solved. I was missing the part of the code for impersonating a user from my domain in order to read his/her mailbox (as explained here).

The corrected code looks like this:

import googleapiclient.discovery
from google.oauth2 import service_account

SCOPES = ['https://www.googleapis.com/auth/gmail.modify']

credentials = service_account.Credentials.from_service_account_file(
    SERVICE_ACCOUNT_FILE, scopes=SCOPES
)
credentials = credentials.with_subject('<email-I-need-to-watch>')

gmail = googleapiclient.discovery.build('gmail', 'v1', credentials=credentials)

watchRequest = {
    'labelIds' : ['INBOX'],
    'topicName' : 'projects/<my-project>/topics/<my-topic>'
}

gmail.users().watch(userId='me', body=watchRequest).execute()

Upvotes: 3

Related Questions