Web APIs Mashup
in a Python Application

Johan Euphrosine @proppy
June 21, 2011

proppy-ep2011.appspot.com

About me

proppy-ep2011.appspot.com

Agenda

proppy-ep2011.appspot.com

How to call Web APIs in Python ?

proppy-ep2011.appspot.com

Anonymous access

proppy-ep2011.appspot.com

Google URL Shortener

proppy-ep2011.appspot.com

Google URL Shortener API

POST /urlshortener/v1/url HTTP/1.1
Content-Type: application/json

{
  "longUrl": "http://www.verylongurl.com/"
}
HTTP/1.1 200 OK

{
  "kind": "urlshortener#url",
  "id": "http://goo.gl/fbsS",
  "longUrl": "http://www.verylongurl.com/"
}
proppy-ep2011.appspot.com

Using httplib2

>>> import httplib2, json
>>> http = httplib2.Http()
>>> url = "https://www.googleapis.com/urlshortener/v1/url"
>>> response, content = http.request(url, "POST", body=json.dumps({
... "longUrl": "http://www.verylongurl.com/"
... }), headers={"content-type":"application/json"})
>>> json.loads(content)["id"]
u'http://goo.gl/bxJ6O'
proppy-ep2011.appspot.com

w/ API key

proppy-ep2011.appspot.com

URL Shortener API

POST /urlshortener/v1/url?key=xyz HTTP/1.1
Content-Type: application/json

{
  "longUrl": "http://www.verylongurl.com/"
}
HTTP/1.1 200 OK

{
  "kind": "urlshortener#url",
  "id": "http://goo.gl/fbsS",
  "longUrl": "http://www.verylongurl.com/"
}
proppy-ep2011.appspot.com

Using httplib2

>>> import httplib2, json
>>> http = httplib2.Http()
>>> url = "https://www.googleapis.com/urlshortener/v1/url"
>>> url += "?key=xyz"
>>> response, content = http.request(url, "POST", body=json.dumps({
... "longUrl": "http://www.verylongurl.com/"
... }), headers={"content-type":"application/json"})
>>> json.loads(content)["id"]
u'http://goo.gl/bxJ6O'
proppy-ep2011.appspot.com

w/ OAuth 2.0

proppy-ep2011.appspot.com

OAuth 2.0 Roles

proppy-ep2011.appspot.com

OAuth 2.0 Dance

     +--------+                               +---------------+
     |        |--(A)- Authorization Request ->|   Resource    |
     |        |                               |     Owner     |
     |        |<-(B)-- Authorization Grant ---|               |
     |        |                               +---------------+
     |        |
     |        |        Authorization Grant &  +---------------+
     |        |--(C)--- Client Credentials -->| Authorization |
     | Client |                               |     Server    |
     |        |<-(D)----- Access Token -------|               |
     |        |                               +---------------+
     |        |
     |        |                               +---------------+
     |        |--(E)----- Access Token ------>|    Resource   |
     |        |                               |     Server    |
     |        |<-(F)--- Protected Resource ---|               |
     +--------+                               +---------------+	  
	
proppy-ep2011.appspot.com

Get Authorization Code

REDIRECT https://accounts.google.com/o/oauth2/auth?
client_id=926365050982.apps.googleusercontent.com&
redirect_uri=https://appid.appspot.com/back&
scope=https://www.googleapis.com/auth/urlshortener&
response_type=code
proppy-ep2011.appspot.com

Back to your application

if the User approved access:

https://appid.appspot.com/back?code=4/P7q8W92a-oMsCeLvIaQm6bTrgtp7
proppy-ep2011.appspot.com

Swap Code for Access Token

POST https://accounts.google.com/o/oauth2/token HTTP/1.1

code=4/P7q8W92a-oMsCeLvIaQm6bTrgtp7&
client_id=21302922996.apps.googleusercontent.com&
client_secret=XTHhXh1SlUNgvyWGwDk1EjXB&
redirect_uri=https://www.example.com/back&
grant_type=authorization_code
HTTP/1.1 200 OK

{
  "access_token": "1/fFAGRNJru1FTz70BzhT3Zg",
  "expires_in": 3920,
  "refresh_token": "1/6BMfW9j53gdGImsixUH6kU5RsR4zwI9lUVX-tqf8JXQ"
}
proppy-ep2011.appspot.com

Use Access Token

GET https://www.googleapis.com/urlshortener/v1/url/history
Authorization: OAuth 1/fFAGRNJru1FTz70BzhT3Zg
HTTP/1.1 200 OK

{
 "totalItems": 72,
 "itemsPerPage": 30,
 "nextPageToken": "2011-06-15T06:59:22.584+00:00",
 "items": [
  {
   "kind": "urlshortener#url",
   "id": "http://goo.gl/fbsS",
   "longUrl": "http://www.verylongurl.com/",
   "status": "OK",
   "created": "2010-12-13T07:22:55.000+00:00",
  } /* , ... */
 ]
}
proppy-ep2011.appspot.com

Using webapp

application = webapp.WSGIApplication([
        ('/', MainHandler),
        ('/oauth2callback', OAuth2Handler)
        ])

def main():
    util.run_wsgi_app(application)
proppy-ep2011.appspot.com

Using webapp

CLIENT_ID = '926365050982.apps.googleusercontent.com'
CLIENT_SECRET = 'SnV9pONw92NBDFYMhkYhAKTJ'
SCOPE = 'https://www.googleapis.com/auth/urlshortener'

class MainHandler(webapp.RequestHandler):
    def get(self):
      args = {
          'client_id': CLIENT_ID,
          'redirect_uri': self.request.host_url+'/oauth2callback',
          'scope': SCOPE,
          'response_type': 'code'
          }
      authorize_url = ('https://accounts.google.com/o/oauth2/auth?'
                       + urllib.urlencode(args))
      self.redirect(authorize_url)
proppy-ep2011.appspot.com

Using webapp

class OAuth2Handler(webapp.RequestHandler):
    def get(self):
        code = self.request.get('code')
        args = {
          'grant_type': 'authorization_code',
          'client_id': CLIENT_ID,
          'client_secret': CLIENT_SECRET,
          'code': code,
          'redirect_uri': self.request.uri,
          'scope': SCOPE
          }
        token_url = 'https://accounts.google.com/o/oauth2/token'
        headers = {
          'user-agent': 'app-id/1.0',
          'content-type': 'application/x-www-form-urlencoded'
        }
        response, content = http.request(token_url, 'POST',
                                         body=urllib.urlencode(args),
                                         headers=headers)
        access_token = json.loads(content)['access_token']
proppy-ep2011.appspot.com

Using webapp

        api_url = 'https://www.googleapis.com/urlshortener/v1/url/history'
        response, content = http.request(api_url, headers={
                'authorization': 'OAuth ' + access_token
                })
        self.response.out.write(content)
proppy-ep2011.appspot.com

Using google-api-python-client

from oauth2client.appengine import OAuth2Decorator
from apiclient.discovery import build

decorator = OAuth2Decorator(
  client_id='926365050982.apps.googleusercontent.com',
  client_secret='SnV9pONw92NBDFYMhkYhAKTJ',
  scope='https://www.googleapis.com/auth/urlshortener',
  user_agent='app-id/1.0')

class MainHandler(webapp.RequestHandler):
  @decorator.oauth_required
  def get(self):
    http = decorator.http()
    service = build("urlshortener", "v1", http=http)
    self.request.out.write(service.url().list().execute()['items'])
	  
proppy-ep2011.appspot.com

More APIs

proppy-ep2011.appspot.com

REST APIs

ResourceURIGETPUTPOSTDELETE
Collection/resources/ListReplaceCreateDelete
Element/resources/idRetrieveUpdateCreateDelete
proppy-ep2011.appspot.com

Google Buzz API Resources

proppy-ep2011.appspot.com

Post an Activity

POST /buzz/v1/activities/@me/@self HTTP/1.1
Authorization: 1/fFAGRNJru1FTz70BzhT3Zg
Content-Type: application/json

{
  "data": {
    "object": {
      "type": "note",
      "content": "Is anybody listening ?"
    }
  }
}
proppy-ep2011.appspot.com

Get your followers

GET /buzz/v1/people/@me/@groups/@followers HTTP/1.1
HTTP/1.1 200 OK

{
  "data": {
    "kind": "buzz#peopleFeed",
    "entry": []
  }
}
proppy-ep2011.appspot.com

Mashups

proppy-ep2011.appspot.com

Multiple APIs, Same Auth Server

proppy-ep2011.appspot.com

2 Scopes, 1 Access Code

REDIRECT https://accounts.google.com/o/oauth2/auth?
client_id=926365050982.apps.googleusercontent.com&
redirect_uri=https://appid.appspot.com/back&
scope=https://www.googleapis.com/auth/urlshortener%20\
https://www.googleapis.com/auth/buzz&
response_type=code
proppy-ep2011.appspot.com

Back to your application

if the User approved access:

https://appid.appspot.com/back?code=4/P7q8W92a-oMsCeLvIaQm6bTrgtp7
proppy-ep2011.appspot.com

Using google-api-python-client

decorator = OAuth2Decorator(
  client_id='531740438494.apps.googleusercontent.com',
  client_secret='GAo_rMg0oZsJrcdOY-PFbSaC',
  scope=('https://www.googleapis.com/auth/urlshortener '
         'https://www.googleapis.com/auth/buzz'),
  user_agent='app-id/1.0')
proppy-ep2011.appspot.com

Using google-api-python-client

class MainHandler(webapp.RequestHandler):
  @decorator.oauth_required
  def get(self):
    long_url = unquote(self.request.get('url'))
    http = decorator.http()
    urlshortener = build('urlshortener', 'v1', http=http)
    url = urlshortener.url()
    short_url = url.insert(body={"longUrl": long_url}).execute()['id']
    soup = BeautifulStoneSoup(urlopen(long_url),
                              convertEntities=BeautifulStoneSoup.ALL_ENTITIES)
    title = soup.html.head.title.string
    activity = {
      'title': title,
      'object': {
         'type': 'note',
         'content': '%s: %s' % (title, short_url)
         }
      }
    buzz = build("buzz", "v1", http=http)
    activities = buzz.activities()
    activity = activities.insert(userId='@me', body=activity).execute()
    self.redirect(activity['links']['alternate'][0]['href'])
proppy-ep2011.appspot.com

Mashup in Action

GET /demo?url=http://ep2011.europython.eu/conference/talks/web-api...
proppy-ep2011.appspot.com

Summary

proppy-ep2011.appspot.com

Questions ?

Feedback on @proppy / https://profiles.google.com/proppy

proppy-ep2011.appspot.com