merge
This commit is contained in:
13
README
13
README
@@ -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
10
TODO
@@ -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
|
||||
@@ -112,7 +112,12 @@
|
||||
"methods": {
|
||||
"delete": {},
|
||||
"get": {},
|
||||
"liked": {},
|
||||
"liked": {
|
||||
"next": {
|
||||
"type": "uri",
|
||||
"location": ["links", "next", 0, "href"]
|
||||
}
|
||||
},
|
||||
"list": {},
|
||||
"relatedToUri": {},
|
||||
"reshared": {},
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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):
|
||||
|
||||
50
functional_tests/data/buzz_credentials.dat
Normal file
50
functional_tests/data/buzz_credentials.dat
Normal 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.
|
||||
@@ -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()
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
66
samples/buzz/buzz.py
Normal 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()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
75
samples/moderator/moderator.py
Normal file
75
samples/moderator/moderator.py
Normal 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()
|
||||
@@ -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()
|
||||
@@ -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": ".*",
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user