merge
diff --git a/README b/README
index ef2c067..43e4611 100644
--- a/README
+++ b/README
@@ -15,18 +15,19 @@
=======
First run three-legged-dance.py to get OAuth
-tokens for Buzz, which will be stored in a file.
+tokens for Buzz, which will be stored in
+buzz.dat in the working directory.
- $ python three-legged-dance.py
+ $ python samples/cmdline/three_legged_dance.py
-Then run sample.py, which will use the apiclient
+Then run buzz.py, which will use the apiclient
library to retrieve the title of the most
-recent entry in Buzz.
+recent entry in Buzz and post a test message.
- $ python sample.py
+ $ python samples/cmdline/buzz.py
-Third Pary Libraries
+Third Party Libraries
====================
http://code.google.com/p/httplib2
diff --git a/TODO b/TODO
index e4785c6..c88756b 100644
--- a/TODO
+++ b/TODO
@@ -11,12 +11,9 @@
- Caching of discovery doc
- - Add in 'Extra Discovery' for pagination
- Layered user-agent header, ala 'my-buzz-client/1.0 google-api-python-client/0.2 httplib2/0.6'
- - Implement requests as Command objects, either for immediate
- execution, or for batching.
- Requests for multiple APIs at one time.
@@ -24,3 +21,10 @@
+DONE
+====
+
+ - Implement requests as Command objects, either for immediate
+ execution, or for batching.
+
+ - Add in 'Extra Discovery' for pagination
\ No newline at end of file
diff --git a/apiclient/contrib/buzz/future.json b/apiclient/contrib/buzz/future.json
index 88abf31..ed6f0f9 100644
--- a/apiclient/contrib/buzz/future.json
+++ b/apiclient/contrib/buzz/future.json
@@ -112,7 +112,12 @@
"methods": {
"delete": {},
"get": {},
- "liked": {},
+ "liked": {
+ "next": {
+ "type": "uri",
+ "location": ["links", "next", 0, "href"]
+ }
+ },
"list": {},
"relatedToUri": {},
"reshared": {},
diff --git a/apiclient/contrib/moderator/future.json b/apiclient/contrib/moderator/future.json
index 7e3d978..87d525b 100644
--- a/apiclient/contrib/moderator/future.json
+++ b/apiclient/contrib/moderator/future.json
@@ -2,7 +2,7 @@
"data": {
"moderator": {
"v1": {
- "baseUrl": "https://www.googleapis.com/",
+ "baseUrl": "https://www.googleapis.com/",
"auth": {
"request": {
"url": "https://www.google.com/accounts/OAuthGetRequestToken",
diff --git a/apiclient/discovery.py b/apiclient/discovery.py
index bb5c305..945db74 100644
--- a/apiclient/discovery.py
+++ b/apiclient/discovery.py
@@ -28,6 +28,10 @@
import uritemplate
import urllib
import urlparse
+try:
+ from urlparse import parse_qsl
+except ImportError:
+ from cgi import parse_qsl
from apiclient.http import HttpRequest
try: # pragma: no cover
@@ -41,13 +45,21 @@
import json as simplejson
-class HttpError(Exception):
+class Error(Exception):
+ """Base error for this module."""
pass
-class UnknownLinkType(Exception):
+class HttpError(Error):
+ """HTTP data was invalid or unexpected."""
pass
+
+class UnknownLinkType(Error):
+ """Link type unknown or unexpected."""
+ pass
+
+
DISCOVERY_URI = ('http://www.googleapis.com/discovery/0.1/describe'
'{?api,apiVersion}')
@@ -82,12 +94,15 @@
if body_value is None:
return (headers, path_params, query, None)
else:
- model = {'data': body_value}
+ if len(body_value) == 1 and 'data' in body_value:
+ model = body_value
+ else:
+ model = {'data': body_value}
headers['content-type'] = 'application/json'
return (headers, path_params, query, simplejson.dumps(model))
def build_query(self, params):
- params.update({'alt': 'json', 'prettyprint': 'true'})
+ params.update({'alt': 'json'})
astuples = []
for key, value in params.iteritems():
if getattr(value, 'encode', False) and callable(value.encode):
@@ -99,6 +114,9 @@
# Error handling is TBD, for example, do we retry
# for some operation/error combinations?
if resp.status < 300:
+ if resp.status == 204:
+ # A 204: No Content response should be treated differently to all the other success states
+ return simplejson.loads('{}')
return simplejson.loads(content)['data']
else:
logging.debug('Content from bad request was: %s' % content)
@@ -148,7 +166,7 @@
def createMethod(theclass, methodName, methodDesc, futureDesc):
- def method(self, **kwargs):
+ def method(self):
return createResource(self._http, self._baseUrl, self._model,
methodName, self._developerKey, methodDesc, futureDesc)
@@ -232,8 +250,14 @@
headers, params, query, body = self._model.request(headers,
actual_path_params, actual_query_params, body_value)
+ # TODO(ade) This exists to fix a bug in V1 of the Buzz discovery document.
+ # Base URLs should not contain any path elements. If they do then urlparse.urljoin will strip them out
+ # This results in an incorrect URL which returns a 404
+ url_result = urlparse.urlsplit(self._baseUrl)
+ new_base_url = url_result.scheme + '://' + url_result.netloc
+
expanded_url = uritemplate.expand(pathUrl, params)
- url = urlparse.urljoin(self._baseUrl, expanded_url + query)
+ url = urlparse.urljoin(new_base_url, url_result.path + expanded_url + query)
logging.info('URL being requested: %s' % url)
return HttpRequest(self._http, url, method=httpMethod, body=body,
@@ -273,7 +297,7 @@
if self._developerKey:
parsed = list(urlparse.urlparse(url))
- q = urlparse.parse_qsl(parsed[4])
+ q = parse_qsl(parsed[4])
q.append(('key', self._developerKey))
parsed[4] = urllib.urlencode(q)
url = urlparse.urlunparse(parsed)
diff --git a/apiclient/oauth.py b/apiclient/oauth.py
index 8b827c6..9907c46 100644
--- a/apiclient/oauth.py
+++ b/apiclient/oauth.py
@@ -21,7 +21,17 @@
from cgi import parse_qs, parse_qsl
-class MissingParameter(Exception):
+class Error(Exception):
+ """Base error for this module."""
+ pass
+
+
+class RequestError(Error):
+ """Error occurred during request."""
+ pass
+
+
+class MissingParameter(Error):
pass
@@ -120,8 +130,10 @@
if headers == None:
headers = {}
headers.update(req.to_header())
- if 'user-agent' not in headers:
- headers['user-agent'] = self.user_agent
+ if 'user-agent' in headers:
+ headers['user-agent'] = self.user_agent + ' ' + headers['user-agent']
+ else:
+ headers['user-agent'] = self.user_agent
return request_orig(uri, method, body, headers,
redirections, connection_type)
@@ -185,7 +197,7 @@
body=body)
if resp['status'] != '200':
logging.error('Failed to retrieve temporary authorization: %s' % content)
- raise Exception('Invalid response %s.' % resp['status'])
+ raise RequestError('Invalid response %s.' % resp['status'])
self.request_token = dict(parse_qsl(content))
@@ -222,7 +234,7 @@
resp, content = client.request(uri, 'POST', headers=headers)
if resp['status'] != '200':
logging.error('Failed to retrieve access token: %s' % content)
- raise Exception('Invalid response %s.' % resp['status'])
+ raise RequestError('Invalid response %s.' % resp['status'])
oauth_params = dict(parse_qsl(content))
token = oauth.Token(
diff --git a/buzz_gae_client.py b/buzz_gae_client.py
index 544655e..f790e02 100644
--- a/buzz_gae_client.py
+++ b/buzz_gae_client.py
@@ -36,6 +36,18 @@
AUTHORIZE_URL = 'https://www.google.com/buzz/api/auth/OAuthAuthorizeToken?domain=anonymous&scope=https://www.googleapis.com/auth/buzz'
ACCESS_TOKEN_URL = 'https://www.google.com/accounts/OAuthGetAccessToken'
+
+class Error(Exception):
+ """Base error for this module."""
+ pass
+
+
+class RequestError(Error):
+ """Request returned failure or unexpected data."""
+ pass
+
+
+# TODO(ade) This class is really a BuzzGaeBuilder. Rename it.
class BuzzGaeClient(object):
def __init__(self, consumer_key='anonymous', consumer_secret='anonymous'):
self.consumer = oauth.Consumer(consumer_key, consumer_secret)
@@ -48,7 +60,7 @@
if resp['status'] != '200':
logging.warn('Request: %s failed with status: %s. Content was: %s' % (url, resp['status'], content))
- raise Exception('Invalid response %s.' % resp['status'])
+ raise RequestError('Invalid response %s.' % resp['status'])
return resp, content
def get_request_token(self, callback_url, display_name = None):
diff --git a/functional_tests/data/buzz_credentials.dat b/functional_tests/data/buzz_credentials.dat
new file mode 100644
index 0000000..91613e4
--- /dev/null
+++ b/functional_tests/data/buzz_credentials.dat
@@ -0,0 +1,50 @@
+ccopy_reg
+_reconstructor
+p0
+(capiclient.oauth
+OAuthCredentials
+p1
+c__builtin__
+object
+p2
+Ntp3
+Rp4
+(dp5
+S'token'
+p6
+g0
+(coauth2
+Token
+p7
+g2
+Ntp8
+Rp9
+(dp10
+S'secret'
+p11
+S'tsAddOKXxs7dypwkRTCSAvPV'
+p12
+sS'key'
+p13
+S'1/Krdi6C3n47g2ny13MdnZbj7AS77JdgaMVg2fDY7ZmwI'
+p14
+sbsS'consumer'
+p15
+g0
+(coauth2
+Consumer
+p16
+g2
+Ntp17
+Rp18
+(dp19
+g11
+S'anonymous'
+p20
+sg13
+g20
+sbsS'user_agent'
+p21
+S'google-api-client-python-buzz-cmdline/1.0'
+p22
+sb.
\ No newline at end of file
diff --git a/functional_tests/test_services.py b/functional_tests/test_services.py
index faab6cf..50a1a4d 100644
--- a/functional_tests/test_services.py
+++ b/functional_tests/test_services.py
@@ -9,39 +9,239 @@
These tests are read-only in order to ensure they're repeatable. They also
only work with publicly visible data in order to avoid dealing with OAuth.
"""
+import httplib2
+import pprint
__author__ = 'ade@google.com (Ade Oshineye)'
from apiclient.discovery import build
+import httplib2
import logging
+import pickle
+import os
import unittest
+# TODO(ade) Remove this mock once the bug in the discovery document is fixed
+DATA_DIR = os.path.join(logging.os.path.dirname(__file__), '../tests/data')
+class HttpMock(object):
+
+ def __init__(self, filename, headers):
+ f = file(os.path.join(DATA_DIR, filename), 'r')
+ self.data = f.read()
+ f.close()
+ self.headers = headers
+
+ def request(self, uri, method="GET", body=None, headers=None, redirections=1, connection_type=None):
+ return httplib2.Response(self.headers), self.data
+
class BuzzFunctionalTest(unittest.TestCase):
+ def test_can_get_specific_activity(self):
+ buzz = build('buzz', 'v1')
+ activity = buzz.activities().get(userId='105037104815911535953',
+ postId='B:z12sspviqyakfvye123wehng0muwz5jzq04').execute()
+
+ self.assertTrue(activity is not None)
+
+ def test_can_get_specific_activity_with_tag_id(self):
+ buzz = build('buzz', 'v1')
+ activity = buzz.activities().get(userId='105037104815911535953',
+ postId='tag:google.com,2010:buzz:z13ptnw5usmnv15ey22fzlswnuqoebasu').execute()
+
+ self.assertTrue(activity is not None)
+
def test_can_get_buzz_activities_with_many_params(self):
buzz = build('buzz', 'v1')
max_results = 2
- actcol = buzz.activities()
- activities = actcol.list(userId='googlebuzz', scope='@self',
+ activities_command = buzz.activities()
+ activities = activities_command.list(userId='googlebuzz', scope='@self',
max_comments=max_results*2 ,max_liked=max_results*3,
max_results=max_results).execute()
activity_count = len(activities['items'])
self.assertEquals(max_results, activity_count)
- activities = actcol.list_next(activities).execute()
+ activities = activities_command.list_next(activities).execute()
activity_count = len(activities['items'])
self.assertEquals(max_results, activity_count)
- def test_can_page_through_users_activities(self):
+ def test_can_get_multiple_pages_of_buzz_activities(self):
buzz = build('buzz', 'v1')
max_results = 2
- actcol = buzz.activities()
+ activities_command = buzz.activities()
- activities = actcol.list(userId='adewale', scope='@self',
+ activities = activities_command.list(userId='adewale', scope='@self',
max_results=max_results).execute()
for count in range(10):
- activities = actcol.list_next(activities).execute()
+ activities = activities_command.list_next(activities).execute()
activity_count = len(activities['items'])
self.assertEquals(max_results, activity_count, 'Failed after %s pages' % str(count))
+ def IGNORE__test_can_get_multiple_pages_of_buzz_likers(self):
+ # Ignore this test until the Buzz API fixes the bug with next links
+ # http://code.google.com/p/google-buzz-api/issues/detail?id=114
+ self.http = HttpMock('buzz.json', {'status': '200'})
+ buzz = build('buzz', 'v1', self.http)
+ max_results = 1
+ people_cmd = buzz.people()
+ #https://www.googleapis.com/buzz/v1/activities/111062888259659218284/@self/B:z13nh535yk2syfob004cdjyb3mjeulcwv3c?alt=json#
+ people = people_cmd.liked(groupId='@liked', userId='googlebuzz', scope='@self',
+ postId='B:z13nh535yk2syfob004cdjyb3mjeulcwv3c', max_results=max_results).execute()
+
+ for count in range(10):
+ people = people_cmd.liked_next(people).execute()
+ people_count = len(people['items'])
+ self.assertEquals(max_results, people_count, 'Failed after %s pages' % str(count))
+
+ def test_can_get_user_profile(self):
+ buzz = build('buzz', 'v1')
+ person = buzz.people().get(userId='googlebuzz').execute()
+
+ self.assertTrue(person is not None)
+ self.assertEquals('buzz#person', person['kind'])
+ self.assertEquals('Google Buzz Team', person['displayName'])
+ self.assertEquals('111062888259659218284', person['id'])
+ self.assertEquals('http://www.google.com/profiles/googlebuzz', person['profileUrl'])
+
+ def test_can_get_followees_of_user(self):
+ buzz = build('buzz', 'v1')
+ expected_followees = 30
+ following = buzz.people().list(userId='googlebuzz', groupId='@following', max_results=expected_followees).execute()
+
+ self.assertEquals(expected_followees, following['totalResults'])
+ self.assertEquals(expected_followees, len(following['entry']))
+
+ def test_can_efficiently_get_follower_count_of_user(self):
+ buzz = build('buzz', 'v1')
+
+ # Restricting max_results to 1 means only a tiny amount of data comes back but the totalResults still has the total.
+ following = buzz.people().list(userId='googlebuzz', groupId='@followers', max_results=1).execute()
+
+ # @googlebuzz has a large but fluctuating number of followers
+ # It is sufficient if the result is bigger than 10, 000
+ follower_count = following['totalResults']
+ self.assertTrue(follower_count > 10000, follower_count)
+
+ def test_follower_count_is_zero_for_user_with_hidden_follower_count(self):
+ buzz = build('buzz', 'v1')
+ following = buzz.people().list(userId='adewale', groupId='@followers').execute()
+
+ self.assertEquals(0, following['totalResults'])
+
+
+class BuzzAuthenticatedFunctionalTest(unittest.TestCase):
+ def __init__(self, method_name):
+ unittest.TestCase.__init__(self, method_name)
+ credentials_dir = os.path.join(logging.os.path.dirname(__file__), './data')
+ f = file(os.path.join(credentials_dir, 'buzz_credentials.dat'), 'r')
+ credentials = pickle.loads(f.read())
+ f.close()
+
+ self.http = credentials.authorize(httplib2.Http())
+
+ def test_can_create_activity(self):
+ buzz = build('buzz', 'v1', http=self.http)
+
+ activity = buzz.activities().insert(userId='@me', body={
+ 'title': 'Testing insert',
+ 'object': {
+ 'content': u'Just a short note to show that insert is working. ?',
+ 'type': 'note'}
+ }
+ ).execute()
+ self.assertTrue(activity is not None)
+
+ def test_can_create_private_activity(self):
+ buzz = build('buzz', 'v1', http=self.http)
+
+ activity = buzz.activities().insert(userId='@me', body={
+ 'title': 'Testing insert',
+ 'object': {
+ 'content': 'This is a private post.'
+ },
+ 'visibility': {
+ 'entries': [
+ { 'id': 'tag:google.com,2010:buzz-group:108242092577082601423:13' }
+ ]
+ }
+ }
+ ).execute()
+ self.assertTrue(activity is not None)
+
+ def test_can_identify_number_of_groups_belonging_to_user(self):
+ buzz = build('buzz', 'v1', http=self.http)
+ groups = buzz.groups().list(userId='108242092577082601423').execute()
+
+ # This should work as long as no-one edits the groups for this test account
+ expected_default_number_of_groups = 4
+ self.assertEquals(expected_default_number_of_groups, len(groups['items']))
+
+ def IGNORE__test_can_like_activity(self):
+ buzz = build('buzz', 'v1', http=self.http)
+ activity = buzz.activities().insert(userId='@me', body={
+ 'title': 'Testing insert',
+ 'object': {
+ 'content': u'Just a short note to show that insert is working. ?',
+ 'type': 'note'}
+ }
+ ).execute()
+ pprint.pprint(activity)
+ id = activity['id']
+ likers = buzz.people().liked(userId='105037104815911535953', postId=id, groupId='@liked', scope='@self').execute()
+ # Todo(ade) Insert the new liker once the Buzz back-end bug is fixed
+
+ def test_can_comment_on_activity(self):
+ buzz = build('buzz', 'v1', http=self.http)
+
+ activity = buzz.activities().insert(userId='@me', body={
+ 'title': 'A new activity',
+ 'object': {
+ 'content': u'The body of the new activity',
+ 'type': 'note'}
+ }
+ ).execute()
+
+ id = activity['id']
+ comment = buzz.comments().insert(userId='@me', postId=id, body={
+ "content": "A comment on the new activity"
+ }).execute()
+
+ def IGNORE__test_can_list_groups_belonging_to_user(self):
+ # TODO(ade) Uncomment this test once the related Buzz back-end bug is fixed
+ buzz = build('buzz', 'v1', http=self.http)
+ groups = buzz.groups().list(userId='108242092577082601423').execute()
+ pprint.pprint(groups)
+
+ group = buzz.groups().get(userId='108242092577082601423', groupId='G:108242092577082601423:15').execute()
+ self.assertEquals('G:108242092577082601423:15', group['id'], group)
+
+ group = buzz.groups().get(userId='108242092577082601423', groupId='G:108242092577082601423:14').execute()
+ self.assertEquals('G:108242092577082601423:14', group['id'], group)
+
+ group = buzz.groups().get(userId='108242092577082601423', groupId='G:108242092577082601423:13').execute()
+ self.assertEquals('G:108242092577082601423:13', group['id'], group)
+
+ group = buzz.groups().get(userId='108242092577082601423', groupId='G:108242092577082601423:6').execute()
+ self.assertEquals('G:108242092577082601423:6', group['id'], group)
+
+ group = buzz.groups().get(userId='108242092577082601423', groupId='G:108242092577082601423:9999999').execute()
+ self.assertEquals(None, group, group)
+
+ def test_can_delete_activity(self):
+ buzz = build('buzz', 'v1', http=self.http)
+
+ activity = buzz.activities().insert(userId='@me', body={
+ 'title': 'Activity to be deleted',
+ 'object': {
+ 'content': u'Created this activity so that it can be deleted.',
+ 'type': 'note'}
+ }
+ ).execute()
+ id = activity['id']
+
+ buzz.activities().delete(scope='@self', userId='@me', postId=id).execute()
+
+ activity_url = activity['links']['self'][0]['href']
+ resp, content = self.http.request(activity_url, 'GET')
+ self.assertEquals(404, resp.status)
+
if __name__ == '__main__':
unittest.main()
diff --git a/httplib2/__init__.py b/httplib2/__init__.py
index 61e9caa..567e24e 100644
--- a/httplib2/__init__.py
+++ b/httplib2/__init__.py
@@ -55,9 +55,9 @@
import socket
try:
- from httplib2 import socks
+ from httplib2 import socks
except ImportError:
- socks = None
+ socks = None
# Build the appropriate socket wrapper for ssl
try:
@@ -83,7 +83,7 @@
__all__ = ['Http', 'Response', 'ProxyInfo', 'HttpLib2Error',
'RedirectMissingLocation', 'RedirectLimit', 'FailedToDecompressContent',
'UnimplementedDigestAuthOptionError', 'UnimplementedHmacDigestAuthOptionError',
- 'debuglevel']
+ 'debuglevel', 'ProxiesUnavailableError']
# The httplib debug level, set to a non-zero value to get debug output
@@ -125,6 +125,7 @@
class RelativeURIError(HttpLib2Error): pass
class ServerNotFoundError(HttpLib2Error): pass
+class ProxiesUnavailableError(HttpLib2Error): pass
# Open Items:
# -----------
@@ -721,6 +722,9 @@
def connect(self):
"""Connect to the host and port specified in __init__."""
# Mostly verbatim from httplib.py.
+ if self.proxy_info and socks is None:
+ raise ProxiesUnavailableError(
+ 'Proxy support missing but proxy use was requested!')
msg = "getaddrinfo returns an empty list"
for res in socket.getaddrinfo(self.host, self.port, 0,
socket.SOCK_STREAM):
diff --git a/httplib2/socks.py b/httplib2/socks.py
index 6f4f020..b65fb38 100644
--- a/httplib2/socks.py
+++ b/httplib2/socks.py
@@ -41,12 +41,13 @@
"""
import socket
+
+if getattr(socket, 'socket', None) is None:
+ raise ImportError('socket.socket missing, proxy support unusable')
+
import struct
import sys
-if not hasattr(socket, 'socket'):
- raise ImportError("Running on App Engine?")
-
PROXY_TYPE_SOCKS4 = 1
PROXY_TYPE_SOCKS5 = 2
PROXY_TYPE_HTTP = 3
diff --git a/samples/buzz/buzz.py b/samples/buzz/buzz.py
new file mode 100644
index 0000000..14b5ea1
--- /dev/null
+++ b/samples/buzz/buzz.py
@@ -0,0 +1,66 @@
+#!/usr/bin/python2.4
+# -*- coding: utf-8 -*-
+#
+# Copyright 2010 Google Inc. All Rights Reserved.
+
+"""Simple command-line example for Buzz.
+
+Command-line application that retrieves the users
+latest content and then adds a new entry.
+"""
+
+__author__ = 'jcgregorio@google.com (Joe Gregorio)'
+
+from apiclient.discovery import build
+
+import httplib2
+import pickle
+import pprint
+
+# Uncomment the next line to get very detailed logging
+# httplib2.debuglevel = 4
+
+def main():
+ f = open("buzz.dat", "r")
+ credentials = pickle.loads(f.read())
+ f.close()
+
+ http = httplib2.Http()
+ http = credentials.authorize(http)
+
+ p = build("buzz", "v1", http=http)
+ activities = p.activities()
+
+ # Retrieve the first two activities
+ activitylist = activities.list(max_results='2', scope='@self', userId='@me').execute()
+ print "Retrieved the first two activities"
+
+ # Retrieve the next two activities
+ activitylist = activities.list_next(activitylist).execute()
+ print "Retrieved the next two activities"
+
+ # Add a new activity
+ new_activity_body = {
+ 'title': 'Testing insert',
+ 'object': {
+ 'content': u'Just a short note to show that insert is working. ☄',
+ 'type': 'note'}
+ }
+ activity = activities.insert(userId='@me', body=new_activity_body).execute()
+ print "Added a new activity"
+
+ activitylist = activities.list(max_results='2', scope='@self', userId='@me').execute()
+
+ # Add a comment to that activity
+ comment_body = {
+ "content": "This is a comment"
+ }
+ item = activitylist['items'][0]
+ comment = p.comments().insert(
+ userId=item['actor']['id'], postId=item['id'], body=comment_body
+ ).execute()
+ print 'Added a comment to the new activity'
+ pprint.pprint(comment)
+
+if __name__ == '__main__':
+ main()
diff --git a/samples/cmdline/three_legged_dance.py b/samples/buzz/three_legged_dance.py
similarity index 100%
rename from samples/cmdline/three_legged_dance.py
rename to samples/buzz/three_legged_dance.py
diff --git a/samples/cmdline/buzz.py b/samples/cmdline/buzz.py
deleted file mode 100644
index 92be46d..0000000
--- a/samples/cmdline/buzz.py
+++ /dev/null
@@ -1,46 +0,0 @@
-#!/usr/bin/python2.4
-# -*- coding: utf-8 -*-
-#
-# Copyright 2010 Google Inc. All Rights Reserved.
-
-"""Simple command-line example for Buzz.
-
-Command-line application that retrieves the users
-latest content and then adds a new entry.
-"""
-
-__author__ = 'jcgregorio@google.com (Joe Gregorio)'
-
-
-from apiclient.discovery import build
-
-import httplib2
-httplib2.debuglevel = 4
-import pickle
-
-
-def main():
- f = open("buzz.dat", "r")
- credentials = pickle.loads(f.read())
- f.close()
-
- http = httplib2.Http()
- http = credentials.authorize(http)
-
- p = build("buzz", "v1", http=http, developerKey='AIzaSyDRRpR3GS1F1_jKNNM9HCNd2wJQyPG3oN0')
- activities = p.activities()
- activitylist = activities.list(max_results='2', scope='@self', userId='@me').execute()
- print activitylist['items'][0]['title']
- activitylist = activities.list_next(activitylist).execute()
- print activitylist['items'][0]['title']
-
- activities.insert(userId='@me', body={
- 'title': 'Testing insert',
- 'object': {
- 'content': u'Just a short note to show that insert is working. ☄',
- 'type': 'note'}
- }
- ).execute()
-
-if __name__ == '__main__':
- main()
diff --git a/samples/cmdline/moderator.py b/samples/cmdline/moderator.py
deleted file mode 100644
index 36f354a..0000000
--- a/samples/cmdline/moderator.py
+++ /dev/null
@@ -1,33 +0,0 @@
-#!/usr/bin/python2.4
-# -*- coding: utf-8 -*-
-#
-# Copyright 2010 Google Inc. All Rights Reserved.
-
-"""Simple command-line example for Buzz.
-
-Command-line application that retrieves the users
-latest content and then adds a new entry.
-"""
-
-__author__ = 'jcgregorio@google.com (Joe Gregorio)'
-
-
-from apiclient.discovery import build
-
-import httplib2
-import pickle
-
-
-def main():
- f = open("moderator.dat", "r")
- credentials = pickle.loads(f.read())
- f.close()
-
- http = httplib2.Http()
- http = credentials.authorize(http)
-
- p = build("moderator", "v1", http=http)
- print p.submissions().list(seriesId="7035", topicId="64").execute()
-
-if __name__ == '__main__':
- main()
diff --git a/samples/moderator/moderator.py b/samples/moderator/moderator.py
new file mode 100644
index 0000000..3d742e8
--- /dev/null
+++ b/samples/moderator/moderator.py
@@ -0,0 +1,75 @@
+#!/usr/bin/python2.4
+# -*- coding: utf-8 -*-
+#
+# Copyright 2010 Google Inc. All Rights Reserved.
+
+"""Simple command-line example for Buzz.
+
+Command-line application that retrieves the users
+latest content and then adds a new entry.
+"""
+
+__author__ = 'jcgregorio@google.com (Joe Gregorio)'
+
+
+from apiclient.discovery import build
+
+import httplib2
+import pickle
+
+# Uncomment to get detailed logging
+# httplib2.debuglevel = 4
+
+def main():
+ f = open("moderator.dat", "r")
+ credentials = pickle.loads(f.read())
+ f.close()
+
+ http = httplib2.Http()
+ http = credentials.authorize(http)
+
+ p = build("moderator", "v1", http=http)
+
+ series_body = {
+ "description": "Share and rank tips for eating healthily on the cheaps!",
+ "name": "Eating Healthy & Cheap",
+ "videoSubmissionAllowed": False
+ }
+ series = p.series().insert(body=series_body).execute()
+ print "Created a new series"
+
+ topic_body = {
+ "data": {
+ "description": "Share your ideas on eating healthy!",
+ "name": "Ideas",
+ "presenter": "liz"
+ }
+ }
+ topic = p.topics().insert(seriesId=series['id']['seriesId'], body=topic_body).execute()
+ print "Created a new topic"
+
+ submission_body = {
+ "data": {
+ "attachmentUrl": "http://www.youtube.com/watch?v=1a1wyc5Xxpg",
+ "attribution": {
+ "displayName": "Bashan",
+ "location": "Bainbridge Island, WA"
+ },
+ "text": "Charlie Ayers @ Google"
+ }
+ }
+ submission = p.submissions().insert(seriesId=topic['id']['seriesId'],
+ topicId=topic['id']['topicId'], body=submission_body).execute()
+ print "Inserted a new submisson on the topic"
+
+ vote_body = {
+ "data": {
+ "vote": "PLUS"
+ }
+ }
+ p.votes().insert(seriesId=topic['id']['seriesId'], submissionId=submission['id']['submissionId'], body=vote_body)
+ print "Voted on the submission"
+
+
+if __name__ == '__main__':
+ main()
diff --git a/samples/cmdline/three_legged_dance_moderator.py b/samples/moderator/three_legged_dance.py
similarity index 95%
rename from samples/cmdline/three_legged_dance_moderator.py
rename to samples/moderator/three_legged_dance.py
index f09410d..fbc90ec 100644
--- a/samples/cmdline/three_legged_dance_moderator.py
+++ b/samples/moderator/three_legged_dance.py
@@ -35,6 +35,7 @@
user_agent='google-api-client-python-mdrtr-cmdline/1.0',
domain='anonymous',
scope='https://www.googleapis.com/auth/moderator',
+ #scope='tag:google.com,2010:auth/moderator',
xoauth_displayname='Google API Client Example App')
authorize_url = flow.step1_get_authorize_url()
diff --git a/tests/data/buzz.json b/tests/data/buzz.json
index f12a445..62af35c 100644
--- a/tests/data/buzz.json
+++ b/tests/data/buzz.json
@@ -621,7 +621,7 @@
"liked": {
"pathUrl": "buzz/v1/activities/{userId}/{scope}/{postId}/{groupId}",
"rpcName": "buzz.people.liked",
- "httpMethod": "POST",
+ "httpMethod": "GET",
"methodType": "rest",
"parameters": {
"groupId": {
@@ -646,6 +646,11 @@
"pattern": "[^/]+",
"required": true
},
+ "scope": {
+ "parameterType": "path",
+ "pattern": "@.*",
+ "required": true
+ },
"postId": {
"parameterType": "path",
"pattern": ".*",
diff --git a/tests/test_discovery.py b/tests/test_discovery.py
index fdca968..8704e92 100644
--- a/tests/test_discovery.py
+++ b/tests/test_discovery.py
@@ -26,6 +26,10 @@
import os
import unittest
import urlparse
+try:
+ from urlparse import parse_qs
+except ImportError:
+ from cgi import parse_qs
from apiclient.discovery import build, key2param
@@ -102,7 +106,7 @@
class Next(unittest.TestCase):
- def test_next(self):
+ def test_next_for_activities_list(self):
self.http = HttpMock('buzz.json', {'status': '200'})
buzz = build('buzz', 'v1', self.http, developerKey='foobie_bletch')
activities = {'links':
@@ -122,10 +126,18 @@
[{'href': 'http://www.googleapis.com/next-link'}]}}
request = buzz.activities().list_next(activities)
parsed = urlparse.urlparse(request.uri)
- q = urlparse.parse_qs(parsed[4])
+ q = parse_qs(parsed[4])
self.assertEqual(q['key'], ['foobie_bletch'])
+ def test_next_for_people_liked(self):
+ self.http = HttpMock('buzz.json', {'status': '200'})
+ buzz = build('buzz', 'v1', self.http)
+ people = {'links':
+ {'next':
+ [{'href': 'http://www.googleapis.com/next-link'}]}}
+ request = buzz.people().liked_next(people)
+ self.assertEqual(request.uri, 'http://www.googleapis.com/next-link')
if __name__ == '__main__':
unittest.main()
diff --git a/upload-diffs.py b/upload-diffs.py
index 1b5daac..c3ff6b9 100644
--- a/upload-diffs.py
+++ b/upload-diffs.py
@@ -487,7 +487,7 @@
help="Base revision/branch/tree to diff against. Use "
"rev1:rev2 range to review already committed changeset.")
group.add_option("--send_mail", action="store_true",
- dest="send_mail", default=False,
+ dest="send_mail", default=True,
help="Send notification email to reviewers.")
group.add_option("--vcs", action="store", dest="vcs",
metavar="VCS", default=None,