Search code examples
pythongoogle-app-enginegoogle-apigmailgoogle-api-client

How to use deferred.defer with oauth2 in google app engine?


I have decorator:

http = httplib2.Http()
service = discovery.build('gmail', 'v1', http=http)
# Creating decorator for OAuth2 account.
decorator = appengine.oauth2decorator_from_clientsecrets(
    CLIENT_SECRETS,
    scope='https://www.googleapis.com/auth/gmail.readonly',
    message=MISSING_CLIENT_SECRETS_MESSAGE)

And class that use it:

class CSVGeneratorHandler(webapp2.RedirectHandler):
    @decorator.oauth_required
    def get(self):
        http = decorator.http()
        first_messages = service.users().messages().list(userId='me').execute(http=http)
        template = JINJA_ENVIRONMENT.get_template('templates/success.html')
        self.response.write(template.render({}))

That works great.

But when I try to move API request service.users().messages().list(userId='me').execute(http=http) to the task (with deferred.defer(get_mails)) I got an error: "Login required".

I understand that get_mails function must to receive context or/and credentials from the main program. But I don't understand how to do that.

Can someone help me?


Solution

  • After few hours I found answer.

    First of all you need to save credentials to some place. Because we you GAE we have especial field type for it:

    from oauth2client.appengine import StorageByKeyName  
    from google.appengine.ext import db
    
    class CredentialsModel(db.Model):
      credentials = CredentialsProperty()
    

    Next step is to set up auth without decorator and only after that send task to queue:

        user = users.get_current_user()
        flow = flow_from_clientsecrets(os.path.join(os.path.dirname(os.path.realpath(__file__)), CLIENT_SECRETS),
                               scope='https://www.googleapis.com/auth/gmail.readonly',
                               redirect_uri='http://127.0.0.1:8080/success')
        auth_uri = str(flow.step1_get_authorize_url())
        code = self.request.get('code')
        if not code:
            return self.redirect(auth_uri)
        credentials = flow.step2_exchange(code)
    
        storage = StorageByKeyName(CredentialsModel, user.user_id(), 'credentials')
        storage.put(credentials)
    
        deferred.defer(get_mails, user.user_id())
    

    Do not forget about getting credentials in deferred function!

    def get_mails(user_id):
        storage = StorageByKeyName(CredentialsModel, user_id, 'credentials')
        credentials = storage.get()
    
        http = httplib2.Http()
        http = credentials.authorize(http)
    
        all_messages = []
        service = discovery.build('gmail', 'v1', http=http)
        first_messages = service.users().messages().list(userId='me').execute()