Search code examples
pythongoogle-app-engineflaskgdatagoogle-app-engine-python

How to authorise a gdata contacts client in python


I'm trying to use Google Contacts API to add a entry to the user's contacts. I've managed to use OAuth2 and get access token and make an authorised request to Google Contacts API. But I did it manually, a simple GET to https://www.google.com/m8/feeds/contacts/default/full. I want to use gdata so I don't have to manually create the xml.

The problem is I cannot authorise the gdata's ContactsClient.

From the doc:

gd_client = gdata.contacts.client.ContactsClient(source='YOUR_APPLICATION_NAME')
# Authorize the client.

But how do I authorise it? The example uses email ans password to create an authorised ContactsClient

self.gd_client = gdata.contacts.client.ContactsClient(source='GoogleInc-ContactsPythonSample-1')
self.gd_client.ClientLogin(email, password, self.gd_client.source)

And so far I haven't found how to authorise the gdata client. I did found that I can pass a gdata.gauth.ClientLoginToken to gdata.contacts.client.ContactsClient, but it didn't work as well

auth_token = gdata.gauth.ClientLoginToken(credentials.access_token)
gd_client = gdata.contacts.client.ContactsClient(
     source='project-id',
     auth_token=auth_token)
contact = create_contact(gd_client)

I get a 401 - Unauthorized "There was an error in your request. That's all we know"

This create_contact comes from the doc


Solution

  • Using example @dyerad indicated and the spinet for create contacts on the Google Contacts API doc I created the example code below.

    I just had to fix a wee error on the create_contact snippet I took from the doc. You should use

        # Set the contact's postal address.
        new_contact.structured_postal_address.append(gdata.data.StructuredPostalAddress(
            rel=gdata.data.WORK_REL, primary='true',
            street=gdata.data.Street(text='1600 Amphitheatre Pkwy'),
            city=gdata.data.City(text='Mountain View'),
            region=gdata.data.Region(text='CA'),
            postcode=gdata.data.Postcode(text='94043'),
            country=gdata.data.Country(text='United States')))
    

    instead of

    # Set the contact's postal address.
    new_contact.structured_postal_address.append(
      rel=gdata.data.WORK_REL, primary='true',
      street=gdata.data.Street(text='1600 Amphitheatre Pkwy'),
      city=gdata.data.City(text='Mountain View'),
      region=gdata.data.Region(text='CA'),
      postcode=gdata.data.Postcode(text='94043'),
      country=gdata.data.Country(text='United States'))
    

    Here is the full code:

    import flask
    import httplib2
    from oauth2client import client
    from logging import info as linfo
    import atom.data
    import gdata.gauth
    import gdata.data
    import gdata.contacts.client
    import gdata.contacts.data
    
    app = flask.Flask(__name__)
    
    client_info = {
        "client_id": "<CLIENT_ID>",
        "client_secret": "<CONTACTS_CLIENT_SECRET>",
        "redirect_uris": [],
        "auth_uri": "https://accounts.google.com/o/oauth2/auth",
        "token_uri": "https://accounts.google.com/o/oauth2/token"
    }
    
    scopes = 'https://www.google.com/m8/feeds/'
    
    
    def create_contact(gd_client):
        new_contact = gdata.contacts.data.ContactEntry()
        # Set the contact's name.
        new_contact.name = gdata.data.Name(
            given_name=gdata.data.GivenName(text='Elizabeth'),
            family_name=gdata.data.FamilyName(text='Bennet'),
            full_name=gdata.data.FullName(text='Elizabeth Bennet'))
        new_contact.content = atom.data.Content(text='Notes')
        # Set the contact's email addresses.
        new_contact.email.append(gdata.data.Email(address='liz@gmail.com',
                                                  primary='true',
                                                  rel=gdata.data.WORK_REL,
                                                  display_name='E. Bennet'))
        new_contact.email.append(gdata.data.Email(address='liz@example.com',
                                                  rel=gdata.data.HOME_REL))
        # Set the contact's phone numbers.
        new_contact.phone_number.append(gdata.data.PhoneNumber(text='(206)555-1212',
                                                               rel=gdata.data.WORK_REL,
                                                               primary='true'))
        new_contact.phone_number.append(gdata.data.PhoneNumber(text='(206)555-1213',
                                                               rel=gdata.data.HOME_REL))
        # Set the contact's IM address.
        new_contact.im.append(gdata.data.Im(address='liz@gmail.com',
                                            primary='true', rel=gdata.data.HOME_REL,
                                            protocol=gdata.data.GOOGLE_TALK_PROTOCOL))
        # Set the contact's postal address.
        new_contact.structured_postal_address.append(gdata.data.StructuredPostalAddress(
            rel=gdata.data.WORK_REL, primary='true',
            street=gdata.data.Street(text='1600 Amphitheatre Pkwy'),
            city=gdata.data.City(text='Mountain View'),
            region=gdata.data.Region(text='CA'),
            postcode=gdata.data.Postcode(text='94043'),
            country=gdata.data.Country(text='United States')))
        # Send the contact data to the server.
        contact_entry = gd_client.CreateContact(new_contact)
        linfo('Contact\'s ID: %s' % contact_entry.id.text)
        return contact_entry
    
    
    @app.route('/gdata/oauth')
    def gdata_oauth():
        request_token = gdata.gauth.OAuth2Token(
            client_id=client_info['client_id'],
            client_secret=client_info['client_secret'],
            scope=scopes,
            user_agent=None)
    
        return flask.redirect(
            request_token.generate_authorize_url(
                redirect_uri=flask.url_for('gdata_oauth2callback', _external=True)))
    
    
    @app.route('/gdata/oauth2callback')
    def gdata_oauth2callback():
        if 'error' in flask.request.args:
            return str(flask.request.args.get('error'))
        elif 'code' not in flask.request.args:
            return flask.redirect(flask.url_for('gdata_oauth'))
        else:
            request_token = gdata.gauth.OAuth2Token(
                client_id=client_info['client_id'],
                client_secret=client_info['client_secret'],
                scope=scopes,
                user_agent=None)
            request_token.redirect_uri =\
                flask.url_for('gdata_oauth2callback', _external=True)
            request_token.get_access_token(flask.request.args.get('code'))
            gd_client = gdata.contacts.client.ContactsClient(
                source='project-id',
                auth_token=request_token)
            contact = create_contact(gd_client)
            return str(contact)