This commit is contained in:
Joe Gregorio
2010-10-11 13:04:24 -04:00
20 changed files with 513 additions and 120 deletions

13
README
View File

@@ -15,18 +15,19 @@ Running
=======
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

10
TODO
View File

@@ -11,12 +11,9 @@ TODO
- 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 @@ TODO
DONE
====
- Implement requests as Command objects, either for immediate
execution, or for batching.
- Add in 'Extra Discovery' for pagination

View File

@@ -112,7 +112,12 @@
"methods": {
"delete": {},
"get": {},
"liked": {},
"liked": {
"next": {
"type": "uri",
"location": ["links", "next", 0, "href"]
}
},
"list": {},
"relatedToUri": {},
"reshared": {},

View File

@@ -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",

View File

@@ -28,6 +28,10 @@ import re
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 @@ except ImportError: # pragma: no cover
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 @@ class JsonModel(object):
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 @@ class JsonModel(object):
# 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 build(serviceName, version, http=None,
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 @@ def createResource(http, baseUrl, model, resourceName, developerKey,
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 @@ def createResource(http, baseUrl, model, resourceName, developerKey,
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)

View File

@@ -21,7 +21,17 @@ except ImportError:
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 @@ class OAuthCredentials(Credentials):
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 @@ class FlowThreeLegged(object):
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 @@ class FlowThreeLegged(object):
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(

View File

@@ -36,6 +36,18 @@ REQUEST_TOKEN_URL = 'https://www.google.com/accounts/OAuthGetRequestToken?domain
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 @@ class BuzzGaeClient(object):
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):

View File

@@ -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.

View File

@@ -9,39 +9,239 @@ Functional tests that verify we can retrieve data from existing services.
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()

View File

@@ -55,9 +55,9 @@ from gettext import gettext as _
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 @@ def has_timeout(timeout): # python 2.6
__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 UnimplementedHmacDigestAuthOptionError(HttpLib2ErrorWithResponse): pass
class RelativeURIError(HttpLib2Error): pass
class ServerNotFoundError(HttpLib2Error): pass
class ProxiesUnavailableError(HttpLib2Error): pass
# Open Items:
# -----------
@@ -721,6 +722,9 @@ class HTTPConnectionWithTimeout(httplib.HTTPConnection):
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):

View File

@@ -41,12 +41,13 @@ mainly to merge bug fixes found in Sourceforge
"""
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

66
samples/buzz/buzz.py Normal file
View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -35,6 +35,7 @@ flow = FlowThreeLegged(moderator_discovery,
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()

View File

@@ -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": ".*",

View File

@@ -26,6 +26,10 @@ import httplib2
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 Discovery(unittest.TestCase):
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 @@ class DeveloperKey(unittest.TestCase):
[{'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()

View File

@@ -487,7 +487,7 @@ group.add_option("--rev", action="store", dest="revision",
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,